Search code examples
clinuxxlib

Can not save screen shot bitmap to file using xlib


I am trying to copy the screen to a bitmap file, I found this write XImage to .bmp file in C thread. I tried the code provided myself, but I can not make the code work. It can save a img file, but won't be able to open it. Anything wrong I am doing here?

#include <iostream>
#include <X11/Xlib.h>

#include <cstring>
#include<stdio.h>

typedef unsigned short  WORD; // 2bytes
typedef unsigned long  DWORD; //4bytes
typedef long LONG;

using namespace std;

void saveXImageToBitmap(XImage *pImage);

int main()
{
        Display *display;
        int screen;
        Window root;
        display = XOpenDisplay(0);
        screen = DefaultScreen(display);
        root = RootWindow(display, screen);
        XImage *img = XGetImage(display,root,0,0,400,400,XAllPlanes(),ZPixmap);

        if (img != NULL)
        {
saveXImageToBitmap(img);
           //save image here
        }
        return 0;
}
#pragma pack(push, 1) 
typedef struct BITMAPFILEHEADER {
WORD    bfType;
DWORD   bfSize;
WORD    bfReserved1;
WORD    bfReserved2;
DWORD   bfOffBits;
};
#pragma pack(pop)
#pragma pack (push,1)
typedef struct BITMAPINFOHEADER{
DWORD  biSize;
LONG   biWidth;
LONG   biHeight;
WORD   biPlanes;
WORD   biBitCount;
DWORD  biCompression;
DWORD  biSizeImage;
LONG   biXPelsPerMeter;
LONG   biYPelsPerMeter;
DWORD  biClrUsed;
DWORD  biClrImportant;
};
#pragma pack(pop)
void saveXImageToBitmap(XImage *pImage)
{
BITMAPFILEHEADER bmpFileHeader;
BITMAPINFOHEADER bmpInfoHeader;
FILE *fp;
static int cnt = 0;
int dummy;
char filePath[255];
memset(&bmpFileHeader, 0, sizeof(BITMAPFILEHEADER));
memset(&bmpInfoHeader, 0, sizeof(BITMAPINFOHEADER));
bmpFileHeader.bfType = 0x4D42;
bmpFileHeader.bfSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) +  pImage->width*pImage->height*4;
bmpFileHeader.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
bmpFileHeader.bfReserved1 = 0;
bmpFileHeader.bfReserved2 = 0;

bmpInfoHeader.biSize = sizeof(BITMAPINFOHEADER);
bmpInfoHeader.biWidth = pImage->width;
bmpInfoHeader.biHeight = pImage->height;
bmpInfoHeader.biPlanes = 1;
bmpInfoHeader.biBitCount = 32;
dummy = (pImage->width * 3) % 4;
if((4-dummy)==4)
    dummy=0;
else
    dummy=4-dummy;

bmpInfoHeader.biSizeImage = ((pImage->width*3)+dummy)*pImage->height;
bmpInfoHeader.biCompression = 0;
bmpInfoHeader.biXPelsPerMeter = 0;
bmpInfoHeader.biYPelsPerMeter = 0;
bmpInfoHeader.biClrUsed = 0;
bmpInfoHeader.biClrImportant = 0;

sprintf(filePath, "bitmap%d.bmp", cnt++);
fp = fopen(filePath,"wb");

if(fp == NULL)
    return;

fwrite(&bmpFileHeader, sizeof(bmpFileHeader), 1, fp);
fwrite(&bmpInfoHeader, sizeof(bmpInfoHeader), 1, fp);
fwrite(pImage->data, 4*pImage->width*pImage->height, 1, fp);
fclose(fp);
}

Solution

  • I figured it out, the struct BITMAPFILEHEADER size should be 14 based on bitmap file format, the types I was using was wrong. please check the revised code for answer

    #include <iostream>
    #include <X11/Xlib.h>
    
    #include <cstring>
    #include<stdio.h>
    
    using namespace std;
    
    void saveXImageToBitmap(XImage *pImage);
    
    int main()
    {
            Display *display;
            int screen;
            Window root;
            display = XOpenDisplay(0);
            screen = DefaultScreen(display);
            root = RootWindow(display, screen);
        XWindowAttributes gwa;
    
       XGetWindowAttributes(display, root, &gwa);
       int width = gwa.width;
       int height = gwa.height;
            XImage *img = XGetImage(display,root,0,0,width,height,XAllPlanes(),ZPixmap);
    
            if (img != NULL)
            {
    saveXImageToBitmap(img);
               //save image here
            }
            return 0;
    }
    
    #pragma pack (1)
    typedef struct BITMAPFILEHEADER 
    {
    short    bfType;
    int    bfSize;
    short    bfReserved1;
    short    bfReserved2;
    int   bfOffBits;
    };
    
    typedef struct BITMAPINFOHEADER
    {
    int  biSize;
    int   biWidth;
    int   biHeight;
    short   biPlanes;
    short   biBitCount;
    int  biCompression;
    int  biSizeImage;
    int   biXPelsPerMeter;
    int   biYPelsPerMeter;
    int  biClrUsed;
    int  biClrImportant;
    };
    
    void saveXImageToBitmap(XImage *pImage)
    {
    BITMAPFILEHEADER bmpFileHeader;
    BITMAPINFOHEADER bmpInfoHeader;
    FILE *fp;
    static int cnt = 0;
    int dummy;
    char filePath[255];
    memset(&bmpFileHeader, 0, sizeof(BITMAPFILEHEADER));
    memset(&bmpInfoHeader, 0, sizeof(BITMAPINFOHEADER));
    bmpFileHeader.bfType = 0x4D42;
    bmpFileHeader.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
    bmpFileHeader.bfReserved1 = 0;
    bmpFileHeader.bfReserved2 = 0;
    int biBitCount =32;
    int dwBmpSize = ((pImage->width * biBitCount + 31) / 32) * 4 * pImage->height;
    printf("size of short:%d\r\n",(int)sizeof(short));
    printf("size of int:%d\r\n",(int)sizeof(int));
    printf("size of long:%d\r\n",(int)sizeof(long));
    printf("dwBmpSize:%d\r\n",(int)dwBmpSize);
    printf("BITMAPFILEHEADER:%d\r\n",(int)sizeof(BITMAPFILEHEADER));
    printf("BITMAPINFOHEADER:%d\r\n",(int)sizeof(BITMAPINFOHEADER));
    bmpFileHeader.bfSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) +  dwBmpSize;
    
    bmpInfoHeader.biSize = sizeof(BITMAPINFOHEADER);
    bmpInfoHeader.biWidth = pImage->width;
    bmpInfoHeader.biHeight = pImage->height;
    bmpInfoHeader.biPlanes = 1;
    bmpInfoHeader.biBitCount = biBitCount;
    bmpInfoHeader.biSizeImage = 0;
    bmpInfoHeader.biCompression = 0;
    bmpInfoHeader.biXPelsPerMeter = 0;
    bmpInfoHeader.biYPelsPerMeter = 0;
    bmpInfoHeader.biClrUsed = 0;
    bmpInfoHeader.biClrImportant = 0;
    
    sprintf(filePath, "bitmap%d.bmp", cnt++);
    fp = fopen(filePath,"wb");
    
    if(fp == NULL)
        return;
    
    fwrite(&bmpFileHeader, sizeof(bmpFileHeader), 1, fp);
    fwrite(&bmpInfoHeader, sizeof(bmpInfoHeader), 1, fp);
    fwrite(pImage->data, dwBmpSize, 1, fp);
    fclose(fp);
    }