Link to home
Start Free TrialLog in
Avatar of Hep_Cat
Hep_Cat

asked on

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
Avatar of Roshan Davis
Roshan Davis
Flag of United States of America image

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
Avatar of migel
migel

Hi!
easest way is to use CDIB class I mentoned in the previous your Q.
Avatar of Hep_Cat

ASKER

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?
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.
Avatar of DanRollins
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.


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
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.
Avatar of Hep_Cat

ASKER

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.
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
Avatar of Hep_Cat

ASKER

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
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
Avatar of Hep_Cat

ASKER

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.
ASKER CERTIFIED SOLUTION
Avatar of DanRollins
DanRollins
Flag of United States of America image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of Hep_Cat

ASKER

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;
}
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
Avatar of Hep_Cat

ASKER

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