I need to get an array of pixel color data values of a picture captured from the window, and from a file (.bmp).
I checked through a debugger with breakpoints. The HBITMAP_ToPixelData() method with the HBITMAP created from the file, returns an array with the correct values, but if the HBITMAP created by capturing, the returned array is empty, all values are zero, while the "success" variable is also zero. GetDIBits() for some reason does not work, although the same HBITMAP is correctly transferred to the clipboard and inserted into the photo editor. What could be the problem?
Main.cpp code:
#include <windows.h>
#include <iostream>
#include <vector>
#include "ImageProcessing.h"
using namespace std;
int main()
{
ImageProcessing a1;
HBITMAP hbmp1 = a1.CaptureWindow(L"Notepad");
vector<unsigned char> p_data1 = a1.HBITMAP_ToPixelData(hbmp1);
HBITMAP hbmp2 = a1.HBITMAP_FromFile();
vector<unsigned char> p_data2 = a1.HBITMAP_ToPixelData(hbmp2);
}
ImageProcessing.h code:
#pragma once
#include <vector>
#include <windows.h>
using namespace std;
class ImageProcessing
{
#pragma once
public:
HBITMAP HBITMAP_FromFile()
{
wstring a = L"C:/pic.bmp";
HBITMAP hBitmap = (HBITMAP)LoadImage(NULL, a.c_str(), IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
return hBitmap;
}
vector<unsigned char> HBITMAP_ToPixelData(HBITMAP hbmp)
{
BITMAP bmp = {};
BITMAPINFO bmp_info = {};
vector <unsigned char> Pixels = vector <unsigned char>();
OpenClipboard(NULL);
EmptyClipboard();
SetClipboardData(CF_BITMAP, hbmp);
CloseClipboard();
HDC DC = CreateCompatibleDC(NULL);
HGDIOBJ old_bmp = SelectObject(DC, hbmp);
GetObject(hbmp, sizeof(bmp), &bmp);
int width = bmp.bmWidth;
int height = bmp.bmHeight;
bmp_info.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bmp_info.bmiHeader.biWidth = width;
bmp_info.bmiHeader.biHeight = height;
bmp_info.bmiHeader.biPlanes = 1;
bmp_info.bmiHeader.biBitCount = 24;
bmp_info.bmiHeader.biCompression = BI_RGB;
bmp_info.bmiHeader.biSizeImage = width * 3 * height;
Pixels.resize(bmp_info.bmiHeader.biSizeImage);
int success = GetDIBits(DC, hbmp, 0, width, &Pixels[0], &bmp_info, DIB_RGB_COLORS);
SelectObject(DC, old_bmp);
DeleteDC(DC);
return Pixels;
}
HBITMAP CaptureWindow(wstring window_title)
{
SetThreadDpiAwarenessContext(DPI_AWARENESS_CONTEXT_SYSTEM_AWARE);
HWND hwnd = FindWindow(NULL, (window_title.c_str()));
if (hwnd == NULL)
{
return NULL;
}
// get window dimentions
RECT windowsize;
GetClientRect(hwnd, &windowsize);
int w = windowsize.right;
int h = windowsize.bottom;
// copy window to bitmap
HDC hdc = GetDC(hwnd);
HDC hCaptureDC = CreateCompatibleDC(hdc);
HBITMAP hBitmap = CreateCompatibleBitmap(hdc, w, h);
HGDIOBJ hOld = SelectObject(hCaptureDC, hBitmap);
BOOL bOK = BitBlt(hCaptureDC, 0, 0, w, h, hdc, 0, 0, SRCCOPY);
SelectObject(hCaptureDC, hOld);
DeleteDC(hCaptureDC);
ReleaseDC(hwnd, hdc);
return hBitmap;
}
};
Related
Using the Windows API, I'm trying to draw opaque text on a semi-transparent background. Using SetLayeredWindowAttributes(hWnd, RGB(0, 0, 0), 128, LWA_ALPHA); and a window style of WS_EX_LAYERED, I've managed to make the entire window semi-transparent, but that also includes the text.
How do I keep the text opaque and the background translucent?
In order to do "proper" alpha in a layered window you need to supply the window manager with a PARGB bitmap by a call to UpdateLayeredWindow.
Try the code below, it works for me.
#include <Windows.h>
#include <stdio.h>
#include <iostream>
#include <ObjIdl.h>
#include <gdiplus.h>
#include <gdiplusheaders.h>
using namespace Gdiplus;
#pragma comment (lib,"Gdiplus.lib")
#define MAX_WIDTH 800
#define MAX_HEIGHT 600
using namespace std;
void Drawtext(HWND hwnd, HDC hdc);
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) {
if (message == WM_DESTROY) {
PostQuitMessage(0);
}
return DefWindowProc(hwnd, message, wParam, lParam);
};
HINSTANCE hinst;
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevinstance, PSTR szCmdLine, int iCmdShow) {
HWND hWnd;
GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR gdiplusToken;
//Initialize GDI+
GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
hinst = GetModuleHandle(NULL);
// create a window class:
WNDCLASS wc = {};
wc.lpfnWndProc = WndProc;
wc.hInstance = hinst;
wc.lpszClassName = L"win32";
// register class with operating system:
RegisterClass(&wc);
// create and show window:
hWnd = CreateWindowExW(
WS_EX_LAYERED | WS_EX_TOPMOST,
L"win32",
L"WinSoup",
WS_POPUP,
0, 0, 1000, 500,
nullptr,
nullptr,
hInstance,
nullptr
);
if (hWnd == NULL) {
return 0;
}
Drawtext(hWnd, GetDC(hWnd));
ShowWindow(hWnd, SW_SHOW);
MSG msg = {};
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
void Drawtext(HWND hwnd, HDC hdc)
{
FontFamily fontFamily(L"Times New Roman");
Font font(&fontFamily, 32, FontStyleRegular, UnitPixel);
PointF pointF(30.0f, 10.0f);
SolidBrush solidBrush(Color(255, 0, 0, 0));
Bitmap softwareBitmap(MAX_WIDTH, MAX_HEIGHT, PixelFormat32bppARGB);
Graphics g(&softwareBitmap);
g.Clear(Gdiplus::Color(30, 0, 0, 0)); // 30: alpha value
g.DrawString(L"Hello Hello Hello Hello Hello Hello Hello Hello", -1, &font, pointF, &solidBrush);
HBITMAP bmp;
softwareBitmap.GetHBITMAP(Color(0, 0, 0, 0), &bmp);
HDC memdc = CreateCompatibleDC(hdc);
HGDIOBJ original = SelectObject(memdc, bmp);
BLENDFUNCTION blend = { 0 };
blend.BlendOp = AC_SRC_OVER;
blend.SourceConstantAlpha = 255;
blend.AlphaFormat = AC_SRC_ALPHA;
POINT ptLocation = { 200, 300 };
SIZE szWnd = { MAX_WIDTH, MAX_HEIGHT };
POINT ptSrc = { 0, 0 };
UpdateLayeredWindow(hwnd, hdc, &ptLocation, &szWnd, memdc, &ptSrc, 0, &blend, ULW_ALPHA);
SelectObject(hdc, original);
DeleteObject(bmp);
DeleteObject(memdc);
}
Debug:
For more details, you can refer to #Koro's answer.
i'm sorry for what i did. i edited.
i'd like to use Fillrect on 32bit HBITMAP which is Created with CreateDIBSection
but i can't make rect visible in color that i want to.
(i Drawed a fillrect with CreateSolidBrush blue(RGB(0, 0, 255)) on 32bit HBITMAP(hdcbmp), but it doesn't appear blue.)
here is source code
is there anyway to show rect color that i want to?
sorry for my poor english.
void DrawAlphaBitmap(HWND hWnd, ULONG uWidth, ULONG uHeight)
{
BLENDFUNCTION bf;
HBITMAP hbitmap;
HBITMAP hOldBitmap;
BITMAPINFO bmi;
PVOID pvBits;
HDC hdcwnd = GetDC(hWnd);
HDC hdcbmp = CreateCompatibleDC(hdcwnd);
ZeroMemory(&bmi, sizeof(BITMAPINFO));
bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bmi.bmiHeader.biWidth = uWidth;
bmi.bmiHeader.biHeight = uHeight;
bmi.bmiHeader.biPlanes = 1;
bmi.bmiHeader.biBitCount = 32;
bmi.bmiHeader.biCompression = BI_RGB;
bmi.bmiHeader.biSizeImage = bmi.bmiHeader.biWidth * bmi.bmiHeader.biHeight * 4;
hbitmap = CreateDIBSection(hdcbmp, &bmi, DIB_RGB_COLORS, &pvBits, NULL, 0x0);
hOldBitmap = (HBITMAP)SelectObject(hdcbmp, hbitmap);
bf.BlendOp = AC_SRC_OVER;
bf.BlendFlags = 0;
bf.SourceConstantAlpha = 0xff;
bf.AlphaFormat = AC_SRC_ALPHA;
RECT rc2 = { 100, 100, 200, 200 };
FillRect(hdcbmp, &rc2, CreateSolidBrush(RGB(0, 0, 255)));
AlphaBlend(hdcwnd, 0, 0, uWidth, uHeight, hdcbmp, 0, 0, uWidth, uHeight, bf);
SelectObject(hdcbmp, hOldBitmap);
DeleteObject(hbitmap);
DeleteDC(hdcbmp);
ReleaseDC(hWnd, hdcwnd);
}
From documentation for BLENDFUNCTION:
AlphaFormat:
This flag is set when the bitmap has an Alpha channel (that is, per-pixel alpha).
In this case, alpha channel is not set. CreateDIBSection initializes the alpha values to zero. When AC_SRC_ALPHA is set, AlphaBlend ignores pixels whose alpha value is zero. Modify your code as follows:
//bf.AlphaFormat = AC_SRC_ALPHA; <- remove
bf.AlphaFormat = 0; //replace with 0
Side note, you have resource leak in creation of HBRUSH handle. Change the code to
HBRUSH hbrush = CreateSolidBrush(RGB(0, 0, 255));
RECT rc2 = { 0, 0, w, h };
FillRect(memdc, &rc2, hbrush);
DeleteObject(hbrush);
Ideally, your function prototype should be void DrawAlphaBitmap(HDC hdc, ULONG uWidth, ULONG uHeight); so that HDC can be passed directly, for example from BeginPaint/EndPaint in WM_PAINT message.
I'm trying to create a borderless window that fills the screen with an OpenGL viewport. The problem is, when I set the window and viewport to be the same size as the desktop, the window flashes black on losing and gaining focus, on exit, and on creation. This may have to do with Windows setting the window to some type of true "fullscreen" mode. This doesn't seem to happen in other applications (DirectX?) that use this type of borderless fullscreen window.
I believe SFML has/had a variant of this problem.
There is a workaround: don't set the window to the exact size of the desktop. For instance, make the width of the window one pixel more than the width of the desktop.
Below is an SSCCE without error checking that shows what I'm talking about. This will create a borderless fullscreen window with a desktop-sized OpenGL viewport displaying an ugly green color (exit with AltF4). You can change the line #define FIX_BUG 0 to #define FIX_BUG 1 which just adds one extra pixel to the window's width to see what the behavior I want looks like.
#define WIN32_LEAN_AND_MEAN
#include "windows.h"
#pragma comment(lib, "OpenGL32.lib")
#define FIX_BUG 0
void MyRegisterClass(HINSTANCE hInstance);
HWND MyCreateWindow(HINSTANCE hInstance);
void MySetupOpenGLContext(HWND hWnd);
void MySetPixelFormat(HDC hDC);
RECT MyGetDesktopRect();
void MyLoadOpenGLFunctions();
#define APIENTRYP APIENTRY *
#define GLAPI extern
#define GL_COLOR 0x1800
typedef int GLint;
typedef int GLsizei;
typedef unsigned int GLenum;
typedef float GLfloat;
typedef void (APIENTRYP PFNGLVIEWPORTPROC) (GLint x, GLint y, GLsizei width, GLsizei height);
PFNGLVIEWPORTPROC glViewport;
typedef void (APIENTRYP PFNGLCLEARBUFFERFVPROC) (GLenum buffer, GLint drawbuffer, const GLfloat *value);
PFNGLCLEARBUFFERFVPROC glClearBufferfv;
static const char *windowClass = "CLASS";
static const char *windowTitle = "TITLE";
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) {
MyRegisterClass(hInstance);
const HWND hWnd = MyCreateWindow(hInstance);
MySetupOpenGLContext(hWnd);
ShowWindow(hWnd, nCmdShow);
HDC hDC = GetDC(hWnd);
MSG msg;
while (IsWindow(hWnd)) {
while (PeekMessage(&msg, hWnd, 0, 0, PM_REMOVE)) DispatchMessage(&msg);
const GLfloat color[] = {0.5f, 0.5f, 0.0f, 1.0f};
glClearBufferfv(GL_COLOR, 0, color);
SwapBuffers(hDC);
}
return 0;
}
void MyRegisterClass(HINSTANCE hInstance) {
WNDCLASSEX wcex = {0};
wcex.cbSize = sizeof wcex;
wcex.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
wcex.lpfnWndProc = &DefWindowProc;
wcex.hInstance = hInstance;
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground = (HBRUSH) (COLOR_WINDOWFRAME + 1);
wcex.lpszClassName = windowClass;
RegisterClassEx(&wcex);
}
HWND MyCreateWindow(HINSTANCE hInstance) {
RECT dRect = MyGetDesktopRect();
return CreateWindow(windowClass, windowTitle, WS_POPUP,
0, 0, dRect.right + FIX_BUG, dRect.bottom,
NULL, NULL, hInstance, NULL);
}
void MySetupOpenGLContext(HWND hWnd) {
HDC hDC = GetDC(hWnd);
MySetPixelFormat(hDC);
HGLRC hGLRC = wglCreateContext(hDC);
wglMakeCurrent(hDC, hGLRC);
MyLoadOpenGLFunctions();
RECT dRect = MyGetDesktopRect();
glViewport(0, 0, dRect.right, dRect.bottom);
}
void MySetPixelFormat(HDC hDC) {
PIXELFORMATDESCRIPTOR pfd = {0};
pfd.nSize = sizeof pfd;
pfd.nVersion = 1;
pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
pfd.iPixelType = PFD_TYPE_RGBA;
pfd.cColorBits = 32;
pfd.cDepthBits = 24;
pfd.cStencilBits = 8;
pfd.iLayerType = PFD_MAIN_PLANE;
int format = ChoosePixelFormat(hDC, &pfd);
SetPixelFormat(hDC, format, &pfd);
}
RECT MyGetDesktopRect() {
RECT desktopRect;
const HWND hDesktop = GetDesktopWindow();
GetWindowRect(hDesktop, &desktopRect);
return desktopRect;
}
void MyLoadOpenGLFunctions() {
glViewport = (PFNGLVIEWPORTPROC) GetProcAddress(GetModuleHandleA("OpenGL32.dll"), "glViewport");
glClearBufferfv = (PFNGLCLEARBUFFERFVPROC) wglGetProcAddress("glClearBufferfv");
}
Github Gist | Raw Text
Why does this happen? Is there a way to get the behavior I want with the window set to the exact size of the desktop?
My platform: Visual Studio 2013 / 64-bit Windows 8 / AMD Radeon HD6850.
Look these two pictures:
Windows 7:
XP:
As you see, there is no image on XP case.
In this post http://social.msdn.microsoft.com/Forums/da-DK/vcmfcatl/thread/6f9b1f03-0c67-4af4-a187-e135fdd1411c, it seems that PUSHBUTTON with BS_BITMAP style doesn't work on XP. Is this right?
If yes, how to do similar things on XP?
In the resource script file, I set the push button with style BS_BITMAP, and below are the codes related to setting the image:
static INT_PTR CALLBACK Dialog_Color_Proc(...)
{
....
case WM_INITDIALOG:
....
SetColorDlgButtonColor(hDlg, IDC_SET_START_PAGE_BG, prefs->bgColor);
....
}
void SetColorDlgButtonColor(HWND hDlg, int nIDDlgItem, COLORREF color)
{
HWND hButton = GetDlgItem(hDlg, nIDDlgItem);
HDC hdc = GetDC(hButton);
HDC memDC = CreateCompatibleDC(hdc);
int dx = ClientRect(hButton).dx;
int dy = ClientRect(hButton).dx;
HBITMAP hMemBmp = CreateCompatibleBitmap(hdc, dx, dy);
HBITMAP hOldBmp = (HBITMAP)SelectObject(memDC, hMemBmp);
Rectangle(memDC, 0, 0, dx, dy);
RECT rc;
rc.left = 0;
rc.top = 0;
rc.right = dx;
rc.bottom = dy;
HBRUSH brush = CreateSolidBrush(color);
FillRect(memDC, &rc, brush);
SelectObject(memDC, hOldBmp);
SendMessage(GetDlgItem(hDlg, nIDDlgItem), BM_SETIMAGE, IMAGE_BITMAP, (LPARAM)hMemBmp);
DeleteObject(brush);
DeleteObject(hMemBmp);
DeleteObject(memDC);
}
I'm trying to make Cairo work in a Win32 window. The idea is just to make it render flicker-free.
If I create a Cairo surface directly with the window's HDC, then the window will flicker on resizing. That's normal, and expected. The usual solution is to create a compatible device context and render to a bitmap, then blit that bitmap to the window HDC.
The problem is that the same drawing code that worked when using the window's HDC doesn't work on the double-buffer device context. I just get a black square rather than a gradient circle.
Here's a small, functional example. If you comment out the #define DOUBLE_BUFFER line, then it will draw the Cairo rendering directly to the window's HDC. Otherwise, it will draw to the created one.
#include <windows.h>
#include <stdlib.h>
#include <string.h>
#include <tchar.h>
#include <cairo.h>
#include <cairo-win32.h>
// Global variables
// The main window class name.
static TCHAR szWindowClass[] = _T("CairoTestApp");
// The string that appears in the application's title bar.
static TCHAR szTitle[] = _T("Cairo Test Application");
HINSTANCE hInst;
// Forward declarations of functions included in this code module:
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
int main(int argc, const char *argv)
{
HINSTANCE hInstance = GetModuleHandle(NULL);
WNDCLASSEX wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_APPLICATION));
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wcex.lpszMenuName = NULL;
wcex.lpszClassName = szWindowClass;
wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_APPLICATION));
if (!RegisterClassEx(&wcex))
{
MessageBox(NULL, _T("Call to RegisterClassEx failed!"), szTitle, NULL);
return 1;
}
hInst = hInstance; // Store instance handle in our global variable
// The parameters to CreateWindow explained:
// szWindowClass: the name of the application
// szTitle: the text that appears in the title bar
// WS_OVERLAPPEDWINDOW: the type of window to create
// CW_USEDEFAULT, CW_USEDEFAULT: initial position (x, y)
// 500, 100: initial size (width, length)
// NULL: the parent of this window
// NULL: this application does not have a menu bar
// hInstance: the first parameter from WinMain
// NULL: not used in this application
HWND hWnd = CreateWindow(
szWindowClass,
szTitle,
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT,
256, 256,
NULL,
NULL,
hInstance,
NULL
);
if (!hWnd)
{
MessageBox(NULL, _T("Call to CreateWindow failed!"), szTitle, NULL);
return 1;
}
// The parameters to ShowWindow explained:
// hWnd: the value returned from CreateWindow
// nCmdShow: the fourth parameter from WinMain
ShowWindow(hWnd, SW_SHOWNORMAL);
UpdateWindow(hWnd);
// Main message loop:
MSG msg;
while(true)
{
if(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
if (msg.message == WM_QUIT)
{
break;
}
else
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
}
// DestroyWindow(hWnd);
UnregisterClass(szWindowClass, hInstance);
return 0;
}
void gradientExample( cairo_t* cr ) {
cairo_pattern_t *pat;
pat = cairo_pattern_create_linear (0.0, 0.0, 0.0, 256.0);
cairo_pattern_add_color_stop_rgba (pat, 1, 0, 0, 0, 1);
cairo_pattern_add_color_stop_rgba (pat, 0, 1, 1, 1, 1);
cairo_rectangle (cr, 0, 0, 256, 256);
cairo_set_source (cr, pat);
cairo_fill (cr);
cairo_pattern_destroy (pat);
pat = cairo_pattern_create_radial (115.2, 102.4, 25.6,
102.4, 102.4, 128.0);
cairo_pattern_add_color_stop_rgba (pat, 0, 1, 1, 1, 1);
cairo_pattern_add_color_stop_rgba (pat, 1, 0, 0, 0, 1);
cairo_set_source (cr, pat);
cairo_arc (cr, 128.0, 128.0, 76.8, 0, 2 * 3.14159);
cairo_fill (cr);
cairo_pattern_destroy (pat);
}
#define DOUBLE_BUFFER
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
PAINTSTRUCT ps;
HDC hdc;
TCHAR greeting[] = _T("Hello, World!");
switch (message)
{
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
{
HDC newDC = CreateCompatibleDC(hdc);
RECT theRect;
GetClientRect(hWnd, &theRect);
int width, height;
width = theRect.right - theRect.left;
height = theRect.bottom - theRect.top;
HBITMAP theBmp = CreateCompatibleBitmap(newDC, width, height);
HGDIOBJ oldBmp = SelectObject(newDC, theBmp);
//Test some text.
#ifdef DOUBLE_BUFFER
TextOut(newDC, 5, 5, greeting, _tcslen(greeting));
#else
TextOut(hdc, 5, 5, greeting, _tcslen(greeting));
#endif
{
#ifdef DOUBLE_BUFFER
cairo_surface_t *surface = cairo_win32_surface_create(newDC);
#else
cairo_surface_t *surface = cairo_win32_surface_create(hdc);
#endif
cairo_t *cr = cairo_create(surface);
// Draw on the cairo context.
cairo_set_source_rgb(cr, 1, 1, 1);
cairo_paint(cr);
gradientExample( cr );
cairo_surface_finish(surface);
// Cleanup.
cairo_destroy(cr);
cairo_surface_destroy(surface);
}
#ifdef DOUBLE_BUFFER
BitBlt(hdc, 0, 0, width, height, newDC, theRect.left, theRect.top, SRCCOPY);
#endif
SelectObject(newDC, oldBmp);
DeleteDC(newDC);
}
EndPaint(hWnd, &ps);
break;
case WM_ERASEBKGND:
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
case WM_KEYDOWN: // Is A Key Being Held Down?
{
if(wParam == VK_ESCAPE)
{
PostMessage(hWnd, WM_CLOSE, 0, 0);
}
}
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
break;
}
return 0;
}
Try calling CreateCompatibleBitmap with the original device context (hdc) as parameter instead. Otherwise I believe you'll get one based on the default monochrome dummy bitmap of the supposedly-compatible DC you just created.