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