This article was contributed by [Pierre Alliez and Magali Mazière].
Environment: [VC6 SP4, NT4 SP3]
This article allows ones to :
We assume that our aim is to display the RGB color distribution of the following image (Fig.1) in an interactive way. In particular, it would be nice to display the RGB cube and the image color cloud.
Fig. 1. Our goal is to display the RGB color distribution of this 24 bits BMP color image in an intuitive way. OpenGL will help us.
Figure 2 illustrates two viewpoints of the color distribution in a dialog. The viewpoint is modifed using the left mouse button that implements the x/y rotation, the dialog may be resized and a popup menu allows us to load and view the current image (Fig. 3).
Fig. 2. Two examples of color distribution in the RGB cube seen under two distinct viewpoints. In particular, the red cloud is linked to the nose of the baboon seen in Fig. 1. Notice that the lines are antialiased, each saturated color is drawn with a sphere , and the cloud is painted using the GL_POINTS primitive.
Fig. 3. A popup menu allows to load a 24 bits RGB image, change the OpenGL clear color (recalled back color), and view the current image in a dialog window.
INITIALIZATION OF THE RENDERING ENGINE
Following lines make the rendering engine to be initialized in a dialog.
//********************************* // OnCreate //********************************* int CColorDlg::OnCreate(LPCREATESTRUCT lpCreateStruct) { // Init OpenGL engine HWND hWnd = GetSafeHwnd(); HDC hDC = ::GetDC(hWnd); if(SetWindowPixelFormat(hDC)==FALSE) return 0; if(CreateViewGLContext(hDC)==FALSE) return 0; // The lines are antialiased glEnable(GL_LINE_SMOOTH); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA); glHint(GL_LINE_SMOOTH_HINT,GL_NICEST); glLineWidth(1.5); // required glPointSize(1.0); glPolygonMode(GL_FRONT,GL_LINE); glPolygonMode(GL_BACK,GL_LINE); glShadeModel(GL_SMOOTH); // Precalculate display lists BuildListCube(); BuildListCloud(); return 0; }
//********************************************** // OpenGL //********************************************** BOOL CColorDlg::SetWindowPixelFormat(HDC hDC) { PIXELFORMATDESCRIPTOR pixelDesc; pixelDesc.nSize = sizeof(PIXELFORMATDESCRIPTOR); pixelDesc.nVersion = 1; pixelDesc.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER | PFD_STEREO_DONTCARE; pixelDesc.iPixelType = PFD_TYPE_RGBA; pixelDesc.cColorBits = 32; pixelDesc.cRedBits = 8; pixelDesc.cRedShift = 16; pixelDesc.cGreenBits = 8; pixelDesc.cGreenShift = 8; pixelDesc.cBlueBits = 8; pixelDesc.cBlueShift = 0; pixelDesc.cAlphaBits = 0; pixelDesc.cAlphaShift = 0; pixelDesc.cAccumBits = 64; pixelDesc.cAccumRedBits = 16; pixelDesc.cAccumGreenBits = 16; pixelDesc.cAccumBlueBits = 16; pixelDesc.cAccumAlphaBits = 0; pixelDesc.cDepthBits = 32; pixelDesc.cStencilBits = 8; pixelDesc.cAuxBuffers = 0; pixelDesc.iLayerType = PFD_MAIN_PLANE; pixelDesc.bReserved = 0; pixelDesc.dwLayerMask = 0; pixelDesc.dwVisibleMask = 0; pixelDesc.dwDamageMask = 0; m_GLPixelIndex = ChoosePixelFormat(hDC,&pixelDesc); if(m_GLPixelIndex==0) // Choose default { m_GLPixelIndex = 1; if(!DescribePixelFormat(hDC,m_GLPixelIndex, sizeof(PIXELFORMATDESCRIPTOR),&pixelDesc)) return FALSE; } if(!SetPixelFormat(hDC,m_GLPixelIndex,&pixelDesc)) return FALSE; return TRUE; } //********************************* // CreateViewGLContext //********************************* BOOL CColorDlg::CreateViewGLContext(HDC hDC) { m_hGLContext = wglCreateContext(hDC); if(m_hGLContext==NULL) return FALSE; if(wglMakeCurrent(hDC,m_hGLContext)==FALSE) return FALSE; return TRUE; }
//********************************* // OnDestroy //********************************* void CColorDlg::OnDestroy() { CDialog::OnDestroy(); if(wglGetCurrentContext() != NULL) wglMakeCurrent(NULL,NULL); if(m_hGLContext != NULL) { wglDeleteContext(m_hGLContext); m_hGLContext = NULL; } glDeleteLists(1,2); }
RENDERING
The RGB cube and the cloud are rendered in two distinct display lists. The cube is a fixed part, while the color cloud is rebuild each time a new image is loaded.
//********************************* // OnPaint //********************************* void CColorDlg::OnPaint() { // ** Draw scene ** CPaintDC dc(this); RenderScene(); SwapBuffers(dc.m_ps.hdc); // double buffer }
//********************************* // RenderScene //********************************* void CColorDlg::RenderScene() { ::glClearColor((float)GetRValue(m_BackColor)/255.0f, (float)GetGValue(m_BackColor)/255.0f, (float)GetBValue(m_BackColor)/255.0f,1.0f); ::glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); ::glPushMatrix(); ::glTranslated(0.0,0.0,-8.0); ::glRotated(m_xRotate, 1.0, 0.0, 0.0); ::glRotated(m_yRotate, 0.0, 1.0, 0.0); ::glCallList(1); // cube ::glCallList(2); // clouds ::glPopMatrix(); } //*********************************************** // BuildList //*********************************************** void CColorDlg::BuildListCube(BOOL list) { GLUquadricObj* pQuadric = gluNewQuadric(); if(list) ::glNewList(1,GL_COMPILE_AND_EXECUTE); float x = m_Size; // RGB cube glBegin(GL_LINE_LOOP); glColor3ub(0,0,0); glVertex3d(-x,-x,-x); glColor3ub(255,0,0); glVertex3d(x,-x,-x); glColor3ub(255,255,0); glVertex3d(x,x,-x); glColor3ub(0,255,0); glVertex3d(-x,x,-x); glEnd(); glBegin(GL_LINE_LOOP); glColor3ub(0,0,255); glVertex3d(-x,-x,x); glColor3ub(255,0,255); glVertex3d(x,-x,x); glColor3ub(255,255,255); glVertex3d(x,x,x); glColor3ub(0,255,255); glVertex3d(-x,x,x); glEnd(); glBegin(GL_LINES); glColor3ub(0,0,0); glVertex3d(-x,-x,-x); glColor3ub(0,0,255); glVertex3d(-x,-x,x); glColor3ub(255,0,0); glVertex3d(x,-x,-x); glColor3ub(255,0,255); glVertex3d(x,-x,x); glColor3ub(255,255,0); glVertex3d(x,x,-x); glColor3ub(255,255,255); glVertex3d(x,x,x); glColor3ub(0,255,0); glVertex3d(-x,x,-x); glColor3ub(0,255,255); glVertex3d(-x,x,x); glEnd(); // Spheres glPolygonMode(GL_FRONT,GL_FILL); glPolygonMode(GL_BACK,GL_FILL); float radius = 0.1f; glPushMatrix(); glTranslated(-m_Size,-m_Size,-m_Size); glColor3ub(0,0,0); gluSphere(pQuadric,radius,12,12); glPopMatrix(); glPushMatrix(); glTranslated(m_Size,-m_Size,-m_Size); glColor3ub(255,0,0); gluSphere(pQuadric,radius,12,12); glPopMatrix(); glPushMatrix(); glTranslated(-m_Size,m_Size,-m_Size); glColor3ub(0,255,0); gluSphere(pQuadric,radius,12,12); glPopMatrix(); glPushMatrix(); glTranslated(-m_Size,-m_Size,m_Size); glColor3ub(0,0,255); gluSphere(pQuadric,radius,12,12); glPopMatrix(); glPushMatrix(); glTranslated(m_Size,m_Size,-m_Size); glColor3ub(255,255,0); gluSphere(pQuadric,radius,12,12); glPopMatrix(); glPushMatrix(); glTranslated(-m_Size,m_Size,m_Size); glColor3ub(0,255,255); gluSphere(pQuadric,radius,12,12); glPopMatrix(); glPushMatrix(); glTranslated(m_Size,-m_Size,m_Size); glColor3ub(255,0,255); gluSphere(pQuadric,radius,12,12); glPopMatrix(); glPushMatrix(); glTranslated(m_Size,m_Size,m_Size); glColor3ub(255,255,255); gluSphere(pQuadric,radius,12,12); glPopMatrix(); if(list) ::glEndList(); gluDeleteQuadric(pQuadric); } //*********************************************** // BuildListCloud //*********************************************** void CColorDlg::BuildListCloud() { TRACE("Build list cloud..."); // Image area unsigned int area = m_Image.GetWidth()*m_Image.GetHeight(); TRACE("area : %d...",area); // Need valid image (24 bits) if(area == 0 || m_Image.GetDepth() != 24) return; // Maximum -> area distinct colors / 2^24 // This table is memory expansive, but short lived TRACE("alloc..."); unsigned char *pTable = new unsigned char[16777216]; memset(pTable,0,16777216); // init 0 // Image data unsigned int wb32 = m_Image.GetWidthByte32(); unsigned char *pData = m_Image.GetData(); // Build a new list TRACE("build list..."); int nb = 0; ::glNewList(2,GL_COMPILE_AND_EXECUTE); glBegin(GL_POINTS); float tmp = 2.0f/255.0f*m_Size; for(unsigned int j=0;j<m_Image.GetHeight();j++) for(unsigned int i=0;i<m_Image.GetWidth();i++) { unsigned char b = pData[wb32*j+3*i]; unsigned char g = pData[wb32*j+3*i+1]; unsigned char r = pData[wb32*j+3*i+2]; if(!pTable[b*65536+g*256+r]) { glColor3ub(r,g,b); float x = -m_Size+(float)r*tmp; float y = -m_Size+(float)g*tmp; float z = -m_Size+(float)b*tmp; glVertex3d(x,y,z); pTable[b*65536+g*256+r] = 1; nb++; } } glEnd(); ::glEndList(); TRACE("%d points...",nb); TRACE("cleanup..."); delete [] pTable; TRACE("ok\n"); }
Download demo project - [530 KB]
Download source - [10 KB]
Date Posted: [10/17/2000]