Creating a CBitmap

I have a 2D array of grayscale values (0-255).  I would like to use them to create a CBitmap to display in a dialog.

How do I use the CBitmap initialization functions to create a grayscale picture with one byte per pixel?  I am confused in particular by the num bytes and color plane stuff.

How do I get a HBITMAP to set on the CStatic on a dialog box
Hep_CatAsked:
Who is Participating?
 
DanRollinsCommented:
First, you can try changing your video settings to 24 bits.  If you must use TrueColor(32 bit) then you should modify the program as follows:

const int nBytes = nPixels * 4;    // 4 Bytes/pixel
...
for (int y = 0; y < nPixels; y++)
{
    lpBits[(y*4)+0] = color;
    lpBits[(y*4)+1] = color;
    lpBits[(y*4)+2] = color;
    lpBits[(y*4)+3] = 0; // I think
}

To avoid having problems with various video settings, you should consider using CreateDIBitmap rather than CreateBitmap (DI is Device Independant).  It is more complicated to set up the API call, but as I recall, the data layout is about the same.  But try the above first.

-- Dan
0
 
Roshan DavisCommented:
First you want to know the width and height of the bitmap, and make the 2d array to continous 1 dimensional array,

Use the bitcount to8. becoz you are dealing with 0 - 255 colors

User the function CBitmap::CreateBitmap

GOOD LUCK
0
 
migelCommented:
Hi!
easest way is to use CDIB class I mentoned in the previous your Q.
0
Cloud Class® Course: MCSA MCSE Windows Server 2012

This course teaches how to install and configure Windows Server 2012 R2.  It is the first step on your path to becoming a Microsoft Certified Solutions Expert (MCSE).

 
Hep_CatAuthor Commented:
What does the nPlanes parameter mean (I want grayscale).  Here is some of my sample source code that isn't working:

void CDisplayDlg::DisplayPicture()
{
     CBitmap cBitmap;
     int nWidth = 20;
     int nHeight = 20;
     int nPlanes   = 1;
     int nBitcount = 8;
     BYTE lpBits[400];
     BYTE color = 0;

     for (int y = 0; y < 400; y++)
     {
          lpBits[y] = color;
     }

     cBitmap.CreateBitmap(nWidth, nHeight, nPlanes, nBitcount, lpBits);
     HBITMAP hBitmap = HBITMAP(cBitmap);
     
     m_cStaticPicture.SetBitmap(hBitmap);
}

m_cStaticPicture is the CStatic control.  Does it matter what size I make the CStatic box on the Dialog window?
0
 
migelCommented:
Hi!
1. nPlanes mean color planes of the your bitmap - in 99% cases it equal to 1
2. Of course static control will clip your bitmap according own size.
0
 
DanRollinsCommented:
Actually CStatic will not clip.  It just grows to be large enough to show the picture, clipped by size of parent window.  Kinda funny that way.


0
 
DanRollinsCommented:
The code you gave will create an image that displays apparently random colors... the 8-bits-per-pixel colors will use the system palette.

If the incoming data is bytes of 0-255, where 0 is black, 128 is gray, and 255 is white, then I suggest creating a 24-bit image.

cBitmap.CreateBitmap(nWidth, nHeight, 1, 24, lpBits);

Make lpBits point to a 1200-byte array (400 pixels x 3 bytes per pixel) and fill that data as follows:
   
for (int j= 0; j < 400; j++) {
      lpBits[(j*3)+0]= inputDataBytes[j];
      lpBits[(j*3)+1]= inputDataBytes[j];
      lpBits[(j*3)+2]= inputDataBytes[j];
}

-- Dan
0
 
fl0ydCommented:
Hep_Cat,
   using 24 as your nBitCount is probably the easiest way to go. Especially with your bitmap being 20x20 pixels you don't really need to worry about bytes your wasting. But there is one pitfall with bitmaps: rows in bitfields must be DWORD-aligned, i.e. each row must start at a multiple of 4. So the width of your array isn't always neccessarily (BitCount / 8) * width. Use this formula to get the actual needed width:
RowWidth = ( ( ( nBitCount >> 3 ) * nWidth ) + 3 ) & ~3;
In your case it doesn't make a difference, but if you look at nWidth = 19:
RowWidth = ( ( ( 24 >> 3 ) * 19 ) + 3 ) & ~3
         = ( ( 3 * 19 ) + 3 ) & ~3
         = 60 & ~3 = 60
So there are 3 bytes at the end of each row that have to be appended to make the next row start at a DWORD-boundary.
0
 
Hep_CatAuthor Commented:
Ok, this all makes sense, but something is going wrong.  Here's all that I have got:

The project is a SIMPLE Dlg app.  I just dropped a picture control on the dialog, set its type to Bitmap, and changed its ID to IDC_PICTURE.  Then I created a control variable "m_cStaticPicture".  Next is all the relevant code.

