varimax
varimax

Reputation: 121

MFC casting Handle into pointer and DIB to DDB conversion

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();
}

Upvotes: 0

Views: 365

Answers (1)

Barmak Shemirani
Barmak Shemirani

Reputation: 31669

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;
}

Upvotes: 1

Related Questions