• Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 993
  • Last Modified:

Saving a bitmap to a file

Hi guys. I haven't had any experience working with images before so this has been tough to me. The only thing I want is to save the image of a the current window into a bmp file. I have been working on this with Dan Rollin's help but I got stucked. Since I am working with Borland C++ compiler and I can't use MFC library this has been tougher.
The program gets compiled but what I get is a file in black (instead of having the window image). I've been going over and over the code for 2 days without success (as a matter of fact i am newbie in c++ as well).
I am almoast sure that the mistake should be when I want to get the DIB from the DDB.
Here is the code, and I would thank very very much if you can take a look at it and tell me where is the mistake.
Thanks in advance. Here it is the entire code:

#include <tchar.h>
#include <windows.h>
#include <iostream.h>
#include <stdio.h>



int main()
{
HDC hdc, hMemDC;
RECT dimension;
HWND hwnd_ventana, hw;
SIZE size;
char buf[1024];
LPCTSTR tit_ventana;
char arch[]="c:\\dddrrrr.bmp";
UINT width;
UINT height;

       hw = GetForegroundWindow();                              //This is to get a handle
       GetWindowText(hw,buf,1023);
       tit_ventana = buf;

if ( (hwnd_ventana = FindWindow(NULL,tit_ventana)) == NULL)
        exit(1);

if ( (hdc = GetWindowDC(hwnd_ventana)) == NULL )               //Now we get the form's DC
        exit(1);

if ( (hMemDC = CreateCompatibleDC(hdc)) == NULL)
        exit(1);

if ( (GetWindowRect(hwnd_ventana,&dimension)) == NULL)        //We get the form size
        exit(1);

size.cx = dimension.right-dimension.left;
size.cy = dimension.bottom-dimension.top;
width = size.cx;
height = size.cy;


HBITMAP bitmap = CreateCompatibleBitmap(hdc, size.cx, size.cy);

//HERE, BEGINS THE PART WHERE I SUPPOSE THE MISTAKE IS.


     BITMAPINFOHEADER header;
      header.biSize=sizeof(BITMAPINFOHEADER);
     header.biWidth=width;
     header.biHeight=height;
     header.biPlanes=1;
     header.biBitCount= 24;
     header.biCompression=BI_RGB;
     header.biSizeImage=0;
     header.biXPelsPerMeter=0;
     header.biYPelsPerMeter=0;
     header.biClrUsed=0;
     header.biClrImportant=0;


 // Inicializar the palette
 HPALETTE hPal;
 if ( (hPal = (HPALETTE) GetStockObject(DEFAULT_PALETTE) )  == NULL) printf("no agarro la paleta");


// Compute the size of the  infoheader and the color table

   DWORD dwLen;
   int nColors = (1 << header.biBitCount);
     if( nColors > 256 )
          nColors = 0;
     dwLen  = header.biSize + nColors * sizeof(RGBQUAD);


      // We need a device context to get the DIB from            

       HDC hDC = GetDC(NULL);
     hPal = SelectPalette(hDC,hPal,FALSE);
     RealizePalette(hDC);


     // Allocate enough memory to hold bitmapinfoheader and color table
   HANDLE     hDIB;
     hDIB = GlobalAlloc(GMEM_FIXED,dwLen);
   if (!hDIB){
          SelectPalette(hDC,hPal,FALSE);
          ReleaseDC(NULL,hDC);
            return NULL;
     }


   LPBITMAPINFOHEADER      lpbi;
   lpbi = (LPBITMAPINFOHEADER)hDIB;

     *lpbi = header;

  // Call GetDIBits with a NULL lpBits param, so the device driver
     // will calculate the biSizeImage field
    GetDIBits(hDC, (HBITMAP)bitmap, 0L, (DWORD)header.biHeight,
               (LPBYTE)NULL, (LPBITMAPINFO)lpbi, (DWORD)DIB_RGB_COLORS);


     header = *lpbi;


     // If the driver did not fill in the biSizeImage field, then compute it
     // Each scan line of the image is aligned on a DWORD (32bit) boundary
     if (header.biSizeImage == 0){
          header.biSizeImage = ((((header.biWidth * header.biBitCount) + 31) & ~31) / 8)
                              * header.biHeight;}


        // Realloc the buffer so that it can hold all the bits
   HANDLE handle;
     dwLen += header.biSizeImage;
     if (handle = GlobalReAlloc(hDIB, dwLen, GMEM_MOVEABLE))
          hDIB = handle;
     else{
          GlobalFree(hDIB);
                }


             // Get the bitmap bits
     lpbi = (LPBITMAPINFOHEADER)hDIB;

     // FINALLY get the DIB
BOOL bGotBits = GetDIBits( hDC,bitmap,
                    0L,                    // Start scan line
                    (DWORD)header.biHeight,          // # of scan lines
                    (LPBYTE)lpbi                // address for bitmap bits
                    + (header.biSize + nColors * sizeof(RGBQUAD)),
                    (LPBITMAPINFO)lpbi,          // address of bitmapinfo
                    (DWORD)DIB_RGB_COLORS);          // Use RGB for color table

      if( !bGotBits )
     {
          GlobalFree(hDIB);
       SelectPalette(hDC,hPal,FALSE);
       ReleaseDC(NULL,hDC);
          return NULL;
     }


     ReleaseDC(NULL,hDC);


  //The DIB is ready, now i save it to the file

   BITMAPFILEHEADER     hdr;

        // Fill in the fields of the file header
     hdr.bfType          = ((WORD) ('M' << 8) | 'B');     // is always "BM"
     hdr.bfSize          = GlobalSize (hDIB) + sizeof( hdr );
     hdr.bfReserved1      = 0;
     hdr.bfReserved2      = 0;
     hdr.bfOffBits          = (DWORD) (sizeof( hdr ) + lpbi->biSize +
                              nColors * sizeof(RGBQUAD));

  FILE* outFile;
  int nActual;

// TBD: add error checks on each fn call

  outFile= fopen( arch, "wb+" ); if (outFile == NULL) printf("no se abre esta mierda");
  nActual= fwrite(&hdr, sizeof(hdr), 1, outFile );
  nActual= fwrite( lpbi, GlobalSize(hDIB), 1, outFile );
  fclose(outFile);





return 0;
}
0
boIudazo
Asked:
boIudazo
1 Solution
 