// Constants in DisplayDlg.h
const int nSize = 20;
const int nPlanes = 1;
const int nBitcount = 24;
const int nPixels = nSize * nSize; // Total pixel count
const int nBytes = nPixels * 3;    // 3 Bytes/pixel

// These are members of the DisplayDlg class
CBitmap cBmp;
HBITMAP hBmp;
BYTE lpBits[nBytes];

// This is the code from the CDisplayDlg::OnInitDialog()
// function that I was hoping would set the bits.
//
BYTE color = 0;
for (int y = 0; y < nPixels; y++)
{
     lpBits[(y*3)+0] = color;
     lpBits[(y*3)+1] = color;
     lpBits[(y*3)+2] = color;
}
cBmp.CreateBitmap(nSize, nSize, nPlanes, nBitcout, lpBits);
hBmp = HBITMAP(cBmp);
m_cStaticPicture.SetBitmap(hBmp);


/////////////////////////////////
Now, when I run this I am getting a bitmap that has some Windows graphics in it (like the X, box, and underscore in the top right corner of most apps.  Also the ? square from minesweeper).  If I make the CBitmap and the HBITMAP local variables instead of class members, then I get an all-white picture with some random black spots in it no matter what color I use.  I am just trying to set it to a single color for now.

I am doing something incorrectly with the memory?
I could email my workspace to anybody willing to take a look at this.
0
 
DanRollinsCommented:
I changed this line to fix a typo:
     cBmp.CreateBitmap(nSize, nSize, nPlanes, nBitcount, lpBits);

and I placed all of tha code in an OnButton1 handler.  (draw a button in the dlg editor and then double-click it).

It works perfectly.  If I leave color=0, it is a small black square.  If I set color=128, it is gray and color=255 is white.

-- Dan
0
 
Hep_CatAuthor Commented:
Dan

I do the same, and I get nothing.  If you'll mail me your workspace (bergstro@eng.utah.edu) I'll give it a try and accept it as an answer if it works.

Hep_Cat
0
 
DanRollinsCommented:
OK, I sent it, but it is the simplest possible dlg-based app with changes only to the OnButton2 handler.  It should have worked for you.  

The only confounding thing might be your video settings.  My screen is set at TrueColor(24 bit)

-- Dan
0
 
Hep_CatAuthor Commented:
I am accepting this as an answer because it definitely should be working.  The code executes fine, but I am not getting any colored boxes displayed.  If anyone knows the problem, I'd love to hear it. My screen is set to True Color (32 bit) under the settings tab of the display properties.
0
 
Hep_CatAuthor Commented:
My Windows doesn't have a 24 bit option.  I tried using the DIB stuff and I finally got it to work just fine.  I've posted the code that I used below (again, it just sets everything to a gray value).

void CDisplayDlg::OnButton1()
{
     const int nSize = 20;
     const int nBytes = nSize * nSize;

     BYTE * bits;
     CClientDC dc(this);

     BITMAPINFO * BitInfo = new BITMAPINFO;
     BitInfo->bmiHeader.biSize               = sizeof(BITMAPINFOHEADER);
     BitInfo->bmiHeader.biWidth               = nSize;
     BitInfo->bmiHeader.biHeight               = -nSize;
     BitInfo->bmiHeader.biPlanes               = 1;
     BitInfo->bmiHeader.biBitCount          = 8;
     BitInfo->bmiHeader.biCompression     = 0;

     CDC memDC;
     memDC.CreateCompatibleDC(&dc);

     HBITMAP hbmp=CreateDIBSection(memDC.m_hDC, BitInfo, DIB_RGB_COLORS,
                                       (void **)&bits, NULL, 0);
     HBITMAP oldBmp=(HBITMAP)SelectObject(memDC.m_hDC, hbmp);
                                         
     BYTE color = 128;
     for (int j=0; j<nBytes; j++)
     {
          bits[j] = color;
     }

     int nColors = 256;
     RGBQUAD * pRGB = new RGBQUAD[nColors];
    for (int i=0; i<256; i++)
     {
          pRGB[i].rgbRed          = i;
          pRGB[i].rgbGreen     = i;
          pRGB[i].rgbBlue          = i;
          pRGB[i].rgbReserved     = 0;
     }
     ::SetDIBColorTable(memDC.m_hDC, 0, nColors, pRGB);

     dc.BitBlt(50, 50, nSize, nSize, &memDC, 0, 0, SRCCOPY);

     DeleteObject(hbmp);
     delete BitInfo;
     delete pRGB;
}
0
 
DanRollinsCommented:
I'm glad we got this working for you.

>>I am accepting this as an answer...
Perhaps you clicked the wrong button?  The question is still open and no points have been awarded.

-- Dan
0
 
Hep_CatAuthor Commented:
I tried Dan's solution on a 24-bit video machine and it worked great.  DIB's are messier, but eliminated some of the headache.

Hep_Cat
0
Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.

All Courses

From novice to tech pro — start learning today.