/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */ /* Cairo - a vector graphics library with display and print output * * Copyright © 2005 Red Hat, Inc. * Copyright © 2012 Intel Corporation * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. * * The Original Code is the cairo graphics library. * * The Initial Developer of the Original Code is Red Hat, Inc. * * Contributor(s): * Owen Taylor * Stuart Parmenter * Vladimir Vukicevic */ #define WIN32_LEAN_AND_MEAN /* We require Windows 2000 features such as ETO_PDY */ #if !defined(WINVER) || (WINVER < 0x0500) # define WINVER 0x0500 #endif #if !defined(_WIN32_WINNT) || (_WIN32_WINNT < 0x0500) # define _WIN32_WINNT 0x0500 #endif #include "cairoint.h" #include "cairo-clip-private.h" #include "cairo-composite-rectangles-private.h" #include "cairo-compositor-private.h" #include "cairo-damage-private.h" #include "cairo-default-context-private.h" #include "cairo-error-private.h" #include "cairo-image-surface-inline.h" #include "cairo-paginated-private.h" #include "cairo-pattern-private.h" #include "cairo-win32-private.h" #include "cairo-scaled-font-subsets-private.h" #include "cairo-surface-fallback-private.h" #include "cairo-surface-backend-private.h" #include #include #if defined(__MINGW32__) && !defined(ETO_PDY) # define ETO_PDY 0x2000 #endif #define PELS_72DPI ((LONG)(72. / 0.0254)) /** * SECTION:cairo-win32 * @Title: Win32 Surfaces * @Short_Description: Microsoft Windows surface support * @See_Also: #cairo_surface_t * * The Microsoft Windows surface is used to render cairo graphics to * Microsoft Windows windows, bitmaps, and printing device contexts. * * The surface returned by cairo_win32_printing_surface_create() is of surface * type %CAIRO_SURFACE_TYPE_WIN32_PRINTING and is a multi-page vector surface * type. * * The surface returned by the other win32 constructors is of surface type * %CAIRO_SURFACE_TYPE_WIN32 and is a raster surface type. **/ /** * CAIRO_HAS_WIN32_SURFACE: * * Defined if the Microsoft Windows surface backend is available. * This macro can be used to conditionally compile backend-specific code. * * Since: 1.0 **/ static const cairo_surface_backend_t cairo_win32_display_surface_backend; static cairo_status_t _create_dc_and_bitmap (cairo_win32_display_surface_t *surface, HDC original_dc, cairo_format_t format, int width, int height, unsigned char **bits_out, int *rowstride_out) { cairo_status_t status; BITMAPINFO *bitmap_info = NULL; struct { BITMAPINFOHEADER bmiHeader; RGBQUAD bmiColors[2]; } bmi_stack; void *bits; int num_palette = 0; /* Quiet GCC */ int i; surface->win32.dc = NULL; surface->bitmap = NULL; surface->is_dib = FALSE; switch (format) { default: case CAIRO_FORMAT_INVALID: case CAIRO_FORMAT_RGB16_565: case CAIRO_FORMAT_RGB30: return _cairo_error (CAIRO_STATUS_INVALID_FORMAT); case CAIRO_FORMAT_ARGB32: case CAIRO_FORMAT_RGB24: num_palette = 0; break; case CAIRO_FORMAT_A8: num_palette = 256; break; case CAIRO_FORMAT_A1: num_palette = 2; break; } if (num_palette > 2) { bitmap_info = _cairo_malloc_ab_plus_c (num_palette, sizeof(RGBQUAD), sizeof(BITMAPINFOHEADER)); if (!bitmap_info) return _cairo_error (CAIRO_STATUS_NO_MEMORY); } else { bitmap_info = (BITMAPINFO *)&bmi_stack; } bitmap_info->bmiHeader.biSize = sizeof (BITMAPINFOHEADER); bitmap_info->bmiHeader.biWidth = width == 0 ? 1 : width; bitmap_info->bmiHeader.biHeight = height == 0 ? -1 : - height; /* top-down */ bitmap_info->bmiHeader.biSizeImage = 0; bitmap_info->bmiHeader.biXPelsPerMeter = PELS_72DPI; /* unused here */ bitmap_info->bmiHeader.biYPelsPerMeter = PELS_72DPI; /* unused here */ bitmap_info->bmiHeader.biPlanes = 1; switch (format) { case CAIRO_FORMAT_INVALID: case CAIRO_FORMAT_RGB16_565: case CAIRO_FORMAT_RGB30: ASSERT_NOT_REACHED; /* We can't create real RGB24 bitmaps because something seems to * break if we do, especially if we don't set up an image * fallback. It could be a bug with using a 24bpp pixman image * (and creating one with masks). So treat them like 32bpp. * Note: This causes problems when using BitBlt/AlphaBlend/etc! * see end of file. */ case CAIRO_FORMAT_RGB24: case CAIRO_FORMAT_ARGB32: bitmap_info->bmiHeader.biBitCount = 32; bitmap_info->bmiHeader.biCompression = BI_RGB; bitmap_info->bmiHeader.biClrUsed = 0; /* unused */ bitmap_info->bmiHeader.biClrImportant = 0; break; case CAIRO_FORMAT_A8: bitmap_info->bmiHeader.biBitCount = 8; bitmap_info->bmiHeader.biCompression = BI_RGB; bitmap_info->bmiHeader.biClrUsed = 256; bitmap_info->bmiHeader.biClrImportant = 0; for (i = 0; i < 256; i++) { bitmap_info->bmiColors[i].rgbBlue = i; bitmap_info->bmiColors[i].rgbGreen = i; bitmap_info->bmiColors[i].rgbRed = i; bitmap_info->bmiColors[i].rgbReserved = 0; } break; case CAIRO_FORMAT_A1: bitmap_info->bmiHeader.biBitCount = 1; bitmap_info->bmiHeader.biCompression = BI_RGB; bitmap_info->bmiHeader.biClrUsed = 2; bitmap_info->bmiHeader.biClrImportant = 0; for (i = 0; i < 2; i++) { bitmap_info->bmiColors[i].rgbBlue = i * 255; bitmap_info->bmiColors[i].rgbGreen = i * 255; bitmap_info->bmiColors[i].rgbRed = i * 255; bitmap_info->bmiColors[i].rgbReserved = 0; } break; } surface->win32.dc = CreateCompatibleDC (original_dc); if (!surface->win32.dc) goto FAIL; surface->bitmap = CreateDIBSection (surface->win32.dc, bitmap_info, DIB_RGB_COLORS, &bits, NULL, 0); if (!surface->bitmap) goto FAIL; surface->is_dib = TRUE; GdiFlush(); surface->saved_dc_bitmap = SelectObject (surface->win32.dc, surface->bitmap); if (!surface->saved_dc_bitmap) goto FAIL; if (bitmap_info && num_palette > 2) free (bitmap_info); if (bits_out) *bits_out = bits; if (rowstride_out) { /* Windows bitmaps are padded to 32-bit (dword) boundaries */ switch (format) { case CAIRO_FORMAT_INVALID: case CAIRO_FORMAT_RGB16_565: case CAIRO_FORMAT_RGB30: ASSERT_NOT_REACHED; case CAIRO_FORMAT_ARGB32: case CAIRO_FORMAT_RGB24: *rowstride_out = 4 * width; break; case CAIRO_FORMAT_A8: *rowstride_out = (width + 3) & ~3; break; case CAIRO_FORMAT_A1: *rowstride_out = ((width + 31) & ~31) / 8; break; } } surface->win32.flags = _cairo_win32_flags_for_dc (surface->win32.dc); return CAIRO_STATUS_SUCCESS; FAIL: status = _cairo_win32_print_gdi_error (__FUNCTION__); if (bitmap_info && num_palette > 2) free (bitmap_info); if (surface->saved_dc_bitmap) { SelectObject (surface->win32.dc, surface->saved_dc_bitmap); surface->saved_dc_bitmap = NULL; } if (surface->bitmap) { DeleteObject (surface->bitmap); surface->bitmap = NULL; } if (surface->win32.dc) { DeleteDC (surface->win32.dc); surface->win32.dc = NULL; } return status; } static cairo_surface_t * _cairo_win32_display_surface_create_for_dc (HDC original_dc, cairo_format_t format, int width, int height) { cairo_status_t status; cairo_device_t *device; cairo_win32_display_surface_t *surface; unsigned char *bits; int rowstride; surface = malloc (sizeof (*surface)); if (surface == NULL) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); surface->fallback = NULL; status = _create_dc_and_bitmap (surface, original_dc, format, width, height, &bits, &rowstride); if (status) goto FAIL; surface->image = cairo_image_surface_create_for_data (bits, format, width, height, rowstride); status = surface->image->status; if (status) goto FAIL; _cairo_image_surface_set_parent (to_image_surface(surface->image), &surface->win32.base); surface->win32.format = format; surface->win32.extents.x = 0; surface->win32.extents.y = 0; surface->win32.extents.width = width; surface->win32.extents.height = height; surface->initial_clip_rgn = NULL; surface->had_simple_clip = FALSE; device = _cairo_win32_device_get (); _cairo_surface_init (&surface->win32.base, &cairo_win32_display_surface_backend, device, _cairo_content_from_format (format)); cairo_device_destroy (device); return &surface->win32.base; FAIL: if (surface->bitmap) { SelectObject (surface->win32.dc, surface->saved_dc_bitmap); DeleteObject (surface->bitmap); DeleteDC (surface->win32.dc); } free (surface); return _cairo_surface_create_in_error (status); } static cairo_surface_t * _cairo_win32_display_surface_create_similar (void *abstract_src, cairo_content_t content, int width, int height) { cairo_win32_display_surface_t *src = abstract_src; cairo_format_t format = _cairo_format_from_content (content); cairo_surface_t *new_surf = NULL; /* We force a DIB always if: * - we need alpha; or * - the parent is a DIB; or * - the parent is for printing (because we don't care about the * bit depth at that point) * * We also might end up with a DIB even if a DDB is requested if * DDB creation failed due to out of memory. */ if (!(src->is_dib || content & CAIRO_CONTENT_ALPHA)) { /* try to create a ddb */ new_surf = cairo_win32_surface_create_with_ddb (src->win32.dc, CAIRO_FORMAT_RGB24, width, height); if (new_surf->status) new_surf = NULL; } if (new_surf == NULL) { new_surf = _cairo_win32_display_surface_create_for_dc (src->win32.dc, format, width, height); } return new_surf; } static cairo_surface_t * _cairo_win32_display_surface_create_similar_image (void *abstract_other, cairo_format_t format, int width, int height) { cairo_win32_display_surface_t *surface = abstract_other; cairo_image_surface_t *image; surface = (cairo_win32_display_surface_t *) _cairo_win32_display_surface_create_for_dc (surface->win32.dc, format, width, height); if (surface->win32.base.status) return &surface->win32.base; /* And clear in order to comply with our user API semantics */ image = (cairo_image_surface_t *) surface->image; if (! image->base.is_clear) { memset (image->data, 0, image->stride * height); image->base.is_clear = TRUE; } return &image->base; } static cairo_status_t _cairo_win32_display_surface_finish (void *abstract_surface) { cairo_win32_display_surface_t *surface = abstract_surface; if (surface->image && to_image_surface(surface->image)->parent) { assert (to_image_surface(surface->image)->parent == &surface->win32.base); /* Unhook ourselves first to avoid the double-unref from the image */ to_image_surface(surface->image)->parent = NULL; cairo_surface_finish (surface->image); cairo_surface_destroy (surface->image); } /* If we created the Bitmap and DC, destroy them */ if (surface->bitmap) { SelectObject (surface->win32.dc, surface->saved_dc_bitmap); DeleteObject (surface->bitmap); DeleteDC (surface->win32.dc); } _cairo_win32_display_surface_discard_fallback (surface); if (surface->initial_clip_rgn) DeleteObject (surface->initial_clip_rgn); return CAIRO_STATUS_SUCCESS; } static cairo_image_surface_t * _cairo_win32_display_surface_map_to_image (void *abstract_surface, const cairo_rectangle_int_t *extents) { cairo_win32_display_surface_t *surface = abstract_surface; cairo_status_t status; TRACE ((stderr, "%s (surface=%d)\n", __FUNCTION__, surface->win32.base.unique_id)); if (surface->image) goto done; if (surface->fallback == NULL) { surface->fallback = _cairo_win32_display_surface_create_for_dc (surface->win32.dc, surface->win32.format, surface->win32.extents.width, surface->win32.extents.height); if (unlikely (status = surface->fallback->status)) goto err; if (!BitBlt (to_win32_surface(surface->fallback)->dc, 0, 0, surface->win32.extents.width, surface->win32.extents.height, surface->win32.dc, 0, 0, SRCCOPY)) { status = _cairo_error (CAIRO_STATUS_DEVICE_ERROR); goto err; } } surface = to_win32_display_surface (surface->fallback); done: GdiFlush(); return _cairo_surface_map_to_image (surface->image, extents); err: cairo_surface_destroy (surface->fallback); surface->fallback = NULL; return _cairo_image_surface_create_in_error (status); } static cairo_int_status_t _cairo_win32_display_surface_unmap_image (void *abstract_surface, cairo_image_surface_t *image) { cairo_win32_display_surface_t *surface = abstract_surface; /* Delay the download until the next flush, which means we also need * to make sure our sources rare flushed. */ TRACE ((stderr, "%s (surface=%d)\n", __FUNCTION__, to_win32_surface(surface)->base.unique_id)); if (surface->fallback) { cairo_rectangle_int_t r; r.x = image->base.device_transform_inverse.x0; r.y = image->base.device_transform_inverse.y0; r.width = image->width; r.height = image->height; TRACE ((stderr, "%s: adding damage (%d,%d)x(%d,%d)\n", __FUNCTION__, r.x, r.y, r.width, r.height)); surface->fallback->damage = _cairo_damage_add_rectangle (surface->fallback->damage, &r); surface = to_win32_display_surface (surface->fallback); } return _cairo_surface_unmap_image (surface->image, image); } static cairo_status_t _cairo_win32_display_surface_flush (void *abstract_surface, unsigned flags) { cairo_win32_display_surface_t *surface = abstract_surface; cairo_status_t status = CAIRO_STATUS_SUCCESS; if (flags) return CAIRO_STATUS_SUCCESS; TRACE ((stderr, "%s (surface=%d)\n", __FUNCTION__, surface->win32.base.unique_id)); if (surface->fallback == NULL) return CAIRO_STATUS_SUCCESS; if (surface->fallback->damage) { cairo_win32_display_surface_t *fallback; cairo_damage_t *damage; damage = _cairo_damage_reduce (surface->fallback->damage); surface->fallback->damage = NULL; fallback = to_win32_display_surface (surface->fallback); assert (fallback->image); TRACE ((stderr, "%s: flushing damage x %d\n", __FUNCTION__, damage->region ? cairo_region_num_rectangles (damage->region) : 0)); if (damage->status) { if (!BitBlt (surface->win32.dc, 0, 0, surface->win32.extents.width, surface->win32.extents.height, fallback->win32.dc, 0, 0, SRCCOPY)) status = _cairo_win32_print_gdi_error (__FUNCTION__); } else if (damage->region) { int n = cairo_region_num_rectangles (damage->region), i; for (i = 0; i < n; i++) { cairo_rectangle_int_t rect; cairo_region_get_rectangle (damage->region, i, &rect); TRACE ((stderr, "%s: damage (%d,%d)x(%d,%d)\n", __FUNCTION__, rect.x, rect.y, rect.width, rect.height)); if (!BitBlt (surface->win32.dc, rect.x, rect.y, rect.width, rect.height, fallback->win32.dc, rect.x, rect.y, SRCCOPY)) { status = _cairo_win32_print_gdi_error (__FUNCTION__); break; } } } _cairo_damage_destroy (damage); } else { cairo_surface_destroy (surface->fallback); surface->fallback = NULL; } return status; } static cairo_status_t _cairo_win32_display_surface_mark_dirty (void *abstract_surface, int x, int y, int width, int height) { _cairo_win32_display_surface_discard_fallback (abstract_surface); return CAIRO_STATUS_SUCCESS; } static cairo_int_status_t _cairo_win32_save_initial_clip (HDC hdc, cairo_win32_display_surface_t *surface) { RECT rect; int clipBoxType; int gm; XFORM saved_xform; /* GetClipBox/GetClipRgn and friends interact badly with a world transform * set. GetClipBox returns values in logical (transformed) coordinates; * it's unclear what GetClipRgn returns, because the region is empty in the * case of a SIMPLEREGION clip, but I assume device (untransformed) coordinates. * Similarly, IntersectClipRect works in logical units, whereas SelectClipRgn * works in device units. * * So, avoid the whole mess and get rid of the world transform * while we store our initial data and when we restore initial coordinates. * * XXX we may need to modify x/y by the ViewportOrg or WindowOrg * here in GM_COMPATIBLE; unclear. */ gm = GetGraphicsMode (hdc); if (gm == GM_ADVANCED) { GetWorldTransform (hdc, &saved_xform); ModifyWorldTransform (hdc, NULL, MWT_IDENTITY); } clipBoxType = GetClipBox (hdc, &rect); if (clipBoxType == ERROR) { _cairo_win32_print_gdi_error (__FUNCTION__); SetGraphicsMode (hdc, gm); /* XXX: Can we make a more reasonable guess at the error cause here? */ return _cairo_error (CAIRO_STATUS_DEVICE_ERROR); } surface->win32.extents.x = rect.left; surface->win32.extents.y = rect.top; surface->win32.extents.width = rect.right - rect.left; surface->win32.extents.height = rect.bottom - rect.top; surface->initial_clip_rgn = NULL; surface->had_simple_clip = FALSE; if (clipBoxType == COMPLEXREGION) { surface->initial_clip_rgn = CreateRectRgn (0, 0, 0, 0); if (GetClipRgn (hdc, surface->initial_clip_rgn) <= 0) { DeleteObject(surface->initial_clip_rgn); surface->initial_clip_rgn = NULL; } } else if (clipBoxType == SIMPLEREGION) { surface->had_simple_clip = TRUE; } if (gm == GM_ADVANCED) SetWorldTransform (hdc, &saved_xform); return CAIRO_STATUS_SUCCESS; } cairo_status_t _cairo_win32_display_surface_set_clip (cairo_win32_display_surface_t *surface, cairo_clip_t *clip) { char stack[512]; cairo_rectangle_int_t extents; int num_rects; RGNDATA *data; size_t data_size; RECT *rects; int i; HRGN gdi_region; cairo_status_t status; cairo_region_t *region; /* The semantics we want is that any clip set by cairo combines * is intersected with the clip on device context that the * surface was created for. To implement this, we need to * save the original clip when first setting a clip on surface. */ assert (_cairo_clip_is_region (clip)); region = _cairo_clip_get_region (clip); if (region == NULL) return CAIRO_STATUS_SUCCESS; cairo_region_get_extents (region, &extents); num_rects = cairo_region_num_rectangles (region); /* XXX see notes in _cairo_win32_save_initial_clip -- * this code will interact badly with a HDC which had an initial * world transform -- we should probably manually transform the * region rects, because SelectClipRgn takes device units, not * logical units (unlike IntersectClipRect). */ data_size = sizeof (RGNDATAHEADER) + num_rects * sizeof (RECT); if (data_size > sizeof (stack)) { data = malloc (data_size); if (!data) return _cairo_error(CAIRO_STATUS_NO_MEMORY); } else data = (RGNDATA *)stack; data->rdh.dwSize = sizeof (RGNDATAHEADER); data->rdh.iType = RDH_RECTANGLES; data->rdh.nCount = num_rects; data->rdh.nRgnSize = num_rects * sizeof (RECT); data->rdh.rcBound.left = extents.x; data->rdh.rcBound.top = extents.y; data->rdh.rcBound.right = extents.x + extents.width; data->rdh.rcBound.bottom = extents.y + extents.height; rects = (RECT *)data->Buffer; for (i = 0; i < num_rects; i++) { cairo_rectangle_int_t rect; cairo_region_get_rectangle (region, i, &rect); rects[i].left = rect.x; rects[i].top = rect.y; rects[i].right = rect.x + rect.width; rects[i].bottom = rect.y + rect.height; } gdi_region = ExtCreateRegion (NULL, data_size, data); if ((char *)data != stack) free (data); if (!gdi_region) return _cairo_error (CAIRO_STATUS_NO_MEMORY); /* AND the new region into our DC */ status = CAIRO_STATUS_SUCCESS; if (ExtSelectClipRgn (surface->win32.dc, gdi_region, RGN_AND) == ERROR) status = _cairo_win32_print_gdi_error (__FUNCTION__); DeleteObject (gdi_region); return status; } void _cairo_win32_display_surface_unset_clip (cairo_win32_display_surface_t *surface) { XFORM saved_xform; int gm = GetGraphicsMode (surface->win32.dc); if (gm == GM_ADVANCED) { GetWorldTransform (surface->win32.dc, &saved_xform); ModifyWorldTransform (surface->win32.dc, NULL, MWT_IDENTITY); } /* initial_clip_rgn will either be a real region or NULL (which means reset to no clip region) */ SelectClipRgn (surface->win32.dc, surface->initial_clip_rgn); if (surface->had_simple_clip) { /* then if we had a simple clip, intersect */ IntersectClipRect (surface->win32.dc, surface->win32.extents.x, surface->win32.extents.y, surface->win32.extents.x + surface->win32.extents.width, surface->win32.extents.y + surface->win32.extents.height); } if (gm == GM_ADVANCED) SetWorldTransform (surface->win32.dc, &saved_xform); } void _cairo_win32_display_surface_discard_fallback (cairo_win32_display_surface_t *surface) { if (surface->fallback) { TRACE ((stderr, "%s (surface=%d)\n", __FUNCTION__, surface->win32.base.unique_id)); cairo_surface_finish (surface->fallback); cairo_surface_destroy (surface->fallback); surface->fallback = NULL; } } static cairo_int_status_t _cairo_win32_display_surface_paint (void *surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_clip_t *clip) { cairo_win32_device_t *device = to_win32_device_from_surface (surface); TRACE ((stderr, "%s (surface=%d)\n", __FUNCTION__, to_win32_surface(surface)->base.unique_id)); if (clip == NULL && (op == CAIRO_OPERATOR_SOURCE || op == CAIRO_OPERATOR_CLEAR)) _cairo_win32_display_surface_discard_fallback (surface); return _cairo_compositor_paint (device->compositor, surface, op, source, clip); } static cairo_int_status_t _cairo_win32_display_surface_mask (void *surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_pattern_t *mask, const cairo_clip_t *clip) { cairo_win32_device_t *device = to_win32_device_from_surface (surface); TRACE ((stderr, "%s (surface=%d)\n", __FUNCTION__, to_win32_surface(surface)->base.unique_id)); if (clip == NULL && op == CAIRO_OPERATOR_SOURCE) _cairo_win32_display_surface_discard_fallback (surface); return _cairo_compositor_mask (device->compositor, surface, op, source, mask, clip); } static cairo_int_status_t _cairo_win32_display_surface_stroke (void *surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_path_fixed_t *path, const cairo_stroke_style_t *style, const cairo_matrix_t *ctm, const cairo_matrix_t *ctm_inverse, double tolerance, cairo_antialias_t antialias, const cairo_clip_t *clip) { cairo_win32_device_t *device = to_win32_device_from_surface (surface); TRACE ((stderr, "%s (surface=%d)\n", __FUNCTION__, to_win32_surface(surface)->base.unique_id)); return _cairo_compositor_stroke (device->compositor, surface, op, source, path, style, ctm, ctm_inverse, tolerance, antialias, clip); } static cairo_int_status_t _cairo_win32_display_surface_fill (void *surface, cairo_operator_t op, const cairo_pattern_t *source, const cairo_path_fixed_t *path, cairo_fill_rule_t fill_rule, double tolerance, cairo_antialias_t antialias, const cairo_clip_t *clip) { cairo_win32_device_t *device = to_win32_device_from_surface (surface); TRACE ((stderr, "%s (surface=%d)\n", __FUNCTION__, to_win32_surface(surface)->base.unique_id)); return _cairo_compositor_fill (device->compositor, surface, op, source, path, fill_rule, tolerance, antialias, clip); } static cairo_int_status_t _cairo_win32_display_surface_glyphs (void *surface, cairo_operator_t op, const cairo_pattern_t *source, cairo_glyph_t *glyphs, int num_glyphs, cairo_scaled_font_t *scaled_font, const cairo_clip_t *clip) { cairo_win32_device_t *device = to_win32_device_from_surface (surface); TRACE ((stderr, "%s (surface=%d)\n", __FUNCTION__, to_win32_surface(surface)->base.unique_id)); return _cairo_compositor_glyphs (device->compositor, surface, op, source, glyphs, num_glyphs, scaled_font, clip); } static const cairo_surface_backend_t cairo_win32_display_surface_backend = { CAIRO_SURFACE_TYPE_WIN32, _cairo_win32_display_surface_finish, _cairo_default_context_create, _cairo_win32_display_surface_create_similar, _cairo_win32_display_surface_create_similar_image, _cairo_win32_display_surface_map_to_image, _cairo_win32_display_surface_unmap_image, _cairo_surface_default_source, _cairo_surface_default_acquire_source_image, _cairo_surface_default_release_source_image, NULL, /* snapshot */ NULL, /* copy_page */ NULL, /* show_page */ _cairo_win32_surface_get_extents, NULL, /* get_font_options */ _cairo_win32_display_surface_flush, _cairo_win32_display_surface_mark_dirty, _cairo_win32_display_surface_paint, _cairo_win32_display_surface_mask, _cairo_win32_display_surface_stroke, _cairo_win32_display_surface_fill, NULL, /* fill/stroke */ _cairo_win32_display_surface_glyphs, }; /* Notes: * * Win32 alpha-understanding functions * * BitBlt - will copy full 32 bits from a 32bpp DIB to result * (so it's safe to use for ARGB32->ARGB32 SOURCE blits) * (but not safe going RGB24->ARGB32, if RGB24 is also represented * as a 32bpp DIB, since the alpha isn't discarded!) * * AlphaBlend - if both the source and dest have alpha, even if AC_SRC_ALPHA isn't set, * it will still copy over the src alpha, because the SCA value (255) will be * multiplied by all the src components. */ /** * cairo_win32_surface_create: * @hdc: the DC to create a surface for * * Creates a cairo surface that targets the given DC. The DC will be * queried for its initial clip extents, and this will be used as the * size of the cairo surface. The resulting surface will always be of * format %CAIRO_FORMAT_RGB24; should you need another surface format, * you will need to create one through * cairo_win32_surface_create_with_dib(). * * Return value: the newly created surface * * Since: 1.0 **/ cairo_surface_t * cairo_win32_surface_create (HDC hdc) { cairo_win32_display_surface_t *surface; cairo_format_t format; cairo_status_t status; cairo_device_t *device; /* Assume that everything coming in as a HDC is RGB24 */ format = CAIRO_FORMAT_RGB24; surface = malloc (sizeof (*surface)); if (surface == NULL) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); status = _cairo_win32_save_initial_clip (hdc, surface); if (status) { free (surface); return _cairo_surface_create_in_error (status); } surface->image = NULL; surface->fallback = NULL; surface->win32.format = format; surface->win32.dc = hdc; surface->bitmap = NULL; surface->is_dib = FALSE; surface->saved_dc_bitmap = NULL; surface->win32.flags = _cairo_win32_flags_for_dc (surface->win32.dc); device = _cairo_win32_device_get (); _cairo_surface_init (&surface->win32.base, &cairo_win32_display_surface_backend, device, _cairo_content_from_format (format)); cairo_device_destroy (device); return &surface->win32.base; } /** * cairo_win32_surface_create_with_dib: * @format: format of pixels in the surface to create * @width: width of the surface, in pixels * @height: height of the surface, in pixels * * Creates a device-independent-bitmap surface not associated with * any particular existing surface or device context. The created * bitmap will be uninitialized. * * Return value: the newly created surface * * Since: 1.2 **/ cairo_surface_t * cairo_win32_surface_create_with_dib (cairo_format_t format, int width, int height) { if (! CAIRO_FORMAT_VALID (format)) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT)); return _cairo_win32_display_surface_create_for_dc (NULL, format, width, height); } /** * cairo_win32_surface_create_with_ddb: * @hdc: a DC compatible with the surface to create * @format: format of pixels in the surface to create * @width: width of the surface, in pixels * @height: height of the surface, in pixels * * Creates a device-dependent-bitmap surface not associated with * any particular existing surface or device context. The created * bitmap will be uninitialized. * * Return value: the newly created surface * * Since: 1.4 **/ cairo_surface_t * cairo_win32_surface_create_with_ddb (HDC hdc, cairo_format_t format, int width, int height) { cairo_win32_display_surface_t *new_surf; HBITMAP ddb; HDC screen_dc, ddb_dc; HBITMAP saved_dc_bitmap; if (format != CAIRO_FORMAT_RGB24) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT)); /* XXX handle these eventually format != CAIRO_FORMAT_A8 || format != CAIRO_FORMAT_A1) */ if (!hdc) { screen_dc = GetDC (NULL); hdc = screen_dc; } else { screen_dc = NULL; } ddb_dc = CreateCompatibleDC (hdc); if (ddb_dc == NULL) { new_surf = (cairo_win32_display_surface_t*) _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); goto FINISH; } ddb = CreateCompatibleBitmap (hdc, width, height); if (ddb == NULL) { DeleteDC (ddb_dc); /* Note that if an app actually does hit this out of memory * condition, it's going to have lots of other issues, as * video memory is probably exhausted. However, it can often * continue using DIBs instead of DDBs. */ new_surf = (cairo_win32_display_surface_t*) _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); goto FINISH; } saved_dc_bitmap = SelectObject (ddb_dc, ddb); new_surf = (cairo_win32_display_surface_t*) cairo_win32_surface_create (ddb_dc); new_surf->bitmap = ddb; new_surf->saved_dc_bitmap = saved_dc_bitmap; new_surf->is_dib = FALSE; FINISH: if (screen_dc) ReleaseDC (NULL, screen_dc); return &new_surf->win32.base; }