Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / desktop / MFC

Masking Texture in OpenGL using Class CImage

3.64/5 (13 votes)
6 May 2022CPOL4 min read 18.4K   1.1K  
Masking Texture in OpenGL from Bitmap Image File using Class CImage (just call the File Name)
My previous article demonstrated useful texture handling techology by calling name of image file for texture mapping without many stages with conditions implementation required. The follow-up is an image masking application provided.

Image 1

Introduction

In my previous article, Image Mapping in OpenGL Using Class CImage, I demonstrated some useful texture handling techology by just calling the name of the image file for texture mapping without a lot of stages with the conditions implementation required. The straight follow-up should be the image masking application provided also arranged as just calling the name of the image bitmap file.

Background

The procedure of masking texture implementation in OpenGL just calling the name of the image bitmap file has been developed with the application of the class CImage. The demo project MaskTexture has been derived from my previous article project ImgTexture derived from the standard MFC Cube Sample project renamed with the CodeProject ProjRename program.

Using the Code

The global procedure LoadMaskTexture(LPCTSTR fName) and related procedure MakeBWMask(CImage * pImg, CDC *pDC, int w, int h, COLORREF eColor, int BPP) have been added to the GlobGLTexture.cpp file from the previous article project ImgTexture. For using the technology provided, you may insert the GlobGLTexture.cpp file into your own project with the menu PROJECT->Add Existing Item... command.

The procedure for masking texture loading:

C++
 DWORD  LoadMaskTexture(LPCTSTR fName)      //Load Mask texture from Bitmap Image file
{
	CImage img;
	HRESULT hResult = img.Load(fName);      //Standard procedure of CImage loading
	if (FAILED(hResult)) {
		_TCHAR fmt[1028];
		_stprintf_s((_TCHAR *)fmt, hResult, 
           _T("Error %d\n%s\nin file:\n%s"), _com_error(hResult).ErrorMessage(), fName);
		MessageBox(NULL, fmt, _T("Error:"), MB_OK | MB_ICONERROR);
		return FALSE;
	}//if(FAILED(hResult))

	if (img.GetBPP() != 24)                 //if Bitmap format not compatible with 
                                            //GL_BGR_EXT 
	{
		CImage tmg;
		tmg.Create(img.GetWidth(), img.GetHeight(), 24);    //Create CImage compatible 
                                                            //with GL_BGR_EXT
		img.BitBlt(tmg.GetDC(), CPoint(0, 0));				//Copy Original Image to 
                                                            //the environment created
	}

	CImage * pSmg = new CImage;								
	CDC * tDC = CDC::FromHandle(img.GetDC());		        //Temporarily graphic context
	//Create Black & White mask from original image:
	MakeBWMask(pSmg, tDC, img.GetWidth(), img.GetHeight(), tDC->GetPixel(0, 0), 
                                                           img.GetBPP());
	CBitmap * pBmw = GetImgBitmap(pSmg);                    //Bitmap of the 
                                                            //Black & White mask
	if (pBmw == NULL)
		return FALSE;

	BITMAP BMPw;
	pBmw->GetBitmap(&BMPw);

	GLuint nBw = LoadGLTexture(&BMPw);		     //Create texture of the 
                                                 //Black & White mask 

	CDC * pDC = new CDC;                         //Temporarily graphic context
	pDC->CreateCompatibleDC(CDC::FromHandle(img.GetDC()));
	CBitmap bmt;
	bmt.CreateCompatibleBitmap(CDC::FromHandle(img.GetDC()), 
                               img.GetWidth(), img.GetHeight());
	CBitmap * pBm = pDC->SelectObject(&bmt);
	//Copy Black & White mask into Temporarily graphic context:
	pDC->BitBlt(0, 0, img.GetWidth(), img.GetHeight(), 
                CDC::FromHandle(pSmg->GetDC()), 0, 0, SRCCOPY);
	//Invert colours in Copy Temporarily graphic context mask int 
    //Temporarily graphic context:
	pDC->BitBlt(0, 0, img.GetWidth(), 
                           img.GetHeight(), NULL, 0, 0, DSTINVERT);
	//Copy Temporarily graphic context into original image into 
    //making background color Black:
	CDC::FromHandle(img.GetDC())->BitBlt(0, 0, img.GetWidth(), 
                           img.GetHeight(), pDC, 0, 0, SRCAND);
	pDC->SelectObject(pBm);
	bmt.DeleteObject();
	pDC->DeleteDC();
	delete pDC;						    //Remove Temporarily graphic context

	CBitmap * pBmi = GetImgBitmap(&img);            
	BITMAP BMPi;
	pBmi->GetBitmap(&BMPi);

	delete pBmw;						//Clear all Temoparily
	delete pSmg;
	pSmg = NULL;

	GLuint nImg = LoadGLTexture(&BMPi); //Create texture of the mask 
                                        //with Black background 
	delete pBmi;
	return MAKELONG(nImg, nBw);         //return HIWORD with color texture name
					                    //and LOWORD with Black & White texture name
}