scervezaCommented:
dude that's too much code.
you are making more complicated than it really is.
try this (it takes a screen cap of the desktop and
saves it to a file, change the HWND capture parameter
to take a cap of another window):

#include<fstream>
#include<windows.h>
using namespace std;

void main()
{
 // get desktop window (but can be any window)
 HWND capture = GetDesktopWindow();
 if(!IsWindow(capture)) return;

 // get window dimensions
 RECT rect;
 GetWindowRect(capture, &rect);

 size_t dx = rect.right - rect.left;
 size_t dy = rect.bottom - rect.top;

 // create BITMAPINFO structure
 // used by CreateDIBSection
 BITMAPINFO info;
 info.bmiHeader.biSize          = sizeof(BITMAPINFOHEADER);
 info.bmiHeader.biWidth         = dx;
 info.bmiHeader.biHeight        = dy;
 info.bmiHeader.biPlanes        = 1;
 info.bmiHeader.biBitCount      = 24;
 info.bmiHeader.biCompression   = BI_RGB;
 info.bmiHeader.biSizeImage     = 0;
 info.bmiHeader.biXPelsPerMeter = 0;
 info.bmiHeader.biYPelsPerMeter = 0;
 info.bmiHeader.biClrUsed       = 0;
 info.bmiHeader.biClrImportant  = 0;

 // a bitmap handle and a pointer its bit data
 HBITMAP bitmap = 0;
 BYTE*   memory = 0;

 // create bitmap
 HDC device = GetDC(capture);
 bitmap = CreateDIBSection(device, &info, DIB_RGB_COLORS, (void**)&memory, 0, 0);
 ReleaseDC(capture, device);
 if(!bitmap || !memory) return;
 
 // blit the contents of the desktop (winDC)
 // to the bitmap (selected in memDC)
 HDC winDC = GetWindowDC(capture);
 HDC memDC = CreateCompatibleDC(winDC);
 SelectObject(memDC, bitmap);
 BitBlt(memDC, 0, 0, dx, dy, winDC, 0, 0, SRCCOPY);
 DeleteDC(memDC);
 ReleaseDC(capture, winDC);

 // create bitmap file
 basic_ofstream<char> file("desktop.bmp", ios::binary);
 if(!file) { DeleteObject(bitmap); return; }

 // initialize bitmap file headers
 BITMAPFILEHEADER fileHeader;
 BITMAPINFOHEADER infoHeader;

 fileHeader.bfType      = 0x4d42;
 fileHeader.bfSize      = 0;
 fileHeader.bfReserved1 = 0;
 fileHeader.bfReserved2 = 0;
 fileHeader.bfOffBits   = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);

 infoHeader.biSize          = sizeof(infoHeader);
 infoHeader.biWidth         = dx;
 infoHeader.biHeight        = dy;
 infoHeader.biPlanes        = 1;
 infoHeader.biBitCount      = 24;
 infoHeader.biCompression   = BI_RGB;
 infoHeader.biSizeImage     = 0;
 infoHeader.biXPelsPerMeter = 0;
 infoHeader.biYPelsPerMeter = 0;
 infoHeader.biClrUsed       = 0;
 infoHeader.biClrImportant  = 0;

 // save file headers
 file.write((char*)&fileHeader, sizeof(fileHeader));
 file.write((char*)&infoHeader, sizeof(infoHeader));

 // save 24-bit bitmap data
 int wbytes = (((24*dx + 31) & (~31))/8);
 int tbytes = (((24*dx + 31) & (~31))/8)*dy;
 file.write((char*)memory, tbytes);

 // delete bitmap
 DeleteObject(bitmap);
 bitmap = 0;
 memory = 0;
}
0
 
