Saving Image to Memory - c++

I have an dev-c++ Application which makes an Screenshot and writes it into an File. Now I want to write the image into an Variable/Stream. Originally I used three Writefile Functions, which write the header, the info and the hbitmap into an file. Now I want to save the data not to the file, but to an Stream, so I can use it for further processing. The Code I use is this:
/* <Include> */
#include <windows.h>
#include <iostream>
#include <sstream>
/* </Include> */
/* <Const> */
const char *AppName="Yeah";
using namespace std;
/* </Const> */
/* <Function> */
void SaveScreen(HWND pScreen, stringstream Path)
{
int Width = GetSystemMetrics(SM_CXSCREEN);//1280;
int Height = GetSystemMetrics(SM_CYSCREEN);//1024;
HDC hdcScreen;
HBITMAP hbmScreen;
//---------------Bitmap Informationen
BITMAPINFO infobmp;
infobmp.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
infobmp.bmiHeader.biWidth = Width;
infobmp.bmiHeader.biHeight = Height;
infobmp.bmiHeader.biPlanes = 1;
infobmp.bmiHeader.biBitCount = 24;
infobmp.bmiHeader.biCompression = 0;
infobmp.bmiHeader.biSizeImage = 0;
infobmp.bmiHeader.biXPelsPerMeter = 0;
infobmp.bmiHeader.biYPelsPerMeter = 0;
infobmp.bmiHeader.biClrUsed = 0;
infobmp.bmiHeader.biClrImportant = 0;
int* bitmap = new int[Width*Height*3];
BITMAPFILEHEADER bfheader;
bfheader.bfType = 19778;
bfheader.bfSize = sizeof(BITMAPFILEHEADER) + Width*Height*3 + sizeof(BITMAPINFOHEADER);
bfheader.bfReserved1 = 0;
bfheader.bfReserved2 = 0;
bfheader.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
//Bitmap ----------------------- Informationen
hdcScreen = GetWindowDC(pScreen);
hbmScreen = CreateCompatibleBitmap(hdcScreen, Width, Height);
// tempor?rer DC
HDC hdcTemp = CreateCompatibleDC(hdcScreen);
// Bitmap reinselektieren
HBITMAP hbmOld = (HBITMAP)SelectObject(hdcTemp, hbmScreen);
// Inhalt von Desktop ?bertragen
BitBlt(hdcTemp, 0, 0, Width, Height, hdcScreen, 0, 0, SRCCOPY);
int iResult = GetDIBits(hdcTemp, hbmScreen, 0, Height, bitmap, &infobmp, DIB_RGB_COLORS);
// aufr?umen
SelectObject(hdcTemp, hbmOld);
DeleteObject(hbmScreen);
DeleteDC(hdcTemp);
// HANDLE hfile = CreateFile(Path, GENERIC_WRITE, 0, 0, OPEN_ALWAYS, 0, 0);
//Datei Schreiben
DWORD word;
WriteFile(Path, &bfheader, 14, &word, NULL);
WriteFile(Path, &infobmp, 40,& word, NULL);
WriteFile(Path, bitmap, Width*Height*3, &word,NULL);
// Path = &bfheader & &infobmp & bitmap;
ReleaseDC(pScreen, hdcScreen);
// CloseHandle(hfile);
delete[] bitmap;
}
/* </Function> */
int WINAPI WinMain(
HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
HWND hWnd = FindWindow(NULL, AppName);
stringstream ms;
SaveScreen(hWnd, ms);
return 0;
}
Can someone please show me what I am doing wrong?

You can create a structure like this:
struct ScreenShotBuffer
{
BITMAPFILEHEADER bfheader;
BITMAPINFO infobmp;
int* bitmap;
};
Then create a global variable using that structure.
struct ScreenShotBuffer myScreenShot;
Anyway you are allocating the "memory" to hold the data using malloc.
So you can just use these three lines instead of WriteBuffer() :
myScreenShot.bfheader=bfheader;
myScreenShot.infobmp=infobmp;
myScreenShot.bitmap=bitmap;

Related

Windows API Function AVIFileOpenW takes PAVISTREAM as input but AVIStreamSetFormat takes PAVIFILE

I am making a clipping software which clips the last 30 seconds of your screen. I am trying to write the results to an AVI file, but I am running into issues. Here is my code:
#include <Windows.h>
#include <Vfw.h>
#include <iostream>
#include <vector>
const int BUFFER_SIZE = 30 * 60; // 30 seconds at 60 fps
int main() {
HMONITOR hMonitor = MonitorFromWindow(NULL, MONITOR_DEFAULTTOPRIMARY);
MONITORINFO info;
info.cbSize = sizeof(MONITORINFO);
GetMonitorInfo(hMonitor, &info);
int width = info.rcMonitor.right - info.rcMonitor.left;
int height = info.rcMonitor.bottom - info.rcMonitor.top;
HDC hDC = GetDC(NULL);
HBITMAP hBitmap = CreateCompatibleBitmap(hDC, width, height);
// Create a device context for the bitmap
HDC hMemDC = CreateCompatibleDC(hDC);
SelectObject(hMemDC, hBitmap);
std::vector<HBITMAP> buffer(BUFFER_SIZE);
int index = 0;
while (true) {
BitBlt(hMemDC, 0, 0, width, height, hDC, 0, 0, SRCCOPY);
buffer[index] = hBitmap;
index = (index + 1) % BUFFER_SIZE;
if (GetAsyncKeyState(VK_ESCAPE) & 0x8000) {
break;
}
}
// PROBLEM HERE:
PAVISTREAM pStream;
AVIFileInit();
AVIFileOpenW(&pStream, L"screen_recording.avi", OF_WRITE | OF_CREATE, NULL); // takes PAVIFILE as first parameter
AVIStreamSetFormat(pStream, 0, &hBitmap, sizeof(BITMAPINFOHEADER)); // takes PAVISTREAM as first parameter
// Write the stored frames to the AVISTREAM object
for (int i = 0; i < BUFFER_SIZE; i++) {
AVIStreamWrite(pStream, i, 1, &buffer[i], sizeof(BITMAPINFOHEADER), AVIIF_KEYFRAME, NULL, NULL);
}
AVIStreamClose(pStream);
AVIFileExit();
ReleaseDC(NULL, hDC);
DeleteDC(hMemDC);
DeleteObject(hBitmap);
return 0;
}
Am I doing this right? I am new to C++, so I am not sure if this is what I should be doing.
You can use the function AVIStreamOpenFromFile to open the avi file as stream, that will give the pointer to PAVISTREAM. So, not necessary to call the AVIFileOpen function.

Create Device Context with Icon and transparent background