The scheme masking texture has been provided in the table below:

Original masking image Bitmap format and one-ton background color; Top Left pixel GetPixel(0,0) must be of background color Image 2   Original Background Image; may be of any format Image 3
  Image 4     Image 5
Black-white masking image provided with MakeBWMask procedure Image 6 Image 7 Clear to black the masking space 1 1! 1! in the Original Bakground Image Image 8
  Image 9     Image 10
Change background to black with the Black-white masking image inverted: Image 11 Image 12 Final Masked Image is a result of composition colour mask with the black background to the original background texture with the black space for masking Image 13

The left column of the table above demonstrates the LoadMaskTexture steps of creation two masking textures:

  • Black-white masking image provided with MakeBWMask procedure
  • Colour masking image with the black background with the Black-white masking the image inverted

The procedure for Black-white masking image creating (change all the background pixels to white, others to black):

C++
void MakeBWMask(CImage * pImg, CDC *pDC, int w, int h, COLORREF eColor, int BPP)
{
	pImg->Create(w, h, BPP);
	CDC * tDC = CDC::FromHandle(pImg->GetDC());
	for (int i = 0; i < w; i++)
		for (int j = 0; j < h; j++)
		{
			COLORREF rgb = pDC->GetPixel(i, j);			  //get pixel colour
			if (rgb == eColor)
				tDC->SetPixel(i, j, RGB(255, 255, 255));  //if the colour is a 
                                                          //background colour 
                                                          //set pixel white;
			else
				tDC->SetPixel(i, j, RGB(0, 0, 0));        //if the colour is not 
                                                          //a background 
                                                          //colour, set pixel black;
		}
}

The variables inserted:

C++
	int m_texNum;				//Current order number of the name of the texture 
	int m_maskNum;              //Current order number of the name of the mask texture
	CWordArray m_globTexture;	//Array of the textures' names available	
	CDWordArray m_globMask;	    //Array of the bitmap mask textures' names available	
enum VIEW_TYPE
{
	VIEW_DEFAULT,               //Original Cube Sample View
	VIEW_TEXTURE,               //Texture View  
        VIEW MASK,              //Masking View
};

The original CImgTextureView::Init() procedure with the procedures related remains unchanged; in CImgTextureView::OnCreate procedure in demo purposes, the two lines of the texture implementation and three lines of masking implementation from the image files in the Data folder of the root directory located:

C++
int CImgTextureView::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
	if (CView::OnCreate(lpCreateStruct) == -1)
		return -1;
	Init(); // initialize OpenGL
	m_globTexture.Add((WORD)LoadImgTexture(_T("Data/MSUN.jpg"))); //you may insert 
                                                                  //here any image file
                                                                  //in any pathway
	m_globTexture.Add((WORD)LoadImgTexture(_T("Data/famrt.jpg")));//and as much as 
                                                                  //you like 

	m_globMask.Add(LoadMaskTexture(_T("Data/vldv.bmp")));  //you may insert here any
                                                           //bitmap image file in 
                                                           //any pathway
	m_globMask.Add(LoadMaskTexture(_T("Data/ussr.bmp")));  //and as much as you like; 
                                                           //just keep in mind that 
                                                           //the color 
	m_globMask.Add(LoadMaskTexture(_T("Data/rsfsr.bmp"))); //of the left top pixel 
                                                           //(GetPixel(0,0)) assumed as 
                                                           //a background color

	return 0;
} ;

