How to grab raw buffer from bitmap in GDI? - c

If I understand GDI correctly then the function GetDIBits retreives the raw pixel data from a captured bitmap image in windows
The GetDIBits function retrieves the bits of the specified compatible bitmap and copies them into a buffer as a DIB using the specified format.
so the following code should grab the pixel data only
HDC srcd , memdc;
HBITMAP hbmp;
BITMAP bmp;
DWORD dwBytesWritten = 0;
DWORD dwSizeofDIB = 0;
HANDLE hFile = NULL;
char* lpbitmap = NULL;
HANDLE hDIB = NULL;
DWORD dwBmpSize = 0;
BITMAPFILEHEADER bmfHeader;
BITMAPINFOHEADER bi;
int Height = GetSystemMetrics(SM_CYSCREEN);
int Width = GetSystemMetrics(SM_CXSCREEN);
srcd = GetDC(NULL);
if (!srcd)
printf("failed to obtain screen");
memdc = CreateCompatibleDC(srcd); // creates memory device context
hbmp = CreateCompatibleBitmap(srcd, Width, Height);
SelectObject(memdc, hbmp);
BitBlt(memdc, 0, 0, Width, Height, srcd, 0, 0, SRCCOPY);
BITMAP bmpScreen;
GetObject(hbmp, sizeof(bmpScreen), (LPVOID)&bmpScreen);
bi.biSize = sizeof(BITMAPINFOHEADER);
bi.biWidth = bmpScreen.bmWidth;
bi.biHeight = bmpScreen.bmHeight;
bi.biPlanes = 1;
bi.biBitCount = 32;
bi.biCompression = BI_RGB;
bi.biSizeImage = 0;
bi.biXPelsPerMeter = 0;
bi.biYPelsPerMeter = 0;
bi.biClrUsed = 0;
bi.biClrImportant = 0;
dwBmpSize = ((bmpScreen.bmWidth * bi.biBitCount + 31) / 32) * 4 * bmpScreen.bmHeight;
hDIB = GlobalAlloc(GHND, dwBmpSize);
lpbitmap = (char*)GlobalLock(hDIB);
GetDIBits(memdc, hbmp, 0,
(UINT)bmpScreen.bmHeight,
lpbitmap,
(BITMAPINFO*)&bi, DIB_RGB_COLORS);
printf("dwBmpSize: %ld\n", dwBmpSize);
printf("Strlen(lpbitmap): %lld\n", strlen(lpbitmap));
In my case I have a single screen with size (1920,1080) with a 32 bit value for each pixel then I should have around 66 mega bits but the output that I get does not make sense
dwBmpSize: 8294400
Strlen(lpbitmap): 103614
with that said I have couple of confusions:
Why does strlen return this small number
Why does the lpbitmap buffer contain garbage values (mostly A letters)

Why does strlen return this small number?
The strlen function is for textual strings, not bitmap data. This is binary data. The strlen simply reports the offset of where the first 0 value is found (usually the string terminator).
Why does the lpbitmap buffer contain garbage values (mostly A letters)?
None of the data is garbage. If you see a lot of one particular value, it is probably the background colour of the image you grabbed. It is meaningless when viewed with a text editor.

Related

PNG file are truncated and cannot be shown by every application

