Visual Servoing Platform  version 3.2.0 under development (2019-01-22)
vpD3DRenderer.cpp
1 /****************************************************************************
2  *
3  * ViSP, open source Visual Servoing Platform software.
4  * Copyright (C) 2005 - 2019 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 http://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  * D3D renderer for windows 32 display
33  *
34  * Authors:
35  * Bruno Renier
36  *
37  *****************************************************************************/
38 #ifndef DOXYGEN_SHOULD_SKIP_THIS
39 
40 #include <algorithm>
41 
42 #include <visp3/core/vpConfig.h>
43 #if (defined(_WIN32) & defined(VISP_HAVE_D3D9))
44 
45 #include <visp3/core/vpColor.h>
46 #include <visp3/core/vpMath.h>
47 #include <visp3/gui/vpD3DRenderer.h>
48 
49 /*
50  Be careful, when using :
51 
52  pd3dText->LockRect(0, &d3dLRect, &r, 0)
53  ...
54  pd3dText->UnlockRect(0, &d3dLRect, &r, 0)
55 
56  to write directly to the texture's surface,
57  the pointer returned in d3dLRect points to
58  the beginning of the locked suface and not
59  to the beginning of the texture's surface.
60  That's why setBufferPixel and other accesses
61  to this buffer are done in the locked surface
62  coordinates system.
63 
64  Moreover, when directly writing to a texture's surface,
65  you musn't forget to take the pitch of this texture
66  into account (see Direct3D documentation).
67 
68 */
69 
74 vpD3DRenderer::vpD3DRenderer()
75 {
76  pD3D = NULL;
77  pd3dDevice = NULL;
78  pSprite = NULL;
79  pd3dText = NULL;
80  pd3dVideoText = NULL;
81  textWidth = 0;
82 
83  // D3D palette
84  vpColor pcolor; // Predefined colors
85  pcolor = vpColor::black;
86  colors[vpColor::id_black] = D3DCOLOR_ARGB(0xFF, pcolor.R, pcolor.G, pcolor.B);
87  pcolor = vpColor::lightBlue;
88  colors[vpColor::id_lightBlue] = D3DCOLOR_ARGB(0xFF, pcolor.R, pcolor.G, pcolor.B);
89  pcolor = vpColor::blue;
90  colors[vpColor::id_blue] = D3DCOLOR_ARGB(0xFF, pcolor.R, pcolor.G, pcolor.B);
91  pcolor = vpColor::darkBlue;
92  colors[vpColor::id_darkBlue] = D3DCOLOR_ARGB(0xFF, pcolor.R, pcolor.G, pcolor.B);
93  pcolor = vpColor::cyan;
94  colors[vpColor::id_cyan] = D3DCOLOR_ARGB(0xFF, pcolor.R, pcolor.G, pcolor.B);
95  pcolor = vpColor::lightGreen;
96  colors[vpColor::id_lightGreen] = D3DCOLOR_ARGB(0xFF, pcolor.R, pcolor.G, pcolor.B);
97  pcolor = vpColor::green;
98  colors[vpColor::id_green] = D3DCOLOR_ARGB(0xFF, pcolor.R, pcolor.G, pcolor.B);
99  pcolor = vpColor::darkGreen;
100  colors[vpColor::id_darkGreen] = D3DCOLOR_ARGB(0xFF, pcolor.R, pcolor.G, pcolor.B);
101  pcolor = vpColor::lightRed;
102  colors[vpColor::id_lightRed] = D3DCOLOR_ARGB(0xFF, pcolor.R, pcolor.G, pcolor.B);
103  pcolor = vpColor::red;
104  colors[vpColor::id_red] = D3DCOLOR_ARGB(0xFF, pcolor.R, pcolor.G, pcolor.B);
105  pcolor = vpColor::darkRed;
106  colors[vpColor::id_darkRed] = D3DCOLOR_ARGB(0xFF, pcolor.R, pcolor.G, pcolor.B);
107  pcolor = vpColor::white;
108  colors[vpColor::id_white] = D3DCOLOR_ARGB(0xFF, pcolor.R, pcolor.G, pcolor.B);
109  pcolor = vpColor::lightGray;
110  colors[vpColor::id_lightGray] = D3DCOLOR_ARGB(0xFF, pcolor.R, pcolor.G, pcolor.B);
111  pcolor = vpColor::gray;
112  colors[vpColor::id_gray] = D3DCOLOR_ARGB(0xFF, pcolor.R, pcolor.G, pcolor.B);
113  pcolor = vpColor::darkGray;
114  colors[vpColor::id_darkGray] = D3DCOLOR_ARGB(0xFF, pcolor.R, pcolor.G, pcolor.B);
115  pcolor = vpColor::yellow;
116  colors[vpColor::id_yellow] = D3DCOLOR_ARGB(0xFF, pcolor.R, pcolor.G, pcolor.B);
117  pcolor = vpColor::orange;
118  colors[vpColor::id_orange] = D3DCOLOR_ARGB(0xFF, pcolor.R, pcolor.G, pcolor.B);
119  pcolor = vpColor::purple;
120  colors[vpColor::id_purple] = D3DCOLOR_ARGB(0xFF, pcolor.R, pcolor.G, pcolor.B);
121 
122  // initialize the GDI palette
123  pcolor = vpColor::black;
124  colorsGDI[vpColor::id_black] = RGB(pcolor.R, pcolor.G, pcolor.B);
125  pcolor = vpColor::lightBlue;
126  colorsGDI[vpColor::id_lightBlue] = RGB(pcolor.R, pcolor.G, pcolor.B);
127  pcolor = vpColor::blue;
128  colorsGDI[vpColor::id_blue] = RGB(pcolor.R, pcolor.G, pcolor.B);
129  pcolor = vpColor::darkBlue;
130  colorsGDI[vpColor::id_darkBlue] = RGB(pcolor.R, pcolor.G, pcolor.B);
131  pcolor = vpColor::cyan;
132  colorsGDI[vpColor::id_cyan] = RGB(pcolor.R, pcolor.G, pcolor.B);
133  pcolor = vpColor::lightGreen;
134  colorsGDI[vpColor::id_lightGreen] = RGB(pcolor.R, pcolor.G, pcolor.B);
135  pcolor = vpColor::green;
136  colorsGDI[vpColor::id_green] = RGB(pcolor.R, pcolor.G, pcolor.B);
137  pcolor = vpColor::darkGreen;
138  colorsGDI[vpColor::id_darkGreen] = RGB(pcolor.R, pcolor.G, pcolor.B);
139  pcolor = vpColor::lightRed;
140  colorsGDI[vpColor::id_lightRed] = RGB(pcolor.R, pcolor.G, pcolor.B);
141  pcolor = vpColor::red;
142  colorsGDI[vpColor::id_red] = RGB(pcolor.R, pcolor.G, pcolor.B);
143  pcolor = vpColor::darkRed;
144  colorsGDI[vpColor::id_darkRed] = RGB(pcolor.R, pcolor.G, pcolor.B);
145  pcolor = vpColor::white;
146  colorsGDI[vpColor::id_white] = RGB(pcolor.R, pcolor.G, pcolor.B);
147  pcolor = vpColor::lightGray;
148  colorsGDI[vpColor::id_lightGray] = RGB(pcolor.R, pcolor.G, pcolor.B);
149  pcolor = vpColor::gray;
150  colorsGDI[vpColor::id_gray] = RGB(pcolor.R, pcolor.G, pcolor.B);
151  pcolor = vpColor::darkGray;
152  colorsGDI[vpColor::id_darkGray] = RGB(pcolor.R, pcolor.G, pcolor.B);
153  pcolor = vpColor::yellow;
154  colorsGDI[vpColor::id_yellow] = RGB(pcolor.R, pcolor.G, pcolor.B);
155  pcolor = vpColor::orange;
156  colorsGDI[vpColor::id_orange] = RGB(pcolor.R, pcolor.G, pcolor.B);
157  pcolor = vpColor::purple;
158  colorsGDI[vpColor::id_purple] = RGB(pcolor.R, pcolor.G, pcolor.B);
159 
160  // Creates a logical font
161  hFont = CreateFont(18, 0, 0, 0, FW_NORMAL, false, false, false, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS,
162  CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | FF_DONTCARE, NULL);
163 }
164 
169 vpD3DRenderer::~vpD3DRenderer()
170 {
171  DeleteObject(hFont);
172 
173  if (pd3dDevice != NULL)
174  pd3dDevice->Release();
175  if (pD3D != NULL)
176  pD3D->Release();
177  if (pd3dText != NULL)
178  pd3dText->Release();
179  if (pd3dVideoText != NULL)
180  pd3dVideoText->Release();
181 }
182 
188 unsigned int vpD3DRenderer::supPowerOf2(unsigned int n)
189 {
190  unsigned int i = 0;
191  while (n > 1) {
192  n >>= 1;
193  i++;
194  }
195  return static_cast<unsigned int>(1 << (i + 1));
196 }
197 
205 bool vpD3DRenderer::init(HWND hwnd, unsigned int width, unsigned int height)
206 {
207  // simple stuff
208  m_rwidth = width;
209  m_rheight = height;
210  hWnd = hwnd;
211 
212  // D3D initialize
213  if (NULL == (pD3D = Direct3DCreate9(D3D_SDK_VERSION)))
214  throw vpDisplayException(vpDisplayException::notInitializedError, "Can't initialize D3D!");
215 
216  D3DDISPLAYMODE d3ddm;
217  if (FAILED(pD3D->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &d3ddm)))
218  throw vpDisplayException(vpDisplayException::notInitializedError, "Can't get the adapter's display mode!");
219 
220  D3DPRESENT_PARAMETERS d3dpp;
221  ZeroMemory(&d3dpp, sizeof(d3dpp));
222  d3dpp.BackBufferCount = 1;
223  d3dpp.Windowed = TRUE;
224  d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
225  d3dpp.BackBufferFormat = d3ddm.Format;
226 
227  // creates a d3d device
228  if (FAILED(pD3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd,
229  D3DCREATE_SOFTWARE_VERTEXPROCESSING | D3DCREATE_MULTITHREADED, &d3dpp, &pd3dDevice)))
230  throw vpDisplayException(vpDisplayException::notInitializedError, "Can't create the Direct3D device!");
231 
232  // disables scene lightning
233  pd3dDevice->SetRenderState(D3DRS_LIGHTING, FALSE);
234 
235  // inits the direct3D view (for 2D rendering)
236  initView((float)m_rwidth, (float)m_rheight);
237 
238  // computes texture size (needs to be a power-of-2 large square)
239  textWidth = supPowerOf2((m_rwidth > m_rheight) ? m_rwidth : m_rheight);
240 
241  // creates the system memory texture (the one we will directly modify)
242  // unfortunately, needs to be X8R8G8B8 in order to be able to use GDI
243  // drawing functions
244  if (D3DXCreateTexture(pd3dDevice, textWidth, textWidth, D3DX_DEFAULT, 0, D3DFMT_X8R8G8B8, D3DPOOL_SYSTEMMEM,
245  &pd3dText) != D3D_OK) {
246  throw vpDisplayException(vpDisplayException::notInitializedError, "Can't create memory texture!");
247  }
248 
249  // creates the video memory texture (the one we will display) -
250  if (D3DXCreateTexture(pd3dDevice, textWidth, textWidth, D3DX_DEFAULT, D3DUSAGE_DYNAMIC, D3DFMT_X8R8G8B8,
251  D3DPOOL_DEFAULT, &pd3dVideoText) != D3D_OK) {
252  throw vpDisplayException(vpDisplayException::notInitializedError, "Can't create video texture!");
253  }
254 
255  // creates the sprite used to render the texture
256  if (D3DXCreateSprite(pd3dDevice, &pSprite) != S_OK)
257  throw vpDisplayException(vpDisplayException::notInitializedError, "Can't create the texture's sprite!");
258 
259  return true;
260 }
261 
267 void vpD3DRenderer::initView(float WindowWidth, float WindowHeight)
268 {
269  D3DXMATRIX Ortho2D;
270  D3DXMATRIX Identity;
271 
272  D3DXMatrixOrthoLH(&Ortho2D, WindowWidth, WindowHeight, 0.0f, 1.0f);
273  D3DXMatrixIdentity(&Identity);
274 
275  if (pd3dDevice->SetTransform(D3DTS_PROJECTION, &Ortho2D) != D3D_OK ||
276  pd3dDevice->SetTransform(D3DTS_WORLD, &Identity) != D3D_OK ||
277  pd3dDevice->SetTransform(D3DTS_VIEW, &Identity) != D3D_OK)
278  throw vpDisplayException(vpDisplayException::notInitializedError, "Can't set the view!");
279 }
280 
287 void vpD3DRenderer::convert(const vpImage<vpRGBa> &I, unsigned char *imBuffer, unsigned int pitch)
288 {
289  if (m_rscale == 1) {
290  for (unsigned int i = 0; i < m_rheight; i++) {
291  unsigned int ii_ = i * pitch;
292  for (unsigned int j = 0; j < m_rwidth; j++) {
293  vpRGBa val = I[i][j];
294  unsigned int index_ = ii_ + j * 4;
295  imBuffer[index_] = val.B;
296  imBuffer[++index_] = val.G;
297  imBuffer[++index_] = val.R;
298  imBuffer[++index_] = val.A;
299  }
300  }
301  } else {
302  for (unsigned int i = 0; i < m_rheight; i++) {
303  unsigned int i_ = i * m_rscale;
304  unsigned int ii_ = i * pitch;
305  for (unsigned int j = 0; j < m_rwidth; j++) {
306  vpRGBa val = I[i_][j * m_rscale];
307  unsigned int index_ = ii_ + j * 4;
308  imBuffer[index_] = val.B;
309  imBuffer[++index_] = val.G;
310  imBuffer[++index_] = val.R;
311  imBuffer[++index_] = val.A;
312  }
313  }
314  }
315 }
316 
323 void vpD3DRenderer::convert(const vpImage<unsigned char> &I, unsigned char *imBuffer, unsigned int pitch)
324 {
325  if (m_rscale == 1) {
326  for (unsigned int i = 0; i < m_rheight; i++) {
327  unsigned int ii_ = i * pitch;
328  for (unsigned int j = 0; j < m_rwidth; j++) {
329  unsigned char val = I[i][j];
330  unsigned int index_ = ii_ + j * 4;
331  imBuffer[index_] = val;
332  imBuffer[++index_] = val;
333  imBuffer[++index_] = val;
334  imBuffer[++index_] = vpRGBa::alpha_default;
335  }
336  }
337  } else {
338  for (unsigned int i = 0; i < m_rheight; i++) {
339  unsigned int i_ = i * m_rscale;
340  unsigned int ii_ = i * pitch;
341  for (unsigned int j = 0; j < m_rwidth; j++) {
342  unsigned char val = I[i_][j * m_rscale];
343  unsigned int index_ = ii_ + j * 4;
344  imBuffer[index_] = val;
345  imBuffer[++index_] = val;
346  imBuffer[++index_] = val;
347  imBuffer[++index_] = vpRGBa::alpha_default;
348  }
349  }
350  }
351 }
352 
361 void vpD3DRenderer::convertROI(const vpImage<unsigned char> &I, unsigned char *imBuffer, unsigned int pitch, int i_min,
362  int j_min, int i_max, int j_max)
363 {
364  int h = i_max - i_min;
365  int w = j_max - j_min;
366 
367  if (m_rscale == 1) {
368  for (int i = 0; i < h; i++) {
369  unsigned int i_ = i_min + i;
370  unsigned int ii_ = i * pitch;
371  for (int j = 0; j < w; j++) {
372  unsigned char val = I[i_][j_min + j];
373  unsigned int index_ = ii_ + j * 4;
374  imBuffer[index_] = val;
375  imBuffer[++index_] = val;
376  imBuffer[++index_] = val;
377  imBuffer[++index_] = vpRGBa::alpha_default;
378  }
379  }
380  } else {
381  for (int i = 0; i < h; i++) {
382  unsigned int i_ = (i_min + i) * m_rscale;
383  unsigned int ii_ = i * pitch;
384  for (int j = 0; j < w; j++) {
385  unsigned char val = I[i_][(j_min + j) * m_rscale];
386  unsigned int index_ = ii_ + j * 4;
387  imBuffer[index_] = val;
388  imBuffer[++index_] = val;
389  imBuffer[++index_] = val;
390  imBuffer[++index_] = vpRGBa::alpha_default;
391  }
392  }
393  }
394 }
395 
404 void vpD3DRenderer::convertROI(const vpImage<vpRGBa> &I, unsigned char *imBuffer, unsigned int pitch, int i_min,
405  int j_min, int i_max, int j_max)
406 {
407  int h = i_max - i_min;
408  int w = j_max - j_min;
409 
410  if (m_rscale == 1) {
411  for (int i = 0; i < h; i++) {
412  unsigned int i_ = i_min + i;
413  unsigned int ii_ = i * pitch;
414  for (int j = 0; j < w; j++) {
415  vpRGBa val = I[i_][j_min + j];
416  unsigned int index_ = ii_ + j * 4;
417  imBuffer[index_] = val.B;
418  imBuffer[++index_] = val.G;
419  imBuffer[++index_] = val.R;
420  imBuffer[++index_] = vpRGBa::alpha_default;
421  }
422  }
423  } else {
424  for (int i = 0; i < h; i++) {
425  unsigned int i_ = (i_min + i) * m_rscale;
426  unsigned int ii_ = i * pitch;
427  for (int j = 0; j < w; j++) {
428  vpRGBa val = I[i_][(j_min + j) * m_rscale];
429  unsigned int index_ = ii_ + j * 4;
430  imBuffer[index_] = val.B;
431  imBuffer[++index_] = val.G;
432  imBuffer[++index_] = val.R;
433  imBuffer[++index_] = vpRGBa::alpha_default;
434  }
435  }
436  }
437 }
438 
443 void vpD3DRenderer::setImg(const vpImage<vpRGBa> &im)
444 {
445  // if the device has been initialized
446  if (pd3dDevice != NULL) {
447  D3DLOCKED_RECT d3dLRect;
448 
449  RECT r;
450  r.top = 0;
451  r.left = 0;
452  r.bottom = static_cast<signed long>(m_rheight);
453  r.right = static_cast<signed long>(m_rwidth);
454 
455  // locks the texture to directly access it
456  if (pd3dText->LockRect(0, &d3dLRect, &r, 0) != D3D_OK) {
457  vpCERROR << "D3D : Couldn't lock the texture!" << std::endl;
458  return;
459  }
460 
461  // gets the buffer and pitch of the texture
462  unsigned int pitch = static_cast<unsigned int>(d3dLRect.Pitch);
463  unsigned char *buf = (unsigned char *)d3dLRect.pBits;
464 
465  // fills this texture with the image data (converted to bgra)
466  convert(im, buf, pitch);
467 
468  // unlocks the texture
469  if (pd3dText->UnlockRect(0) != D3D_OK)
470  vpCERROR << "D3D : Couldn't unlock the texture!" << std::endl;
471  }
472 }
473 
478 void vpD3DRenderer::setImgROI(const vpImage<vpRGBa> &im, const vpImagePoint &iP, const unsigned int width,
479  const unsigned int height)
480 {
481  // if the device has been initialized
482  if (pd3dDevice != NULL) {
483  D3DLOCKED_RECT d3dLRect;
484 
485  int i_min = (std::max)((int)ceil(iP.get_i() / m_rscale), 0);
486  int j_min = (std::max)((int)ceil(iP.get_j() / m_rscale), 0);
487  int i_max = (std::min)((int)ceil((iP.get_i() + height) / m_rscale), (int)m_rheight);
488  int j_max = (std::min)((int)ceil((iP.get_j() + width) / m_rscale), (int)m_rwidth);
489 
490  RECT r;
491  r.top = (LONG)i_min;
492  r.left = (LONG)j_min;
493  r.bottom = (LONG)i_max;
494  r.right = (LONG)j_max;
495 
496  // locks the texture to directly access it
497  if (pd3dText->LockRect(0, &d3dLRect, &r, 0) != D3D_OK) {
498  vpCERROR << "D3D : Couldn't lock the texture!" << std::endl;
499  return;
500  }
501 
502  // gets the buffer and pitch of the texture
503  unsigned int pitch = static_cast<unsigned int>(d3dLRect.Pitch);
504  unsigned char *buf = (unsigned char *)d3dLRect.pBits;
505 
506  // fills this texture with the image data (converted to bgra)
507  convertROI(im, buf, pitch, i_min, j_min, i_max, j_max);
508 
509  // unlocks the texture
510  if (pd3dText->UnlockRect(0) != D3D_OK)
511  vpCERROR << "D3D : Couldn't unlock the texture!" << std::endl;
512  }
513 }
514 
519 void vpD3DRenderer::setImg(const vpImage<unsigned char> &im)
520 {
521  // if the device has been initialized
522  if (pd3dDevice != NULL) {
523  D3DLOCKED_RECT d3dLRect;
524 
525  RECT r;
526  r.top = 0;
527  r.left = 0;
528  r.bottom = static_cast<LONG>(m_rheight);
529  r.right = static_cast<LONG>(m_rwidth);
530 
531  // locks the texture to directly access it
532  if (pd3dText->LockRect(0, &d3dLRect, &r, 0) != D3D_OK) {
533  vpCERROR << "D3D : Couldn't lock the texture!" << std::endl;
534  return;
535  }
536 
537  // gets the buffer and pitch of the texture
538  unsigned int pitch = static_cast<unsigned int>(d3dLRect.Pitch);
539  unsigned char *buf = (unsigned char *)d3dLRect.pBits;
540 
541  // fills this texture with the image data (converted to bgra)
542  convert(im, buf, pitch);
543 
544  // unlocks the texture
545  if (pd3dText->UnlockRect(0) != D3D_OK)
546  vpCERROR << "D3D : Couldn't unlock the texture!" << std::endl;
547  }
548 }
549 
554 void vpD3DRenderer::setImgROI(const vpImage<unsigned char> &im, const vpImagePoint &iP, const unsigned int width,
555  const unsigned int height)
556 {
557  // if the device has been initialized
558  if (pd3dDevice != NULL) {
559  D3DLOCKED_RECT d3dLRect;
560 
561  int i_min = (std::max)((int)ceil(iP.get_i() / m_rscale), 0);
562  int j_min = (std::max)((int)ceil(iP.get_j() / m_rscale), 0);
563  int i_max = (std::min)((int)ceil((iP.get_i() + height) / m_rscale), (int)m_rheight);
564  int j_max = (std::min)((int)ceil((iP.get_j() + width) / m_rscale), (int)m_rwidth);
565 
566  RECT r;
567  r.top = (LONG)i_min;
568  r.left = (LONG)j_min;
569  r.bottom = (LONG)i_max;
570  r.right = (LONG)j_max;
571 
572  // locks the texture to directly access it
573  if (pd3dText->LockRect(0, &d3dLRect, &r, 0) != D3D_OK) {
574  vpCERROR << "D3D : Couldn't lock the texture!" << std::endl;
575  return;
576  }
577 
578  // gets the buffer and pitch of the texture
579  unsigned int pitch = static_cast<unsigned int>(d3dLRect.Pitch);
580  unsigned char *buf = (unsigned char *)d3dLRect.pBits;
581 
582  // fills this texture with the image data (converted to bgra)
583  convertROI(im, buf, pitch, i_min, j_min, i_max, j_max);
584 
585  // unlocks the texture
586  if (pd3dText->UnlockRect(0) != D3D_OK)
587  vpCERROR << "D3D : Couldn't unlock the texture!" << std::endl;
588  }
589 }
590 
596 bool vpD3DRenderer::render()
597 {
598  // Clears the back buffer to a blue color
599  // pd3dDevice->Clear( 0, NULL, D3DCLEAR_TARGET,
600  // D3DCOLOR_XRGB(0,0,255), 1.0f, 0 );
601 
602  // Begins the scene.
603  pd3dDevice->BeginScene();
604 
605  // Texture rectangle to display
606  RECT r;
607  r.top = 0;
608  r.left = 0;
609  r.bottom = static_cast<LONG>(m_rheight);
610  r.right = static_cast<LONG>(m_rwidth);
611 
612  // Updates the video memory texture with the content of the system
613  // memory texture
614  pd3dDevice->UpdateTexture(pd3dText, pd3dVideoText);
615 
616 // Displays this texture as a sprite
617 
618 #if (D3DX_SDK_VERSION <= 9)
619  pSprite->Begin(); //
620  pSprite->Draw(pd3dVideoText, &r, NULL, NULL, NULL, NULL, 0xFFFFFFFF);
621 #else
622  pSprite->Begin(0);
623  pSprite->Draw(pd3dVideoText, &r, NULL, NULL, 0xFFFFFFFF);
624 #endif
625  pSprite->End();
626 
627  // Ends the scene.
628  pd3dDevice->EndScene();
629  // Presents the backbuffer
630  pd3dDevice->Present(NULL, NULL, NULL, NULL);
631 
632  return true;
633 }
634 
641 void vpD3DRenderer::setPixel(const vpImagePoint &iP, const vpColor &color)
642 {
643  vpImagePoint iPscaled = iP / m_rscale;
644  if (iPscaled.get_i() < 0 || iPscaled.get_j() < 0 || iPscaled.get_i() >= (int)m_rheight ||
645  iPscaled.get_j() >= (int)m_rwidth) {
646  return;
647  }
648 
649  // if the device has been initialized
650  if (pd3dDevice != NULL) {
651  D3DLOCKED_RECT d3dLRect;
652 
653  RECT r;
654 
655  r.top = (LONG)iPscaled.get_i();
656  r.left = (LONG)iPscaled.get_j();
657  r.bottom = (LONG)iPscaled.get_i() + 1;
658  r.right = (LONG)iPscaled.get_j() + 1;
659 
660  // locks the texture to directly access it
661  if (pd3dText->LockRect(0, &d3dLRect, &r, 0) != D3D_OK) {
662  vpCERROR << "D3D : Couldn't lock the texture!" << std::endl;
663  return;
664  }
665 
666  // gets the buffer and pitch of the texture
667  unsigned int pitch = static_cast<unsigned int>(d3dLRect.Pitch);
668  unsigned char *buf = (unsigned char *)d3dLRect.pBits;
669 
670  // the coordinates are in the locked area base
671  setBufferPixel(buf, pitch, 0, 0, color);
672 
673  // unlocks the texture
674  if (pd3dText->UnlockRect(0) != D3D_OK)
675  vpCERROR << "D3D : Couldn't unlock the texture!" << std::endl;
676  }
677 }
678 
686 void vpD3DRenderer::drawLine(const vpImagePoint &ip1, const vpImagePoint &ip2, const vpColor &color,
687  unsigned int thickness, int style)
688 {
689  // if the device has been initialized
690  if (pd3dDevice != NULL) {
691  // Will contain the texture's surface drawing context
692  HDC hDCMem;
693 
694  // The texture's surface
695  IDirect3DSurface9 *pd3dSurf;
696  pd3dText->GetSurfaceLevel(0, &pd3dSurf);
697 
698  // We get its DC
699  pd3dSurf->GetDC(&hDCMem);
700 
701  // create the pen
702  HPEN hPen;
703  if (color.id < vpColor::id_unknown)
704  hPen = CreatePen(style, static_cast<int>(thickness), colorsGDI[color.id]);
705  else {
706  COLORREF gdicolor = RGB(color.R, color.G, color.B);
707  hPen = CreatePen(style, static_cast<int>(thickness), gdicolor);
708  }
709 
710  // we don't use the bkColor
711  SetBkMode(hDCMem, TRANSPARENT);
712 
713  // select the pen
714  SelectObject(hDCMem, hPen);
715 
716  // Warning: When thickness > 1 and pen style is PS_DASHDOT, the drawing
717  // displays a solid line That's why in that case we implement the dashdot
718  // line manually drawing multiple small lines
719  if (thickness != 1 && style != PS_SOLID) {
720  double size = 10. * m_rscale;
721  double length = sqrt(vpMath::sqr(ip2.get_i() - ip1.get_i()) + vpMath::sqr(ip2.get_j() - ip1.get_j()));
722  double deltaj = size / length * (ip2.get_j() - ip1.get_j());
723  double deltai = size / length * (ip2.get_i() - ip1.get_i());
724  double slope = (ip2.get_i() - ip1.get_i()) / (ip2.get_j() - ip1.get_j());
725  double orig = ip1.get_i() - slope * ip1.get_j();
726  for (unsigned int j = (unsigned int)ip1.get_j(); j < ip2.get_j(); j += (unsigned int)(2 * deltaj)) {
727  double i = slope * j + orig;
728  // move to the starting point
729  MoveToEx(hDCMem, vpMath::round(j / m_rscale), vpMath::round(i / m_rscale), NULL);
730  // Draw the line
731  LineTo(hDCMem, vpMath::round((j + deltaj) / m_rscale), vpMath::round((i + deltai) / m_rscale));
732  }
733  } else {
734  // move to the starting point
735  MoveToEx(hDCMem, vpMath::round(ip1.get_u() / m_rscale), vpMath::round(ip1.get_v() / m_rscale), NULL);
736  // Draw the line
737  LineTo(hDCMem, vpMath::round(ip2.get_u() / m_rscale), vpMath::round(ip2.get_v() / m_rscale));
738  }
739 
740  // Releases the DC
741  pd3dSurf->ReleaseDC(hDCMem);
742  // Releases the surface's interface
743  pd3dSurf->Release();
744  // Deletes additional objects
745  DeleteObject(hPen);
746  }
747 }
748 
758 void vpD3DRenderer::drawRect(const vpImagePoint &topLeft, unsigned int width, unsigned int height, const vpColor &color,
759  bool fill, unsigned int thickness)
760 {
761  // if the device has been initialized
762  if (pd3dDevice != NULL) {
763  if (fill == false) {
764  drawLine(topLeft, topLeft + vpImagePoint(0, width), color, thickness);
765  drawLine(topLeft + vpImagePoint(0, width), topLeft + vpImagePoint(height, width), color, thickness);
766  drawLine(topLeft + vpImagePoint(height, width), topLeft + vpImagePoint(height, 0), color, thickness);
767  drawLine(topLeft + vpImagePoint(height, 0), topLeft, color, thickness);
768  } else {
769  vpImagePoint topLeftScaled = topLeft / m_rscale;
770  unsigned int widthScaled = width / m_rscale;
771  unsigned int heightScaled = height / m_rscale;
772 
773  if (topLeftScaled.get_i() > (int)m_rheight - 1 || topLeftScaled.get_j() > (int)m_rwidth - 1 ||
774  topLeftScaled.get_i() + height < 0 || topLeftScaled.get_j() + width < 0) {
775  // vpCERROR<<"Invalid parameters!"<<std::endl;
776  return;
777  }
778 
779  D3DLOCKED_RECT d3dLRect;
780 
781  RECT r;
782  r.top = (LONG)((topLeftScaled.get_i() > 0) ? topLeftScaled.get_i() : 0);
783  r.left = (LONG)((topLeftScaled.get_j() > 0) ? topLeftScaled.get_j() : 0);
784  r.bottom = (LONG)((topLeftScaled.get_i() + heightScaled < (int)m_rheight) ? topLeftScaled.get_i() + heightScaled
785  : m_rheight - 1);
786  r.right = (LONG)((topLeftScaled.get_j() + widthScaled < (int)m_rwidth) ? topLeftScaled.get_j() + widthScaled
787  : m_rwidth - 1);
788 
789  /* unsigned */ int rectW = r.right - r.left;
790  /* unsigned */ int rectH = r.bottom - r.top;
791 
792  // locks the texture to directly access it
793  if (pd3dText->LockRect(0, &d3dLRect, &r, 0) != D3D_OK) {
794  vpCERROR << "D3D : Couldn't lock the texture!" << std::endl;
795  return;
796  }
797 
798  // gets the buffer and pitch of the texture
799  unsigned int pitch = static_cast<unsigned int>(d3dLRect.Pitch);
800  unsigned char *buf = (unsigned char *)d3dLRect.pBits;
801 
802  if (topLeftScaled.get_i() >= 0 && topLeftScaled.get_j() + widthScaled < m_rwidth &&
803  topLeftScaled.get_i() + heightScaled < m_rheight && topLeftScaled.get_j() >= 0) {
804  for (int x = 0; x < rectW; x++) {
805  for (int y = 0; y < rectH; y++)
806  setBufferPixel(buf, pitch, x, y, color);
807  }
808  }
809 
810  // unlocks the texture
811  if (pd3dText->UnlockRect(0) != D3D_OK)
812  vpCERROR << "D3D : Couldn't unlock the texture!" << std::endl;
813  }
814  }
815 }
816 
821 void vpD3DRenderer::clear(const vpColor &color)
822 {
823  // if the device has been initialized
824  if (pd3dDevice != NULL) {
825  D3DLOCKED_RECT d3dLRect;
826 
827  RECT r;
828  r.top = 0;
829  r.left = 0;
830  r.bottom = static_cast<LONG>(m_rheight);
831  r.right = static_cast<LONG>(m_rwidth);
832 
833  // locks the texture to directly access it
834  if (pd3dText->LockRect(0, &d3dLRect, &r, 0) != D3D_OK) {
835  vpCERROR << "D3D : Couldn't lock the texture!" << std::endl;
836  return;
837  }
838 
839  // gets the buffer and pitch of the texture
840  unsigned int pitch = static_cast<unsigned int>(d3dLRect.Pitch);
841  long *buf = (long *)(d3dLRect.pBits);
842 
843  unsigned long c;
844  if (color.id < vpColor::id_unknown)
845  c = colors[color.id];
846  else {
847  c = D3DCOLOR_ARGB(0xFF, color.R, color.G, color.B);
848  }
849  long *end = (long *)(buf + (pitch * m_rheight));
850 
851  // fills the whole image
852  while (buf < end)
853  *buf++ = static_cast<long>(c);
854 
855  // unlocks the texture
856  if (pd3dText->UnlockRect(0) != D3D_OK)
857  vpCERROR << "D3D : Couldn't unlock the texture!" << std::endl;
858  }
859 }
860 
861 // writes current circle pixels using symetry to reduce the algorithm's
862 // complexity
863 void vpD3DRenderer::subDrawCircle(int i, int j, int x, int y, vpColor col, unsigned char *buf, unsigned int pitch,
864  unsigned int maxX, unsigned int maxY)
865 {
866  if (x == 0) {
867  setBufferPixel(buf, pitch, i, j + y, col, maxX, maxY);
868  setBufferPixel(buf, pitch, i, j - y, col, maxX, maxY);
869  setBufferPixel(buf, pitch, i + y, j, col, maxX, maxY);
870  setBufferPixel(buf, pitch, i - y, j, col, maxX, maxY);
871  } else if (x == y) {
872  setBufferPixel(buf, pitch, i + x, j + y, col, maxX, maxY);
873  setBufferPixel(buf, pitch, i - x, j + y, col, maxX, maxY);
874  setBufferPixel(buf, pitch, i + x, j - y, col, maxX, maxY);
875  setBufferPixel(buf, pitch, i - x, j - y, col, maxX, maxY);
876  } else if (x < y) {
877  setBufferPixel(buf, pitch, i + x, j + y, col, maxX, maxY);
878  setBufferPixel(buf, pitch, i - x, j + y, col, maxX, maxY);
879  setBufferPixel(buf, pitch, i + x, j - y, col, maxX, maxY);
880  setBufferPixel(buf, pitch, i - x, j - y, col, maxX, maxY);
881  setBufferPixel(buf, pitch, i + y, j + x, col, maxX, maxY);
882  setBufferPixel(buf, pitch, i - y, j + x, col, maxX, maxY);
883  setBufferPixel(buf, pitch, i + y, j - x, col, maxX, maxY);
884  setBufferPixel(buf, pitch, i - y, j - x, col, maxX, maxY);
885  }
886 }
887 
894 void vpD3DRenderer::drawCircle(const vpImagePoint &center, unsigned int radius, const vpColor &color, bool /*fill*/,
895  unsigned int /*thickness*/)
896 {
897  unsigned int radiusScaled = radius / m_rscale;
898  vpImagePoint centerScaled = center / m_rscale;
899  if (radiusScaled < 1 || vpMath::round(centerScaled.get_i() + radiusScaled) < 0 ||
900  vpMath::round(centerScaled.get_i() - radiusScaled) > (int)m_rheight ||
901  vpMath::round(centerScaled.get_j() + radiusScaled) < 0 ||
902  vpMath::round(centerScaled.get_j() - radiusScaled) > (int)m_rwidth)
903  return;
904 
905  // if the device has been initialized
906  if (pd3dDevice != NULL) {
907  D3DLOCKED_RECT d3dLRect;
908 
909  RECT rec;
910  int radiusScaled_ = static_cast<int>(radiusScaled);
911  int rleft = (vpMath::round(centerScaled.get_j() - radiusScaled_) > 0)
912  ? vpMath::round(centerScaled.get_j()) - radiusScaled_
913  : 0;
914  int rtop = (vpMath::round(centerScaled.get_i() - radiusScaled_) > 0)
915  ? vpMath::round(centerScaled.get_i()) - radiusScaled_
916  : 0;
917 
918  rec.top = rtop;
919  rec.left = rleft;
920  rec.bottom = (LONG)((vpMath::round(centerScaled.get_i() + radiusScaled_) < (int)m_rheight)
921  ? centerScaled.get_i() + radiusScaled_
922  : m_rheight - 1);
923  rec.right = (LONG)((vpMath::round(centerScaled.get_j() + radiusScaled_) < (int)m_rwidth)
924  ? centerScaled.get_j() + radiusScaled_
925  : m_rwidth - 1);
926 
927  // used as maxX and maxY for setBufferPixel
928  unsigned int rectW = static_cast<unsigned int>(rec.right - rleft);
929  unsigned int rectH = static_cast<unsigned int>(rec.bottom - rtop);
930 
931  // locks the texture to directly access it
932  if (pd3dText->LockRect(0, &d3dLRect, &rec, 0) != D3D_OK) {
933  vpCERROR << "D3D : Couldn't lock the texture!" << std::endl;
934  return;
935  }
936 
937  // gets the buffer and pitch of the texture
938  unsigned int pitch = static_cast<unsigned int>(d3dLRect.Pitch);
939  unsigned char *buf = (unsigned char *)d3dLRect.pBits;
940 
941  // Bresenham 's circle algorithm
942 
943  int x = 0;
944  int y = static_cast<int>(radiusScaled);
945  int p = (3 - (y << 1));
946 
947  vpImagePoint ip;
948  ip.set_i(centerScaled.get_i() - rtop);
949  ip.set_j(centerScaled.get_j() - rleft);
950 
951  subDrawCircle(vpMath::round(ip.get_i()), vpMath::round(ip.get_j()), x, y, color, buf, pitch, rectW, rectH);
952  while (x < y) {
953  x++;
954  if (p < 0) {
955  p += ((x << 1) + 1) << 1;
956  } else {
957  y--;
958  p += (((x - y) << 1) + 1) << 1;
959  }
960  subDrawCircle(vpMath::round(ip.get_i()), vpMath::round(ip.get_j()), x, y, color, buf, pitch, rectW, rectH);
961  }
962 
963  // unlocks the texture
964  if (pd3dText->UnlockRect(0) != D3D_OK)
965  vpCERROR << "D3D : Couldn't unlock the texture!" << std::endl;
966  }
967 }
968 
975 void vpD3DRenderer::drawText(const vpImagePoint &ip, const char *text, const vpColor &color)
976 {
977  // Will contain the texture's surface drawing context
978  HDC hDCMem;
979 
980  // The texture's surface
981  IDirect3DSurface9 *pd3dSurf;
982  pd3dText->GetSurfaceLevel(0, &pd3dSurf);
983 
984  // We get its DC
985  pd3dSurf->GetDC(&hDCMem);
986 
987  // Select the font
988  SelectObject(hDCMem, hFont);
989 
990  // set the text color
991  if (color.id < vpColor::id_unknown)
992  SetTextColor(hDCMem, colorsGDI[color.id]);
993  else {
994  COLORREF gdicolor = RGB(color.R, color.G, color.B);
995  SetTextColor(hDCMem, gdicolor);
996  }
997 
998  // we don't use the bkColor
999  SetBkMode(hDCMem, TRANSPARENT);
1000 
1001  SIZE size;
1002  int length = (int)strlen(text);
1003 
1004  // get the displayed string dimensions
1005  GetTextExtentPoint32(hDCMem, text, length, &size);
1006 
1007  // displays the string
1008  TextOut(hDCMem, vpMath::round(ip.get_u() / m_rscale), vpMath::round(ip.get_v() / m_rscale), text, length);
1009 
1010  // Releases the DC
1011  pd3dSurf->ReleaseDC(hDCMem);
1012  // Releases the surface's interface
1013  pd3dSurf->Release();
1014  // Deletes additional objects
1015  DeleteObject(hFont);
1016 }
1017 
1025 void vpD3DRenderer::drawCross(const vpImagePoint &ip, unsigned int size, const vpColor &color, unsigned int thickness)
1026 {
1027  double i = ip.get_i();
1028  double j = ip.get_j();
1029  vpImagePoint ip1, ip2;
1030 
1031  ip1.set_i(i - size / 2);
1032  ip1.set_j(j);
1033  ip2.set_i(i + size / 2);
1034  ip2.set_j(j);
1035  drawLine(ip1, ip2, color, thickness);
1036 
1037  ip1.set_i(i);
1038  ip1.set_j(j - size / 2);
1039  ip2.set_i(i);
1040  ip2.set_j(j + size / 2);
1041 
1042  drawLine(ip1, ip2, color, thickness);
1043 }
1044 
1052 void vpD3DRenderer::drawArrow(const vpImagePoint &ip1, const vpImagePoint &ip2, const vpColor &color, unsigned int w,
1053  unsigned int h, unsigned int thickness)
1054 {
1055  double a = ip2.get_i() - ip1.get_i();
1056  double b = ip2.get_j() - ip1.get_j();
1057  double lg = sqrt(vpMath::sqr(a) + vpMath::sqr(b));
1058 
1059  // if ( ( a==0 ) && ( b==0 ) )
1060  if ((std::fabs(a) <= std::numeric_limits<double>::epsilon()) &&
1061  (std::fabs(b) <= std::numeric_limits<double>::epsilon())) {
1062  // DisplayCrossLarge(i1,j1,3,col) ;
1063  } else {
1064  a /= lg;
1065  b /= lg;
1066 
1067  vpImagePoint ip3;
1068  ip3.set_i(ip2.get_i() - w * a);
1069  ip3.set_j(ip2.get_j() - w * b);
1070 
1071  vpImagePoint ip4;
1072  ip4.set_i(ip3.get_i() - b * h);
1073  ip4.set_j(ip3.get_j() + a * h);
1074 
1075  if (lg > 2 * vpImagePoint::distance(ip2, ip4))
1076  drawLine(ip2, ip4, color, thickness);
1077 
1078  ip4.set_i(ip3.get_i() + b * h);
1079  ip4.set_j(ip3.get_j() - a * h);
1080 
1081  if (lg > 2 * vpImagePoint::distance(ip2, ip4))
1082  drawLine(ip2, ip4, color, thickness);
1083 
1084  drawLine(ip1, ip2, color, thickness);
1085  }
1086 }
1087 
1094 void TextureToRGBa(vpImage<vpRGBa> &I, unsigned char *imBuffer, unsigned int pitch)
1095 {
1096  unsigned int j = I.getWidth();
1097 
1098  unsigned int k = 0;
1099  for (unsigned int i = 0; i < I.getHeight() * I.getWidth(); i++) {
1100  // go to the next line
1101  if (j == 0) {
1102  k += pitch - (I.getWidth() * 4);
1103  j = I.getWidth();
1104  }
1105 
1106  // simple conversion from bgra to rgba
1107  I.bitmap[i].B = imBuffer[k + 0];
1108  I.bitmap[i].G = imBuffer[k + 1];
1109  I.bitmap[i].R = imBuffer[k + 2];
1110  I.bitmap[i].A = imBuffer[k + 3];
1111 
1112  k += 4;
1113  j--;
1114  }
1115 }
1116 
1121 void vpD3DRenderer::getImage(vpImage<vpRGBa> &I)
1122 {
1123  // if the device has been initialized
1124  if (pd3dDevice != NULL) {
1125 
1126  // resize the destination image as needed
1127  I.resize(m_rheight, m_rwidth);
1128 
1129  D3DLOCKED_RECT d3dLRect;
1130 
1131  RECT r;
1132  r.top = 0;
1133  r.left = 0;
1134  r.bottom = static_cast<LONG>(m_rheight);
1135  r.right = static_cast<LONG>(m_rwidth);
1136 
1137  // locks the whole texture to directly access it
1138  if (pd3dText->LockRect(0, &d3dLRect, &r, 0) != D3D_OK) {
1139  vpCERROR << "D3D : Couldn't lock the texture!" << std::endl;
1140  return;
1141  }
1142 
1143  // gets the buffer and pitch of the texture
1144  unsigned int pitch = static_cast<unsigned int>(d3dLRect.Pitch);
1145  unsigned char *buf = (unsigned char *)d3dLRect.pBits;
1146 
1147  // fills this image with the texture's data
1148  TextureToRGBa(I, buf, pitch);
1149 
1150  // unlocks the texture
1151  if (pd3dText->UnlockRect(0) != D3D_OK)
1152  vpCERROR << "D3D : Couldn't unlock the texture!" << std::endl;
1153  }
1154 }
1155 
1156 #elif !defined(VISP_BUILD_SHARED_LIBS)
1157 // Work arround to avoid warning: libvisp_core.a(vpD3DRenderer.cpp.o) has no
1158 // symbols
1159 void dummy_vpD3DRenderer(){};
1160 #endif
1161 #endif
double get_v() const
Definition: vpImagePoint.h:274
double get_i() const
Definition: vpImagePoint.h:204
unsigned int getWidth() const
Definition: vpImage.h:239
#define vpCERROR
Definition: vpDebug.h:365
unsigned char B
Blue component.
Definition: vpRGBa.h:150
Type * bitmap
points toward the bitmap
Definition: vpImage.h:133
static const vpColor black
Definition: vpColor.h:174
static const vpColor darkRed
Definition: vpColor.h:181
Class to define colors available for display functionnalities.
Definition: vpColor.h:120
double get_u() const
Definition: vpImagePoint.h:263
static const vpColor lightGray
Definition: vpColor.h:176
static const vpColor darkBlue
Definition: vpColor.h:187
unsigned char G
Green component.
Definition: vpRGBa.h:149
static const vpColor green
Definition: vpColor.h:183
static int round(const double x)
Definition: vpMath.h:235
double get_j() const
Definition: vpImagePoint.h:215
static const vpColor lightRed
Definition: vpColor.h:179
Definition: vpRGBa.h:66
static const vpColor red
Definition: vpColor.h:180
static const vpColor orange
Definition: vpColor.h:190
vpColorIdentifier id
Definition: vpColor.h:169
void set_i(const double ii)
Definition: vpImagePoint.h:167
static const vpColor cyan
Definition: vpColor.h:189
static const vpColor lightGreen
Definition: vpColor.h:182
static double sqr(double x)
Definition: vpMath.h:108
unsigned char A
Additionnal component.
Definition: vpRGBa.h:151
void resize(const unsigned int h, const unsigned int w)
resize the image : Image initialization
Definition: vpImage.h:866
static const vpColor gray
Definition: vpColor.h:177
static const vpColor darkGray
Definition: vpColor.h:178
void set_j(const double jj)
Definition: vpImagePoint.h:178
Error that can be emited by the vpDisplay class and its derivates.
unsigned char R
Red component.
Definition: vpRGBa.h:148
unsigned int getHeight() const
Definition: vpImage.h:178
static const vpColor darkGreen
Definition: vpColor.h:184
Class that defines a 2D point in an image. This class is useful for image processing and stores only ...
Definition: vpImagePoint.h:88
static const vpColor yellow
Definition: vpColor.h:188
static const vpColor lightBlue
Definition: vpColor.h:185
static const vpColor purple
Definition: vpColor.h:191
static const vpColor white
Definition: vpColor.h:175
static double distance(const vpImagePoint &iP1, const vpImagePoint &iP2)
Definition: vpImagePoint.h:285
static const vpColor blue
Definition: vpColor.h:186