diff --git a/src/win32/cairo-win32-display-surface.c b/src/win32/cairo-win32-display-surface.c index 025b2c9eb..76ab5a1c8 100644 --- a/src/win32/cairo-win32-display-surface.c +++ b/src/win32/cairo-win32-display-surface.c @@ -325,6 +325,8 @@ _cairo_win32_display_surface_create_for_dc (HDC original_dc, surface->win32.extents.y = 0; surface->win32.extents.width = width; surface->win32.extents.height = height; + surface->win32.x_ofs = 0; + surface->win32.y_ofs = 0; surface->initial_clip_rgn = NULL; surface->had_simple_clip = FALSE; @@ -466,7 +468,8 @@ _cairo_win32_display_surface_map_to_image (void *abstract_sur surface->win32.extents.width, surface->win32.extents.height, surface->win32.dc, - surface->win32.extents.x, surface->win32.extents.y, + surface->win32.extents.x + surface->win32.x_ofs, /* Handling multi-monitor... */ + surface->win32.extents.y + surface->win32.y_ofs, /* ... setup on Win32 */ SRCCOPY)) { status = _cairo_error (CAIRO_STATUS_DEVICE_ERROR); goto err; @@ -544,11 +547,12 @@ _cairo_win32_display_surface_flush (void *abstract_surface, unsigned flags) if (damage->status) { if (!BitBlt (surface->win32.dc, - 0, 0, + surface->win32.extents.x + surface->win32.x_ofs, /* Handling multi-monitor... */ + surface->win32.extents.y + surface->win32.y_ofs, /* ... setup on Win32 */ surface->win32.extents.width, surface->win32.extents.height, fallback->win32.dc, - 0, 0, + fallback->win32.extents.x, fallback->win32.extents.y, SRCCOPY)) status = _cairo_win32_print_gdi_error (__FUNCTION__); } else if (damage->region) { @@ -561,7 +565,8 @@ _cairo_win32_display_surface_flush (void *abstract_surface, unsigned flags) rect.x, rect.y, rect.width, rect.height)); if (!BitBlt (surface->win32.dc, - rect.x, rect.y, + rect.x + surface->win32.x_ofs, /* Handling multi-monitor... */ + rect.y + surface->win32.y_ofs, /* ... setup on Win32 */ rect.width, rect.height, fallback->win32.dc, rect.x, rect.y, @@ -628,6 +633,35 @@ _cairo_win32_save_initial_clip (HDC hdc, cairo_win32_display_surface_t *surface) surface->win32.extents.width = rect.right - rect.left; surface->win32.extents.height = rect.bottom - rect.top; + /* On multi-monitor setup, under Windows, the primary monitor always + * have origin (0,0). Any monitors that extends to the left or above + * will have coordinates in the negative range. Take this into + * account, by forcing our Win32 surface to start at extent (0,0) and + * using a device offset. Cairo does not handle extents with negative + * offsets. + */ + surface->win32.x_ofs = 0; + surface->win32.y_ofs = 0; + if ((surface->win32.extents.x < 0) || + (surface->win32.extents.y < 0)) { + /* Negative offsets occurs for (and ONLY for) the desktop DC (virtual + * desktop), when a monitor extend to the left or above the primary + * monitor. + * + * More info @ https://www.microsoft.com/msj/0697/monitor/monitor.aspx + * + * Note that any other DC, including memory DC created with + * CreateCompatibleDC() will have extents in the + * positive range. This will be taken into account later when we perform + * raster operations between the DC (may have to perform offset + * translation). + */ + surface->win32.x_ofs = surface->win32.extents.x; + surface->win32.y_ofs = surface->win32.extents.y; + surface->win32.extents.x = 0; + surface->win32.extents.y = 0; + } + surface->initial_clip_rgn = NULL; surface->had_simple_clip = FALSE; diff --git a/src/win32/cairo-win32-private.h b/src/win32/cairo-win32-private.h index 6fdf96f87..87ef38521 100644 --- a/src/win32/cairo-win32-private.h +++ b/src/win32/cairo-win32-private.h @@ -100,6 +100,24 @@ typedef struct _cairo_win32_surface { * that match bounds of the clipped region. */ cairo_rectangle_int_t extents; + + /* Offset added to extents, used when the extents start with a negative + * offset, which occur on Windows for, and only for, desktop DC. This + * occurs when you have multiple monitors, and at least one monitor + * extends to the left, or above, the primaty monitor. The primary + * monitor on Windows always start with offset (0,0), and any other points + * to the left, or above, have negative offset. So the 'desktop DC' is + * in fact a 'virtual desktop' which can start with extents in the negative + * range. + * + * Why use new variables, and not the device transform? Simply because since + * the device transform functions are exposed, a lot of 3rd party libraries + * simply overwrite those, disregarding the prior content, instead of actually + * adding the offset. GTK for example simply reset the device transform of the + * desktop cairo surface to zero. So make some private member variables for + * this, which will not be fiddled with externally. + */ + int x_ofs, y_ofs; } cairo_win32_surface_t; #define to_win32_surface(S) ((cairo_win32_surface_t *)(S))