You may insert here any image files and any masking bitmap image files from any pathway valid and as much as you like. In the original CMaskTextureView::DrawScene procedure, the block of masking texture handling inserted:

C++
case VIEW_MASK:
    {
        //Every face visibility checking required(depth test)
        GLdouble model[16];                        //MODELVIEW_MATRIX
        glGetDoublev(GL_MODELVIEW_MATRIX, model);
        GLdouble proj[16];                         //PROJECTION_MATRIX
        glGetDoublev(GL_PROJECTION_MATRIX, proj);
        GLint vw[4];                               //VIEWPORT MATRIX
        glGetIntegerv(GL_VIEWPORT, vw);

        GLdouble xt0, yt0, zt0;                    //true coord of the cube center
        gluProject(0.0, 0.0, 0.0, model,
                 proj, vw, &xt0, &yt0, &zt0);      //calc true coord of the
                                                   //cube center

        //////////////Front Face

        // Front Face  Bkg
        GLdouble xt, yt, zt;
        gluProject(0.0, 0.0, a, model, proj, vw, &xt, &yt, &zt); //calc true coord
                                                                 //of the face center
        if (zt < zt0) //if face is visible
        {
            glBindTexture(GL_TEXTURE_2D, m_globTexture[m_texNum]);
            glBegin(GL_QUADS);

            glTexCoord2f(0.0f, 0.0f); glVertex3f(-a, -a, a);
            glTexCoord2f(2.0f, 0.0f); glVertex3f(a, -a, a);
            glTexCoord2f(2.0f, 2.0f); glVertex3f(a, a, a);
            glTexCoord2f(0.0f, 2.0f); glVertex3f(-a, a, a);
            glEnd();

            // Front Face BW
            glBindTexture(GL_TEXTURE_2D, HIWORD(m_globMask[m_maskNum]));
            glEnable(GL_BLEND);                      // Enable Blending
            glDisable(GL_DEPTH_TEST);                // Disable Depth Testing
            glBlendFunc(GL_DST_COLOR, GL_ZERO);      // Blend Screen Color With
                                                     // Zero (Black)

            glBegin(GL_QUADS);
            glTexCoord2f(roll + 0.0f, 0.0f); glVertex3f(-a, -a, a);
            glTexCoord2f(roll + 2.0f, 0.0f); glVertex3f(a, -a, a);
            glTexCoord2f(roll + 2.0f, 2.0f); glVertex3f(a, a, a);
            glTexCoord2f(roll + 0.0f, 2.0f); glVertex3f(-a, a, a);
            glEnd();

            // Front Face Mask
            glBindTexture(GL_TEXTURE_2D, LOWORD(m_globMask[m_maskNum]));
            glBlendFunc(GL_ONE, GL_ONE);             // Copy Image 2 Color to
                                                     // the screen
            glBegin(GL_QUADS);
            glTexCoord2f(roll + 0.0f, 0.0f); glVertex3f(-a, -a, a);
            glTexCoord2f(roll + 2.0f, 0.0f); glVertex3f(a, -a, a);
            glTexCoord2f(roll + 2.0f, 2.0f); glVertex3f(a, a, a);
            glTexCoord2f(roll + 0.0f, 2.0f); glVertex3f(-a, a, a);
            glEnd();

            glEnable(GL_DEPTH_TEST);                  // Enable Depth Testing
            glDisable(GL_BLEND);                      // Disable Blending
        }
        //////////////Back Face
        gluProject(0.0, 0.0, -a, model, proj, vw, &xt, &yt, &zt); //calc true coord
                                                                  //of the face center
        if (zt < zt0) //if face is visible
        {
    ..............................................................................
    ..............................................................................
        ///////////////////////////////////////////// Left face
        // Left Face bkg
        gluProject(-a, 0.0, 0.0,
               model, proj, vw, &xt, &yt, &zt); //calc true coord of
                                                    //the face center
        if (zt < zt0)           //if face is visible
        {
            glBindTexture(GL_TEXTURE_2D,
                                           m_globTexture[m_texNum]);
            glColor3f(1.0f, 1.0f, 1.0f);

            glBegin(GL_QUADS);
            glTexCoord2f(0.0f, 0.0f); glVertex3f(-a, -a, -a);
            glTexCoord2f(1.0f, 0.0f); glVertex3f(-a, -a, a);
            glTexCoord2f(1.0f, 1.0f); glVertex3f(-a, a, a);
            glTexCoord2f(0.0f, 1.0f); glVertex3f(-a, a, -a);
            glEnd();

            // Left Face BW
            glBindTexture
            (GL_TEXTURE_2D, HIWORD(m_globMask[m_maskNum]));
            glEnable(GL_BLEND);                     // Enable Blending
            glDisable(GL_DEPTH_TEST);               // Disable Depth Testing
            glBlendFunc(GL_DST_COLOR, GL_ZERO);     // Blend Screen Color
                                                    // With Zero (Black)

            glBegin(GL_QUADS);
            glTexCoord2f(0.0f, roll + 0.0f); glVertex3f(-a, -a, -a);
            glTexCoord2f(1.0f, roll + 0.0f); glVertex3f(-a, -a, a);
            glTexCoord2f(1.0f, roll + 1.0f); glVertex3f(-a, a, a);
            glTexCoord2f(0.0f, roll + 1.0f); glVertex3f(-a, a, -a);
            glEnd();

            // Left Face Mask
            glBindTexture
                             (GL_TEXTURE_2D, LOWORD(m_globMask[m_maskNum]));
            glBlendFunc(GL_ONE, GL_ONE);        // Copy Image 2 Color To The Screen
            glBegin(GL_QUADS);
            glTexCoord2f(0.0f, roll + 0.0f); glVertex3f(-a, -a, -a);
            glTexCoord2f(1.0f, roll + 0.0f); glVertex3f(-a, -a, a);
            glTexCoord2f(1.0f, roll + 1.0f); glVertex3f(-a, a, a);
            glTexCoord2f(0.0f, roll + 1.0f); glVertex3f(-a, a, -a);
            glEnd();

            glEnable(GL_DEPTH_TEST);            // Enable Depth Testing
            glDisable(GL_BLEND);                // Disable Blending
        }

        roll += 0.05f;           // Increase Our Texture Roll Variable
        if (roll>1.0f)            // Is Roll Greater Than One
        {
            roll -= 1.0f;        // Subtract 1 From Roll
        }

    }
        break;

In this block, we need to arrange a depth test before every face handling because GL_DEPTH_TEST is set to disabled for blending. Before every scene drawing, we call standard gluProject procedure to calc the true coord of the cube center. Before every face drawing, we call standard gluProject procedure to calc the true coord of the face center. The face is visible if the center of the face is shorter to observe than the center of the cube.

The right column of the table demonstrates the DrawScene steps of masking texture:

After masking performance, the scroll factor increased.

All the following Menu and Accelerator Commands have been done with standard AppWizard technologies.

Application Demo Menu and Keyboard Commands

Some menu and some special Accelerator keys arranged in order to demonstrate the ImgTexture project implementation:

Image 14

  • Menu File->Open - selecting the file with the image for texture mapping
  • Menu File->Open Mask *.bmp - selecting the file with the bitmap image for texture masking
  • Menu View->Default - original Cube performance
  • Menu View->Texture - current texture performance
  • Menu View->Next Texture - next texture performance (also Right Arrow click)
  • Menu View->Prev Texture - previous texture performance (also Left Arrow click)
  • Menu View->Next Mask - next masking performance (also Page Up click)
  • Menu View->Prev Mask - previous masking performance (also Page Down click)

Points of Interest

The technology provided above is an attempt to demonstrate one of the simple masking texture implementation possibilities. You may use the foregoing procedures in your own applications inserting the GlobGLTexture.cpp file into your own project with the menu PROJECT->Add Existing Item... command.

The project has been developed in MFC platform. Nevertheless, the GlobGLTexture.cpp procedures are also valid in Win32 and are acceptable for use in Mobile applications.

History

  • 21st November, 2016: Initial version

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)