SalteCommented:
I didn't read all your code but I noticed one odd thing:

you have 24 bits in bitcount which - if I remember correctly - is the number of bits per pixel and then you define a palette.

The 24 bits per pixel format doesn't use a palette, so dump that palette.

And although I didn't read scerveza's answer completely I agree with him that you appear to do things more complicated than you have to.

Windows have a fairly standard way of dumping a window as a bitmap. The point is that internally in windows itself, all windows - text or images or what-have-you - are saved as bitmap images. This is because this is how they are dumped to the screen which is a pixel per pixel graphic display. So dumping a window as a bitmap image is fairly standard and should therefore be fairly easy.

I am pretty sure that there's a function that can get you a HBITMAP from a window more or less autmatically and from a HBITMAP to a bitmap file the steps are fairly simple and easy, essentialy you have to get a BITMAPINFOHEADER pointer and once you got that all you have to do is write a bitmap file header first to the file and then dump out the bitmapinfoheader and all the data that follows after it without having to worry about palette or pixels or anything.

Alf
0
 
boIudazoAuthor Commented:
TTTTTTTTHHHHHHHHHHHAAAAAAAAAANNNNNNNNNNNKKKKKKKKK YYYYYYYYYYYYOOOOOOOOOUUUUUUUUUUUUU!!!!!!!!!!!!!!

I saw your code and I couldn't believe it was so simple!! There were a couple of things that I didn't fully understood (like the palette thing, the colours), but I had been getting familiar with the main structures and functions. I was more than happy when I saw that your code worked; and at a first glance I couldn't spot where my mistake was. Now i will analize it deeply.
But, aaAAhhhhhhhh what a relief. I am in a kind of hurry with this, as you can see. But I don't want to repeat myself all the time. So:

for (i=0;i<10000000000000;i++)
{
THANK YOU
}


0

Featured Post

Become an Android App Developer

Ready to kick start your career in 2018? Learn how to build an Android app in January’s Course of the Month and open the door to new opportunities.

Tackle projects and never again get stuck behind a technical roadblock.
Join Now