mirror of
https://gitlab.freedesktop.org/cairo/cairo.git
synced 2025-12-31 16:00:14 +01:00
Boilerplate/Win32: Switch to a DIB section
The Win32 boilerplated works by positioning a test HWND at (INT_MIN, INT_MIN) and drawing into the client HDC. However, the image read-back always yields a blank image (_cairo_boilerplate_get_image_surface). The window is positioned offscreen, thus GDI returns an HDC that is completely clipped, as GDI always clips to the visible region of the client area. In short, every drawing operation turns into a no-op. It's probably best to use a bitmap target with a memory DC. This commit reworks the Win32 boilerplate to target a DIB section.
This commit is contained in:
parent
d04906aad6
commit
bc84f415bb
1 changed files with 124 additions and 142 deletions
|
|
@ -30,180 +30,162 @@
|
|||
|
||||
#include <errno.h>
|
||||
#include <limits.h>
|
||||
#include <assert.h>
|
||||
|
||||
static const cairo_user_data_key_t win32_closure_key;
|
||||
|
||||
typedef struct _win32_target_closure {
|
||||
HWND wnd;
|
||||
HDC dc;
|
||||
ATOM bpl_atom;
|
||||
cairo_surface_t *surface;
|
||||
HBITMAP hbitmap; /* Bitmap */
|
||||
void *data; /* Bitmap data */
|
||||
HDC hdc; /* Memory DC with bitmap selected */
|
||||
} win32_target_closure_t;
|
||||
|
||||
static void
|
||||
_cairo_boilerplate_win32_cleanup_window_surface (void *closure)
|
||||
_cairo_boilerplate_win32_cleanup_ddb_surface (void *closure)
|
||||
{
|
||||
win32_target_closure_t *win32tc = closure;
|
||||
|
||||
if (win32tc != NULL)
|
||||
{
|
||||
if (win32tc->wnd != NULL &&
|
||||
ReleaseDC (win32tc->wnd, win32tc->dc) != 1)
|
||||
fprintf (stderr,
|
||||
"Failed to release DC of a test window when cleaning up.\n");
|
||||
if (win32tc->wnd != NULL &&
|
||||
DestroyWindow (win32tc->wnd) == 0)
|
||||
fprintf (stderr,
|
||||
"Failed to destroy a test window when cleaning up, GLE is %lu.\n",
|
||||
GetLastError ());
|
||||
if (win32tc->bpl_atom != 0 &&
|
||||
UnregisterClassA ((LPCSTR) MAKELPARAM (win32tc->bpl_atom, 0), GetModuleHandle (NULL)) == 0 &&
|
||||
GetLastError () != ERROR_CLASS_DOES_NOT_EXIST)
|
||||
fprintf (stderr,
|
||||
"Failed to unregister boilerplate window class, GLE is %lu.\n",
|
||||
GetLastError ());
|
||||
if (win32tc->hdc != NULL) {
|
||||
/* https://devblogs.microsoft.com/oldnewthing/20100416-00/?p=14313 */
|
||||
SelectObject (win32tc->hdc, CreateBitmap (0, 0, 1, 1, NULL));
|
||||
|
||||
free (win32tc);
|
||||
if (!DeleteDC (win32tc->hdc)) {
|
||||
fprintf (stderr, "%s failed during cleanup\n", "DeleteDC");
|
||||
}
|
||||
}
|
||||
|
||||
if (win32tc->hbitmap) {
|
||||
if (!DeleteObject (win32tc->hbitmap)) {
|
||||
fprintf (stderr, "%s failed during cleanup\n", "DeleteObject");
|
||||
}
|
||||
}
|
||||
|
||||
free (win32tc);
|
||||
}
|
||||
}
|
||||
|
||||
static win32_target_closure_t *
|
||||
_cairo_boilerplate_win32_create_window (int width,
|
||||
int height)
|
||||
{
|
||||
WNDCLASSEXA wincl;
|
||||
win32_target_closure_t *win32tc;
|
||||
LPCSTR window_class_name;
|
||||
|
||||
ZeroMemory (&wincl, sizeof (WNDCLASSEXA));
|
||||
wincl.cbSize = sizeof (WNDCLASSEXA);
|
||||
wincl.hInstance = GetModuleHandle (0);
|
||||
wincl.lpszClassName = "cairo_boilerplate_win32_dummy";
|
||||
wincl.lpfnWndProc = DefWindowProcA;
|
||||
wincl.style = CS_OWNDC;
|
||||
|
||||
win32tc = calloc (1, sizeof (win32_target_closure_t));
|
||||
|
||||
if (win32tc == NULL)
|
||||
{
|
||||
int error = errno;
|
||||
fprintf (stderr, "Ran out of memory: %d.\n", error);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ZeroMemory (win32tc, sizeof (win32_target_closure_t));
|
||||
|
||||
win32tc->bpl_atom = RegisterClassExA (&wincl);
|
||||
|
||||
if (win32tc->bpl_atom == 0 && GetLastError () != ERROR_CLASS_ALREADY_EXISTS)
|
||||
{
|
||||
fprintf (stderr,
|
||||
"Failed to register a boilerplate window class, GLE is %lu.\n",
|
||||
GetLastError ());
|
||||
_cairo_boilerplate_win32_cleanup_window_surface (win32tc);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (win32tc->bpl_atom == 0)
|
||||
window_class_name = wincl.lpszClassName;
|
||||
else
|
||||
window_class_name = (LPCSTR) MAKELPARAM (win32tc->bpl_atom, 0);
|
||||
|
||||
win32tc->wnd = CreateWindowExA (WS_EX_TOOLWINDOW,
|
||||
window_class_name,
|
||||
0,
|
||||
WS_POPUP,
|
||||
0,
|
||||
0,
|
||||
width,
|
||||
height,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0);
|
||||
|
||||
if (win32tc->wnd == NULL)
|
||||
{
|
||||
fprintf (stderr,
|
||||
"Failed to create a test window, GLE is %lu.\n",
|
||||
GetLastError ());
|
||||
_cairo_boilerplate_win32_cleanup_window_surface (win32tc);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
win32tc->dc = GetDC (win32tc->wnd);
|
||||
|
||||
if (win32tc->dc == NULL)
|
||||
{
|
||||
fprintf (stderr, "Failed to get test window DC.\n");
|
||||
_cairo_boilerplate_win32_cleanup_window_surface (win32tc);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
SetWindowPos (win32tc->wnd,
|
||||
HWND_BOTTOM,
|
||||
INT_MIN,
|
||||
INT_MIN,
|
||||
width,
|
||||
height,
|
||||
SWP_NOACTIVATE | SWP_SHOWWINDOW);
|
||||
|
||||
return win32tc;
|
||||
}
|
||||
|
||||
static cairo_surface_t *
|
||||
_cairo_boilerplate_win32_create_window_surface (const char *name,
|
||||
cairo_content_t content,
|
||||
double width,
|
||||
double height,
|
||||
double max_width,
|
||||
double max_height,
|
||||
cairo_boilerplate_mode_t mode,
|
||||
void **closure)
|
||||
_cairo_boilerplate_win32_create_ddb_surface (const char *name,
|
||||
cairo_content_t content,
|
||||
double width,
|
||||
double height,
|
||||
double max_width,
|
||||
double max_height,
|
||||
cairo_boilerplate_mode_t mode,
|
||||
void **closure)
|
||||
{
|
||||
win32_target_closure_t *win32tc;
|
||||
cairo_surface_t *surface;
|
||||
win32_target_closure_t *win32tc = NULL;
|
||||
cairo_format_t format;
|
||||
cairo_surface_t *surface;
|
||||
cairo_status_t status;
|
||||
WORD pixel_bits;
|
||||
struct {
|
||||
BITMAPINFOHEADER header;
|
||||
RGBQUAD color_table[256];
|
||||
} bitmap_desc = {0};
|
||||
HBITMAP hbitmap_old;
|
||||
|
||||
win32tc = _cairo_boilerplate_win32_create_window (width, height);
|
||||
if (width < 1.0)
|
||||
width = 1.0;
|
||||
if (height < 1.0)
|
||||
height = 1.0;
|
||||
|
||||
if (win32tc == NULL)
|
||||
return NULL;
|
||||
win32tc = calloc (1, sizeof (win32_target_closure_t));
|
||||
if (win32tc == NULL) {
|
||||
fprintf (stderr, "%s failed with errno %d\n", "calloc", errno);
|
||||
*closure = NULL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
format = cairo_boilerplate_format_from_content (content);
|
||||
memset (win32tc, 0, sizeof (*win32tc));
|
||||
|
||||
surface = cairo_win32_surface_create_with_format (win32tc->dc, format);
|
||||
*closure = win32tc;
|
||||
|
||||
win32tc->surface = surface;
|
||||
switch (content) {
|
||||
case CAIRO_CONTENT_COLOR:
|
||||
format = CAIRO_FORMAT_RGB24;
|
||||
pixel_bits = 32;
|
||||
break;
|
||||
case CAIRO_CONTENT_COLOR_ALPHA:
|
||||
format = CAIRO_FORMAT_ARGB32;
|
||||
pixel_bits = 32;
|
||||
break;
|
||||
case CAIRO_CONTENT_ALPHA:
|
||||
format = CAIRO_FORMAT_A8;
|
||||
pixel_bits = 8;
|
||||
break;
|
||||
default:
|
||||
assert (0); /* not reached */
|
||||
format = CAIRO_FORMAT_INVALID;
|
||||
break;
|
||||
}
|
||||
|
||||
if (pixel_bits <= 8) {
|
||||
assert (pixel_bits == 8);
|
||||
|
||||
for (BYTE i = 0; i <= 255; i++) {
|
||||
bitmap_desc.color_table[i].rgbBlue = i;
|
||||
bitmap_desc.color_table[i].rgbGreen = i;
|
||||
bitmap_desc.color_table[i].rgbRed = i;
|
||||
bitmap_desc.color_table[i].rgbReserved = 0;
|
||||
}
|
||||
}
|
||||
|
||||
assert (width < (double)LONG_MAX);
|
||||
assert (height < (double)LONG_MAX);
|
||||
|
||||
bitmap_desc.header.biSize = sizeof (bitmap_desc.header);
|
||||
bitmap_desc.header.biWidth = (LONG)width;
|
||||
bitmap_desc.header.biHeight = -(LONG)height; /* a negative height tells GDI to
|
||||
use a top-down coordinate system */
|
||||
bitmap_desc.header.biPlanes = 1;
|
||||
bitmap_desc.header.biBitCount = pixel_bits;
|
||||
bitmap_desc.header.biCompression = BI_RGB;
|
||||
|
||||
/* From https://learn.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-createdibsection:
|
||||
* CreateDIBSection does not use the parameters biXPelsPerMeter or biYPelsPerMeter */
|
||||
|
||||
win32tc->hbitmap = CreateDIBSection (NULL, (BITMAPINFO*)&bitmap_desc, DIB_RGB_COLORS, &win32tc->data, NULL, 0);
|
||||
if (win32tc->hbitmap == NULL) {
|
||||
fprintf (stderr, "%s failed with error code %u\n", "CreateDIBSection", (unsigned int) GetLastError ());
|
||||
return NULL;
|
||||
}
|
||||
|
||||
win32tc->hdc = CreateCompatibleDC (NULL);
|
||||
if (win32tc->hdc == NULL) {
|
||||
fprintf (stderr, "%s failed\n", "CreateCompatibleDC");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
hbitmap_old = SelectObject (win32tc->hdc, win32tc->hbitmap);
|
||||
assert (hbitmap_old == CreateBitmap (0, 0, 1, 1, NULL));
|
||||
|
||||
surface = cairo_win32_surface_create_with_format (win32tc->hdc, format);
|
||||
|
||||
status = cairo_surface_status (surface);
|
||||
|
||||
if (status != CAIRO_STATUS_SUCCESS)
|
||||
{
|
||||
if (status != CAIRO_STATUS_SUCCESS) {
|
||||
fprintf (stderr,
|
||||
"Failed to create the test surface: %s [%d].\n",
|
||||
cairo_status_to_string (status), status);
|
||||
_cairo_boilerplate_win32_cleanup_window_surface (win32tc);
|
||||
cairo_surface_destroy (surface);
|
||||
_cairo_boilerplate_win32_cleanup_ddb_surface (win32tc);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
status = cairo_surface_set_user_data (surface, &win32_closure_key, win32tc, NULL);
|
||||
|
||||
if (status != CAIRO_STATUS_SUCCESS)
|
||||
{
|
||||
if (status != CAIRO_STATUS_SUCCESS) {
|
||||
fprintf (stderr,
|
||||
"Failed to set surface userdata: %s [%d].\n",
|
||||
cairo_status_to_string (status), status);
|
||||
|
||||
cairo_surface_destroy (surface);
|
||||
_cairo_boilerplate_win32_cleanup_window_surface (win32tc);
|
||||
|
||||
cairo_surface_destroy (surface);
|
||||
_cairo_boilerplate_win32_cleanup_ddb_surface (win32tc);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
*closure = win32tc;
|
||||
|
||||
return surface;
|
||||
}
|
||||
|
||||
|
|
@ -228,7 +210,7 @@ _cairo_boilerplate_win32_create_dib_surface (const char *name,
|
|||
|
||||
static const cairo_boilerplate_target_t targets[] = {
|
||||
{
|
||||
"win32", "win32", NULL, NULL,
|
||||
"win32-DIB", "win32", NULL, NULL,
|
||||
CAIRO_SURFACE_TYPE_WIN32, CAIRO_CONTENT_COLOR, 0,
|
||||
"cairo_win32_surface_create_with_dib",
|
||||
_cairo_boilerplate_win32_create_dib_surface,
|
||||
|
|
@ -242,11 +224,11 @@ static const cairo_boilerplate_target_t targets[] = {
|
|||
NULL,
|
||||
TRUE, FALSE, FALSE
|
||||
},
|
||||
/* Testing the win32 surface isn't interesting, since for
|
||||
* ARGB images it just chains to the image backend
|
||||
/* Testing the win32 surface for ARGB32 DIBs isn't interesting,
|
||||
* since it just chains to the image backend
|
||||
*/
|
||||
{
|
||||
"win32", "win32", NULL, NULL,
|
||||
"win32-DIB", "win32", NULL, NULL,
|
||||
CAIRO_SURFACE_TYPE_WIN32, CAIRO_CONTENT_COLOR_ALPHA, 0,
|
||||
"cairo_win32_surface_create_with_dib",
|
||||
_cairo_boilerplate_win32_create_dib_surface,
|
||||
|
|
@ -261,31 +243,31 @@ static const cairo_boilerplate_target_t targets[] = {
|
|||
FALSE, FALSE, FALSE
|
||||
},
|
||||
{
|
||||
"win32-window-color", "win32", NULL, NULL,
|
||||
"win32-DDB", "win32", NULL, NULL,
|
||||
CAIRO_SURFACE_TYPE_WIN32, CAIRO_CONTENT_COLOR, 1,
|
||||
"cairo_win32_surface_create",
|
||||
_cairo_boilerplate_win32_create_window_surface,
|
||||
_cairo_boilerplate_win32_create_ddb_surface,
|
||||
cairo_surface_create_similar,
|
||||
NULL,
|
||||
NULL,
|
||||
_cairo_boilerplate_get_image_surface,
|
||||
cairo_surface_write_to_png,
|
||||
_cairo_boilerplate_win32_cleanup_window_surface,
|
||||
_cairo_boilerplate_win32_cleanup_ddb_surface,
|
||||
NULL,
|
||||
NULL,
|
||||
FALSE, FALSE, FALSE
|
||||
},
|
||||
{
|
||||
"win32-window-coloralpha", "win32", NULL, NULL,
|
||||
"win32-DDB", "win32", NULL, NULL,
|
||||
CAIRO_SURFACE_TYPE_WIN32, CAIRO_CONTENT_COLOR_ALPHA, 1,
|
||||
"cairo_win32_surface_create_with_format",
|
||||
_cairo_boilerplate_win32_create_window_surface,
|
||||
_cairo_boilerplate_win32_create_ddb_surface,
|
||||
cairo_surface_create_similar,
|
||||
NULL,
|
||||
NULL,
|
||||
_cairo_boilerplate_get_image_surface,
|
||||
cairo_surface_write_to_png,
|
||||
_cairo_boilerplate_win32_cleanup_window_surface,
|
||||
_cairo_boilerplate_win32_cleanup_ddb_surface,
|
||||
NULL,
|
||||
NULL,
|
||||
FALSE, FALSE, FALSE
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue