1591 lines
50 KiB
C++
1591 lines
50 KiB
C++
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
#include "cairoint.h"
|
|
|
|
#include "cairo-win32-private.h"
|
|
#include "cairo-surface-private.h"
|
|
#include "cairo-clip-private.h"
|
|
|
|
#include "cairo-d2d-private.h"
|
|
#include "cairo-dwrite-private.h"
|
|
#include "cairo-truetype-subset-private.h"
|
|
#include <float.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);
|
|
|
|
class D2DFactory
|
|
{
|
|
public:
|
|
static ID2D1Factory *Instance()
|
|
{
|
|
if (!mFactoryInstance) {
|
|
D2D1CreateFactoryFunc createD2DFactory = (D2D1CreateFactoryFunc)
|
|
GetProcAddress(LoadLibraryW(L"d2d1.dll"), "D2D1CreateFactory");
|
|
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 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 ID2D1DCRenderTarget *mRenderTarget;
|
|
};
|
|
|
|
IDWriteFactory *DWriteFactory::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 void
|
|
_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);
|
|
|
|
cairo_warn cairo_int_status_t
|
|
_cairo_dwrite_scaled_show_glyphs(void *scaled_font,
|
|
cairo_operator_t op,
|
|
const cairo_pattern_t *pattern,
|
|
cairo_surface_t *surface,
|
|
int source_x,
|
|
int source_y,
|
|
int dest_x,
|
|
int dest_y,
|
|
unsigned int width,
|
|
unsigned int height,
|
|
cairo_glyph_t *glyphs,
|
|
int num_glyphs,
|
|
cairo_region_t *clip_region,
|
|
int *remaining_glyphs);
|
|
|
|
cairo_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);
|
|
|
|
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,
|
|
_cairo_dwrite_ucs4_to_index,
|
|
_cairo_dwrite_scaled_show_glyphs,
|
|
_cairo_dwrite_load_truetype_table,
|
|
NULL,
|
|
};
|
|
|
|
/* Helper conversion functions */
|
|
|
|
/**
|
|
* Get a D2D matrix from a cairo matrix. Note that D2D uses row vectors where cairo
|
|
* uses column vectors. Hence the transposition.
|
|
*
|
|
* \param Cairo matrix
|
|
* \return D2D matrix
|
|
*/
|
|
static D2D1::Matrix3x2F
|
|
_cairo_d2d_matrix_from_matrix(const cairo_matrix_t *matrix)
|
|
{
|
|
return D2D1::Matrix3x2F((FLOAT)matrix->xx,
|
|
(FLOAT)matrix->yx,
|
|
(FLOAT)matrix->xy,
|
|
(FLOAT)matrix->yy,
|
|
(FLOAT)matrix->x0,
|
|
(FLOAT)matrix->y0);
|
|
}
|
|
|
|
|
|
/**
|
|
* 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);
|
|
|
|
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;
|
|
}
|
|
|
|
cairo_dwrite_font_face_t *face = (cairo_dwrite_font_face_t*)malloc(sizeof(cairo_dwrite_font_face_t));
|
|
HRESULT hr = family->GetFirstMatchingFont(weight, DWRITE_FONT_STRETCH_NORMAL, style, &face->font);
|
|
if (SUCCEEDED(hr)) {
|
|
// Cannot use C++ style new since cairo deallocates this.
|
|
*font_face = (cairo_font_face_t*)face;
|
|
_cairo_font_face_init (&(*(_cairo_dwrite_font_face**)font_face)->base, &_cairo_dwrite_font_face_backend);
|
|
} else {
|
|
free(face);
|
|
}
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
|
}
|
|
|
|
static void
|
|
_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();
|
|
if (dwrite_font_face->font)
|
|
dwrite_font_face->font->Release();
|
|
}
|
|
|
|
|
|
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_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 *dwriteFont = (cairo_dwrite_scaled_font_t*)malloc(sizeof(cairo_dwrite_scaled_font_t));
|
|
*font = reinterpret_cast<cairo_scaled_font_t*>(dwriteFont);
|
|
_cairo_scaled_font_init(&dwriteFont->base, &font_face->base, font_matrix, ctm, options, &_cairo_dwrite_scaled_font_backend);
|
|
|
|
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;
|
|
|
|
dwriteFont->mat = dwriteFont->base.ctm;
|
|
cairo_matrix_multiply(&dwriteFont->mat, &dwriteFont->mat, font_matrix);
|
|
dwriteFont->mat_inverse = dwriteFont->mat;
|
|
cairo_matrix_invert (&dwriteFont->mat_inverse);
|
|
|
|
cairo_antialias_t default_quality = CAIRO_ANTIALIAS_SUBPIXEL;
|
|
|
|
dwriteFont->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;
|
|
dwriteFont->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;
|
|
dwriteFont->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) {
|
|
dwriteFont->antialias_mode = default_quality;
|
|
} else {
|
|
dwriteFont->antialias_mode = options->antialias;
|
|
}
|
|
|
|
dwriteFont->manual_show_glyphs_allowed = TRUE;
|
|
dwriteFont->rendering_mode =
|
|
default_quality == CAIRO_ANTIALIAS_SUBPIXEL ?
|
|
cairo_d2d_surface_t::TEXT_RENDERING_NORMAL : cairo_d2d_surface_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)
|
|
{
|
|
cairo_dwrite_scaled_font_t *scaled_dwrite_font = static_cast<cairo_dwrite_scaled_font_t*>(scaled_font);
|
|
cairo_int_status_t status;
|
|
|
|
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_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_warn cairo_int_status_t
|
|
_cairo_dwrite_scaled_show_glyphs(void *scaled_font,
|
|
cairo_operator_t op,
|
|
const cairo_pattern_t *pattern,
|
|
cairo_surface_t *generic_surface,
|
|
int source_x,
|
|
int source_y,
|
|
int dest_x,
|
|
int dest_y,
|
|
unsigned int width,
|
|
unsigned int height,
|
|
cairo_glyph_t *glyphs,
|
|
int num_glyphs,
|
|
cairo_region_t *clip_region,
|
|
int *remaining_glyphs)
|
|
{
|
|
cairo_win32_surface_t *surface = (cairo_win32_surface_t *)generic_surface;
|
|
cairo_int_status_t status;
|
|
|
|
if (width == 0 || height == 0)
|
|
return (cairo_int_status_t)CAIRO_STATUS_SUCCESS;
|
|
|
|
if (_cairo_surface_is_win32 (generic_surface) &&
|
|
surface->format == CAIRO_FORMAT_RGB24 &&
|
|
op == CAIRO_OPERATOR_OVER) {
|
|
|
|
//XXX: we need to set the clip region here
|
|
|
|
status = (cairo_int_status_t)_cairo_dwrite_show_glyphs_on_surface (surface, op, pattern,
|
|
glyphs, num_glyphs,
|
|
(cairo_scaled_font_t*)scaled_font, NULL);
|
|
|
|
return status;
|
|
} else {
|
|
cairo_dwrite_scaled_font_t *dwritesf =
|
|
static_cast<cairo_dwrite_scaled_font_t*>(scaled_font);
|
|
BOOL transform = FALSE;
|
|
|
|
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);
|
|
|
|
run.bidiLevel = 0;
|
|
run.fontFace = ((cairo_dwrite_font_face_t*)dwritesf->base.font_face)->dwriteface;
|
|
run.isSideways = FALSE;
|
|
IDWriteGlyphRunAnalysis *analysis;
|
|
|
|
if (dwritesf->mat.xy == 0 && dwritesf->mat.yx == 0 &&
|
|
dwritesf->mat.xx == dwritesf->base.font_matrix.xx &&
|
|
dwritesf->mat.yy == dwritesf->base.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)dest_y - (FLOAT)glyphs[i].y;
|
|
offsets[i].advanceOffset = (FLOAT)glyphs[i].x - dest_x;
|
|
advances[i] = 0.0;
|
|
}
|
|
run.fontEmSize = (FLOAT)dwritesf->base.font_matrix.yy;
|
|
} else {
|
|
transform = TRUE;
|
|
|
|
for (int i = 0; i < num_glyphs; i++) {
|
|
indices[i] = (WORD) glyphs[i].index;
|
|
double x = glyphs[i].x - dest_x;
|
|
double y = glyphs[i].y - dest_y;
|
|
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.
|
|
offsets[i].ascenderOffset = -(FLOAT)y;
|
|
offsets[i].advanceOffset = (FLOAT)x;
|
|
advances[i] = 0.0;
|
|
}
|
|
run.fontEmSize = 1.0f;
|
|
}
|
|
|
|
HRESULT hr;
|
|
if (!transform) {
|
|
hr = DWriteFactory::Instance()->CreateGlyphRunAnalysis(&run,
|
|
1.0f,
|
|
NULL,
|
|
DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL_SYMMETRIC,
|
|
DWRITE_MEASURING_MODE_NATURAL,
|
|
0,
|
|
0,
|
|
&analysis);
|
|
} else {
|
|
DWRITE_MATRIX dwmatrix = _cairo_dwrite_matrix_from_matrix(&dwritesf->mat);
|
|
hr = DWriteFactory::Instance()->CreateGlyphRunAnalysis(&run,
|
|
1.0f,
|
|
&dwmatrix,
|
|
DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL_SYMMETRIC,
|
|
DWRITE_MEASURING_MODE_NATURAL,
|
|
0,
|
|
0,
|
|
&analysis);
|
|
}
|
|
|
|
if (FAILED(hr) || !analysis) {
|
|
return CAIRO_INT_STATUS_UNSUPPORTED;
|
|
}
|
|
|
|
RECT r;
|
|
r.left = 0;
|
|
r.top = 0;
|
|
r.right = width;
|
|
r.bottom = height;
|
|
|
|
BYTE *surface = new BYTE[width * height * 3];
|
|
|
|
analysis->CreateAlphaTexture(DWRITE_TEXTURE_CLEARTYPE_3x1, &r, surface, width * height * 3);
|
|
|
|
cairo_image_surface_t *mask_surface =
|
|
(cairo_image_surface_t*)cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height);
|
|
|
|
cairo_surface_flush(&mask_surface->base);
|
|
|
|
for (unsigned int y = 0; y < height; y++) {
|
|
for (unsigned int x = 0; x < width; x++) {
|
|
mask_surface->data[y * mask_surface->stride + x * 4] = surface[y * width * 3 + x * 3 + 1];
|
|
mask_surface->data[y * mask_surface->stride + x * 4 + 1] = surface[y * width * 3 + x * 3 + 1];
|
|
mask_surface->data[y * mask_surface->stride + x * 4 + 2] = surface[y * width * 3 + x * 3 + 1];
|
|
mask_surface->data[y * mask_surface->stride + x * 4 + 3] = surface[y * width * 3 + x * 3 + 1];
|
|
}
|
|
}
|
|
cairo_surface_mark_dirty(&mask_surface->base);
|
|
|
|
pixman_image_set_component_alpha(mask_surface->pixman_image, 1);
|
|
|
|
cairo_surface_pattern_t mask;
|
|
_cairo_pattern_init_for_surface (&mask, &mask_surface->base);
|
|
|
|
status = (cairo_int_status_t)_cairo_surface_composite (op, pattern,
|
|
&mask.base,
|
|
generic_surface,
|
|
source_x, source_y,
|
|
0, 0,
|
|
dest_x, dest_y,
|
|
width, height,
|
|
clip_region);
|
|
|
|
_cairo_pattern_fini (&mask.base);
|
|
|
|
analysis->Release();
|
|
delete [] surface;
|
|
|
|
cairo_surface_destroy (&mask_surface->base);
|
|
*remaining_glyphs = 0;
|
|
|
|
return (cairo_int_status_t)CAIRO_STATUS_SUCCESS;
|
|
}
|
|
}
|
|
|
|
/* 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));
|
|
}
|
|
|
|
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));
|
|
}
|
|
}
|
|
|
|
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));
|
|
}
|
|
}
|
|
|
|
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]));
|
|
}
|
|
}
|
|
|
|
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_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);
|
|
_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 CAIRO_INT_STATUS_SUCCESS;
|
|
}
|
|
|
|
/* Helper function also stolen from cairo-win32-font.c */
|
|
|
|
/* Compute an alpha-mask from a monochrome RGB24 image
|
|
*/
|
|
static cairo_surface_t *
|
|
_compute_a8_mask (cairo_win32_surface_t *mask_surface)
|
|
{
|
|
cairo_image_surface_t *image24 = (cairo_image_surface_t *)mask_surface->image;
|
|
cairo_image_surface_t *image8;
|
|
int i, j;
|
|
|
|
if (image24->base.status)
|
|
return cairo_surface_reference (&image24->base);
|
|
|
|
image8 = (cairo_image_surface_t *)cairo_image_surface_create (CAIRO_FORMAT_A8,
|
|
image24->width, image24->height);
|
|
if (image8->base.status)
|
|
return &image8->base;
|
|
|
|
for (i = 0; i < image24->height; i++) {
|
|
uint32_t *p = (uint32_t *) (image24->data + i * image24->stride);
|
|
unsigned char *q = (unsigned char *) (image8->data + i * image8->stride);
|
|
|
|
for (j = 0; j < image24->width; j++) {
|
|
*q = 255 - ((*p & 0x0000ff00) >> 8);
|
|
p++;
|
|
q++;
|
|
}
|
|
}
|
|
|
|
return &image8->base;
|
|
}
|
|
|
|
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);
|
|
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;
|
|
}
|
|
|
|
// WIN32 Helper Functions
|
|
cairo_font_face_t*
|
|
cairo_dwrite_font_face_create_for_dwrite_fontface(void* dwrite_font, void* dwrite_font_face)
|
|
{
|
|
IDWriteFont *dwritefont = static_cast<IDWriteFont*>(dwrite_font);
|
|
IDWriteFontFace *dwriteface = static_cast<IDWriteFontFace*>(dwrite_font_face);
|
|
cairo_dwrite_font_face_t *face = new cairo_dwrite_font_face_t;
|
|
cairo_font_face_t *font_face;
|
|
|
|
dwriteface->AddRef();
|
|
|
|
face->dwriteface = dwriteface;
|
|
face->font = NULL;
|
|
|
|
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_allow_manual_show_glyphs(cairo_scaled_font_t *dwrite_scaled_font, cairo_bool_t allowed)
|
|
{
|
|
cairo_dwrite_scaled_font_t *font = reinterpret_cast<cairo_dwrite_scaled_font_t*>(dwrite_scaled_font);
|
|
font->manual_show_glyphs_allowed = allowed;
|
|
}
|
|
|
|
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_d2d_surface_t::TEXT_RENDERING_NORMAL) {
|
|
font->rendering_mode = cairo_d2d_surface_t::TEXT_RENDERING_GDI_CLASSIC;
|
|
} else if (!force && font->rendering_mode == cairo_d2d_surface_t::TEXT_RENDERING_GDI_CLASSIC) {
|
|
font->rendering_mode = cairo_d2d_surface_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_d2d_surface_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_d2d_surface_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_d2d_surface_t::TEXT_RENDERING_NORMAL ||
|
|
renderingState == cairo_d2d_surface_t::TEXT_RENDERING_GDI_CLASSIC) &&
|
|
!surface->base.permit_subpixel_antialiasing) {
|
|
renderingState = cairo_d2d_surface_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_d2d_surface_t::TEXT_RENDERING_GDI_CLASSIC:
|
|
case cairo_d2d_surface_t::TEXT_RENDERING_NO_CLEARTYPE:
|
|
measureMode = DWRITE_MEASURING_MODE_GDI_CLASSIC;
|
|
break;
|
|
default:
|
|
measureMode = DWRITE_MEASURING_MODE_NATURAL;
|
|
break;
|
|
}
|
|
HRESULT hr = 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);
|
|
|
|
printf("Rendering to surface: %p\n", surface->dc);
|
|
|
|
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 (clip != NULL) {
|
|
if ((dst->flags & CAIRO_WIN32_SURFACE_FOR_PRINTING) == 0) {
|
|
cairo_region_t *clip_region;
|
|
cairo_int_status_t status;
|
|
|
|
status = _cairo_clip_get_region (clip, &clip_region);
|
|
assert (status != CAIRO_INT_STATUS_NOTHING_TO_DO);
|
|
if (status)
|
|
return status;
|
|
|
|
_cairo_win32_surface_set_clip_region (dst, clip_region);
|
|
}
|
|
} else {
|
|
_cairo_win32_surface_set_clip_region (surface, NULL);
|
|
}
|
|
|
|
/* 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;
|
|
}
|