?
Solved

Saving a bitmap to a file

Posted on 2003-02-20
3
Medium Priority
?
952 Views
Last Modified: 2008-02-01
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
Comment
Question by:boIudazo
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
3 Comments
 
LVL 1

Accepted Solution

by:
scerveza earned 300 total points
ID: 7992350
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
 
LVL 12

Expert Comment

by:Salte
ID: 7992760
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
 

Author Comment

by:boIudazo
ID: 7993630
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

What does it mean to be "Always On"?

Is your cloud always on? With an Always On cloud you won't have to worry about downtime for maintenance or software application code updates, ensuring that your bottom line isn't affected.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

  Included as part of the C++ Standard Template Library (STL) is a collection of generic containers. Each of these containers serves a different purpose and has different pros and cons. It is often difficult to decide which container to use and …
Go is an acronym of golang, is a programming language developed Google in 2007. Go is a new language that is mostly in the C family, with significant input from Pascal/Modula/Oberon family. Hence Go arisen as low-level language with fast compilation…
The viewer will learn how to user default arguments when defining functions. This method of defining functions will be contrasted with the non-default-argument of defining functions.
The viewer will learn additional member functions of the vector class. Specifically, the capacity and swap member functions will be introduced.

770 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question