Thread: Bitmap, save to file problem.

  1. #1
    Registered User
    Join Date
    Sep 2010
    Posts
    69

    Bitmap, save to file problem.

    Hey guys,
    I've been reading up on working with bitmaps and getting a good feel for it.
    Using the Win32 API, I can create a bitmap, load a bitmap from file and I can even save to file a previously loaded bitmap.
    Unfortunately, I am unable to save to file a bitmap which I create in memory. Now, I have searched and read thru the archives here and have not yet found the solution.
    Below is my code for creating a bitmap, which I bitblt to my double-buffer and display on my main screen. This works great.
    It creates a 150 X 100 pixel rectangle with an ellipse drawn in the center of it.
    Code:
    void MakeBitmap(HWND hwnd)
    {
        char bmpFile[BUFSIZE];
        int bpp = 1;
        SIZE SBitmap = {150, 100};
    
        HDC      hDC = HdcBuffer;       // handle to double buffer
        HBITMAP  hBmp, hBmpOld;
        HDC      hMemDC;
        COLORREF clrZeros, clrOnes;
    
        PBITMAPINFO pbmpi;
    
        hBmp = CreateBitmap(SBitmap.cx, SBitmap.cy, 1, bpp, NULL);
        hMemDC = CreateCompatibleDC(hDC);
        hBmpOld = (HBITMAP) SelectObject(hMemDC, hBmp);
    
        clrZeros = PALETTERGB(0,0,255);
        SetTextColor(hDC, clrZeros);
        PatBlt(hMemDC, 0, 0, SBitmap.cx, SBitmap.cy, BLACKNESS);
    
        clrOnes = PALETTERGB(255,0,0);
        SetBkColor(hDC, clrOnes);
        Ellipse(hMemDC, 0, 0, SBitmap.cx, SBitmap.cy);
    
        BitBlt(hDC, 10, 10, SBitmap.cx, SBitmap.cy, hMemDC, 0, 0, SRCCOPY);
    
        InvalidateRect(hwnd, &glrect, FALSE);
        UpdateWindow(hwnd);
    
    //..............HERE: is the BITMAP save to file section:
        strcpy(bmpFile, "test.bmp");
        pbmpi = CreateBitmapInfoStruct(hwnd, hBmp);
        CreateBMPFile(hwnd, bmpFile, pbmpi, hBmp, hMemDC);
    //......................................................
    
        DeleteObject(SelectObject(hMemDC, hBmpOld));
        DeleteDC(hMemDC);
        DeleteDC(hBmp);
        ReleaseDC(hwnd, hDC);
    }
    Near the bottom, I call two functions taken from the SDK (Storing an Image) which does not seem to work in this case.

    Here is the code for Storing an Image:
    Code:
    PBITMAPINFO CreateBitmapInfoStruct(HWND hwnd, HBITMAP hBmp)
    {
        BITMAP      bmp;
        PBITMAPINFO pbmi;
        WORD        cClrBits;
    
                          // Retrieve the bitmap color format, width, and height.
        if(!GetObject(hBmp, sizeof(BITMAP), (LPSTR)&bmp))
        {
            printf("error:GetObject\n");
            exit(0);
        }
                                 // Convert the color format to a count of bits.
        cClrBits = (WORD)(bmp.bmPlanes * bmp.bmBitsPixel);
    
        if(cClrBits == 1)
        {
            cClrBits = 1;
        }
        else if(cClrBits <= 4)
        {
            cClrBits = 4;
        }
        else if(cClrBits <= 8)
        {
            cClrBits = 8;
        }
        else if(cClrBits <= 16)
        {
            cClrBits = 16;
        }
        else if(cClrBits <= 24)
        {
            cClrBits = 24;
        }
        else
        {
            cClrBits = 32;
        }
    
        if(cClrBits != 24)
        {
            pbmi = (PBITMAPINFO) LocalAlloc(LPTR, sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * (1<< cClrBits));
        }
        else
        {
            pbmi = (PBITMAPINFO) LocalAlloc(LPTR, sizeof(BITMAPINFOHEADER));
        }
    
        pbmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
        pbmi->bmiHeader.biWidth = bmp.bmWidth;
        pbmi->bmiHeader.biHeight = bmp.bmHeight;
        pbmi->bmiHeader.biPlanes = bmp.bmPlanes;
        pbmi->bmiHeader.biBitCount = bmp.bmBitsPixel;
    
        if(cClrBits < 24)
        {
            pbmi->bmiHeader.biClrUsed = (1<<cClrBits);
        }
    
        pbmi->bmiHeader.biCompression = BI_RGB;
        pbmi->bmiHeader.biSizeImage = ((pbmi->bmiHeader.biWidth * cClrBits +31) & ~31) /8 * pbmi->bmiHeader.biHeight;
        pbmi->bmiHeader.biClrImportant = 0;
    
        return pbmi;
    }
    Code:
    void CreateBMPFile(HWND hwnd, LPTSTR pszFile, PBITMAPINFO pbi, HBITMAP hBMP, HDC hDC)
    {
        HANDLE hf;                  // file handle
        BITMAPFILEHEADER hdr;       // bitmap file-header
        PBITMAPINFOHEADER pbih;     // bitmap info-header
        LPBYTE lpBits;              // memory pointer
        DWORD dwTotal;              // total count of bytes
        DWORD cb;                   // incremental count of bytes
        BYTE *hp;                   // byte pointer
        DWORD dwTmp;
    
        pbih = (PBITMAPINFOHEADER) pbi;
        lpBits = (LPBYTE) GlobalAlloc(GMEM_FIXED, pbih->biSizeImage);
    
        if(!lpBits)
        {
            printf("error:GlobalAlloc\n");
            exit(0);
        }
    
        if(!GetDIBits(hDC, hBMP, 0, (WORD) pbih->biHeight, lpBits, pbi, DIB_RGB_COLORS))
        {
            printf("error:GetDIBits\n");
            exit(0);
        }
    
        hf = CreateFile(pszFile, GENERIC_READ | GENERIC_WRITE, (DWORD) 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, (HANDLE) NULL);
    
        if(hf == INVALID_HANDLE_VALUE)
        {
            printf("error:CreateFile\n");
            exit(0);
        }
    
        hdr.bfType = 0x4d42;        // 0x42 = "B" 0x4d = "M"
    
        hdr.bfSize = (DWORD) (sizeof(BITMAPFILEHEADER) + pbih->biSize + pbih->biClrUsed * sizeof(RGBQUAD) + pbih->biSizeImage);
        hdr.bfReserved1 = 0;
        hdr.bfReserved2 = 0;
    
        hdr.bfOffBits = (DWORD) sizeof(BITMAPFILEHEADER) + pbih->biSize + pbih->biClrUsed * sizeof(RGBQUAD);
    
                              // Copy the BITMAPFILEHEADER into the .BMP file.
        if(!WriteFile(hf, (LPVOID) &hdr, sizeof(BITMAPFILEHEADER), (LPDWORD) &dwTmp,  NULL))
        {
            printf("error:WriteFile-1\n");
            exit(0);
        }
                     // Copy the BITMAPINFOHEADER and RGBQUAD array into the file.
        if(!WriteFile(hf, (LPVOID) pbih, (sizeof(BITMAPINFOHEADER) + pbih->biClrUsed * sizeof(RGBQUAD)), (LPDWORD) &dwTmp, NULL))
        {
            printf("error:WriteFile-2\n");
            exit(0);
        }
                           // Copy the array of color indices into the .BMP file.
        dwTotal = cb = pbih->biSizeImage;
        hp = lpBits;
    
        if(!WriteFile(hf, (LPSTR) hp, (int) cb, (LPDWORD) &dwTmp,NULL))
        {
            printf("error:WriteFile-3\n");
            exit(0);
        }
    
        if(!CloseHandle(hf))
        {
            printf("error:CloseHandle\n");
            exit(0);
        }
    
        GlobalFree((HGLOBAL)lpBits);
    }
    *Re: the printf statements: I use a console window for error messages.

    The Storing an Image code works when I load an image from a disk-file, so I know it does work. Just not when I create the bitmap in memory.
    What I end up with is a BLACK rectangle saved to file, instead of my bitmap.
    I have read of this exact same problem in the archives. However, I cannot figure out a solution.
    Any ideas ?
    Am I missing something obvious ?

  2. #2
    'Allo, 'Allo, Allo
    Join Date
    Apr 2008
    Posts
    639
    For one thing, with anything other than 24bpp bitmaps the sample code is missing the intermediary step of actually filling in the pallette of colours used. That's to be expected though, code from ~23 years ago can't tell which colours you want in the bitmap. So after somebody figures out why it's buggy, at some point you'd need to fill in those RGBQUADs it allocates.

    I don't care to debug it at 2:42 in the morning, but in those intervening 23 years Windows actually figured out saving picture files might be something useful. Of course, calling it SaveImage would be too easy so they made you do something like this:

    Code:
    #include <olectl.h>
    // need to link to ole32.lib and oleaut32.lib
    
    void CreateBMPFile(const char* pFile, HBITMAP hbm)
    {
        PICTDESC pict = {sizeof(pict), 0};
        IPicture* pPicture = NULL;
        HRESULT hr;
        // tell the picture object what format we have
        pict.picType = PICTYPE_BITMAP;
        pict.bmp.hbitmap = hbm;
        // create a picture object from the hbm
        hr = OleCreatePictureIndirect(&pict, IID_IPicture, FALSE, (void**)&pPicture);
        if(SUCCEEDED(hr))
        {
            // allocate some growable memory for the picture object
            // to save the bitmap into
            HGLOBAL hMem = GlobalAlloc(GHND, 0);
            IStream* pStream = NULL;
            // create a wrapper around the memory for the picture object to use
            hr = CreateStreamOnHGlobal(hMem, TRUE, &pStream);
            if(SUCCEEDED(hr))
            {
                // save the picture into the wrapper
                LONG byteSize = 0;
                pPicture->SaveAsFile(pStream, TRUE, &byteSize);
                // If you're usig C, the above line will have to be
                // pPicture->lpVtbl->SaveAsFile(pPicture, pStream, FALSE, &byteSize);
                // create the file
                FILE* f = fopen(pFile, "wb");
                if(f)
                {
                    // lock the growable memory so we can get a pointer to it
                    PVOID pData = GlobalLock(hMem);
                    // copy it to the file
                    fwrite(pData, byteSize, 1, f);
                    fclose(f);
                    // tell Windows it's free to move the memory again
                    GlobalUnlock(hMem);
                }
                // delete the stream, and the growable memory block
                pStream->Release();
                // in C
                // pStream->lpVtbl->Release(pStream);
            }
            // delete the picture object
            pPicture->Release();
            // in C, you get the pattern
            // pPicture->lpVtbl->Release(pPicture);
        }
    }
    Last edited by adeyblue; 04-06-2011 at 07:45 PM.

  3. #3
    Registered User
    Join Date
    Sep 2010
    Posts
    69
    Hey adeyblue, I'll look at your code.

    I have noticed that if I switch-out these two lines:
    Code:
    //    hbitmap = CreateBitmap(SBitmap.cx, SBitmap.cy, 1, bpp, NULL);
        hbitmap = CreateCompatibleBitmap(hDC, SBitmap.cx, SBitmap.cy);
    then something interesting (different) does happen.

    The original code creates a blue rectangle with a red ellipse, on the screen, which merely ends up as a black rectangle in the disk file.

    The modified code produces a black rectangle with a white ellipse, both on screen and in the disk file. Even tho' there is code that should affect the colors.

    I can't figure why the two versions work with such different results.

  4. #4
    Registered User
    Join Date
    Sep 2010
    Posts
    69
    @adeyblue: I couldn't quite figure how to get your example to compile in C.
    I'm not sure I understand the code example itself to be able to play around with it.

    Quote Originally Posted by adeyblue
    So after somebody figures out why it's buggy, at some point you'd need to fill in those RGBQUADs it allocates.
    I was trying to find out just how you might do that, but, other than this reference:
    Quote Originally Posted by platform sdk
    // There is no RGBQUAD array for the 24-bit-per-pixel format.
    there doesn't appear to be (that I can see) any method for doing such.
    Just how do you "fill in those RGBQUADs" ?
    Everything I try doesn't seem to work.
    There doesn't seem to be an example in the SDK of just what the RGBQUAD should look like when it is filled-in, other than:
    Code:
    typedef struct tagRGBQUAD {
      BYTE    rgbBlue; 
      BYTE    rgbGreen; 
      BYTE    rgbRed; 
      BYTE    rgbReserved; 
    } RGBQUAD;

    Also, I'm not so sure it is buggy.
    I think it's more to do with being able to save a BMP created with CreateBitmap().
    I find it peculiar that I can load a bitmap from file (using LoadImage() ) and turn around and re-save it, (with a different name), using the posted code and yet it fails when I save a bitmap produced with CreateBitmap().
    What is different about the two methods ?
    Isn't a bitmap still a bitmap ?

  5. #5
    Officially An Architect brewbuck's Avatar
    Join Date
    Mar 2007
    Location
    Portland, OR
    Posts
    7,396
    You don't seem to really understand what you're doing. Your question has been answered already... You have this 1-bit bitmap. You PatBlt() it with a color onto a memory DC -- the bitmap acquires color at that moment. Then you blit that to the display and you see color.

    However it's still a 1-bit bitmap and has no "color" to it -- when you save this to file you get one and zero bits. You just did some operations while putting it on the screen that caused color to appear on the screen. I'm not sure how you expect to be able to save color in a 1-bit bitmap.
    Code:
    //try
    //{
    	if (a) do { f( b); } while(1);
    	else   do { f(!b); } while(1);
    //}

  6. #6
    Registered User
    Join Date
    Sep 2010
    Posts
    69
    Quote Originally Posted by brewbuck View Post
    You don't seem to really understand what you're doing. Your question has been answered already...
    No need to be rude in your response.
    I fail to see where my question was answered, anywhere.
    And yes, I am completely new to bitmaps, I thought that was clear in my preface.
    I am beginning to get an understanding of them, but, I don't claim to understand them.

    Doesn't it make sense to you that if I knew what I was doing I would already have done it and not posted a question here ?

    You have this 1-bit bitmap. You PatBlt() it with a color onto a memory DC -- the bitmap acquires color at that moment. Then you blit that to the display and you see color.
    Okay, this I get.

    However it's still a 1-bit bitmap and has no "color" to it -- when you save this to file you get one and zero bits. You just did some operations while putting it on the screen that caused color to appear on the screen.
    This I don't get.
    My assumption was that this "operation" was adding color to the bitmap. Which could be copied and/or saved.

    I'm not sure how you expect to be able to save color in a 1-bit bitmap.
    And, I would know that how... ?
    You assume that I know it is a 1-bit bitmap.

    A natural assumption would be that if one can add color to it and make it appear on the screen showing colors, it must not be made up of a single bit.

    How then, one might ask, can one create this bitmap as a multi-bit bitmap ?
    One that can display color and be saved with color.

    Perhaps, since the solution seems so obvious to you, you can offer up a piece of code that might provide a solution to the problem.

    Edit:
    BTW, the code example for MakeBitmap() isn't a creation of my own.
    If it appears stupid (for some reason) so be it.
    It's a code example directly from a text book:
    "Graphics API, Black Book", by: Chandler & Fotsch, ISBN 1-57610-876-7, page 308.
    Last edited by Steve A.; 04-08-2011 at 02:11 PM.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. File transfer- the file sometimes not full transferred
    By shu_fei86 in forum C# Programming
    Replies: 13
    Last Post: 03-13-2009, 12:44 PM
  2. File Writing Problem
    By polskash in forum C Programming
    Replies: 3
    Last Post: 02-13-2009, 10:47 AM
  3. Can we have vector of vector?
    By ketu1 in forum C++ Programming
    Replies: 24
    Last Post: 01-03-2008, 05:02 AM
  4. Inventory records
    By jsbeckton in forum C Programming
    Replies: 23
    Last Post: 06-28-2007, 04:14 AM
  5. what does this mean to you?
    By pkananen in forum C++ Programming
    Replies: 8
    Last Post: 02-04-2002, 03:58 PM