diff options
author | Chris Wilson <chris@chris-wilson.co.uk> | 2012-02-12 10:17:12 +0000 |
---|---|---|
committer | Chris Wilson <chris@chris-wilson.co.uk> | 2012-02-15 14:21:01 +0000 |
commit | 92c0b37d04f9df8df53a455c8e8dda1946c84c87 (patch) | |
tree | a76b5bf3e8b3cf3df77c3ff2468bd7aa72fb6da6 /src/win32 | |
parent | 8bea52bb0b55e2b041fbd43bc36221b5eb07b863 (diff) |
win32: Move to separate directoy
I suspect I may split the win32 code into a few more files, so move it
to its own directory to reduce the clutter.
Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
Diffstat (limited to 'src/win32')
-rw-r--r-- | src/win32/cairo-win32-font.c | 2437 | ||||
-rw-r--r-- | src/win32/cairo-win32-printing-surface.c | 1915 | ||||
-rw-r--r-- | src/win32/cairo-win32-private.h | 219 | ||||
-rw-r--r-- | src/win32/cairo-win32-surface.c | 2087 |
4 files changed, 6658 insertions, 0 deletions
diff --git a/src/win32/cairo-win32-font.c b/src/win32/cairo-win32-font.c new file mode 100644 index 00000000..73fc052d --- /dev/null +++ b/src/win32/cairo-win32-font.c @@ -0,0 +1,2437 @@ +/* -*- 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 + * + * 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): + */ + +#define WIN32_LEAN_AND_MEAN +/* We require Windows 2000 features such as GetGlyphIndices */ +#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-win32-private.h" + +#include "cairo-array-private.h" +#include "cairo-error-private.h" +#include "cairo-image-surface-private.h" +#include "cairo-pattern-private.h" +#include "cairo-scaled-font-subsets-private.h" + +#include <wchar.h> + +#ifndef SPI_GETFONTSMOOTHINGTYPE +#define SPI_GETFONTSMOOTHINGTYPE 0x200a +#endif +#ifndef FE_FONTSMOOTHINGCLEARTYPE +#define FE_FONTSMOOTHINGCLEARTYPE 2 +#endif +#ifndef CLEARTYPE_QUALITY +#define CLEARTYPE_QUALITY 5 +#endif +#ifndef TT_PRIM_CSPLINE +#define TT_PRIM_CSPLINE 3 +#endif + +#define CMAP_TAG 0x70616d63 + +/** + * SECTION:cairo-win32-fonts + * @Title: Win32 Fonts + * @Short_Description: Font support for Microsoft Windows + * @See_Also: #cairo_font_face_t + * + * The Microsoft Windows font backend is primarily used to render text on + * Microsoft Windows systems. + */ + +/** + * CAIRO_HAS_WIN32_FONT: + * + * Defined if the Microsoft Windows font backend is available. + * This macro can be used to conditionally compile backend-specific code. + */ + +const cairo_scaled_font_backend_t _cairo_win32_scaled_font_backend; + +typedef struct { + cairo_scaled_font_t base; + + LOGFONTW logfont; + + BYTE quality; + + /* We do drawing and metrics computation in a "logical space" which + * is similar to font space, except that it is scaled by a factor + * of the (desired font size) * (WIN32_FONT_LOGICAL_SCALE). The multiplication + * by WIN32_FONT_LOGICAL_SCALE allows for sub-pixel precision. + */ + double logical_scale; + + /* The size we should actually request the font at from Windows; differs + * from the logical_scale because it is quantized for orthogonal + * transformations + */ + double logical_size; + + /* Transformations from device <=> logical space + */ + cairo_matrix_t logical_to_device; + cairo_matrix_t device_to_logical; + + /* We special case combinations of 90-degree-rotations, scales and + * flips ... that is transformations that take the axes to the + * axes. If preserve_axes is true, then swap_axes/swap_x/swap_y + * encode the 8 possibilities for orientation (4 rotation angles with + * and without a flip), and scale_x, scale_y the scale components. + */ + cairo_bool_t preserve_axes; + cairo_bool_t swap_axes; + cairo_bool_t swap_x; + cairo_bool_t swap_y; + double x_scale; + double y_scale; + + /* The size of the design unit of the font + */ + int em_square; + + HFONT scaled_hfont; + HFONT unscaled_hfont; + + cairo_bool_t is_bitmap; + cairo_bool_t is_type1; + cairo_bool_t delete_scaled_hfont; + cairo_bool_t has_type1_notdef_index; + unsigned long type1_notdef_index; +} cairo_win32_scaled_font_t; + +static cairo_status_t +_cairo_win32_scaled_font_set_metrics (cairo_win32_scaled_font_t *scaled_font); + +static cairo_status_t +_cairo_win32_scaled_font_init_glyph_metrics (cairo_win32_scaled_font_t *scaled_font, + cairo_scaled_glyph_t *scaled_glyph); + +static cairo_status_t +_cairo_win32_scaled_font_init_glyph_surface (cairo_win32_scaled_font_t *scaled_font, + cairo_scaled_glyph_t *scaled_glyph); + +static cairo_status_t +_cairo_win32_scaled_font_init_glyph_path (cairo_win32_scaled_font_t *scaled_font, + cairo_scaled_glyph_t *scaled_glyph); + +static void +_cairo_win32_font_face_destroy (void *abstract_face); + + +#define NEARLY_ZERO(d) (fabs(d) < (1. / 65536.)) + +static HDC +_get_global_font_dc (void) +{ + static HDC hdc; + + if (!hdc) { + hdc = CreateCompatibleDC (NULL); + if (!hdc) { + _cairo_win32_print_gdi_error ("_get_global_font_dc"); + return NULL; + } + + if (!SetGraphicsMode (hdc, GM_ADVANCED)) { + _cairo_win32_print_gdi_error ("_get_global_font_dc"); + DeleteDC (hdc); + return NULL; + } + } + + return hdc; +} + +static cairo_status_t +_compute_transform (cairo_win32_scaled_font_t *scaled_font, + cairo_matrix_t *sc) +{ + cairo_status_t status; + + if (NEARLY_ZERO (sc->yx) && NEARLY_ZERO (sc->xy) && + !NEARLY_ZERO(sc->xx) && !NEARLY_ZERO(sc->yy)) { + scaled_font->preserve_axes = TRUE; + scaled_font->x_scale = sc->xx; + scaled_font->swap_x = (sc->xx < 0); + scaled_font->y_scale = sc->yy; + scaled_font->swap_y = (sc->yy < 0); + scaled_font->swap_axes = FALSE; + + } else if (NEARLY_ZERO (sc->xx) && NEARLY_ZERO (sc->yy) && + !NEARLY_ZERO(sc->yx) && !NEARLY_ZERO(sc->xy)) { + scaled_font->preserve_axes = TRUE; + scaled_font->x_scale = sc->yx; + scaled_font->swap_x = (sc->yx < 0); + scaled_font->y_scale = sc->xy; + scaled_font->swap_y = (sc->xy < 0); + scaled_font->swap_axes = TRUE; + + } else { + scaled_font->preserve_axes = FALSE; + scaled_font->swap_x = scaled_font->swap_y = scaled_font->swap_axes = FALSE; + } + + if (scaled_font->preserve_axes) { + if (scaled_font->swap_x) + scaled_font->x_scale = - scaled_font->x_scale; + if (scaled_font->swap_y) + scaled_font->y_scale = - scaled_font->y_scale; + + scaled_font->logical_scale = WIN32_FONT_LOGICAL_SCALE * scaled_font->y_scale; + scaled_font->logical_size = WIN32_FONT_LOGICAL_SCALE * + _cairo_lround (scaled_font->y_scale); + } + + /* The font matrix has x and y "scale" components which we extract and + * use as character scale values. + */ + cairo_matrix_init (&scaled_font->logical_to_device, + sc->xx, sc->yx, sc->xy, sc->yy, 0, 0); + + if (!scaled_font->preserve_axes) { + status = _cairo_matrix_compute_basis_scale_factors (&scaled_font->logical_to_device, + &scaled_font->x_scale, &scaled_font->y_scale, + TRUE); /* XXX: Handle vertical text */ + if (status) + return status; + + scaled_font->logical_size = _cairo_lround (WIN32_FONT_LOGICAL_SCALE * + scaled_font->y_scale); + scaled_font->logical_scale = WIN32_FONT_LOGICAL_SCALE * scaled_font->y_scale; + } + + cairo_matrix_scale (&scaled_font->logical_to_device, + 1.0 / scaled_font->logical_scale, 1.0 / scaled_font->logical_scale); + + scaled_font->device_to_logical = scaled_font->logical_to_device; + + status = cairo_matrix_invert (&scaled_font->device_to_logical); + if (status) + cairo_matrix_init_identity (&scaled_font->device_to_logical); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_bool_t +_have_cleartype_quality (void) +{ + OSVERSIONINFO version_info; + + version_info.dwOSVersionInfoSize = sizeof (OSVERSIONINFO); + + if (!GetVersionEx (&version_info)) { + _cairo_win32_print_gdi_error ("_have_cleartype_quality"); + return FALSE; + } + + return (version_info.dwMajorVersion > 5 || + (version_info.dwMajorVersion == 5 && + version_info.dwMinorVersion >= 1)); /* XP or newer */ +} + +static BYTE +_get_system_quality (void) +{ + BOOL font_smoothing; + UINT smoothing_type; + + if (!SystemParametersInfo (SPI_GETFONTSMOOTHING, 0, &font_smoothing, 0)) { + _cairo_win32_print_gdi_error ("_get_system_quality"); + return DEFAULT_QUALITY; + } + + if (font_smoothing) { + if (_have_cleartype_quality ()) { + if (!SystemParametersInfo (SPI_GETFONTSMOOTHINGTYPE, + 0, &smoothing_type, 0)) { + _cairo_win32_print_gdi_error ("_get_system_quality"); + return DEFAULT_QUALITY; + } + + if (smoothing_type == FE_FONTSMOOTHINGCLEARTYPE) + return CLEARTYPE_QUALITY; + } + + return ANTIALIASED_QUALITY; + } else { + return DEFAULT_QUALITY; + } +} + +/* If face_hfont is non-%NULL then font_matrix must be a simple scale by some + * factor S, ctm must be the identity, logfont->lfHeight must be -S, + * logfont->lfWidth, logfont->lfEscapement, logfont->lfOrientation must + * all be 0, and face_hfont is the result of calling CreateFontIndirectW on + * logfont. + */ +static cairo_status_t +_win32_scaled_font_create (LOGFONTW *logfont, + HFONT face_hfont, + cairo_font_face_t *font_face, + const cairo_matrix_t *font_matrix, + const cairo_matrix_t *ctm, + const cairo_font_options_t *options, + cairo_scaled_font_t **font_out) +{ + HDC hdc; + cairo_win32_scaled_font_t *f; + cairo_matrix_t scale; + cairo_status_t status; + + hdc = _get_global_font_dc (); + if (hdc == NULL) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + f = malloc (sizeof(cairo_win32_scaled_font_t)); + if (f == NULL) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + f->logfont = *logfont; + + /* We don't have any control over the hinting style or subpixel + * order in the Win32 font API, so we ignore those parts of + * cairo_font_options_t. We use the 'antialias' field to set + * the 'quality'. + * + * XXX: The other option we could pay attention to, but don't + * here is the hint_metrics options. + */ + if (options->antialias == CAIRO_ANTIALIAS_DEFAULT) + f->quality = _get_system_quality (); + else { + switch (options->antialias) { + case CAIRO_ANTIALIAS_NONE: + f->quality = NONANTIALIASED_QUALITY; + break; + case CAIRO_ANTIALIAS_GRAY: + case CAIRO_ANTIALIAS_FAST: + case CAIRO_ANTIALIAS_GOOD: + f->quality = ANTIALIASED_QUALITY; + break; + case CAIRO_ANTIALIAS_SUBPIXEL: + case CAIRO_ANTIALIAS_BEST: + if (_have_cleartype_quality ()) + f->quality = CLEARTYPE_QUALITY; + else + f->quality = ANTIALIASED_QUALITY; + break; + case CAIRO_ANTIALIAS_DEFAULT: + ASSERT_NOT_REACHED; + } + } + + f->em_square = 0; + f->scaled_hfont = NULL; + f->unscaled_hfont = NULL; + f->has_type1_notdef_index = FALSE; + + if (f->quality == logfont->lfQuality || + (logfont->lfQuality == DEFAULT_QUALITY && + options->antialias == CAIRO_ANTIALIAS_DEFAULT)) { + /* If face_hfont is non-NULL, then we can use it to avoid creating our + * own --- because the constraints on face_hfont mentioned above + * guarantee it was created in exactly the same way that + * _win32_scaled_font_get_scaled_hfont would create it. + */ + f->scaled_hfont = face_hfont; + } + /* don't delete the hfont if we're using the one passed in to us */ + f->delete_scaled_hfont = !f->scaled_hfont; + + cairo_matrix_multiply (&scale, font_matrix, ctm); + status = _compute_transform (f, &scale); + if (status) + goto FAIL; + + status = _cairo_scaled_font_init (&f->base, font_face, + font_matrix, ctm, options, + &_cairo_win32_scaled_font_backend); + if (status) + goto FAIL; + + status = _cairo_win32_scaled_font_set_metrics (f); + if (status) { + _cairo_scaled_font_fini (&f->base); + goto FAIL; + } + + *font_out = &f->base; + return CAIRO_STATUS_SUCCESS; + + FAIL: + free (f); + return status; +} + +static cairo_status_t +_win32_scaled_font_set_world_transform (cairo_win32_scaled_font_t *scaled_font, + HDC hdc) +{ + XFORM xform; + + _cairo_matrix_to_win32_xform (&scaled_font->logical_to_device, &xform); + + if (!SetWorldTransform (hdc, &xform)) + return _cairo_win32_print_gdi_error ("_win32_scaled_font_set_world_transform"); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_win32_scaled_font_set_identity_transform (HDC hdc) +{ + if (!ModifyWorldTransform (hdc, NULL, MWT_IDENTITY)) + return _cairo_win32_print_gdi_error ("_win32_scaled_font_set_identity_transform"); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_win32_scaled_font_get_scaled_hfont (cairo_win32_scaled_font_t *scaled_font, + HFONT *hfont_out) +{ + if (!scaled_font->scaled_hfont) { + LOGFONTW logfont = scaled_font->logfont; + logfont.lfHeight = -scaled_font->logical_size; + logfont.lfWidth = 0; + logfont.lfEscapement = 0; + logfont.lfOrientation = 0; + logfont.lfQuality = scaled_font->quality; + + scaled_font->scaled_hfont = CreateFontIndirectW (&logfont); + if (!scaled_font->scaled_hfont) + return _cairo_win32_print_gdi_error ("_win32_scaled_font_get_scaled_hfont"); + } + + *hfont_out = scaled_font->scaled_hfont; + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_win32_scaled_font_get_unscaled_hfont (cairo_win32_scaled_font_t *scaled_font, + HDC hdc, + HFONT *hfont_out) +{ + if (scaled_font->unscaled_hfont == NULL) { + OUTLINETEXTMETRIC *otm; + unsigned int otm_size; + HFONT scaled_hfont; + LOGFONTW logfont; + cairo_status_t status; + + status = _win32_scaled_font_get_scaled_hfont (scaled_font, + &scaled_hfont); + if (status) + return status; + + if (! SelectObject (hdc, scaled_hfont)) + return _cairo_win32_print_gdi_error ("_win32_scaled_font_get_unscaled_hfont:SelectObject"); + + otm_size = GetOutlineTextMetrics (hdc, 0, NULL); + if (! otm_size) + return _cairo_win32_print_gdi_error ("_win32_scaled_font_get_unscaled_hfont:GetOutlineTextMetrics"); + + otm = malloc (otm_size); + if (otm == NULL) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + if (! GetOutlineTextMetrics (hdc, otm_size, otm)) { + status = _cairo_win32_print_gdi_error ("_win32_scaled_font_get_unscaled_hfont:GetOutlineTextMetrics"); + free (otm); + return status; + } + + scaled_font->em_square = otm->otmEMSquare; + free (otm); + + logfont = scaled_font->logfont; + logfont.lfHeight = -scaled_font->em_square; + logfont.lfWidth = 0; + logfont.lfEscapement = 0; + logfont.lfOrientation = 0; + logfont.lfQuality = scaled_font->quality; + + scaled_font->unscaled_hfont = CreateFontIndirectW (&logfont); + if (! scaled_font->unscaled_hfont) + return _cairo_win32_print_gdi_error ("_win32_scaled_font_get_unscaled_hfont:CreateIndirect"); + } + + *hfont_out = scaled_font->unscaled_hfont; + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_win32_scaled_font_select_unscaled_font (cairo_scaled_font_t *scaled_font, + HDC hdc) +{ + cairo_status_t status; + HFONT hfont; + HFONT old_hfont = NULL; + + status = _win32_scaled_font_get_unscaled_hfont ((cairo_win32_scaled_font_t *)scaled_font, hdc, &hfont); + if (status) + return status; + + old_hfont = SelectObject (hdc, hfont); + if (!old_hfont) + return _cairo_win32_print_gdi_error ("_cairo_win32_scaled_font_select_unscaled_font"); + + status = _win32_scaled_font_set_identity_transform (hdc); + if (status) { + SelectObject (hdc, old_hfont); + return status; + } + + SetMapMode (hdc, MM_TEXT); + + return CAIRO_STATUS_SUCCESS; +} + +cairo_bool_t +_cairo_win32_scaled_font_is_type1 (cairo_scaled_font_t *scaled_font) +{ + cairo_win32_scaled_font_t *win32_scaled_font; + + win32_scaled_font = (cairo_win32_scaled_font_t *) scaled_font; + + return win32_scaled_font->is_type1; +} + +cairo_bool_t +_cairo_win32_scaled_font_is_bitmap (cairo_scaled_font_t *scaled_font) +{ + cairo_win32_scaled_font_t *win32_scaled_font; + + win32_scaled_font = (cairo_win32_scaled_font_t *) scaled_font; + + return win32_scaled_font->is_bitmap; +} + +static void +_cairo_win32_scaled_font_done_unscaled_font (cairo_scaled_font_t *scaled_font) +{ +} + +/* implement the font backend interface */ + +static cairo_status_t +_cairo_win32_font_face_create_for_toy (cairo_toy_font_face_t *toy_face, + cairo_font_face_t **font_face) +{ + LOGFONTW logfont; + uint16_t *face_name; + int face_name_len; + cairo_status_t status; + + status = _cairo_utf8_to_utf16 (toy_face->family, -1, + &face_name, &face_name_len); + if (status) + return status; + + if (face_name_len > LF_FACESIZE - 1) + face_name_len = LF_FACESIZE - 1; + + memcpy (logfont.lfFaceName, face_name, sizeof (uint16_t) * face_name_len); + logfont.lfFaceName[face_name_len] = 0; + free (face_name); + + logfont.lfHeight = 0; /* filled in later */ + logfont.lfWidth = 0; /* filled in later */ + logfont.lfEscapement = 0; /* filled in later */ + logfont.lfOrientation = 0; /* filled in later */ + + switch (toy_face->weight) { + case CAIRO_FONT_WEIGHT_NORMAL: + default: + logfont.lfWeight = FW_NORMAL; + break; + case CAIRO_FONT_WEIGHT_BOLD: + logfont.lfWeight = FW_BOLD; + break; + } + + switch (toy_face->slant) { + case CAIRO_FONT_SLANT_NORMAL: + default: + logfont.lfItalic = FALSE; + break; + case CAIRO_FONT_SLANT_ITALIC: + case CAIRO_FONT_SLANT_OBLIQUE: + logfont.lfItalic = TRUE; + break; + } + + logfont.lfUnderline = FALSE; + logfont.lfStrikeOut = FALSE; + /* The docs for LOGFONT discourage using this, since the + * interpretation is locale-specific, but it's not clear what + * would be a better alternative. + */ + logfont.lfCharSet = DEFAULT_CHARSET; + logfont.lfOutPrecision = OUT_DEFAULT_PRECIS; + logfont.lfClipPrecision = CLIP_DEFAULT_PRECIS; + logfont.lfQuality = DEFAULT_QUALITY; /* filled in later */ + logfont.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE; + + *font_face = cairo_win32_font_face_create_for_logfontw (&logfont); + + return CAIRO_STATUS_SUCCESS; +} + +static void +_cairo_win32_scaled_font_fini (void *abstract_font) +{ + cairo_win32_scaled_font_t *scaled_font = abstract_font; + + if (scaled_font == NULL) + return; + + if (scaled_font->scaled_hfont && scaled_font->delete_scaled_hfont) + DeleteObject (scaled_font->scaled_hfont); + + if (scaled_font->unscaled_hfont) + DeleteObject (scaled_font->unscaled_hfont); +} + +static cairo_int_status_t +_cairo_win32_scaled_font_type1_text_to_glyphs (cairo_win32_scaled_font_t *scaled_font, + double x, + double y, + const char *utf8, + cairo_glyph_t **glyphs, + int *num_glyphs) +{ + uint16_t *utf16; + int n16; + int i; + WORD *glyph_indices = NULL; + cairo_status_t status; + double x_pos, y_pos; + HDC hdc = NULL; + cairo_matrix_t mat; + + status = _cairo_utf8_to_utf16 (utf8, -1, &utf16, &n16); + if (status) + return status; + + glyph_indices = _cairo_malloc_ab (n16 + 1, sizeof (WORD)); + if (!glyph_indices) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto FAIL1; + } + + hdc = _get_global_font_dc (); + assert (hdc != NULL); + + status = cairo_win32_scaled_font_select_font (&scaled_font->base, hdc); + if (status) + goto FAIL2; + + if (GetGlyphIndicesW (hdc, utf16, n16, glyph_indices, 0) == GDI_ERROR) { + status = _cairo_win32_print_gdi_error ("_cairo_win32_scaled_font_type1_text_to_glyphs:GetGlyphIndicesW"); + goto FAIL3; + } + + *num_glyphs = n16; + *glyphs = _cairo_malloc_ab (n16, sizeof (cairo_glyph_t)); + if (!*glyphs) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto FAIL3; + } + + x_pos = x; + y_pos = y; + + mat = scaled_font->base.ctm; + status = cairo_matrix_invert (&mat); + assert (status == CAIRO_STATUS_SUCCESS); + + _cairo_scaled_font_freeze_cache (&scaled_font->base); + + for (i = 0; i < n16; i++) { + cairo_scaled_glyph_t *scaled_glyph; + + (*glyphs)[i].index = glyph_indices[i]; + (*glyphs)[i].x = x_pos; + (*glyphs)[i].y = y_pos; + + status = _cairo_scaled_glyph_lookup (&scaled_font->base, + glyph_indices[i], + CAIRO_SCALED_GLYPH_INFO_METRICS, + &scaled_glyph); + if (status) { + free (*glyphs); + *glyphs = NULL; + break; + } + + x = scaled_glyph->x_advance; + y = scaled_glyph->y_advance; + cairo_matrix_transform_distance (&mat, &x, &y); + x_pos += x; + y_pos += y; + } + + _cairo_scaled_font_thaw_cache (&scaled_font->base); + +FAIL3: + cairo_win32_scaled_font_done_font (&scaled_font->base); +FAIL2: + free (glyph_indices); +FAIL1: + free (utf16); + + return status; +} + +static cairo_int_status_t +_cairo_win32_scaled_font_text_to_glyphs (void *abstract_font, + double x, + double y, + const char *utf8, + cairo_glyph_t **glyphs, + int *num_glyphs) +{ + cairo_win32_scaled_font_t *scaled_font = abstract_font; + uint16_t *utf16; + int n16; + GCP_RESULTSW gcp_results; + unsigned int buffer_size, i; + WCHAR *glyph_indices = NULL; + int *dx = NULL; + cairo_status_t status; + double x_pos, y_pos; + double x_incr, y_incr; + HDC hdc = NULL; + + /* GetCharacterPlacement() returns utf16 instead of glyph indices + * for Type 1 fonts. Use GetGlyphIndices for Type 1 fonts. */ + if (scaled_font->is_type1) + return _cairo_win32_scaled_font_type1_text_to_glyphs (scaled_font, + x, + y, + utf8, + glyphs, + num_glyphs); + + /* Compute a vector in user space along the baseline of length one logical space unit */ + x_incr = 1; + y_incr = 0; + cairo_matrix_transform_distance (&scaled_font->base.font_matrix, &x_incr, &y_incr); + x_incr /= scaled_font->logical_scale; + y_incr /= scaled_font->logical_scale; + + status = _cairo_utf8_to_utf16 (utf8, -1, &utf16, &n16); + if (status) + return status; + + gcp_results.lStructSize = sizeof (GCP_RESULTS); + gcp_results.lpOutString = NULL; + gcp_results.lpOrder = NULL; + gcp_results.lpCaretPos = NULL; + gcp_results.lpClass = NULL; + + buffer_size = MAX (n16 * 1.2, 16); /* Initially guess number of chars plus a few */ + if (buffer_size > INT_MAX) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto FAIL1; + } + + hdc = _get_global_font_dc (); + assert (hdc != NULL); + + status = cairo_win32_scaled_font_select_font (&scaled_font->base, hdc); + if (status) + goto FAIL1; + + while (TRUE) { + free (glyph_indices); + glyph_indices = NULL; + + free (dx); + dx = NULL; + + glyph_indices = _cairo_malloc_ab (buffer_size, sizeof (WCHAR)); + dx = _cairo_malloc_ab (buffer_size, sizeof (int)); + if (!glyph_indices || !dx) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto FAIL2; + } + + gcp_results.nGlyphs = buffer_size; + gcp_results.lpDx = dx; + gcp_results.lpGlyphs = glyph_indices; + + if (!GetCharacterPlacementW (hdc, utf16, n16, + 0, + &gcp_results, + GCP_DIACRITIC | GCP_LIGATE | GCP_GLYPHSHAPE | GCP_REORDER)) { + status = _cairo_win32_print_gdi_error ("_cairo_win32_scaled_font_text_to_glyphs"); + goto FAIL2; + } + + if (gcp_results.lpDx && gcp_results.lpGlyphs) + break; + + /* Too small a buffer, try again */ + + buffer_size += buffer_size / 2; + if (buffer_size > INT_MAX) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto FAIL2; + } + } + + *num_glyphs = gcp_results.nGlyphs; + *glyphs = _cairo_malloc_ab (gcp_results.nGlyphs, sizeof (cairo_glyph_t)); + if (!*glyphs) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto FAIL2; + } + + x_pos = x; + y_pos = y; + + for (i = 0; i < gcp_results.nGlyphs; i++) { + (*glyphs)[i].index = glyph_indices[i]; + (*glyphs)[i].x = x_pos ; + (*glyphs)[i].y = y_pos; + + x_pos += x_incr * dx[i]; + y_pos += y_incr * dx[i]; + } + + FAIL2: + free (glyph_indices); + free (dx); + + cairo_win32_scaled_font_done_font (&scaled_font->base); + + FAIL1: + free (utf16); + + return status; +} + +static unsigned long +_cairo_win32_scaled_font_ucs4_to_index (void *abstract_font, + uint32_t ucs4) +{ + cairo_win32_scaled_font_t *scaled_font = abstract_font; + wchar_t unicode[2]; + WORD glyph_index; + HDC hdc = NULL; + cairo_status_t status; + + hdc = _get_global_font_dc (); + assert (hdc != NULL); + + status = cairo_win32_scaled_font_select_font (&scaled_font->base, hdc); + if (status) + return 0; + + unicode[0] = ucs4; + unicode[1] = 0; + if (GetGlyphIndicesW (hdc, unicode, 1, &glyph_index, 0) == GDI_ERROR) { + _cairo_win32_print_gdi_error ("_cairo_win32_scaled_font_ucs4_to_index:GetGlyphIndicesW"); + glyph_index = 0; + } + + cairo_win32_scaled_font_done_font (&scaled_font->base); + + return glyph_index; +} + +static cairo_status_t +_cairo_win32_scaled_font_set_metrics (cairo_win32_scaled_font_t *scaled_font) +{ + cairo_status_t status; + cairo_font_extents_t extents; + + TEXTMETRIC metrics; + HDC hdc; + + hdc = _get_global_font_dc (); + assert (hdc != NULL); + + if (scaled_font->preserve_axes || scaled_font->base.options.hint_metrics == CAIRO_HINT_METRICS_OFF) { + /* For 90-degree rotations (including 0), we get the metrics + * from the GDI in logical space, then convert back to font space + */ + status = cairo_win32_scaled_font_select_font (&scaled_font->base, hdc); + if (status) + return status; + GetTextMetrics (hdc, &metrics); + cairo_win32_scaled_font_done_font (&scaled_font->base); + + extents.ascent = metrics.tmAscent / scaled_font->logical_scale; + extents.descent = metrics.tmDescent / scaled_font->logical_scale; + + extents.height = (metrics.tmHeight + metrics.tmExternalLeading) / scaled_font->logical_scale; + extents.max_x_advance = metrics.tmMaxCharWidth / scaled_font->logical_scale; + extents.max_y_advance = 0; + + } else { + /* For all other transformations, we use the design metrics + * of the font. The GDI results from GetTextMetrics() on a + * transformed font are inexplicably large and we want to + * avoid them. + */ + status = _cairo_win32_scaled_font_select_unscaled_font (&scaled_font->base, hdc); + if (status) + return status; + GetTextMetrics (hdc, &metrics); + _cairo_win32_scaled_font_done_unscaled_font (&scaled_font->base); + + extents.ascent = (double)metrics.tmAscent / scaled_font->em_square; + extents.descent = (double)metrics.tmDescent / scaled_font->em_square; + extents.height = (double)(metrics.tmHeight + metrics.tmExternalLeading) / scaled_font->em_square; + extents.max_x_advance = (double)(metrics.tmMaxCharWidth) / scaled_font->em_square; + extents.max_y_advance = 0; + + } + + scaled_font->is_bitmap = !(metrics.tmPitchAndFamily & TMPF_VECTOR); + + /* Need to determine if this is a Type 1 font for the special + * handling in _text_to_glyphs. Unlike TrueType or OpenType, + * Type1 fonts do not have a "cmap" table (or any other table). + * However GetFontData() will retrieve a Type1 font when + * requesting that GetFontData() retrieve data from the start of + * the file. This is to distinguish Type1 from stroke fonts such + * as "Script" and "Modern". The TMPF_TRUETYPE test is redundant + * but improves performance for the most common fonts. + */ + scaled_font->is_type1 = FALSE; + if (!(metrics.tmPitchAndFamily & TMPF_TRUETYPE) && + (metrics.tmPitchAndFamily & TMPF_VECTOR)) + { + if ((GetFontData (hdc, CMAP_TAG, 0, NULL, 0) == GDI_ERROR) && + (GetFontData (hdc, 0, 0, NULL, 0) != GDI_ERROR)) + { + scaled_font->is_type1 = TRUE; + } + } + + return _cairo_scaled_font_set_metrics (&scaled_font->base, &extents); +} + +static cairo_status_t +_cairo_win32_scaled_font_init_glyph_metrics (cairo_win32_scaled_font_t *scaled_font, + cairo_scaled_glyph_t *scaled_glyph) +{ + static const MAT2 matrix = { { 0, 1 }, { 0, 0 }, { 0, 0 }, { 0, 1 } }; + GLYPHMETRICS metrics; + cairo_status_t status; + cairo_text_extents_t extents; + HDC hdc; + + hdc = _get_global_font_dc (); + assert (hdc != NULL); + + if (scaled_font->is_bitmap) { + /* GetGlyphOutline will not work. Assume that the glyph does not extend outside the font box. */ + cairo_font_extents_t font_extents; + INT width = 0; + UINT charIndex = _cairo_scaled_glyph_index (scaled_glyph); + + cairo_scaled_font_extents (&scaled_font->base, &font_extents); + + status = cairo_win32_scaled_font_select_font (&scaled_font->base, hdc); + if (status) + return status; + + if (!GetCharWidth32(hdc, charIndex, charIndex, &width)) { + status = _cairo_win32_print_gdi_error ("_cairo_win32_scaled_font_init_glyph_metrics:GetCharWidth32"); + width = 0; + } + cairo_win32_scaled_font_done_font (&scaled_font->base); + if (status) + return status; + + extents.x_bearing = 0; + extents.y_bearing = scaled_font->base.ctm.yy * (-font_extents.ascent / scaled_font->y_scale); + extents.width = width / (WIN32_FONT_LOGICAL_SCALE * scaled_font->x_scale); + extents.height = scaled_font->base.ctm.yy * (font_extents.ascent + font_extents.descent) / scaled_font->y_scale; + extents.x_advance = extents.width; + extents.y_advance = 0; + } else if (scaled_font->preserve_axes && scaled_font->base.options.hint_metrics != CAIRO_HINT_METRICS_OFF) { + /* If we aren't rotating / skewing the axes, then we get the metrics + * from the GDI in device space and convert to font space. + */ + status = cairo_win32_scaled_font_select_font (&scaled_font->base, hdc); + if (status) + return status; + + if (GetGlyphOutlineW (hdc, _cairo_scaled_glyph_index (scaled_glyph), + GGO_METRICS | GGO_GLYPH_INDEX, + &metrics, 0, NULL, &matrix) == GDI_ERROR) { + memset (&metrics, 0, sizeof (GLYPHMETRICS)); + } else { + if (metrics.gmBlackBoxX > 0 && scaled_font->base.options.antialias != CAIRO_ANTIALIAS_NONE) { + /* The bounding box reported by Windows supposedly contains the glyph's "black" area; + * however, antialiasing (especially with ClearType) means that the actual image that + * needs to be rendered may "bleed" into the adjacent pixels, mainly on the right side. + * To avoid clipping the glyphs when drawn by _cairo_surface_fallback_show_glyphs, + * for example, or other code that uses glyph extents to determine the area to update, + * we add a pixel of "slop" to left side of the nominal "black" area returned by GDI, + * and two pixels to the right (as tests show some glyphs bleed into this column). + */ + metrics.gmptGlyphOrigin.x -= 1; + metrics.gmBlackBoxX += 3; + } + } + cairo_win32_scaled_font_done_font (&scaled_font->base); + + if (scaled_font->swap_axes) { + extents.x_bearing = - metrics.gmptGlyphOrigin.y / scaled_font->y_scale; + extents.y_bearing = metrics.gmptGlyphOrigin.x / scaled_font->x_scale; + extents.width = metrics.gmBlackBoxY / scaled_font->y_scale; + extents.height = metrics.gmBlackBoxX / scaled_font->x_scale; + extents.x_advance = metrics.gmCellIncY / scaled_font->x_scale; + extents.y_advance = metrics.gmCellIncX / scaled_font->y_scale; + } else { + extents.x_bearing = metrics.gmptGlyphOrigin.x / scaled_font->x_scale; + extents.y_bearing = - metrics.gmptGlyphOrigin.y / scaled_font->y_scale; + extents.width = metrics.gmBlackBoxX / scaled_font->x_scale; + extents.height = metrics.gmBlackBoxY / scaled_font->y_scale; + extents.x_advance = metrics.gmCellIncX / scaled_font->x_scale; + extents.y_advance = metrics.gmCellIncY / scaled_font->y_scale; + } + + if (scaled_font->swap_x) { + extents.x_bearing = (- extents.x_bearing - extents.width); + extents.x_advance = - extents.x_advance; + } + + if (scaled_font->swap_y) { + extents.y_bearing = (- extents.y_bearing - extents.height); + extents.y_advance = - extents.y_advance; + } + + } else { + /* For all other transformations, we use the design metrics + * of the font. + */ + status = _cairo_win32_scaled_font_select_unscaled_font (&scaled_font->base, hdc); + if (status) + return status; + + if (GetGlyphOutlineW (hdc, _cairo_scaled_glyph_index (scaled_glyph), + GGO_METRICS | GGO_GLYPH_INDEX, + &metrics, 0, NULL, &matrix) == GDI_ERROR) { + memset (&metrics, 0, sizeof (GLYPHMETRICS)); + } + _cairo_win32_scaled_font_done_unscaled_font (&scaled_font->base); + + extents.x_bearing = (double)metrics.gmptGlyphOrigin.x / scaled_font->em_square; + extents.y_bearing = - (double)metrics.gmptGlyphOrigin.y / scaled_font->em_square; + extents.width = (double)metrics.gmBlackBoxX / scaled_font->em_square; + extents.height = (double)metrics.gmBlackBoxY / scaled_font->em_square; + extents.x_advance = (double)metrics.gmCellIncX / scaled_font->em_square; + extents.y_advance = (double)metrics.gmCellIncY / scaled_font->em_square; + } + + _cairo_scaled_glyph_set_metrics (scaled_glyph, + &scaled_font->base, + &extents); + + return CAIRO_STATUS_SUCCESS; +} + +/* Not currently used code, but may be useful in the future if we add + * back the capability to the scaled font backend interface to get the + * actual device space bbox rather than computing it from the + * font-space metrics. + */ +#if 0 +static cairo_status_t +_cairo_win32_scaled_font_glyph_bbox (void *abstract_font, + const cairo_glyph_t *glyphs, + int num_glyphs, + cairo_box_t *bbox) +{ + static const MAT2 matrix = { { 0, 1 }, { 0, 0 }, { 0, 0 }, { 0, 1 } }; + cairo_win32_scaled_font_t *scaled_font = abstract_font; + int x1 = 0, x2 = 0, y1 = 0, y2 = 0; + + if (num_glyphs > 0) { + HDC hdc; + GLYPHMETRICS metrics; + cairo_status_t status; + int i; + + hdc = _get_global_font_dc (); + assert (hdc != NULL); + + status = cairo_win32_scaled_font_select_font (&scaled_font->base, hdc); + if (status) + return status; + + for (i = 0; i < num_glyphs; i++) { + int x = _cairo_lround (glyphs[i].x); + int y = _cairo_lround (glyphs[i].y); + + GetGlyphOutlineW (hdc, glyphs[i].index, GGO_METRICS | GGO_GLYPH_INDEX, + &metrics, 0, NULL, &matrix); + + if (i == 0 || x1 > x + metrics.gmptGlyphOrigin.x) + x1 = x + metrics.gmptGlyphOrigin.x; + if (i == 0 || y1 > y - metrics.gmptGlyphOrigin.y) + y1 = y - metrics.gmptGlyphOrigin.y; + if (i == 0 || x2 < x + metrics.gmptGlyphOrigin.x + (int)metrics.gmBlackBoxX) + x2 = x + metrics.gmptGlyphOrigin.x + (int)metrics.gmBlackBoxX; + if (i == 0 || y2 < y - metrics.gmptGlyphOrigin.y + (int)metrics.gmBlackBoxY) + y2 = y - metrics.gmptGlyphOrigin.y + (int)metrics.gmBlackBoxY; + } + + cairo_win32_scaled_font_done_font (&scaled_font->base); + } + + bbox->p1.x = _cairo_fixed_from_int (x1); + bbox->p1.y = _cairo_fixed_from_int (y1); + bbox->p2.x = _cairo_fixed_from_int (x2); + bbox->p2.y = _cairo_fixed_from_int (y2); + + return CAIRO_STATUS_SUCCESS; +} +#endif + +typedef struct { + cairo_win32_scaled_font_t *scaled_font; + HDC hdc; + + cairo_array_t glyphs; + cairo_array_t dx; + + int start_x; + int last_x; + int last_y; +} cairo_glyph_state_t; + +static void +_start_glyphs (cairo_glyph_state_t *state, + cairo_win32_scaled_font_t *scaled_font, + HDC hdc) +{ + state->hdc = hdc; + state->scaled_font = scaled_font; + + _cairo_array_init (&state->glyphs, sizeof (WCHAR)); + _cairo_array_init (&state->dx, sizeof (int)); +} + +static cairo_status_t +_flush_glyphs (cairo_glyph_state_t *state) +{ + cairo_status_t status; + int dx = 0; + WCHAR * elements; + int * dx_elements; + + status = _cairo_array_append (&state->dx, &dx); + if (status) + return status; + + elements = _cairo_array_index (&state->glyphs, 0); + dx_elements = _cairo_array_index (&state->dx, 0); + if (!ExtTextOutW (state->hdc, + state->start_x, state->last_y, + ETO_GLYPH_INDEX, + NULL, + elements, + state->glyphs.num_elements, + dx_elements)) { + return _cairo_win32_print_gdi_error ("_flush_glyphs"); + } + + _cairo_array_truncate (&state->glyphs, 0); + _cairo_array_truncate (&state->dx, 0); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_add_glyph (cairo_glyph_state_t *state, + unsigned long index, + double device_x, + double device_y) +{ + cairo_status_t status; + double user_x = device_x; + double user_y = device_y; + WCHAR glyph_index = index; + int logical_x, logical_y; + + cairo_matrix_transform_point (&state->scaled_font->device_to_logical, &user_x, &user_y); + + logical_x = _cairo_lround (user_x); + logical_y = _cairo_lround (user_y); + + if (state->glyphs.num_elements > 0) { + int dx; + + if (logical_y != state->last_y) { + status = _flush_glyphs (state); + if (status) + return status; + state->start_x = logical_x; + } else { + dx = logical_x - state->last_x; + status = _cairo_array_append (&state->dx, &dx); + if (status) + return status; + } + } else { + state->start_x = logical_x; + } + + state->last_x = logical_x; + state->last_y = logical_y; + + status = _cairo_array_append (&state->glyphs, &glyph_index); + if (status) + return status; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_finish_glyphs (cairo_glyph_state_t *state) +{ + cairo_status_t status; + + status = _flush_glyphs (state); + + _cairo_array_fini (&state->glyphs); + _cairo_array_fini (&state->dx); + + return status; +} + +static cairo_status_t +_draw_glyphs_on_surface (cairo_win32_surface_t *surface, + cairo_win32_scaled_font_t *scaled_font, + COLORREF color, + int x_offset, + int y_offset, + const cairo_glyph_t *glyphs, + int num_glyphs) +{ + cairo_glyph_state_t state; + cairo_status_t status, status2; + int i; + + if (!SaveDC (surface->dc)) + return _cairo_win32_print_gdi_error ("_draw_glyphs_on_surface:SaveDC"); + + status = cairo_win32_scaled_font_select_font (&scaled_font->base, surface->dc); + if (status) + goto FAIL1; + + SetTextColor (surface->dc, color); + SetTextAlign (surface->dc, TA_BASELINE | TA_LEFT); + SetBkMode (surface->dc, TRANSPARENT); + + _start_glyphs (&state, scaled_font, surface->dc); + + for (i = 0; i < num_glyphs; i++) { + status = _add_glyph (&state, glyphs[i].index, + glyphs[i].x - x_offset, glyphs[i].y - y_offset); + if (status) + goto FAIL2; + } + + FAIL2: + status2 = _finish_glyphs (&state); + if (status == CAIRO_STATUS_SUCCESS) + status = status2; + + cairo_win32_scaled_font_done_font (&scaled_font->base); + FAIL1: + RestoreDC (surface->dc, -1); + + return status; +} + +/* Duplicate the green channel of a 4-channel mask in the alpha channel, then + * invert the whole mask. + */ +static void +_compute_argb32_mask_alpha (cairo_win32_surface_t *mask_surface) +{ + cairo_image_surface_t *image = (cairo_image_surface_t *)mask_surface->image; + int i, j; + + for (i = 0; i < image->height; i++) { + uint32_t *p = (uint32_t *) (image->data + i * image->stride); + for (j = 0; j < image->width; j++) { + *p = 0xffffffff ^ (*p | ((*p & 0x0000ff00) << 16)); + p++; + } + } +} + +/* Invert a mask + */ +static void +_invert_argb32_mask (cairo_win32_surface_t *mask_surface) +{ + cairo_image_surface_t *image = (cairo_image_surface_t *)mask_surface->image; + int i, j; + + for (i = 0; i < image->height; i++) { + uint32_t *p = (uint32_t *) (image->data + i * image->stride); + for (j = 0; j < image->width; j++) { + *p = 0xffffffff ^ *p; + p++; + } + } +} + +/* Compute an alpha-mask from a monochrome RGB24 image + */ +static cairo_surface_t * +_compute_a8_mask (cairo_win32_surface_t *mask_surface) +{ + cairo_image_surface_t *image24 = (cairo_image_surface_t *)mask_surface->image; + cairo_image_surface_t *image8; + int i, j; + + if (image24->base.status) + return cairo_surface_reference (&image24->base); + + image8 = (cairo_image_surface_t *)cairo_image_surface_create (CAIRO_FORMAT_A8, + image24->width, image24->height); + if (image8->base.status) + return &image8->base; + + for (i = 0; i < image24->height; i++) { + uint32_t *p = (uint32_t *) (image24->data + i * image24->stride); + unsigned char *q = (unsigned char *) (image8->data + i * image8->stride); + + for (j = 0; j < image24->width; j++) { + *q = 255 - ((*p & 0x0000ff00) >> 8); + p++; + q++; + } + } + + return &image8->base; +} + +static cairo_int_status_t +_cairo_win32_scaled_font_glyph_init (void *abstract_font, + cairo_scaled_glyph_t *scaled_glyph, + cairo_scaled_glyph_info_t info) +{ + cairo_win32_scaled_font_t *scaled_font = abstract_font; + cairo_status_t status; + + if ((info & CAIRO_SCALED_GLYPH_INFO_METRICS) != 0) { + status = _cairo_win32_scaled_font_init_glyph_metrics (scaled_font, scaled_glyph); + if (status) + return status; + } + + if (info & CAIRO_SCALED_GLYPH_INFO_SURFACE) { + status = _cairo_win32_scaled_font_init_glyph_surface (scaled_font, scaled_glyph); + if (status) + return status; + } + + if ((info & CAIRO_SCALED_GLYPH_INFO_PATH) != 0) { + status = _cairo_win32_scaled_font_init_glyph_path (scaled_font, scaled_glyph); + if (status) + return status; + } + + return CAIRO_STATUS_SUCCESS; +} + +#if 0 +static cairo_int_status_t +_cairo_win32_scaled_font_show_glyphs (void *abstract_font, + cairo_operator_t op, + const cairo_pattern_t *pattern, + cairo_surface_t *generic_surface, + int source_x, + int source_y, + int dest_x, + int dest_y, + unsigned int width, + unsigned int height, + cairo_glyph_t *glyphs, + int num_glyphs, + cairo_region_t *clip_region, + int *remaining_glyphs) +{ + cairo_win32_scaled_font_t *scaled_font = abstract_font; + cairo_win32_surface_t *surface = (cairo_win32_surface_t *)generic_surface; + cairo_status_t status; + + if (width == 0 || height == 0) + return CAIRO_STATUS_SUCCESS; + + if (_cairo_surface_is_win32 (generic_surface) && + surface->format == CAIRO_FORMAT_RGB24 && + op == CAIRO_OPERATOR_OVER && + _cairo_pattern_is_opaque_solid (pattern)) { + + cairo_solid_pattern_t *solid_pattern = (cairo_solid_pattern_t *)pattern; + + /* When compositing OVER on a GDI-understood surface, with a + * solid opaque color, we can just call ExtTextOut directly. + */ + COLORREF new_color; + + status = _cairo_win32_surface_set_clip_region (surface, clip_region); + if (unlikely (status)) + return status; + + new_color = RGB (((int)solid_pattern->color.red_short) >> 8, + ((int)solid_pattern->color.green_short) >> 8, + ((int)solid_pattern->color.blue_short) >> 8); + + return _draw_glyphs_on_surface (surface, scaled_font, new_color, + 0, 0, + glyphs, num_glyphs); + } else { + /* Otherwise, we need to draw using software fallbacks. We create a mask + * surface by drawing the the glyphs onto a DIB, black-on-white then + * inverting. GDI outputs gamma-corrected images so inverted black-on-white + * is very different from white-on-black. We favor the more common + * case where the final output is dark-on-light. + */ + cairo_win32_surface_t *tmp_surface; + cairo_surface_t *mask_surface; + cairo_surface_pattern_t mask; + RECT r; + + tmp_surface = (cairo_win32_surface_t *)cairo_win32_surface_create_with_dib (CAIRO_FORMAT_ARGB32, width, height); + if (tmp_surface->base.status) + return tmp_surface->base.status; + + r.left = 0; + r.top = 0; + r.right = width; + r.bottom = height; + FillRect (tmp_surface->dc, &r, GetStockObject (WHITE_BRUSH)); + + status = _draw_glyphs_on_surface (tmp_surface, + scaled_font, RGB (0, 0, 0), + dest_x, dest_y, + glyphs, num_glyphs); + if (status) { + cairo_surface_destroy (&tmp_surface->base); + return status; + } + + if (scaled_font->quality == CLEARTYPE_QUALITY) { + /* For ClearType, we need a 4-channel mask. If we are compositing on + * a surface with alpha, we need to compute the alpha channel of + * the mask (we just copy the green channel). But for a destination + * surface without alpha the alpha channel of the mask is ignored + */ + + if (surface->format != CAIRO_FORMAT_RGB24) + _compute_argb32_mask_alpha (tmp_surface); + else + _invert_argb32_mask (tmp_surface); + + mask_surface = &tmp_surface->base; + } else { + mask_surface = _compute_a8_mask (tmp_surface); + cairo_surface_destroy (&tmp_surface->base); + status = mask_surface->status; + if (status) + return status; + } + + /* For op == OVER, no-cleartype, a possible optimization here is to + * draw onto an intermediate ARGB32 surface and alpha-blend that with the + * destination + */ + _cairo_pattern_init_for_surface (&mask, mask_surface); + cairo_surface_destroy (mask_surface); + + if (scaled_font->quality == CLEARTYPE_QUALITY) + mask.base.has_component_alpha = TRUE; + + status = _cairo_surface_mask (&surface->base, op, pattern, &mask.base, + clip_region); + + _cairo_pattern_fini (&mask.base); + + return status; + } +} +#endif + +static cairo_int_status_t +_cairo_win32_scaled_font_load_truetype_table (void *abstract_font, + unsigned long tag, + long offset, + unsigned char *buffer, + unsigned long *length) +{ + cairo_win32_scaled_font_t *scaled_font = abstract_font; + HDC hdc; + cairo_status_t status; + DWORD ret; + + hdc = _get_global_font_dc (); + assert (hdc != NULL); + + tag = (tag&0x000000ff)<<24 | (tag&0x0000ff00)<<8 | (tag&0x00ff0000)>>8 | (tag&0xff000000)>>24; + status = cairo_win32_scaled_font_select_font (&scaled_font->base, hdc); + if (status) + return status; + + ret = GetFontData (hdc, tag, offset, buffer, *length); + if (ret == GDI_ERROR || (buffer && ret != *length)) + status = CAIRO_INT_STATUS_UNSUPPORTED; + else + *length = ret; + + cairo_win32_scaled_font_done_font (&scaled_font->base); + + return status; +} + +static cairo_int_status_t +_cairo_win32_scaled_font_index_to_ucs4 (void *abstract_font, + unsigned long index, + uint32_t *ucs4) +{ + cairo_win32_scaled_font_t *scaled_font = abstract_font; + GLYPHSET *glyph_set; + uint16_t *utf16 = NULL; + WORD *glyph_indices = NULL; + HDC hdc = NULL; + int res; + unsigned int i, j, num_glyphs; + cairo_status_t status; + + hdc = _get_global_font_dc (); + assert (hdc != NULL); + + status = cairo_win32_scaled_font_select_font (&scaled_font->base, hdc); + if (status) + return status; + + res = GetFontUnicodeRanges(hdc, NULL); + if (res == 0) { + status = _cairo_win32_print_gdi_error ( + "_cairo_win32_scaled_font_index_to_ucs4:GetFontUnicodeRanges"); + goto exit1; + } + + glyph_set = malloc (res); + if (glyph_set == NULL) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto exit1; + } + + res = GetFontUnicodeRanges(hdc, glyph_set); + if (res == 0) { + status = _cairo_win32_print_gdi_error ( + "_cairo_win32_scaled_font_index_to_ucs4:GetFontUnicodeRanges"); + goto exit1; + } + + *ucs4 = (uint32_t) -1; + for (i = 0; i < glyph_set->cRanges; i++) { + num_glyphs = glyph_set->ranges[i].cGlyphs; + + utf16 = _cairo_malloc_ab (num_glyphs + 1, sizeof (uint16_t)); + if (utf16 == NULL) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto exit1; + } + + glyph_indices = _cairo_malloc_ab (num_glyphs + 1, sizeof (WORD)); + if (glyph_indices == NULL) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto exit2; + } + + for (j = 0; j < num_glyphs; j++) + utf16[j] = glyph_set->ranges[i].wcLow + j; + utf16[j] = 0; + + if (GetGlyphIndicesW (hdc, utf16, num_glyphs, glyph_indices, 0) == GDI_ERROR) { + status = _cairo_win32_print_gdi_error ( + "_cairo_win32_scaled_font_index_to_ucs4:GetGlyphIndicesW"); + goto exit2; + } + + for (j = 0; j < num_glyphs; j++) { + if (glyph_indices[j] == index) { + *ucs4 = utf16[j]; + goto exit2; + } + } + + free (glyph_indices); + glyph_indices = NULL; + free (utf16); + utf16 = NULL; + } + +exit2: + free (glyph_indices); + free (utf16); + free (glyph_set); +exit1: + cairo_win32_scaled_font_done_font (&scaled_font->base); + + return status; +} + +static cairo_bool_t +_cairo_win32_scaled_font_is_synthetic (void *abstract_font) +{ + cairo_win32_scaled_font_t *scaled_font = abstract_font; + cairo_status_t status; + int weight; + cairo_bool_t bold; + cairo_bool_t italic; + + status = _cairo_truetype_get_style (&scaled_font->base, + &weight, + &bold, + &italic); + /* If this doesn't work assume it is not synthetic to avoid + * unneccessary subsetting fallbacks. */ + if (status != CAIRO_STATUS_SUCCESS) + return FALSE; + + if (scaled_font->logfont.lfWeight != weight || + scaled_font->logfont.lfItalic != italic) + return TRUE; + + return FALSE; +} + +static cairo_int_status_t +_cairo_win32_scaled_font_index_to_glyph_name (void *abstract_font, + char **glyph_names, + int num_glyph_names, + unsigned long glyph_index, + unsigned long *glyph_array_index) +{ + cairo_win32_scaled_font_t *scaled_font = abstract_font; + int i; + + /* Windows puts .notdef at index 0 then numbers the remaining + * glyphs starting from 1 in the order they appear in the font. */ + + /* Find the position of .notdef in the list of glyph names. We + * only need to do this once per scaled font. */ + if (! scaled_font->has_type1_notdef_index) { + for (i = 0; i < num_glyph_names; i++) { + if (strcmp (glyph_names[i], ".notdef") == 0) { + scaled_font->type1_notdef_index = i; + scaled_font->has_type1_notdef_index = TRUE; + break; + } + } + if (! scaled_font->has_type1_notdef_index) + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + /* Once we know the position of .notdef the position of any glyph + * in the font can easily be obtained. */ + if (glyph_index == 0) + *glyph_array_index = scaled_font->type1_notdef_index; + else if (glyph_index <= scaled_font->type1_notdef_index) + *glyph_array_index = glyph_index - 1; + else if (glyph_index < num_glyph_names) + *glyph_array_index = glyph_index; + else + return CAIRO_INT_STATUS_UNSUPPORTED; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +_cairo_win32_scaled_font_load_type1_data (void *abstract_font, + long offset, + unsigned char *buffer, + unsigned long *length) +{ + cairo_win32_scaled_font_t *scaled_font = abstract_font; + + if (! scaled_font->is_type1) + return CAIRO_INT_STATUS_UNSUPPORTED; + + /* Using the tag 0 retrieves the entire font file. This works with + * Type 1 fonts as well as TTF/OTF fonts. */ + return _cairo_win32_scaled_font_load_truetype_table (scaled_font, + 0, + offset, + buffer, + length); +} + +static cairo_status_t +_cairo_win32_scaled_font_init_glyph_surface (cairo_win32_scaled_font_t *scaled_font, + cairo_scaled_glyph_t *scaled_glyph) +{ + cairo_status_t status; + cairo_glyph_t glyph; + cairo_win32_surface_t *surface; + cairo_t *cr; + cairo_surface_t *image; + int width, height; + int x1, y1, x2, y2; + + x1 = _cairo_fixed_integer_floor (scaled_glyph->bbox.p1.x); + y1 = _cairo_fixed_integer_floor (scaled_glyph->bbox.p1.y); + x2 = _cairo_fixed_integer_ceil (scaled_glyph->bbox.p2.x); + y2 = _cairo_fixed_integer_ceil (scaled_glyph->bbox.p2.y); + width = x2 - x1; + height = y2 - y1; + + surface = (cairo_win32_surface_t *) + cairo_win32_surface_create_with_dib (CAIRO_FORMAT_RGB24, width, height); + + cr = cairo_create (&surface->base); + cairo_set_source_rgb (cr, 1, 1, 1); + cairo_paint (cr); + status = cairo_status (cr); + cairo_destroy(cr); + if (status) + goto FAIL; + + glyph.index = _cairo_scaled_glyph_index (scaled_glyph); + glyph.x = -x1; + glyph.y = -y1; + status = _draw_glyphs_on_surface (surface, scaled_font, RGB(0,0,0), + 0, 0, &glyph, 1); + if (status) + goto FAIL; + + GdiFlush(); + + image = _compute_a8_mask (surface); + status = image->status; + if (status) + goto FAIL; + + cairo_surface_set_device_offset (image, -x1, -y1); + _cairo_scaled_glyph_set_surface (scaled_glyph, + &scaled_font->base, + (cairo_image_surface_t *) image); + + FAIL: + cairo_surface_destroy (&surface->base); + + return status; +} + +static void +_cairo_win32_transform_FIXED_to_fixed (cairo_matrix_t *matrix, + FIXED Fx, FIXED Fy, + cairo_fixed_t *fx, cairo_fixed_t *fy) +{ + double x = Fx.value + Fx.fract / 65536.0; + double y = Fy.value + Fy.fract / 65536.0; + cairo_matrix_transform_point (matrix, &x, &y); + *fx = _cairo_fixed_from_double (x); + *fy = _cairo_fixed_from_double (y); +} + +static cairo_status_t +_cairo_win32_scaled_font_init_glyph_path (cairo_win32_scaled_font_t *scaled_font, + cairo_scaled_glyph_t *scaled_glyph) +{ + static const MAT2 matrix = { { 0, 1 }, { 0, 0 }, { 0, 0 }, { 0, -1 } }; + cairo_status_t status; + GLYPHMETRICS metrics; + HDC hdc; + DWORD bytesGlyph; + unsigned char *buffer, *ptr; + cairo_path_fixed_t *path; + cairo_matrix_t transform; + cairo_fixed_t x, y; + + if (scaled_font->is_bitmap) + return CAIRO_INT_STATUS_UNSUPPORTED; + + hdc = _get_global_font_dc (); + assert (hdc != NULL); + + path = _cairo_path_fixed_create (); + if (!path) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + if (scaled_font->base.options.hint_style == CAIRO_HINT_STYLE_NONE) { + status = _cairo_win32_scaled_font_select_unscaled_font (&scaled_font->base, hdc); + transform = scaled_font->base.scale; + cairo_matrix_scale (&transform, 1.0/scaled_font->em_square, 1.0/scaled_font->em_square); + } else { + status = cairo_win32_scaled_font_select_font (&scaled_font->base, hdc); + cairo_matrix_init_identity(&transform); + } + if (status) + goto CLEANUP_PATH; + + bytesGlyph = GetGlyphOutlineW (hdc, _cairo_scaled_glyph_index (scaled_glyph), + GGO_NATIVE | GGO_GLYPH_INDEX, + &metrics, 0, NULL, &matrix); + + if (bytesGlyph == GDI_ERROR) { + status = _cairo_win32_print_gdi_error ("_cairo_win32_scaled_font_glyph_path"); + goto CLEANUP_FONT; + } + + ptr = buffer = malloc (bytesGlyph); + if (!buffer) { + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + goto CLEANUP_FONT; + } + + if (GetGlyphOutlineW (hdc, _cairo_scaled_glyph_index (scaled_glyph), + GGO_NATIVE | GGO_GLYPH_INDEX, + &metrics, bytesGlyph, buffer, &matrix) == GDI_ERROR) { + status = _cairo_win32_print_gdi_error ("_cairo_win32_scaled_font_glyph_path"); + goto CLEANUP_BUFFER; + } + + while (ptr < buffer + bytesGlyph) { + TTPOLYGONHEADER *header = (TTPOLYGONHEADER *)ptr; + unsigned char *endPoly = ptr + header->cb; + + ptr += sizeof (TTPOLYGONHEADER); + + _cairo_win32_transform_FIXED_to_fixed (&transform, + header->pfxStart.x, + header->pfxStart.y, + &x, &y); + status = _cairo_path_fixed_move_to (path, x, y); + if (status) + goto CLEANUP_BUFFER; + + while (ptr < endPoly) { + TTPOLYCURVE *curve = (TTPOLYCURVE *)ptr; + POINTFX *points = curve->apfx; + int i; + switch (curve->wType) { + case TT_PRIM_LINE: + for (i = 0; i < curve->cpfx; i++) { + _cairo_win32_transform_FIXED_to_fixed (&transform, + points[i].x, + points[i].y, + &x, &y); + status = _cairo_path_fixed_line_to (path, x, y); + if (status) + goto CLEANUP_BUFFER; + } + break; + case TT_PRIM_QSPLINE: + for (i = 0; i < curve->cpfx - 1; i++) { + cairo_fixed_t p1x, p1y, p2x, p2y, cx, cy, c1x, c1y, c2x, c2y; + if (! _cairo_path_fixed_get_current_point (path, &p1x, &p1y)) + goto CLEANUP_BUFFER; + _cairo_win32_transform_FIXED_to_fixed (&transform, + points[i].x, + points[i].y, + &cx, &cy); + + if (i + 1 == curve->cpfx - 1) { + _cairo_win32_transform_FIXED_to_fixed (&transform, + points[i + 1].x, + points[i + 1].y, + &p2x, &p2y); + } else { + /* records with more than one curve use interpolation for + control points, per http://support.microsoft.com/kb/q87115/ */ + _cairo_win32_transform_FIXED_to_fixed (&transform, + points[i + 1].x, + points[i + 1].y, + &x, &y); + p2x = (cx + x) / 2; + p2y = (cy + y) / 2; + } + + c1x = 2 * cx / 3 + p1x / 3; + c1y = 2 * cy / 3 + p1y / 3; + c2x = 2 * cx / 3 + p2x / 3; + c2y = 2 * cy / 3 + p2y / 3; + + status = _cairo_path_fixed_curve_to (path, c1x, c1y, c2x, c2y, p2x, p2y); + if (status) + goto CLEANUP_BUFFER; + } + break; + case TT_PRIM_CSPLINE: + for (i = 0; i < curve->cpfx - 2; i += 2) { + cairo_fixed_t x1, y1, x2, y2; + _cairo_win32_transform_FIXED_to_fixed (&transform, + points[i].x, + points[i].y, + &x, &y); + _cairo_win32_transform_FIXED_to_fixed (&transform, + points[i + 1].x, + points[i + 1].y, + &x1, &y1); + _cairo_win32_transform_FIXED_to_fixed (&transform, + points[i + 2].x, + points[i + 2].y, + &x2, &y2); + status = _cairo_path_fixed_curve_to (path, x, y, x1, y1, x2, y2); + if (status) + goto CLEANUP_BUFFER; + } + break; + } + ptr += sizeof(TTPOLYCURVE) + sizeof (POINTFX) * (curve->cpfx - 1); + } + status = _cairo_path_fixed_close_path (path); + if (status) + goto CLEANUP_BUFFER; + } + + _cairo_scaled_glyph_set_path (scaled_glyph, + &scaled_font->base, + path); + + CLEANUP_BUFFER: + free (buffer); + + CLEANUP_FONT: + if (scaled_font->base.options.hint_style == CAIRO_HINT_STYLE_NONE) + _cairo_win32_scaled_font_done_unscaled_font (&scaled_font->base); + else + cairo_win32_scaled_font_done_font (&scaled_font->base); + + CLEANUP_PATH: + if (status != CAIRO_STATUS_SUCCESS) + _cairo_path_fixed_destroy (path); + + return status; +} + +const cairo_scaled_font_backend_t _cairo_win32_scaled_font_backend = { + CAIRO_FONT_TYPE_WIN32, + _cairo_win32_scaled_font_fini, + _cairo_win32_scaled_font_glyph_init, + NULL, /* _cairo_win32_scaled_font_text_to_glyphs, FIXME */ + _cairo_win32_scaled_font_ucs4_to_index, + _cairo_win32_scaled_font_load_truetype_table, + _cairo_win32_scaled_font_index_to_ucs4, + _cairo_win32_scaled_font_is_synthetic, + _cairo_win32_scaled_font_index_to_glyph_name, + _cairo_win32_scaled_font_load_type1_data +}; + +/* #cairo_win32_font_face_t */ + +typedef struct _cairo_win32_font_face cairo_win32_font_face_t; + +/* If hfont is non-%NULL then logfont->lfHeight must be -S for some S, + * logfont->lfWidth, logfont->lfEscapement, logfont->lfOrientation must + * all be 0, and hfont is the result of calling CreateFontIndirectW on + * logfont. + */ +struct _cairo_win32_font_face { + cairo_font_face_t base; + LOGFONTW logfont; + HFONT hfont; +}; + +/* implement the platform-specific interface */ + +static cairo_bool_t +_is_scale (const cairo_matrix_t *matrix, double scale) +{ + return matrix->xx == scale && matrix->yy == scale && + matrix->xy == 0. && matrix->yx == 0. && + matrix->x0 == 0. && matrix->y0 == 0.; +} + +static cairo_status_t +_cairo_win32_font_face_scaled_font_create (void *abstract_face, + const cairo_matrix_t *font_matrix, + const cairo_matrix_t *ctm, + const cairo_font_options_t *options, + cairo_scaled_font_t **font) +{ + HFONT hfont = NULL; + + cairo_win32_font_face_t *font_face = abstract_face; + + if (font_face->hfont) { + /* Check whether it's OK to go ahead and use the font-face's HFONT. */ + if (_is_scale (ctm, 1.) && + _is_scale (font_matrix, -font_face->logfont.lfHeight)) { + hfont = font_face->hfont; + } + } + + return _win32_scaled_font_create (&font_face->logfont, + hfont, + &font_face->base, + font_matrix, ctm, options, + font); +} + +const cairo_font_face_backend_t _cairo_win32_font_face_backend = { + CAIRO_FONT_TYPE_WIN32, + _cairo_win32_font_face_create_for_toy, + _cairo_win32_font_face_destroy, + _cairo_win32_font_face_scaled_font_create +}; + +/* We maintain a hash table from LOGFONT,HFONT => #cairo_font_face_t. + * The primary purpose of this mapping is to provide unique + * #cairo_font_face_t values so that our cache and mapping from + * #cairo_font_face_t => #cairo_scaled_font_t works. Once the + * corresponding #cairo_font_face_t objects fall out of downstream + * caches, we don't need them in this hash table anymore. + * + * Modifications to this hash table are protected by + * _cairo_win32_font_face_mutex. + */ + +static cairo_hash_table_t *cairo_win32_font_face_hash_table = NULL; + +static int +_cairo_win32_font_face_keys_equal (const void *key_a, + const void *key_b); + +static void +_cairo_win32_font_face_hash_table_destroy (void) +{ + cairo_hash_table_t *hash_table; + + /* We manually acquire the lock rather than calling + * _cairo_win32_font_face_hash_table_lock simply to avoid creating + * the table only to destroy it again. */ + CAIRO_MUTEX_LOCK (_cairo_win32_font_face_mutex); + hash_table = cairo_win32_font_face_hash_table; + cairo_win32_font_face_hash_table = NULL; + CAIRO_MUTEX_UNLOCK (_cairo_win32_font_face_mutex); + + if (hash_table != NULL) + _cairo_hash_table_destroy (hash_table); +} + +static cairo_hash_table_t * +_cairo_win32_font_face_hash_table_lock (void) +{ + CAIRO_MUTEX_LOCK (_cairo_win32_font_face_mutex); + + if (unlikely (cairo_win32_font_face_hash_table == NULL)) + { + cairo_win32_font_face_hash_table = + _cairo_hash_table_create (_cairo_win32_font_face_keys_equal); + + if (unlikely (cairo_win32_font_face_hash_table == NULL)) { + CAIRO_MUTEX_UNLOCK (_cairo_win32_font_face_mutex); + _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); + return NULL; + } + } + + return cairo_win32_font_face_hash_table; +} + +static void +_cairo_win32_font_face_hash_table_unlock (void) +{ + CAIRO_MUTEX_UNLOCK (_cairo_win32_font_face_mutex); +} + +static void +_cairo_win32_font_face_destroy (void *abstract_face) +{ + cairo_win32_font_face_t *font_face = abstract_face; + cairo_hash_table_t *hash_table; + + hash_table = _cairo_win32_font_face_hash_table_lock (); + /* All created objects must have been mapped in the hash table. */ + assert (hash_table != NULL); + + if (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&font_face->base.ref_count)) { + /* somebody recreated the font whilst we waited for the lock */ + _cairo_win32_font_face_hash_table_unlock (); + return; + } + + /* Font faces in SUCCESS status are guaranteed to be in the + * hashtable. Font faces in an error status are removed from the + * hashtable if they are found during a lookup, thus they should + * only be removed if they are in the hashtable. */ + if (likely (font_face->base.status == CAIRO_STATUS_SUCCESS) || + _cairo_hash_table_lookup (hash_table, &font_face->base.hash_entry) == font_face) + _cairo_hash_table_remove (hash_table, &font_face->base.hash_entry); + + _cairo_win32_font_face_hash_table_unlock (); +} + +static void +_cairo_win32_font_face_init_key (cairo_win32_font_face_t *key, + LOGFONTW *logfont, + HFONT font) +{ + unsigned long hash = _CAIRO_HASH_INIT_VALUE; + + key->logfont = *logfont; + key->hfont = font; + + hash = _cairo_hash_bytes (0, logfont->lfFaceName, 2*wcslen(logfont->lfFaceName)); + hash = _cairo_hash_bytes (hash, &logfont->lfWeight, sizeof(logfont->lfWeight)); + hash = _cairo_hash_bytes (hash, &logfont->lfItalic, sizeof(logfont->lfItalic)); + + key->base.hash_entry.hash = hash; +} + +static int +_cairo_win32_font_face_keys_equal (const void *key_a, + const void *key_b) +{ + const cairo_win32_font_face_t *face_a = key_a; + const cairo_win32_font_face_t *face_b = key_b; + + if (face_a->logfont.lfWeight == face_b->logfont.lfWeight && + face_a->logfont.lfItalic == face_b->logfont.lfItalic && + face_a->logfont.lfUnderline == face_b->logfont.lfUnderline && + face_a->logfont.lfStrikeOut == face_b->logfont.lfStrikeOut && + face_a->logfont.lfCharSet == face_b->logfont.lfCharSet && + face_a->logfont.lfOutPrecision == face_b->logfont.lfOutPrecision && + face_a->logfont.lfClipPrecision == face_b->logfont.lfClipPrecision && + face_a->logfont.lfPitchAndFamily == face_b->logfont.lfPitchAndFamily && + (wcscmp (face_a->logfont.lfFaceName, face_b->logfont.lfFaceName) == 0)) + return TRUE; + else + return FALSE; +} + +/** + * cairo_win32_font_face_create_for_logfontw_hfont: + * @logfont: A #LOGFONTW structure specifying the font to use. + * If @font is %NULL then the lfHeight, lfWidth, lfOrientation and lfEscapement + * fields of this structure are ignored. Otherwise lfWidth, lfOrientation and + * lfEscapement must be zero. + * @font: An #HFONT that can be used when the font matrix is a scale by + * -lfHeight and the CTM is identity. + * + * Creates a new font for the Win32 font backend based on a + * #LOGFONT. This font can then be used with + * cairo_set_font_face() or cairo_scaled_font_create(). + * The #cairo_scaled_font_t + * returned from cairo_scaled_font_create() is also for the Win32 backend + * and can be used with functions such as cairo_win32_scaled_font_select_font(). + * + * Return value: a newly created #cairo_font_face_t. Free with + * cairo_font_face_destroy() when you are done using it. + **/ +cairo_font_face_t * +cairo_win32_font_face_create_for_logfontw_hfont (LOGFONTW *logfont, HFONT font) +{ + cairo_win32_font_face_t *font_face, key; + cairo_hash_table_t *hash_table; + cairo_status_t status; + + hash_table = _cairo_win32_font_face_hash_table_lock (); + if (unlikely (hash_table == NULL)) { + _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); + return (cairo_font_face_t *)&_cairo_font_face_nil; + } + + _cairo_win32_font_face_init_key (&key, logfont, font); + + /* Return existing unscaled font if it exists in the hash table. */ + font_face = _cairo_hash_table_lookup (hash_table, + &key.base.hash_entry); + if (font_face != NULL) { + if (font_face->base.status == CAIRO_STATUS_SUCCESS) { + cairo_font_face_reference (&font_face->base); + _cairo_win32_font_face_hash_table_unlock (); + return &font_face->base; + } + + /* remove the bad font from the hash table */ + _cairo_hash_table_remove (hash_table, &font_face->base.hash_entry); + } + + /* Otherwise create it and insert into hash table. */ + font_face = malloc (sizeof (cairo_win32_font_face_t)); + if (!font_face) { + _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); + goto FAIL; + } + + _cairo_win32_font_face_init_key (font_face, logfont, font); + _cairo_font_face_init (&font_face->base, &_cairo_win32_font_face_backend); + + assert (font_face->base.hash_entry.hash == key.base.hash_entry.hash); + status = _cairo_hash_table_insert (hash_table, + &font_face->base.hash_entry); + if (unlikely (status)) + goto FAIL; + + _cairo_win32_font_face_hash_table_unlock (); + return &font_face->base; + +FAIL: + _cairo_win32_font_face_hash_table_unlock (); + return (cairo_font_face_t *)&_cairo_font_face_nil; +} + +/** + * cairo_win32_font_face_create_for_logfontw: + * @logfont: A #LOGFONTW structure specifying the font to use. + * The lfHeight, lfWidth, lfOrientation and lfEscapement + * fields of this structure are ignored. + * + * Creates a new font for the Win32 font backend based on a + * #LOGFONT. This font can then be used with + * cairo_set_font_face() or cairo_scaled_font_create(). + * The #cairo_scaled_font_t + * returned from cairo_scaled_font_create() is also for the Win32 backend + * and can be used with functions such as cairo_win32_scaled_font_select_font(). + * + * Return value: a newly created #cairo_font_face_t. Free with + * cairo_font_face_destroy() when you are done using it. + **/ +cairo_font_face_t * +cairo_win32_font_face_create_for_logfontw (LOGFONTW *logfont) +{ + return cairo_win32_font_face_create_for_logfontw_hfont (logfont, NULL); +} + +/** + * cairo_win32_font_face_create_for_hfont: + * @font: An #HFONT structure specifying the font to use. + * + * Creates a new font for the Win32 font backend based on a + * #HFONT. This font can then be used with + * cairo_set_font_face() or cairo_scaled_font_create(). + * The #cairo_scaled_font_t + * returned from cairo_scaled_font_create() is also for the Win32 backend + * and can be used with functions such as cairo_win32_scaled_font_select_font(). + * + * Return value: a newly created #cairo_font_face_t. Free with + * cairo_font_face_destroy() when you are done using it. + **/ +cairo_font_face_t * +cairo_win32_font_face_create_for_hfont (HFONT font) +{ + LOGFONTW logfont; + GetObjectW (font, sizeof(logfont), &logfont); + + if (logfont.lfEscapement != 0 || logfont.lfOrientation != 0 || + logfont.lfWidth != 0) { + /* We can't use this font because that optimization requires that + * lfEscapement, lfOrientation and lfWidth be zero. */ + font = NULL; + } + + return cairo_win32_font_face_create_for_logfontw_hfont (&logfont, font); +} + +static cairo_bool_t +_cairo_scaled_font_is_win32 (cairo_scaled_font_t *scaled_font) +{ + return scaled_font->backend == &_cairo_win32_scaled_font_backend; +} + +/** + * cairo_win32_scaled_font_select_font: + * @scaled_font: A #cairo_scaled_font_t from the Win32 font backend. Such an + * object can be created with cairo_win32_scaled_font_create_for_logfontw(). + * @hdc: a device context + * + * Selects the font into the given device context and changes the + * map mode and world transformation of the device context to match + * that of the font. This function is intended for use when using + * layout APIs such as Uniscribe to do text layout with the + * cairo font. After finishing using the device context, you must call + * cairo_win32_scaled_font_done_font() to release any resources allocated + * by this function. + * + * See cairo_win32_scaled_font_get_metrics_factor() for converting logical + * coordinates from the device context to font space. + * + * Normally, calls to SaveDC() and RestoreDC() would be made around + * the use of this function to preserve the original graphics state. + * + * Return value: %CAIRO_STATUS_SUCCESS if the operation succeeded. + * otherwise an error such as %CAIRO_STATUS_NO_MEMORY and + * the device context is unchanged. + **/ +cairo_status_t +cairo_win32_scaled_font_select_font (cairo_scaled_font_t *scaled_font, + HDC hdc) +{ + cairo_status_t status; + HFONT hfont; + HFONT old_hfont = NULL; + int old_mode; + + if (! _cairo_scaled_font_is_win32 (scaled_font)) { + return _cairo_error (CAIRO_STATUS_FONT_TYPE_MISMATCH); + } + + if (scaled_font->status) + return scaled_font->status; + + status = _win32_scaled_font_get_scaled_hfont ((cairo_win32_scaled_font_t *)scaled_font, &hfont); + if (status) + return status; + + old_hfont = SelectObject (hdc, hfont); + if (!old_hfont) + return _cairo_win32_print_gdi_error ("cairo_win32_scaled_font_select_font:SelectObject"); + + old_mode = SetGraphicsMode (hdc, GM_ADVANCED); + if (!old_mode) { + status = _cairo_win32_print_gdi_error ("cairo_win32_scaled_font_select_font:SetGraphicsMode"); + SelectObject (hdc, old_hfont); + return status; + } + + status = _win32_scaled_font_set_world_transform ((cairo_win32_scaled_font_t *)scaled_font, hdc); + if (status) { + SetGraphicsMode (hdc, old_mode); + SelectObject (hdc, old_hfont); + return status; + } + + SetMapMode (hdc, MM_TEXT); + + return CAIRO_STATUS_SUCCESS; +} + +/** + * cairo_win32_scaled_font_done_font: + * @scaled_font: A scaled font from the Win32 font backend. + * + * Releases any resources allocated by cairo_win32_scaled_font_select_font() + **/ +void +cairo_win32_scaled_font_done_font (cairo_scaled_font_t *scaled_font) +{ + if (! _cairo_scaled_font_is_win32 (scaled_font)) { + _cairo_error_throw (CAIRO_STATUS_FONT_TYPE_MISMATCH); + } +} + +/** + * cairo_win32_scaled_font_get_metrics_factor: + * @scaled_font: a scaled font from the Win32 font backend + * + * Gets a scale factor between logical coordinates in the coordinate + * space used by cairo_win32_scaled_font_select_font() (that is, the + * coordinate system used by the Windows functions to return metrics) and + * font space coordinates. + * + * Return value: factor to multiply logical units by to get font space + * coordinates. + **/ +double +cairo_win32_scaled_font_get_metrics_factor (cairo_scaled_font_t *scaled_font) +{ + if (! _cairo_scaled_font_is_win32 (scaled_font)) { + _cairo_error_throw (CAIRO_STATUS_FONT_TYPE_MISMATCH); + return 1.; + } + return 1. / ((cairo_win32_scaled_font_t *)scaled_font)->logical_scale; +} + +/** + * cairo_win32_scaled_font_get_logical_to_device: + * @scaled_font: a scaled font from the Win32 font backend + * @logical_to_device: matrix to return + * + * Gets the transformation mapping the logical space used by @scaled_font + * to device space. + * + * Since: 1.4 + **/ +void +cairo_win32_scaled_font_get_logical_to_device (cairo_scaled_font_t *scaled_font, + cairo_matrix_t *logical_to_device) +{ + cairo_win32_scaled_font_t *win_font = (cairo_win32_scaled_font_t *)scaled_font; + if (! _cairo_scaled_font_is_win32 (scaled_font)) { + _cairo_error_throw (CAIRO_STATUS_FONT_TYPE_MISMATCH); + cairo_matrix_init_identity (logical_to_device); + return; + } + *logical_to_device = win_font->logical_to_device; +} + +/** + * cairo_win32_scaled_font_get_device_to_logical: + * @scaled_font: a scaled font from the Win32 font backend + * @device_to_logical: matrix to return + * + * Gets the transformation mapping device space to the logical space + * used by @scaled_font. + * + * Since: 1.4 + **/ +void +cairo_win32_scaled_font_get_device_to_logical (cairo_scaled_font_t *scaled_font, + cairo_matrix_t *device_to_logical) +{ + cairo_win32_scaled_font_t *win_font = (cairo_win32_scaled_font_t *)scaled_font; + if (! _cairo_scaled_font_is_win32 (scaled_font)) { + _cairo_error_throw (CAIRO_STATUS_FONT_TYPE_MISMATCH); + cairo_matrix_init_identity (device_to_logical); + return; + } + *device_to_logical = win_font->device_to_logical; +} + +void +_cairo_win32_font_reset_static_data (void) +{ + _cairo_win32_font_face_hash_table_destroy (); +} diff --git a/src/win32/cairo-win32-printing-surface.c b/src/win32/cairo-win32-printing-surface.c new file mode 100644 index 00000000..17a68f78 --- /dev/null +++ b/src/win32/cairo-win32-printing-surface.c @@ -0,0 +1,1915 @@ +/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */ +/* Cairo - a vector graphics library with display and print output + * + * Copyright © 2007, 2008 Adrian Johnson + * + * 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 Adrian Johnson. + * + * Contributor(s): + * Adrian Johnson <ajohnson@redneon.com> + * Vladimir Vukicevic <vladimir@pobox.com> + */ + +#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-default-context-private.h" +#include "cairo-error-private.h" +#include "cairo-paginated-private.h" + +#include "cairo-clip-private.h" +#include "cairo-win32-private.h" +#include "cairo-recording-surface-private.h" +#include "cairo-scaled-font-subsets-private.h" +#include "cairo-image-info-private.h" +#include "cairo-image-surface-private.h" +#include "cairo-surface-backend-private.h" +#include "cairo-surface-clipper-private.h" + +#include <windows.h> + +#if !defined(POSTSCRIPT_IDENTIFY) +# define POSTSCRIPT_IDENTIFY 0x1015 +#endif + +#if !defined(PSIDENT_GDICENTRIC) +# define PSIDENT_GDICENTRIC 0x0000 +#endif + +#if !defined(GET_PS_FEATURESETTING) +# define GET_PS_FEATURESETTING 0x1019 +#endif + +#if !defined(FEATURESETTING_PSLEVEL) +# define FEATURESETTING_PSLEVEL 0x0002 +#endif + +#if !defined(GRADIENT_FILL_RECT_H) +# define GRADIENT_FILL_RECT_H 0x00 +#endif + +#if !defined(CHECKJPEGFORMAT) +# define CHECKJPEGFORMAT 0x1017 +#endif + +#if !defined(CHECKPNGFORMAT) +# define CHECKPNGFORMAT 0x1018 +#endif + +#define PELS_72DPI ((LONG)(72. / 0.0254)) + +static const char *_cairo_win32_printing_supported_mime_types[] = +{ + CAIRO_MIME_TYPE_JPEG, + CAIRO_MIME_TYPE_PNG, + NULL +}; + +static const cairo_surface_backend_t cairo_win32_printing_surface_backend; +static const cairo_paginated_surface_backend_t cairo_win32_surface_paginated_backend; + +static void +_cairo_win32_printing_surface_init_ps_mode (cairo_win32_surface_t *surface) +{ + DWORD word; + INT ps_feature, ps_level; + + word = PSIDENT_GDICENTRIC; + if (ExtEscape (surface->dc, POSTSCRIPT_IDENTIFY, sizeof(DWORD), (char *)&word, 0, (char *)NULL) <= 0) + return; + + ps_feature = FEATURESETTING_PSLEVEL; + if (ExtEscape (surface->dc, GET_PS_FEATURESETTING, sizeof(INT), + (char *)&ps_feature, sizeof(INT), (char *)&ps_level) <= 0) + return; + + if (ps_level >= 3) + surface->flags |= CAIRO_WIN32_SURFACE_CAN_RECT_GRADIENT; +} + +static void +_cairo_win32_printing_surface_init_image_support (cairo_win32_surface_t *surface) +{ + DWORD word; + + word = CHECKJPEGFORMAT; + if (ExtEscape(surface->dc, QUERYESCSUPPORT, sizeof(word), (char *)&word, 0, (char *)NULL) > 0) + surface->flags |= CAIRO_WIN32_SURFACE_CAN_CHECK_JPEG; + + word = CHECKPNGFORMAT; + if (ExtEscape(surface->dc, QUERYESCSUPPORT, sizeof(word), (char *)&word, 0, (char *)NULL) > 0) + surface->flags |= CAIRO_WIN32_SURFACE_CAN_CHECK_PNG; +} + +/* When creating an EMF file, ExtTextOut with ETO_GLYPH_INDEX does not + * work unless the GDI function GdiInitializeLanguagePack() has been + * called. + * + * http://m-a-tech.blogspot.com/2009/04/emf-buffer-idiocracy.html + * + * The only information I could find on the how to use this + * undocumented function is the use in: + * + * http://src.chromium.org/viewvc/chrome/trunk/src/chrome/renderer/render_process.cc?view=markup + * + * to solve the same problem. The above code first checks if LPK.DLL + * is already loaded. If it is not it calls + * GdiInitializeLanguagePack() using the prototype + * BOOL GdiInitializeLanguagePack (int) + * and argument 0. + */ +static void +_cairo_win32_printing_surface_init_language_pack (cairo_win32_surface_t *surface) +{ + typedef BOOL (WINAPI *gdi_init_lang_pack_func_t)(int); + gdi_init_lang_pack_func_t gdi_init_lang_pack; + HMODULE module; + + if (GetModuleHandleW (L"LPK.DLL")) + return; + + module = GetModuleHandleW (L"GDI32.DLL"); + if (module) { + gdi_init_lang_pack = (gdi_init_lang_pack_func_t) + GetProcAddress (module, "GdiInitializeLanguagePack"); + if (gdi_init_lang_pack) + gdi_init_lang_pack (0); + } +} + +static cairo_int_status_t +analyze_surface_pattern_transparency (cairo_surface_pattern_t *pattern) +{ + cairo_image_surface_t *image; + void *image_extra; + cairo_int_status_t status; + cairo_image_transparency_t transparency; + + status = _cairo_surface_acquire_source_image (pattern->surface, + &image, + &image_extra); + if (status) + return status; + + transparency = _cairo_image_analyze_transparency (image); + switch (transparency) { + case CAIRO_IMAGE_UNKNOWN: + ASSERT_NOT_REACHED; + case CAIRO_IMAGE_IS_OPAQUE: + status = CAIRO_STATUS_SUCCESS; + break; + + case CAIRO_IMAGE_HAS_BILEVEL_ALPHA: + case CAIRO_IMAGE_HAS_ALPHA: + status = CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY; + break; + } + + _cairo_surface_release_source_image (pattern->surface, image, image_extra); + + return status; +} + +static cairo_bool_t +surface_pattern_supported (const cairo_surface_pattern_t *pattern) +{ + if (_cairo_surface_is_recording (pattern->surface)) + return TRUE; + + if (cairo_surface_get_type (pattern->surface) != CAIRO_SURFACE_TYPE_WIN32 && + pattern->surface->backend->acquire_source_image == NULL) + { + return FALSE; + } + + return TRUE; +} + +static cairo_bool_t +pattern_supported (cairo_win32_surface_t *surface, const cairo_pattern_t *pattern) +{ + if (pattern->type == CAIRO_PATTERN_TYPE_SOLID) + return TRUE; + + if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) + return surface_pattern_supported ((const cairo_surface_pattern_t *) pattern); + + if (pattern->type == CAIRO_PATTERN_TYPE_LINEAR) + return surface->flags & CAIRO_WIN32_SURFACE_CAN_RECT_GRADIENT; + + return FALSE; +} + +static cairo_int_status_t +_cairo_win32_printing_surface_analyze_operation (cairo_win32_surface_t *surface, + cairo_operator_t op, + const cairo_pattern_t *pattern) +{ + if (! pattern_supported (surface, pattern)) + return CAIRO_INT_STATUS_UNSUPPORTED; + + if (!(op == CAIRO_OPERATOR_SOURCE || + op == CAIRO_OPERATOR_OVER || + op == CAIRO_OPERATOR_CLEAR)) + return CAIRO_INT_STATUS_UNSUPPORTED; + + if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) { + cairo_surface_pattern_t *surface_pattern = (cairo_surface_pattern_t *) pattern; + + if ( _cairo_surface_is_recording (surface_pattern->surface)) + return CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN; + } + + if (op == CAIRO_OPERATOR_SOURCE || + op == CAIRO_OPERATOR_CLEAR) + return CAIRO_STATUS_SUCCESS; + + /* CAIRO_OPERATOR_OVER is only supported for opaque patterns. If + * the pattern contains transparency, we return + * CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY to the analysis + * surface. If the analysis surface determines that there is + * anything drawn under this operation, a fallback image will be + * used. Otherwise the operation will be replayed during the + * render stage and we blend the transarency into the white + * background to convert the pattern to opaque. + */ + + if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) { + cairo_surface_pattern_t *surface_pattern = (cairo_surface_pattern_t *) pattern; + + return analyze_surface_pattern_transparency (surface_pattern); + } + + if (_cairo_pattern_is_opaque (pattern, NULL)) + return CAIRO_STATUS_SUCCESS; + else + return CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY; +} + +static cairo_bool_t +_cairo_win32_printing_surface_operation_supported (cairo_win32_surface_t *surface, + cairo_operator_t op, + const cairo_pattern_t *pattern) +{ + if (_cairo_win32_printing_surface_analyze_operation (surface, op, pattern) != CAIRO_INT_STATUS_UNSUPPORTED) + return TRUE; + else + return FALSE; +} + +static void +_cairo_win32_printing_surface_init_clear_color (cairo_win32_surface_t *surface, + cairo_solid_pattern_t *color) +{ + if (surface->content == CAIRO_CONTENT_COLOR_ALPHA) + _cairo_pattern_init_solid (color, CAIRO_COLOR_WHITE); + else + _cairo_pattern_init_solid (color, CAIRO_COLOR_BLACK); +} + +static COLORREF +_cairo_win32_printing_surface_flatten_transparency (cairo_win32_surface_t *surface, + const cairo_color_t *color) +{ + COLORREF c; + BYTE red, green, blue; + + red = color->red_short >> 8; + green = color->green_short >> 8; + blue = color->blue_short >> 8; + + if (!CAIRO_COLOR_IS_OPAQUE(color)) { + if (surface->content == CAIRO_CONTENT_COLOR_ALPHA) { + /* Blend into white */ + uint8_t one_minus_alpha = 255 - (color->alpha_short >> 8); + + red = (color->red_short >> 8) + one_minus_alpha; + green = (color->green_short >> 8) + one_minus_alpha; + blue = (color->blue_short >> 8) + one_minus_alpha; + } else { + /* Blend into black */ + red = (color->red_short >> 8); + green = (color->green_short >> 8); + blue = (color->blue_short >> 8); + } + } + c = RGB (red, green, blue); + + return c; +} + +static cairo_status_t +_cairo_win32_printing_surface_select_solid_brush (cairo_win32_surface_t *surface, + const cairo_pattern_t *source) +{ + cairo_solid_pattern_t *pattern = (cairo_solid_pattern_t *) source; + COLORREF color; + + color = _cairo_win32_printing_surface_flatten_transparency (surface, + &pattern->color); + surface->brush = CreateSolidBrush (color); + if (!surface->brush) + return _cairo_win32_print_gdi_error ("_cairo_win32_surface_select_solid_brush(CreateSolidBrush)"); + surface->old_brush = SelectObject (surface->dc, surface->brush); + + return CAIRO_STATUS_SUCCESS; +} + +static void +_cairo_win32_printing_surface_done_solid_brush (cairo_win32_surface_t *surface) +{ + if (surface->old_brush) { + SelectObject (surface->dc, surface->old_brush); + DeleteObject (surface->brush); + surface->old_brush = NULL; + } +} + +static cairo_status_t +_cairo_win32_printing_surface_get_ctm_clip_box (cairo_win32_surface_t *surface, + RECT *clip) +{ + XFORM xform; + + _cairo_matrix_to_win32_xform (&surface->ctm, &xform); + if (!ModifyWorldTransform (surface->dc, &xform, MWT_LEFTMULTIPLY)) + return _cairo_win32_print_gdi_error ("_cairo_win32_printing_surface_get_clip_box:ModifyWorldTransform"); + GetClipBox (surface->dc, clip); + + _cairo_matrix_to_win32_xform (&surface->gdi_ctm, &xform); + if (!SetWorldTransform (surface->dc, &xform)) + return _cairo_win32_print_gdi_error ("_cairo_win32_printing_surface_get_clip_box:SetWorldTransform"); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_win32_printing_surface_paint_solid_pattern (cairo_win32_surface_t *surface, + const cairo_pattern_t *pattern) +{ + RECT clip; + cairo_status_t status; + + GetClipBox (surface->dc, &clip); + status = _cairo_win32_printing_surface_select_solid_brush (surface, pattern); + if (status) + return status; + + FillRect (surface->dc, &clip, surface->brush); + _cairo_win32_printing_surface_done_solid_brush (surface); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_win32_printing_surface_paint_recording_pattern (cairo_win32_surface_t *surface, + cairo_surface_pattern_t *pattern) +{ + cairo_content_t old_content; + cairo_matrix_t old_ctm; + cairo_bool_t old_has_ctm; + cairo_rectangle_int_t recording_extents; + cairo_status_t status; + cairo_extend_t extend; + cairo_matrix_t p2d; + XFORM xform; + int x_tile, y_tile, left, right, top, bottom; + RECT clip; + cairo_recording_surface_t *recording_surface = (cairo_recording_surface_t *) pattern->surface; + cairo_box_t bbox; + + extend = cairo_pattern_get_extend (&pattern->base); + + p2d = pattern->base.matrix; + status = cairo_matrix_invert (&p2d); + /* _cairo_pattern_set_matrix guarantees invertibility */ + assert (status == CAIRO_STATUS_SUCCESS); + + old_ctm = surface->ctm; + old_has_ctm = surface->has_ctm; + cairo_matrix_multiply (&p2d, &p2d, &surface->ctm); + surface->ctm = p2d; + SaveDC (surface->dc); + _cairo_matrix_to_win32_xform (&p2d, &xform); + + status = _cairo_recording_surface_get_bbox (recording_surface, &bbox, NULL); + if (status) + return status; + + _cairo_box_round_to_rectangle (&bbox, &recording_extents); + + status = _cairo_win32_printing_surface_get_ctm_clip_box (surface, &clip); + if (status) + return status; + + if (extend == CAIRO_EXTEND_REPEAT || extend == CAIRO_EXTEND_REFLECT) { + left = floor (clip.left / _cairo_fixed_to_double (bbox.p2.x - bbox.p1.x)); + right = ceil (clip.right / _cairo_fixed_to_double (bbox.p2.x - bbox.p1.x)); + top = floor (clip.top / _cairo_fixed_to_double (bbox.p2.y - bbox.p1.y)); + bottom = ceil (clip.bottom / _cairo_fixed_to_double (bbox.p2.y - bbox.p1.y)); + } else { + left = 0; + right = 1; + top = 0; + bottom = 1; + } + + old_content = surface->content; + if (recording_surface->base.content == CAIRO_CONTENT_COLOR) { + surface->content = CAIRO_CONTENT_COLOR; + status = _cairo_win32_printing_surface_paint_solid_pattern (surface, + &_cairo_pattern_black.base); + if (status) + return status; + } + + for (y_tile = top; y_tile < bottom; y_tile++) { + for (x_tile = left; x_tile < right; x_tile++) { + cairo_matrix_t m; + double x, y; + + SaveDC (surface->dc); + m = p2d; + cairo_matrix_translate (&m, + x_tile*recording_extents.width, + y_tile*recording_extents.height); + if (extend == CAIRO_EXTEND_REFLECT) { + if (x_tile % 2) { + cairo_matrix_translate (&m, recording_extents.width, 0); + cairo_matrix_scale (&m, -1, 1); + } + if (y_tile % 2) { + cairo_matrix_translate (&m, 0, recording_extents.height); + cairo_matrix_scale (&m, 1, -1); + } + } + surface->ctm = m; + surface->has_ctm = !_cairo_matrix_is_identity (&surface->ctm); + + /* Set clip path around bbox of the pattern. */ + BeginPath (surface->dc); + + x = 0; + y = 0; + cairo_matrix_transform_point (&surface->ctm, &x, &y); + MoveToEx (surface->dc, (int) x, (int) y, NULL); + + x = recording_extents.width; + y = 0; + cairo_matrix_transform_point (&surface->ctm, &x, &y); + LineTo (surface->dc, (int) x, (int) y); + + x = recording_extents.width; + y = recording_extents.height; + cairo_matrix_transform_point (&surface->ctm, &x, &y); + LineTo (surface->dc, (int) x, (int) y); + + x = 0; + y = recording_extents.height; + cairo_matrix_transform_point (&surface->ctm, &x, &y); + LineTo (surface->dc, (int) x, (int) y); + + CloseFigure (surface->dc); + EndPath (surface->dc); + SelectClipPath (surface->dc, RGN_AND); + + SaveDC (surface->dc); /* Allow clip path to be reset during replay */ + status = _cairo_recording_surface_replay_region (&recording_surface->base, NULL, + &surface->base, + CAIRO_RECORDING_REGION_NATIVE); + assert (status != CAIRO_INT_STATUS_UNSUPPORTED); + /* Restore both the clip save and our earlier path SaveDC */ + RestoreDC (surface->dc, -2); + + if (status) + return status; + } + } + + surface->content = old_content; + surface->ctm = old_ctm; + surface->has_ctm = old_has_ctm; + RestoreDC (surface->dc, -1); + + return status; +} + +static cairo_int_status_t +_cairo_win32_printing_surface_check_jpeg (cairo_win32_surface_t *surface, + cairo_surface_t *source, + const unsigned char **data, + unsigned long *length, + cairo_image_info_t *info) +{ + const unsigned char *mime_data; + unsigned long mime_data_length; + cairo_int_status_t status; + DWORD result; + + if (!(surface->flags & CAIRO_WIN32_SURFACE_CAN_CHECK_JPEG)) + return CAIRO_INT_STATUS_UNSUPPORTED; + + cairo_surface_get_mime_data (source, CAIRO_MIME_TYPE_JPEG, + &mime_data, &mime_data_length); + if (mime_data == NULL) + return CAIRO_INT_STATUS_UNSUPPORTED; + + status = _cairo_image_info_get_jpeg_info (info, mime_data, mime_data_length); + if (status) + return status; + + result = 0; + if (ExtEscape(surface->dc, CHECKJPEGFORMAT, mime_data_length, (char *) mime_data, + sizeof(result), (char *) &result) <= 0) + return CAIRO_INT_STATUS_UNSUPPORTED; + + if (result != 1) + return CAIRO_INT_STATUS_UNSUPPORTED; + + *data = mime_data; + *length = mime_data_length; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +_cairo_win32_printing_surface_check_png (cairo_win32_surface_t *surface, + cairo_surface_t *source, + const unsigned char **data, + unsigned long *length, + cairo_image_info_t *info) +{ + const unsigned char *mime_data; + unsigned long mime_data_length; + + cairo_int_status_t status; + DWORD result; + + if (!(surface->flags & CAIRO_WIN32_SURFACE_CAN_CHECK_PNG)) + return CAIRO_INT_STATUS_UNSUPPORTED; + + cairo_surface_get_mime_data (source, CAIRO_MIME_TYPE_PNG, + &mime_data, &mime_data_length); + if (mime_data == NULL) + return CAIRO_INT_STATUS_UNSUPPORTED; + + status = _cairo_image_info_get_png_info (info, mime_data, mime_data_length); + if (status) + return status; + + result = 0; + if (ExtEscape(surface->dc, CHECKPNGFORMAT, mime_data_length, (char *) mime_data, + sizeof(result), (char *) &result) <= 0) + return CAIRO_INT_STATUS_UNSUPPORTED; + + if (result != 1) + return CAIRO_INT_STATUS_UNSUPPORTED; + + *data = mime_data; + *length = mime_data_length; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_win32_printing_surface_paint_image_pattern (cairo_win32_surface_t *surface, + cairo_surface_pattern_t *pattern) +{ + cairo_status_t status; + cairo_extend_t extend; + cairo_image_surface_t *image; + void *image_extra; + cairo_image_surface_t *opaque_image = NULL; + BITMAPINFO bi; + cairo_matrix_t m; + int oldmode; + XFORM xform; + int x_tile, y_tile, left, right, top, bottom; + RECT clip; + const cairo_color_t *background_color; + const unsigned char *mime_data; + unsigned long mime_size; + cairo_image_info_t mime_info; + cairo_bool_t use_mime; + DWORD mime_type; + + /* If we can't use StretchDIBits with this surface, we can't do anything + * here. + */ + if (!(surface->flags & CAIRO_WIN32_SURFACE_CAN_STRETCHDIB)) + return CAIRO_INT_STATUS_UNSUPPORTED; + + if (surface->content == CAIRO_CONTENT_COLOR_ALPHA) + background_color = CAIRO_COLOR_WHITE; + else + background_color = CAIRO_COLOR_BLACK; + + extend = cairo_pattern_get_extend (&pattern->base); + + status = _cairo_surface_acquire_source_image (pattern->surface, + &image, &image_extra); + if (status) + return status; + + if (image->base.status) { + status = image->base.status; + goto CLEANUP_IMAGE; + } + + if (image->width == 0 || image->height == 0) { + status = CAIRO_STATUS_SUCCESS; + goto CLEANUP_IMAGE; + } + + mime_type = BI_JPEG; + status = _cairo_win32_printing_surface_check_jpeg (surface, + pattern->surface, + &mime_data, + &mime_size, + &mime_info); + if (status == CAIRO_INT_STATUS_UNSUPPORTED) { + mime_type = BI_PNG; + status = _cairo_win32_printing_surface_check_png (surface, + pattern->surface, + &mime_data, + &mime_size, + &mime_info); + } + if (_cairo_status_is_error (status)) + return status; + + use_mime = (status == CAIRO_STATUS_SUCCESS); + + if (!use_mime && image->format != CAIRO_FORMAT_RGB24) { + cairo_surface_t *opaque_surface; + cairo_surface_pattern_t image_pattern; + cairo_solid_pattern_t background_pattern; + + opaque_surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, + image->width, + image->height); + if (opaque_surface->status) { + status = opaque_surface->status; + goto CLEANUP_OPAQUE_IMAGE; + } + + _cairo_pattern_init_solid (&background_pattern, + background_color); + status = _cairo_surface_paint (opaque_surface, + CAIRO_OPERATOR_SOURCE, + &background_pattern.base, + NULL); + if (status) + goto CLEANUP_OPAQUE_IMAGE; + + _cairo_pattern_init_for_surface (&image_pattern, &image->base); + status = _cairo_surface_paint (opaque_surface, + CAIRO_OPERATOR_OVER, + &image_pattern.base, + NULL); + _cairo_pattern_fini (&image_pattern.base); + if (status) + goto CLEANUP_OPAQUE_IMAGE; + + opaque_image = (cairo_image_surface_t *) opaque_surface; + } else { + opaque_image = image; + } + + bi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + bi.bmiHeader.biWidth = use_mime ? mime_info.width : opaque_image->width; + bi.bmiHeader.biHeight = use_mime ? - mime_info.height : -opaque_image->height; + bi.bmiHeader.biSizeImage = use_mime ? mime_size : 0; + bi.bmiHeader.biXPelsPerMeter = PELS_72DPI; + bi.bmiHeader.biYPelsPerMeter = PELS_72DPI; + bi.bmiHeader.biPlanes = 1; + bi.bmiHeader.biBitCount = 32; + bi.bmiHeader.biCompression = use_mime ? mime_type : BI_RGB; + bi.bmiHeader.biClrUsed = 0; + bi.bmiHeader.biClrImportant = 0; + + m = pattern->base.matrix; + status = cairo_matrix_invert (&m); + /* _cairo_pattern_set_matrix guarantees invertibility */ + assert (status == CAIRO_STATUS_SUCCESS); + + cairo_matrix_multiply (&m, &m, &surface->gdi_ctm); + SaveDC (surface->dc); + _cairo_matrix_to_win32_xform (&m, &xform); + + if (! SetWorldTransform (surface->dc, &xform)) { + status = _cairo_win32_print_gdi_error ("_cairo_win32_printing_surface_paint_image_pattern"); + goto CLEANUP_OPAQUE_IMAGE; + } + + oldmode = SetStretchBltMode(surface->dc, HALFTONE); + + GetClipBox (surface->dc, &clip); + if (extend == CAIRO_EXTEND_REPEAT || extend == CAIRO_EXTEND_REFLECT) { + left = floor ( clip.left / (double) opaque_image->width); + right = ceil (clip.right / (double) opaque_image->width); + top = floor (clip.top / (double) opaque_image->height); + bottom = ceil (clip.bottom / (double) opaque_image->height); + } else { + left = 0; + right = 1; + top = 0; + bottom = 1; + } + + for (y_tile = top; y_tile < bottom; y_tile++) { + for (x_tile = left; x_tile < right; x_tile++) { + if (!StretchDIBits (surface->dc, + x_tile*opaque_image->width, + y_tile*opaque_image->height, + opaque_image->width, + opaque_image->height, + 0, + 0, + use_mime ? mime_info.width : opaque_image->width, + use_mime ? mime_info.height : opaque_image->height, + use_mime ? mime_data : opaque_image->data, + &bi, + DIB_RGB_COLORS, + SRCCOPY)) + { + status = _cairo_win32_print_gdi_error ("_cairo_win32_printing_surface_paint(StretchDIBits)"); + goto CLEANUP_OPAQUE_IMAGE; + } + } + } + SetStretchBltMode(surface->dc, oldmode); + RestoreDC (surface->dc, -1); + +CLEANUP_OPAQUE_IMAGE: + if (opaque_image != image) + cairo_surface_destroy (&opaque_image->base); +CLEANUP_IMAGE: + _cairo_surface_release_source_image (pattern->surface, image, image_extra); + + return status; +} + +static cairo_status_t +_cairo_win32_printing_surface_paint_surface_pattern (cairo_win32_surface_t *surface, + cairo_surface_pattern_t *pattern) +{ + if (_cairo_surface_is_recording (pattern->surface)) { + return _cairo_win32_printing_surface_paint_recording_pattern (surface, + pattern); + } else { + return _cairo_win32_printing_surface_paint_image_pattern (surface, + pattern); + } +} + +static void +vertex_set_color (TRIVERTEX *vert, cairo_color_stop_t *color) +{ + /* MSDN says that the range here is 0x0000 .. 0xff00; + * that may well be a typo, but just chop the low bits + * here. */ + vert->Alpha = 0xff00; + vert->Red = color->red_short & 0xff00; + vert->Green = color->green_short & 0xff00; + vert->Blue = color->blue_short & 0xff00; +} + +static cairo_int_status_t +_cairo_win32_printing_surface_paint_linear_pattern (cairo_win32_surface_t *surface, + cairo_linear_pattern_t *pattern) +{ + TRIVERTEX *vert; + GRADIENT_RECT *rect; + RECT clip; + XFORM xform; + int i, num_stops; + cairo_matrix_t mat, rot; + double p1x, p1y, p2x, p2y, xd, yd, d, sn, cs; + cairo_extend_t extend; + int range_start, range_stop, num_ranges, num_rects, stop; + int total_verts, total_rects; + cairo_status_t status; + + extend = cairo_pattern_get_extend (&pattern->base.base); + SaveDC (surface->dc); + + mat = pattern->base.base.matrix; + status = cairo_matrix_invert (&mat); + /* _cairo_pattern_set_matrix guarantees invertibility */ + assert (status == CAIRO_STATUS_SUCCESS); + + cairo_matrix_multiply (&mat, &surface->ctm, &mat); + + p1x = pattern->pd1.x; + p1y = pattern->pd1.y; + p2x = pattern->pd2.x; + p2y = pattern->pd2.y; + cairo_matrix_translate (&mat, p1x, p1y); + + xd = p2x - p1x; + yd = p2y - p1y; + d = sqrt (xd*xd + yd*yd); + sn = yd/d; + cs = xd/d; + cairo_matrix_init (&rot, + cs, sn, + -sn, cs, + 0, 0); + cairo_matrix_multiply (&mat, &rot, &mat); + + _cairo_matrix_to_win32_xform (&mat, &xform); + + if (!SetWorldTransform (surface->dc, &xform)) + return _cairo_win32_print_gdi_error ("_win32_printing_surface_paint_linear_pattern:SetWorldTransform2"); + + GetClipBox (surface->dc, &clip); + + if (extend == CAIRO_EXTEND_REPEAT || extend == CAIRO_EXTEND_REFLECT) { + range_start = floor (clip.left / d); + range_stop = ceil (clip.right / d); + } else { + range_start = 0; + range_stop = 1; + } + num_ranges = range_stop - range_start; + num_stops = pattern->base.n_stops; + num_rects = num_stops - 1; + + /* Add an extra four points and two rectangles for EXTEND_PAD */ + vert = malloc (sizeof (TRIVERTEX) * (num_rects*2*num_ranges + 4)); + rect = malloc (sizeof (GRADIENT_RECT) * (num_rects*num_ranges + 2)); + + for (i = 0; i < num_ranges*num_rects; i++) { + vert[i*2].y = (LONG) clip.top; + if (i%num_rects == 0) { + stop = 0; + if (extend == CAIRO_EXTEND_REFLECT && (range_start+(i/num_rects))%2) + stop = num_rects; + vert[i*2].x = (LONG)(d*(range_start + i/num_rects)); + vertex_set_color (&vert[i*2], &pattern->base.stops[stop].color); + } else { + vert[i*2].x = vert[i*2-1].x; + vert[i*2].Red = vert[i*2-1].Red; + vert[i*2].Green = vert[i*2-1].Green; + vert[i*2].Blue = vert[i*2-1].Blue; + vert[i*2].Alpha = vert[i*2-1].Alpha; + } + + stop = i%num_rects + 1; + vert[i*2+1].x = (LONG)(d*(range_start + i/num_rects + pattern->base.stops[stop].offset)); + vert[i*2+1].y = (LONG) clip.bottom; + if (extend == CAIRO_EXTEND_REFLECT && (range_start+(i/num_rects))%2) + stop = num_rects - stop; + vertex_set_color (&vert[i*2+1], &pattern->base.stops[stop].color); + + rect[i].UpperLeft = i*2; + rect[i].LowerRight = i*2 + 1; + } + total_verts = 2*num_ranges*num_rects; + total_rects = num_ranges*num_rects; + + if (extend == CAIRO_EXTEND_PAD) { + vert[i*2].x = vert[i*2-1].x; + vert[i*2].y = (LONG) clip.top; + vert[i*2].Red = vert[i*2-1].Red; + vert[i*2].Green = vert[i*2-1].Green; + vert[i*2].Blue = vert[i*2-1].Blue; + vert[i*2].Alpha = 0xff00; + vert[i*2+1].x = clip.right; + vert[i*2+1].y = (LONG) clip.bottom; + vert[i*2+1].Red = vert[i*2-1].Red; + vert[i*2+1].Green = vert[i*2-1].Green; + vert[i*2+1].Blue = vert[i*2-1].Blue; + vert[i*2+1].Alpha = 0xff00; + rect[i].UpperLeft = i*2; + rect[i].LowerRight = i*2 + 1; + + i++; + + vert[i*2].x = clip.left; + vert[i*2].y = (LONG) clip.top; + vert[i*2].Red = vert[0].Red; + vert[i*2].Green = vert[0].Green; + vert[i*2].Blue = vert[0].Blue; + vert[i*2].Alpha = 0xff00; + vert[i*2+1].x = vert[0].x; + vert[i*2+1].y = (LONG) clip.bottom; + vert[i*2+1].Red = vert[0].Red; + vert[i*2+1].Green = vert[0].Green; + vert[i*2+1].Blue = vert[0].Blue; + vert[i*2+1].Alpha = 0xff00; + rect[i].UpperLeft = i*2; + rect[i].LowerRight = i*2 + 1; + + total_verts += 4; + total_rects += 2; + } + + if (!GradientFill (surface->dc, + vert, total_verts, + rect, total_rects, + GRADIENT_FILL_RECT_H)) + return _cairo_win32_print_gdi_error ("_win32_printing_surface_paint_linear_pattern:GradientFill"); + + free (rect); + free (vert); + RestoreDC (surface->dc, -1); + + return 0; +} + +static cairo_int_status_t +_cairo_win32_printing_surface_paint_pattern (cairo_win32_surface_t *surface, + const cairo_pattern_t *pattern) +{ + cairo_status_t status; + + switch (pattern->type) { + case CAIRO_PATTERN_TYPE_SOLID: + status = _cairo_win32_printing_surface_paint_solid_pattern (surface, pattern); + if (status) + return status; + break; + + case CAIRO_PATTERN_TYPE_SURFACE: + status = _cairo_win32_printing_surface_paint_surface_pattern (surface, + (cairo_surface_pattern_t *) pattern); + if (status) + return status; + break; + + case CAIRO_PATTERN_TYPE_LINEAR: + status = _cairo_win32_printing_surface_paint_linear_pattern (surface, (cairo_linear_pattern_t *) pattern); + if (status) + return status; + break; + + case CAIRO_PATTERN_TYPE_RADIAL: + return CAIRO_INT_STATUS_UNSUPPORTED; + break; + + case CAIRO_PATTERN_TYPE_MESH: + ASSERT_NOT_REACHED; + } + + return CAIRO_STATUS_SUCCESS; +} + +typedef struct _win32_print_path_info { + cairo_win32_surface_t *surface; +} win32_path_info_t; + +static cairo_status_t +_cairo_win32_printing_surface_path_move_to (void *closure, + const cairo_point_t *point) +{ + win32_path_info_t *path_info = closure; + + if (path_info->surface->has_ctm) { + double x, y; + + x = _cairo_fixed_to_double (point->x); + y = _cairo_fixed_to_double (point->y); + cairo_matrix_transform_point (&path_info->surface->ctm, &x, &y); + MoveToEx (path_info->surface->dc, (int) x, (int) y, NULL); + } else { + MoveToEx (path_info->surface->dc, + _cairo_fixed_integer_part (point->x), + _cairo_fixed_integer_part (point->y), + NULL); + } + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_win32_printing_surface_path_line_to (void *closure, + const cairo_point_t *point) +{ + win32_path_info_t *path_info = closure; + + path_info->surface->path_empty = FALSE; + if (path_info->surface->has_ctm) { + double x, y; + + x = _cairo_fixed_to_double (point->x); + y = _cairo_fixed_to_double (point->y); + cairo_matrix_transform_point (&path_info->surface->ctm, &x, &y); + LineTo (path_info->surface->dc, (int) x, (int) y); + } else { + LineTo (path_info->surface->dc, + _cairo_fixed_integer_part (point->x), + _cairo_fixed_integer_part (point->y)); + } + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_win32_printing_surface_path_curve_to (void *closure, + const cairo_point_t *b, + const cairo_point_t *c, + const cairo_point_t *d) +{ + win32_path_info_t *path_info = closure; + POINT points[3]; + + path_info->surface->path_empty = FALSE; + if (path_info->surface->has_ctm) { + double x, y; + + x = _cairo_fixed_to_double (b->x); + y = _cairo_fixed_to_double (b->y); + cairo_matrix_transform_point (&path_info->surface->ctm, &x, &y); + points[0].x = (LONG) x; + points[0].y = (LONG) y; + + x = _cairo_fixed_to_double (c->x); + y = _cairo_fixed_to_double (c->y); + cairo_matrix_transform_point (&path_info->surface->ctm, &x, &y); + points[1].x = (LONG) x; + points[1].y = (LONG) y; + + x = _cairo_fixed_to_double (d->x); + y = _cairo_fixed_to_double (d->y); + cairo_matrix_transform_point (&path_info->surface->ctm, &x, &y); + points[2].x = (LONG) x; + points[2].y = (LONG) y; + } else { + points[0].x = _cairo_fixed_integer_part (b->x); + points[0].y = _cairo_fixed_integer_part (b->y); + points[1].x = _cairo_fixed_integer_part (c->x); + points[1].y = _cairo_fixed_integer_part (c->y); + points[2].x = _cairo_fixed_integer_part (d->x); + points[2].y = _cairo_fixed_integer_part (d->y); + } + PolyBezierTo (path_info->surface->dc, points, 3); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_win32_printing_surface_path_close_path (void *closure) +{ + win32_path_info_t *path_info = closure; + + CloseFigure (path_info->surface->dc); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_win32_printing_surface_emit_path (cairo_win32_surface_t *surface, + const cairo_path_fixed_t *path) +{ + win32_path_info_t path_info; + + path_info.surface = surface; + return _cairo_path_fixed_interpret (path, + _cairo_win32_printing_surface_path_move_to, + _cairo_win32_printing_surface_path_line_to, + _cairo_win32_printing_surface_path_curve_to, + _cairo_win32_printing_surface_path_close_path, + &path_info); +} + +static cairo_int_status_t +_cairo_win32_printing_surface_show_page (void *abstract_surface) +{ + cairo_win32_surface_t *surface = abstract_surface; + + /* Undo both SaveDC's that we did in start_page */ + RestoreDC (surface->dc, -2); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_win32_printing_surface_clipper_intersect_clip_path (cairo_surface_clipper_t *clipper, + cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance, + cairo_antialias_t antialias) +{ + cairo_win32_surface_t *surface = cairo_container_of (clipper, + cairo_win32_surface_t, + clipper); + cairo_status_t status; + + if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) + return CAIRO_STATUS_SUCCESS; + + if (path == NULL) { + RestoreDC (surface->dc, -1); + SaveDC (surface->dc); + + return CAIRO_STATUS_SUCCESS; + } + + BeginPath (surface->dc); + status = _cairo_win32_printing_surface_emit_path (surface, path); + EndPath (surface->dc); + + switch (fill_rule) { + case CAIRO_FILL_RULE_WINDING: + SetPolyFillMode (surface->dc, WINDING); + break; + case CAIRO_FILL_RULE_EVEN_ODD: + SetPolyFillMode (surface->dc, ALTERNATE); + break; + default: + ASSERT_NOT_REACHED; + } + + SelectClipPath (surface->dc, RGN_AND); + + return status; +} + +static void +_cairo_win32_printing_surface_get_font_options (void *abstract_surface, + cairo_font_options_t *options) +{ + _cairo_font_options_init_default (options); + + cairo_font_options_set_hint_style (options, CAIRO_HINT_STYLE_NONE); + cairo_font_options_set_hint_metrics (options, CAIRO_HINT_METRICS_OFF); + cairo_font_options_set_antialias (options, CAIRO_ANTIALIAS_GRAY); + _cairo_font_options_set_round_glyph_positions (options, CAIRO_ROUND_GLYPH_POS_ON); +} + +static cairo_int_status_t +_cairo_win32_printing_surface_paint (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_clip_t *clip) +{ + cairo_win32_surface_t *surface = abstract_surface; + cairo_solid_pattern_t clear; + cairo_status_t status; + + status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); + if (status) + return status; + + if (op == CAIRO_OPERATOR_CLEAR) { + _cairo_win32_printing_surface_init_clear_color (surface, &clear); + source = (cairo_pattern_t*) &clear; + op = CAIRO_OPERATOR_SOURCE; + } + + if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) + return _cairo_win32_printing_surface_analyze_operation (surface, op, source); + + assert (_cairo_win32_printing_surface_operation_supported (surface, op, source)); + + return _cairo_win32_printing_surface_paint_pattern (surface, source); +} + +static int +_cairo_win32_line_cap (cairo_line_cap_t cap) +{ + switch (cap) { + case CAIRO_LINE_CAP_BUTT: + return PS_ENDCAP_FLAT; + case CAIRO_LINE_CAP_ROUND: + return PS_ENDCAP_ROUND; + case CAIRO_LINE_CAP_SQUARE: + return PS_ENDCAP_SQUARE; + default: + ASSERT_NOT_REACHED; + return 0; + } +} + +static int +_cairo_win32_line_join (cairo_line_join_t join) +{ + switch (join) { + case CAIRO_LINE_JOIN_MITER: + return PS_JOIN_MITER; + case CAIRO_LINE_JOIN_ROUND: + return PS_JOIN_ROUND; + case CAIRO_LINE_JOIN_BEVEL: + return PS_JOIN_BEVEL; + default: + ASSERT_NOT_REACHED; + return 0; + } +} + +static void +_cairo_matrix_factor_out_scale (cairo_matrix_t *m, double *scale) +{ + double s; + + s = fabs (m->xx); + if (fabs (m->xy) > s) + s = fabs (m->xy); + if (fabs (m->yx) > s) + s = fabs (m->yx); + if (fabs (m->yy) > s) + s = fabs (m->yy); + *scale = s; + s = 1.0/s; + cairo_matrix_scale (m, s, s); +} + +static cairo_int_status_t +_cairo_win32_printing_surface_stroke (void *abstract_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 *stroke_ctm, + const cairo_matrix_t *stroke_ctm_inverse, + double tolerance, + cairo_antialias_t antialias, + const cairo_clip_t *clip) +{ + cairo_win32_surface_t *surface = abstract_surface; + cairo_int_status_t status; + HPEN pen; + LOGBRUSH brush; + COLORREF color; + XFORM xform; + DWORD pen_style; + DWORD *dash_array; + HGDIOBJ obj; + unsigned int i; + cairo_solid_pattern_t clear; + cairo_matrix_t mat; + double scale; + + status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); + if (status) + return status; + + if (op == CAIRO_OPERATOR_CLEAR) { + _cairo_win32_printing_surface_init_clear_color (surface, &clear); + source = (cairo_pattern_t*) &clear; + op = CAIRO_OPERATOR_SOURCE; + } + + if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) { + /* Win32 does not support a dash offset. */ + if (style->num_dashes > 0 && style->dash_offset != 0.0) + return CAIRO_INT_STATUS_UNSUPPORTED; + + return _cairo_win32_printing_surface_analyze_operation (surface, op, source); + } + + assert (_cairo_win32_printing_surface_operation_supported (surface, op, source)); + assert (!(style->num_dashes > 0 && style->dash_offset != 0.0)); + + cairo_matrix_multiply (&mat, stroke_ctm, &surface->ctm); + _cairo_matrix_factor_out_scale (&mat, &scale); + + pen_style = PS_GEOMETRIC; + dash_array = NULL; + if (style->num_dashes) { + pen_style |= PS_USERSTYLE; + dash_array = calloc (sizeof (DWORD), style->num_dashes); + for (i = 0; i < style->num_dashes; i++) { + dash_array[i] = (DWORD) (scale * style->dash[i]); + } + } else { + pen_style |= PS_SOLID; + } + + SetMiterLimit (surface->dc, (FLOAT) (style->miter_limit), NULL); + if (source->type == CAIRO_PATTERN_TYPE_SOLID) { + cairo_solid_pattern_t *solid = (cairo_solid_pattern_t *) source; + + + color = _cairo_win32_printing_surface_flatten_transparency (surface, + &solid->color); + } else { + /* Color not used as the pen will only be used by WidenPath() */ + color = RGB (0,0,0); + } + brush.lbStyle = BS_SOLID; + brush.lbColor = color; + brush.lbHatch = 0; + pen_style |= _cairo_win32_line_cap (style->line_cap); + pen_style |= _cairo_win32_line_join (style->line_join); + pen = ExtCreatePen(pen_style, + scale * style->line_width, + &brush, + style->num_dashes, + dash_array); + if (pen == NULL) + return _cairo_win32_print_gdi_error ("_win32_surface_stroke:ExtCreatePen"); + obj = SelectObject (surface->dc, pen); + if (obj == NULL) + return _cairo_win32_print_gdi_error ("_win32_surface_stroke:SelectObject"); + + BeginPath (surface->dc); + status = _cairo_win32_printing_surface_emit_path (surface, path); + EndPath (surface->dc); + if (status) + return status; + + /* + * Switch to user space to set line parameters + */ + SaveDC (surface->dc); + + _cairo_matrix_to_win32_xform (&mat, &xform); + xform.eDx = 0.0f; + xform.eDy = 0.0f; + + if (!ModifyWorldTransform (surface->dc, &xform, MWT_LEFTMULTIPLY)) + return _cairo_win32_print_gdi_error ("_win32_surface_stroke:SetWorldTransform"); + + if (source->type == CAIRO_PATTERN_TYPE_SOLID) { + StrokePath (surface->dc); + } else { + if (!WidenPath (surface->dc)) + return _cairo_win32_print_gdi_error ("_win32_surface_stroke:WidenPath"); + if (!SelectClipPath (surface->dc, RGN_AND)) + return _cairo_win32_print_gdi_error ("_win32_surface_stroke:SelectClipPath"); + + /* Return to device space to paint the pattern */ + _cairo_matrix_to_win32_xform (&surface->gdi_ctm, &xform); + if (!SetWorldTransform (surface->dc, &xform)) + return _cairo_win32_print_gdi_error ("_win32_surface_stroke:ModifyWorldTransform"); + status = _cairo_win32_printing_surface_paint_pattern (surface, source); + } + RestoreDC (surface->dc, -1); + DeleteObject (pen); + free (dash_array); + + return status; +} + +static cairo_int_status_t +_cairo_win32_printing_surface_fill (void *abstract_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_surface_t *surface = abstract_surface; + cairo_int_status_t status; + cairo_solid_pattern_t clear; + + status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); + if (status) + return status; + + if (op == CAIRO_OPERATOR_CLEAR) { + _cairo_win32_printing_surface_init_clear_color (surface, &clear); + source = (cairo_pattern_t*) &clear; + op = CAIRO_OPERATOR_SOURCE; + } + + if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) + return _cairo_win32_printing_surface_analyze_operation (surface, op, source); + + assert (_cairo_win32_printing_surface_operation_supported (surface, op, source)); + + surface->path_empty = TRUE; + BeginPath (surface->dc); + status = _cairo_win32_printing_surface_emit_path (surface, path); + EndPath (surface->dc); + + switch (fill_rule) { + case CAIRO_FILL_RULE_WINDING: + SetPolyFillMode (surface->dc, WINDING); + break; + case CAIRO_FILL_RULE_EVEN_ODD: + SetPolyFillMode (surface->dc, ALTERNATE); + break; + default: + ASSERT_NOT_REACHED; + } + + if (source->type == CAIRO_PATTERN_TYPE_SOLID) { + status = _cairo_win32_printing_surface_select_solid_brush (surface, source); + if (status) + return status; + + FillPath (surface->dc); + _cairo_win32_printing_surface_done_solid_brush (surface); + } else if (surface->path_empty == FALSE) { + SaveDC (surface->dc); + SelectClipPath (surface->dc, RGN_AND); + status = _cairo_win32_printing_surface_paint_pattern (surface, source); + RestoreDC (surface->dc, -1); + } + + fflush(stderr); + + return status; +} + +static cairo_int_status_t +_cairo_win32_printing_surface_emit_win32_glyphs (cairo_win32_surface_t *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_matrix_t ctm; + cairo_glyph_t *unicode_glyphs; + cairo_scaled_font_subsets_glyph_t subset_glyph; + int i, first; + cairo_bool_t sequence_is_unicode; + cairo_status_t status = CAIRO_STATUS_SUCCESS; + + /* Where possible reverse the glyph indices back to unicode + * characters. Strings of glyphs that could not be reversed to + * unicode will be printed with ETO_GLYPH_INDEX. + * + * As _cairo_win32_scaled_font_index_to_ucs4() is a slow + * operation, the font subsetting function + * _cairo_scaled_font_subsets_map_glyph() is used to obtain + * the unicode value because it caches the reverse mapping in + * the subsets. + */ + + if (surface->has_ctm) { + for (i = 0; i < num_glyphs; i++) + cairo_matrix_transform_point (&surface->ctm, &glyphs[i].x, &glyphs[i].y); + cairo_matrix_multiply (&ctm, &scaled_font->ctm, &surface->ctm); + scaled_font = cairo_scaled_font_create (scaled_font->font_face, + &scaled_font->font_matrix, + &ctm, + &scaled_font->options); + } + + unicode_glyphs = _cairo_malloc_ab (num_glyphs, sizeof (cairo_glyph_t)); + if (unicode_glyphs == NULL) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + memcpy (unicode_glyphs, glyphs, num_glyphs * sizeof (cairo_glyph_t)); + for (i = 0; i < num_glyphs; i++) { + status = _cairo_scaled_font_subsets_map_glyph (surface->font_subsets, + scaled_font, + glyphs[i].index, + NULL, 0, + &subset_glyph); + if (status) + goto fail; + + unicode_glyphs[i].index = subset_glyph.unicode; + } + + i = 0; + first = 0; + sequence_is_unicode = unicode_glyphs[0].index <= 0xffff; + while (i < num_glyphs) { + if (i == num_glyphs - 1 || + ((unicode_glyphs[i + 1].index < 0xffff) != sequence_is_unicode)) + { + status = _cairo_win32_surface_show_glyphs_internal ( + surface, + op, + source, + sequence_is_unicode ? &unicode_glyphs[first] : &glyphs[first], + i - first + 1, + scaled_font, + clip, + ! sequence_is_unicode); + first = i + 1; + if (i < num_glyphs - 1) + sequence_is_unicode = unicode_glyphs[i + 1].index <= 0xffff; + } + i++; + } + +fail: + if (surface->has_ctm) + cairo_scaled_font_destroy (scaled_font); + + free (unicode_glyphs); + + return status; +} + +static cairo_int_status_t +_cairo_win32_printing_surface_show_glyphs (void *abstract_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_surface_t *surface = abstract_surface; + cairo_status_t status = CAIRO_STATUS_SUCCESS; + cairo_scaled_glyph_t *scaled_glyph; + cairo_pattern_t *opaque = NULL; + int i; + cairo_matrix_t old_ctm; + cairo_bool_t old_has_ctm; + cairo_solid_pattern_t clear; + + status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); + if (status) + return status; + + if (op == CAIRO_OPERATOR_CLEAR) { + _cairo_win32_printing_surface_init_clear_color (surface, &clear); + source = (cairo_pattern_t*) &clear; + op = CAIRO_OPERATOR_SOURCE; + } + + if (surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE) { + /* When printing bitmap fonts to a printer DC, Windows may + * substitute an outline font for bitmap font. As the win32 + * font backend always uses a screen DC when obtaining the + * font metrics the metrics of the substituted font will not + * match the metrics that the win32 font backend returns. + * + * If we are printing a bitmap font, use fallback images to + * ensure the font is not substituted. + */ +#if CAIRO_HAS_WIN32_FONT + if (cairo_scaled_font_get_type (scaled_font) == CAIRO_FONT_TYPE_WIN32) { + if (_cairo_win32_scaled_font_is_bitmap (scaled_font)) + return CAIRO_INT_STATUS_UNSUPPORTED; + else + return _cairo_win32_printing_surface_analyze_operation (surface, op, source); + } +#endif + + /* For non win32 fonts we need to check that each glyph has a + * path available. If a path is not available, + * _cairo_scaled_glyph_lookup() will return + * CAIRO_INT_STATUS_UNSUPPORTED and a fallback image will be + * used. + */ + for (i = 0; i < num_glyphs; i++) { + status = _cairo_scaled_glyph_lookup (scaled_font, + glyphs[i].index, + CAIRO_SCALED_GLYPH_INFO_PATH, + &scaled_glyph); + if (status) + return status; + } + + return _cairo_win32_printing_surface_analyze_operation (surface, op, source); + } + + if (source->type == CAIRO_PATTERN_TYPE_SOLID) { + cairo_solid_pattern_t *solid = (cairo_solid_pattern_t *) source; + COLORREF color; + + color = _cairo_win32_printing_surface_flatten_transparency (surface, + &solid->color); + opaque = cairo_pattern_create_rgb (GetRValue (color) / 255.0, + GetGValue (color) / 255.0, + GetBValue (color) / 255.0); + if (opaque->status) + return opaque->status; + source = opaque; + } + +#if CAIRO_HAS_WIN32_FONT + if (cairo_scaled_font_get_type (scaled_font) == CAIRO_FONT_TYPE_WIN32 && + source->type == CAIRO_PATTERN_TYPE_SOLID) + { + return _cairo_win32_printing_surface_emit_win32_glyphs (surface, + op, + source, + glyphs, + num_glyphs, + scaled_font, + clip); + } +#endif + + SaveDC (surface->dc); + old_ctm = surface->ctm; + old_has_ctm = surface->has_ctm; + surface->has_ctm = TRUE; + surface->path_empty = TRUE; + BeginPath (surface->dc); + for (i = 0; i < num_glyphs; i++) { + status = _cairo_scaled_glyph_lookup (scaled_font, + glyphs[i].index, + CAIRO_SCALED_GLYPH_INFO_PATH, + &scaled_glyph); + if (status) + break; + surface->ctm = old_ctm; + cairo_matrix_translate (&surface->ctm, glyphs[i].x, glyphs[i].y); + status = _cairo_win32_printing_surface_emit_path (surface, scaled_glyph->path); + } + EndPath (surface->dc); + surface->ctm = old_ctm; + surface->has_ctm = old_has_ctm; + if (status == CAIRO_STATUS_SUCCESS && surface->path_empty == FALSE) { + if (source->type == CAIRO_PATTERN_TYPE_SOLID) { + status = _cairo_win32_printing_surface_select_solid_brush (surface, source); + if (status) + return status; + + SetPolyFillMode (surface->dc, WINDING); + FillPath (surface->dc); + _cairo_win32_printing_surface_done_solid_brush (surface); + } else { + SelectClipPath (surface->dc, RGN_AND); + status = _cairo_win32_printing_surface_paint_pattern (surface, source); + } + } + RestoreDC (surface->dc, -1); + + if (opaque) + cairo_pattern_destroy (opaque); + + return status; +} + +static const char ** +_cairo_win32_printing_surface_get_supported_mime_types (void *abstract_surface) +{ + return _cairo_win32_printing_supported_mime_types; +} + +static cairo_surface_t * +_cairo_win32_printing_surface_create_similar (void *abstract_surface, + cairo_content_t content, + int width, + int height) +{ + cairo_rectangle_t extents; + + extents.x = extents.y = 0; + extents.width = width; + extents.height = height; + return cairo_recording_surface_create (content, &extents); +} + +static cairo_int_status_t +_cairo_win32_printing_surface_start_page (void *abstract_surface) +{ + cairo_win32_surface_t *surface = abstract_surface; + XFORM xform; + double x_res, y_res; + cairo_matrix_t inverse_ctm; + cairo_status_t status; + + SaveDC (surface->dc); /* Save application context first, before doing MWT */ + + /* As the logical coordinates used by GDI functions (eg LineTo) + * are integers we need to do some additional work to prevent + * rounding errors. For example the obvious way to paint a recording + * pattern is to: + * + * SaveDC() + * transform the device context DC by the pattern to device matrix + * replay the recording surface + * RestoreDC() + * + * The problem here is that if the pattern to device matrix is + * [100 0 0 100 0 0], coordinates in the recording pattern such as + * (1.56, 2.23) which correspond to (156, 223) in device space + * will be rounded to (100, 200) due to (1.56, 2.23) being + * truncated to integers. + * + * This is solved by saving the current GDI CTM in surface->ctm, + * switch the GDI CTM to identity, and transforming all + * coordinates by surface->ctm before passing them to GDI. When + * painting a recording pattern, surface->ctm is transformed by the + * pattern to device matrix. + * + * For printing device contexts where 1 unit is 1 dpi, switching + * the GDI CTM to identity maximises the possible resolution of + * coordinates. + * + * If the device context is an EMF file, using an identity + * transform often provides insufficent resolution. The workaround + * is to set the GDI CTM to a scale < 1 eg [1.0/16 0 0 1/0/16 0 0] + * and scale the cairo CTM by [16 0 0 16 0 0]. The + * SetWorldTransform function call to scale the GDI CTM by 1.0/16 + * will be recorded in the EMF followed by all the graphics + * functions by their coordinateds multiplied by 16. + * + * To support allowing the user to set a GDI CTM with scale < 1, + * we avoid switching to an identity CTM if the CTM xx and yy is < 1. + */ + SetGraphicsMode (surface->dc, GM_ADVANCED); + GetWorldTransform(surface->dc, &xform); + if (xform.eM11 < 1 && xform.eM22 < 1) { + cairo_matrix_init_identity (&surface->ctm); + surface->gdi_ctm.xx = xform.eM11; + surface->gdi_ctm.xy = xform.eM21; + surface->gdi_ctm.yx = xform.eM12; + surface->gdi_ctm.yy = xform.eM22; + surface->gdi_ctm.x0 = xform.eDx; + surface->gdi_ctm.y0 = xform.eDy; + } else { + surface->ctm.xx = xform.eM11; + surface->ctm.xy = xform.eM21; + surface->ctm.yx = xform.eM12; + surface->ctm.yy = xform.eM22; + surface->ctm.x0 = xform.eDx; + surface->ctm.y0 = xform.eDy; + cairo_matrix_init_identity (&surface->gdi_ctm); + if (!ModifyWorldTransform (surface->dc, NULL, MWT_IDENTITY)) + return _cairo_win32_print_gdi_error ("_cairo_win32_printing_surface_start_page:ModifyWorldTransform"); + } + + surface->has_ctm = !_cairo_matrix_is_identity (&surface->ctm); + surface->has_gdi_ctm = !_cairo_matrix_is_identity (&surface->gdi_ctm); + inverse_ctm = surface->ctm; + status = cairo_matrix_invert (&inverse_ctm); + if (status) + return status; + + x_res = GetDeviceCaps (surface->dc, LOGPIXELSX); + y_res = GetDeviceCaps (surface->dc, LOGPIXELSY); + cairo_matrix_transform_distance (&inverse_ctm, &x_res, &y_res); + _cairo_surface_set_resolution (&surface->base, x_res, y_res); + + SaveDC (surface->dc); /* Then save Cairo's known-good clip state, so the clip path can be reset */ + + return CAIRO_STATUS_SUCCESS; +} + +static void +_cairo_win32_printing_surface_set_paginated_mode (void *abstract_surface, + cairo_paginated_mode_t paginated_mode) +{ + cairo_win32_surface_t *surface = abstract_surface; + + surface->paginated_mode = paginated_mode; +} + +static cairo_bool_t +_cairo_win32_printing_surface_supports_fine_grained_fallbacks (void *abstract_surface) +{ + return TRUE; +} + +/** + * cairo_win32_printing_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 DC should be a printing DC; + * antialiasing will be ignored, and GDI will be used as much as + * possible to draw to the surface. + * + * The returned surface will be wrapped using the paginated surface to + * provide correct complex rendering behaviour; cairo_surface_show_page() and + * associated methods must be used for correct output. + * + * Return value: the newly created surface + * + * Since: 1.6 + **/ +cairo_surface_t * +cairo_win32_printing_surface_create (HDC hdc) +{ + cairo_win32_surface_t *surface; + cairo_surface_t *paginated; + RECT rect; + + surface = malloc (sizeof (cairo_win32_surface_t)); + if (surface == NULL) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + + if (_cairo_win32_save_initial_clip (hdc, surface) != CAIRO_STATUS_SUCCESS) { + free (surface); + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + } + + _cairo_surface_clipper_init (&surface->clipper, + _cairo_win32_printing_surface_clipper_intersect_clip_path); + + surface->image = NULL; + surface->format = CAIRO_FORMAT_RGB24; + surface->content = CAIRO_CONTENT_COLOR_ALPHA; + + surface->dc = hdc; + surface->bitmap = NULL; + surface->is_dib = FALSE; + surface->saved_dc_bitmap = NULL; + surface->brush = NULL; + surface->old_brush = NULL; + surface->font_subsets = _cairo_scaled_font_subsets_create_scaled (); + if (surface->font_subsets == NULL) { + free (surface); + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + } + + GetClipBox(hdc, &rect); + surface->extents.x = rect.left; + surface->extents.y = rect.top; + surface->extents.width = rect.right - rect.left; + surface->extents.height = rect.bottom - rect.top; + + surface->flags = _cairo_win32_flags_for_dc (surface->dc); + surface->flags |= CAIRO_WIN32_SURFACE_FOR_PRINTING; + + _cairo_win32_printing_surface_init_ps_mode (surface); + _cairo_win32_printing_surface_init_image_support (surface); + _cairo_win32_printing_surface_init_language_pack (surface); + _cairo_surface_init (&surface->base, + &cairo_win32_printing_surface_backend, + NULL, /* device */ + CAIRO_CONTENT_COLOR_ALPHA); + + paginated = _cairo_paginated_surface_create (&surface->base, + CAIRO_CONTENT_COLOR_ALPHA, + &cairo_win32_surface_paginated_backend); + + /* paginated keeps the only reference to surface now, drop ours */ + cairo_surface_destroy (&surface->base); + + return paginated; +} + +cairo_bool_t +_cairo_surface_is_win32_printing (cairo_surface_t *surface) +{ + return surface->backend == &cairo_win32_printing_surface_backend; +} + +static const cairo_surface_backend_t cairo_win32_printing_surface_backend = { + CAIRO_SURFACE_TYPE_WIN32_PRINTING, + _cairo_win32_surface_finish, + + _cairo_default_context_create, + + _cairo_win32_printing_surface_create_similar, + NULL, /* create similar image */ + NULL, /* map to image */ + NULL, /* unmap image */ + + _cairo_surface_default_source, + NULL, /* acquire_source_image */ + NULL, /* release_source_image */ + NULL, /* snapshot */ + + NULL, /* copy_page */ + _cairo_win32_printing_surface_show_page, + + _cairo_win32_surface_get_extents, + _cairo_win32_printing_surface_get_font_options, + + NULL, /* flush */ + NULL, /* mark_dirty_rectangle */ + + _cairo_win32_printing_surface_paint, + NULL, /* mask */ + _cairo_win32_printing_surface_stroke, + _cairo_win32_printing_surface_fill, + NULL, /* fill/stroke */ + _cairo_win32_printing_surface_show_glyphs, + NULL, /* has_show_text_glyphs */ + NULL, /* show_text_glyphs */ + _cairo_win32_printing_surface_get_supported_mime_types, +}; + +static const cairo_paginated_surface_backend_t cairo_win32_surface_paginated_backend = { + _cairo_win32_printing_surface_start_page, + _cairo_win32_printing_surface_set_paginated_mode, + NULL, /* set_bounding_box */ + NULL, /* _cairo_win32_printing_surface_has_fallback_images, */ + _cairo_win32_printing_surface_supports_fine_grained_fallbacks, +}; diff --git a/src/win32/cairo-win32-private.h b/src/win32/cairo-win32-private.h new file mode 100644 index 00000000..07830dc2 --- /dev/null +++ b/src/win32/cairo-win32-private.h @@ -0,0 +1,219 @@ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2005 Red Hat, Inc + * + * 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 <otaylor@redhat.com> + */ + +#ifndef CAIRO_WIN32_PRIVATE_H +#define CAIRO_WIN32_PRIVATE_H + +#include "cairo-win32.h" +#include "cairoint.h" +#include "cairo-surface-clipper-private.h" + +#ifndef SHADEBLENDCAPS +#define SHADEBLENDCAPS 120 +#endif +#ifndef SB_NONE +#define SB_NONE 0 +#endif + +#define WIN32_FONT_LOGICAL_SCALE 32 + +typedef struct _cairo_win32_surface { + cairo_surface_t base; + + cairo_format_t format; + + HDC dc; + + /* We create off-screen surfaces as DIBs or DDBs, based on what we created + * originally*/ + HBITMAP bitmap; + cairo_bool_t is_dib; + + /* Used to save the initial 1x1 monochrome bitmap for the DC to + * select back into the DC before deleting the DC and our + * bitmap. For Windows XP, this doesn't seem to be necessary + * ... we can just delete the DC and that automatically unselects + * out bitmap. But it's standard practice so apparently is needed + * on some versions of Windows. + */ + HBITMAP saved_dc_bitmap; + + cairo_surface_t *image; + + /* We use the x and y parts of extents for situations where + * we're not supposed to draw to the entire surface. + * For example, during a paint event a program will get + * a DC that has been clipped to the dirty region. + * A cairo surface constructed for that DC will have extents + * that match bounds of the clipped region. + * + * jrmuizel: I'm not sure if storing these extents instead + * of just using the size is better... */ + cairo_rectangle_int_t extents; + + /* Initial clip bits + * We need these kept around so that we maintain + * whatever clip was set on the original DC at creation + * time when cairo is asked to reset the surface clip. + */ + cairo_rectangle_int_t clip_rect; + HRGN initial_clip_rgn; + cairo_bool_t had_simple_clip; + cairo_region_t *clip_region; + + /* For path clipping to the printing-surface */ + cairo_surface_clipper_t clipper; + + /* Surface DC flags */ + uint32_t flags; + + /* printing surface bits */ + cairo_paginated_mode_t paginated_mode; + cairo_content_t content; + cairo_bool_t path_empty; + cairo_bool_t has_ctm; + cairo_matrix_t ctm; + cairo_bool_t has_gdi_ctm; + cairo_matrix_t gdi_ctm; + HBRUSH brush, old_brush; + cairo_scaled_font_subsets_t *font_subsets; +} cairo_win32_surface_t; + +/* Surface DC flag values */ +enum { + /* If this is a surface created for printing or not */ + CAIRO_WIN32_SURFACE_FOR_PRINTING = (1<<0), + + /* Whether the DC is a display DC or not */ + CAIRO_WIN32_SURFACE_IS_DISPLAY = (1<<1), + + /* Whether we can use BitBlt with this surface */ + CAIRO_WIN32_SURFACE_CAN_BITBLT = (1<<2), + + /* Whether we can use AlphaBlend with this surface */ + CAIRO_WIN32_SURFACE_CAN_ALPHABLEND = (1<<3), + + /* Whether we can use StretchBlt with this surface */ + CAIRO_WIN32_SURFACE_CAN_STRETCHBLT = (1<<4), + + /* Whether we can use StretchDIBits with this surface */ + CAIRO_WIN32_SURFACE_CAN_STRETCHDIB = (1<<5), + + /* Whether we can use GradientFill rectangles with this surface */ + CAIRO_WIN32_SURFACE_CAN_RECT_GRADIENT = (1<<6), + + /* Whether we can use the CHECKJPEGFORMAT escape function */ + CAIRO_WIN32_SURFACE_CAN_CHECK_JPEG = (1<<7), + + /* Whether we can use the CHECKJPEGFORMAT escape function */ + CAIRO_WIN32_SURFACE_CAN_CHECK_PNG = (1<<8), +}; + +cairo_status_t +_cairo_win32_print_gdi_error (const char *context); + +cairo_bool_t +_cairo_surface_is_win32 (cairo_surface_t *surface); + +cairo_bool_t +_cairo_surface_is_win32_printing (cairo_surface_t *surface); + +cairo_status_t +_cairo_win32_surface_finish (void *abstract_surface); + +cairo_bool_t +_cairo_win32_surface_get_extents (void *abstract_surface, + cairo_rectangle_int_t *rectangle); + +uint32_t +_cairo_win32_flags_for_dc (HDC dc); + +cairo_status_t +_cairo_win32_surface_set_clip_region (void *abstract_surface, + cairo_region_t *region); + +cairo_int_status_t +_cairo_win32_surface_show_glyphs_internal (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_bool_t glyph_indices); + +cairo_int_status_t +_cairo_win32_surface_show_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_surface_t * +_cairo_win32_surface_create_similar (void *abstract_src, + cairo_content_t content, + int width, + int height); + +static inline void +_cairo_matrix_to_win32_xform (const cairo_matrix_t *m, + XFORM *xform) +{ + xform->eM11 = (FLOAT) m->xx; + xform->eM21 = (FLOAT) m->xy; + xform->eM12 = (FLOAT) m->yx; + xform->eM22 = (FLOAT) m->yy; + xform->eDx = (FLOAT) m->x0; + xform->eDy = (FLOAT) m->y0; +} + +cairo_int_status_t +_cairo_win32_save_initial_clip (HDC dc, cairo_win32_surface_t *surface); + +cairo_int_status_t +_cairo_win32_restore_initial_clip (cairo_win32_surface_t *surface); + +void +_cairo_win32_debug_dump_hrgn (HRGN rgn, char *header); + +cairo_bool_t +_cairo_win32_scaled_font_is_type1 (cairo_scaled_font_t *scaled_font); + +cairo_bool_t +_cairo_win32_scaled_font_is_bitmap (cairo_scaled_font_t *scaled_font); + +#endif /* CAIRO_WIN32_PRIVATE_H */ diff --git a/src/win32/cairo-win32-surface.c b/src/win32/cairo-win32-surface.c new file mode 100644 index 00000000..ec351d3f --- /dev/null +++ b/src/win32/cairo-win32-surface.c @@ -0,0 +1,2087 @@ +/* -*- 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. + * + * 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 <otaylor@redhat.com> + * Stuart Parmenter <stuart@mozilla.com> + * Vladimir Vukicevic <vladimir@pobox.com> + */ + +#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-default-context-private.h" +#include "cairo-error-private.h" +#include "cairo-image-surface-private.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 <wchar.h> +#include <windows.h> + +#if defined(__MINGW32__) && !defined(ETO_PDY) +# define ETO_PDY 0x2000 +#endif + +#undef DEBUG_COMPOSITE + +/* for older SDKs */ +#ifndef SHADEBLENDCAPS +#define SHADEBLENDCAPS 120 +#endif +#ifndef SB_NONE +#define SB_NONE 0x00000000 +#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. + */ + +static const cairo_surface_backend_t cairo_win32_surface_backend; + +/** + * _cairo_win32_print_gdi_error: + * @context: context string to display along with the error + * + * Helper function to dump out a human readable form of the + * current error code. + * + * Return value: A cairo status code for the error code + **/ +cairo_status_t +_cairo_win32_print_gdi_error (const char *context) +{ + void *lpMsgBuf; + DWORD last_error = GetLastError (); + + if (!FormatMessageW (FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM, + NULL, + last_error, + MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPWSTR) &lpMsgBuf, + 0, NULL)) { + fprintf (stderr, "%s: Unknown GDI error", context); + } else { + fprintf (stderr, "%s: %S", context, (wchar_t *)lpMsgBuf); + + LocalFree (lpMsgBuf); + } + + fflush (stderr); + + /* We should switch off of last_status, but we'd either return + * CAIRO_STATUS_NO_MEMORY or CAIRO_STATUS_UNKNOWN_ERROR and there + * is no CAIRO_STATUS_UNKNOWN_ERROR. + */ + + return _cairo_error (CAIRO_STATUS_NO_MEMORY); +} + +uint32_t +_cairo_win32_flags_for_dc (HDC dc) +{ + uint32_t flags = 0; + + if (GetDeviceCaps(dc, TECHNOLOGY) == DT_RASDISPLAY) { + flags |= CAIRO_WIN32_SURFACE_IS_DISPLAY; + + /* These will always be possible, but the actual GetDeviceCaps + * calls will return whether they're accelerated or not. + * We may want to use our own (pixman) routines sometimes + * if they're eventually faster, but for now have GDI do + * everything. + */ + flags |= CAIRO_WIN32_SURFACE_CAN_BITBLT; + flags |= CAIRO_WIN32_SURFACE_CAN_ALPHABLEND; + flags |= CAIRO_WIN32_SURFACE_CAN_STRETCHBLT; + flags |= CAIRO_WIN32_SURFACE_CAN_STRETCHDIB; + } else { + int cap; + + cap = GetDeviceCaps(dc, SHADEBLENDCAPS); + if (cap != SB_NONE) + flags |= CAIRO_WIN32_SURFACE_CAN_ALPHABLEND; + + cap = GetDeviceCaps(dc, RASTERCAPS); + if (cap & RC_BITBLT) + flags |= CAIRO_WIN32_SURFACE_CAN_BITBLT; + if (cap & RC_STRETCHBLT) + flags |= CAIRO_WIN32_SURFACE_CAN_STRETCHBLT; + if (cap & RC_STRETCHDIB) + flags |= CAIRO_WIN32_SURFACE_CAN_STRETCHDIB; + } + + return flags; +} + +static cairo_status_t +_create_dc_and_bitmap (cairo_win32_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->dc = NULL; + surface->bitmap = NULL; + surface->is_dib = FALSE; + + switch (format) { + default: + case CAIRO_FORMAT_INVALID: + 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) { + /* 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->dc = CreateCompatibleDC (original_dc); + if (!surface->dc) + goto FAIL; + + surface->bitmap = CreateDIBSection (surface->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->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_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->flags = _cairo_win32_flags_for_dc (surface->dc); + + return CAIRO_STATUS_SUCCESS; + + FAIL: + status = _cairo_win32_print_gdi_error ("_create_dc_and_bitmap"); + + if (bitmap_info && num_palette > 2) + free (bitmap_info); + + if (surface->saved_dc_bitmap) { + SelectObject (surface->dc, surface->saved_dc_bitmap); + surface->saved_dc_bitmap = NULL; + } + + if (surface->bitmap) { + DeleteObject (surface->bitmap); + surface->bitmap = NULL; + } + + if (surface->dc) { + DeleteDC (surface->dc); + surface->dc = NULL; + } + + return status; +} + +static cairo_surface_t * +_cairo_win32_surface_create_for_dc (HDC original_dc, + cairo_format_t format, + int width, + int height) +{ + cairo_status_t status; + cairo_win32_surface_t *surface; + unsigned char *bits; + int rowstride; + + if (! CAIRO_FORMAT_VALID (format)) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT)); + + surface = malloc (sizeof (cairo_win32_surface_t)); + if (surface == NULL) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + + surface->clip_region = 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; + + surface->format = format; + + surface->clip_rect.x = 0; + surface->clip_rect.y = 0; + surface->clip_rect.width = width; + surface->clip_rect.height = height; + + surface->initial_clip_rgn = NULL; + surface->had_simple_clip = FALSE; + + surface->extents = surface->clip_rect; + surface->font_subsets = NULL; + + _cairo_surface_init (&surface->base, + &cairo_win32_surface_backend, + NULL, /* device */ + _cairo_content_from_format (format)); + + return &surface->base; + + FAIL: + if (surface->bitmap) { + SelectObject (surface->dc, surface->saved_dc_bitmap); + DeleteObject (surface->bitmap); + DeleteDC (surface->dc); + } + free (surface); + + return _cairo_surface_create_in_error (status); +} + +static cairo_surface_t * +_cairo_win32_surface_create_similar_internal (void *abstract_src, + cairo_content_t content, + int width, + int height, + cairo_bool_t force_dib) +{ + cairo_win32_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) || + src->base.backend->type == CAIRO_SURFACE_TYPE_WIN32_PRINTING) + { + force_dib = TRUE; + } + + if (!force_dib) { + /* try to create a ddb */ + new_surf = cairo_win32_surface_create_with_ddb (src->dc, CAIRO_FORMAT_RGB24, width, height); + + if (new_surf->status != CAIRO_STATUS_SUCCESS) + new_surf = NULL; + } + + if (new_surf == NULL) { + new_surf = _cairo_win32_surface_create_for_dc (src->dc, format, width, height); + } + + return new_surf; +} + +cairo_surface_t * +_cairo_win32_surface_create_similar (void *abstract_src, + cairo_content_t content, + int width, + int height) +{ + return _cairo_win32_surface_create_similar_internal (abstract_src, content, width, height, FALSE); +} + +cairo_status_t +_cairo_win32_surface_finish (void *abstract_surface) +{ + cairo_win32_surface_t *surface = abstract_surface; + + if (surface->image) + cairo_surface_destroy (surface->image); + + /* If we created the Bitmap and DC, destroy them */ + if (surface->bitmap) { + SelectObject (surface->dc, surface->saved_dc_bitmap); + DeleteObject (surface->bitmap); + DeleteDC (surface->dc); + } else { + _cairo_win32_restore_initial_clip (surface); + } + + if (surface->initial_clip_rgn) + DeleteObject (surface->initial_clip_rgn); + + if (surface->font_subsets != NULL) + _cairo_scaled_font_subsets_destroy (surface->font_subsets); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_win32_surface_get_subimage (cairo_win32_surface_t *surface, + int x, + int y, + int width, + int height, + cairo_win32_surface_t **local_out) +{ + cairo_win32_surface_t *local; + cairo_int_status_t status; + cairo_content_t content = _cairo_content_from_format (surface->format); + + local = + (cairo_win32_surface_t *) _cairo_win32_surface_create_similar_internal + (surface, content, width, height, TRUE); + if (local == NULL) + return CAIRO_INT_STATUS_UNSUPPORTED; + if (local->base.status) + return local->base.status; + + status = CAIRO_INT_STATUS_UNSUPPORTED; + + /* Only BitBlt if the source surface supports it. */ + if ((surface->flags & CAIRO_WIN32_SURFACE_CAN_BITBLT) && + BitBlt (local->dc, + 0, 0, + width, height, + surface->dc, + x, y, + SRCCOPY)) + { + status = CAIRO_STATUS_SUCCESS; + } + + if (status) { + /* If we failed here, most likely the source or dest doesn't + * support BitBlt/AlphaBlend (e.g. a printer). + * You can't reliably get bits from a printer DC, so just fill in + * the surface as white (common case for printing). + */ + + RECT r; + r.left = r.top = 0; + r.right = width; + r.bottom = height; + FillRect(local->dc, &r, (HBRUSH)GetStockObject(WHITE_BRUSH)); + } + + *local_out = local; + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_surface_t * +_cairo_win32_surface_map_to_image (void *abstract_surface, + const cairo_rectangle_int_t *extents) +{ + cairo_win32_surface_t *surface = abstract_surface; + cairo_win32_surface_t *local = NULL; + cairo_status_t status; + + if (surface->image) { + GdiFlush(); + return _cairo_surface_create_for_rectangle_int (surface->image, + extents); + } + + status = _cairo_win32_surface_get_subimage (abstract_surface, + extents->x, + extents->y, + extents->width, + extents->height, + &local); + if (unlikely (status)) + return _cairo_surface_create_in_error (status); + + status = cairo_surface_set_user_data (local->image, + (const cairo_user_data_key_t *)surface->image, + local, NULL); + if (unlikely (status)) { + cairo_surface_destroy (&local->base); + return _cairo_surface_create_in_error (status); + } + + cairo_surface_set_device_offset (local->image, -extents->x, -extents->y); + return local->image; +} + +static cairo_int_status_t +_cairo_win32_surface_unmap_image (void *abstract_surface, + cairo_image_surface_t *image) +{ + cairo_win32_surface_t *surface = abstract_surface; + cairo_win32_surface_t *local; + + local = cairo_surface_get_user_data (&image->base, + (const cairo_user_data_key_t *) surface->image); + if (!local) + return CAIRO_INT_STATUS_SUCCESS; + + if (!BitBlt (surface->dc, + image->base.device_transform.x0, + image->base.device_transform.y0, + image->width, image->height, + local->dc, + 0, 0, + SRCCOPY)) + _cairo_win32_print_gdi_error ("_cairo_win32_surface_release_dest_image"); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_win32_surface_acquire_source_image (void *abstract_surface, + cairo_image_surface_t **image_out, + void **image_extra) +{ + cairo_win32_surface_t *surface = abstract_surface; + cairo_win32_surface_t *local; + cairo_status_t status; + + if (surface->image) { + *image_out = (cairo_image_surface_t *)surface->image; + *image_extra = NULL; + return CAIRO_STATUS_SUCCESS; + } + + status = _cairo_win32_surface_get_subimage (abstract_surface, 0, 0, + surface->extents.width, + surface->extents.height, &local); + if (status) + return status; + + *image_out = (cairo_image_surface_t *)local->image; + *image_extra = local; + return CAIRO_STATUS_SUCCESS; +} + +static void +_cairo_win32_surface_release_source_image (void *abstract_surface, + cairo_image_surface_t *image, + void *image_extra) +{ + cairo_win32_surface_t *local = image_extra; + + if (local) + cairo_surface_destroy ((cairo_surface_t *)local); +} + +cairo_status_t +_cairo_win32_surface_set_clip_region (void *abstract_surface, + cairo_region_t *region) +{ + cairo_win32_surface_t *surface = abstract_surface; + cairo_status_t status = CAIRO_STATUS_SUCCESS; + + if (surface->clip_region == region) + return CAIRO_STATUS_SUCCESS; + + cairo_region_destroy (surface->clip_region); + surface->clip_region = cairo_region_reference (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. + */ + + /* Clear any clip set by cairo, return to the original first */ + status = _cairo_win32_restore_initial_clip (surface); + + /* Then combine any new region with it */ + if (region) { + cairo_rectangle_int_t extents; + int num_rects; + RGNDATA *data; + size_t data_size; + RECT *rects; + int i; + HRGN gdi_region; + + /* Create a GDI region for the cairo region */ + + 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); + data = malloc (data_size); + if (!data) + return _cairo_error(CAIRO_STATUS_NO_MEMORY); + rects = (RECT *)data->Buffer; + + 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; + + 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); + free (data); + + if (!gdi_region) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + + /* AND the new region into our DC */ + if (ExtSelectClipRgn (surface->dc, gdi_region, RGN_AND) == ERROR) + status = _cairo_win32_print_gdi_error ("_cairo_win32_surface_set_clip_region"); + + DeleteObject (gdi_region); + } + + return status; +} + +#if !defined(AC_SRC_OVER) +#define AC_SRC_OVER 0x00 +#pragma pack(1) +typedef struct { + BYTE BlendOp; + BYTE BlendFlags; + BYTE SourceConstantAlpha; + BYTE AlphaFormat; +}BLENDFUNCTION; +#pragma pack() +#endif + +/* for compatibility with VC++ 6 */ +#ifndef AC_SRC_ALPHA +#define AC_SRC_ALPHA 0x01 +#endif + +typedef BOOL (WINAPI *cairo_alpha_blend_func_t) (HDC hdcDest, + int nXOriginDest, + int nYOriginDest, + int nWidthDest, + int hHeightDest, + HDC hdcSrc, + int nXOriginSrc, + int nYOriginSrc, + int nWidthSrc, + int nHeightSrc, + BLENDFUNCTION blendFunction); + +static cairo_int_status_t +_composite_alpha_blend (cairo_win32_surface_t *dst, + cairo_win32_surface_t *src, + int alpha, + int src_x, + int src_y, + int src_w, + int src_h, + int dst_x, + int dst_y, + int dst_w, + int dst_h) +{ + static unsigned alpha_blend_checked = FALSE; + static cairo_alpha_blend_func_t alpha_blend = NULL; + + BLENDFUNCTION blend_function; + + /* Check for AlphaBlend dynamically to allow compiling on + * MSVC 6 and use on older windows versions + */ + if (!alpha_blend_checked) { + OSVERSIONINFO os; + + os.dwOSVersionInfoSize = sizeof (os); + GetVersionEx (&os); + + /* If running on Win98, disable using AlphaBlend() + * to avoid Win98 AlphaBlend() bug */ + if (VER_PLATFORM_WIN32_WINDOWS != os.dwPlatformId || + os.dwMajorVersion != 4 || os.dwMinorVersion != 10) + { + HMODULE msimg32_dll = LoadLibraryW (L"msimg32"); + + if (msimg32_dll != NULL) + alpha_blend = (cairo_alpha_blend_func_t)GetProcAddress (msimg32_dll, + "AlphaBlend"); + } + + alpha_blend_checked = TRUE; + } + + if (alpha_blend == NULL) + return CAIRO_INT_STATUS_UNSUPPORTED; + if (!(dst->flags & CAIRO_WIN32_SURFACE_CAN_ALPHABLEND)) + return CAIRO_INT_STATUS_UNSUPPORTED; + if (src->format == CAIRO_FORMAT_RGB24 && dst->format == CAIRO_FORMAT_ARGB32) + return CAIRO_INT_STATUS_UNSUPPORTED; + + blend_function.BlendOp = AC_SRC_OVER; + blend_function.BlendFlags = 0; + blend_function.SourceConstantAlpha = alpha; + blend_function.AlphaFormat = (src->format == CAIRO_FORMAT_ARGB32) ? AC_SRC_ALPHA : 0; + + if (!alpha_blend (dst->dc, + dst_x, dst_y, + dst_w, dst_h, + src->dc, + src_x, src_y, + src_w, src_h, + blend_function)) + return _cairo_win32_print_gdi_error ("_cairo_win32_surface_composite(AlphaBlend)"); + + return CAIRO_STATUS_SUCCESS; +} + +static cairo_int_status_t +_cairo_win32_surface_composite_inner (cairo_win32_surface_t *src, + cairo_image_surface_t *src_image, + cairo_win32_surface_t *dst, + cairo_rectangle_int_t src_extents, + cairo_rectangle_int_t src_r, + cairo_rectangle_int_t dst_r, + int alpha, + cairo_bool_t needs_alpha, + cairo_bool_t needs_scale) +{ + /* Then do BitBlt, StretchDIBits, StretchBlt, AlphaBlend, or MaskBlt */ + if (src_image) { + if (needs_alpha || needs_scale) + return CAIRO_INT_STATUS_UNSUPPORTED; + + if (dst->flags & CAIRO_WIN32_SURFACE_CAN_STRETCHBLT) { + BITMAPINFO bi; + bi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + bi.bmiHeader.biWidth = src_image->width; + bi.bmiHeader.biHeight = - src_image->height; + bi.bmiHeader.biSizeImage = 0; + bi.bmiHeader.biXPelsPerMeter = PELS_72DPI; + bi.bmiHeader.biYPelsPerMeter = PELS_72DPI; + bi.bmiHeader.biPlanes = 1; + bi.bmiHeader.biBitCount = 32; + bi.bmiHeader.biCompression = BI_RGB; + bi.bmiHeader.biClrUsed = 0; + bi.bmiHeader.biClrImportant = 0; + + /* StretchDIBits is broken with top-down dibs; you need to do some + * special munging to make the coordinate space work (basically, + * need to address everything based on the bottom left, instead of top left, + * and need to tell it to flip the resulting image. + * + * See http://blog.vlad1.com/archives/2006/10/26/134/ and comments. + */ + if (!StretchDIBits (dst->dc, + /* dst x,y,w,h */ + dst_r.x, dst_r.y + dst_r.height - 1, + dst_r.width, - (int) dst_r.height, + /* src x,y,w,h */ + src_r.x, src_extents.height - src_r.y + 1, + src_r.width, - (int) src_r.height, + src_image->data, + &bi, + DIB_RGB_COLORS, + SRCCOPY)) + return _cairo_win32_print_gdi_error ("_cairo_win32_surface_composite(StretchDIBits)"); + } + } else if (!needs_alpha) { + /* BitBlt or StretchBlt? */ + if (!needs_scale && (dst->flags & CAIRO_WIN32_SURFACE_CAN_BITBLT)) { + if (!BitBlt (dst->dc, + dst_r.x, dst_r.y, + dst_r.width, dst_r.height, + src->dc, + src_r.x, src_r.y, + SRCCOPY)) + return _cairo_win32_print_gdi_error ("_cairo_win32_surface_composite(BitBlt)"); + } else if (dst->flags & CAIRO_WIN32_SURFACE_CAN_STRETCHBLT) { + /* StretchBlt? */ + /* XXX check if we want HALFTONE, based on the src filter */ + BOOL success; + int oldmode = SetStretchBltMode(dst->dc, HALFTONE); + success = StretchBlt(dst->dc, + dst_r.x, dst_r.y, + dst_r.width, dst_r.height, + src->dc, + src_r.x, src_r.y, + src_r.width, src_r.height, + SRCCOPY); + SetStretchBltMode(dst->dc, oldmode); + + if (!success) + return _cairo_win32_print_gdi_error ("StretchBlt"); + } + } else if (needs_alpha && !needs_scale) { + return _composite_alpha_blend (dst, src, alpha, + src_r.x, src_r.y, src_r.width, src_r.height, + dst_r.x, dst_r.y, dst_r.width, dst_r.height); + } + + return CAIRO_STATUS_SUCCESS; +} + +/* from pixman-private.h */ +#define MOD(a,b) ((a) < 0 ? ((b) - ((-(a) - 1) % (b))) - 1 : (a) % (b)) + +static cairo_int_status_t +_cairo_win32_surface_composite (cairo_operator_t op, + const cairo_pattern_t *pattern, + const cairo_pattern_t *mask_pattern, + void *abstract_dst, + int src_x, + int src_y, + int mask_x, + int mask_y, + int dst_x, + int dst_y, + unsigned int width, + unsigned int height, + cairo_region_t *clip_region) +{ + cairo_win32_surface_t *dst = abstract_dst; + cairo_win32_surface_t *src; + cairo_surface_pattern_t *src_surface_pattern; + int alpha; + double scalex, scaley; + cairo_fixed_t x0_fixed, y0_fixed; + cairo_int_status_t status; + + cairo_bool_t needs_alpha, needs_scale, needs_repeat; + cairo_image_surface_t *src_image = NULL; + + cairo_format_t src_format; + cairo_rectangle_int_t src_extents; + + cairo_rectangle_int_t src_r = { src_x, src_y, width, height }; + cairo_rectangle_int_t dst_r = { dst_x, dst_y, width, height }; + +#ifdef DEBUG_COMPOSITE + fprintf (stderr, "+++ composite: %d %p %p %p [%d %d] [%d %d] [%d %d] %dx%d\n", + op, pattern, mask_pattern, abstract_dst, src_x, src_y, mask_x, mask_y, dst_x, dst_y, width, height); +#endif + + /* If the destination can't do any of these, then + * we may as well give up, since this is what we'll + * look to for optimization. + */ + if ((dst->flags & (CAIRO_WIN32_SURFACE_CAN_BITBLT | + CAIRO_WIN32_SURFACE_CAN_ALPHABLEND | + CAIRO_WIN32_SURFACE_CAN_STRETCHBLT | + CAIRO_WIN32_SURFACE_CAN_STRETCHDIB)) + == 0) + { + goto UNSUPPORTED; + } + + if (pattern->type != CAIRO_PATTERN_TYPE_SURFACE) + goto UNSUPPORTED; + + if (pattern->extend != CAIRO_EXTEND_NONE && + pattern->extend != CAIRO_EXTEND_REPEAT) + goto UNSUPPORTED; + + if (mask_pattern) { + /* FIXME: When we fully support RENDER style 4-channel + * masks we need to check r/g/b != 1.0. + */ + if (mask_pattern->type != CAIRO_PATTERN_TYPE_SOLID) + return CAIRO_INT_STATUS_UNSUPPORTED; + + alpha = ((cairo_solid_pattern_t *)mask_pattern)->color.alpha_short >> 8; + } else { + alpha = 255; + } + + src_surface_pattern = (cairo_surface_pattern_t *)pattern; + src = (cairo_win32_surface_t *)src_surface_pattern->surface; + + if (src->base.type == CAIRO_SURFACE_TYPE_IMAGE && + dst->flags & (CAIRO_WIN32_SURFACE_CAN_STRETCHDIB)) + { + /* In some very limited cases, we can use StretchDIBits to draw + * an image surface directly: + * - source is CAIRO_FORMAT_ARGB32 + * - dest is CAIRO_FORMAT_ARGB32 + * - alpha is 255 + * - operator is SOURCE or OVER + * - image stride is 4*width + */ + src_image = (cairo_image_surface_t*) src; + + if (src_image->format != CAIRO_FORMAT_RGB24 || + dst->format != CAIRO_FORMAT_RGB24 || + alpha != 255 || + (op != CAIRO_OPERATOR_SOURCE && op != CAIRO_OPERATOR_OVER) || + src_image->stride != (src_image->width * 4)) + { + goto UNSUPPORTED; + } + + src_format = src_image->format; + src_extents.x = 0; + src_extents.y = 0; + src_extents.width = src_image->width; + src_extents.height = src_image->height; + } else if (src->base.backend != dst->base.backend) { + goto UNSUPPORTED; + } else { + src_format = src->format; + src_extents = src->extents; + } + + +#ifdef DEBUG_COMPOSITE + fprintf (stderr, "Before check: src size: (%d %d) xy [%d %d] -> dst [%d %d %d %d] {srcmat %f %f %f %f}\n", + src_extents.width, src_extents.height, + src_x, src_y, + dst_x, dst_y, width, height, + pattern->matrix.x0, pattern->matrix.y0, pattern->matrix.xx, pattern->matrix.yy); +#endif + + /* We can only use GDI functions if the source and destination rectangles + * are on integer pixel boundaries. Figure that out here. + */ + x0_fixed = _cairo_fixed_from_double(pattern->matrix.x0 / pattern->matrix.xx); + y0_fixed = _cairo_fixed_from_double(pattern->matrix.y0 / pattern->matrix.yy); + + if (pattern->matrix.yx != 0.0 || + pattern->matrix.xy != 0.0 || + !_cairo_fixed_is_integer(x0_fixed) || + !_cairo_fixed_is_integer(y0_fixed)) + { + goto UNSUPPORTED; + } + + scalex = pattern->matrix.xx; + scaley = pattern->matrix.yy; + + src_r.x += _cairo_fixed_integer_part(x0_fixed); + src_r.y += _cairo_fixed_integer_part(y0_fixed); + + /* Success, right? */ + if (scalex == 0.0 || scaley == 0.0) + return CAIRO_STATUS_SUCCESS; + + if (scalex != 1.0 || scaley != 1.0) + goto UNSUPPORTED; + + /* If the src coordinates are outside of the source surface bounds, + * we have to fix them up, because this is an error for the GDI + * functions. + */ + +#ifdef DEBUG_COMPOSITE + fprintf (stderr, "before: [%d %d %d %d] -> [%d %d %d %d]\n", + src_r.x, src_r.y, src_r.width, src_r.height, + dst_r.x, dst_r.y, dst_r.width, dst_r.height); + fflush (stderr); +#endif + + /* If the src rectangle doesn't wholly lie within the src extents, + * fudge things. We really need to do fixup on the unpainted + * region -- e.g. the SOURCE operator is broken for areas outside + * of the extents, because it won't clear that area to transparent + * black. + */ + + if (pattern->extend != CAIRO_EXTEND_REPEAT) { + needs_repeat = FALSE; + + /* If the src rect and the extents of the source image don't overlap at all, + * we can't do anything useful here. + */ + if (src_r.x > src_extents.width || src_r.y > src_extents.height || + (src_r.x + src_r.width) < 0 || (src_r.y + src_r.height) < 0) + { + if (op == CAIRO_OPERATOR_OVER) + return CAIRO_STATUS_SUCCESS; + goto UNSUPPORTED; + } + + if (src_r.x < 0) { + src_r.width += src_r.x; + src_r.x = 0; + + dst_r.width += src_r.x; + dst_r.x -= src_r.x; + } + + if (src_r.y < 0) { + src_r.height += src_r.y; + src_r.y = 0; + + dst_r.height += dst_r.y; + dst_r.y -= src_r.y; + } + + if (src_r.x + src_r.width > src_extents.width) { + src_r.width = src_extents.width - src_r.x; + dst_r.width = src_r.width; + } + + if (src_r.y + src_r.height > src_extents.height) { + src_r.height = src_extents.height - src_r.y; + dst_r.height = src_r.height; + } + } else { + needs_repeat = TRUE; + } + + /* + * Operations that we can do: + * + * RGB OVER RGB -> BitBlt (same as SOURCE) + * RGB OVER ARGB -> UNSUPPORTED (AlphaBlend treats this as a BitBlt, even with SCA 255 and no AC_SRC_ALPHA) + * ARGB OVER ARGB -> AlphaBlend, with AC_SRC_ALPHA + * ARGB OVER RGB -> AlphaBlend, with AC_SRC_ALPHA; we'll have junk in the dst A byte + * + * RGB OVER RGB + mask -> AlphaBlend, no AC_SRC_ALPHA + * RGB OVER ARGB + mask -> UNSUPPORTED + * ARGB OVER ARGB + mask -> AlphaBlend, with AC_SRC_ALPHA + * ARGB OVER RGB + mask -> AlphaBlend, with AC_SRC_ALPHA; junk in the dst A byte + * + * RGB SOURCE RGB -> BitBlt + * RGB SOURCE ARGB -> UNSUPPORTED (AlphaBlend treats this as a BitBlt, even with SCA 255 and no AC_SRC_ALPHA) + * ARGB SOURCE ARGB -> BitBlt + * ARGB SOURCE RGB -> BitBlt + * + * RGB SOURCE RGB + mask -> unsupported + * RGB SOURCE ARGB + mask -> unsupported + * ARGB SOURCE ARGB + mask -> unsupported + * ARGB SOURCE RGB + mask -> unsupported + */ + + /* + * Figure out what action to take. + */ + if (op == CAIRO_OPERATOR_OVER) { + if (alpha == 0) + return CAIRO_STATUS_SUCCESS; + + if (src_format == dst->format) { + if (alpha == 255 && src_format == CAIRO_FORMAT_RGB24) { + needs_alpha = FALSE; + } else { + needs_alpha = TRUE; + } + } else if (src_format == CAIRO_FORMAT_ARGB32 && + dst->format == CAIRO_FORMAT_RGB24) + { + needs_alpha = TRUE; + } else { + goto UNSUPPORTED; + } + } else if (alpha == 255 && op == CAIRO_OPERATOR_SOURCE) { + if ((src_format == dst->format) || + (src_format == CAIRO_FORMAT_ARGB32 && dst->format == CAIRO_FORMAT_RGB24)) + { + needs_alpha = FALSE; + } else { + goto UNSUPPORTED; + } + } else { + goto UNSUPPORTED; + } + + if (scalex == 1.0 && scaley == 1.0) { + needs_scale = FALSE; + } else { + /* Should never be reached until we turn StretchBlt back on */ + needs_scale = TRUE; + } + +#ifdef DEBUG_COMPOSITE + fprintf (stderr, "action: [%d %d %d %d] -> [%d %d %d %d]\n", + src_r.x, src_r.y, src_r.width, src_r.height, + dst_r.x, dst_r.y, dst_r.width, dst_r.height); + fflush (stderr); +#endif + + status = _cairo_win32_surface_set_clip_region (dst, clip_region); + if (status) + return status; + + /* If we need to repeat, we turn the repeated blit into + * a bunch of piece-by-piece blits. + */ + if (needs_repeat) { + cairo_rectangle_int_t piece_src_r, piece_dst_r; + uint32_t rendered_width = 0, rendered_height = 0; + uint32_t to_render_height, to_render_width; + int32_t piece_x, piece_y; + int32_t src_start_x = MOD(src_r.x, src_extents.width); + int32_t src_start_y = MOD(src_r.y, src_extents.height); + + if (needs_scale) + goto UNSUPPORTED; + + /* If both the src and dest have an image, we may as well fall + * back, because it will be faster than our separate blits. + * Our blit code will be fastest when the src is a DDB and the + * destination is a DDB. + */ + if ((src_image || src->image) && dst->image) + goto UNSUPPORTED; + + /* If the src is not a bitmap but an on-screen (or unknown) + * DC, chances are that fallback will be faster. + */ + if (src->bitmap == NULL) + goto UNSUPPORTED; + + /* If we can use PatBlt, just do so */ + if (!src_image && !needs_alpha) + { + HBRUSH brush; + HGDIOBJ old_brush; + POINT old_brush_origin; + + /* Set up the brush with our bitmap */ + brush = CreatePatternBrush (src->bitmap); + + /* SetBrushOrgEx sets the coordinates in the destination DC of where the + * pattern should start. + */ + SetBrushOrgEx (dst->dc, dst_r.x - src_start_x, + dst_r.y - src_start_y, &old_brush_origin); + + old_brush = SelectObject (dst->dc, brush); + + PatBlt (dst->dc, dst_r.x, dst_r.y, dst_r.width, dst_r.height, PATCOPY); + + /* Restore the old brush and pen */ + SetBrushOrgEx (dst->dc, old_brush_origin.x, old_brush_origin.y, NULL); + SelectObject (dst->dc, old_brush); + DeleteObject (brush); + + return CAIRO_STATUS_SUCCESS; + } + + /* If we were not able to use PatBlt, then manually expand out the blit */ + + /* Arbitrary factor; we think that going through + * fallback will be faster if we have to do more + * than this amount of blits in either direction. + */ + if (dst_r.width / src_extents.width > 5 || + dst_r.height / src_extents.height > 5) + goto UNSUPPORTED; + + for (rendered_height = 0; + rendered_height < dst_r.height; + rendered_height += to_render_height) + { + piece_y = (src_start_y + rendered_height) % src_extents.height; + to_render_height = src_extents.height - piece_y; + + if (rendered_height + to_render_height > dst_r.height) + to_render_height = dst_r.height - rendered_height; + + for (rendered_width = 0; + rendered_width < dst_r.width; + rendered_width += to_render_width) + { + piece_x = (src_start_x + rendered_width) % src_extents.width; + to_render_width = src_extents.width - piece_x; + + if (rendered_width + to_render_width > dst_r.width) + to_render_width = dst_r.width - rendered_width; + + piece_src_r.x = piece_x; + piece_src_r.y = piece_y; + piece_src_r.width = to_render_width; + piece_src_r.height = to_render_height; + + piece_dst_r.x = dst_r.x + rendered_width; + piece_dst_r.y = dst_r.y + rendered_height; + piece_dst_r.width = to_render_width; + piece_dst_r.height = to_render_height; + + status = _cairo_win32_surface_composite_inner (src, src_image, dst, + src_extents, piece_src_r, piece_dst_r, + alpha, needs_alpha, needs_scale); + if (status != CAIRO_STATUS_SUCCESS) { + /* Uh oh. If something failed, and it's the first + * piece, then we can jump to UNSUPPORTED. + * Otherwise, this is bad times, because part of the + * rendering was already done. */ + if (rendered_width == 0 && + rendered_height == 0) + { + goto UNSUPPORTED; + } + + return status; + } + } + } + } else { + status = _cairo_win32_surface_composite_inner (src, src_image, dst, + src_extents, src_r, dst_r, + alpha, needs_alpha, needs_scale); + } + + if (status == CAIRO_STATUS_SUCCESS) + return status; + +UNSUPPORTED: + /* Fall back to image surface directly, if this is a DIB surface */ + if (dst->image) { + GdiFlush(); + +#if 0 + return dst->image->backend->composite (op, pattern, mask_pattern, + dst->image, + src_x, src_y, + mask_x, mask_y, + dst_x, dst_y, + width, height, + clip_region); +#endif + } + + return CAIRO_INT_STATUS_UNSUPPORTED; +} + +/* This big function tells us how to optimize operators for the + * case of solid destination and constant-alpha source + * + * Note: This function needs revisiting if we add support for + * super-luminescent colors (a == 0, r,g,b > 0) + */ +static enum { DO_CLEAR, DO_SOURCE, DO_NOTHING, DO_UNSUPPORTED } +categorize_solid_dest_operator (cairo_operator_t op, + unsigned short alpha) +{ + enum { SOURCE_TRANSPARENT, SOURCE_LIGHT, SOURCE_SOLID, SOURCE_OTHER } source; + + if (alpha >= 0xff00) + source = SOURCE_SOLID; + else if (alpha < 0x100) + source = SOURCE_TRANSPARENT; + else + source = SOURCE_OTHER; + + switch (op) { + case CAIRO_OPERATOR_CLEAR: /* 0 0 */ + case CAIRO_OPERATOR_OUT: /* 1 - Ab 0 */ + return DO_CLEAR; + break; + + case CAIRO_OPERATOR_SOURCE: /* 1 0 */ + case CAIRO_OPERATOR_IN: /* Ab 0 */ + return DO_SOURCE; + break; + + case CAIRO_OPERATOR_OVER: /* 1 1 - Aa */ + case CAIRO_OPERATOR_ATOP: /* Ab 1 - Aa */ + if (source == SOURCE_SOLID) + return DO_SOURCE; + else if (source == SOURCE_TRANSPARENT) + return DO_NOTHING; + else + return DO_UNSUPPORTED; + break; + + case CAIRO_OPERATOR_DEST_OUT: /* 0 1 - Aa */ + case CAIRO_OPERATOR_XOR: /* 1 - Ab 1 - Aa */ + if (source == SOURCE_SOLID) + return DO_CLEAR; + else if (source == SOURCE_TRANSPARENT) + return DO_NOTHING; + else + return DO_UNSUPPORTED; + break; + + case CAIRO_OPERATOR_DEST: /* 0 1 */ + case CAIRO_OPERATOR_DEST_OVER:/* 1 - Ab 1 */ + case CAIRO_OPERATOR_SATURATE: /* min(1,(1-Ab)/Aa) 1 */ + return DO_NOTHING; + break; + + case CAIRO_OPERATOR_DEST_IN: /* 0 Aa */ + case CAIRO_OPERATOR_DEST_ATOP:/* 1 - Ab Aa */ + if (source == SOURCE_SOLID) + return DO_NOTHING; + else if (source == SOURCE_TRANSPARENT) + return DO_CLEAR; + else + return DO_UNSUPPORTED; + break; + + case CAIRO_OPERATOR_ADD: /* 1 1 */ + if (source == SOURCE_TRANSPARENT) + return DO_NOTHING; + else + return DO_UNSUPPORTED; + break; + + case CAIRO_OPERATOR_MULTIPLY: + case CAIRO_OPERATOR_SCREEN: + case CAIRO_OPERATOR_OVERLAY: + case CAIRO_OPERATOR_DARKEN: + case CAIRO_OPERATOR_LIGHTEN: + case CAIRO_OPERATOR_COLOR_DODGE: + case CAIRO_OPERATOR_COLOR_BURN: + case CAIRO_OPERATOR_HARD_LIGHT: + case CAIRO_OPERATOR_SOFT_LIGHT: + case CAIRO_OPERATOR_DIFFERENCE: + case CAIRO_OPERATOR_EXCLUSION: + case CAIRO_OPERATOR_HSL_HUE: + case CAIRO_OPERATOR_HSL_SATURATION: + case CAIRO_OPERATOR_HSL_COLOR: + case CAIRO_OPERATOR_HSL_LUMINOSITY: + return DO_UNSUPPORTED; + + default: + ASSERT_NOT_REACHED; + return DO_UNSUPPORTED; + } +} + +static cairo_int_status_t +_cairo_win32_surface_fill_rectangles (void *abstract_surface, + cairo_operator_t op, + const cairo_color_t *color, + cairo_rectangle_int_t *rects, + int num_rects) +{ + cairo_win32_surface_t *surface = abstract_surface; + cairo_status_t status; + COLORREF new_color; + HBRUSH new_brush; + int i; + + /* XXXperf If it's not RGB24, we need to do a little more checking + * to figure out when we can use GDI. We don't have that checking + * anywhere at the moment, so just bail and use the fallback + * paths. */ + if (surface->format != CAIRO_FORMAT_RGB24) + return CAIRO_INT_STATUS_UNSUPPORTED; + + status = _cairo_win32_surface_set_clip_region (surface, NULL); + if (status) + return status; + + /* Optimize for no destination alpha (surface->pixman_image is non-NULL for all + * surfaces with alpha.) + */ + switch (categorize_solid_dest_operator (op, color->alpha_short)) { + case DO_CLEAR: + new_color = RGB (0, 0, 0); + break; + case DO_SOURCE: + new_color = RGB (color->red_short >> 8, color->green_short >> 8, color->blue_short >> 8); + break; + case DO_NOTHING: + return CAIRO_STATUS_SUCCESS; + case DO_UNSUPPORTED: + default: + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + new_brush = CreateSolidBrush (new_color); + if (!new_brush) + return _cairo_win32_print_gdi_error ("_cairo_win32_surface_fill_rectangles"); + + for (i = 0; i < num_rects; i++) { + RECT rect; + + rect.left = rects[i].x; + rect.top = rects[i].y; + rect.right = rects[i].x + rects[i].width; + rect.bottom = rects[i].y + rects[i].height; + + if (!FillRect (surface->dc, &rect, new_brush)) + goto FAIL; + } + + DeleteObject (new_brush); + + return CAIRO_STATUS_SUCCESS; + + FAIL: + status = _cairo_win32_print_gdi_error ("_cairo_win32_surface_fill_rectangles"); + + DeleteObject (new_brush); + + return status; +} + +cairo_bool_t +_cairo_win32_surface_get_extents (void *abstract_surface, + cairo_rectangle_int_t *rectangle) +{ + cairo_win32_surface_t *surface = abstract_surface; + + *rectangle = surface->extents; + return TRUE; +} + +static cairo_status_t +_cairo_win32_surface_flush (void *abstract_surface) +{ + return _cairo_win32_surface_set_clip_region (abstract_surface, NULL); +} + +#define STACK_GLYPH_SIZE 256 + +cairo_int_status_t +_cairo_win32_surface_show_glyphs_internal (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_bool_t glyph_indexing) +{ +#if CAIRO_HAS_WIN32_FONT + cairo_win32_surface_t *dst = surface; + + WORD glyph_buf_stack[STACK_GLYPH_SIZE]; + WORD *glyph_buf = glyph_buf_stack; + int dxy_buf_stack[2 * STACK_GLYPH_SIZE]; + int *dxy_buf = dxy_buf_stack; + + BOOL win_result = 0; + int i, j; + + cairo_solid_pattern_t *solid_pattern; + COLORREF color; + + cairo_matrix_t device_to_logical; + + int start_x, start_y; + double user_x, user_y; + int logical_x, logical_y; + unsigned int glyph_index_option; + + /* We can only handle win32 fonts */ + if (cairo_scaled_font_get_type (scaled_font) != CAIRO_FONT_TYPE_WIN32) + return CAIRO_INT_STATUS_UNSUPPORTED; + + /* We can only handle opaque solid color sources */ + if (!_cairo_pattern_is_opaque_solid(source)) + return CAIRO_INT_STATUS_UNSUPPORTED; + + /* We can only handle operator SOURCE or OVER with the destination + * having no alpha */ + if ((op != CAIRO_OPERATOR_SOURCE && op != CAIRO_OPERATOR_OVER) || + (dst->format != CAIRO_FORMAT_RGB24)) + return CAIRO_INT_STATUS_UNSUPPORTED; + + /* If we have a fallback mask clip set on the dst, we have + * to go through the fallback path, but only if we're not + * doing this for printing */ + if (clip != NULL) { + if ((dst->flags & CAIRO_WIN32_SURFACE_FOR_PRINTING) == 0) { + if (! _cairo_clip_is_region (clip)) + return CAIRO_INT_STATUS_UNSUPPORTED; + + _cairo_win32_surface_set_clip_region (surface, + _cairo_clip_get_region (clip)); + } + } + + solid_pattern = (cairo_solid_pattern_t *)source; + color = RGB(((int)solid_pattern->color.red_short) >> 8, + ((int)solid_pattern->color.green_short) >> 8, + ((int)solid_pattern->color.blue_short) >> 8); + + cairo_win32_scaled_font_get_device_to_logical(scaled_font, &device_to_logical); + + SaveDC(dst->dc); + + cairo_win32_scaled_font_select_font(scaled_font, dst->dc); + SetTextColor(dst->dc, color); + SetTextAlign(dst->dc, TA_BASELINE | TA_LEFT); + SetBkMode(dst->dc, TRANSPARENT); + + if (num_glyphs > STACK_GLYPH_SIZE) { + glyph_buf = (WORD *) _cairo_malloc_ab (num_glyphs, sizeof(WORD)); + dxy_buf = (int *) _cairo_malloc_abc (num_glyphs, sizeof(int), 2); + } + + /* It is vital that dx values for dxy_buf are calculated from the delta of + * _logical_ x coordinates (not user x coordinates) or else the sum of all + * previous dx values may start to diverge from the current glyph's x + * coordinate due to accumulated rounding error. As a result strings could + * be painted shorter or longer than expected. */ + + user_x = glyphs[0].x; + user_y = glyphs[0].y; + + cairo_matrix_transform_point(&device_to_logical, + &user_x, &user_y); + + logical_x = _cairo_lround (user_x); + logical_y = _cairo_lround (user_y); + + start_x = logical_x; + start_y = logical_y; + + for (i = 0, j = 0; i < num_glyphs; ++i, j = 2 * i) { + glyph_buf[i] = (WORD) glyphs[i].index; + if (i == num_glyphs - 1) { + dxy_buf[j] = 0; + dxy_buf[j+1] = 0; + } else { + double next_user_x = glyphs[i+1].x; + double next_user_y = glyphs[i+1].y; + int next_logical_x, next_logical_y; + + cairo_matrix_transform_point(&device_to_logical, + &next_user_x, &next_user_y); + + next_logical_x = _cairo_lround (next_user_x); + next_logical_y = _cairo_lround (next_user_y); + + dxy_buf[j] = _cairo_lround (next_logical_x - logical_x); + dxy_buf[j+1] = _cairo_lround (next_logical_y - logical_y); + + logical_x = next_logical_x; + logical_y = next_logical_y; + } + } + + if (glyph_indexing) + glyph_index_option = ETO_GLYPH_INDEX; + else + glyph_index_option = 0; + + win_result = ExtTextOutW(dst->dc, + start_x, + start_y, + glyph_index_option | ETO_PDY, + NULL, + glyph_buf, + num_glyphs, + dxy_buf); + if (!win_result) { + _cairo_win32_print_gdi_error("_cairo_win32_surface_show_glyphs(ExtTextOutW failed)"); + } + + RestoreDC(dst->dc, -1); + + if (glyph_buf != glyph_buf_stack) { + free(glyph_buf); + free(dxy_buf); + } + return (win_result) ? CAIRO_STATUS_SUCCESS : CAIRO_INT_STATUS_UNSUPPORTED; +#else + return CAIRO_INT_STATUS_UNSUPPORTED; +#endif +} + +#undef STACK_GLYPH_SIZE + +cairo_int_status_t +_cairo_win32_surface_show_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) +{ + return _cairo_win32_surface_show_glyphs_internal (surface, + op, + source, + glyphs, + num_glyphs, + scaled_font, + clip, + TRUE); +} + + +/** + * 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 + **/ +cairo_surface_t * +cairo_win32_surface_create (HDC hdc) +{ + cairo_win32_surface_t *surface; + + cairo_format_t format; + RECT rect; + + /* Assume that everything coming in as a HDC is RGB24 */ + format = CAIRO_FORMAT_RGB24; + + surface = malloc (sizeof (cairo_win32_surface_t)); + if (surface == NULL) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + + if (_cairo_win32_save_initial_clip (hdc, surface) != CAIRO_STATUS_SUCCESS) { + free (surface); + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + } + + surface->clip_region = NULL; + surface->image = NULL; + surface->format = format; + + surface->dc = hdc; + surface->bitmap = NULL; + surface->is_dib = FALSE; + surface->saved_dc_bitmap = NULL; + surface->brush = NULL; + surface->old_brush = NULL; + surface->font_subsets = NULL; + + GetClipBox(hdc, &rect); + surface->extents.x = rect.left; + surface->extents.y = rect.top; + surface->extents.width = rect.right - rect.left; + surface->extents.height = rect.bottom - rect.top; + + surface->flags = _cairo_win32_flags_for_dc (surface->dc); + + _cairo_surface_init (&surface->base, + &cairo_win32_surface_backend, + NULL, /* device */ + _cairo_content_from_format (format)); + + return &surface->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) +{ + return _cairo_win32_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_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_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_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_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 (cairo_surface_t*) new_surf; +} + +/** + * _cairo_surface_is_win32: + * @surface: a #cairo_surface_t + * + * Checks if a surface is a win32 surface. This will + * return False if this is a win32 printing surface; use + * _cairo_surface_is_win32_printing() to check for that. + * + * Return value: True if the surface is an win32 surface + **/ +int +_cairo_surface_is_win32 (cairo_surface_t *surface) +{ + return surface->backend == &cairo_win32_surface_backend; +} + +/** + * cairo_win32_surface_get_dc + * @surface: a #cairo_surface_t + * + * Returns the HDC associated with this surface, or %NULL if none. + * Also returns %NULL if the surface is not a win32 surface. + * + * A call to cairo_surface_flush() is required before using the HDC to + * ensure that all pending drawing operations are finished and to + * restore any temporary modification cairo has made to its state. A + * call to cairo_surface_mark_dirty() is required after the state or + * the content of the HDC has been modified. + * + * Return value: HDC or %NULL if no HDC available. + * + * Since: 1.2 + **/ +HDC +cairo_win32_surface_get_dc (cairo_surface_t *surface) +{ + if (_cairo_surface_is_win32 (surface)) + return ((cairo_win32_surface_t *) target)->dc; + + if (_cairo_surface_is_paginated (surface)) { + cairo_surface_t *target = _cairo_paginated_surface_get_target (surface); + if (_cairo_surface_is_win32_printing (target)) + return ((cairo_win32_surface_t *) target)->dc; + } + + return NULL; +} + +/** + * cairo_win32_surface_get_image + * @surface: a #cairo_surface_t + * + * Returns a #cairo_surface_t image surface that refers to the same bits + * as the DIB of the Win32 surface. If the passed-in win32 surface + * is not a DIB surface, %NULL is returned. + * + * Return value: a #cairo_surface_t (owned by the win32 #cairo_surface_t), + * or %NULL if the win32 surface is not a DIB. + * + * Since: 1.4 + */ +cairo_surface_t * +cairo_win32_surface_get_image (cairo_surface_t *surface) +{ + if (!_cairo_surface_is_win32(surface)) + return NULL; + + return ((cairo_win32_surface_t*)surface)->image; +} + +static const cairo_surface_backend_t cairo_win32_surface_backend = { + CAIRO_SURFACE_TYPE_WIN32, + _cairo_win32_surface_finish, + + _cairo_default_context_create, + + _cairo_win32_surface_create_similar, + NULL, + _cairo_win32_surface_map_to_image, + _cairo_win32_surface_unmap_image, + + _cairo_surface_default_source, + _cairo_win32_surface_acquire_source_image, + _cairo_win32_surface_release_source_image, + NULL, /* snapshot */ + + NULL, /* copy_page */ + NULL, /* show_page */ + + _cairo_win32_surface_get_extents, + NULL, /* get_font_options */ + + _cairo_win32_surface_flush, + NULL, /* mark_dirty_rectangle */ + + NULL, /* paint */ + NULL, /* mask */ + NULL, /* stroke */ + NULL, /* fill */ + NULL, /* fill/stroke */ + _cairo_win32_surface_show_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_int_status_t +_cairo_win32_save_initial_clip (HDC hdc, cairo_win32_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 ("cairo_win32_surface_create"); + SetGraphicsMode (hdc, gm); + /* XXX: Can we make a more reasonable guess at the error cause here? */ + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } + + surface->clip_rect.x = rect.left; + surface->clip_rect.y = rect.top; + surface->clip_rect.width = rect.right - rect.left; + surface->clip_rect.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_int_status_t +_cairo_win32_restore_initial_clip (cairo_win32_surface_t *surface) +{ + cairo_int_status_t status = CAIRO_STATUS_SUCCESS; + + XFORM saved_xform; + int gm = GetGraphicsMode (surface->dc); + if (gm == GM_ADVANCED) { + GetWorldTransform (surface->dc, &saved_xform); + ModifyWorldTransform (surface->dc, NULL, MWT_IDENTITY); + } + + /* initial_clip_rgn will either be a real region or NULL (which means reset to no clip region) */ + SelectClipRgn (surface->dc, surface->initial_clip_rgn); + + if (surface->had_simple_clip) { + /* then if we had a simple clip, intersect */ + IntersectClipRect (surface->dc, + surface->clip_rect.x, + surface->clip_rect.y, + surface->clip_rect.x + surface->clip_rect.width, + surface->clip_rect.y + surface->clip_rect.height); + } + + if (gm == GM_ADVANCED) + SetWorldTransform (surface->dc, &saved_xform); + + return status; +} + +void +_cairo_win32_debug_dump_hrgn (HRGN rgn, char *header) +{ + RGNDATA *rd; + unsigned int z; + + if (header) + fprintf (stderr, "%s\n", header); + + if (rgn == NULL) { + fprintf (stderr, " NULL\n"); + } + + z = GetRegionData(rgn, 0, NULL); + rd = (RGNDATA*) malloc(z); + z = GetRegionData(rgn, z, rd); + + fprintf (stderr, " %ld rects, bounds: %ld %ld %ld %ld\n", + rd->rdh.nCount, + rd->rdh.rcBound.left, + rd->rdh.rcBound.top, + rd->rdh.rcBound.right - rd->rdh.rcBound.left, + rd->rdh.rcBound.bottom - rd->rdh.rcBound.top); + + for (z = 0; z < rd->rdh.nCount; z++) { + RECT r = ((RECT*)rd->Buffer)[z]; + fprintf (stderr, " [%d]: [%ld %ld %ld %ld]\n", + z, r.left, r.top, r.right - r.left, r.bottom - r.top); + } + + free(rd); + fflush (stderr); +} |