I'm trying to get a image with HICON but the background is not transparent. How can I make it transparent? I need a winapi example because the code is in Dart, but it has all the windows calls/functions.
I've tried different version I've found on the internet but it didn't worked. I can access icon mask if that can help for a solution.
Current code:
var icon = SendMessage(hWnd, WM_GETICON, 2, 0); // ICON_SMALL2 - User Made Apps
if (icon == 0) icon = GetClassLongPtr(hWnd, -14); // GCLP_HICON - Microsoft Win Apps
final int hScreen = GetDC(hWnd);
final int hDC = CreateCompatibleDC(hScreen);
final int hBitmap = CreateCompatibleBitmap(hScreen, GetSystemMetrics(SM_CXICON), GetSystemMetrics(SM_CYICON));
SelectObject(hDC, hBitmap);
// SetBkMode(hDC, TRANSPARENT); - Works for text only
// PatBlt(hDC, 0, 0, 545, 850, WHITENESS); - only white/black;
DrawIconEx(hDC, 0, 0, icon, GetSystemMetrics(SM_CXICON), GetSystemMetrics(SM_CYICON), NULL, NULL, 3);
The icon is transparent:
Even if I don't draw the icon, the output is a black square.
Can you suggest how to remove the background? In basic winapi calls. The code can be in cpp if it doesn't use special classes from libraries, I can use only dllCalls
Here is full working code:
// ignore_for_file: depend_on_referenced_packages, non_constant_identifier_names, avoid_print, unrelated_type_equality_checks
import 'dart:ffi';
import 'dart:io';
import 'package:win32/win32.dart';
import 'package:ffi/ffi.dart';
int enumWindowsProc(int hWnd, int lparam) {
if (IsWindowVisible(hWnd) == FALSE) return TRUE;
final length = GetWindowTextLength(hWnd);
if (length == 0) return TRUE;
var icon = SendMessage(hWnd, WM_GETICON, 2, 0); // ICON_SMALL2 - User Made Apps
if (icon == 0) icon = GetClassLongPtr(hWnd, -14); // GCLP_HICON - Microsoft Win Apps
if (icon == 0) {
icon = 0;
return 1;
}
final int hScreen = GetDC(hWnd);
final int hDC = CreateCompatibleDC(hScreen);
final int hBitmap = CreateCompatibleBitmap(hScreen, GetSystemMetrics(SM_CXICON), GetSystemMetrics(SM_CYICON));
SelectObject(hDC, hBitmap);
SetBkMode(hDC, TRANSPARENT); //- Works for text only
PatBlt(hDC, 0, 0, GetSystemMetrics(SM_CXICON) ~/ 2, GetSystemMetrics(SM_CYICON), WHITENESS); // test, half white half black.
DrawIconEx(hDC, 0, 0, icon, GetSystemMetrics(SM_CXICON), GetSystemMetrics(SM_CYICON), NULL, NULL, 3);
//Turn to bytes
final bmpScreen = calloc<BITMAP>();
GetObject(hBitmap, sizeOf<BITMAP>(), bmpScreen);
final bitmapFileHeader = calloc<BITMAPFILEHEADER>();
final bitmapInfoHeader = calloc<BITMAPINFOHEADER>()
..ref.biSize = sizeOf<BITMAPINFOHEADER>()
..ref.biWidth = bmpScreen.ref.bmWidth
..ref.biHeight = bmpScreen.ref.bmHeight
..ref.biPlanes = 1
..ref.biBitCount = 32
..ref.biCompression = BI_RGB;
final dwBmpSize = ((bmpScreen.ref.bmWidth * bitmapInfoHeader.ref.biBitCount + 31) / 32 * 4 * bmpScreen.ref.bmHeight).toInt();
final lpBitmap = calloc<Uint8>(dwBmpSize);
GetDIBits(hDC, hBitmap, 0, bmpScreen.ref.bmHeight, lpBitmap, bitmapInfoHeader.cast(), DIB_RGB_COLORS);
final dwSizeOfDIB = dwBmpSize + sizeOf<BITMAPFILEHEADER>() + sizeOf<BITMAPINFOHEADER>();
bitmapFileHeader.ref.bfOffBits = sizeOf<BITMAPFILEHEADER>() + sizeOf<BITMAPINFOHEADER>();
bitmapFileHeader.ref.bfSize = dwSizeOfDIB;
bitmapFileHeader.ref.bfType = 0x4D42; // BM
var b = BytesBuilder();
b.add(Pointer<Uint8>.fromAddress(bitmapFileHeader.address).asTypedList(sizeOf<BITMAPFILEHEADER>()));
b.add(Pointer<Uint8>.fromAddress(bitmapInfoHeader.address).asTypedList(sizeOf<BITMAPINFOHEADER>()));
b.add(lpBitmap.asTypedList(dwBmpSize));
// I need the Bitmap in Bytes, I save it to file just for debugging.
//capture?.icon = b.takeBytes();
//
DeleteDC(hDC);
DeleteObject(hBitmap);
free(bmpScreen);
free(bitmapFileHeader);
free(bitmapInfoHeader);
free(lpBitmap);
Directory current = Directory.current;
File("${current.path}/imgs/i_${icon.toString()}.bmp").writeAsBytes(b.takeBytes());
return 1;
}
void main() {
final imgs = "${Directory.current.path}/imgs";
if (Directory(imgs).exists() == true) {
Directory(imgs).deleteSync(recursive: true);
}
final wndProc = Pointer.fromFunction<EnumWindowsProc>(enumWindowsProc, 0);
EnumWindows(wndProc, 0);
}
final _user32 = DynamicLibrary.open('user32.dll');
int DrawIconEx(int hdc, int xLeft, int yTop, int hIcon, int cxWidth, int cyWidth, int istepIfAniCur, int hbrFlickerFreeDraw, int diFlags) =>
_DrawIconEx(hdc, xLeft, yTop, hIcon, cxWidth, cyWidth, istepIfAniCur, hbrFlickerFreeDraw, diFlags);
final _DrawIconEx = _user32.lookupFunction<
Int32 Function(IntPtr hdc, Int32 xLeft, Int32 yTop, IntPtr hIcon, Int32 cxWidth, Int32 cyWidth, Uint32 istepIfAniCur, IntPtr hbrFlickerFreeDraw, Uint32 diFlags),
int Function(int hdc, int xLeft, int yTop, int hIcon, int cxWidth, int cyWidth, int istepIfAniCur, int hbrFlickerFreeDraw, int diFlags)>('DrawIconEx');
final _gdi32 = DynamicLibrary.open('gdi32.dll');
int PatBlt(int hdc, int x, int y, int w, int h, int rop) => _PatBlt(hdc, x, y, w, h, rop);
final _PatBlt =
_gdi32.lookupFunction<Int32 Function(IntPtr hdc, Int32 x, Int32 y, Int32 w, Int32 h, Uint32 rop), int Function(int hdc, int x, int y, int w, int h, int rop)>('PatBlt');
Found a even better solution, works with buffer and write to file, alpha channel and everything :)
https://github.com/pelayomendez/exe-icon-extractor/blob/master/src/module.cc
#include <tchar.h>
#include <iostream>
#include <windows.h>
#include <fstream>
#include <cassert>
using namespace std;
// Check windows
#if _WIN32 || _WIN64
#if _WIN64
#define ENV64BIT
#else
#define ENV32BIT
#endif
#endif
// Check GCC
#if __GNUC__
#if __x86_64__ || __ppc64__
#define ENV64BIT
#else
#define ENV32BIT
#endif
#endif
typedef struct
{
WORD idReserved; // must be 0
WORD idType; // 1 = ICON, 2 = CURSOR
WORD idCount; // number of images (and ICONDIRs)
// ICONDIR [1...n]
// ICONIMAGE [1...n]
} ICONHEADER;
//
// An array of ICONDIRs immediately follow the ICONHEADER
//
typedef struct
{
BYTE bWidth;
BYTE bHeight;
BYTE bColorCount;
BYTE bReserved;
WORD wPlanes; // for cursors, this field = wXHotSpot
WORD wBitCount; // for cursors, this field = wYHotSpot
DWORD dwBytesInRes;
DWORD dwImageOffset; // file-offset to the start of ICONIMAGE
} ICONDIR;
//
// After the ICONDIRs follow the ICONIMAGE structures -
// consisting of a BITMAPINFOHEADER, (optional) RGBQUAD array, then
// the color and mask bitmap bits (all packed together
//
typedef struct
{
BITMAPINFOHEADER biHeader; // header for color bitmap (no mask header)
//RGBQUAD rgbColors[1...n];
//BYTE bXOR[1]; // DIB bits for color bitmap
//BYTE bAND[1]; // DIB bits for mask bitmap
} ICONIMAGE;
//
// Return the number of BYTES the bitmap will take ON DISK
//
static UINT NumBitmapBytes(BITMAP* pBitmap)
{
int nWidthBytes = pBitmap->bmWidthBytes;
// bitmap scanlines MUST be a multiple of 4 bytes when stored
// inside a bitmap resource, so round up if necessary
if (nWidthBytes & 3)
nWidthBytes = (nWidthBytes + 4) & ~3;
return nWidthBytes * pBitmap->bmHeight;
}
static BOOL GetIconBitmapInfo(HICON hIcon, ICONINFO* pIconInfo, BITMAP* pbmpColor, BITMAP* pbmpMask)
{
if (!GetIconInfo(hIcon, pIconInfo))
return FALSE;
if (!GetObject(pIconInfo->hbmColor, sizeof(BITMAP), pbmpColor))
return FALSE;
if (!GetObject(pIconInfo->hbmMask, sizeof(BITMAP), pbmpMask))
return FALSE;
return TRUE;
}
//
// Write one icon directory entry - specify the index of the image
//
static void WriteIconDirectoryEntry(BYTE* buffer, int* pBufferOffset, int nIdx, HICON hIcon, UINT nImageOffset)
{
ICONINFO iconInfo;
ICONDIR iconDir;
BITMAP bmpColor;
BITMAP bmpMask;
UINT nColorCount;
UINT nImageBytes;
GetIconBitmapInfo(hIcon, &iconInfo, &bmpColor, &bmpMask);
nImageBytes = NumBitmapBytes(&bmpColor) + NumBitmapBytes(&bmpMask);
if (bmpColor.bmBitsPixel >= 8)
nColorCount = 0;
else
nColorCount = 1 << (bmpColor.bmBitsPixel * bmpColor.bmPlanes);
// Create the ICONDIR structure
iconDir.bWidth = (BYTE)bmpColor.bmWidth;
iconDir.bHeight = (BYTE)bmpColor.bmHeight;
iconDir.bColorCount = nColorCount;
iconDir.bReserved = 0;
iconDir.wPlanes = bmpColor.bmPlanes;
iconDir.wBitCount = bmpColor.bmBitsPixel;
iconDir.dwBytesInRes = sizeof(BITMAPINFOHEADER) + nImageBytes;
iconDir.dwImageOffset = nImageOffset;
// Write to disk
memcpy(&buffer[*pBufferOffset], &iconDir, sizeof(iconDir));
(*pBufferOffset) += sizeof(iconDir);
// Free resources
DeleteObject(iconInfo.hbmColor);
DeleteObject(iconInfo.hbmMask);
}
static UINT WriteIconData(BYTE* buffer, int* pBufferOffset, HBITMAP hBitmap)
{
BITMAP bmp;
int i;
BYTE* pIconData;
UINT nBitmapBytes;
GetObject(hBitmap, sizeof(BITMAP), &bmp);
nBitmapBytes = NumBitmapBytes(&bmp);
pIconData = (BYTE*)malloc(nBitmapBytes);
GetBitmapBits(hBitmap, nBitmapBytes, pIconData);
// bitmaps are stored inverted (vertically) when on disk..
// so write out each line in turn, starting at the bottom + working
// towards the top of the bitmap. Also, the bitmaps are stored in packed
// in memory - scanlines are NOT 32bit aligned, just 1-after-the-other
for (i = bmp.bmHeight - 1; i >= 0; i--)
{
memcpy(&buffer[*pBufferOffset], pIconData + (i * bmp.bmWidthBytes), bmp.bmWidthBytes);
(*pBufferOffset) += bmp.bmWidthBytes;
// extend to a 32bit boundary (in the file) if necessary
if (bmp.bmWidthBytes & 3)
{
DWORD padding = 0;
memcpy(&buffer[*pBufferOffset], &padding, 4 - bmp.bmWidthBytes);
(*pBufferOffset) += 4 - bmp.bmWidthBytes;
}
}
free(pIconData);
return nBitmapBytes;
}
//
// Create a .ICO file, using the specified array of HICON images
//
BOOL SaveIcon3(HICON hIcon[], int nNumIcons, BYTE* buffer, int* pWritten)
{
int i;
int* pImageOffset = (int*)malloc(nNumIcons * sizeof(int));
int bufferOffset = 0;
if (hIcon == 0 || nNumIcons < 1)
return 0;
//
// Write the iconheader first of all
//
ICONHEADER iconheader;
// Setup the icon header
iconheader.idReserved = 0; // Must be 0
iconheader.idType = 1; // Type 1 = ICON (type 2 = CURSOR)
iconheader.idCount = nNumIcons; // number of ICONDIRs
// Write the header to disk
memcpy(&(buffer[bufferOffset]), &iconheader, sizeof(iconheader));
bufferOffset += sizeof(iconheader);
//
// Leave space for the IconDir entries
//
bufferOffset += sizeof(ICONDIR) * nNumIcons;
//
// Now write the actual icon images!
//
for (i = 0; i < nNumIcons; i++) {
ICONINFO iconInfo;
BITMAP bmpColor, bmpMask;
// GetIconBitmapInfo
GetIconBitmapInfo(hIcon[i], &iconInfo, &bmpColor, &bmpMask);
// record the file-offset of the icon image for when we write the icon directories
pImageOffset[i] = bufferOffset;
// WriteIconImageHeader
BITMAPINFOHEADER biHeader;
UINT nImageBytes;
// calculate how much space the COLOR and MASK bitmaps take
nImageBytes = NumBitmapBytes(&bmpColor) + NumBitmapBytes(&bmpMask);
// write the ICONIMAGE to disk (first the BITMAPINFOHEADER)
ZeroMemory(&biHeader, sizeof(biHeader));
// Fill in only those fields that are necessary
biHeader.biSize = sizeof(biHeader);
biHeader.biWidth = bmpColor.bmWidth;
biHeader.biHeight = bmpColor.bmHeight * 2; // height of color+mono
biHeader.biPlanes = bmpColor.bmPlanes;
biHeader.biBitCount = bmpColor.bmBitsPixel;
biHeader.biSizeImage = nImageBytes;
// write the BITMAPINFOHEADER
memcpy(&(buffer[bufferOffset]), &biHeader, sizeof(biHeader));
bufferOffset += sizeof(biHeader);
// color and mask bitmaps
WriteIconData(buffer, &bufferOffset, iconInfo.hbmColor);
WriteIconData(buffer, &bufferOffset, iconInfo.hbmMask);
DeleteObject(iconInfo.hbmColor);
DeleteObject(iconInfo.hbmMask);
}
*pWritten = bufferOffset;
//
// Lastly, skip back and write the icon directories.
//
bufferOffset = sizeof(ICONHEADER);
for (i = 0; i < nNumIcons; i++)
{
WriteIconDirectoryEntry(buffer, &bufferOffset, i, hIcon[i], pImageOffset[i]);
}
free(pImageOffset);
return 1;
}
void main()
{
HICON hIconLarge;
HICON hIconSmall;
int extractIcon = ExtractIconExW(L"E:\\Program Files\\Microsoft VS Code Insiders\\Code - Insiders.exe", 0, &hIconLarge, &hIconSmall, 1);
if (extractIcon <= 0) {
std::cout << "No icon";
return;
}
BYTE buffer[(256 * 256) * 4]; // (256x256) Max Windows Icon Size x 4 bytes (32 bits)
int written;
SaveIcon3(&hIconLarge, 1, buffer, &written);
std::ofstream file;
file.open("E:/t.ico", std::ios_base::binary);
assert(file.is_open());
for (int i = 0; i < sizeof(buffer) / sizeof(buffer[0]); ++i)
file.write((char*)(buffer + i * sizeof(buffer[0])), sizeof(buffer[0]));
file.close();
}
Found a solution.
static BITMAP_AND_BYTES createAlphaChannelBitmapFromIcon(HICON hIcon) {
// Get the icon info
ICONINFO iconInfo = {0};
GetIconInfo(hIcon, &iconInfo);
// Get the screen DC
HDC dc = GetDC(NULL);
// Get icon size info
BITMAP bm = {0};
GetObject( iconInfo.hbmColor, sizeof( BITMAP ), &bm );
// Set up BITMAPINFO
BITMAPINFO bmi = {0};
bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bmi.bmiHeader.biWidth = bm.bmWidth;
bmi.bmiHeader.biHeight = -bm.bmHeight;
bmi.bmiHeader.biPlanes = 1;
bmi.bmiHeader.biBitCount = 32;
bmi.bmiHeader.biCompression = BI_RGB;
// Extract the color bitmap
int nBits = bm.bmWidth * bm.bmHeight;
int32_t* colorBits = new int32_t[nBits];
GetDIBits(dc, iconInfo.hbmColor, 0, bm.bmHeight, colorBits, &bmi, DIB_RGB_COLORS);
// Check whether the color bitmap has an alpha channel.
// (On my Windows 7, all file icons I tried have an alpha channel.)
BOOL hasAlpha = FALSE;
for (int i = 0; i < nBits; i++) {
if ((colorBits[i] & 0xff000000) != 0) {
hasAlpha = TRUE;
break;
}
}
// If no alpha values available, apply the mask bitmap
if (!hasAlpha) {
// Extract the mask bitmap
int32_t* maskBits = new int32_t[nBits];
GetDIBits(dc, iconInfo.hbmMask, 0, bm.bmHeight, maskBits, &bmi, DIB_RGB_COLORS);
// Copy the mask alphas into the color bits
for (int i = 0; i < nBits; i++) {
if (maskBits[i] == 0) {
colorBits[i] |= 0xff000000;
}
}
delete[] maskBits;
}
// Release DC and GDI bitmaps
ReleaseDC(NULL, dc);
::DeleteObject(iconInfo.hbmColor);
::DeleteObject(iconInfo.hbmMask);
// Create GDI+ Bitmap
Gdiplus::Bitmap* bmp = new Gdiplus::Bitmap(bm.bmWidth, bm.bmHeight, bm.bmWidth*4, PixelFormat32bppARGB, (BYTE*)colorBits);
BITMAP_AND_BYTES ret = {bmp, colorBits};
return ret;
}
Original post: https://stackoverflow.com/a/22885412/1456151
And also from PowerToys source code:
HBITMAP CreateBitmapFromIcon(_In_ HICON hIcon, _In_opt_ UINT width, _In_opt_ UINT height)
{
HBITMAP hBitmapResult = NULL;
// Create compatible DC
HDC hDC = CreateCompatibleDC(NULL);
if (hDC != NULL)
{
// Get bitmap rectangle size
RECT rc = { 0 };
rc.left = 0;
rc.right = (width != 0) ? width : GetSystemMetrics(SM_CXSMICON);
rc.top = 0;
rc.bottom = (height != 0) ? height : GetSystemMetrics(SM_CYSMICON);
// Create bitmap compatible with DC
BITMAPINFO BitmapInfo;
ZeroMemory(&BitmapInfo, sizeof(BITMAPINFO));
BitmapInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
BitmapInfo.bmiHeader.biWidth = rc.right;
BitmapInfo.bmiHeader.biHeight = rc.bottom;
BitmapInfo.bmiHeader.biPlanes = 1;
BitmapInfo.bmiHeader.biBitCount = 32;
BitmapInfo.bmiHeader.biCompression = BI_RGB;
HDC hDCBitmap = GetDC(NULL);
HBITMAP hBitmap = CreateDIBSection(hDCBitmap, &BitmapInfo, DIB_RGB_COLORS, NULL, NULL, 0);
ReleaseDC(NULL, hDCBitmap);
if (hBitmap != NULL)
{
// Select bitmap into DC
HBITMAP hBitmapOld = (HBITMAP)SelectObject(hDC, hBitmap);
if (hBitmapOld != NULL)
{
// Draw icon into DC
if (DrawIconEx(hDC, 0, 0, hIcon, rc.right, rc.bottom, 0, NULL, DI_NORMAL))
{
// Restore original bitmap in DC
hBitmapResult = (HBITMAP)SelectObject(hDC, hBitmapOld);
hBitmapOld = NULL;
hBitmap = NULL;
}
if (hBitmapOld != NULL)
{
SelectObject(hDC, hBitmapOld);
}
}
if (hBitmap != NULL)
{
DeleteObject(hBitmap);
}
}
DeleteDC(hDC);
}
return hBitmapResult;
}

C++: Hbitmap/BITMAP into .bmp file [duplicate]

This question already has answers here:
Save HBITMAP to *.bmp file using only Win32
(5 answers)
Closed 8 years ago.
Ok, whole story is, I am trying to use Leptonica+Tesseract OCR in C++ to take a screenshot, save it to a *.bmp file, then load it back up to OCR with it. I won't need to do this frequently, but as I cannot seem to copy the screenshot data directly into a Leptonica PIX structure, I need to save it to a file first..actually a solution to this would be preferably.
Here's some code I've found online, trying to help me out.
Screen cap:
HBITMAP ScreenCapture(){
int width=100;
int height=100;
// get the device context of the screen
HDC hScreenDC = CreateDC(L"DISPLAY", NULL, NULL, NULL);
// and a device context to put it in
HDC hMemoryDC = CreateCompatibleDC(hScreenDC);
int x = GetDeviceCaps(hScreenDC, HORZRES);
int y = GetDeviceCaps(hScreenDC, VERTRES);
// maybe worth checking these are positive values
HBITMAP hBitmap = CreateCompatibleBitmap(hScreenDC, x, y);
// get a new bitmap
HBITMAP hOldBitmap = (HBITMAP)SelectObject(hMemoryDC, hBitmap);
BitBlt(hMemoryDC, 0, 0, width, height, hScreenDC, 0, 0, SRCCOPY);
hBitmap = (HBITMAP)SelectObject(hMemoryDC, hOldBitmap);
//GlobalAlloc(GPTR, hBitmap)
WriteDIB(L"test.bmp", (HGLOBAL)hBitmap);
// clean up
DeleteDC(hMemoryDC);
DeleteDC(hScreenDC);
return hBitmap;
// now your image is held in hBitmap. You can save it or do whatever with it
}
Attempt to write function:
BOOL WriteDIB( LPTSTR szFile, HANDLE hDIB)
{
cout<<endl<<"Running save function";
/*HANDLE hDIB=GlobalAlloc(GPTR, sizeof(hDIBtochange));//this doesn't work, the result is four. Also the HANDLE parameter's name would be changed to hDIBtochange, so that the rest of the function uses the old 'hDIB' throughout
cout<<endl<<sizeof(hDIBtochange);*/
BITMAPFILEHEADER hdr;
LPBITMAPINFOHEADER lpbi;
if (!hDIB)
return FALSE;
CFile file;
if( !file.Open( szFile, CFile::modeWrite|CFile::modeCreate) )
return FALSE;
lpbi = (LPBITMAPINFOHEADER)hDIB;
int nColors = 1 << lpbi->biBitCount;
// 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));
// Write the file header
file.Write( &hdr, sizeof(hdr) );
// Write the DIB header and the bits
file.Write( lpbi, GlobalSize(hDIB) );
return TRUE;
}
Shamelessly copied from people's posts over the years.
Ok! Problem I face is, I cannot seem to understand how to GlobalAlloc the Hbitmap into a globally accessible Handle, that can be converted or use with LPBITMAPINFOHEADER.
Soon as lpbi is created, every single field inside of it is "Unable to read memory" error in Visual Studio 2012 debugging. It's inaccessible, despite being created.
Solutions..
Go straight from screencap to PIX, inside of memory..
Find a way to save to bitmap and create them periodically to read..
Find another way entirely that makes more sense..
Preferring first, but, I'm asking for a solution in this, to the second one..or third.
If you need more info I can try to provide it. This mostly boils down to "I've never done code like this before and it wasn't taught in my classes so I'm trying to learn as I go".
A much easier way to save an HBITMAP to file is to make use of GDI+.
This gives you the advantage of being able to save to any format that windows supports natively, while freeing you from the muck of playing around with or even needing to understand, various image formats.
In the below example, I've just used LoadImage as a quik and dirty way of loading a pre-existing image - you could simply use the HBITMAP you've already captured.
Here's an example that loads a bitmap and saves it again. (I had initially used "image/png" as the output type, along with an appropriate output filename)
#include <windows.h>
#include <gdiplus.h>
using namespace Gdiplus;
int GetEncoderClsid(const WCHAR* format, CLSID* pClsid)
{
UINT num = 0; // number of image encoders
UINT size = 0; // size of the image encoder array in bytes
ImageCodecInfo* pImageCodecInfo = NULL;
GetImageEncodersSize(&num, &size);
if(size == 0)
return -1; // Failure
pImageCodecInfo = (ImageCodecInfo*)(malloc(size));
if(pImageCodecInfo == NULL)
return -1; // Failure
GetImageEncoders(num, size, pImageCodecInfo);
for(UINT j = 0; j < num; ++j)
{
if( wcscmp(pImageCodecInfo[j].MimeType, format) == 0 )
{
*pClsid = pImageCodecInfo[j].Clsid;
free(pImageCodecInfo);
return j; // Success
}
}
free(pImageCodecInfo);
return -1; // Failure
}
int main()
{
GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR gdiplusToken;
GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
HBITMAP hBitmap = (HBITMAP)LoadImage(GetModuleHandle(NULL), "babe.bmp", IMAGE_BITMAP, 0,0, LR_LOADFROMFILE);
Bitmap *image = new Bitmap(hBitmap, NULL);
CLSID myClsId;
int retVal = GetEncoderClsid(L"image/bmp", &myClsId);
image->Save(L"output.bmp", &myClsId, NULL);
delete image;
GdiplusShutdown(gdiplusToken);
return 0;
}
I recently had to do the same thing you are doing and successfully used GlobalAlloc.
The basis of this code is from This MSDN Article.
It looks like you Got your example code from here.
MSDN is really reliable for win32 operations, definitely prefer it over other sites in my experaince.
What seems to be happening is that the sizeof(hDIBtochange) is returning 4, so you are only allocating 4 bytes of memory. which would not be enough to hold a pbi structure.
Here is my code with a GlobalAlloc which hopefully will show the correct usage.
void
WriteBmpTofile(const bool remote, LPSTR pszFile, PBITMAPINFO pbi, HBITMAP hBmp, HDC hDC)
{
HANDLE hFile;
BITMAPFILEHEADER hdr;
PBITMAPINFOHEADER pbih;
LPBYTE lpBits;
DWORD dwTemp;
pbih = (PBITMAPINFOHEADER)pbi;
lpBits = (LPBYTE) GlobalAlloc(GMEM_FIXED, pbih->biSizeImage);
if(!lpBits)
{
return; // could not allocate bitmap
}
GetDIBits(hDC, hBmp, 0, (WORD)pbih->biHeight, lpBits, pbi, DIB_RGB_COLORS);
hFile = CreateFile(pszFile,
GENERIC_READ | GENERIC_WRITE,
0,
NULL,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
NULL);
if(hFile == INVALID_HANDLE_VALUE)
{
return; // Could not open screenshot file
}
// type == BM
hdr.bfType = 0x4d42;
hdr.bfSize = (sizeof(BITMAPFILEHEADER) + pbih->biSize + pbih->biClrUsed * sizeof(RGBQUAD) + pbih->biSizeImage);
hdr.bfReserved1 = 0;
hdr.bfReserved2 = 0;
hdr.bfOffBits = sizeof(BITMAPFILEHEADER) + pbih->biSize + pbih->biClrUsed * sizeof(RGBQUAD);
// write the bitmap file header to file
WriteFile(hFile, (LPVOID)&hdr, sizeof(BITMAPFILEHEADER), &dwTemp, NULL);
// write the bitmap header to file
WriteFile(hFile, (LPVOID)pbih, sizeof(BITMAPINFOHEADER) + pbih->biClrUsed * sizeof(RGBQUAD), &dwTemp, NULL);
// copy the bitmap colour data into the file
WriteFile(hFile, (LPSTR)lpBits, pbih->biSizeImage, &dwTemp, NULL);
CloseHandle(hFile);
GlobalFree((HGLOBAL)lpBits);
}
Here is the top function in that MSDN article, if you need it (again modified by me).
PBITMAPINFO
Print::CreateBitmapInfo(HBITMAP hBmp)
{
BITMAP bmp;
PBITMAPINFO pbmi;
GetObject(hBmp, sizeof(BITMAP), &bmp);
pbmi = static_cast<PBITMAPINFO>(LocalAlloc(LPTR, sizeof(BITMAPINFOHEADER)));
pbmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
pbmi->bmiHeader.biWidth = bmp.bmWidth;
pbmi->bmiHeader.biHeight = bmp.bmHeight;
pbmi->bmiHeader.biPlanes = bmp.bmPlanes; // we are assuming that there is only one plane
pbmi->bmiHeader.biBitCount = bmp.bmBitsPixel;
// no compression this is an rgb bitmap
pbmi->bmiHeader.biCompression = BI_RGB;
// calculate size and align to a DWORD (8bit), we are assuming there is only one plane.
pbmi->bmiHeader.biSizeImage = ((pbmi->bmiHeader.biWidth * bmp.bmBitsPixel +31) & -31) * pbmi->bmiHeader.biHeight;
// all device colours are important
pbmi->bmiHeader.biClrImportant = 0;
return pbmi;
}
I'm guessing you got your code from here Storing an Image. A while back I had to modify the code to work with WinCE 5.0 and WinCE 6.0. Here is the beta-sample though it is kinda messy. It does it without the GlobalAlloc. It uses CreateDibSection instead.
int CreateBMPFile(HWND hwnd, LPCTSTR pszFile, PBITMAPINFO pbi,
HBITMAP hBMP, HDC hDC)
{
HANDLE hf; // file handle
BITMAPFILEHEADER hdr; // bitmap file-header
PBITMAPINFOHEADER pbih; // bitmap info-header
//LPBYTE lpBits; // memory pointer
DWORD dwTotal; // total count of bytes
DWORD cb; // incremental count of bytes
BYTE *hp; // byte pointer
DWORD dwTmp;
int ret = 0;
pbi = CreateBitmapInfoStruct(NULL, hBMP);
if(pbi == NULL)
{
return ret;
}
pbih = (PBITMAPINFOHEADER) pbi;
/*
lpBits = (LPBYTE) GlobalAlloc(GMEM_FIXED, pbih->biSizeImage);
if (!lpBits)
{
//errhandler("GlobalAlloc", hwnd);
return;
}
*/
RGBQUAD *rgbq;
rgbq = pbi->bmiColors;
PALETTEENTRY pe[256];
GetSystemPaletteEntries(hDC, 0, pbih->biClrUsed, pe);
for(DWORD i = 0; i < pbih->biClrUsed; i++)
{
rgbq[i].rgbRed = pe[i].peRed;
rgbq[i].rgbBlue = pe[i].peBlue;
rgbq[i].rgbGreen = pe[i].peGreen;
rgbq[i].rgbReserved = 0;
}
// CE5.0 + CE6.0
HDC tHDC;
tHDC = CreateCompatibleDC(hDC);
HBITMAP h = CreateDIBSection(hDC, pbi, DIB_PAL_COLORS, (void **)&hp, NULL, 0);
if(h == NULL)
{
goto close_bmp;
}
SelectObject(tHDC, h);
BitBlt(tHDC, 0, 0, SCREEN_W, SCREEN_H, hDC, 0, 0, SRCCOPY);
/*
// Retrieve the color table (RGBQUAD array) and the bits
// (array of palette indices) from the DIB.
if (!GetDIBits(hDC, hBMP, 0, (WORD) pbih->biHeight, lpBits, pbi,
DIB_RGB_COLORS))
{
//errhandler("GetDIBits", hwnd);
return;
}
*/
// Create the .BMP file.
hf = CreateFile(pszFile,
GENERIC_READ | GENERIC_WRITE,
(DWORD) 0,
NULL,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
(HANDLE) NULL);
if (hf == INVALID_HANDLE_VALUE)
{
//errhandler("CreateFile", hwnd);
goto close_bmp;
}
hdr.bfType = 0x4d42; // 0x42 = "B" 0x4d = "M"
// Compute the size of the entire file.
hdr.bfSize = (DWORD) (sizeof(BITMAPFILEHEADER) +
pbih->biSize + pbih->biClrUsed
* sizeof(RGBQUAD) + pbih->biSizeImage);
hdr.bfReserved1 = 0;
hdr.bfReserved2 = 0;
// Compute the offset to the array of color indices.
hdr.bfOffBits = (DWORD) sizeof(BITMAPFILEHEADER) +
pbih->biSize + pbih->biClrUsed
* sizeof (RGBQUAD);
// Copy the BITMAPFILEHEADER into the .BMP file.
if (!WriteFile(hf, (LPVOID) &hdr, sizeof(BITMAPFILEHEADER),
(LPDWORD) &dwTmp, NULL))
{
//errhandler("WriteFile", hwnd);
goto close_bmp;
}
// Copy the BITMAPINFOHEADER and RGBQUAD array into the file.
if (!WriteFile(hf, (LPVOID) pbih, sizeof(BITMAPINFOHEADER)
+ pbih->biClrUsed * sizeof (RGBQUAD),
(LPDWORD) &dwTmp, ( NULL)))
{
//errhandler("WriteFile", hwnd);
}
// Copy the array of color indices into the .BMP file.
dwTotal = cb = pbih->biSizeImage;
//hp = lpBits;
if (!WriteFile(hf, (LPSTR) hp, (int) cb, (LPDWORD) &dwTmp,NULL))
{
//errhandler("WriteFile", hwnd);
goto close_bmp;
}
close_bmp:
// Close the .BMP file.
if(hf != INVALID_HANDLE_VALUE)
{
if (!CloseHandle(hf))
{
//errhandler("CloseHandle", hwnd);
}
else
{
ret = 1;
}
}
// Free memory.
// GlobalFree((HGLOBAL)lpBits);
if(tHDC != NULL)
DeleteObject(tHDC);
if(h != NULL)
DeleteObject(h);
if(pbi != NULL)
{
//LocalFree(pbi);
free(pbi);
}
return ret;
}