I have a code that produce PNG file (using libpng).
I can open these files with EOG (Eye Of Gnome) but with GIMP, imagemagic and others I have an error.
Exiftool tells me that png file is truncated, but I don't see where. On EOG everything is ok.
The code:
int savepng(const char *name, fits *fit, uint32_t bytes_per_sample,
gboolean is_colour) {
int32_t ret = -1;
png_structp png_ptr;
png_infop info_ptr;
const uint32_t width = fit->rx;
const uint32_t height = fit->ry;
char *filename = strdup(name);
if (!ends_with(filename, ".png")) {
filename = str_append(&filename, ".png");
}
FILE *p_png_file = g_fopen(name, "wb");
if (p_png_file == NULL) {
return ret;
}
/* Create and initialize the png_struct with the desired error handler
* functions. If you want to use the default stderr and longjump method,
* you can supply NULL for the last three parameters. We also check that
* the library version is compatible with the one used at compile time,
* in case we are using dynamically linked libraries. REQUIRED.
*/
png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
if (png_ptr == NULL) {
fclose(p_png_file);
return ret;
}
/* Allocate/initialize the image information data. REQUIRED */
info_ptr = png_create_info_struct(png_ptr);
if (info_ptr == NULL) {
fclose(p_png_file);
png_destroy_write_struct(&png_ptr, NULL);
return ret;
}
/* Set error handling. REQUIRED if you aren't supplying your own
* error handling functions in the png_create_write_struct() call.
*/
if (setjmp(png_jmpbuf(png_ptr))) {
/* If we get here, we had a problem writing the file */
fclose(p_png_file);
png_destroy_write_struct(&png_ptr, &info_ptr);
return ret;
}
/* Set up the output control if you are using standard C streams */
png_init_io(png_ptr, p_png_file);
/* Set the image information here. Width and height are up to 2^31,
* bit_depth is one of 1, 2, 4, 8, or 16, but valid values also depend on
* the color_type selected. color_type is one of PNG_COLOR_TYPE_GRAY,
* PNG_COLOR_TYPE_GRAY_ALPHA, PNG_COLOR_TYPE_PALETTE, PNG_COLOR_TYPE_RGB,
* or PNG_COLOR_TYPE_RGB_ALPHA. interlace is either PNG_INTERLACE_NONE or
* PNG_INTERLACE_ADAM7, and the compression_type and filter_type MUST
* currently be PNG_COMPRESSION_TYPE_BASE and PNG_FILTER_TYPE_BASE. REQUIRED
*/
if (is_colour) {
png_set_IHDR(png_ptr, info_ptr, width, height, bytes_per_sample * 8,
PNG_COLOR_TYPE_RGB,
PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE,
PNG_FILTER_TYPE_DEFAULT);
uint32_t profile_len;
const unsigned char *profile = get_sRGB_profile_data(&profile_len);
if (profile_len > 0) {
png_set_iCCP(png_ptr, info_ptr, *name ? name : "icc", 0, (png_const_bytep) profile, profile_len);
}
} else {
png_set_IHDR(png_ptr, info_ptr, width, height, bytes_per_sample * 8,
PNG_COLOR_TYPE_GRAY,
PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE,
PNG_FILTER_TYPE_DEFAULT);
}
/* Write the file header information. REQUIRED */
png_write_info(png_ptr, info_ptr);
png_bytep *row_pointers = malloc((size_t) height * sizeof(png_bytep));
int samples_per_pixel;
if (is_colour) {
samples_per_pixel = 3;
} else {
samples_per_pixel = 1;
}
if (bytes_per_sample == 2) {
/* swap bytes of 16 bit files to most significant bit first */
png_set_swap(png_ptr);
WORD *data = convert_data(fit);
for (unsigned i = 0, j = height - 1; i < height; i++)
row_pointers[j--] = (png_bytep) ((uint16_t*) data + (size_t) samples_per_pixel * i * width);
} else {
uint8_t *data = convert_data8(fit);
for (unsigned i = 0, j = height - 1; i < height; i++)
row_pointers[j--] = (uint8_t*) data + (size_t) samples_per_pixel * i * width;
}
png_write_image(png_ptr, row_pointers);
/* Clean up after the write, and free any memory allocated */
png_destroy_write_struct(&png_ptr, &info_ptr);
/* Close the file */
fclose(p_png_file);
free(row_pointers);
free(filename);
return 0;
}
Could someone point me out my error? Software that can't read the image just display an error dialog. That's all. So it is difficult for me to know where is the error.
As proposed by #MarkSetchell, one line was missing:
/* Clean up after the write, and free any memory allocated */
png_write_end(png_ptr, info_ptr); // this line was missing
png_destroy_write_struct(&png_ptr, &info_ptr);
That's all.
Thank you.
I think that png_write_image() only writes the image row data, so various headers or elements of meta-data are missing. I normally use png_write_png() to write the whole file.
I've attached some code that definitely works for me, in that the output can be read by Gimp, etc. I don't claim it's production-quality ;)
int bitmap_write_png (const bitmap_t *bitmap, const char *path)
{
int ret = -1;
size_t x, y;
int pixel_size = 3;
int depth = 8;
FILE *fp = fopen (path, "wb");
if (fp)
{
ret = 0;
png_structp png_ptr = png_create_write_struct (PNG_LIBPNG_VER_STRING,
NULL, NULL, NULL);
png_infop info_ptr = info_ptr = png_create_info_struct (png_ptr);
png_set_IHDR (png_ptr,
info_ptr,
bitmap->width,
bitmap->height,
depth,
PNG_COLOR_TYPE_RGB,
PNG_INTERLACE_NONE,
PNG_COMPRESSION_TYPE_DEFAULT,
PNG_FILTER_TYPE_DEFAULT);
png_byte ** row_pointers = png_malloc (png_ptr,
bitmap->height * sizeof (png_byte *));
for (y = 0; y < bitmap->height; y++)
{
png_byte *row =
png_malloc (png_ptr, sizeof (uint8_t) * bitmap->width * pixel_size);
row_pointers[y] = row;
for (x = 0; x < bitmap->width; x++)
{
pixel_t *pixel = pixel_at_const (bitmap, x, y);
*row++ = pixel->red * 255;
*row++ = pixel->green * 255;
*row++ = pixel->blue * 255;
}
}
png_init_io (png_ptr, fp);
png_set_rows (png_ptr, info_ptr, row_pointers);
png_write_png (png_ptr, info_ptr, PNG_TRANSFORM_IDENTITY, NULL);
for (y = 0; y < bitmap->height; y++)
{
png_free (png_ptr, row_pointers[y]);
}
png_free (png_ptr, row_pointers);
close (fp);
}
else
{
ret = errno;
}
return ret;
}

