C: 尝试将 BITMAP 转换为 .bmp 文件时出现问题
C: Trouble when trying to comvert HBITMAP into .bmp file
我正在创建一个将屏幕截图保存到 .bmp
文件的功能,我已经设法将屏幕保存到 HBITMAP
,但我在保存时遇到了问题。我将不胜感激。
这里是 header 函数:
#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);
}
这里还有 main.c:
#include "main.h"
int main()
{
getScreen();
return 0;
}
我已经在 Whosebug 中阅读了关于这个主题的所有问题,其中 none 已经为我澄清了问题。
bitmapInfo.bmiHeader.biSize = screenWidth * screenHeight * 3;
biSize
不是总像素数,是位图信息结构的大小,总是sizeof(BITMAPINFOHEADER)
.
您想改为设置 bitmapInfo.bmiHeader.biSizeImage
。这个值大概是 (width * height * bits_per_pixel/8
) 但必须调整,因为每一行都被填充了。也不要将 fopen
句柄与 CreateFile
句柄混合使用,如注释中所述。
fopen("screen.png", "wb");
您无法使用此方法保存为png 格式。对于 png 格式,您需要一个不同的库,例如 Gdiplus。
要保存为位图,您必须设置BITMAFILEHEADER
、BITMAPINFOHEADER
并将它们写入文件,然后写入从GetDIBits
获得的位
您可能希望避免使用 typedef unsigned char u8;
等。您可以包含 <stdint.h>
并使用标准类型 uint8_t
、uint16_t
、uint32_t
等。或者您可以使用标准 Windows 类型,例如 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);
}
我正在创建一个将屏幕截图保存到 .bmp
文件的功能,我已经设法将屏幕保存到 HBITMAP
,但我在保存时遇到了问题。我将不胜感激。
这里是 header 函数:
#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);
}
这里还有 main.c:
#include "main.h"
int main()
{
getScreen();
return 0;
}
我已经在 Whosebug 中阅读了关于这个主题的所有问题,其中 none 已经为我澄清了问题。
bitmapInfo.bmiHeader.biSize = screenWidth * screenHeight * 3;
biSize
不是总像素数,是位图信息结构的大小,总是sizeof(BITMAPINFOHEADER)
.
您想改为设置 bitmapInfo.bmiHeader.biSizeImage
。这个值大概是 (width * height * bits_per_pixel/8
) 但必须调整,因为每一行都被填充了。也不要将 fopen
句柄与 CreateFile
句柄混合使用,如注释中所述。
fopen("screen.png", "wb");
您无法使用此方法保存为png 格式。对于 png 格式,您需要一个不同的库,例如 Gdiplus。
要保存为位图,您必须设置BITMAFILEHEADER
、BITMAPINFOHEADER
并将它们写入文件,然后写入从GetDIBits
您可能希望避免使用 typedef unsigned char u8;
等。您可以包含 <stdint.h>
并使用标准类型 uint8_t
、uint16_t
、uint32_t
等。或者您可以使用标准 Windows 类型,例如 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);
}