Getting HBITMAP from layered window - incorrect data

I created a layered window (with WS_EX_LAYERED), size of about 400X300 px.
When drawing the window (using UpdateLayeredWindow) everything works great.
The problem is that I'm unable to get the HBITMAP of the window after drawing it.
When trying to get the HBITMAP through the window's HDC, I get an empty (black) bitmap, the size of my entire desktop (1920X1080 px insted of 400X300 px).
Does anybody know if it is even possible to get the HDC\HBITMAP of a layered window?
Code samples
Here's the code of how I draw the layered window (again, works great):
void MyLayeredWindow::DrawLayered() const
{
RECT rcWindow;
GetWindowRect(rcWindow);
int nWidth = abs(rcWindow.right - rcWindow.left);
int nHeight = abs(rcWindow.bottom - rcWindow.top);
// Create 32Bit bitmap to apply transparency
// (have to set negative height because if not the (0,0) point would be the bottom left instead of top left)
VOID *ppvBits = NULL;
BITMAPINFO BitmapInfo = {0};
BitmapInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
BitmapInfo.bmiHeader.biWidth = nWidth;
BitmapInfo.bmiHeader.biHeight = -nHeight;
BitmapInfo.bmiHeader.biPlanes = 1;
BitmapInfo.bmiHeader.biBitCount = 32;
BitmapInfo.bmiHeader.biCompression = BI_RGB;
// Copy view buffer to a temp DC and bitmap
HDC hDcTemp = ::CreateCompatibleDC(NULL);
assert(hDcTemp);
HBITMAP hBitmapTemp = ::CreateDIBSection(hDcTemp, &BitmapInfo, DIB_RGB_COLORS, &ppvBits, NULL, 0);
assert(hBitmapTemp && hBitmapTemp!=(HBITMAP)ERROR_INVALID_PARAMETER)
::SelectObject(hDcTemp, hBitmapTemp);
// Darwing the window's conent here
// ....
// ....
// Call UpdateLayeredWindow
BLENDFUNCTION blend = {0};
blend.BlendOp = AC_SRC_OVER;
blend.SourceConstantAlpha = 190;
blend.AlphaFormat = AC_SRC_ALPHA;
SIZE sizeWnd = {0};
sizeWnd.cx = nWidth;
sizeWnd.cy = nHeight;
POINT ptPos = {0};
ptPos.x = rcWindow.left;
ptPos.y = rcWindow.top;
POINT ptSrc = {0,0};
::UpdateLayeredWindow(m_hWnd, NULL, &ptPos, &sizeWnd, hDcTemp, &ptSrc, 0, &blend, ULW_ALPHA);
if(hDcTemp)
::DeleteDC(hDcTemp);
if(hBitmapTemp)
::DeleteObject(hBitmapTemp);
}
Here's the code of how I capture the window's bitmap and save it to a file:
(NOTICE: It works of "normal" windows, such as the Calculator)
bool MyLayeredWindow::SaveBitmapFile(__in const HWND& hWnd, __in const wstring& sFileName)
{
HDC hDC = ::GetDC(hWnd);
// get bitmap of DC
HBITMAP hBmp = (HBITMAP)::GetCurrentObject( hDC, OBJ_BITMAP );
// get info of bitmap
BITMAPINFO stBmpInfo;
stBmpInfo.bmiHeader.biSize = sizeof( stBmpInfo.bmiHeader );
stBmpInfo.bmiHeader.biBitCount = 0;
GetDIBits( hDC, hBmp, 0, 0, NULL, &stBmpInfo, DIB_RGB_COLORS );
// init info size
ULONG iBmpInfoSize;
switch( stBmpInfo.bmiHeader.biBitCount )
{
case 24:
iBmpInfoSize = sizeof(BITMAPINFOHEADER);
break;
case 16:
case 32:
iBmpInfoSize = sizeof(BITMAPINFOHEADER)+sizeof(DWORD)*3;
break;
default:
iBmpInfoSize = sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * ( 1 << stBmpInfo.bmiHeader.biBitCount );
break;
}
// copy header
PBITMAPINFO pstBmpInfo = NULL;
if( iBmpInfoSize != sizeof(BITMAPINFOHEADER) )
{
pstBmpInfo = (PBITMAPINFO)GlobalAlloc( GMEM_FIXED | GMEM_ZEROINIT, iBmpInfoSize );
PBYTE pbtBmpInfoDest = (PBYTE)pstBmpInfo;
PBYTE pbtBmpInfoSrc = (PBYTE)&stBmpInfo;
ULONG iSizeTmp = sizeof( BITMAPINFOHEADER );
while( iSizeTmp-- )
*( ( pbtBmpInfoDest )++ ) = *( ( pbtBmpInfoSrc )++ );
}
// create file
HANDLE hFile = CreateFile( sFileName.c_str(), GENERIC_WRITE, 0 , NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_ARCHIVE, NULL );
// init bmp file header
BITMAPFILEHEADER stBmpFileHder = {0};
stBmpFileHder.bfType = 0x4D42; // 'BM'
stBmpFileHder.bfSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + iBmpInfoSize + pstBmpInfo->bmiHeader.biSizeImage;
stBmpFileHder.bfReserved1 = 0;
stBmpFileHder.bfReserved2 = 0;
stBmpFileHder.bfOffBits = sizeof(BITMAPFILEHEADER) + iBmpInfoSize;
// write header to file
DWORD dRet;
WriteFile( hFile, (LPCVOID)&stBmpFileHder, sizeof(BITMAPFILEHEADER), &dRet, NULL );
// allocate size for rest of bmp (body)
PBYTE pBits = (PBYTE)GlobalAlloc( GMEM_FIXED | GMEM_ZEROINIT, stBmpInfo.bmiHeader.biSizeImage );
// get bmp bits
HBITMAP hBmpOld;
HBITMAP hTmpBmp = CreateCompatibleBitmap( hDC, pstBmpInfo->bmiHeader.biWidth, pstBmpInfo->bmiHeader.biHeight );
hBmpOld = (HBITMAP)SelectObject( hDC, hTmpBmp );
GetDIBits( hDC, hBmp, 0, pstBmpInfo->bmiHeader.biHeight, (LPSTR)pBits, pstBmpInfo, DIB_RGB_COLORS );
// write bmp info
WriteFile( hFile, (LPCVOID)pstBmpInfo, iBmpInfoSize, &dRet, NULL );
// write bmp bits
WriteFile( hFile, (LPCVOID)pBits, pstBmpInfo->bmiHeader.biSizeImage, &dRet, NULL );
// release handles and free memory
SelectObject( hDC, hBmpOld );
DeleteObject( hTmpBmp );
CloseHandle( hFile );
GlobalFree( pstBmpInfo );
GlobalFree( pBits );
ReleaseDC( hWnd, hDC );
return true;
}
Thanks!
Since I didn't get any better answer, I simply called a "Draw" function that paints my layered window, onto a temporary HDC.
Meaning I don't copy existing bitmap, but create an identical one, using the same drawing function.
I'd still love to get a better answer for this question.