C: Trouble when trying to comvert HBITMAP into .bmp file

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.
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);
}

how to split the image and save it in one-dimensional arrays

I'm making a sliding puzzle using image.
And in the process, I'm going to bring up the images, and divide them into tiles of a certain size, and put them in one-dimensional arrays in order.
I will register the image file as a resource, or I will import the image file using the HBITMAP and LoadImage functions.
But I just can't think of how we're going to divide this image file into a certain size (rectangular) and store it in a one-dimensional array.
Would it be OK to save the segmented tiles to each image file and then save the path of the files in a one-dimensional array?
*Note: One-dimensional array and double buffering must be used.
You don't need to serialize the pieces to files. If you want to, that is a separate issue.
You can load the image as one image and split it in memory. Basically create correctly sized bitmaps for the piece images in memory and paint a section of the main bitmap into each one. You also do not need to fool around with CreateDIBSection to do it this way -- can just do it all with compatible bitmaps and device contexts.
Something like the following (I did not test this)
std::vector<HBITMAP> SplitBitmap(HBITMAP bmp, int columns, int rows)
{
// get the bitmap dimensions
BITMAP bm;
GetObject(bmp, sizeof(BITMAP), &bm);
int wd = bm.bmWidth, hgt = bm.bmHeight;
int piece_wd = wd / columns;
int piece_hgt = hgt / rows;
// Select the given bitmap into a device context.
auto hdcScreen = GetDC(NULL);
auto hdcBitmap = CreateCompatibleDC(hdcScreen);
auto hbmOldBmp = SelectObject(hdcBitmap, bmp);
std::vector<HBITMAP> pieces(columns*rows);
for (int row = 0; row < rows; row++) {
for (int col = 0; col < columns; col++) {
// create a device context for a piece and select an appropriately sized bitmap into it
auto hdcPiece = CreateCompatibleDC(hdcScreen);
auto hbmPiece = CreateCompatibleBitmap(hdcScreen, piece_wd, piece_hgt);
auto hbmOldPiece = SelectObject(hdcPiece, hbmPiece);
// paint a piece of the whole bitmap into the piece bitmap
BitBlt(hdcPiece, 0, 0, piece_wd, piece_hgt, hdcBitmap, col * piece_wd, row * piece_hgt, SRCCOPY);
// cleanup per piece resources we dont need.
SelectObject(hdcPiece, hbmOldPiece);
DeleteDC(hdcPiece);
pieces[row * columns + col] = hbmPiece;
}
}
SelectObject(hdcBitmap, hbmOldBmp);
DeleteDC(hdcBitmap);
ReleaseDC(NULL, hdcScreen);
return pieces;
}
...
auto hbm = (HBITMAP)LoadImage(NULL, L"C:\\work\\test.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
auto pieces = SplitBitmap(hbm, 4, 4);
You can do this.
Here are some simplified codes for your reference.
int Spilt(HWND hWnd)
{
BYTE* bitPointer;
HBITMAP g_BitMap = (HBITMAP)LoadImage(NULL, L"C:\\Users\\strives\\Desktop\\timg.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
HDC dc = GetDC(hWnd);
HDC dc_1 = GetDC(hWnd);
HDC hdcMemDC = CreateCompatibleDC(dc);
HDC hdcTemp = CreateCompatibleDC(dc);
HDC hdcMemDC_1 = GetDC(hWnd);
BITMAP bmp;
BITMAP bmp_3;
GetObject(g_BitMap, sizeof(BITMAP), &bmp);
static BITMAPINFO bitmap;
bitmap.bmiHeader.biSize = sizeof(bitmap.bmiHeader);
bitmap.bmiHeader.biWidth = bmp.bmWidth / 2;
bitmap.bmiHeader.biHeight = bmp.bmHeight;
bitmap.bmiHeader.biPlanes = 1;
bitmap.bmiHeader.biBitCount = 24;
bitmap.bmiHeader.biCompression = BI_RGB;
bitmap.bmiHeader.biSizeImage = bmp.bmWidth / 2 * bmp.bmHeight * 4;
bitmap.bmiHeader.biClrUsed = 0;
bitmap.bmiHeader.biClrImportant = 0;
HBITMAP hBitmap2 = CreateDIBSection(hdcTemp, &bitmap, DIB_RGB_COLORS, (void**)(&bitPointer), NULL, NULL);
HBITMAP bmp_1 = CreateCompatibleBitmap(hdcMemDC_1, bmp.bmWidth / 2, bmp.bmHeight );
SelectObject(hdcMemDC, g_BitMap);
SelectObject(hdcTemp, bmp_1);
BitBlt(hdcTemp, 0, 0, bmp.bmWidth / 2, bmp.bmHeight, hdcMemDC, 0, 0, SRCCOPY);
GetObject(bmp_1, sizeof(BITMAP), &bmp_3);
BITMAPFILEHEADER bmfHeader;
BITMAPINFOHEADER bi;
bi.biSize = sizeof(BITMAPINFOHEADER);
bi.biWidth = bmp_3.bmWidth;
bi.biHeight = bmp_3.bmHeight;
bi.biPlanes = 1;
bi.biBitCount = 24;
bi.biCompression = BI_RGB;
bi.biSizeImage = 0;
bi.biXPelsPerMeter = 0;
bi.biYPelsPerMeter = 0;
bi.biClrUsed = 0;
bi.biClrImportant = 0;
DWORD dwBmpSize = ((bmp_3.bmWidth * bi.biBitCount + 31) / 32) * 4 * bmp_3.bmHeight;
// Starting with 24-bit Windows, GlobalAlloc and LocalAlloc are implemented as wrapper functions that
// call HeapAlloc using a handle to the process's default heap. Therefore, GlobalAlloc and LocalAlloc
// have greater overhead than HeapAlloc.
HANDLE hDIB = GlobalAlloc(GHND, dwBmpSize);
char *lpbitmap = (char *)GlobalLock(hDIB);
GetDIBits(hdcMemDC, bmp_1, 0,
(UINT)bmp_3.bmHeight,
lpbitmap,
(BITMAPINFO *)&bi,
DIB_RGB_COLORS);
// A file is created, this is where we will save the screen capture.
HANDLE hFile = CreateFile(L"spilt_1.bmp",
GENERIC_WRITE,
0,
NULL,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL, NULL);
DWORD dwSizeofDIB = dwBmpSize + sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
bmfHeader.bfOffBits = (DWORD)sizeof(BITMAPFILEHEADER) + (DWORD)sizeof(BITMAPINFOHEADER);
bmfHeader.bfSize = dwSizeofDIB;
bmfHeader.bfType = 0x4D42; //BM
DWORD dwBytesWritten = 0;
WriteFile(hFile, (LPSTR)&bmfHeader, sizeof(BITMAPFILEHEADER), &dwBytesWritten, NULL);
WriteFile(hFile, (LPSTR)&bi, sizeof(BITMAPINFOHEADER), &dwBytesWritten, NULL);
WriteFile(hFile, (LPSTR)lpbitmap, dwBmpSize, &dwBytesWritten, NULL);
//Unlock and Free the DIB from the heap
GlobalUnlock(hDIB);
GlobalFree(hDIB);
//Close the handle for the file that was created
CloseHandle(hFile);
DeleteObject(hdcTemp);
DeleteObject(hdcMemDC);
DeleteObject(hBitmap2);
DeleteObject(bmp_1);
return 0;
}
For convenience of understanding, I divide the bitmap into two pieces and save them as image files. Finally, I can save the file path to a one-dimensional array.
Code reference: Capturing an Image and consulted #Jonathan Potter advice.

Add imagedata to Bitmap in raw bytes

I am trying to add imagedata to an existing bitmap.
The existing bitmap is stored in an byte array:
That's how the image looks like:
If I want to add, let's say 2 lines of blue to the bottom, how could I do that?
#include <Windows.h>
const char tred[] = {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xC8,0xD1,0xF8,0x95,0xA7,0xF4,0x65,0x80,0xED,0xB0,0xBC,0xF6,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xD0,0xD7,0xFA,0x48,0x68,0xEB,0x01,0x2E,0xE2,0x06,0x30,0xE4,
0x12,0x3A,0xE4,0x0D,0x36,0xE5,0x71,0x8A,0xEF,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xF4,
0xF6,0xFD,0x35,0x58,0xE9,0x00,0x2B,0xE3,0x19,0x40,0xE6,0x21,0x46,0xE7,0x21,0x47,0xE7,0x21,0x47,0xE7,0x17,0x3E,0xE6,0x78,0x90,0xF0,0x00,
0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xAD,0xBA,0xF6,0x00,0x27,0xE3,0x1A,0x41,0xE6,0x21,0x47,0xE7,0x21,0x47,
0xE7,0x21,0x47,0xE7,0x21,0x47,0xE7,0x21,0x47,0xE7,0x17,0x3E,0xE6,0x78,0x90,0xF0,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0x94,0xA6,0xF2,0x00,0x28,0xE3,0x21,0x47,0xE7,0x21,0x47,0xE7,0x21,0x47,0xE7,0x21,0x47,0xE7,0x21,0x47,0xE7,0x21,0x47,0xE7,0x21,0x47,0xE7,
0x17,0x3E,0xE6,0x78,0x90,0xF0,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xBB,0xC6,0xF7,0x00,0x27,0xE2,0x22,0x47,0xE7,0x21,0x47,0xE7,0x21,
0x47,0xE7,0x21,0x47,0xE7,0x21,0x47,0xE7,0x21,0x47,0xE7,0x21,0x47,0xE7,0x21,0x47,0xE7,0x17,0x3E,0xE6,0x78,0x90,0xF0,0x00,0x00,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFF,0x00,0x2C,0xE3,0x20,0x46,0xE7,0x21,0x47,0xE7,0x21,0x47,0xE7,0x21,0x47,0xE7,0x21,0x47,0xE7,0x21,0x47,0xE7,0x21,0x47,
0xE7,0x21,0x47,0xE7,0x21,0x47,0xE7,0x17,0x3E,0xE6,0x78,0x90,0xF0,0x00,0x00,0xFF,0xFF,0xFF,0x67,0x82,0xEE,0x13,0x3C,0xE5,0x21,0x47,0xE7,
0x21,0x47,0xE7,0x21,0x47,0xE7,0x21,0x47,0xE7,0x21,0x47,0xE7,0x21,0x47,0xE7,0x21,0x47,0xE7,0x21,0x47,0xE7,0x21,0x47,0xE7,0x17,0x3E,0xE6,
0x78,0x90,0xF0,0x00,0x00,0xFF,0xFF,0xFF,0x00,0x2C,0xE3,0x20,0x46,0xE7,0x21,0x47,0xE7,0x21,0x47,0xE7,0x21,0x47,0xE7,0x21,0x47,0xE7,0x21,
0x47,0xE7,0x21,0x47,0xE7,0x21,0x47,0xE7,0x21,0x47,0xE7,0x21,0x47,0xE7,0x17,0x3E,0xE6,0x78,0x90,0xF0,0x00,0x00,0xC3,0xCD,0xF8,0x07,0x31,
0xE4,0x21,0x47,0xE7,0x21,0x47,0xE7,0x21,0x47,0xE7,0x21,0x47,0xE7,0x21,0x47,0xE7,0x21,0x47,0xE7,0x21,0x47,0xE7,0x21,0x47,0xE7,0x21,0x47,
0xE7,0x21,0x47,0xE7,0x17,0x3E,0xE6,0x78,0x90,0xF0,0x00,0x00,0x8B,0x9F,0xF1,0x13,0x3B,0xE5,0x21,0x47,0xE7,0x21,0x47,0xE7,0x21,0x47,0xE7,
0x21,0x47,0xE7,0x21,0x47,0xE7,0x21,0x47,0xE7,0x21,0x47,0xE7,0x21,0x47,0xE7,0x21,0x47,0xE7,0x21,0x47,0xE7,0x17,0x3E,0xE6,0x78,0x90,0xF0,
0x00,0x00,0x78,0x90,0xF0,0x17,0x3E,0xE5,0x21,0x47,0xE7,0x21,0x47,0xE7,0x21,0x47,0xE7,0x21,0x47,0xE7,0x21,0x47,0xE7,0x21,0x47,0xE7,0x21,
0x47,0xE7,0x21,0x47,0xE7,0x21,0x47,0xE7,0x21,0x47,0xE7,0x17,0x3E,0xE6,0x78,0x90,0xF0,0x00,0x00,0x79,0x8F,0xF0,0x17,0x3E,0xE5,0x21,0x47,
0xE7,0x21,0x47,0xE7,0x21,0x47,0xE7,0x21,0x47,0xE7,0x21,0x47,0xE7,0x21,0x47,0xE7,0x21,0x47,0xE7,0x21,0x47,0xE7,0x21,0x47,0xE7,0x21,0x47,
0xE7,0x17,0x3E,0xE6,0x78,0x90,0xF0,0x00,0x00,0x8E,0xA2,0xF2,0x12,0x3A,0xE5,0x21,0x47,0xE7,0x21,0x47,0xE7,0x21,0x47,0xE7,0x21,0x47,0xE7,
0x21,0x47,0xE7,0x21,0x47,0xE7,0x21,0x47,0xE7,0x21,0x47,0xE7,0x21,0x47,0xE7,0x21,0x47,0xE7,0x17,0x3E,0xE6,0x78,0x90,0xF0,0x00,0x00,0xC9,
0xD1,0xF9,0x05,0x30,0xE4,0x21,0x47,0xE7,0x21,0x47,0xE7,0x21,0x47,0xE7,0x21,0x47,0xE7,0x21,0x47,0xE7,0x21,0x47,0xE7,0x21,0x47,0xE7,0x21,
0x47,0xE7,0x21,0x47,0xE7,0x21,0x47,0xE7,0x17,0x3E,0xE6,0x78,0x90,0xF0,0x00,0x00,0xFF,0xFF,0xFF,0x04,0x2F,0xE3,0x20,0x46,0xE6,0x21,0x47,
0xE7,0x21,0x47,0xE7,0x21,0x47,0xE7,0x21,0x47,0xE7,0x21,0x47,0xE7,0x21,0x47,0xE7,0x21,0x47,0xE7,0x21,0x47,0xE7,0x21,0x47,0xE7,0x17,0x3E,
0xE6,0x78,0x90,0xF0,0x00,0x00,0xFF,0xFF,0xFF,0x75,0x8C,0xEF,0x11,0x3A,0xE5,0x21,0x47,0xE7,0x21,0x47,0xE7,0x21,0x47,0xE7,0x21,0x47,0xE7,
0x21,0x47,0xE7,0x21,0x47,0xE7,0x21,0x47,0xE7,0x21,0x47,0xE7,0x21,0x47,0xE7,0x17,0x3E,0xE6,0x78,0x90,0xF0,0x00,0x00,0xFF,0xFF,0xFF,0xFF,
0xFF,0xFF,0x05,0x2F,0xE3,0x20,0x46,0xE6,0x21,0x47,0xE7,0x21,0x47,0xE7,0x21,0x47,0xE7,0x21,0x47,0xE7,0x21,0x47,0xE7,0x21,0x47,0xE7,0x21,
0x47,0xE7,0x21,0x47,0xE7,0x17,0x3E,0xE6,0x78,0x90,0xF0,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xD0,0xD7,0xF9,0x00,0x25,0xE3,0x21,0x47,
0xE7,0x21,0x47,0xE7,0x21,0x47,0xE7,0x21,0x47,0xE7,0x21,0x47,0xE7,0x21,0x47,0xE7,0x21,0x47,0xE7,0x21,0x47,0xE7,0x17,0x3E,0xE6,0x78,0x90,
0xF0,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xAE,0xBC,0xF6,0x00,0x26,0xE3,0x20,0x46,0xE6,0x21,0x47,0xE7,0x21,0x47,0xE7,
0x21,0x47,0xE7,0x21,0x47,0xE7,0x21,0x47,0xE7,0x21,0x47,0xE7,0x17,0x3E,0xE6,0x78,0x90,0xF0,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFF,0xFF,0xC8,0xD1,0xF8,0x02,0x2D,0xE3,0x15,0x3D,0xE6,0x21,0x47,0xE7,0x21,0x47,0xE7,0x21,0x47,0xE7,0x21,0x47,0xE7,0x21,
0x47,0xE7,0x17,0x3E,0xE6,0x78,0x90,0xF0,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFE,0x51,0x6F,0xEB,0x00,0x27,0xE3,0x16,0x3D,0xE6,0x1F,0x46,0xE6,0x22,0x47,0xE7,0x21,0x47,0xE7,0x17,0x3E,0xE6,0x78,0x90,0xF0,0x00,0x00,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xEB,0xEF,0xFC,0x65,0x7E,0xEE,
0x0C,0x36,0xE3,0x00,0x2A,0xE3,0x0D,0x36,0xE4,0x0A,0x34,0xE4,0x70,0x89,0xEF,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xE4,0xE8,0xFC,0xAE,0xBC,0xF6,0x7E,
0x94,0xF0,0xBD,0xC7,0xF7,0x00,0x00 };
int main(int argc, char* argv[]) {
BITMAPFILEHEADER bmfh;
BITMAPINFOHEADER bmih;
bmih.biSize = sizeof(BITMAPINFOHEADER);;
bmih.biWidth = 14;
bmih.biHeight = 96;
bmih.biPlanes = 1;
bmih.biBitCount = 24;
bmih.biCompression = 0;
bmih.biSizeImage = 0;
bmih.biXPelsPerMeter = 0;
bmih.biYPelsPerMeter = 0;
bmih.biClrUsed = 0;
bmih.biClrImportant = 0;
bmfh.bfType = 19778;
bmfh.bfSize = ((bmih.biWidth * bmih.biBitCount + 31) / 32) * 4 * bmih.biHeight;
bmfh.bfOffBits = (DWORD)sizeof(BITMAPFILEHEADER) + (DWORD)sizeof(BITMAPINFOHEADER); // 54
bmfh.bfReserved1 = 0;
bmfh.bfReserved2 = 0;
char* bmpData = (char*)malloc(bmfh.bfSize);
memcpy(bmpData, tred, sizeof(tred));
HANDLE h = CreateFile("C:\\tmp\\a.bmp", GENERIC_WRITE, 0, 0, CREATE_ALWAYS, 0, 0);
DWORD d;
WriteFile(h, &bmfh, sizeof(bmfh), &d, 0);
WriteFile(h, &bmih, sizeof(bmih), &d, 0);
WriteFile(h, bmpData, bmfh.bfSize, &d, 0);
CloseHandle(h);
delete[] bmpData;
return 0;
}
What I do not understand: Original file is 14px*24px = 366px in total. In a 24bit Bitmap, there is RGB (3 Bytes) for each pixel, so that would be 366*3=1008bytes. The original file (without header) has 1056bytes.
Maybe that's the reason why my approach, by adding 14*3=42bytes of 0xFF did not add an extra line?

Mouse cursor bitmap

I am trying to get bitmap from mouse cursor, but with next code, i just can't get colors.
CURSORINFO cursorInfo = { 0 };
cursorInfo.cbSize = sizeof(cursorInfo);
if (GetCursorInfo(&cursorInfo)) {
ICONINFO ii = {0};
int p = GetIconInfo(cursorInfo.hCursor, &ii);
// get screen
HDC dc = GetDC(NULL);
HDC memDC = CreateCompatibleDC(dc);
//SelectObject(memDC, ii.hbmColor);
int counter = 0;
//
byte* bits[1000];// = new byte[w * 4];
BITMAPINFO bmi;
memset(&bmi, 0, sizeof(BITMAPINFO));
bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bmi.bmiHeader.biWidth = 16;
bmi.bmiHeader.biHeight = 16;
bmi.bmiHeader.biBitCount = 32;
bmi.bmiHeader.biPlanes = 1;
bmi.bmiHeader.biCompression = BI_RGB;
bmi.bmiHeader.biSizeImage = 0;
bmi.bmiHeader.biXPelsPerMeter = 0;
bmi.bmiHeader.biYPelsPerMeter = 0;
bmi.bmiHeader.biClrUsed = 0;
bmi.bmiHeader.biClrImportant = 0;
int rv = ::GetDIBits(memDC, ii.hbmColor, 0, 1, (void**)&bits, &bmi, DIB_RGB_COLORS);
}
Start by getting the parameters of the bitmap as recorded by Windows:
BITMAP bitmap = {0};
GetObject(ii.hbmColor, sizeof(bitmap), &bitmap);
You can use the returned values to populate the bmi structure.
And about the bmi structure: BITMAPINFO does not reserve enough space for a palette. You should create your own structure for this:
struct BitmapPlusPalette
{
BITMAPINFOHEADER bmiHeader;
RGBQUAD palette[256];
};
Calculating the number of bytes needed for the bitmap is a bit tricky because it needs to be rounded up:
w = ((bitmap.bmWidth * bitmap.bmBitsPixel) + 31) / 8;
byte* bits = new byte[w * bitmap.bmHeight];
And here's a corrected version of your final line:
int rv = ::GetDIBits(dc, ii.hbmColor, 0, bitmap.bmHeight, bits, (BITMAPINFO *)&bmi, DIB_RGB_COLORS);
The problem with your code I think is in the way you allocated memory for 'bits' variable and how you used it then in GetDIBits function.
Firstly, the commented part byte* bits = new byte[w*4] was better than byte* bits[1000]. When you write byte* bits[1000] computer allocates 1000 POINTERS to byte. Each of these pointers doesn't point to anything.
Secondly, GetDIBits accepts LPVOID lpvBits as a 5th param. So, its a pointer to void.
In most platforms sizeof(void *) > sizeof(byte), so you can't just pass it a byte array, probably it would be better to pass a pointer to int or unsigned int (I'm not good at Windows types, so maybe something more appropriate should be better, sorry).
So, my guess is this:
unsigned bits[1000];
memset(bits, 0, sizeof(bits));
//...
int tv = GetDIBits(memDC, ii.hmbColor, 0, 1, (LPVOID)bits, /* ... */);

Resources