0

I am trying to create a bitmap by hardcoding an array of pixel values, converting this array of pixels into a DIB, and then turn this DIB into a DDB. I found two functions to convert CreateBitmapFromPixels and DIBToDDB on the internet. My problem is that the program would crash at line 244. I found that, at line 243, lpbi does not retrieve information from hDIB. Then I added the code at lines 229 and 230 to see if doing the same thing in the function that created the BITMAPINFO structure would help. Still, nothing was gotten from the HBITMAP. I am wondering if there is anything wrong with casting a handle into a pointer, what does it do, and are there other ways to get the HBITMAPINFOHEADER from a handle to a DIB so I can fix the problem.

 HBITMAP ColorChange2Dlg::CreateBitmapFromPixels( HDC hDC,
 UINT uWidth, UINT uHeight, UINT uBitsPerPixel, LPVOID pBits)
{
 if(uBitsPerPixel < 8) // NOT IMPLEMENTED YET
 return NULL;
 if(uBitsPerPixel == 8)
 return Create8bppBitmap(hDC, uWidth, uHeight, pBits);
 HBITMAP hBitmap = 0;
 if ( !uWidth || !uHeight || !uBitsPerPixel )
 return hBitmap;
 LONG lBmpSize = uWidth * uHeight * (uBitsPerPixel/8) ;
 BITMAPINFO bmpInfo = { 0 };
 bmpInfo.bmiHeader.biBitCount = uBitsPerPixel;
 bmpInfo.bmiHeader.biHeight = uHeight;
 bmpInfo.bmiHeader.biWidth = uWidth;
 bmpInfo.bmiHeader.biPlanes = 1;
 bmpInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
 if(bmpInfo.bmiHeader.biBitCount==32) {
 bmpInfo.bmiHeader.biCompression=BI_RGB;
 //bmpInfo.bmiColors=NULL;
 }
 // Pointer to access the pixels of bitmap
 UINT * pPixels = 0;
 hBitmap = CreateDIBSection( hDC, (BITMAPINFO *)&
 bmpInfo, DIB_RGB_COLORS, (void **)&
 pPixels , NULL, 0);
 if ( !hBitmap )
 return hBitmap; // return if invalid bitmaps
 //SetBitmapBits( hBitmap, lBmpSize, pBits);
 // Directly Write
 memcpy(pPixels, pBits, lBmpSize );
 LPBITMAPINFOHEADER lpbi; //Line 229
 lpbi = (LPBITMAPINFOHEADER)hBitmap; //Line 230
 return hBitmap;
}
HBITMAP ColorChange2Dlg::DIBToDDB( HANDLE hDIB, CDC& dc ) 
{ 
 LPBITMAPINFOHEADER lpbi; 
 HBITMAP hbm; 
 CPalette pal; 
 CPalette* pOldPal; 
 //CClientDC dc(NULL); 
 if (hDIB == NULL) 
 return NULL; 
 lpbi = (LPBITMAPINFOHEADER)hDIB; //Line 243
 int nColors = lpbi->biClrUsed ? lpbi->biClrUsed : 1 << lpbi->biBitCount; //Line 244
 BITMAPINFO &bmInfo = *(LPBITMAPINFO)hDIB ; 
 LPVOID lpDIBBits; 
 if( bmInfo.bmiHeader.biBitCount > 8 ) 
 lpDIBBits = (LPVOID)((LPDWORD)(bmInfo.bmiColors + 
 bmInfo.bmiHeader.biClrUsed) + 
 ((bmInfo.bmiHeader.biCompression == BI_BITFIELDS) ? 3 : 0)); 
 else 
 lpDIBBits = (LPVOID)(bmInfo.bmiColors + nColors); 
 // Create and select a logical palette if needed 
 if( nColors <= 256 && dc.GetDeviceCaps(RASTERCAPS) & RC_PALETTE) 
 { 
 UINT nSize = sizeof(LOGPALETTE) + (sizeof(PALETTEENTRY) * nColors); 
 LOGPALETTE *pLP = (LOGPALETTE *) new BYTE[nSize]; 
 pLP->palVersion = 0x300; 
 pLP->palNumEntries = nColors; 
 for( int i=0; i < nColors; i++) 
 { 
 pLP->palPalEntry[i].peRed = bmInfo.bmiColors[i].rgbRed; 
 pLP->palPalEntry[i].peGreen = bmInfo.bmiColors[i].rgbGreen; 
 pLP->palPalEntry[i].peBlue = bmInfo.bmiColors[i].rgbBlue; 
 pLP->palPalEntry[i].peFlags = 0; 
 } 
 pal.CreatePalette( pLP ); 
 delete[] pLP; 
 // Select and realize the palette 
 pOldPal = dc.SelectPalette( &pal, FALSE ); 
 dc.RealizePalette(); 
 } 
 hbm = CreateDIBitmap(dc.GetSafeHdc(), // handle to device context 
 (LPBITMAPINFOHEADER)lpbi, // pointer to bitmap info header 
 (LONG)CBM_INIT, // initialization flag 
 lpDIBBits, // pointer to initialization data 
 (LPBITMAPINFO)lpbi, // pointer to bitmap info 
 DIB_RGB_COLORS ); // color-data usage 
 if (pal.GetSafeHandle()) 
 dc.SelectPalette(pOldPal,FALSE); 
 return hbm; 
} 
void ColorChange2Dlg::OnBnClickedButton1()
{
 // TODO: Add your control notification handler code here
 CClientDC dc(this);
 COLORREF *pix = (COLORREF *)malloc(255*255*sizeof(COLORREF));
 //int x = 1;
 if(pix!=NULL){
 for(int i=0;i<255;i++)
 {
 for(int j=0;j<255;j++)
 {
 pix[i*255+j] = RGB(i,j,0);
 }
 }
 }
 CDC tempDC;
 tempDC.CreateCompatibleDC(&dc);
 HBITMAP dib = CreateBitmapFromPixels(tempDC.m_hDC,255,255,8*sizeof(COLORREF),(BYTE*)pix);
 HBITMAP finalMap = DIBToDDB(dib,tempDC);
 HBITMAP oldMap = (HBITMAP)tempDC.SelectObject(finalMap);
 dc.BitBlt(201,50,255,255,&tempDC,0,0,SRCCOPY);
 tempDC.SelectObject(oldMap);
 tempDC.DeleteDC();
}
asked Jun 27, 2016 at 3:27
2
  • dc.GetDeviceCaps(RASTERCAPS) & RC_PALETTE I think this code is for old display drivers. Modern computers support 32 bit colors. Images with 256 colors, or lower, contain their pixel information in BITMAPINFO::bmpColors. You don't have to manually call the palette functions because GDI functions know there is a palette. You should explain in more detail what it is you are trying to do (your overall goal?) Commented Jun 27, 2016 at 5:09
  • I want to create a 255 by 255 pixel bitmap where each pixel has a unique red and green color. (All pixels have 0 blue) I have heard that I have to use a DIB instead of DDB to set individual pixel colors. So I searched through the web and came up with this. Commented Jun 27, 2016 at 5:15

