diff options
author | Adrian Johnson <ajohnson@redneon.com> | 2022-03-05 03:54:58 +0000 |
---|---|---|
committer | Adrian Johnson <ajohnson@redneon.com> | 2022-03-05 03:54:58 +0000 |
commit | 135327c112fafa6300c140f46c7ce1a346e49515 (patch) | |
tree | 531d1685956f550be2ef29c66ccaa9cfa7a9b56c | |
parent | 1fee5ac98590c9121890e44960a65928293ecaaf (diff) | |
parent | fa3a1bca7d4cf04bf5002a3e0e886dffe29ccfab (diff) | |
download | cairo-135327c112fafa6300c140f46c7ce1a346e49515.tar.gz |
Merge branch 'win32-color-fonts' into 'master'
Windows color fonts
See merge request cairo/cairo!244
-rw-r--r-- | meson.build | 32 | ||||
-rw-r--r-- | src/cairo-atomic-private.h | 2 | ||||
-rw-r--r-- | src/cairo-device.c | 1 | ||||
-rw-r--r-- | src/cairo-error-private.h | 1 | ||||
-rw-r--r-- | src/cairo-misc.c | 2 | ||||
-rw-r--r-- | src/cairo-region.c | 1 | ||||
-rw-r--r-- | src/cairo-spans.c | 2 | ||||
-rw-r--r-- | src/cairo-surface.c | 1 | ||||
-rw-r--r-- | src/cairo-win32.h | 10 | ||||
-rw-r--r-- | src/cairo.c | 4 | ||||
-rw-r--r-- | src/cairo.h | 6 | ||||
-rw-r--r-- | src/cairoint.h | 14 | ||||
-rw-r--r-- | src/meson.build | 3 | ||||
-rw-r--r-- | src/win32/cairo-dwrite-font.cpp | 1758 | ||||
-rw-r--r-- | src/win32/cairo-dwrite-private.hpp | 266 | ||||
-rw-r--r-- | src/win32/cairo-win32-display-surface.c | 2 | ||||
-rw-r--r-- | src/win32/cairo-win32-font.c | 6 | ||||
-rw-r--r-- | src/win32/cairo-win32-printing-surface.c | 37 | ||||
-rw-r--r-- | src/win32/cairo-win32-private.h | 30 | ||||
-rw-r--r-- | src/win32/cairo-win32-refptr.hpp | 178 | ||||
-rw-r--r-- | src/win32/cairo-win32-surface.c | 13 | ||||
-rw-r--r-- | src/win32/d2d1-extra.h | 111 | ||||
-rw-r--r-- | src/win32/dw-extra.h | 78 |
23 files changed, 2546 insertions, 12 deletions
diff --git a/meson.build b/meson.build index 75a7bdfa7..d7dd9b428 100644 --- a/meson.build +++ b/meson.build @@ -1,4 +1,4 @@ -project('cairo', 'c', +project('cairo', 'c', 'cpp', meson_version: '>= 0.56.0', version: run_command(find_program('version.py'), check: true).stdout().strip(), default_options: ['warning_level=2'], @@ -501,6 +501,35 @@ if host_machine.system() == 'windows' 'deps': win32_extra_deps, } ] + + cpp_compiler = meson.get_compiler('cpp') + d2d_dep = cpp_compiler.find_library('d2d1', required: false) + dwrite_dep = cpp_compiler.find_library('dwrite', required: false) + d2d_header = cpp_compiler.has_header('d2d1.h') + d2d_3_header = cpp_compiler.has_header('d2d1_3.h') + dwrite_header = cpp_compiler.has_header('dwrite.h') + dwrite_3_header = cpp_compiler.has_header('dwrite_3.h') + wincodec_dep = cpp_compiler.find_library('windowscodecs', required: false) + wincodec_header = cpp_compiler.has_header('wincodec.h') + + if d2d_dep.found() and dwrite_dep.found() and d2d_header and dwrite_header and wincodec_dep.found() and wincodec_header + feature_conf.set('CAIRO_HAS_DWRITE_FONT', 1) + built_features += [{ + 'name': 'cairo-win32-dwrite-font', + 'description': 'Microsoft Windows DWrite font backend', + 'deps': [dwrite_dep, d2d_dep, wincodec_dep], + }] + deps += [dwrite_dep, d2d_dep, wincodec_dep] + + if cpp_compiler.has_header('d2d1_3.h') + conf.set('HAVE_D2D1_3_H', 1) + endif + + # Exclude MinGW dwrite_3.h because it has a broken definition of DWRITE_COLOR_GLYPH_RUN1. + if cpp_compiler.has_header('dwrite_3.h') and cpp_compiler.get_define('__MINGW32__') == '' + conf.set('HAVE_DWRITE_3_H', 1) + endif + endif endif # GL / GLESV2 / GLESV3 are mutually exclusive @@ -941,6 +970,7 @@ summary({ 'FreeType': feature_conf.get('CAIRO_HAS_FT_FONT', 0) == 1, 'Fontconfig': feature_conf.get('CAIRO_HAS_FC_FONT', 0) == 1, 'Win32': feature_conf.get('CAIRO_HAS_WIN32_FONT', 0) == 1, + 'Win32 DWrite': feature_conf.get('CAIRO_HAS_DWRITE_FONT', 0) == 1, 'Quartz': feature_conf.get('CAIRO_HAS_QUARTZ_FONT', 0) == 1, }, section: 'Font Backends', bool_yn: true) diff --git a/src/cairo-atomic-private.h b/src/cairo-atomic-private.h index d23192b41..a9eb38a7f 100644 --- a/src/cairo-atomic-private.h +++ b/src/cairo-atomic-private.h @@ -313,7 +313,7 @@ _cairo_atomic_int_cmpxchg (cairo_atomic_int_t *x, cairo_atomic_int_t oldv, cairo_atomic_int_t newv) { - return InterlockedCompareExchange (x, newv, oldv) == oldv; + return InterlockedCompareExchange ((unsigned int*)x, (unsigned int)newv, (unsigned int)oldv) == oldv; } static cairo_always_inline void * diff --git a/src/cairo-device.c b/src/cairo-device.c index 965c84c65..50e7ee484 100644 --- a/src/cairo-device.c +++ b/src/cairo-device.c @@ -163,6 +163,7 @@ _cairo_device_create_in_error (cairo_status_t status) case CAIRO_STATUS_FREETYPE_ERROR: case CAIRO_STATUS_WIN32_GDI_ERROR: case CAIRO_STATUS_TAG_ERROR: + case CAIRO_STATUS_DWRITE_ERROR: default: _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); return (cairo_device_t *) &_nil_device; diff --git a/src/cairo-error-private.h b/src/cairo-error-private.h index d8cc7c004..d84d4c23d 100644 --- a/src/cairo-error-private.h +++ b/src/cairo-error-private.h @@ -98,6 +98,7 @@ enum _cairo_int_status { CAIRO_INT_STATUS_FREETYPE_ERROR, CAIRO_INT_STATUS_WIN32_GDI_ERROR, CAIRO_INT_STATUS_TAG_ERROR, + CAIRO_INT_STATUS_DWRITE_ERROR, CAIRO_INT_STATUS_LAST_STATUS, diff --git a/src/cairo-misc.c b/src/cairo-misc.c index 3dc1570b5..bf8a62730 100644 --- a/src/cairo-misc.c +++ b/src/cairo-misc.c @@ -174,6 +174,8 @@ cairo_status_to_string (cairo_status_t status) return "error occurred in the Windows Graphics Device Interface"; case CAIRO_STATUS_TAG_ERROR: return "invalid tag name, attributes, or nesting"; + case CAIRO_STATUS_DWRITE_ERROR: + return "Window Direct Write error"; default: case CAIRO_STATUS_LAST_STATUS: return "<unknown error status>"; diff --git a/src/cairo-region.c b/src/cairo-region.c index c1d35e174..eb78cf4a8 100644 --- a/src/cairo-region.c +++ b/src/cairo-region.c @@ -111,6 +111,7 @@ _cairo_region_create_in_error (cairo_status_t status) case CAIRO_STATUS_FREETYPE_ERROR: case CAIRO_STATUS_WIN32_GDI_ERROR: case CAIRO_STATUS_TAG_ERROR: + case CAIRO_STATUS_DWRITE_ERROR: default: _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); return (cairo_region_t *) &_cairo_region_nil; diff --git a/src/cairo-spans.c b/src/cairo-spans.c index 59452c0ba..1b46adf4d 100644 --- a/src/cairo-spans.c +++ b/src/cairo-spans.c @@ -132,6 +132,7 @@ _cairo_scan_converter_create_in_error (cairo_status_t status) case CAIRO_STATUS_FREETYPE_ERROR: case CAIRO_STATUS_WIN32_GDI_ERROR: case CAIRO_STATUS_TAG_ERROR: + case CAIRO_STATUS_DWRITE_ERROR: default: break; } @@ -249,6 +250,7 @@ _cairo_span_renderer_create_in_error (cairo_status_t status) case CAIRO_STATUS_FREETYPE_ERROR: RETURN_NIL; case CAIRO_STATUS_WIN32_GDI_ERROR: RETURN_NIL; case CAIRO_STATUS_TAG_ERROR: RETURN_NIL; + case CAIRO_STATUS_DWRITE_ERROR: RETURN_NIL; default: break; } diff --git a/src/cairo-surface.c b/src/cairo-surface.c index 46f39cb20..2fc265a2c 100644 --- a/src/cairo-surface.c +++ b/src/cairo-surface.c @@ -3073,6 +3073,7 @@ _cairo_surface_create_in_error (cairo_status_t status) case CAIRO_STATUS_PNG_ERROR: case CAIRO_STATUS_FREETYPE_ERROR: case CAIRO_STATUS_WIN32_GDI_ERROR: + case CAIRO_INT_STATUS_DWRITE_ERROR: case CAIRO_STATUS_TAG_ERROR: default: _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); diff --git a/src/cairo-win32.h b/src/cairo-win32.h index db4cac69f..4f257d7ed 100644 --- a/src/cairo-win32.h +++ b/src/cairo-win32.h @@ -107,6 +107,16 @@ cairo_win32_scaled_font_get_device_to_logical (cairo_scaled_font_t *scaled_font, #endif /* CAIRO_HAS_WIN32_FONT */ +#if CAIRO_HAS_DWRITE_FONT + +/* + * Win32 DirectWrite font support + */ +cairo_public cairo_font_face_t * +cairo_dwrite_font_face_create_for_dwrite_fontface (void *dwrite_font_face); + +#endif /* CAIRO_HAS_DWRITE_FONT */ + CAIRO_END_DECLS #else /* CAIRO_HAS_WIN32_SURFACE */ diff --git a/src/cairo.c b/src/cairo.c index d141b56d2..f55429405 100644 --- a/src/cairo.c +++ b/src/cairo.c @@ -371,8 +371,8 @@ static const cairo_t _cairo_nil[] = { DEFINE_NIL_CONTEXT (CAIRO_STATUS_PNG_ERROR), DEFINE_NIL_CONTEXT (CAIRO_STATUS_FREETYPE_ERROR), DEFINE_NIL_CONTEXT (CAIRO_STATUS_WIN32_GDI_ERROR), - DEFINE_NIL_CONTEXT (CAIRO_STATUS_TAG_ERROR) - + DEFINE_NIL_CONTEXT (CAIRO_STATUS_TAG_ERROR), + DEFINE_NIL_CONTEXT (CAIRO_STATUS_DWRITE_ERROR) }; COMPILE_TIME_ASSERT (ARRAY_LENGTH (_cairo_nil) == CAIRO_STATUS_LAST_STATUS - 1); diff --git a/src/cairo.h b/src/cairo.h index fb1da4146..32ffc6b3d 100644 --- a/src/cairo.h +++ b/src/cairo.h @@ -296,6 +296,7 @@ typedef struct _cairo_user_data_key { * @CAIRO_STATUS_FREETYPE_ERROR: error occurred in libfreetype (Since 1.16) * @CAIRO_STATUS_WIN32_GDI_ERROR: error occurred in the Windows Graphics Device Interface (Since 1.16) * @CAIRO_STATUS_TAG_ERROR: invalid tag name, attributes, or nesting (Since 1.16) + * @CAIRO_STATUS_DWRITE_ERROR: error occurred in the Windows Direct Write API (Since 1.18) * @CAIRO_STATUS_LAST_STATUS: this is a special value indicating the number of * status values defined in this enumeration. When using this value, note * that the version of cairo at run-time may have additional status values @@ -356,6 +357,7 @@ typedef enum _cairo_status { CAIRO_STATUS_FREETYPE_ERROR, CAIRO_STATUS_WIN32_GDI_ERROR, CAIRO_STATUS_TAG_ERROR, + CAIRO_STATUS_DWRITE_ERROR, CAIRO_STATUS_LAST_STATUS } cairo_status_t; @@ -1545,6 +1547,7 @@ cairo_font_face_status (cairo_font_face_t *font_face); * @CAIRO_FONT_TYPE_QUARTZ: The font is of type Quartz (Since: 1.6, in 1.2 and * 1.4 it was named CAIRO_FONT_TYPE_ATSUI) * @CAIRO_FONT_TYPE_USER: The font was create using cairo's user font api (Since: 1.8) + * @CAIRO_FONT_TYPE_DWRITE: The font is of type Win32 DWrite (Since: 1.18) * * #cairo_font_type_t is used to describe the type of a given font * face or scaled font. The font types are also known as "font @@ -1581,7 +1584,8 @@ typedef enum _cairo_font_type { CAIRO_FONT_TYPE_FT, CAIRO_FONT_TYPE_WIN32, CAIRO_FONT_TYPE_QUARTZ, - CAIRO_FONT_TYPE_USER + CAIRO_FONT_TYPE_USER, + CAIRO_FONT_TYPE_DWRITE } cairo_font_type_t; cairo_public cairo_font_type_t diff --git a/src/cairoint.h b/src/cairoint.h index 80b695feb..cfa77ddbb 100644 --- a/src/cairoint.h +++ b/src/cairoint.h @@ -677,6 +677,12 @@ extern const cairo_private struct _cairo_font_face_backend _cairo_win32_font_fac #endif +#if CAIRO_HAS_DWRITE_FONT + +extern const cairo_private struct _cairo_font_face_backend _cairo_dwrite_font_face_backend; + +#endif + #if CAIRO_HAS_QUARTZ_FONT extern const cairo_private struct _cairo_font_face_backend _cairo_quartz_font_face_backend; @@ -705,11 +711,17 @@ struct _cairo_surface_attributes { #define CAIRO_FONT_WEIGHT_DEFAULT CAIRO_FONT_WEIGHT_NORMAL #define CAIRO_WIN32_FONT_FAMILY_DEFAULT "Arial" +#define CAIRO_DWRITE_FONT_FAMILY_DEFAULT "Arial" #define CAIRO_QUARTZ_FONT_FAMILY_DEFAULT "Helvetica" #define CAIRO_FT_FONT_FAMILY_DEFAULT "" #define CAIRO_USER_FONT_FAMILY_DEFAULT "@cairo:" -#if CAIRO_HAS_WIN32_FONT +#if CAIRO_HAS_DWRITE_FONT + +#define CAIRO_FONT_FAMILY_DEFAULT CAIRO_DWRITE_FONT_FAMILY_DEFAULT +#define CAIRO_FONT_FACE_BACKEND_DEFAULT &_cairo_dwrite_font_face_backend + +#elif CAIRO_HAS_WIN32_FONT #define CAIRO_FONT_FAMILY_DEFAULT CAIRO_WIN32_FONT_FAMILY_DEFAULT #define CAIRO_FONT_FACE_BACKEND_DEFAULT &_cairo_win32_font_face_backend diff --git a/src/meson.build b/src/meson.build index df8764649..2b37fbc0f 100644 --- a/src/meson.build +++ b/src/meson.build @@ -177,6 +177,9 @@ cairo_feature_sources = { 'cairo-win32-font': [ 'win32/cairo-win32-font.c', ], + 'cairo-win32-dwrite-font': [ + 'win32/cairo-dwrite-font.cpp', + ], 'cairo-gl': [ 'cairo-gl-composite.c', 'cairo-gl-device.c', diff --git a/src/win32/cairo-dwrite-font.cpp b/src/win32/cairo-dwrite-font.cpp new file mode 100644 index 000000000..9c21eebba --- /dev/null +++ b/src/win32/cairo-dwrite-font.cpp @@ -0,0 +1,1758 @@ +/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */ +/* Cairo - a vector graphics library with display and print output + * + * Copyright © 2010 Mozilla Foundation + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 the Mozilla Foundation + * + * Contributor(s): + * Bas Schouten <bschouten@mozilla.com> + */ + +#include "cairoint.h" + +#include "cairo-win32-private.h" +#include "cairo-pattern-private.h" +#include "cairo-surface-private.h" +#include "cairo-image-surface-private.h" +#include "cairo-clip-private.h" +#include "cairo-win32-refptr.hpp" +#include "cairo-dwrite-private.hpp" +#include "cairo-truetype-subset-private.h" +#include <float.h> + +#include <wincodec.h> + +typedef HRESULT (WINAPI*D2D1CreateFactoryFunc)( + D2D1_FACTORY_TYPE factoryType, + REFIID iid, + CONST D2D1_FACTORY_OPTIONS *pFactoryOptions, + void **factory +); + +#define CAIRO_INT_STATUS_SUCCESS (cairo_int_status_t)CAIRO_STATUS_SUCCESS + +// Forward declarations +cairo_int_status_t +_dwrite_draw_glyphs_to_gdi_surface_d2d(cairo_win32_surface_t *surface, + DWRITE_MATRIX *transform, + DWRITE_GLYPH_RUN *run, + COLORREF color, + const RECT &area); + +cairo_int_status_t +_dwrite_draw_glyphs_to_gdi_surface_gdi(cairo_win32_surface_t *surface, + DWRITE_MATRIX *transform, + DWRITE_GLYPH_RUN *run, + COLORREF color, + cairo_dwrite_scaled_font_t *scaled_font, + const RECT &area); + +/** + * _cairo_dwrite_error: + * @hr HRESULT code + * @context: context string to display along with the error + * + * Helper function to print a human readable form a HRESULT. + * + * Return value: A cairo status code for the error code + **/ +cairo_int_status_t +_cairo_dwrite_error (HRESULT hr, const char *context) +{ + void *lpMsgBuf; + + if (!FormatMessageW (FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM, + NULL, + hr, + MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPWSTR) &lpMsgBuf, + 0, NULL)) { + fprintf (stderr, "%s: Unknown DWrite error HRESULT=0x%08lx\n", context, (unsigned long)hr); + } else { + fprintf (stderr, "%s: %S\n", context, (wchar_t *)lpMsgBuf); + LocalFree (lpMsgBuf); + } + fflush (stderr); + + return (cairo_int_status_t)_cairo_error (CAIRO_STATUS_DWRITE_ERROR); +} + +class D2DFactory +{ +public: + static ID2D1Factory *Instance() + { + if (!mFactoryInstance) { +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wcast-function-type" +#endif + D2D1CreateFactoryFunc createD2DFactory = (D2D1CreateFactoryFunc) + GetProcAddress(LoadLibraryW(L"d2d1.dll"), "D2D1CreateFactory"); +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif + if (createD2DFactory) { + D2D1_FACTORY_OPTIONS options; + options.debugLevel = D2D1_DEBUG_LEVEL_NONE; + createD2DFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, + __uuidof(ID2D1Factory), + &options, + (void**)&mFactoryInstance); + } + } + return mFactoryInstance; + } + + static IDWriteFactory4 *Instance4() + { + if (!mFactoryInstance4) { + if (Instance()) { + Instance()->QueryInterface(&mFactoryInstance4); + } + } + return mFactoryInstance4; + } + + static ID2D1DCRenderTarget *RenderTarget() + { + if (!mRenderTarget) { + if (!Instance()) { + return NULL; + } + // Create a DC render target. + D2D1_RENDER_TARGET_PROPERTIES props = D2D1::RenderTargetProperties( + D2D1_RENDER_TARGET_TYPE_DEFAULT, + D2D1::PixelFormat( + DXGI_FORMAT_B8G8R8A8_UNORM, + D2D1_ALPHA_MODE_PREMULTIPLIED), + 0, + 0, + D2D1_RENDER_TARGET_USAGE_NONE, + D2D1_FEATURE_LEVEL_DEFAULT + ); + + Instance()->CreateDCRenderTarget(&props, &mRenderTarget); + } + return mRenderTarget; + } + +private: + static ID2D1Factory *mFactoryInstance; + static IDWriteFactory4 *mFactoryInstance4; + static ID2D1DCRenderTarget *mRenderTarget; +}; + +class WICImagingFactory +{ +public: + static IWICImagingFactory *Instance() + { + if (!mFactoryInstance) { + CoInitialize(NULL); + CoCreateInstance(CLSID_WICImagingFactory, + NULL, + CLSCTX_INPROC_SERVER, + IID_PPV_ARGS(&mFactoryInstance)); + } + return mFactoryInstance; + } +private: + static IWICImagingFactory *mFactoryInstance; +}; + + +IDWriteFactory *DWriteFactory::mFactoryInstance = NULL; +IDWriteFactory4 *DWriteFactory::mFactoryInstance4 = NULL; + +IWICImagingFactory *WICImagingFactory::mFactoryInstance = NULL; +IDWriteFontCollection *DWriteFactory::mSystemCollection = NULL; +IDWriteRenderingParams *DWriteFactory::mDefaultRenderingParams = NULL; +IDWriteRenderingParams *DWriteFactory::mCustomClearTypeRenderingParams = NULL; +IDWriteRenderingParams *DWriteFactory::mForceGDIClassicRenderingParams = NULL; +FLOAT DWriteFactory::mGamma = -1.0; +FLOAT DWriteFactory::mEnhancedContrast = -1.0; +FLOAT DWriteFactory::mClearTypeLevel = -1.0; +int DWriteFactory::mPixelGeometry = -1; +int DWriteFactory::mRenderingMode = -1; + +ID2D1Factory *D2DFactory::mFactoryInstance = NULL; +ID2D1DCRenderTarget *D2DFactory::mRenderTarget = NULL; + +/* Functions #cairo_font_face_backend_t */ +static cairo_status_t +_cairo_dwrite_font_face_create_for_toy (cairo_toy_font_face_t *toy_face, + cairo_font_face_t **font_face); +static cairo_bool_t +_cairo_dwrite_font_face_destroy (void *font_face); + +static cairo_status_t +_cairo_dwrite_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); + +const cairo_font_face_backend_t _cairo_dwrite_font_face_backend = { + CAIRO_FONT_TYPE_DWRITE, + _cairo_dwrite_font_face_create_for_toy, + _cairo_dwrite_font_face_destroy, + _cairo_dwrite_font_face_scaled_font_create +}; + +/* Functions #cairo_scaled_font_backend_t */ + +void _cairo_dwrite_scaled_font_fini(void *scaled_font); + +static cairo_warn cairo_int_status_t +_cairo_dwrite_scaled_glyph_init(void *scaled_font, + cairo_scaled_glyph_t *scaled_glyph, + cairo_scaled_glyph_info_t info, + const cairo_color_t *foreground_color); + +cairo_int_status_t +_cairo_dwrite_load_truetype_table(void *scaled_font, + unsigned long tag, + long offset, + unsigned char *buffer, + unsigned long *length); + +unsigned long +_cairo_dwrite_ucs4_to_index(void *scaled_font, + uint32_t ucs4); + +static cairo_bool_t +_cairo_dwrite_has_color_glyphs(void *scaled_font); + +const cairo_scaled_font_backend_t _cairo_dwrite_scaled_font_backend = { + CAIRO_FONT_TYPE_DWRITE, + _cairo_dwrite_scaled_font_fini, + _cairo_dwrite_scaled_glyph_init, + NULL, /* text_to_glyphs */ + _cairo_dwrite_ucs4_to_index, + _cairo_dwrite_load_truetype_table, + NULL, /* index_to_ucs4 */ + NULL, /* is_synthetic */ + NULL, /* index_to_glyph_name */ + NULL, /* load_type1_data */ + _cairo_dwrite_has_color_glyphs +}; + +/* Helper conversion functions */ + + +/** + * _cairo_dwrite_matrix_from_matrix: + * Get a DirectWrite matrix from a cairo matrix. Note that DirectWrite uses row + * vectors where cairo uses column vectors. Hence the transposition. + * + * \param Cairo matrix + * \return DirectWrite matrix + **/ +DWRITE_MATRIX +_cairo_dwrite_matrix_from_matrix(const cairo_matrix_t *matrix) +{ + DWRITE_MATRIX dwmat; + dwmat.m11 = (FLOAT)matrix->xx; + dwmat.m12 = (FLOAT)matrix->yx; + dwmat.m21 = (FLOAT)matrix->xy; + dwmat.m22 = (FLOAT)matrix->yy; + dwmat.dx = (FLOAT)matrix->x0; + dwmat.dy = (FLOAT)matrix->y0; + return dwmat; +} + +/* Helper functions for cairo_dwrite_scaled_glyph_init() */ +cairo_int_status_t +_cairo_dwrite_scaled_font_init_glyph_metrics + (cairo_dwrite_scaled_font_t *scaled_font, cairo_scaled_glyph_t *scaled_glyph); + +static cairo_int_status_t +_cairo_dwrite_scaled_font_init_glyph_color_surface(cairo_dwrite_scaled_font_t *scaled_font, + cairo_scaled_glyph_t *scaled_glyph, + const cairo_color_t *foreground_color); + +cairo_int_status_t +_cairo_dwrite_scaled_font_init_glyph_surface + (cairo_dwrite_scaled_font_t *scaled_font, cairo_scaled_glyph_t *scaled_glyph); + +cairo_int_status_t +_cairo_dwrite_scaled_font_init_glyph_path + (cairo_dwrite_scaled_font_t *scaled_font, cairo_scaled_glyph_t *scaled_glyph); + +/* implement the font backend interface */ + +static cairo_status_t +_cairo_dwrite_font_face_create_for_toy (cairo_toy_font_face_t *toy_face, + cairo_font_face_t **font_face) +{ + WCHAR *face_name; + int face_name_len; + + if (!DWriteFactory::Instance()) { + return (cairo_status_t)CAIRO_INT_STATUS_UNSUPPORTED; + } + + face_name_len = MultiByteToWideChar(CP_UTF8, 0, toy_face->family, -1, NULL, 0); + face_name = new WCHAR[face_name_len]; + MultiByteToWideChar(CP_UTF8, 0, toy_face->family, -1, face_name, face_name_len); + + IDWriteFontFamily *family = DWriteFactory::FindSystemFontFamily(face_name); + delete face_name; + if (!family) { + *font_face = (cairo_font_face_t*)&_cairo_font_face_nil; + return CAIRO_STATUS_FONT_TYPE_MISMATCH; + } + + DWRITE_FONT_WEIGHT weight; + switch (toy_face->weight) { + case CAIRO_FONT_WEIGHT_BOLD: + weight = DWRITE_FONT_WEIGHT_BOLD; + break; + case CAIRO_FONT_WEIGHT_NORMAL: + default: + weight = DWRITE_FONT_WEIGHT_NORMAL; + break; + } + + DWRITE_FONT_STYLE style; + switch (toy_face->slant) { + case CAIRO_FONT_SLANT_ITALIC: + style = DWRITE_FONT_STYLE_ITALIC; + break; + case CAIRO_FONT_SLANT_OBLIQUE: + style = DWRITE_FONT_STYLE_OBLIQUE; + break; + case CAIRO_FONT_SLANT_NORMAL: + default: + style = DWRITE_FONT_STYLE_NORMAL; + break; + } + + IDWriteFont *font; + HRESULT hr = family->GetFirstMatchingFont(weight, DWRITE_FONT_STRETCH_NORMAL, style, &font); + if (SUCCEEDED(hr)) { + IDWriteFontFace *dwriteface; + hr = font->CreateFontFace(&dwriteface); + if (SUCCEEDED(hr)) { + *font_face = cairo_dwrite_font_face_create_for_dwrite_fontface(dwriteface); + return CAIRO_STATUS_SUCCESS; + } + } + return (cairo_status_t)CAIRO_INT_STATUS_UNSUPPORTED; +} + +static cairo_bool_t +_cairo_dwrite_font_face_destroy (void *font_face) +{ + cairo_dwrite_font_face_t *dwrite_font_face = static_cast<cairo_dwrite_font_face_t*>(font_face); + if (dwrite_font_face->dwriteface) + dwrite_font_face->dwriteface->Release(); + return TRUE; +} + + +static inline unsigned short +read_short(const char *buf) +{ + return be16_to_cpu(*(unsigned short*)buf); +} + +void +_cairo_dwrite_glyph_run_from_glyphs(cairo_glyph_t *glyphs, + int num_glyphs, + cairo_dwrite_scaled_font_t *scaled_font, + AutoDWriteGlyphRun *run, + cairo_bool_t *transformed) +{ + run->allocate(num_glyphs); + + UINT16 *indices = const_cast<UINT16*>(run->glyphIndices); + FLOAT *advances = const_cast<FLOAT*>(run->glyphAdvances); + DWRITE_GLYPH_OFFSET *offsets = const_cast<DWRITE_GLYPH_OFFSET*>(run->glyphOffsets); + + cairo_dwrite_font_face_t *dwriteff = reinterpret_cast<cairo_dwrite_font_face_t*>(scaled_font->base.font_face); + + run->bidiLevel = 0; + run->fontFace = dwriteff->dwriteface; + run->glyphCount = num_glyphs; + run->isSideways = FALSE; + + if (scaled_font->mat.xy == 0 && scaled_font->mat.yx == 0 && + scaled_font->mat.xx == scaled_font->base.font_matrix.xx && + scaled_font->mat.yy == scaled_font->base.font_matrix.yy) { + // Fast route, don't actually use a transform but just + // set the correct font size. + *transformed = 0; + + run->fontEmSize = (FLOAT)scaled_font->base.font_matrix.yy; + + for (int i = 0; i < num_glyphs; i++) { + indices[i] = (WORD) glyphs[i].index; + + offsets[i].ascenderOffset = -(FLOAT)(glyphs[i].y); + offsets[i].advanceOffset = (FLOAT)(glyphs[i].x); + advances[i] = 0.0; + } + } else { + *transformed = 1; + // Transforming positions by the inverse matrix, then by the original + // matrix later may introduce small errors, especially because the + // D2D matrix is single-precision whereas the cairo one is double. + // This is a problem when glyph positions were originally at exactly + // half-pixel locations, which eventually round to whole pixels for + // GDI rendering - the errors introduced here cause them to round in + // unpredictable directions, instead of all rounding in a consistent + // way, leading to poor glyph spacing (bug 675383). + // To mitigate this, nudge the positions by a tiny amount to try and + // ensure that after the two transforms, they'll still round in a + // consistent direction. + const double EPSILON = 0.0001; + for (int i = 0; i < num_glyphs; i++) { + indices[i] = (WORD) glyphs[i].index; + double x = glyphs[i].x + EPSILON; + double y = glyphs[i].y; + cairo_matrix_transform_point(&scaled_font->mat_inverse, &x, &y); + // Since we will multiply by our ctm matrix later for rotation effects + // and such, adjust positions by the inverse matrix now. Y-axis is + // inverted! Therefor the offset is -y. + offsets[i].ascenderOffset = -(FLOAT)y; + offsets[i].advanceOffset = (FLOAT)x; + advances[i] = 0.0; + } + // The font matrix takes care of the scaling if we have a transform, + // emSize should be 1. + run->fontEmSize = 1.0f; + } +} + +#define GASP_TAG 0x70736167 +#define GASP_DOGRAY 0x2 + +static cairo_bool_t +do_grayscale(IDWriteFontFace *dwface, unsigned int ppem) +{ + void *tableContext; + char *tableData; + UINT32 tableSize; + BOOL exists; + dwface->TryGetFontTable(GASP_TAG, (const void**)&tableData, &tableSize, &tableContext, &exists); + + if (exists) { + if (tableSize < 4) { + dwface->ReleaseFontTable(tableContext); + return true; + } + struct gaspRange { + unsigned short maxPPEM; // Stored big-endian + unsigned short behavior; // Stored big-endian + }; + unsigned short numRanges = read_short(tableData + 2); + if (tableSize < (UINT)4 + numRanges * 4) { + dwface->ReleaseFontTable(tableContext); + return true; + } + gaspRange *ranges = (gaspRange *)(tableData + 4); + for (int i = 0; i < numRanges; i++) { + if (be16_to_cpu(ranges[i].maxPPEM) > ppem) { + if (!(be16_to_cpu(ranges[i].behavior) & GASP_DOGRAY)) { + dwface->ReleaseFontTable(tableContext); + return false; + } + break; + } + } + dwface->ReleaseFontTable(tableContext); + } + return true; +} + +static cairo_status_t +_cairo_dwrite_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) +{ + cairo_status_t status; + cairo_dwrite_font_face_t *font_face = static_cast<cairo_dwrite_font_face_t*>(abstract_face); + + // Must do malloc and not C++ new, since Cairo frees this. + cairo_dwrite_scaled_font_t *dwrite_font = (cairo_dwrite_scaled_font_t*)_cairo_malloc( + sizeof(cairo_dwrite_scaled_font_t)); + *font = reinterpret_cast<cairo_scaled_font_t*>(dwrite_font); + status = _cairo_scaled_font_init(&dwrite_font->base, &font_face->base, font_matrix, ctm, options, &_cairo_dwrite_scaled_font_backend); + if (status) { + free(dwrite_font); + return status; + } + + cairo_font_extents_t extents; + + DWRITE_FONT_METRICS metrics; + font_face->dwriteface->GetMetrics(&metrics); + + extents.ascent = (FLOAT)metrics.ascent / metrics.designUnitsPerEm; + extents.descent = (FLOAT)metrics.descent / metrics.designUnitsPerEm; + extents.height = (FLOAT)(metrics.ascent + metrics.descent + metrics.lineGap) / metrics.designUnitsPerEm; + extents.max_x_advance = 14.0; + extents.max_y_advance = 0.0; + + dwrite_font->mat = dwrite_font->base.ctm; + cairo_matrix_multiply(&dwrite_font->mat, &dwrite_font->mat, font_matrix); + dwrite_font->mat_inverse = dwrite_font->mat; + cairo_matrix_invert (&dwrite_font->mat_inverse); + + cairo_antialias_t default_quality = CAIRO_ANTIALIAS_SUBPIXEL; + + dwrite_font->measuring_mode = DWRITE_MEASURING_MODE_NATURAL; + + // The following code detects the system quality at scaled_font creation time, + // this means that if cleartype settings are changed but the scaled_fonts + // are re-used, they might not adhere to the new system setting until re- + // creation. + switch (cairo_win32_get_system_text_quality()) { + case CLEARTYPE_QUALITY: + default_quality = CAIRO_ANTIALIAS_SUBPIXEL; + break; + case ANTIALIASED_QUALITY: + default_quality = CAIRO_ANTIALIAS_GRAY; + dwrite_font->measuring_mode = DWRITE_MEASURING_MODE_GDI_CLASSIC; + break; + case DEFAULT_QUALITY: + // _get_system_quality() seems to think aliased is default! + default_quality = CAIRO_ANTIALIAS_NONE; + dwrite_font->measuring_mode = DWRITE_MEASURING_MODE_GDI_CLASSIC; + break; + } + + if (default_quality == CAIRO_ANTIALIAS_GRAY) { + if (!do_grayscale(font_face->dwriteface, (unsigned int)_cairo_round(font_matrix->yy))) { + default_quality = CAIRO_ANTIALIAS_NONE; + } + } + + if (options->antialias == CAIRO_ANTIALIAS_DEFAULT) { + dwrite_font->antialias_mode = default_quality; + } else { + dwrite_font->antialias_mode = options->antialias; + } + + dwrite_font->rendering_mode = + default_quality == CAIRO_ANTIALIAS_SUBPIXEL ? + cairo_dwrite_scaled_font_t::TEXT_RENDERING_NORMAL : cairo_dwrite_scaled_font_t::TEXT_RENDERING_NO_CLEARTYPE; + + return _cairo_scaled_font_set_metrics (*font, &extents); +} + +/* Implementation #cairo_dwrite_scaled_font_backend_t */ +void +_cairo_dwrite_scaled_font_fini(void *scaled_font) +{ +} + +static cairo_int_status_t +_cairo_dwrite_scaled_glyph_init(void *scaled_font, + cairo_scaled_glyph_t *scaled_glyph, + cairo_scaled_glyph_info_t info, + const cairo_color_t *foreground_color) +{ + cairo_dwrite_scaled_font_t *scaled_dwrite_font = static_cast<cairo_dwrite_scaled_font_t*>(scaled_font); + cairo_int_status_t status = CAIRO_INT_STATUS_UNSUPPORTED; + + if ((info & CAIRO_SCALED_GLYPH_INFO_METRICS) != 0) { + status = _cairo_dwrite_scaled_font_init_glyph_metrics (scaled_dwrite_font, scaled_glyph); + if (status) + return status; + } + + if (info & CAIRO_SCALED_GLYPH_INFO_COLOR_SURFACE) { + status = _cairo_dwrite_scaled_font_init_glyph_color_surface (scaled_dwrite_font, scaled_glyph, foreground_color); + if (status) + return status; + } + + if (info & CAIRO_SCALED_GLYPH_INFO_SURFACE) { + status = _cairo_dwrite_scaled_font_init_glyph_surface (scaled_dwrite_font, scaled_glyph); + if (status) + return status; + } + + if ((info & CAIRO_SCALED_GLYPH_INFO_PATH) != 0) { + status = _cairo_dwrite_scaled_font_init_glyph_path (scaled_dwrite_font, scaled_glyph); + if (status) + return status; + } + + return CAIRO_INT_STATUS_SUCCESS; +} + +unsigned long +_cairo_dwrite_ucs4_to_index(void *scaled_font, + uint32_t ucs4) +{ + cairo_dwrite_scaled_font_t *dwritesf = static_cast<cairo_dwrite_scaled_font_t*>(scaled_font); + cairo_dwrite_font_face_t *face = reinterpret_cast<cairo_dwrite_font_face_t*>(dwritesf->base.font_face); + + UINT16 index; + face->dwriteface->GetGlyphIndicesA(&ucs4, 1, &index); + return index; +} + +/* cairo_dwrite_scaled_glyph_init() helper function bodies */ +cairo_int_status_t +_cairo_dwrite_scaled_font_init_glyph_metrics(cairo_dwrite_scaled_font_t *scaled_font, + cairo_scaled_glyph_t *scaled_glyph) +{ + UINT16 charIndex = (UINT16)_cairo_scaled_glyph_index (scaled_glyph); + cairo_dwrite_font_face_t *font_face = (cairo_dwrite_font_face_t*)scaled_font->base.font_face; + cairo_text_extents_t extents; + + DWRITE_GLYPH_METRICS metrics; + DWRITE_FONT_METRICS fontMetrics; + font_face->dwriteface->GetMetrics(&fontMetrics); + HRESULT hr = font_face->dwriteface->GetDesignGlyphMetrics(&charIndex, 1, &metrics); + if (FAILED(hr)) { + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + // TODO: Treat swap_xy. + extents.width = (FLOAT)(metrics.advanceWidth - metrics.leftSideBearing - metrics.rightSideBearing) / + fontMetrics.designUnitsPerEm; + extents.height = (FLOAT)(metrics.advanceHeight - metrics.topSideBearing - metrics.bottomSideBearing) / + fontMetrics.designUnitsPerEm; + extents.x_advance = (FLOAT)metrics.advanceWidth / fontMetrics.designUnitsPerEm; + extents.x_bearing = (FLOAT)metrics.leftSideBearing / fontMetrics.designUnitsPerEm; + extents.y_advance = 0.0; + extents.y_bearing = (FLOAT)(metrics.topSideBearing - metrics.verticalOriginY) / + fontMetrics.designUnitsPerEm; + + // We pad the extents here because GetDesignGlyphMetrics returns "ideal" metrics + // for the glyph outline, without accounting for hinting/gridfitting/antialiasing, + // and therefore it does not always cover all pixels that will actually be touched. + if (scaled_font->base.options.antialias != CAIRO_ANTIALIAS_NONE && + extents.width > 0 && extents.height > 0) { + extents.width += scaled_font->mat_inverse.xx * 2; + extents.x_bearing -= scaled_font->mat_inverse.xx; + } + + _cairo_scaled_glyph_set_metrics (scaled_glyph, + &scaled_font->base, + &extents); + return CAIRO_INT_STATUS_SUCCESS; +} + +/* + * Stack-based helper implementing IDWriteGeometrySink. + * Used to determine the path of the glyphs. + */ + +class GeometryRecorder : public IDWriteGeometrySink +{ +public: + GeometryRecorder(cairo_path_fixed_t *aCairoPath) + : mCairoPath(aCairoPath) {} + + // IUnknown interface + IFACEMETHOD(QueryInterface)(IID const& iid, OUT void** ppObject) + { + if (iid != __uuidof(IDWriteGeometrySink)) + return E_NOINTERFACE; + + *ppObject = static_cast<IDWriteGeometrySink*>(this); + + return S_OK; + } + + IFACEMETHOD_(ULONG, AddRef)() + { + return 1; + } + + IFACEMETHOD_(ULONG, Release)() + { + return 1; + } + + IFACEMETHODIMP_(void) SetFillMode(D2D1_FILL_MODE fillMode) + { + return; + } + + STDMETHODIMP Close() + { + return S_OK; + } + + IFACEMETHODIMP_(void) SetSegmentFlags(D2D1_PATH_SEGMENT vertexFlags) + { + return; + } + + cairo_fixed_t GetFixedX(const D2D1_POINT_2F &point) + { + unsigned int control_word; + _controlfp_s(&control_word, _CW_DEFAULT, MCW_PC); + return _cairo_fixed_from_double(point.x); + } + + cairo_fixed_t GetFixedY(const D2D1_POINT_2F &point) + { + unsigned int control_word; + _controlfp_s(&control_word, _CW_DEFAULT, MCW_PC); + return _cairo_fixed_from_double(point.y); + } + + IFACEMETHODIMP_(void) BeginFigure( + D2D1_POINT_2F startPoint, + D2D1_FIGURE_BEGIN figureBegin) + { + mStartPoint = startPoint; + cairo_status_t status = _cairo_path_fixed_move_to(mCairoPath, + GetFixedX(startPoint), + GetFixedY(startPoint)); + (void)status; /* squelch warning */ + } + + IFACEMETHODIMP_(void) EndFigure( + D2D1_FIGURE_END figureEnd) + { + if (figureEnd == D2D1_FIGURE_END_CLOSED) { + cairo_status_t status = _cairo_path_fixed_line_to(mCairoPath, + GetFixedX(mStartPoint), + GetFixedY(mStartPoint)); + (void)status; /* squelch warning */ + } + } + + IFACEMETHODIMP_(void) AddBeziers( + const D2D1_BEZIER_SEGMENT *beziers, + UINT beziersCount) + { + for (unsigned int i = 0; i < beziersCount; i++) { + cairo_status_t status = _cairo_path_fixed_curve_to(mCairoPath, + GetFixedX(beziers[i].point1), + GetFixedY(beziers[i].point1), + GetFixedX(beziers[i].point2), + GetFixedY(beziers[i].point2), + GetFixedX(beziers[i].point3), + GetFixedY(beziers[i].point3)); + (void)status; /* squelch warning */ + } + } + + IFACEMETHODIMP_(void) AddLines( + const D2D1_POINT_2F *points, + UINT pointsCount) + { + for (unsigned int i = 0; i < pointsCount; i++) { + cairo_status_t status = _cairo_path_fixed_line_to(mCairoPath, + GetFixedX(points[i]), + GetFixedY(points[i])); + (void)status; /* squelch warning */ + } + } + +private: + cairo_path_fixed_t *mCairoPath; + D2D1_POINT_2F mStartPoint; +}; + +cairo_int_status_t +_cairo_dwrite_scaled_font_init_glyph_path(cairo_dwrite_scaled_font_t *scaled_font, + cairo_scaled_glyph_t *scaled_glyph) +{ + cairo_int_status_t status; + cairo_path_fixed_t *path; + path = _cairo_path_fixed_create(); + GeometryRecorder recorder(path); + + DWRITE_GLYPH_OFFSET offset; + offset.advanceOffset = 0; + offset.ascenderOffset = 0; + UINT16 glyphId = (UINT16)_cairo_scaled_glyph_index(scaled_glyph); + FLOAT advance = 0.0; + cairo_dwrite_font_face_t *dwriteff = (cairo_dwrite_font_face_t*)scaled_font->base.font_face; + dwriteff->dwriteface->GetGlyphRunOutline((FLOAT)scaled_font->base.font_matrix.yy, + &glyphId, + &advance, + &offset, + 1, + FALSE, + FALSE, + &recorder); + status = (cairo_int_status_t)_cairo_path_fixed_close_path(path); + + /* Now apply our transformation to the drawn path. */ + _cairo_path_fixed_transform(path, &scaled_font->base.ctm); + + _cairo_scaled_glyph_set_path (scaled_glyph, + &scaled_font->base, + path); + return status; +} + +cairo_int_status_t +_cairo_dwrite_scaled_font_init_glyph_color_surface(cairo_dwrite_scaled_font_t *scaled_font, + cairo_scaled_glyph_t *scaled_glyph, + const cairo_color_t *foreground_color) +{ + int width, height; + double x1, y1, x2, y2; + cairo_glyph_t glyph; + cairo_bool_t uses_foreground_color = FALSE; + + cairo_dwrite_font_face_t *dwrite_font_face = (cairo_dwrite_font_face_t *)scaled_font->base.font_face; + if (!dwrite_font_face->have_color) + return CAIRO_INT_STATUS_UNSUPPORTED; + + 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 = (int)(x2 - x1); + height = (int)(y2 - y1); + + glyph.index = _cairo_scaled_glyph_index (scaled_glyph); + glyph.x = x1; + glyph.y = y1; + + DWRITE_GLYPH_RUN run; + FLOAT advance = 0; + UINT16 index = (UINT16)glyph.index; + DWRITE_GLYPH_OFFSET offset; + double x = -glyph.x; + double y = -glyph.y; + DWRITE_MATRIX matrix; + D2D1_POINT_2F origin = {0, 0}; + IDWriteColorGlyphRunEnumerator1 *run_enumerator; + HRESULT hr; + + /** + * We transform by the inverse transformation here. This will put our glyph + * locations in the space in which we draw. Which is later transformed by + * the transformation matrix that we use. This will transform the + * glyph positions back to where they were before when drawing, but the + * glyph shapes will be transformed by the transformation matrix. + */ + cairo_matrix_transform_point(&scaled_font->mat_inverse, &x, &y); + offset.advanceOffset = (FLOAT)x; + /** Y-axis is inverted */ + offset.ascenderOffset = -(FLOAT)y; + + run.fontFace = dwrite_font_face->dwriteface; + run.fontEmSize = 1; + run.glyphCount = 1; + run.glyphIndices = &index; + run.glyphAdvances = &advance; + run.glyphOffsets = &offset; + run.isSideways = FALSE; + run.bidiLevel = 0; + + matrix = _cairo_dwrite_matrix_from_matrix(&scaled_font->mat); + + /* The list of glyph image formats this renderer is prepared to support. */ + DWRITE_GLYPH_IMAGE_FORMATS supported_formats = + DWRITE_GLYPH_IMAGE_FORMATS_COLR | + DWRITE_GLYPH_IMAGE_FORMATS_SVG | + DWRITE_GLYPH_IMAGE_FORMATS_PNG | + DWRITE_GLYPH_IMAGE_FORMATS_JPEG | + DWRITE_GLYPH_IMAGE_FORMATS_TIFF | + DWRITE_GLYPH_IMAGE_FORMATS_PREMULTIPLIED_B8G8R8A8; + + hr = DWriteFactory::Instance4()->TranslateColorGlyphRun( + origin, + &run, + NULL, /* glyphRunDescription */ + supported_formats, + DWRITE_MEASURING_MODE_NATURAL, + &matrix, + 0, + &run_enumerator); + + if (hr == DWRITE_E_NOCOLOR) + return CAIRO_INT_STATUS_UNSUPPORTED; /* No color glyphs */ + + if (!SUCCEEDED(hr)) + return _cairo_dwrite_error (hr, "TranslateColorGlyphRun failed"); + + /* We have a color glyph(s). Use Direct2D to render it to a bitmap */ + if (!WICImagingFactory::Instance() || !D2DFactory::Instance()) + return _cairo_dwrite_error (hr, "Instance failed"); + + IWICBitmap *bitmap = NULL; + hr = WICImagingFactory::Instance()->CreateBitmap ((UINT)width, + (UINT)height, + GUID_WICPixelFormat32bppPBGRA, + WICBitmapCacheOnLoad, + &bitmap); + if (!SUCCEEDED(hr)) + return _cairo_dwrite_error (hr, "CreateBitmap failed"); + + D2D1_RENDER_TARGET_PROPERTIES properties = D2D1::RenderTargetProperties( + D2D1_RENDER_TARGET_TYPE_DEFAULT, + D2D1::PixelFormat( + DXGI_FORMAT_B8G8R8A8_UNORM, + D2D1_ALPHA_MODE_PREMULTIPLIED), + 0, + 0, + D2D1_RENDER_TARGET_USAGE_NONE, + D2D1_FEATURE_LEVEL_DEFAULT); + + ID2D1RenderTarget* rt = NULL; + hr = D2DFactory::Instance()->CreateWicBitmapRenderTarget (bitmap, properties, &rt); + if (!SUCCEEDED(hr)) + return _cairo_dwrite_error (hr, "CreateWicBitmapRenderTarget failed"); + + ID2D1DeviceContext4* dc4 = NULL; + if (!SUCCEEDED(rt->QueryInterface(&dc4))) + return _cairo_dwrite_error (hr, "QueryInterface failed"); + + ID2D1SolidColorBrush *foreground_color_brush; + if (foreground_color) { + dc4->CreateSolidColorBrush( + D2D1::ColorF(foreground_color->red, + foreground_color->green, + foreground_color->blue, + foreground_color->alpha), &foreground_color_brush); + } else { + dc4->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::Black), &foreground_color_brush); + } + + ID2D1SolidColorBrush *color_brush; + dc4->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::Black), &color_brush); + + dc4->SetDpi(96, 96); /* 1 unit = 1 pixel */ + rt->SetTransform(D2D1::Matrix3x2F(matrix.m11, + matrix.m12, + matrix.m21, + matrix.m22, + matrix.dx, + matrix.dy)); + + dc4->BeginDraw(); + dc4->Clear(NULL); /* Transparent black */ + + while (true) { + BOOL have_run; + hr = run_enumerator->MoveNext(&have_run); + if (!SUCCEEDED(hr) || !have_run) + break; + + DWRITE_COLOR_GLYPH_RUN1 const* color_run; + if (!SUCCEEDED(run_enumerator->GetCurrentRun (&color_run))) + return _cairo_dwrite_error (hr, "GetCurrentRun failed"); + + switch (color_run->glyphImageFormat) { + case DWRITE_GLYPH_IMAGE_FORMATS_PNG: + case DWRITE_GLYPH_IMAGE_FORMATS_JPEG: + case DWRITE_GLYPH_IMAGE_FORMATS_TIFF: + case DWRITE_GLYPH_IMAGE_FORMATS_PREMULTIPLIED_B8G8R8A8: + /* Bitmap glyphs */ + dc4->DrawColorBitmapGlyphRun(color_run->glyphImageFormat, + origin, + &color_run->glyphRun, + DWRITE_MEASURING_MODE_NATURAL, + D2D1_COLOR_BITMAP_GLYPH_SNAP_OPTION_DEFAULT); + break; + + case DWRITE_GLYPH_IMAGE_FORMATS_SVG: + /* SVG glyphs */ + dc4->DrawSvgGlyphRun(origin, + &color_run->glyphRun, + foreground_color_brush, + nullptr, + 0, + DWRITE_MEASURING_MODE_NATURAL); + if (foreground_color) + uses_foreground_color = TRUE; + break; + case DWRITE_GLYPH_IMAGE_FORMATS_TRUETYPE: + case DWRITE_GLYPH_IMAGE_FORMATS_CFF: + case DWRITE_GLYPH_IMAGE_FORMATS_COLR: + default: + /* Outline glyphs */ + if (color_run->paletteIndex == 0xFFFF) { + D2D1_COLOR_F color = foreground_color_brush->GetColor(); + color_brush->SetColor(&color); + uses_foreground_color = TRUE; + } else { + color_brush->SetColor(color_run->runColor); + } + + dc4->DrawGlyphRun(origin, + &color_run->glyphRun, + color_run->glyphRunDescription, + color_brush, + DWRITE_MEASURING_MODE_NATURAL); + } + } + + hr = dc4->EndDraw(); + if (!SUCCEEDED(hr)) + return _cairo_dwrite_error (hr, "dc4 failed"); + + color_brush->Release(); + foreground_color_brush->Release(); + + cairo_surface_t *image = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height); + int stride = cairo_image_surface_get_stride (image); + WICRect rect = { 0, 0, width, height }; + bitmap->CopyPixels(&rect, + stride, + height * stride, + cairo_image_surface_get_data (image)); + cairo_surface_mark_dirty (image); + bitmap->Release(); + cairo_surface_set_device_offset (image, -x1, -y1); + _cairo_scaled_glyph_set_color_surface (scaled_glyph, + &scaled_font->base, + (cairo_image_surface_t *) image, + uses_foreground_color); + + return CAIRO_INT_STATUS_SUCCESS; +} + +/* Helper function adapted from _compute_mask in cairo-win32-font.c */ + +/* Compute an alpha-mask from a monochrome RGB24 image + */ +static cairo_surface_t * +_compute_a8_mask (cairo_surface_t *surface) +{ + cairo_image_surface_t *glyph; + cairo_image_surface_t *mask; + int i, j; + + glyph = (cairo_image_surface_t *)cairo_surface_map_to_image (surface, NULL); + if (unlikely (glyph->base.status)) + return &glyph->base; + + /* No quality param, just use the non-ClearType path */ + + /* Compute an alpha-mask by using the green channel of a (presumed monochrome) + * RGB24 image. + */ + mask = (cairo_image_surface_t *) + cairo_image_surface_create (CAIRO_FORMAT_A8, glyph->width, glyph->height); + if (likely (mask->base.status == CAIRO_STATUS_SUCCESS)) { + for (i = 0; i < glyph->height; i++) { + uint32_t *p = (uint32_t *) (glyph->data + i * glyph->stride); + uint8_t *q = (uint8_t *) (mask->data + i * mask->stride); + + for (j = 0; j < glyph->width; j++) + *q++ = 255 - ((*p++ & 0x0000ff00) >> 8); + } + } + + cairo_surface_unmap_image (surface, &glyph->base); + return &mask->base; +} + +cairo_int_status_t +_cairo_dwrite_scaled_font_init_glyph_surface(cairo_dwrite_scaled_font_t *scaled_font, + cairo_scaled_glyph_t *scaled_glyph) +{ + cairo_int_status_t status; + cairo_glyph_t glyph; + cairo_win32_surface_t *surface; + cairo_t *cr; + cairo_surface_t *image; + int width, height; + double 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 = (int)(x2 - x1); + height = (int)(y2 - y1); + + glyph.index = _cairo_scaled_glyph_index (scaled_glyph); + glyph.x = -x1; + glyph.y = -y1; + + DWRITE_GLYPH_RUN run; + FLOAT advance = 0; + UINT16 index = (UINT16)glyph.index; + DWRITE_GLYPH_OFFSET offset; + double x = glyph.x; + double y = glyph.y; + RECT area; + DWRITE_MATRIX matrix; + + 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_int_status_t)cairo_status (cr); + cairo_destroy(cr); + if (status) + goto FAIL; + + /** + * We transform by the inverse transformation here. This will put our glyph + * locations in the space in which we draw. Which is later transformed by + * the transformation matrix that we use. This will transform the + * glyph positions back to where they were before when drawing, but the + * glyph shapes will be transformed by the transformation matrix. + */ + cairo_matrix_transform_point(&scaled_font->mat_inverse, &x, &y); + offset.advanceOffset = (FLOAT)x; + /** Y-axis is inverted */ + offset.ascenderOffset = -(FLOAT)y; + + area.top = 0; + area.bottom = height; + area.left = 0; + area.right = width; + + run.glyphCount = 1; + run.glyphAdvances = &advance; + run.fontFace = ((cairo_dwrite_font_face_t*)scaled_font->base.font_face)->dwriteface; + run.fontEmSize = 1.0f; + run.bidiLevel = 0; + run.glyphIndices = &index; + run.isSideways = FALSE; + run.glyphOffsets = &offset; + + matrix = _cairo_dwrite_matrix_from_matrix(&scaled_font->mat); + + status = _dwrite_draw_glyphs_to_gdi_surface_gdi (surface, &matrix, &run, + RGB(0,0,0), scaled_font, area); + if (status) + goto FAIL; + + GdiFlush(); + + image = _compute_a8_mask (&surface->base); + status = (cairo_int_status_t)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; +} + +cairo_int_status_t +_cairo_dwrite_load_truetype_table(void *scaled_font, + unsigned long tag, + long offset, + unsigned char *buffer, + unsigned long *length) +{ + cairo_dwrite_scaled_font_t *dwritesf = static_cast<cairo_dwrite_scaled_font_t*>(scaled_font); + cairo_dwrite_font_face_t *face = reinterpret_cast<cairo_dwrite_font_face_t*>(dwritesf->base.font_face); + + const void *data; + UINT32 size; + void *tableContext; + BOOL exists; + face->dwriteface->TryGetFontTable(be32_to_cpu (tag), + &data, + &size, + &tableContext, + &exists); + + if (!exists) { + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + if (buffer && *length && (UINT32)offset < size) { + size = MIN(size - (UINT32)offset, *length); + memcpy(buffer, (const char*)data + offset, size); + } + *length = size; + + if (tableContext) { + face->dwriteface->ReleaseFontTable(tableContext); + } + return (cairo_int_status_t)CAIRO_STATUS_SUCCESS; +} + +static cairo_bool_t +_cairo_dwrite_has_color_glyphs(void *scaled_font) +{ + cairo_dwrite_scaled_font_t *dwritesf = static_cast<cairo_dwrite_scaled_font_t*>(scaled_font); + + return ((cairo_dwrite_font_face_t *)dwritesf->base.font_face)->have_color; +} + +// WIN32 Helper Functions +cairo_font_face_t* +cairo_dwrite_font_face_create_for_dwrite_fontface(void* dwrite_font_face) +{ + IDWriteFontFace *dwriteface = static_cast<IDWriteFontFace*>(dwrite_font_face); + // Must do malloc and not C++ new, since Cairo frees this. + cairo_dwrite_font_face_t *face = (cairo_dwrite_font_face_t *)_cairo_malloc(sizeof(cairo_dwrite_font_face_t)); + cairo_font_face_t *font_face; + + dwriteface->AddRef(); + + face->dwriteface = dwriteface; + + face->have_color = false; + /* Ensure IDWriteFactory4 is available before enabling color fonts */ + if (DWriteFactory::Instance4()) { + IDWriteFontFace2 *fontFace2; + if (SUCCEEDED(dwriteface->QueryInterface(&fontFace2))) { + if (fontFace2->IsColorFont()) + face->have_color = true; + } + } + + font_face = (cairo_font_face_t*)face; + + _cairo_font_face_init (&((cairo_dwrite_font_face_t*)font_face)->base, &_cairo_dwrite_font_face_backend); + + return font_face; +} + +void +cairo_dwrite_scaled_font_set_force_GDI_classic(cairo_scaled_font_t *dwrite_scaled_font, cairo_bool_t force) +{ + cairo_dwrite_scaled_font_t *font = reinterpret_cast<cairo_dwrite_scaled_font_t*>(dwrite_scaled_font); + if (force && font->rendering_mode == cairo_dwrite_scaled_font_t::TEXT_RENDERING_NORMAL) { + font->rendering_mode = cairo_dwrite_scaled_font_t::TEXT_RENDERING_GDI_CLASSIC; + } else if (!force && font->rendering_mode == cairo_dwrite_scaled_font_t::TEXT_RENDERING_GDI_CLASSIC) { + font->rendering_mode = cairo_dwrite_scaled_font_t::TEXT_RENDERING_NORMAL; + } +} + +cairo_bool_t +cairo_dwrite_scaled_font_get_force_GDI_classic(cairo_scaled_font_t *dwrite_scaled_font) +{ + cairo_dwrite_scaled_font_t *font = reinterpret_cast<cairo_dwrite_scaled_font_t*>(dwrite_scaled_font); + return font->rendering_mode == cairo_dwrite_scaled_font_t::TEXT_RENDERING_GDI_CLASSIC; +} + +void +cairo_dwrite_set_cleartype_params(FLOAT gamma, FLOAT contrast, FLOAT level, + int geometry, int mode) +{ + DWriteFactory::SetRenderingParams(gamma, contrast, level, geometry, mode); +} + +int +cairo_dwrite_get_cleartype_rendering_mode() +{ + return DWriteFactory::GetClearTypeRenderingMode(); +} + +cairo_int_status_t +_dwrite_draw_glyphs_to_gdi_surface_gdi(cairo_win32_surface_t *surface, + DWRITE_MATRIX *transform, + DWRITE_GLYPH_RUN *run, + COLORREF color, + cairo_dwrite_scaled_font_t *scaled_font, + const RECT &area) +{ + IDWriteGdiInterop *gdiInterop; + DWriteFactory::Instance()->GetGdiInterop(&gdiInterop); + IDWriteBitmapRenderTarget *rt; + HRESULT rv; + + cairo_dwrite_scaled_font_t::TextRenderingState renderingState = + scaled_font->rendering_mode; + + rv = gdiInterop->CreateBitmapRenderTarget(surface->dc, + area.right - area.left, + area.bottom - area.top, + &rt); + + if (FAILED(rv)) { + if (rv == E_OUTOFMEMORY) { + return (cairo_int_status_t)CAIRO_STATUS_NO_MEMORY; + } else { + return CAIRO_INT_STATUS_UNSUPPORTED; + } + } + + if ((renderingState == cairo_dwrite_scaled_font_t::TEXT_RENDERING_NORMAL || + renderingState == cairo_dwrite_scaled_font_t::TEXT_RENDERING_GDI_CLASSIC) + /* && !surface->base.permit_subpixel_antialiasing */ ) { + renderingState = cairo_dwrite_scaled_font_t::TEXT_RENDERING_NO_CLEARTYPE; + IDWriteBitmapRenderTarget1* rt1; + rv = rt->QueryInterface(&rt1); + + if (SUCCEEDED(rv) && rt1) { + rt1->SetTextAntialiasMode(DWRITE_TEXT_ANTIALIAS_MODE_GRAYSCALE); + rt1->Release(); + } + } + + IDWriteRenderingParams *params = + DWriteFactory::RenderingParams(renderingState); + + /** + * We set the number of pixels per DIP to 1.0. This is because we always want + * to draw in device pixels, and not device independent pixels. On high DPI + * systems this value will be higher than 1.0 and automatically upscale + * fonts, we don't want this since we do our own upscaling for various reasons. + */ + rt->SetPixelsPerDip(1.0); + + if (transform) { + rt->SetCurrentTransform(transform); + } + BitBlt(rt->GetMemoryDC(), + 0, 0, + area.right - area.left, area.bottom - area.top, + surface->dc, + area.left, area.top, + SRCCOPY | NOMIRRORBITMAP); + DWRITE_MEASURING_MODE measureMode; + switch (renderingState) { + case cairo_dwrite_scaled_font_t::TEXT_RENDERING_GDI_CLASSIC: + case cairo_dwrite_scaled_font_t::TEXT_RENDERING_NO_CLEARTYPE: + measureMode = DWRITE_MEASURING_MODE_GDI_CLASSIC; + break; + default: + measureMode = DWRITE_MEASURING_MODE_NATURAL; + break; + } + rt->DrawGlyphRun(0, 0, measureMode, run, params, color); + BitBlt(surface->dc, + area.left, area.top, + area.right - area.left, area.bottom - area.top, + rt->GetMemoryDC(), + 0, 0, + SRCCOPY | NOMIRRORBITMAP); + params->Release(); + rt->Release(); + gdiInterop->Release(); + return CAIRO_INT_STATUS_SUCCESS; +} + +cairo_int_status_t +_dwrite_draw_glyphs_to_gdi_surface_d2d(cairo_win32_surface_t *surface, + DWRITE_MATRIX *transform, + DWRITE_GLYPH_RUN *run, + COLORREF color, + const RECT &area) +{ + HRESULT rv; + + ID2D1DCRenderTarget *rt = D2DFactory::RenderTarget(); + + // XXX don't we need to set RenderingParams on this RenderTarget? + + rv = rt->BindDC(surface->dc, &area); + + if (FAILED(rv)) { + rt->Release(); + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + // D2D uses 0x00RRGGBB not 0x00BBGGRR like COLORREF. + color = (color & 0xFF) << 16 | + (color & 0xFF00) | + (color & 0xFF0000) >> 16; + ID2D1SolidColorBrush *brush; + rv = rt->CreateSolidColorBrush(D2D1::ColorF(color, 1.0), &brush); + + if (FAILED(rv)) { + rt->Release(); + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + if (transform) { + rt->SetTransform(D2D1::Matrix3x2F(transform->m11, + transform->m12, + transform->m21, + transform->m22, + transform->dx, + transform->dy)); + } + rt->BeginDraw(); + rt->DrawGlyphRun(D2D1::Point2F(0, 0), run, brush); + rt->EndDraw(); + if (transform) { + rt->SetTransform(D2D1::Matrix3x2F::Identity()); + } + brush->Release(); + if (FAILED(rv)) { + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + return CAIRO_INT_STATUS_SUCCESS; +} + +/* Surface helper function */ +cairo_int_status_t +_cairo_dwrite_show_glyphs_on_surface(void *surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_glyph_t *glyphs, + int num_glyphs, + cairo_scaled_font_t *scaled_font, + cairo_clip_t *clip) +{ + // TODO: Check font & surface for types. + cairo_dwrite_scaled_font_t *dwritesf = reinterpret_cast<cairo_dwrite_scaled_font_t*>(scaled_font); + cairo_dwrite_font_face_t *dwriteff = reinterpret_cast<cairo_dwrite_font_face_t*>(scaled_font->font_face); + cairo_win32_surface_t *dst = reinterpret_cast<cairo_win32_surface_t*>(surface); + cairo_int_status_t status; + /* We can only handle dwrite fonts */ + if (cairo_scaled_font_get_type (scaled_font) != CAIRO_FONT_TYPE_DWRITE) + 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) + return CAIRO_INT_STATUS_UNSUPPORTED; + + /* If we have a fallback mask clip set on the dst, we have + * to go through the fallback path */ + if (!_cairo_surface_is_win32_printing (&dst->base)) { + if (clip != NULL) + _cairo_win32_display_surface_set_clip (to_win32_display_surface (dst), clip); + else + _cairo_win32_display_surface_unset_clip (to_win32_display_surface (dst)); + } + + /* 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. */ + + AutoDWriteGlyphRun run; + run.allocate(num_glyphs); + + UINT16 *indices = const_cast<UINT16*>(run.glyphIndices); + FLOAT *advances = const_cast<FLOAT*>(run.glyphAdvances); + DWRITE_GLYPH_OFFSET *offsets = const_cast<DWRITE_GLYPH_OFFSET*>(run.glyphOffsets); + + BOOL transform = FALSE; + /* Needed to calculate bounding box for efficient blitting */ + INT32 smallestX = INT_MAX; + INT32 largestX = 0; + INT32 smallestY = INT_MAX; + INT32 largestY = 0; + for (int i = 0; i < num_glyphs; i++) { + if (glyphs[i].x < smallestX) { + smallestX = (INT32)glyphs[i].x; + } + if (glyphs[i].x > largestX) { + largestX = (INT32)glyphs[i].x; + } + if (glyphs[i].y < smallestY) { + smallestY = (INT32)glyphs[i].y; + } + if (glyphs[i].y > largestY) { + largestY = (INT32)glyphs[i].y; + } + } + /** + * Here we try to get a rough estimate of the area that this glyph run will + * cover on the surface. Since we use GDI interop to draw we will be copying + * data around the size of the area of the surface that we map. We will want + * to map an area as small as possible to prevent large surfaces to be + * copied around. We take the X/Y-size of the font as margin on the left/top + * twice the X/Y-size of the font as margin on the right/bottom. + * This should always cover the entire area where the glyphs are. + */ + RECT fontArea; + fontArea.left = (INT32)(smallestX - scaled_font->font_matrix.xx); + fontArea.right = (INT32)(largestX + scaled_font->font_matrix.xx * 2); + fontArea.top = (INT32)(smallestY - scaled_font->font_matrix.yy); + fontArea.bottom = (INT32)(largestY + scaled_font->font_matrix.yy * 2); + if (fontArea.left < 0) + fontArea.left = 0; + if (fontArea.top < 0) + fontArea.top = 0; + if (fontArea.bottom > dst->extents.height) { + fontArea.bottom = dst->extents.height; + } + if (fontArea.right > dst->extents.width) { + fontArea.right = dst->extents.width; + } + if (fontArea.right <= fontArea.left || + fontArea.bottom <= fontArea.top) { + return CAIRO_INT_STATUS_SUCCESS; + } + if (fontArea.right > dst->extents.width) { + fontArea.right = dst->extents.width; + } + if (fontArea.bottom > dst->extents.height) { + fontArea.bottom = dst->extents.height; + } + + run.bidiLevel = 0; + run.fontFace = dwriteff->dwriteface; + run.isSideways = FALSE; + if (dwritesf->mat.xy == 0 && dwritesf->mat.yx == 0 && + dwritesf->mat.xx == scaled_font->font_matrix.xx && + dwritesf->mat.yy == scaled_font->font_matrix.yy) { + + for (int i = 0; i < num_glyphs; i++) { + indices[i] = (WORD) glyphs[i].index; + // Since we will multiply by our ctm matrix later for rotation effects + // and such, adjust positions by the inverse matrix now. + offsets[i].ascenderOffset = (FLOAT)(fontArea.top - glyphs[i].y); + offsets[i].advanceOffset = (FLOAT)(glyphs[i].x - fontArea.left); + advances[i] = 0.0; + } + run.fontEmSize = (FLOAT)scaled_font->font_matrix.yy; + } else { + transform = TRUE; + // See comment about EPSILON in _cairo_dwrite_glyph_run_from_glyphs + const double EPSILON = 0.0001; + for (int i = 0; i < num_glyphs; i++) { + indices[i] = (WORD) glyphs[i].index; + double x = glyphs[i].x - fontArea.left + EPSILON; + double y = glyphs[i].y - fontArea.top; + cairo_matrix_transform_point(&dwritesf->mat_inverse, &x, &y); + /** + * Since we will multiply by our ctm matrix later for rotation effects + * and such, adjust positions by the inverse matrix now. The Y-axis + * is inverted so the offset becomes negative. + */ + offsets[i].ascenderOffset = -(FLOAT)y; + offsets[i].advanceOffset = (FLOAT)x; + advances[i] = 0.0; + } + run.fontEmSize = 1.0f; + } + + cairo_solid_pattern_t *solid_pattern = (cairo_solid_pattern_t *)source; + COLORREF color = RGB(((int)solid_pattern->color.red_short) >> 8, + ((int)solid_pattern->color.green_short) >> 8, + ((int)solid_pattern->color.blue_short) >> 8); + + DWRITE_MATRIX matrix = _cairo_dwrite_matrix_from_matrix(&dwritesf->mat); + + DWRITE_MATRIX *mat; + if (transform) { + mat = &matrix; + } else { + mat = NULL; + } + + RECT area; + area.left = dst->extents.x; + area.top = dst->extents.y; + area.right = area.left + dst->extents.width; + area.bottom = area.top + dst->extents.height; + +#ifdef CAIRO_TRY_D2D_TO_GDI + status = _dwrite_draw_glyphs_to_gdi_surface_d2d(dst, + mat, + &run, + color, + fontArea); + + if (status == (cairo_status_t)CAIRO_INT_STATUS_UNSUPPORTED) { +#endif + status = _dwrite_draw_glyphs_to_gdi_surface_gdi(dst, + mat, + &run, + color, + dwritesf, + fontArea); + +#ifdef CAIRO_TRY_D2D_TO_GDI + } +#endif + + return status; +} + +#define ENHANCED_CONTRAST_REGISTRY_KEY \ + HKEY_CURRENT_USER, "Software\\Microsoft\\Avalon.Graphics\\DISPLAY1\\EnhancedContrastLevel" + +void +DWriteFactory::CreateRenderingParams() +{ + if (!Instance()) { + return; + } + + Instance()->CreateRenderingParams(&mDefaultRenderingParams); + + // For EnhancedContrast, we override the default if the user has not set it + // in the registry (by using the ClearType Tuner). + FLOAT contrast; + if (mEnhancedContrast >= 0.0 && mEnhancedContrast <= 10.0) { + contrast = mEnhancedContrast; + } else { + HKEY hKey; + if (RegOpenKeyExA(ENHANCED_CONTRAST_REGISTRY_KEY, + 0, KEY_READ, &hKey) == ERROR_SUCCESS) + { + contrast = mDefaultRenderingParams->GetEnhancedContrast(); + RegCloseKey(hKey); + } else { + contrast = 1.0; + } + } + + // For parameters that have not been explicitly set via the SetRenderingParams API, + // we copy values from default params (or our overridden value for contrast) + FLOAT gamma = + mGamma >= 1.0 && mGamma <= 2.2 ? + mGamma : mDefaultRenderingParams->GetGamma(); + FLOAT clearTypeLevel = + mClearTypeLevel >= 0.0 && mClearTypeLevel <= 1.0 ? + mClearTypeLevel : mDefaultRenderingParams->GetClearTypeLevel(); + DWRITE_PIXEL_GEOMETRY pixelGeometry = + mPixelGeometry >= DWRITE_PIXEL_GEOMETRY_FLAT && mPixelGeometry <= DWRITE_PIXEL_GEOMETRY_BGR ? + (DWRITE_PIXEL_GEOMETRY)mPixelGeometry : mDefaultRenderingParams->GetPixelGeometry(); + DWRITE_RENDERING_MODE renderingMode = + mRenderingMode >= DWRITE_RENDERING_MODE_DEFAULT && mRenderingMode <= DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL_SYMMETRIC ? + (DWRITE_RENDERING_MODE)mRenderingMode : mDefaultRenderingParams->GetRenderingMode(); + Instance()->CreateCustomRenderingParams(gamma, contrast, clearTypeLevel, + pixelGeometry, renderingMode, + &mCustomClearTypeRenderingParams); + Instance()->CreateCustomRenderingParams(gamma, contrast, clearTypeLevel, + pixelGeometry, DWRITE_RENDERING_MODE_CLEARTYPE_GDI_CLASSIC, + &mForceGDIClassicRenderingParams); +} + +static cairo_bool_t +_name_tables_match (cairo_scaled_font_t *font1, + cairo_scaled_font_t *font2) +{ + unsigned long size1; + unsigned long size2; + cairo_int_status_t status1; + cairo_int_status_t status2; + unsigned char *buffer1; + unsigned char *buffer2; + cairo_bool_t result = false; + + if (!font1->backend || !font2->backend || + !font1->backend->load_truetype_table || + !font2->backend->load_truetype_table) + return false; + + status1 = font1->backend->load_truetype_table (font1, + TT_TAG_name, 0, NULL, &size1); + status2 = font2->backend->load_truetype_table (font2, + TT_TAG_name, 0, NULL, &size2); + if (status1 || status2) + return false; + if (size1 != size2) + return false; + + buffer1 = (unsigned char*)malloc (size1); + buffer2 = (unsigned char*)malloc (size2); + + if (buffer1 && buffer2) { + status1 = font1->backend->load_truetype_table (font1, + TT_TAG_name, 0, buffer1, &size1); + status2 = font2->backend->load_truetype_table (font2, + TT_TAG_name, 0, buffer2, &size2); + if (!status1 && !status2) { + result = memcmp (buffer1, buffer2, size1) == 0; + } + } + + free (buffer1); + free (buffer2); + return result; +} + +// Helper for _cairo_win32_printing_surface_show_glyphs to create a win32 equivalent +// of a dwrite scaled_font so that we can print using ExtTextOut instead of drawing +// paths or blitting glyph bitmaps. +cairo_int_status_t +_cairo_dwrite_scaled_font_create_win32_scaled_font (cairo_scaled_font_t *scaled_font, + cairo_scaled_font_t **new_font) +{ + if (cairo_scaled_font_get_type (scaled_font) != CAIRO_FONT_TYPE_DWRITE) { + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + cairo_font_face_t *face = cairo_scaled_font_get_font_face (scaled_font); + cairo_dwrite_font_face_t *dwface = reinterpret_cast<cairo_dwrite_font_face_t*>(face); + + RefPtr<IDWriteGdiInterop> gdiInterop; + DWriteFactory::Instance()->GetGdiInterop(&gdiInterop); + if (!gdiInterop) { + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + LOGFONTW logfont; + if (FAILED(gdiInterop->ConvertFontFaceToLOGFONT (dwface->dwriteface, &logfont))) { + return CAIRO_INT_STATUS_UNSUPPORTED; + } + // DW must have been using an outline font, so we want GDI to use the same, + // even if there's also a bitmap face available + logfont.lfOutPrecision = OUT_OUTLINE_PRECIS; + + cairo_font_face_t *win32_face = cairo_win32_font_face_create_for_logfontw (&logfont); + if (!win32_face) { + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + cairo_matrix_t font_matrix; + cairo_scaled_font_get_font_matrix (scaled_font, &font_matrix); + + cairo_matrix_t ctm; + cairo_scaled_font_get_ctm (scaled_font, &ctm); + + cairo_font_options_t options; + cairo_scaled_font_get_font_options (scaled_font, &options); + + cairo_scaled_font_t *font = cairo_scaled_font_create (win32_face, + &font_matrix, + &ctm, + &options); + cairo_font_face_destroy (win32_face); + + if (!font) { + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + if (!_name_tables_match (font, scaled_font)) { + // If the font name tables aren't equal, then GDI may have failed to + // find the right font and substituted a different font. + cairo_scaled_font_destroy (font); + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + *new_font = font; + return CAIRO_INT_STATUS_SUCCESS; +} diff --git a/src/win32/cairo-dwrite-private.hpp b/src/win32/cairo-dwrite-private.hpp new file mode 100644 index 000000000..07fb0e765 --- /dev/null +++ b/src/win32/cairo-dwrite-private.hpp @@ -0,0 +1,266 @@ +/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */ +/* Cairo - a vector graphics library with display and print output + * + * Copyright © 2010 Mozilla Foundation + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 the Mozilla Foundation + * + * Contributor(s): + * Bas Schouten <bschouten@mozilla.com> + */ + +#include "cairoint.h" +#include <dwrite.h> +#include <d2d1.h> + +/* If either of the dwrite_2.h or d2d1_3.h headers required for color fonts + * is not available, include our own version containing just the functions we need. + */ + +#if HAVE_DWRITE_3_H +#include <dwrite_3.h> +#else +#include "dw-extra.h" +#endif + +#if HAVE_D2D1_3_H +#include <d2d1_3.h> +#else +#include "d2d1-extra.h" +#endif + + +// DirectWrite is not available on all platforms. +typedef HRESULT (WINAPI*DWriteCreateFactoryFunc)( + DWRITE_FACTORY_TYPE factoryType, + REFIID iid, + IUnknown **factory +); + +/* #cairo_scaled_font_t implementation */ +struct _cairo_dwrite_scaled_font { + cairo_scaled_font_t base; + cairo_matrix_t mat; + cairo_matrix_t mat_inverse; + cairo_antialias_t antialias_mode; + DWRITE_MEASURING_MODE measuring_mode; + enum TextRenderingState { + TEXT_RENDERING_UNINITIALIZED, + TEXT_RENDERING_NO_CLEARTYPE, + TEXT_RENDERING_NORMAL, + TEXT_RENDERING_GDI_CLASSIC + }; + TextRenderingState rendering_mode; +}; +typedef struct _cairo_dwrite_scaled_font cairo_dwrite_scaled_font_t; + +class DWriteFactory +{ +public: + static IDWriteFactory *Instance() + { + if (!mFactoryInstance) { +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wcast-function-type" +#endif + DWriteCreateFactoryFunc createDWriteFactory = (DWriteCreateFactoryFunc) + GetProcAddress(LoadLibraryW(L"dwrite.dll"), "DWriteCreateFactory"); +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif + if (createDWriteFactory) { + HRESULT hr = createDWriteFactory( + DWRITE_FACTORY_TYPE_SHARED, + __uuidof(IDWriteFactory), + reinterpret_cast<IUnknown**>(&mFactoryInstance)); + assert(SUCCEEDED(hr)); + } + } + return mFactoryInstance; + } + + static IDWriteFactory4 *Instance4() + { + if (!mFactoryInstance4) { + if (Instance()) { + Instance()->QueryInterface(&mFactoryInstance4); + } + } + return mFactoryInstance4; + } + + static IDWriteFontCollection *SystemCollection() + { + if (!mSystemCollection) { + if (Instance()) { + HRESULT hr = Instance()->GetSystemFontCollection(&mSystemCollection); + assert(SUCCEEDED(hr)); + } + } + return mSystemCollection; + } + + static IDWriteFontFamily *FindSystemFontFamily(const WCHAR *aFamilyName) + { + UINT32 idx; + BOOL found; + if (!SystemCollection()) { + return NULL; + } + SystemCollection()->FindFamilyName(aFamilyName, &idx, &found); + if (!found) { + return NULL; + } + + IDWriteFontFamily *family; + SystemCollection()->GetFontFamily(idx, &family); + return family; + } + + static IDWriteRenderingParams *RenderingParams(cairo_dwrite_scaled_font_t::TextRenderingState mode) + { + if (!mDefaultRenderingParams || + !mForceGDIClassicRenderingParams || + !mCustomClearTypeRenderingParams) + { + CreateRenderingParams(); + } + IDWriteRenderingParams *params; + if (mode == cairo_dwrite_scaled_font_t::TEXT_RENDERING_NO_CLEARTYPE) { + params = mDefaultRenderingParams; + } else if (mode == cairo_dwrite_scaled_font_t::TEXT_RENDERING_GDI_CLASSIC && mRenderingMode < 0) { + params = mForceGDIClassicRenderingParams; + } else { + params = mCustomClearTypeRenderingParams; + } + if (params) { + params->AddRef(); + } + return params; + } + + static void SetRenderingParams(FLOAT aGamma, + FLOAT aEnhancedContrast, + FLOAT aClearTypeLevel, + int aPixelGeometry, + int aRenderingMode) + { + mGamma = aGamma; + mEnhancedContrast = aEnhancedContrast; + mClearTypeLevel = aClearTypeLevel; + mPixelGeometry = aPixelGeometry; + mRenderingMode = aRenderingMode; + // discard any current RenderingParams objects + if (mCustomClearTypeRenderingParams) { + mCustomClearTypeRenderingParams->Release(); + mCustomClearTypeRenderingParams = NULL; + } + if (mForceGDIClassicRenderingParams) { + mForceGDIClassicRenderingParams->Release(); + mForceGDIClassicRenderingParams = NULL; + } + if (mDefaultRenderingParams) { + mDefaultRenderingParams->Release(); + mDefaultRenderingParams = NULL; + } + } + + static int GetClearTypeRenderingMode() { + return mRenderingMode; + } + +private: + static void CreateRenderingParams(); + + static IDWriteFactory *mFactoryInstance; + static IDWriteFactory4 *mFactoryInstance4; + static IDWriteFontCollection *mSystemCollection; + static IDWriteRenderingParams *mDefaultRenderingParams; + static IDWriteRenderingParams *mCustomClearTypeRenderingParams; + static IDWriteRenderingParams *mForceGDIClassicRenderingParams; + static FLOAT mGamma; + static FLOAT mEnhancedContrast; + static FLOAT mClearTypeLevel; + static int mPixelGeometry; + static int mRenderingMode; +}; + +class AutoDWriteGlyphRun : public DWRITE_GLYPH_RUN +{ + static const int kNumAutoGlyphs = 256; + +public: + AutoDWriteGlyphRun() { + glyphCount = 0; + } + + ~AutoDWriteGlyphRun() { + if (glyphCount > kNumAutoGlyphs) { + delete[] glyphIndices; + delete[] glyphAdvances; + delete[] glyphOffsets; + } + } + + void allocate(int aNumGlyphs) { + glyphCount = aNumGlyphs; + if (aNumGlyphs <= kNumAutoGlyphs) { + glyphIndices = &mAutoIndices[0]; + glyphAdvances = &mAutoAdvances[0]; + glyphOffsets = &mAutoOffsets[0]; + } else { + glyphIndices = new UINT16[aNumGlyphs]; + glyphAdvances = new FLOAT[aNumGlyphs]; + glyphOffsets = new DWRITE_GLYPH_OFFSET[aNumGlyphs]; + } + } + +private: + DWRITE_GLYPH_OFFSET mAutoOffsets[kNumAutoGlyphs]; + FLOAT mAutoAdvances[kNumAutoGlyphs]; + UINT16 mAutoIndices[kNumAutoGlyphs]; +}; + +/* #cairo_font_face_t implementation */ +struct _cairo_dwrite_font_face { + cairo_font_face_t base; + IDWriteFontFace *dwriteface; + cairo_bool_t have_color; +}; +typedef struct _cairo_dwrite_font_face cairo_dwrite_font_face_t; + +DWRITE_MATRIX _cairo_dwrite_matrix_from_matrix(const cairo_matrix_t *matrix); + +// This will initialize a DWrite glyph run from cairo glyphs and a scaled_font. +void +_cairo_dwrite_glyph_run_from_glyphs(cairo_glyph_t *glyphs, + int num_glyphs, + cairo_dwrite_scaled_font_t *scaled_font, + AutoDWriteGlyphRun *run, + cairo_bool_t *transformed); diff --git a/src/win32/cairo-win32-display-surface.c b/src/win32/cairo-win32-display-surface.c index a3f224c58..841725474 100644 --- a/src/win32/cairo-win32-display-surface.c +++ b/src/win32/cairo-win32-display-surface.c @@ -168,7 +168,7 @@ _create_dc_and_bitmap (cairo_win32_display_surface_t *surface, case CAIRO_FORMAT_RGB30: case CAIRO_FORMAT_RGB96F: case CAIRO_FORMAT_RGBA128F: - ASSERT_NOT_REACHED; + ASSERT_NOT_REACHED; /* We can't create real RGB24 bitmaps because something seems to * break if we do, especially if we don't set up an image * fallback. It could be a bug with using a 24bpp pixman image diff --git a/src/win32/cairo-win32-font.c b/src/win32/cairo-win32-font.c index ae375ae06..4cd0a1203 100644 --- a/src/win32/cairo-win32-font.c +++ b/src/win32/cairo-win32-font.c @@ -286,8 +286,8 @@ _have_cleartype_quality (void) version_info.dwMinorVersion >= 1)); /* XP or newer */ } -static BYTE -_get_system_quality (void) +BYTE +cairo_win32_get_system_text_quality (void) { BOOL font_smoothing; UINT smoothing_type; @@ -354,7 +354,7 @@ _win32_scaled_font_create (LOGFONTW *logfont, * here is the hint_metrics options. */ if (options->antialias == CAIRO_ANTIALIAS_DEFAULT) - f->quality = _get_system_quality (); + f->quality = cairo_win32_get_system_text_quality (); else { switch (options->antialias) { case CAIRO_ANTIALIAS_NONE: diff --git a/src/win32/cairo-win32-printing-surface.c b/src/win32/cairo-win32-printing-surface.c index d01210745..602a4f3f0 100644 --- a/src/win32/cairo-win32-printing-surface.c +++ b/src/win32/cairo-win32-printing-surface.c @@ -251,7 +251,7 @@ _cairo_win32_printing_surface_acquire_image_pattern ( case CAIRO_PATTERN_TYPE_MESH: default: ASSERT_NOT_REACHED; - break; + return CAIRO_INT_STATUS_UNSUPPORTED; } _cairo_pattern_init_for_surface (image_pattern, &image->base); @@ -1818,6 +1818,7 @@ _cairo_win32_printing_surface_show_glyphs (void *abstract_surfac cairo_solid_pattern_t clear; cairo_composite_rectangles_t extents; cairo_bool_t overlap; + cairo_scaled_font_t *local_scaled_font = NULL; status = _cairo_composite_rectangles_init_for_glyphs (&extents, &surface->win32.base, @@ -1861,6 +1862,13 @@ _cairo_win32_printing_surface_show_glyphs (void *abstract_surfac } #endif +#if CAIRO_HAS_DWRITE_FONT + if (cairo_scaled_font_get_type (scaled_font) == CAIRO_FONT_TYPE_DWRITE) { + status = _cairo_win32_printing_surface_analyze_operation (surface, op, source, &extents.bounded); + goto cleanup_composite; + } +#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 @@ -1901,6 +1909,23 @@ _cairo_win32_printing_surface_show_glyphs (void *abstract_surfac source = opaque; } +#if CAIRO_HAS_DWRITE_FONT + /* For a printer, the dwrite path is not desirable as it goes through the + * bitmap-blitting GDI interop route. Better to create a win32 (GDI) font + * so that ExtTextOut can be used, giving the printer driver the chance + * to do the right thing with the text. + */ + if (cairo_scaled_font_get_type (scaled_font) == CAIRO_FONT_TYPE_DWRITE) { + status = _cairo_dwrite_scaled_font_create_win32_scaled_font (scaled_font, &local_scaled_font); + if (status == CAIRO_STATUS_SUCCESS) { + scaled_font = local_scaled_font; + } else { + /* Reset status; we'll fall back to drawing glyphs as paths */ + status = CAIRO_STATUS_SUCCESS; + } + } +#endif + #if CAIRO_HAS_WIN32_FONT if (cairo_scaled_font_get_type (scaled_font) == CAIRO_FONT_TYPE_WIN32 && source->type == CAIRO_PATTERN_TYPE_SOLID) @@ -1960,6 +1985,10 @@ _cairo_win32_printing_surface_show_glyphs (void *abstract_surfac cleanup_composite: _cairo_composite_rectangles_fini (&extents); + + if (local_scaled_font) + cairo_scaled_font_destroy (local_scaled_font); + return status; } @@ -2193,6 +2222,12 @@ cairo_win32_printing_surface_create (HDC hdc) return paginated; } +cairo_bool_t +_cairo_surface_is_win32_printing (const cairo_surface_t *surface) +{ + return surface->backend && surface->backend->type == CAIRO_SURFACE_TYPE_WIN32_PRINTING; +} + static const cairo_surface_backend_t cairo_win32_printing_surface_backend = { CAIRO_SURFACE_TYPE_WIN32_PRINTING, _cairo_win32_printing_surface_finish, diff --git a/src/win32/cairo-win32-private.h b/src/win32/cairo-win32-private.h index 85f88a9e8..486b12811 100644 --- a/src/win32/cairo-win32-private.h +++ b/src/win32/cairo-win32-private.h @@ -53,6 +53,8 @@ #define WIN32_FONT_LOGICAL_SCALE 32 +CAIRO_BEGIN_DECLS + /* Surface DC flag values */ enum { /* If this is a surface created for printing or not */ @@ -199,6 +201,12 @@ _cairo_win32_gdi_compositor_get (void); cairo_status_t _cairo_win32_print_gdi_error (const char *context); +cairo_bool_t +_cairo_surface_is_win32 (const cairo_surface_t *surface); + +cairo_bool_t +_cairo_surface_is_win32_printing (const cairo_surface_t *surface); + cairo_private void _cairo_win32_display_surface_discard_fallback (cairo_win32_display_surface_t *surface); @@ -245,4 +253,26 @@ _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); +cairo_public BYTE +cairo_win32_get_system_text_quality (void); + +#if CAIRO_HAS_DWRITE_FONT + +cairo_int_status_t +_cairo_dwrite_show_glyphs_on_surface (void *surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_glyph_t *glyphs, + int num_glyphs, + cairo_scaled_font_t *scaled_font, + cairo_clip_t *clip); + +cairo_int_status_t +_cairo_dwrite_scaled_font_create_win32_scaled_font (cairo_scaled_font_t *scaled_font, + cairo_scaled_font_t **new_font); + +#endif /* CAIRO_HAS_DWRITE_FONT */ + +CAIRO_END_DECLS + #endif /* CAIRO_WIN32_PRIVATE_H */ diff --git a/src/win32/cairo-win32-refptr.hpp b/src/win32/cairo-win32-refptr.hpp new file mode 100644 index 000000000..e361e4cf7 --- /dev/null +++ b/src/win32/cairo-win32-refptr.hpp @@ -0,0 +1,178 @@ +/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */ +/* Cairo - a vector graphics library with display and print output + * + * Copyright <A9> 2010 Mozilla Foundation + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 the Mozilla Foundation + * + * Contributor(s): + * Bas Schouten <bschouten@mozilla.com> + */ +#ifndef CAIRO_WIN32_REFPTR_H +#define CAIRO_WIN32_REFPTR_H + +template<typename T> class TemporaryRef; + +/* + * RefPtr points to a refcounted thing that has AddRef and Release + * methods to increase/decrease the refcount, respectively. After a + * RefPtr<T> is assigned a T*, the T* can be used through the RefPtr + * as if it were a T*. + * + * A RefPtr can forget its underlying T*, which results in the T* + * being wrapped in a temporary object until the T* is either + * re-adopted from or released by the temporary. + */ +template<typename T> +class RefPtr +{ + // To allow them to use unref() + friend class TemporaryRef<T>; + + struct dontRef {}; + +public: + RefPtr() : ptr(0) { } + RefPtr(const RefPtr& o) : ptr(ref(o.ptr)) {} + RefPtr(const TemporaryRef<T>& o) : ptr(o.drop()) {} + RefPtr(T* t) : ptr(ref(t)) {} + + template<typename U> + RefPtr(const RefPtr<U>& o) : ptr(ref(o.get())) {} + + ~RefPtr() { unref(ptr); } + + RefPtr& operator=(const RefPtr& o) { + assign(ref(o.ptr)); + return *this; + } + RefPtr& operator=(const TemporaryRef<T>& o) { + assign(o.drop()); + return *this; + } + RefPtr& operator=(T* t) { + assign(ref(t)); + return *this; + } + + template<typename U> + RefPtr& operator=(const RefPtr<U>& o) { + assign(ref(o.get())); + return *this; + } + + TemporaryRef<T> forget() { + T* tmp = ptr; + ptr = 0; + return TemporaryRef<T>(tmp, dontRef()); + } + + T* get() const { return ptr; } + operator T*() const { return ptr; } + T* operator->() const { return ptr; } + T& operator*() const { return *ptr; } + template<typename U> + operator TemporaryRef<U>() { return TemporaryRef<U>(ptr); } + + /** + * WARNING for ease of use, passing a reference will release/clear out ptr! + * We null out the ptr before returning its address so people passing byref + * as input will most likely get functions returning errors rather than accessing + * freed memory. Further more accessing it after this point if it hasn't + * been set will produce a null pointer dereference. + */ + T** operator&() + { + if (ptr) { + ptr->Release(); + ptr = NULL; + } + return &ptr; + } + +private: + void assign(T* t) { + unref(ptr); + ptr = t; + } + + T* ptr; + + static inline T* ref(T* t) { + if (t) { + t->AddRef(); + } + return t; + } + + static inline void unref(T* t) { + if (t) { + t->Release(); + } + } +}; + +/* + * TemporaryRef<T> represents an object that holds a temporary + * reference to a T. TemporaryRef objects can't be manually ref'd or + * unref'd (being temporaries, not lvalues), so can only relinquish + * references to other objects, or unref on destruction. + */ +template<typename T> +class TemporaryRef +{ + // To allow it to construct TemporaryRef from a bare T* + friend class RefPtr<T>; + + typedef typename RefPtr<T>::dontRef dontRef; + +public: + TemporaryRef(T* t) : ptr(RefPtr<T>::ref(t)) {} + TemporaryRef(const TemporaryRef& o) : ptr(o.drop()) {} + + template<typename U> + TemporaryRef(const TemporaryRef<U>& o) : ptr(o.drop()) {} + + ~TemporaryRef() { RefPtr<T>::unref(ptr); } + + T* drop() const { + T* tmp = ptr; + ptr = 0; + return tmp; + } + +private: + TemporaryRef(T* t, const dontRef&) : ptr(t) {} + + mutable T* ptr; + + TemporaryRef(); + TemporaryRef& operator=(const TemporaryRef&); +}; + +#endif diff --git a/src/win32/cairo-win32-surface.c b/src/win32/cairo-win32-surface.c index 73078362d..e53c16540 100644 --- a/src/win32/cairo-win32-surface.c +++ b/src/win32/cairo-win32-surface.c @@ -48,6 +48,7 @@ #include "cairoint.h" +#include "cairo-backend-private.h" #include "cairo-default-context-private.h" #include "cairo-error-private.h" #include "cairo-image-surface-private.h" @@ -178,7 +179,7 @@ cairo_win32_surface_get_dc (cairo_surface_t *surface) * * Return value: %TRUE if the surface is an win32 surface **/ -static inline cairo_bool_t +cairo_bool_t _cairo_surface_is_win32 (const cairo_surface_t *surface) { /* _cairo_surface_nil sets a NULL backend so be safe */ @@ -219,6 +220,16 @@ _cairo_win32_surface_emit_glyphs (cairo_win32_surface_t *dst, cairo_scaled_font_t *scaled_font, cairo_bool_t glyph_indexing) { +#if CAIRO_HAS_DWRITE_FONT + if (scaled_font->backend->type == CAIRO_FONT_TYPE_DWRITE) { + if (!glyph_indexing) return CAIRO_INT_STATUS_UNSUPPORTED; + + // FIXME: fake values for params that aren't currently passed in here + cairo_operator_t op = CAIRO_OPERATOR_SOURCE; + cairo_clip_t *clip = NULL; + return _cairo_dwrite_show_glyphs_on_surface (dst, op, source, glyphs, num_glyphs, scaled_font, clip /* , glyph_indexing */ ); + } +#endif #if CAIRO_HAS_WIN32_FONT WORD glyph_buf_stack[STACK_GLYPH_SIZE]; WORD *glyph_buf = glyph_buf_stack; diff --git a/src/win32/d2d1-extra.h b/src/win32/d2d1-extra.h new file mode 100644 index 000000000..16099d834 --- /dev/null +++ b/src/win32/d2d1-extra.h @@ -0,0 +1,111 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ + +/* Mingw-w64 does not have d2d1_2.h and d2d2_3.h. + * + * + * We only need the definitions of two functions: + * ID2D1DeviceContext4::DrawColorBitmapGlyphRun() + * ID2D1DeviceContext4::DrawSvgGlyphRun() + * + * But we need to include all the prior functions in the same struct, + * and parent structs, so that the functions are in the correct position + * in the vtable. The parameters of the unused functions are not + * required as we only need a function in the struct to create a + * function pointer in the vtable. + */ + +#ifndef D2D1_EXTRA_H +#define D2D1_EXTRA_H + +#include <d2d1_1.h> + +interface ID2D1DeviceContext1; +interface ID2D1DeviceContext2; +interface ID2D1DeviceContext3; +interface ID2D1DeviceContext4; +interface ID2D1SvgGlyphStyle; + +enum D2D1_COLOR_BITMAP_GLYPH_SNAP_OPTION { + D2D1_COLOR_BITMAP_GLYPH_SNAP_OPTION_DEFAULT, + D2D1_COLOR_BITMAP_GLYPH_SNAP_OPTION_DISABLE, + D2D1_COLOR_BITMAP_GLYPH_SNAP_OPTION_FORCE_DWORD +}; + +DEFINE_GUID(IID_ID2D1DeviceContext1, 0xd37f57e4, 0x6908, 0x459f, 0xa1, 0x99, 0xe7, 0x2f, 0x24, 0xf7, 0x99, 0x87); +MIDL_INTERFACE("d37f57e4-6908-459f-a199-e72f24f79987") +ID2D1DeviceContext1 : public ID2D1DeviceContext +{ + virtual void STDMETHODCALLTYPE CreateFilledGeometryRealization() = 0; + virtual void STDMETHODCALLTYPE CreateStrokedGeometryRealization() = 0; + virtual void STDMETHODCALLTYPE DrawGeometryRealization() = 0; +}; +__CRT_UUID_DECL(ID2D1DeviceContext1, 0xd37f57e4, 0x6908, 0x459f, 0xa1, 0x99, 0xe7, 0x2f, 0x24, 0xf7, 0x99, 0x87) + +DEFINE_GUID(IID_ID2D1DeviceContext2, 0x394ea6a3, 0x0c34, 0x4321, 0x95, 0x0b, 0x6c, 0xa2, 0x0f, 0x0b, 0xe6, 0xc7); +MIDL_INTERFACE("394ea6a3-0c34-4321-950b-6ca20f0be6c7") +ID2D1DeviceContext2 : public ID2D1DeviceContext1 +{ + virtual void STDMETHODCALLTYPE CreateInk() = 0; + virtual void STDMETHODCALLTYPE CreateInkStyle() = 0; + virtual void STDMETHODCALLTYPE CreateGradientMesh() = 0; + virtual void STDMETHODCALLTYPE CreateImageSourceFromWic() = 0; + virtual void STDMETHODCALLTYPE CreateLookupTable3D() = 0; + virtual void STDMETHODCALLTYPE CreateImageSourceFromDxgi() = 0; + virtual void STDMETHODCALLTYPE GetGradientMeshWorldBounds() = 0; + virtual void STDMETHODCALLTYPE DrawInk() = 0; + virtual void STDMETHODCALLTYPE DrawGradientMesh() = 0; + virtual void STDMETHODCALLTYPE DrawGdiMetafile() = 0; + virtual void STDMETHODCALLTYPE CreateTransformedImageSource() = 0; +}; +__CRT_UUID_DECL(ID2D1DeviceContext2, 0x394ea6a3, 0x0c34, 0x4321, 0x95, 0x0b, 0x6c, 0xa2, 0x0f, 0x0b, 0xe6, 0xc7) + + +DEFINE_GUID(IID_ID2D1DeviceContext3, 0x235a7496, 0x8351, 0x414c, 0xbc, 0xd4, 0x66, 0x72, 0xab, 0x2d, 0x8e, 0x00); +MIDL_INTERFACE("235a7496-8351-414c-bcd4-6672ab2d8e00") +ID2D1DeviceContext3 : public ID2D1DeviceContext2 +{ + virtual void STDMETHODCALLTYPE CreateSpriteBatch() = 0; + virtual void STDMETHODCALLTYPE DrawSpriteBatch() = 0; +}; +__CRT_UUID_DECL(ID2D1DeviceContext3, 0x235a7496, 0x8351, 0x414c, 0xbc, 0xd4, 0x66, 0x72, 0xab, 0x2d, 0x8e, 0x00) + + +DEFINE_GUID(IID_ID2D1SvgGlyphStyle, 0xaf671749, 0xd241, 0x4db8, 0x8e, 0x41, 0xdc, 0xc2, 0xe5, 0xc1, 0xa4, 0x38); +MIDL_INTERFACE("af671749-d241-4db8-8e41-dcc2e5c1a438") +ID2D1SvgGlyphStyle : public ID2D1Resource +{ + virtual void STDMETHODCALLTYPE SetFill() = 0; + virtual void STDMETHODCALLTYPE GetFill() = 0; + virtual void STDMETHODCALLTYPE SetStroke() = 0; + virtual void STDMETHODCALLTYPE GetStrokeDashesCount() = 0; + virtual void STDMETHODCALLTYPE GetStroke() = 0; +}; +__CRT_UUID_DECL(ID2D1SvgGlyphStyle, 0xaf671749, 0xd241, 0x4db8, 0x8e, 0x41, 0xdc, 0xc2, 0xe5, 0xc1, 0xa4, 0x38) + + +DEFINE_GUID(IID_ID2D1DeviceContext4, 0x8c427831, 0x3d90, 0x4476, 0xb6, 0x47, 0xc4, 0xfa, 0xe3, 0x49, 0xe4, 0xdb); +MIDL_INTERFACE("8c427831-3d90-4476-b647-c4fae349e4db") +ID2D1DeviceContext4 : public ID2D1DeviceContext3 +{ + virtual void STDMETHODCALLTYPE CreateSvgGlyphStyle() = 0; + virtual void STDMETHODCALLTYPE DrawText() = 0; + virtual void STDMETHODCALLTYPE DrawTextLayout() = 0; + virtual void STDMETHODCALLTYPE DrawColorBitmapGlyphRun( + DWRITE_GLYPH_IMAGE_FORMATS glyphImageFormat, + D2D1_POINT_2F baselineOrigin, + const DWRITE_GLYPH_RUN *glyphRun, + DWRITE_MEASURING_MODE measuringMode, + D2D1_COLOR_BITMAP_GLYPH_SNAP_OPTION bitmapSnapOption) = 0; + + virtual void STDMETHODCALLTYPE DrawSvgGlyphRun( + D2D1_POINT_2F baselineOrigin, + const DWRITE_GLYPH_RUN *glyphRun, + ID2D1Brush *defaultFillBrush, + ID2D1SvgGlyphStyle *svgGlyphStyle, + UINT32 colorPaletteIndex, + DWRITE_MEASURING_MODE measuringMode) = 0; + +}; +__CRT_UUID_DECL(ID2D1DeviceContext4, 0x8c427831, 0x3d90, 0x4476, 0xb6, 0x47, 0xc4, 0xfa, 0xe3, 0x49, 0xe4, 0xdb) + +#endif diff --git a/src/win32/dw-extra.h b/src/win32/dw-extra.h new file mode 100644 index 000000000..f6e37e609 --- /dev/null +++ b/src/win32/dw-extra.h @@ -0,0 +1,78 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ + +/* Mingw-w64 dwrite_3.h is broken + * + * We only need the definitions of one function and its dependencies. + * IDWriteFactory4::TranslateColorGlyphRun + * + * But we need to include all the prior functions in the same struct, + * and parent structs, so that the functions are in the correct position + * in the vtable. The parameters of the unused functions are not + * required as we only need a function in the struct to create a + * function pointer in the vtable. + */ + +#ifndef DWRITE_EXTRA_H +#define DWRITE_EXTRA_H + +#pragma GCC diagnostic ignored "-Wnon-virtual-dtor" + +#include <dwrite_2.h> + +interface IDWriteFactory3; +interface IDWriteFactory4; +interface IDWriteColorGlyphRunEnumerator1; + +DEFINE_ENUM_FLAG_OPERATORS(DWRITE_GLYPH_IMAGE_FORMATS); + +struct DWRITE_COLOR_GLYPH_RUN1 : DWRITE_COLOR_GLYPH_RUN +{ + DWRITE_GLYPH_IMAGE_FORMATS glyphImageFormat; + DWRITE_MEASURING_MODE measuringMode; +}; + + +DEFINE_GUID(IID_IDWriteColorGlyphRunEnumerator1, 0x7c5f86da, 0xc7a1, 0x4f05, 0xb8,0xe1, 0x55,0xa1,0x79,0xfe,0x5a,0x35); +MIDL_INTERFACE("7c5f86da-c7a1-4f05-b8e1-55a179fe5a35") +IDWriteColorGlyphRunEnumerator1 : public IDWriteColorGlyphRunEnumerator +{ + virtual HRESULT STDMETHODCALLTYPE GetCurrentRun( + const DWRITE_COLOR_GLYPH_RUN1 **run) = 0; + +}; +__CRT_UUID_DECL(IDWriteColorGlyphRunEnumerator1, 0x7c5f86da, 0xc7a1, 0x4f05, 0xb8,0xe1, 0x55,0xa1,0x79,0xfe,0x5a,0x35) + +DEFINE_GUID(IID_IDWriteFactory3, 0x9a1b41c3, 0xd3bb, 0x466a, 0x87,0xfc, 0xfe,0x67,0x55,0x6a,0x3b,0x65); +MIDL_INTERFACE("9a1b41c3-d3bb-466a-87fc-fe67556a3b65") +IDWriteFactory3 : public IDWriteFactory2 +{ + virtual void STDMETHODCALLTYPE CreateGlyphRunAnalysis() = 0; + virtual void STDMETHODCALLTYPE CreateCustomRenderingParams() = 0; + virtual void STDMETHODCALLTYPE CreateFontFaceReference() = 0; + virtual void STDMETHODCALLTYPE CreateFontFaceReference2() = 0; + virtual void STDMETHODCALLTYPE GetSystemFontSet() = 0; + virtual void STDMETHODCALLTYPE CreateFontSetBuilder() = 0; + virtual void STDMETHODCALLTYPE CreateFontCollectionFromFontSet() = 0; + virtual void STDMETHODCALLTYPE GetSystemFontCollection() = 0; + virtual void STDMETHODCALLTYPE GetFontDownloadQueue() = 0; +}; +__CRT_UUID_DECL(IDWriteFactory3, 0x9a1b41c3, 0xd3bb, 0x466a, 0x87,0xfc, 0xfe,0x67,0x55,0x6a,0x3b,0x65) + +DEFINE_GUID(IID_IDWriteFactory4, 0x4b0b5bd3, 0x0797, 0x4549, 0x8a,0xc5, 0xfe,0x91,0x5c,0xc5,0x38,0x56); +MIDL_INTERFACE("4b0b5bd3-0797-4549-8ac5-fe915cc53856") +IDWriteFactory4 : public IDWriteFactory3 +{ + virtual HRESULT STDMETHODCALLTYPE TranslateColorGlyphRun( + D2D1_POINT_2F baselineOrigin, + DWRITE_GLYPH_RUN const *glyphRun, + DWRITE_GLYPH_RUN_DESCRIPTION const *glyphRunDescription, + DWRITE_GLYPH_IMAGE_FORMATS desiredGlyphImageFormats, + DWRITE_MEASURING_MODE measuringMode, + DWRITE_MATRIX const *worldAndDpiTransform, + UINT32 colorPaletteIndex, + IDWriteColorGlyphRunEnumerator1 **colorLayers) = 0; +}; +__CRT_UUID_DECL(IDWriteFactory4, 0x4b0b5bd3, 0x0797, 0x4549, 0x8a,0xc5, 0xfe,0x91,0x5c,0xc5,0x38,0x56) + + +#endif /* DWRITE_EXTRA_H */ |