Reputation: 13
I am creating a function to save a screenshot into a .bmp
file, I've managed to save the screen into an HBITMAP
but I'm having trouble when saving it. I would appreciate some help.
Here is the header with the function:
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <stdbool.h>
#include <wingdi.h>
#include <winuser.h>
typedef unsigned char u8;
typedef unsigned short u16;
typedef unsigned long u32;
typedef unsigned long long u64;
void getScreen()
{
u16 screenHeight = GetSystemMetrics(SM_CYVIRTUALSCREEN);
u16 screenWidth = GetSystemMetrics(SM_CXVIRTUALSCREEN);
HDC hdc = GetDC(NULL); //get a desktop dc (NULL for entire screen)
HDC hDest = CreateCompatibleDC(hdc); //create a dc for capture
ReleaseDC(NULL, hdc);
HBITMAP hbCapture = CreateCompatibleBitmap(hdc, screenWidth, screenHeight);
SelectObject(hDest, hbCapture);
//Copy screen to bitmap
BitBlt(hDest, 0, 0, screenWidth, screenHeight, hdc, 0, 0, SRCCOPY);
//test
ReleaseDC(NULL, hdc);
char memBuffer[10000];
BITMAPINFO bitmapInfo;
bitmapInfo.bmiHeader.biHeight = screenHeight;
bitmapInfo.bmiHeader.biWidth = screenWidth;
bitmapInfo.bmiHeader.biSize = screenWidth * screenHeight * 3;
bitmapInfo.bmiHeader.biCompression = BI_RGB;//NOT SURE
bitmapInfo.bmiHeader.biPlanes = 1;
bitmapInfo.bmiHeader.biBitCount = 8; //NOT SURE
bitmapInfo.bmiHeader.biClrImportant = 0;
GetDIBits(hDest, hbCapture, 0, screenHeight, &memBuffer, &bitmapInfo, DIB_RGB_COLORS);
FILE * fPointer = fopen("screen.png", "wb");//TO CHANGE
WriteFile(fPointer, &memBuffer, (WORD) sizeof(memBuffer), 0, NULL);
fclose(fPointer);
//test
//Clean up
ReleaseDC(NULL, hdc);
DeleteDC(hDest);
DeleteObject(hbCapture);
}
And here is also the main.c:
#include "main.h"
int main()
{
getScreen();
return 0;
}
I've already read all the questions about this topic in StackOverflow and none of them has clarified the issue for me.
Upvotes: 0
Views: 139
Reputation: 31629
bitmapInfo.bmiHeader.biSize = screenWidth * screenHeight * 3;
biSize
is not the total pixel count, it's the size of bitmap info structure, it's always sizeof(BITMAPINFOHEADER)
.
You want to set bitmapInfo.bmiHeader.biSizeImage
instead. This value is roughly (width * height * bits_per_pixel/8
) but it has to be adjusted because each row is padded. Also don't mix fopen
handle with CreateFile
handle as noted in comments.
fopen("screen.png", "wb");
You cannot save to png format using this method. You need a different library like Gdiplus for png format.
To save as bitmap, you have to setup BITMAFILEHEADER
, BITMAPINFOHEADER
and write them to file, and then write the bits obtained from GetDIBits
You may want to avoid using typedef unsigned char u8;
etc. You can include <stdint.h>
and use standard types uint8_t
, uint16_t
, uint32_t
etc. Or you can use standard Windows types like BYTE
void getScreen()
{
int screenWidth = GetSystemMetrics(SM_CXVIRTUALSCREEN);
int screenHeight = GetSystemMetrics(SM_CYVIRTUALSCREEN);
HDC hdc = GetDC(NULL);
HDC hDest = CreateCompatibleDC(hdc);
HBITMAP hbCapture = CreateCompatibleBitmap(hdc, screenWidth , screenHeight );
HBITMAP oldbmp = (HBITMAP)SelectObject(hDest, hbCapture);
BitBlt(hDest, 0, 0, screenWidth , screenHeight , hdc, 0, 0, SRCCOPY);
WORD bpp = 24; //<- bits per pixel
int size = ((screenWidth * bpp + 31) / 32) * 4 * screenHeight ;
BITMAPINFOHEADER bmp_info_hdr;
bmp_info_hdr.biSize = sizeof(bmp_info_hdr);
bmp_info_hdr.biHeight = screenHeight ;
bmp_info_hdr.biWidth = screenWidth ;
bmp_info_hdr.biPlanes = 1;
bmp_info_hdr.biBitCount = bpp;
bmp_info_hdr.biCompression = BI_RGB;
bmp_info_hdr.biSizeImage = size;
BITMAPFILEHEADER bmp_file_hdr;
bmp_file_hdr.bfType = (WORD)'MB';
bmp_file_hdr.bfOffBits = sizeof(bmp_file_hdr) + sizeof(bmp_info_hdr);
bmp_file_hdr.bfSize = bmp_file_hdr.bfOffBits + size;
char *buffer = malloc(size);
GetDIBits(hDest, hbCapture, 0, screenHeight , buffer,
(BITMAPINFO*)&bmp_info_hdr, DIB_RGB_COLORS);
FILE * fPointer = fopen("screen.bmp", "wb");
fwrite((char*)&bmp_file_hdr, sizeof(bmp_file_hdr), 1, fPointer);
fwrite((char*)&bmp_info_hdr, sizeof(bmp_info_hdr), 1, fPointer);
fwrite(buffer, size, 1, fPointer);
fclose(fPointer);
free(buffer);
SelectObject(hDest, oldbmp);
DeleteObject(hbCapture);
DeleteDC(hDest);
ReleaseDC(NULL, hdc);
}
Upvotes: 0