Visual Servoing Platform  version 3.6.1 under development (2024-11-15)
vpGDIRenderer.cpp
1 /*
2  * ViSP, open source Visual Servoing Platform software.
3  * Copyright (C) 2005 - 2024 by Inria. All rights reserved.
4  *
5  * This software is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  * See the file LICENSE.txt at the root directory of this source
10  * distribution for additional information about the GNU GPL.
11  *
12  * For using ViSP with software that can not be combined with the GNU
13  * GPL, please contact Inria about acquiring a ViSP Professional
14  * Edition License.
15  *
16  * See https://visp.inria.fr for more information.
17  *
18  * This software was developed at:
19  * Inria Rennes - Bretagne Atlantique
20  * Campus Universitaire de Beaulieu
21  * 35042 Rennes Cedex
22  * France
23  *
24  * If you have questions regarding the use of this file, please contact
25  * Inria at visp@inria.fr
26  *
27  * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
28  * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
29  *
30  * Description:
31  * GDI renderer for windows 32 display
32  */
33 
34 #include <visp3/core/vpConfig.h>
35 #define GDI_ROBUST
36 #if (defined(VISP_HAVE_GDI))
37 
38 #ifndef DOXYGEN_SHOULD_SKIP_THIS
39 
40 #include <visp3/gui/vpGDIRenderer.h>
41 
42 BEGIN_VISP_NAMESPACE
43 
47 vpGDIRenderer::vpGDIRenderer() : m_bmp(nullptr), m_bmp_width(0), m_bmp_height(0), timelost(0)
48 {
49  // if the screen depth is not 32bpp, throw an exception
50  int bpp = GetDeviceCaps(GetDC(nullptr), BITSPIXEL);
51  if (bpp != 32)
53  "vpGDIRenderer supports only 32bits depth: screen is %dbits depth!", bpp);
54 
55  InitializeCriticalSection(&m_criticalSection);
56 
57  // initialize GDI the palette
58  vpColor pcolor; // Predefined colors
59 
60  pcolor = vpColor::black;
61  m_colors[vpColor::id_black] = RGB(pcolor.R, pcolor.G, pcolor.B);
62  pcolor = vpColor::lightBlue;
63  m_colors[vpColor::id_lightBlue] = RGB(pcolor.R, pcolor.G, pcolor.B);
64  pcolor = vpColor::blue;
65  m_colors[vpColor::id_blue] = RGB(pcolor.R, pcolor.G, pcolor.B);
66  pcolor = vpColor::darkBlue;
67  m_colors[vpColor::id_darkBlue] = RGB(pcolor.R, pcolor.G, pcolor.B);
68  pcolor = vpColor::cyan;
69  m_colors[vpColor::id_cyan] = RGB(pcolor.R, pcolor.G, pcolor.B);
70  pcolor = vpColor::lightGreen;
71  m_colors[vpColor::id_lightGreen] = RGB(pcolor.R, pcolor.G, pcolor.B);
72  pcolor = vpColor::green;
73  m_colors[vpColor::id_green] = RGB(pcolor.R, pcolor.G, pcolor.B);
74  pcolor = vpColor::darkGreen;
75  m_colors[vpColor::id_darkGreen] = RGB(pcolor.R, pcolor.G, pcolor.B);
76  pcolor = vpColor::lightRed;
77  m_colors[vpColor::id_lightRed] = RGB(pcolor.R, pcolor.G, pcolor.B);
78  pcolor = vpColor::red;
79  m_colors[vpColor::id_red] = RGB(pcolor.R, pcolor.G, pcolor.B);
80  pcolor = vpColor::darkRed;
81  m_colors[vpColor::id_darkRed] = RGB(pcolor.R, pcolor.G, pcolor.B);
82  pcolor = vpColor::white;
83  m_colors[vpColor::id_white] = RGB(pcolor.R, pcolor.G, pcolor.B);
84  pcolor = vpColor::lightGray;
85  m_colors[vpColor::id_lightGray] = RGB(pcolor.R, pcolor.G, pcolor.B);
86  pcolor = vpColor::gray;
87  m_colors[vpColor::id_gray] = RGB(pcolor.R, pcolor.G, pcolor.B);
88  pcolor = vpColor::darkGray;
89  m_colors[vpColor::id_darkGray] = RGB(pcolor.R, pcolor.G, pcolor.B);
90  pcolor = vpColor::yellow;
91  m_colors[vpColor::id_yellow] = RGB(pcolor.R, pcolor.G, pcolor.B);
92  pcolor = vpColor::orange;
93  m_colors[vpColor::id_orange] = RGB(pcolor.R, pcolor.G, pcolor.B);
94  pcolor = vpColor::purple;
95  m_colors[vpColor::id_purple] = RGB(pcolor.R, pcolor.G, pcolor.B);
96 
97  m_rwidth = 0;
98  m_rheight = 0;
99 }
100 
104 vpGDIRenderer::~vpGDIRenderer()
105 {
106  // Deletes the critical section object
107  DeleteCriticalSection(&m_criticalSection);
108  // Deletes the bitmap
109  DeleteObject(m_bmp);
110  // Deletes the font object
111  DeleteObject(m_hFont);
112 }
113 
120 bool vpGDIRenderer::init(HWND hWindow, unsigned int width, unsigned int height)
121 {
122  timelost = 0.;
123  m_hWnd = hWindow;
124 
125  m_rwidth = width;
126  m_rheight = height;
127 
128  // creates the font
129  m_hFont = CreateFont(18, 0, 0, 0, FW_NORMAL, false, false, false, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS,
130  CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | FF_DONTCARE, nullptr);
131  return true;
132 }
133 
138 void vpGDIRenderer::setImg(const vpImage<vpRGBa> &I)
139 {
140  // converts the image into a HBITMAP
141  convert(I, m_bmp);
142 }
143 
150 void vpGDIRenderer::setImgROI(const vpImage<vpRGBa> &I, const vpImagePoint &iP, unsigned int width, unsigned int height)
151 {
152  // converts the image into a HBITMAP
153  convertROI(I, iP, width, height);
154 }
155 
160 void vpGDIRenderer::setImg(const vpImage<unsigned char> &I)
161 {
162  // converts the image into a HBITMAP
163  convert(I, m_bmp);
164 }
165 
172 void vpGDIRenderer::setImgROI(const vpImage<unsigned char> &I, const vpImagePoint &iP, unsigned int width,
173  unsigned int height)
174 {
175  // converts the image into a HBITMAP
176  convertROI(I, iP, width, height);
177 }
178 
182 bool vpGDIRenderer::render()
183 {
184  // gets the window's DC
185  PAINTSTRUCT ps;
186  HDC hDCScreen = BeginPaint(m_hWnd, &ps);
187 
188  // create a memory DC
189  HDC hDCMem = CreateCompatibleDC(hDCScreen);
190 
191  // selects this bmp in memory
192  EnterCriticalSection(&m_criticalSection);
193  SelectObject(hDCMem, m_bmp);
194 
195  // blits it on the window's DC
196  BitBlt(hDCScreen, 0, 0, static_cast<int>(m_rwidth), static_cast<int>(m_rheight), hDCMem, 0, 0, SRCCOPY);
197 
198  LeaveCriticalSection(&m_criticalSection);
199  // DeleteDC(hDCMem);
200  DeleteObject(hDCMem);
201 
202  EndPaint(m_hWnd, &ps);
203 
204  return true;
205 }
206 
212 void vpGDIRenderer::convert(const vpImage<vpRGBa> &I, HBITMAP &hBmp)
213 {
214  // allocate the buffer
215  unsigned char *imBuffer = new unsigned char[m_rwidth * m_rheight * 4];
216 
217  if (m_rscale == 1) {
218  for (unsigned int i = 0, k = 0; i < m_rwidth * m_rheight * 4; i += 4, ++k) {
219  imBuffer[i + 0] = I.bitmap[k].B;
220  imBuffer[i + 1] = I.bitmap[k].G;
221  imBuffer[i + 2] = I.bitmap[k].R;
222  imBuffer[i + 3] = I.bitmap[k].A;
223  }
224  }
225  else {
226  for (unsigned int i = 0; i < m_rheight; ++i) {
227  unsigned int i_ = i * m_rscale;
228  unsigned int ii_ = i * m_rwidth;
229  for (unsigned int j = 0; j < m_rwidth; ++j) {
230  vpRGBa val = I[i_][j * m_rscale];
231  unsigned int index_ = (ii_ + j) * 4;
232  imBuffer[index_] = val.B;
233  imBuffer[++index_] = val.G;
234  imBuffer[++index_] = val.R;
235  imBuffer[++index_] = val.A;
236  }
237  }
238  }
239 
240  // updates the bitmap's pixel data
241  updateBitmap(hBmp, imBuffer, m_rwidth, m_rheight);
242 
243  // we don't need this buffer anymore
244  delete[] imBuffer;
245 }
246 
253 void vpGDIRenderer::convertROI(const vpImage<vpRGBa> &I, const vpImagePoint &iP, unsigned int width,
254  unsigned int height)
255 {
256  int i_min = std::max<int>((int)ceil(iP.get_i() / m_rscale), 0);
257  int j_min = std::max<int>((int)ceil(iP.get_j() / m_rscale), 0);
258  int i_max = std::min<int>((int)ceil((iP.get_i() + height) / m_rscale), (int)m_rheight);
259  int j_max = std::min<int>((int)ceil((iP.get_j() + width) / m_rscale), (int)m_rwidth);
260 
261  int h = i_max - i_min;
262  int w = j_max - j_min;
263 
264  // allocate the buffer
265  unsigned char *imBuffer = new unsigned char[w * h * 4];
266 
267  if (m_rscale == 1) {
268  vpRGBa *bitmap = I.bitmap;
269  unsigned int iwidth = I.getWidth();
270  bitmap = bitmap + (int)(i_min * iwidth + j_min);
271 
272  int k = 0;
273  for (int i = 0; i < w * h * 4; i += 4) {
274  imBuffer[i + 0] = (bitmap + k)->B;
275  imBuffer[i + 1] = (bitmap + k)->G;
276  imBuffer[i + 2] = (bitmap + k)->R;
277  imBuffer[i + 3] = (bitmap + k)->A;
278  // bitmap++;
279  k++;
280  if (k == w) {
281  bitmap = bitmap + iwidth;
282  k = 0;
283  }
284  }
285  }
286  else {
287  for (int i = 0; i < h; ++i) {
288  unsigned int i_ = (i_min + i) * m_rscale;
289  unsigned int ii_ = i * w;
290  for (int j = 0; j < w; ++j) {
291  vpRGBa val = I[i_][(j_min + j) * m_rscale];
292  unsigned int index_ = (ii_ + j) * 4;
293  imBuffer[index_] = val.B;
294  imBuffer[++index_] = val.G;
295  imBuffer[++index_] = val.R;
296  imBuffer[++index_] = val.A;
297  }
298  }
299  }
300 
301  // updates the bitmap's pixel data
302  updateBitmapROI(imBuffer, i_min, j_min, w, h);
303 
304  // we don't need this buffer anymore
305  delete[] imBuffer;
306 }
307 
313 void vpGDIRenderer::convert(const vpImage<unsigned char> &I, HBITMAP &hBmp)
314 {
315  // allocate the buffer
316  unsigned char *imBuffer = new unsigned char[m_rwidth * m_rheight * 4];
317 
318  if (m_rscale == 1) {
319  for (unsigned int i = 0, k = 0; i < m_rwidth * m_rheight * 4; i += 4, ++k) {
320  imBuffer[i + 0] = I.bitmap[k];
321  imBuffer[i + 1] = I.bitmap[k];
322  imBuffer[i + 2] = I.bitmap[k];
323  imBuffer[i + 3] = vpRGBa::alpha_default;
324  }
325  }
326  else {
327  for (unsigned int i = 0; i < m_rheight; ++i) {
328  unsigned int i_ = i * m_rscale;
329  unsigned int ii_ = i * m_rwidth;
330  for (unsigned int j = 0; j < m_rwidth; ++j) {
331  unsigned char val = I[i_][j * m_rscale];
332  unsigned int index_ = (ii_ + j) * 4;
333  imBuffer[index_] = val;
334  imBuffer[++index_] = val;
335  imBuffer[++index_] = val;
336  imBuffer[++index_] = vpRGBa::alpha_default;
337  }
338  }
339  }
340 
341  // updates the bitmap's pixel data
342  updateBitmap(hBmp, imBuffer, m_rwidth, m_rheight);
343 
344  // we don't need this buffer anymore
345  delete[] imBuffer;
346 }
347 
354 void vpGDIRenderer::convertROI(const vpImage<unsigned char> &I, const vpImagePoint &iP, unsigned int width,
355  unsigned int height)
356 {
357  int i_min = std::max<int>((int)ceil(iP.get_i() / m_rscale), 0);
358  int j_min = std::max<int>((int)ceil(iP.get_j() / m_rscale), 0);
359  int i_max = std::min<int>((int)ceil((iP.get_i() + height) / m_rscale), (int)m_rheight);
360  int j_max = std::min<int>((int)ceil((iP.get_j() + width) / m_rscale), (int)m_rwidth);
361 
362  int h = i_max - i_min;
363  int w = j_max - j_min;
364 
365  // allocate the buffer
366  unsigned char *imBuffer = new unsigned char[w * h * 4];
367 
368  if (m_rscale == 1) {
369  for (int i = 0; i < h; ++i) {
370  unsigned int i_ = i_min + i;
371  unsigned int ii_ = i * w;
372  for (int j = 0; j < w; ++j) {
373  unsigned char val = I[i_][j_min + j];
374  unsigned int index_ = (ii_ + j) * 4;
375  imBuffer[index_] = val;
376  imBuffer[++index_] = val;
377  imBuffer[++index_] = val;
378  imBuffer[++index_] = vpRGBa::alpha_default;
379  }
380  }
381  }
382  else {
383  for (int i = 0; i < h; ++i) {
384  unsigned int i_ = (i_min + i) * m_rscale;
385  unsigned int ii_ = i * w;
386  for (int j = 0; j < w; ++j) {
387  unsigned char val = I[i_][(j_min + j) * m_rscale];
388  unsigned int index_ = (ii_ + j) * 4;
389  imBuffer[index_] = val;
390  imBuffer[++index_] = val;
391  imBuffer[++index_] = val;
392  imBuffer[++index_] = vpRGBa::alpha_default;
393  }
394  }
395  }
396 
397  // updates the bitmap's pixel data
398  updateBitmapROI(imBuffer, i_min, j_min, w, h);
399 
400  // we don't need this buffer anymore
401  delete[] imBuffer;
402 }
403 
414 bool vpGDIRenderer::updateBitmap(HBITMAP &hBmp, unsigned char *imBuffer, unsigned int w, unsigned int h)
415 {
416  // the bitmap may only be accessed by one thread at the same time
417  // that's why we enter critical section
418  EnterCriticalSection(&m_criticalSection);
419 
420  // if the existing bitmap object is of the right size
421  if ((m_bmp_width == w) && (m_bmp_height == h) && w != 0 && h != 0) {
422  // just replace the content
423  SetBitmapBits(hBmp, w * h * 4, imBuffer);
424  }
425  else {
426  if (hBmp != nullptr) {
427  // delete the old BITMAP
428  DeleteObject(hBmp);
429  }
430  // create a new BITMAP from this buffer
431  if ((hBmp = CreateBitmap(static_cast<int>(w), static_cast<int>(h), 1, 32, (void *)imBuffer)) == nullptr)
432  return false;
433 
434  m_bmp_width = w;
435  m_bmp_height = h;
436  }
437 
438  LeaveCriticalSection(&m_criticalSection);
439  return true;
440 }
441 
452 bool vpGDIRenderer::updateBitmapROI(unsigned char *imBuffer, int i_min, int j_min, int w, int h)
453 {
454  HBITMAP htmp = CreateBitmap(w, h, 1, 32, (void *)imBuffer);
455 
456  // get the window's DC
457  HDC hDCScreen = GetDC(m_hWnd);
458  HDC hDCMem = CreateCompatibleDC(hDCScreen);
459  HDC hDCMem2 = CreateCompatibleDC(hDCScreen);
460 
461  // select this bmp in memory
462  EnterCriticalSection(&m_criticalSection);
463  SelectObject(hDCMem, m_bmp);
464  SelectObject(hDCMem2, htmp);
465 
466  BitBlt(hDCMem, j_min, i_min, w, h, hDCMem2, 0, 0, SRCCOPY);
467  LeaveCriticalSection(&m_criticalSection);
468 
469  DeleteDC(hDCMem);
470  ReleaseDC(m_hWnd, hDCScreen);
471  DeleteObject(htmp);
472 
473  return true;
474 }
475 
482 void vpGDIRenderer::setPixel(const vpImagePoint &iP, const vpColor &color)
483 {
484  // get the window's DC
485  HDC hDCScreen = GetDC(m_hWnd);
486  HDC hDCMem = CreateCompatibleDC(hDCScreen);
487 
488  // select this bmp in memory
489  EnterCriticalSection(&m_criticalSection);
490  SelectObject(hDCMem, m_bmp);
491 
492  if (color.id < vpColor::id_unknown)
493  SetPixel(hDCMem, vpMath::round(iP.get_u() / m_rscale), vpMath::round(iP.get_v() / m_rscale), m_colors[color.id]);
494  else {
495  COLORREF gdicolor = RGB(color.R, color.G, color.B);
496  SetPixel(hDCMem, vpMath::round(iP.get_u() / m_rscale), vpMath::round(iP.get_v() / m_rscale), gdicolor);
497  }
498  // display the result (flush)
499  // BitBlt(hDCScreen, x, y, 1, 1, hDCMem, x, y, SRCCOPY);
500 
501  LeaveCriticalSection(&m_criticalSection);
502 
503  DeleteDC(hDCMem);
504  ReleaseDC(m_hWnd, hDCScreen);
505 }
506 
514 void vpGDIRenderer::drawLine(const vpImagePoint &ip1, const vpImagePoint &ip2, const vpColor &color,
515  unsigned int thickness, int style)
516 {
517  HDC hDCScreen = nullptr, hDCMem = nullptr;
518  HPEN hPen = nullptr;
519 #ifdef GDI_ROBUST
520  double start = vpTime::measureTimeMs();
521  while (vpTime::measureTimeMs() - start < 1000) {
522  hDCScreen = GetDC(m_hWnd);
523 
524  if (!hDCScreen)
525  continue;
526  hDCMem = CreateCompatibleDC(hDCScreen);
527  if (!hDCMem) {
528  ReleaseDC(m_hWnd, hDCScreen);
529  continue;
530  }
531 
532  // create the pen
533  if (color.id < vpColor::id_unknown)
534  hPen = CreatePen(style, static_cast<int>(thickness), m_colors[color.id]);
535  else {
536  COLORREF gdicolor = RGB(color.R, color.G, color.B);
537  hPen = CreatePen(style, static_cast<int>(thickness), gdicolor);
538  }
539  if (!hPen) {
540  DeleteDC(hDCMem);
541  ReleaseDC(m_hWnd, hDCScreen);
542  continue;
543  }
544  if (!SetBkMode(hDCMem, TRANSPARENT)) {
545  DeleteObject(hPen);
546  DeleteDC(hDCMem);
547  ReleaseDC(m_hWnd, hDCScreen);
548  continue;
549  }
550 
551  // select this bmp in memory
552  EnterCriticalSection(&m_criticalSection);
553 
554  if (!SelectObject(hDCMem, m_bmp)) {
555  LeaveCriticalSection(&m_criticalSection);
556  DeleteObject(hPen);
557  DeleteDC(hDCMem);
558  ReleaseDC(m_hWnd, hDCScreen);
559  continue;
560  }
561 
562  // select the pen
563  if (!SelectObject(hDCMem, hPen)) {
564  LeaveCriticalSection(&m_criticalSection);
565  DeleteObject(hPen);
566  DeleteDC(hDCMem);
567  ReleaseDC(m_hWnd, hDCScreen);
568  continue;
569  }
570  break;
571  }
572  timelost += (vpTime::measureTimeMs() - start);
573 #else
574  // get the window's DC
575  hDCScreen = GetDC(m_hWnd);
576  hDCMem = CreateCompatibleDC(hDCScreen);
577  // create the pen
578  if (color.id < vpColor::id_unknown)
579  hPen = CreatePen(style, static_cast<int>(thickness), m_colors[color.id]);
580  else {
581  COLORREF gdicolor = RGB(color.R, color.G, color.B);
582  hPen = CreatePen(style, static_cast<int>(thickness), gdicolor);
583  }
584  SetBkMode(hDCMem, TRANSPARENT);
585 
586  // select this bmp in memory
587  EnterCriticalSection(&m_criticalSection);
588  SelectObject(hDCMem, m_bmp);
589 
590  // select the pen
591  SelectObject(hDCMem, hPen);
592 #endif
593  // Warning: When thickness > 1 and pen style is PS_DASHDOT, the drawing
594  // displays a solid line That's why in that case we implement the dashdot
595  // line manually drawing multiple small lines
596  if (thickness != 1 && style != PS_SOLID) {
597  vpImagePoint ip1_ = ip1;
598  vpImagePoint ip2_ = ip2;
599 
600  double size = 10. * m_rscale;
601  double length = sqrt(vpMath::sqr(ip2_.get_i() - ip1_.get_i()) + vpMath::sqr(ip2_.get_j() - ip1_.get_j()));
602  bool vertical_line = (int)ip2_.get_j() == (int)ip1_.get_j();
603  if (vertical_line) {
604  if (ip2_.get_i() < ip1_.get_i()) {
605  std::swap(ip1_, ip2_);
606  }
607  }
608  else if (ip2_.get_j() < ip1_.get_j()) {
609  std::swap(ip1_, ip2_);
610  }
611 
612  double diff_j = vertical_line ? 1 : ip2_.get_j() - ip1_.get_j();
613  double deltaj = size / length * diff_j;
614  double deltai = size / length * (ip2_.get_i() - ip1_.get_i());
615  double slope = (ip2_.get_i() - ip1_.get_i()) / diff_j;
616  double orig = ip1_.get_i() - slope * ip1_.get_j();
617 
618  if (vertical_line) {
619  for (unsigned int i = (unsigned int)ip1_.get_i(); i < ip2_.get_i(); i += (unsigned int)(2 * deltai)) {
620  double j = ip1_.get_j();
621 
622  // Move to the starting point
623  MoveToEx(hDCMem, vpMath::round(j / m_rscale), vpMath::round(i / m_rscale), nullptr);
624  // Draw the line
625  LineTo(hDCMem, vpMath::round(j / m_rscale), vpMath::round((i + deltai) / m_rscale));
626  }
627  }
628  else {
629  for (unsigned int j = (unsigned int)ip1_.get_j(); j < ip2_.get_j(); j += (unsigned int)(2 * deltaj)) {
630  double i = slope * j + orig;
631  // Move to the starting point
632  MoveToEx(hDCMem, vpMath::round(j / m_rscale), vpMath::round(i / m_rscale), nullptr);
633  // Draw the line
634  LineTo(hDCMem, vpMath::round((j + deltaj) / m_rscale), vpMath::round((i + deltai) / m_rscale));
635  }
636  }
637  }
638  else {
639  // move to the starting point
640  MoveToEx(hDCMem, vpMath::round(ip1.get_u() / m_rscale), vpMath::round(ip1.get_v() / m_rscale), nullptr);
641  // Draw the line
642  LineTo(hDCMem, vpMath::round(ip2.get_u() / m_rscale), vpMath::round(ip2.get_v() / m_rscale));
643  }
644 
645  LeaveCriticalSection(&m_criticalSection);
646 
647  DeleteObject(hPen);
648  DeleteDC(hDCMem);
649  ReleaseDC(m_hWnd, hDCScreen);
650 }
651 
661 void vpGDIRenderer::drawRect(const vpImagePoint &topLeft, unsigned int width, unsigned int height, const vpColor &color,
662  bool fill, unsigned int thickness)
663 {
664  if (thickness == 0)
665  thickness = 1;
666  // get the window's DC
667  HDC hDCScreen = GetDC(m_hWnd);
668  HDC hDCMem = CreateCompatibleDC(hDCScreen);
669 
670  // create the pen
671  HPEN hPen;
672  COLORREF gdicolor = RGB(0, 0, 0);
673 
674  if (color.id < vpColor::id_unknown)
675  hPen = CreatePen(PS_SOLID, static_cast<int>(thickness), m_colors[color.id]);
676  else {
677  gdicolor = RGB(color.R, color.G, color.B);
678  hPen = CreatePen(PS_SOLID, static_cast<int>(thickness), gdicolor);
679  }
680 
681  // create an hollow or solid brush (depends on boolean fill)
682  LOGBRUSH lBrush;
683  if (fill) {
684  lBrush.lbStyle = BS_SOLID;
685  if (color.id < vpColor::id_unknown)
686  lBrush.lbColor = m_colors[color.id];
687  else {
688  lBrush.lbColor = gdicolor;
689  }
690  }
691  else
692  lBrush.lbStyle = BS_HOLLOW;
693  HBRUSH hbrush = CreateBrushIndirect(&lBrush);
694 
695  // select this bmp in memory
696  EnterCriticalSection(&m_criticalSection);
697  SelectObject(hDCMem, m_bmp);
698 
699  // select the brush
700  SelectObject(hDCMem, hbrush);
701  // select the pen
702  SelectObject(hDCMem, hPen);
703 
704  // draw the rectangle
705  Rectangle(hDCMem, vpMath::round(topLeft.get_u() / m_rscale), vpMath::round(topLeft.get_v() / m_rscale),
706  vpMath::round((topLeft.get_u() + width) / m_rscale), vpMath::round((topLeft.get_v() + height) / m_rscale));
707 
708  // display the result (flush)
709  // BitBlt(hDCScreen, j, i, width, height, hDCMem, j, i, SRCCOPY);
710 
711  LeaveCriticalSection(&m_criticalSection);
712 
713  DeleteObject(hbrush);
714  DeleteObject(hPen);
715  DeleteDC(hDCMem);
716  ReleaseDC(m_hWnd, hDCScreen);
717 }
718 
723 void vpGDIRenderer::clear(const vpColor &color)
724 {
725  vpImagePoint ip;
726  ip.set_i(0);
727  ip.set_j(0);
728  drawRect(ip, m_rwidth, m_rheight, color, true, 0);
729 }
730 
739 void vpGDIRenderer::drawCircle(const vpImagePoint &center, unsigned int radius, const vpColor &color, bool fill,
740  unsigned int thickness)
741 {
742 
743  // get the window's DC
744  HDC hDCScreen = GetDC(m_hWnd);
745  HDC hDCMem = CreateCompatibleDC(hDCScreen);
746 
747  // create the pen
748  HPEN hPen;
749  if (color.id < vpColor::id_unknown)
750  hPen = CreatePen(PS_SOLID, static_cast<int>(thickness), m_colors[color.id]);
751  else {
752  COLORREF gdicolor = RGB(color.R, color.G, color.B);
753  hPen = CreatePen(PS_SOLID, static_cast<int>(thickness), gdicolor);
754  }
755 
756  // create an hollow brush
757  LOGBRUSH lBrush;
758  lBrush.lbStyle = BS_HOLLOW;
759  HBRUSH hbrush = CreateBrushIndirect(&lBrush);
760 
761  // computes bounding rectangle
762  int radius_ = static_cast<int>(radius);
763  int x1 = vpMath::round(center.get_u() / m_rscale) - radius_ / m_rscale;
764  int y1 = vpMath::round(center.get_v() / m_rscale) - radius_ / m_rscale;
765  int x2 = vpMath::round(center.get_u() / m_rscale) + radius_ / m_rscale;
766  int y2 = vpMath::round(center.get_v() / m_rscale) + radius_ / m_rscale;
767 
768  // select this bmp in memory
769  EnterCriticalSection(&m_criticalSection);
770  SelectObject(hDCMem, m_bmp);
771 
772  // select the brush
773  SelectObject(hDCMem, hbrush);
774  // select the pen
775  SelectObject(hDCMem, hPen);
776 
777  // draw the circle
778  if (fill == false)
779  Ellipse(hDCMem, x1, y1, x2, y2);
780 
781  else {
782  while (x2 - x1 > 0) {
783  x1++;
784  x2--;
785  y1++;
786  y2--;
787  Ellipse(hDCMem, x1, y1, x2, y2);
788  }
789  }
790 
791  // display the result (flush)
792  // BitBlt(hDCScreen, x1, y1, x2-x1, y2-y1, hDCMem, x1, y1, SRCCOPY);
793 
794  LeaveCriticalSection(&m_criticalSection);
795 
796  DeleteObject(hbrush);
797  DeleteObject(hPen);
798  DeleteDC(hDCMem);
799  ReleaseDC(m_hWnd, hDCScreen);
800 }
801 
808 void vpGDIRenderer::drawText(const vpImagePoint &ip, const char *text, const vpColor &color)
809 {
810  // get the window's DC
811  HDC hDCScreen = GetDC(m_hWnd);
812  HDC hDCMem = CreateCompatibleDC(hDCScreen);
813 
814  // select this bmp in memory
815  EnterCriticalSection(&m_criticalSection);
816  SelectObject(hDCMem, m_bmp);
817 
818  // Select the font
819  SelectObject(hDCMem, m_hFont);
820 
821  // set the text color
822  if (color.id < vpColor::id_unknown)
823  SetTextColor(hDCMem, m_colors[color.id]);
824  else {
825  COLORREF gdicolor = RGB(color.R, color.G, color.B);
826  SetTextColor(hDCMem, gdicolor);
827  }
828 
829  // we don't use the bkColor
830  SetBkMode(hDCMem, TRANSPARENT);
831 
832  SIZE size;
833  int length = (int)strlen(text);
834 
835  // get the displayed string dimensions
836  GetTextExtentPoint32(hDCMem, text, length, &size);
837 
838  // displays the string
839  TextOut(hDCMem, vpMath::round(ip.get_u() / m_rscale), vpMath::round(ip.get_v() / m_rscale), text, length);
840 
841  // display the result (flush)
842  // BitBlt(hDCScreen, j, i, size.cx, size.cy, hDCMem, j, i, SRCCOPY);
843 
844  LeaveCriticalSection(&m_criticalSection);
845 
846  DeleteDC(hDCMem);
847  ReleaseDC(m_hWnd, hDCScreen);
848 }
849 
857 void vpGDIRenderer::drawCross(const vpImagePoint &ip, unsigned int size, const vpColor &color, unsigned int thickness)
858 {
859  /* unsigned */ int half_size = static_cast<int>(size / 2 / m_rscale);
860 
861  // if half_size is equal to zero, nothing is displayed with the code
862  // just below. So, if half_size is equal to zero we just draw the
863  // pixel.
864  if (half_size) {
865  // get the window's DC
866  HDC hDCScreen = GetDC(m_hWnd);
867  HDC hDCMem = CreateCompatibleDC(hDCScreen);
868 
869  // create the pen
870  HPEN hPen;
871  if (color.id < vpColor::id_unknown)
872  hPen = CreatePen(PS_SOLID, static_cast<int>(thickness), m_colors[color.id]);
873  else {
874  COLORREF gdicolor = RGB(color.R, color.G, color.B);
875  hPen = CreatePen(PS_SOLID, static_cast<int>(thickness), gdicolor);
876  }
877 
878  // select this bmp in memory
879  EnterCriticalSection(&m_criticalSection);
880  SelectObject(hDCMem, m_bmp);
881 
882  // select the pen
883  SelectObject(hDCMem, hPen);
884 
885  // move to the starting point
886  MoveToEx(hDCMem, vpMath::round(ip.get_u() / m_rscale) - half_size, vpMath::round(ip.get_v() / m_rscale), nullptr);
887  // Draw the first line (horizontal)
888  LineTo(hDCMem, vpMath::round(ip.get_u() / m_rscale) + half_size, vpMath::round(ip.get_v() / m_rscale));
889 
890  // move to the starting point
891  MoveToEx(hDCMem, vpMath::round(ip.get_u() / m_rscale), vpMath::round(ip.get_v() / m_rscale) - half_size, nullptr);
892  // Draw the second line (vertical)
893  LineTo(hDCMem, vpMath::round(ip.get_u() / m_rscale), vpMath::round(ip.get_v() / m_rscale) + half_size);
894 
895  // display the result (flush)
896  // BitBlt(hDCScreen, j-(size/2), i-(size/2), size, size, hDCMem, j-(size/2), i-(size/2), SRCCOPY);
897 
898  LeaveCriticalSection(&m_criticalSection);
899 
900  DeleteObject(hPen);
901  DeleteDC(hDCMem);
902  ReleaseDC(m_hWnd, hDCScreen);
903  }
904  else {
905  setPixel(ip, color);
906  }
907 }
908 
916 void vpGDIRenderer::drawArrow(const vpImagePoint &ip1, const vpImagePoint &ip2, const vpColor &color, unsigned int w,
917  unsigned int h, unsigned int thickness)
918 {
919  double a = ip2.get_i() / m_rscale - ip1.get_i() / m_rscale;
920  double b = ip2.get_j() / m_rscale - ip1.get_j() / m_rscale;
921  double lg = sqrt(vpMath::sqr(a) + vpMath::sqr(b));
922 
923  // computes the coordinates of the rectangle to blit later
924  // unsigned int x = (j2 >= j1) ? j1 : j2;
925  // unsigned int y = (i2 >= i1) ? i1 : i2;
926  // unsigned int w = (j2 >= j1) ? j2-j1 : j1-j2;
927  // unsigned int h = (i2 >= i1) ? i2-i1 : i1-i2;
928 
929  // get the window's DC
930  HDC hDCScreen = GetDC(m_hWnd);
931  HDC hDCMem = CreateCompatibleDC(hDCScreen);
932 
933  // create the pen
934  HPEN hPen;
935  if (color.id < vpColor::id_unknown)
936  hPen = CreatePen(PS_SOLID, static_cast<int>(thickness), m_colors[color.id]);
937  else {
938  COLORREF gdicolor = RGB(color.R, color.G, color.B);
939  hPen = CreatePen(PS_SOLID, static_cast<int>(thickness), gdicolor);
940  }
941 
942  // select this bmp in memory
943  EnterCriticalSection(&m_criticalSection);
944  SelectObject(hDCMem, m_bmp);
945 
946  // select the pen
947  SelectObject(hDCMem, hPen);
948 
949  if ((a == 0) && (b == 0)) {
950  // DisplayCrossLarge(i1,j1,3,col) ;
951  }
952  else {
953  a /= lg;
954  b /= lg;
955 
956  vpImagePoint ip3;
957  ip3.set_i(ip2.get_i() / m_rscale - w * a);
958  ip3.set_j(ip2.get_j() / m_rscale - w * b);
959 
960  vpImagePoint ip4;
961 
962  // double t = 0 ;
963  // while (t<=_l)
964  {
965  ip4.set_i(ip3.get_i() - b * h);
966  ip4.set_j(ip3.get_j() + a * h);
967 
968  if (lg > 2 * vpImagePoint::distance(ip2 / m_rscale, ip4)) {
969  MoveToEx(hDCMem, vpMath::round(ip2.get_u() / m_rscale), vpMath::round(ip2.get_v() / m_rscale), nullptr);
970  LineTo(hDCMem, vpMath::round(ip4.get_u()), vpMath::round(ip4.get_v()));
971  }
972  // t+=0.1 ;
973  }
974 
975  // t = 0 ;
976  // while (t>= -_l)
977  {
978  ip4.set_i(ip3.get_i() + b * h);
979  ip4.set_j(ip3.get_j() - a * h);
980 
981  if (lg > 2 * vpImagePoint::distance(ip2 / m_rscale, ip4)) {
982  MoveToEx(hDCMem, vpMath::round(ip2.get_u() / m_rscale), vpMath::round(ip2.get_v() / m_rscale), nullptr);
983  LineTo(hDCMem, vpMath::round(ip4.get_u()), vpMath::round(ip4.get_v()));
984  }
985 
986  // t-=0.1 ;
987  }
988  MoveToEx(hDCMem, vpMath::round(ip1.get_u() / m_rscale), vpMath::round(ip1.get_v() / m_rscale), nullptr);
989  LineTo(hDCMem, vpMath::round(ip2.get_u() / m_rscale), vpMath::round(ip2.get_v() / m_rscale));
990  }
991 
992  // display the result (flush)
993  // BitBlt(hDCScreen, x, y, w, h, hDCMem, x, y, SRCCOPY);
994 
995  LeaveCriticalSection(&m_criticalSection);
996 
997  DeleteObject(hPen);
998  DeleteDC(hDCMem);
999  ReleaseDC(m_hWnd, hDCScreen);
1000 }
1001 
1006 void vpGDIRenderer::getImage(vpImage<vpRGBa> &I)
1007 {
1008  // size of image buffer : m_rwidth*m_rheight*4
1009  unsigned int size = m_rwidth * m_rheight * 4;
1010  unsigned char *imBuffer = new unsigned char[size];
1011 
1012  // gets the hbitmap's bitmap
1013  GetBitmapBits(m_bmp, static_cast<LONG>(size), (void *)imBuffer);
1014 
1015  // resize the destination image as needed
1016  I.resize(m_rheight, m_rwidth);
1017 
1018  // copy the content
1019  for (unsigned int i = 0; i < size; i += 4) {
1020  I.bitmap[i >> 2].R = imBuffer[i + 2];
1021  I.bitmap[i >> 2].G = imBuffer[i + 1];
1022  I.bitmap[i >> 2].B = imBuffer[i + 0];
1023  I.bitmap[i >> 2].A = vpRGBa::alpha_default; // default opacity
1024  }
1025 
1026  delete[] imBuffer;
1027 }
1028 
1029 END_VISP_NAMESPACE
1030 #endif
1031 #elif !defined(VISP_BUILD_SHARED_LIBS)
1032 // Work around to avoid warning: libvisp_gui.a(vpGDIRenderer.cpp.o) has no symbols
1033 void dummy_vpGDIRenderer() { };
1034 #endif
Class to define RGB colors available for display functionalities.
Definition: vpColor.h:157
static const vpColor white
Definition: vpColor.h:212
vpColorIdentifier id
Definition: vpColor.h:206
static const vpColor red
Definition: vpColor.h:217
static const vpColor darkGray
Definition: vpColor.h:215
static const vpColor cyan
Definition: vpColor.h:226
static const vpColor orange
Definition: vpColor.h:227
static const vpColor darkRed
Definition: vpColor.h:218
static const vpColor blue
Definition: vpColor.h:223
static const vpColor lightGray
Definition: vpColor.h:213
static const vpColor lightBlue
Definition: vpColor.h:222
static const vpColor darkGreen
Definition: vpColor.h:221
static const vpColor darkBlue
Definition: vpColor.h:224
static const vpColor purple
Definition: vpColor.h:228
static const vpColor lightGreen
Definition: vpColor.h:219
static const vpColor yellow
Definition: vpColor.h:225
@ id_lightBlue
Definition: vpColor.h:184
@ id_yellow
Definition: vpColor.h:190
@ id_darkGray
Definition: vpColor.h:170
@ id_green
Definition: vpColor.h:180
@ id_darkRed
Definition: vpColor.h:176
@ id_lightGray
Definition: vpColor.h:166
@ id_red
Definition: vpColor.h:174
@ id_lightRed
Definition: vpColor.h:172
@ id_white
Definition: vpColor.h:164
@ id_black
Definition: vpColor.h:162
@ id_blue
Definition: vpColor.h:186
@ id_darkGreen
Definition: vpColor.h:182
@ id_gray
Definition: vpColor.h:168
@ id_lightGreen
Definition: vpColor.h:178
@ id_purple
Definition: vpColor.h:196
@ id_orange
Definition: vpColor.h:194
@ id_cyan
Definition: vpColor.h:192
@ id_darkBlue
Definition: vpColor.h:188
@ id_unknown
Definition: vpColor.h:199
static const vpColor lightRed
Definition: vpColor.h:216
static const vpColor black
Definition: vpColor.h:211
static const vpColor green
Definition: vpColor.h:220
static const vpColor gray
Definition: vpColor.h:214
Error that can be emitted by the vpDisplay class and its derivatives.
@ depthNotSupportedError
Color depth not supported.
Class that defines a 2D point in an image. This class is useful for image processing and stores only ...
Definition: vpImagePoint.h:82
void set_j(double jj)
Definition: vpImagePoint.h:309
double get_j() const
Definition: vpImagePoint.h:125
static double distance(const vpImagePoint &iP1, const vpImagePoint &iP2)
void set_i(double ii)
Definition: vpImagePoint.h:298
double get_u() const
Definition: vpImagePoint.h:136
double get_i() const
Definition: vpImagePoint.h:114
double get_v() const
Definition: vpImagePoint.h:147
unsigned int getWidth() const
Definition: vpImage.h:242
void resize(unsigned int h, unsigned int w)
resize the image : Image initialization
Definition: vpImage.h:542
Type * bitmap
points toward the bitmap
Definition: vpImage.h:135
static double sqr(double x)
Definition: vpMath.h:203
static int round(double x)
Definition: vpMath.h:410
Definition: vpRGBa.h:65
unsigned char B
Blue component.
Definition: vpRGBa.h:169
unsigned char R
Red component.
Definition: vpRGBa.h:167
unsigned char G
Green component.
Definition: vpRGBa.h:168
@ alpha_default
Definition: vpRGBa.h:67
unsigned char A
Additionnal component.
Definition: vpRGBa.h:170
VISP_EXPORT double measureTimeMs()