Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

Categories

Easiest way to save the contents of HDC to bitmap file?

In c/c++ can someone tell me an easy way to save the contents of a windows HDC to a bitmap file? I don't really know much about doing this and all i managed to find on google was Visual Basic stuff so i was hoping maybe i could get some help here?

Will i have to setup the bitmap headers then run a loop calling GetPixel with my hdc to get each of the pixel colour values then write them to the file or is there an easier way?

Thanks!

..Some day i will finish this game..
«1

Comments

  • AsmGuru62AsmGuru62 Member Posts: 6,519
    : In c/c++ can someone tell me an easy way to save the contents of a windows HDC to a bitmap file? I don't really know much about doing this and all i managed to find on google was Visual Basic stuff so i was hoping maybe i could get some help here?
    :
    : Will i have to setup the bitmap headers then run a loop calling GetPixel with my hdc to get each of the pixel colour values then write them to the file or is there an easier way?
    :
    : Thanks!
    :
    : ..Some day i will finish this game..
    :
    [blue]On my site:

    http://www.codexxi.com/MyBlocks.html

    There is a code sample called: "Printing Full Screen" - I believe it has a method to copy the HDC into an HBITMAP, but after that you have to write this HBITMAP to disk using Windows GDI:

    http://msdn.microsoft.com/library/default.asp?url=/library/en-us/gdi/bitmaps_87eb.asp

    See there ^^^ GetDIBits()...

    And here is the BMP format explanations:

    http://msdn.microsoft.com/library/default.asp?url=/library/en-us/gdi/bitmaps_4v1h.asp
    [/blue]
  • DarknezzDarknezz Member Posts: 245
    Thanks, i think i have the code done to copy the HDC to a HBITMAP now. I found some information on saving HBITMAPs to bitmap files but when i make the call to GetDIBits() it returns 0 for error.

    Here is my function up to the point where it gets an error from GetDIBits and returns. Sorry if it looks a bit lengthy but it's nicely formatted and the comments are in green so they're easier to see so hopefully it won't be that bad :)

    Someone please take a quick look and try to tell me where i've gone wrong? Thanks so much :)

    [code]
    [green]//---------------------------------------------------------
    // Name: SaveFont()
    // Desc: Saves the current bitmap font.
    // Retn: void.
    //---------------------------------------------------------[/green]
    void SaveFont(void)
    {
    ofstream outFile; [green]//used to write the data to the file[/green]
    OPENFILENAME ofn; [green]//used to open a save file common dialog box.[/green]
    char cFilename[MAX_PATH]; [green]//buffer to get the filename to save as[/green]
    HDC windc; [green]//used to get the windows dc[/green]
    HDC cdc; [green]//used to create a compatible dc[/green]
    HBITMAP cbm; [green]//used to create a compatible bitmap[/green]
    HBITMAP oldbm; [green]//used to replace the old bitmap onto the compatible dc[/green]
    BITMAP bm; [green]//used to get info about the compatible bitmap[/green]
    BITMAPINFOHEADER bmih; [green]//the bitmap info header[/green]
    BITMAPFILEHEADER bmfh; [green]//the bitmap file header[/green]
    BITMAPINFO bmi; [green]//used to get the bitmap bits[/green]
    UCHAR *bmData = NULL; [green]//pointer to memory to recieve the bitmap bits[/green]

    [green]//-----------------
    // Make sure we can run the function
    //-----------------[/green]
    if(bFontChosen == false)
    return; [green]//no font chosen[/green]

    [green]//-----------------
    // Get a filename
    //-----------------
    //setup the OPENFILENAME struct[/green]
    ZeroMemory(cFilename, MAX_PATH);
    ZeroMemory(&ofn, sizeof(OPENFILENAME));
    ofn.lStructSize = sizeof(OPENFILENAME);
    ofn.hwndOwner = hwnd_main;
    ofn.lpstrFilter = "Frogger Font Files (*.fft)*.fftAll Files*.*";
    ofn.nFilterIndex = 0;
    ofn.lpstrFile = cFilename;
    ofn.nMaxFile = MAX_PATH;
    ofn.lpstrTitle = "Save Frogger Font";
    ofn.Flags = OFN_OVERWRITEPROMPT;

    [green]//get a filename to save as[/green]
    if(GetSaveFileName(&ofn) == false)
    return; [green]//no filename chosen[/green]

    [green]//-----------------
    // Create compatible HDC/HBITMAP
    //-----------------
    //get the windows dc[/green]
    windc = GetDC(hwnd_main);
    if(!windc)
    return; [green]//couldn't get the windows dc[/green]

    [green]//create a compatible dc[/green]
    cdc = CreateCompatibleDC(windc);
    if(!cdc)
    {
    ReleaseDC(hwnd_main, windc);
    return; [green]//couldn't create a compatible dc[/green]
    }

    [green]//create a compatible bitmap[/green]
    cbm = CreateCompatibleBitmap(windc, 8 * ffHeader.iCellWidth, 8 * ffHeader.iCellHeight);
    if(!cbm)
    {
    DeleteDC(cdc);
    ReleaseDC(hwnd_main, windc);
    return; [green]//couldn't create a compatible bitmap[/green]
    }

    [green]//select the bitmap onto the compatible dc and save the old bitmap[/green]
    oldbm = (HBITMAP)SelectObject(cdc, cbm);

    [green]//bitblt from the windows dc to the compatible dc[/green]
    BitBlt(cdc, 0, 0, 8 * ffHeader.iCellWidth, 8 * ffHeader.iCellHeight, windc, 0, 0, SRCCOPY);

    [green]//-----------------
    // Get bitmap information
    //-----------------[/green]
    GetObject(cbm, sizeof(BITMAP), (void*)&bm);

    [green]//-----------------
    // Setup the bitmap headers
    //-----------------
    //Setup the bitmap file header[/green]
    bmfh.bfOffBits = 54; [green]//same for all 24-bit bitmaps[/green]
    bmfh.bfReserved1 = 0; [green]//reserved, set to 0[/green]
    bmfh.bfReserved2 = 0; [green]//again, reserved, set to 0[/green]
    bmfh.bfSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + (3 * (bm.bmHeight * bm.bmWidth)); [green]//size of the bitmap file[/green]
    bmfh.bfType = 0x4D42; [green]//'BM', the global bitmap ID[/green]

    [green]//Setup the bitmap info header[/green]
    bmih.biBitCount = 24; [green]//24bpp[/green]
    bmih.biClrImportant = 0; [green]//number of important colout table entries[/green]
    bmih.biClrUsed = 0; [green]//number of colour table entries that are used[/green]
    bmih.biCompression = 0; [green]//no compression[/green]
    bmih.biHeight = bm.bmHeight; [green]//the height of the bitmap[/green]
    bmih.biPlanes = bm.bmPlanes; [green]//the number of planes in the bitmap[/green]
    bmih.biSize = sizeof(BITMAPINFOHEADER); [green]//the size of this header[/green]
    bmih.biSizeImage = 3 * (bm.bmHeight * bm.bmWidth); [green]//the size of the image data[/green]
    bmih.biWidth = bm.bmWidth; [green]//the width of the bitmap[/green]
    bmih.biXPelsPerMeter = 0; [green]//pixels per meter along the x axis, not required[/green]
    bmih.biYPelsPerMeter = 0; [green]//pixels per meter along the y axis, not required[/green]

    [green]//-----------------
    // Get the bitmap pixel data
    //-----------------
    //replace the old bitmap onto the dc[/green]
    SelectObject(cdc, oldbm);

    [green]//allocate memory[/green]
    bmData = (UCHAR*)malloc(3 * (bm.bmHeight * bm.bmWidth));

    [green]//get the pixel data[/green]
    bmi.bmiHeader = bmih; [green]//copy the bitmap info header[/green]
    if(GetDIBits(cdc, cbm, 0, bmih.biHeight, (void*)&bmData, &bmi, DIB_RGB_COLORS) == 0)
    {
    [green]/*
    Error here, GetDIBits fails.
    */[/green]
    free(bmData);
    DeleteObject(cbm);
    DeleteDC(cdc);
    ReleaseDC(hwnd_main, windc);
    return; [green]//couldn't get the bitmap bits[/green]
    }

    [green]//Function Truncated as it does not run this far so the problem cannot lie down here :)[/green]
    return;
    }
    [/code]

    Thanks!
  • AsmGuru62AsmGuru62 Member Posts: 6,519
    [b][red]This message was edited by AsmGuru62 at 2003-11-21 11:6:33[/red][/b][hr]
    : Thanks, i think i have the code done to copy the HDC to a HBITMAP now. I found some information on saving HBITMAPs to bitmap files but when i make the call to GetDIBits() it returns 0 for error.
    :
    : Here is my function up to the point where it gets an error from GetDIBits and returns. Sorry if it looks a bit lengthy but it's nicely formatted and the comments are in green so they're easier to see so hopefully it won't be that bad :)
    :
    : Someone please take a quick look and try to tell me where i've gone wrong? Thanks so much :)
    :
    : [code]
    : [green]//---------------------------------------------------------
    : // Name: SaveFont()
    : // Desc: Saves the current bitmap font.
    : // Retn: void.
    : //---------------------------------------------------------[/green]
    : void SaveFont(void)
    : {
    : ofstream outFile; [green]//used to write the data to the file[/green]
    : OPENFILENAME ofn; [green]//used to open a save file common dialog box.[/green]
    : char cFilename[MAX_PATH]; [green]//buffer to get the filename to save as[/green]
    : HDC windc; [green]//used to get the windows dc[/green]
    : HDC cdc; [green]//used to create a compatible dc[/green]
    : HBITMAP cbm; [green]//used to create a compatible bitmap[/green]
    : HBITMAP oldbm; [green]//used to replace the old bitmap onto the compatible dc[/green]
    : BITMAP bm; [green]//used to get info about the compatible bitmap[/green]
    : BITMAPINFOHEADER bmih; [green]//the bitmap info header[/green]
    : BITMAPFILEHEADER bmfh; [green]//the bitmap file header[/green]
    : BITMAPINFO bmi; [green]//used to get the bitmap bits[/green]
    : UCHAR *bmData = NULL; [green]//pointer to memory to recieve the bitmap bits[/green]
    :
    : [green]//-----------------
    : // Make sure we can run the function
    : //-----------------[/green]
    : if(bFontChosen == false)
    : return; [green]//no font chosen[/green]
    :
    : [green]//-----------------
    : // Get a filename
    : //-----------------
    : //setup the OPENFILENAME struct[/green]
    : ZeroMemory(cFilename, MAX_PATH);
    : ZeroMemory(&ofn, sizeof(OPENFILENAME));
    : ofn.lStructSize = sizeof(OPENFILENAME);
    : ofn.hwndOwner = hwnd_main;
    : ofn.lpstrFilter = "Frogger Font Files (*.fft)*.fftAll Files*.*";
    : ofn.nFilterIndex = 0;
    : ofn.lpstrFile = cFilename;
    : ofn.nMaxFile = MAX_PATH;
    : ofn.lpstrTitle = "Save Frogger Font";
    : ofn.Flags = OFN_OVERWRITEPROMPT;
    :
    : [green]//get a filename to save as[/green]
    : if(GetSaveFileName(&ofn) == false)
    : return; [green]//no filename chosen[/green]
    :
    : [green]//-----------------
    : // Create compatible HDC/HBITMAP
    : //-----------------
    : //get the windows dc[/green]
    : windc = GetDC(hwnd_main);
    : if(!windc)
    : return; [green]//couldn't get the windows dc[/green]
    :
    : [green]//create a compatible dc[/green]
    : cdc = CreateCompatibleDC(windc);
    : if(!cdc)
    : {
    : ReleaseDC(hwnd_main, windc);
    : return; [green]//couldn't create a compatible dc[/green]
    : }
    :
    : [green]//create a compatible bitmap[/green]
    : cbm = CreateCompatibleBitmap(windc, 8 * ffHeader.iCellWidth, 8 * ffHeader.iCellHeight);
    : if(!cbm)
    : {
    : DeleteDC(cdc);
    : ReleaseDC(hwnd_main, windc);
    : return; [green]//couldn't create a compatible bitmap[/green]
    : }
    :
    : [green]//select the bitmap onto the compatible dc and save the old bitmap[/green]
    : oldbm = (HBITMAP)SelectObject(cdc, cbm);
    :
    : [green]//bitblt from the windows dc to the compatible dc[/green]
    : BitBlt(cdc, 0, 0, 8 * ffHeader.iCellWidth, 8 * ffHeader.iCellHeight, windc, 0, 0, SRCCOPY);
    :
    : [green]//-----------------
    : // Get bitmap information
    : //-----------------[/green]
    : GetObject(cbm, sizeof(BITMAP), (void*)&bm);
    :
    : [green]//-----------------
    : // Setup the bitmap headers
    : //-----------------
    : //Setup the bitmap file header[/green]
    : bmfh.bfOffBits = 54; [green]//same for all 24-bit bitmaps[/green]
    : bmfh.bfReserved1 = 0; [green]//reserved, set to 0[/green]
    : bmfh.bfReserved2 = 0; [green]//again, reserved, set to 0[/green]
    : bmfh.bfSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + (3 * (bm.bmHeight * bm.bmWidth)); [green]//size of the bitmap file[/green]
    : bmfh.bfType = 0x4D42; [green]//'BM', the global bitmap ID[/green]
    :
    : [green]//Setup the bitmap info header[/green]
    : bmih.biBitCount = 24; [green]//24bpp[/green]
    : bmih.biClrImportant = 0; [green]//number of important colout table entries[/green]
    : bmih.biClrUsed = 0; [green]//number of colour table entries that are used[/green]
    : bmih.biCompression = 0; [green]//no compression[/green]
    : bmih.biHeight = bm.bmHeight; [green]//the height of the bitmap[/green]
    : bmih.biPlanes = bm.bmPlanes; [green]//the number of planes in the bitmap[/green]
    : bmih.biSize = sizeof(BITMAPINFOHEADER); [green]//the size of this header[/green]
    : bmih.biSizeImage = 3 * (bm.bmHeight * bm.bmWidth); [green]//the size of the image data[/green]
    : bmih.biWidth = bm.bmWidth; [green]//the width of the bitmap[/green]
    : bmih.biXPelsPerMeter = 0; [green]//pixels per meter along the x axis, not required[/green]
    : bmih.biYPelsPerMeter = 0; [green]//pixels per meter along the y axis, not required[/green]
    :
    : [green]//-----------------
    : // Get the bitmap pixel data
    : //-----------------
    : //replace the old bitmap onto the dc[/green]
    : SelectObject(cdc, oldbm); [red][b]// < move this immediately after BitBlt()[/b][/red]
    :
    : [green]//allocate memory[/green]
    : bmData = (UCHAR*)malloc(3 * (bm.bmHeight * bm.bmWidth));
    :
    : [green]//get the pixel data[/green]
    : bmi.bmiHeader = bmih; [green]//copy the bitmap info header[/green]
    : if(GetDIBits(cdc, cbm, 0, bmih.biHeight, (void*)&bmData, &bmi, DIB_RGB_COLORS) == 0)
    : {
    : [green]/*
    : Error here, GetDIBits fails.
    : */[/green]
    : free(bmData);
    : DeleteObject(cbm);
    : DeleteDC(cdc);
    : ReleaseDC(hwnd_main, windc);
    : return; [green]//couldn't get the bitmap bits[/green]
    : }
    :
    : [green]//Function Truncated as it does not run this far so the problem cannot lie down here :)[/green]
    : return; [red][b]// Where is all free(),DeleteDC(),ReleaseDC()?[/b][/red]
    : }
    : [/code]
    :
    : Thanks!
    :
    [blue]A few things... see RED. Also, bitmap data (RGB triplets) are supposed to be padded up to 4 bytes. Every bitmap row is padded. So, your evaluation of size: "3 * width" is incorrect - it should include the padding. The funny thing is that I was just digging it out yesterday...
    [/blue][code]
    int iWidthPixels, iHeightPixels; // Have these ready...
    int iRawDataBytes = 3 * iWidthPixels;
    int iPadding = 0;

    if ((iRawDataBytes % 4) > 0) {
    iPadding = 4 - (iRawDataBytes % 4);
    }

    int iRowSizeWithPadding = iPadding + iRawDataBytes;
    int iFullBmpDataSize = iRowSizeWithPadding * iHeightPixels;
    int iFulBmpFileSize = iFullBmpDataSize + sizeof (/*both headers*/);
    [/code]
    [blue]Maybe, your code has more issues, but for now - fix these...

    Also, 'returns' in the middle of function is a bad style - very easy to cause resource leaks... use nested IFs, like so:
    [code]
    if () {
    if () {
    if () {
    //...
    }
    }
    }
    [/code]Or use WHILE(), like so:
    [code]
    // reset all resources, to NULL...

    while (1) {
    if (...failed... #1) {
    break;
    }
    if (...failed... #2) {
    break;
    }
    if (...failed... #3) {
    break;
    }
    // nothing failed...
    break;
    }

    // Release all resources, if some where allocated
    // Almost all functions do nothing on NULL, so it is safe
    // to call: "free (NULL); or DeleteDC (NULL);"
    return; // Only one return statement per function.
    [/code]
    [/blue]


  • DarknezzDarknezz Member Posts: 245
    As in the last comment, the function is truncated. I do have free()/ReleaseDC()/etc calls after i have finished with everything but i probably should of mentioned that :)

    Also i was always told that embedding lots of if statements how you said was bad coding style :P I've always returned in the middle of functions if something goes wrong and yes i do sometimes forget to free up resources etc but i think it looks nicer :)

    I moved the line that replaces the old HBITMAP onto the HDC to right after the call to BitBlt() but it didn't make a difference. As for the padding stuff, that's not my problem as far as i know.

    24bit bitmap files contain 3 bytes of data per pixel (8bits * 3 = 24bits). To be 100% sure on this i did 2 things..
    1) I stepped through the code up to the part where it calculates the size of the bitmap file. At this point i created a bitmap in windows paint with the same dimensions as the one my program was working with and saved it, the file size was the same as what my program calculates using 3 bytes per pixel.
    2) I allocated 4 bytes of memory per pixel before calling GetDIBBits() and i also set the image size to allow 4 bytes per pixel and tested with different combinations but nothing worked.

    Thanks for helping! :)
  • AsmGuru62AsmGuru62 Member Posts: 6,519
    : As in the last comment, the function is truncated. I do have free()/ReleaseDC()/etc calls after i have finished with everything but i probably should of mentioned that :)
    :
    : Also i was always told that embedding lots of if statements how you said was bad coding style :P I've always returned in the middle of functions if something goes wrong and yes i do sometimes forget to free up resources etc but i think it looks nicer :)
    :
    : I moved the line that replaces the old HBITMAP onto the HDC to right after the call to BitBlt() but it didn't make a difference. As for the padding stuff, that's not my problem as far as i know.
    :
    : 24bit bitmap files contain 3 bytes of data per pixel (8bits * 3 = 24bits). To be 100% sure on this i did 2 things..
    : 1) I stepped through the code up to the part where it calculates the size of the bitmap file. At this point i created a bitmap in windows paint with the same dimensions as the one my program was working with and saved it, the file size was the same as what my program calculates using 3 bytes per pixel.
    : 2) I allocated 4 bytes of memory per pixel before calling GetDIBBits() and i also set the image size to allow 4 bytes per pixel and tested with different combinations but nothing worked.
    :
    : Thanks for helping! :)
    :
    [blue]...that's strange - you know, I have a site where I have some code pieces - that is a good research project I can do and post it on my site. It is a weekend now, so I have some spare time for coding. I will try to save a bitmap before Monday... not guaranteeing it, but still - it may be a good piece of code to post on my site...

    You right about the styles - one can argue that his style is better... but the coding style should allow only one place to modify the same code and with a lot of returns you have more places, so you can forget to modify some point and you have a leak... but whatever... let everyone work in their own style...

    Cheers!
    [/blue]
  • DarknezzDarknezz Member Posts: 245
    Thanks dude, good luck :) I'll still be trying to work it out so if i find anything out i'll post here :)

  • AsmGuru62AsmGuru62 Member Posts: 6,519
    : Thanks dude, good luck :) I'll still be trying to work it out so if i find anything out i'll post here :)
    :
    :
    [blue]It is done. I will post the code on my site Monday. Can do it only from work...[/blue]

  • DarknezzDarknezz Member Posts: 245
    Dude, thanks so much! :)

    I didn't really manage to find anything else out cause i started downloading the DirectX9 SDK and started reading through the DirectX8 help files to start learning Direct3D (finally!).

    I'll continue with my D3D learning project for tonight then tommorow i'll take a look at your code and fix mine then get my game finished and out of the way so i can get some other stuff done :)

    Thanks again for your help :)

  • AsmGuru62AsmGuru62 Member Posts: 6,519
    : Dude, thanks so much! :)
    :
    : I didn't really manage to find anything else out cause i started downloading the DirectX9 SDK and started reading through the DirectX8 help files to start learning Direct3D (finally!).
    :
    : I'll continue with my D3D learning project for tonight then tommorow i'll take a look at your code and fix mine then get my game finished and out of the way so i can get some other stuff done :)
    :
    : Thanks again for your help :)
    :
    :
    [blue]It is posted on my site: www.codexxi.com -> Components[/blue]
  • DarknezzDarknezz Member Posts: 245
    Thanks, i've just finished the program i was making so now i can carry on writing my game :)

«1
Sign In or Register to comment.