1 Answer 1

1

To write compatible code, it's better not to access bits directly at all. You can use Gradient functions and GDI or GDI+ draw functions to do anything you want.

The code you have in mind pix[i*255+j] = RGB(i,j,0); is of a 32-bit image. Each pixel points to a color. It's not a palette image where each pixel points to an entry in the color table.

If display is 32 bit (most modern computers are, but check to make sure), you can do this with the following code

CBitmap m_bitmap;
void CMyWnd::make_bitmap()
{
 if (m_bitmap.GetSafeHandle()) return;
 int w = 255; 
 int h = 255;
 int *pix = new int[w*h];
 for (int i = 0; i < w; i++) 
 for (int j = 0; j < h; j++)
 pix[i + j*w] = RGB(i, j, 0);
 m_bitmap.CreateBitmap(w, h, 1, 32, pix);
 delete[]pix;
}

And to draw the bitmap:

void CMyWnd::paint_bitmap(CDC &dc)
{
 if (!m_bitmap.GetSafeHandle()) return;
 CDC memdc;
 memdc.CreateCompatibleDC(&dc);
 HBITMAP oldbitmap = (HBITMAP)memdc.SelectObject(m_bitmap);
 BITMAP bm;
 m_bitmap.GetBitmap(&bm);
 dc.BitBlt(0, 0, bm.bmWidth, bm.bmHeight, &memdc, 0, 0, SRCCOPY);
 memdc.SelectObject(oldbitmap);
}
void CMyWnd::OnPaint()
{
 __super::OnPaint();
 CClientDC dc(this);
 paint_bitmap(dc);
}


Edit: For historical reasons the RGB value are saved backward as BGR. Use this function instead:

void CMyWnd::make_bitmap()
{
 if (m_bitmap.GetSafeHandle()) return;
 int w = 256;
 int h = 256;
 BYTE *pix = new BYTE[4*w*h];
 for (int i = 0; i < w; i++)
 {
 for (int j = 0; j < h; j++)
 {
 int p = (i + j*w) * 4;
 pix[p + 0] = 0;//blue
 pix[p + 1] = i;//green
 pix[p + 2] = j;//red
 pix[p + 3] = 0;//not used in GDI functions
 }
 }
 m_bitmap.CreateBitmap(w, h, 1, 32, pix);
 delete[]pix;
}
answered Jun 27, 2016 at 7:06
Sign up to request clarification or add additional context in comments.

4 Comments

very interesting. I tinkered with your code and it worked. I had to call OnPaint right after delete[]pix; in CMyWnd::make_bitmap(); Is this OnPaint function different from CMyWnd::OnPaint?
void OnPaint() is reserved in MFC for handling ON_WM_PAINT, it should not be called directly.
Also the picture generated is a blue and green mixture instead of red and green
Yes you can use Invalidate() or InvalidateRect to force repaint. The colors red and blue are swapped. See edit...

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.