array[byte] to HBITMAP or CBitmap

I have an array of bytes (which I read through a stream directly from a .bmp and then store as a BLOB in a database) which I want to display as icons in a CImageList. Therefore I want to somehow load my data into an HBITMAP or CBitmap. I have done it like this up to now, reading from a file:
hPic = (HBITMAP)LoadImage(NULL, strPath, IMAGE_BITMAP, dwWidth, dwHeight, LR_LOADFROMFILE | LR_VGACOLOR);
...
CBitmap bitmap;
bitmap.Attach(hPicRet);
But obviously, that only works for files, but not for byte-arrays. How can I get the same result, but reading from an array of byte?
Edit:
Note that my array does not contain just the colour information, but rather the complete file as it is written on disk, including all headers and meta-data. It seems to me that discarding all that information is a bad idea.
Assuming you have the information loaded into a BYTE array named bytes....
BITMAPFILEHEADER* bmfh;
bmfh = (BITMAPFILEHEADER*)bytes;
BITMAPINFOHEADER* bmih;
bmih = (BITMAPINFOHEADER*)(bytes + sizeof(BITMAPFILEHEADER));
BITMAPINFO* bmi;
bmi = (BITMAPINFO*)bmih;
void* bits;
bits = (void*)(bytes + bmfh->bfOffBits);
HDC hdc = ::GetDC(NULL);
HBITMAP hbmp = CreateDIBitmap(hdc, bmih, CBM_INIT, bits, bmi, DIB_RGB_COLORS) ;
::ReleaseDC(NULL, hdc);
It's a little messy and could use a hefty dose of error checking, but the basic idea is sound.
Following sample could help you.
BITMAPINFO bmInfo;
BITMAPINFOHEADER &bmInfohdr = (BITMAPINFOHEADER)bmInfo.bmiHeader;
bmInfohdr.biSize = 40 + 255; //I think it's not of use
bmInfohdr.biWidth = x;
bmInfohdr.biHeight = y;
bmInfohdr.biPlanes=1;
bmInfohdr.biBitCount=8;
bmInfohdr.biCompression=0;
bmInfohdr.biSizeImage=0;
bmInfohdr.biXPelsPerMeter = 0;
bmInfohdr.biYPelsPerMeter = 0;
bmInfohdr.biClrUsed = 0;
bmInfohdr.biClrImportant = 0;
// should I allocate memory further than the
// bmColors[1]?? anyway the compiler gives an
// error for type mismatch!
//bmInfo.bmiColors = (RGBQUAD *)
malloc(sizeof(RGBQUAD) * 256);
// here I define the 256 graylevel palette
for (int i=0; i<256; i++)
{
bmInfo.bmiColors[i].rgbRed = i;
bmInfo.bmiColors[i].rgbGreen = i;
bmInfo.bmiColors[i].rgbBlue = i;
}
BYTE *matrix;
matrix = (BYTE*)malloc(size*sizeof(BYTE));
// here I put the BYTE values of the pixels
CDC *pdcDest = this->GetDC();
HBITMAP hBmp = CreateDIBitmap( pdcDest->m_hDC,
&bmInfohdr,
CBM_INIT,
matrix,
&bmInfo,
DIB_RGB_COLORS);
m_bmpBitmap.Attach( hBmp );
Something like this worked for me:
int bitmap[WX*WY]; // truecolor bitmap data
BITMAPINFO bm = { sizeof(BITMAPINFOHEADER), WX, WY, 1, 32, BI_RGB, 0, 0, 0, 0, 0 };
HBITMAP bmp = CreateDIBSection( GetDC(win), &bm, DIB_RGB_COLORS, (void**)&bitmap, 0,0 );
(This is specifically configured for 32-bit colors, but you can specify any kind).
Ok, here's a complete example: http://nishi.dreamhosters.com/u/so_bmp_v0.zip
#include <stdio.h>
#include <windows.h>
#pragma comment(lib,"gdi32.lib")
#pragma comment(lib,"user32.lib")
char buf[1<<22];
int main( int argc, char **argv ) {
FILE* f = fopen( "winnt.bmp", "rb" ); if( f==0 ) return 1;
fread( buf, 1,sizeof(buf), f );
fclose(f);
BITMAPFILEHEADER& bfh = (BITMAPFILEHEADER&)buf[0];
BITMAPINFO& bi = (BITMAPINFO&)buf[sizeof(BITMAPFILEHEADER)];
BITMAPINFOHEADER& bih = bi.bmiHeader;
char* bitmap = &buf[bfh.bfOffBits];
int WX=1024, WY=512; // window's width/height
int SX=bih.biWidth, SY=bih.biHeight;
HWND win = CreateWindow( "STATIC", "Bitmap test", 0x90C0, 0,0, WX,WY, 0,0, GetModuleHandle(0), 0 );
MSG msg;
PAINTSTRUCT ps;
HDC DC = GetDC(win); // window's DC
HBITMAP dib = CreateDIBitmap( DC, &bih, CBM_INIT, bitmap, &bi, DIB_RGB_COLORS );
HDC dibDC = CreateCompatibleDC( DC ); SelectObject( dibDC, dib );
ShowWindow( win, SW_SHOWNOACTIVATE );
SetFocus( win );
while( GetMessage(&msg,win,0,0) ) {
int m = msg.message;
if( m==WM_PAINT ) {
DC = BeginPaint( win, &ps );
StretchBlt( DC, 0,0,WX,WY, dibDC,0,0,SX,SY, SRCCOPY );
EndPaint( win, &ps );
} else if( (m==WM_KEYDOWN) || (m==WM_SYSKEYDOWN) ) {
break;
} else {
DispatchMessage(&msg);
}
}
return 0;
}