Merge branch 'dwrite-font-variations' into 'master'

DWrite: Add support for font variations

Closes #877

See merge request cairo/cairo!601
This commit is contained in:
Emmanuele Bassi 2025-01-30 09:26:49 +00:00
commit e2a52c0870
2 changed files with 144 additions and 42 deletions

View file

@ -507,7 +507,7 @@ _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();
dwrite_font_face->dwriteface->Release();
if (dwrite_font_face->rendering_params)
dwrite_font_face->rendering_params->Release();
return TRUE;
@ -532,10 +532,8 @@ _cairo_dwrite_glyph_run_from_glyphs(cairo_glyph_t *glyphs,
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->fontFace = scaled_font->dwriteface;
run->glyphCount = num_glyphs;
run->isSideways = FALSE;
@ -628,6 +626,54 @@ do_grayscale(IDWriteFontFace *dwface, unsigned int ppem)
return true;
}
static void
set_dwrite_axes_from_string (const char *string,
DWRITE_FONT_AXIS_VALUE *dwrite_axes,
UINT32 count)
{
const char *p = string;
while (p && *p) {
const char *start;
const char *end, *end2;
DWRITE_FONT_AXIS_TAG tag;
double value;
while (_cairo_isspace (*p)) p++;
start = p;
end = strchr (p, ',');
if (end && (end - p < 6))
goto skip;
tag = DWRITE_MAKE_FONT_AXIS_TAG (p[0], p[1], p[2], p[3]);
p += 4;
while (_cairo_isspace (*p)) p++;
if (*p == '=') p++;
if (p - start < 5)
goto skip;
value = _cairo_strtod (p, (char **) &end2);
while (end2 && _cairo_isspace (*end2)) end2++;
if (end2 && (*end2 != ',' && *end2 != '\0'))
goto skip;
for (UINT32 i = 0; i < count; i++) {
if (dwrite_axes[i].axisTag == tag) {
dwrite_axes[i].value = value;
break;
}
}
skip:
p = end ? end + 1 : NULL;
}
}
static cairo_status_t
_cairo_dwrite_font_face_scaled_font_create (void *abstract_face,
const cairo_matrix_t *font_matrix,
@ -656,6 +702,60 @@ _cairo_dwrite_font_face_scaled_font_create (void *abstract_face,
return status;
}
if (options->variations) {
RefPtr<IDWriteFontFace5> dwriteface5;
/* Since Windows 10 20348 */
if (SUCCEEDED (font_face->dwriteface->QueryInterface(&dwriteface5))) {
RefPtr<IDWriteFontResource> dwritefontresource;
if (dwriteface5->HasVariations () &&
SUCCEEDED (dwriteface5->GetFontResource (&dwritefontresource)))
{
UINT32 count = MIN (dwriteface5->GetFontAxisValueCount (), 500);
DWRITE_FONT_AXIS_VALUE *dwrite_axes = new DWRITE_FONT_AXIS_VALUE[count];
UINT32 variables_count = 0;
/* Sort variable axes first */
for (UINT32 i = 0; i < count; i++) {
if (dwritefontresource->GetFontAxisAttributes (i) & DWRITE_FONT_AXIS_ATTRIBUTES_VARIABLE) {
if (variables_count != i) {
DWRITE_FONT_AXIS_VALUE swap_aux = dwrite_axes[variables_count];
dwrite_axes[variables_count] = dwrite_axes[i];
dwrite_axes[i] = swap_aux;
}
variables_count++;
}
}
if (SUCCEEDED (dwriteface5->GetFontAxisValues(dwrite_axes, count))) {
RefPtr<IDWriteFontFace5> dwriteface_new5;
set_dwrite_axes_from_string (options->variations, dwrite_axes, variables_count);
/* Can't use constexpr with mingw-w64 headers */
const DWRITE_FONT_SIMULATIONS all_simulations = DWRITE_FONT_SIMULATIONS_BOLD |
DWRITE_FONT_SIMULATIONS_OBLIQUE;
if (SUCCEEDED (dwritefontresource->CreateFontFace(all_simulations,
dwrite_axes,
count,
&dwriteface_new5)))
{
dwrite_font->dwriteface = dwriteface_new5.forget().drop();
}
}
delete[] dwrite_axes;
}
}
}
if (!dwrite_font->dwriteface) {
font_face->dwriteface->AddRef ();
dwrite_font->dwriteface = font_face->dwriteface;
}
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;
@ -667,9 +767,9 @@ _cairo_dwrite_font_face_scaled_font_create (void *abstract_face,
if (dwrite_font->measuring_mode == DWRITE_MEASURING_MODE_GDI_CLASSIC ||
dwrite_font->measuring_mode == DWRITE_MEASURING_MODE_GDI_NATURAL) {
DWRITE_MATRIX transform = _cairo_dwrite_matrix_from_matrix (&dwrite_font->mat);
font_face->dwriteface->GetGdiCompatibleMetrics(1, 1, &transform, &metrics);
dwrite_font->dwriteface->GetGdiCompatibleMetrics(1, 1, &transform, &metrics);
} else {
font_face->dwriteface->GetMetrics(&metrics);
dwrite_font->dwriteface->GetMetrics(&metrics);
}
extents.ascent = (FLOAT)metrics.ascent / metrics.designUnitsPerEm;
@ -698,7 +798,7 @@ _cairo_dwrite_font_face_scaled_font_create (void *abstract_face,
}
if (default_quality == CAIRO_ANTIALIAS_GRAY) {
if (!do_grayscale(font_face->dwriteface, (unsigned int)_cairo_round(font_matrix->yy))) {
if (!do_grayscale(dwrite_font->dwriteface, (unsigned int)_cairo_round(font_matrix->yy))) {
default_quality = CAIRO_ANTIALIAS_NONE;
}
}
@ -720,6 +820,9 @@ static void
_cairo_dwrite_scaled_font_fini(void *scaled_font)
{
cairo_dwrite_scaled_font_t *dwrite_font = static_cast<cairo_dwrite_scaled_font_t*>(scaled_font);
dwrite_font->dwriteface->Release ();
if (dwrite_font->rendering_params)
dwrite_font->rendering_params->Release();
}
@ -765,10 +868,9 @@ _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 = 0;
UINT16 index;
face->dwriteface->GetGlyphIndicesA(&ucs4, 1, &index);
dwritesf->dwriteface->GetGlyphIndicesA(&ucs4, 1, &index);
return index;
}
@ -787,12 +889,12 @@ _cairo_dwrite_scaled_font_init_glyph_metrics(cairo_dwrite_scaled_font_t *scaled_
if (font_face->measuring_mode == DWRITE_MEASURING_MODE_GDI_CLASSIC ||
font_face->measuring_mode == DWRITE_MEASURING_MODE_GDI_NATURAL) {
DWRITE_MATRIX transform = _cairo_dwrite_matrix_from_matrix (&scaled_font->mat);
font_face->dwriteface->GetGdiCompatibleMetrics(1, 1, &transform, &fontMetrics);
scaled_font->dwriteface->GetGdiCompatibleMetrics(1, 1, &transform, &fontMetrics);
BOOL natural = font_face->measuring_mode == DWRITE_MEASURING_MODE_GDI_NATURAL;
hr = font_face->dwriteface->GetGdiCompatibleGlyphMetrics (1, 1, &transform, natural, &charIndex, 1, &metrics, FALSE);
hr = scaled_font->dwriteface->GetGdiCompatibleGlyphMetrics (1, 1, &transform, natural, &charIndex, 1, &metrics, FALSE);
} else {
font_face->dwriteface->GetMetrics(&fontMetrics);
hr = font_face->dwriteface->GetDesignGlyphMetrics(&charIndex, 1, &metrics);
scaled_font->dwriteface->GetMetrics(&fontMetrics);
hr = scaled_font->dwriteface->GetDesignGlyphMetrics(&charIndex, 1, &metrics);
}
if (FAILED(hr)) {
return CAIRO_INT_STATUS_UNSUPPORTED;
@ -972,16 +1074,15 @@ _cairo_dwrite_scaled_font_init_glyph_path(cairo_dwrite_scaled_font_t *scaled_fon
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;
HRESULT hr = dwriteff->dwriteface->GetGlyphRunOutline(1,
&glyphId,
&advance,
&offset,
1,
FALSE,
FALSE,
&recorder);
HRESULT hr = scaled_font->dwriteface->GetGlyphRunOutline(1,
&glyphId,
&advance,
&offset,
1,
FALSE,
FALSE,
&recorder);
if (!SUCCEEDED(hr))
return _cairo_dwrite_error (hr, "GetGlyphRunOutline failed");
@ -1039,7 +1140,7 @@ _cairo_dwrite_scaled_font_init_glyph_color_surface(cairo_dwrite_scaled_font_t *s
/* Y-axis is inverted */
offset.ascenderOffset = -(FLOAT)y;
run.fontFace = dwrite_font_face->dwriteface;
run.fontFace = scaled_font->dwriteface;
run.fontEmSize = 1;
run.glyphCount = 1;
run.glyphIndices = &index;
@ -1061,7 +1162,7 @@ _cairo_dwrite_scaled_font_init_glyph_color_surface(cairo_dwrite_scaled_font_t *s
RefPtr<IDWriteFontFace2> fontFace2;
UINT32 palette_count = 0;
if (SUCCEEDED(dwrite_font_face->dwriteface->QueryInterface(&fontFace2)))
if (SUCCEEDED(scaled_font->dwriteface->QueryInterface(&fontFace2)))
palette_count = fontFace2->GetColorPaletteCount();
UINT32 palette_index = CAIRO_COLOR_PALETTE_DEFAULT;
@ -1287,7 +1388,7 @@ _cairo_dwrite_scaled_font_init_glyph_surface(cairo_dwrite_scaled_font_t *scaled_
run.glyphCount = 1;
run.glyphAdvances = &advance;
run.fontFace = ((cairo_dwrite_font_face_t*)scaled_font->base.font_face)->dwriteface;
run.fontFace = scaled_font->dwriteface;
run.fontEmSize = 1.0f;
run.bidiLevel = 0;
run.glyphIndices = &index;
@ -1327,18 +1428,17 @@ _cairo_dwrite_load_truetype_table(void *scaled_font,
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;
HRESULT hr;
hr = face->dwriteface->TryGetFontTable (be32_to_cpu (tag),
&data,
&size,
&tableContext,
&exists);
hr = dwritesf->dwriteface->TryGetFontTable (be32_to_cpu (tag),
&data,
&size,
&tableContext,
&exists);
if (FAILED(hr))
return _cairo_dwrite_error (hr, "TryGetFontTable failed");
@ -1353,7 +1453,7 @@ _cairo_dwrite_load_truetype_table(void *scaled_font,
*length = size;
if (tableContext) {
face->dwriteface->ReleaseFontTable(tableContext);
dwritesf->dwriteface->ReleaseFontTable(tableContext);
}
return (cairo_int_status_t)CAIRO_STATUS_SUCCESS;
}
@ -1363,17 +1463,16 @@ _cairo_dwrite_is_synthetic(void *scaled_font,
cairo_bool_t *is_synthetic)
{
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);
HRESULT hr;
cairo_int_status_t status;
if (face->dwriteface->GetSimulations() != DWRITE_FONT_SIMULATIONS_NONE) {
if (dwritesf->dwriteface->GetSimulations() != DWRITE_FONT_SIMULATIONS_NONE) {
*is_synthetic = FALSE;
return CAIRO_INT_STATUS_SUCCESS;
}
RefPtr<IDWriteFontFace5> fontFace5;
if (FAILED(face->dwriteface->QueryInterface(&fontFace5))) {
if (FAILED(dwritesf->dwriteface->QueryInterface(&fontFace5))) {
/* If IDWriteFontFace5 is not available, assume this version of
* DirectWrite does not support variations.
*/
@ -1825,10 +1924,10 @@ compare_font_tables (cairo_dwrite_font_face_t *dwface,
HRESULT hr;
hr = dwface->dwriteface->TryGetFontTable(be32_to_cpu (tag),
&dw_data,
&dw_size,
&dw_tableContext,
&dw_exists);
&dw_data,
&dw_size,
&dw_tableContext,
&dw_exists);
if (FAILED(hr))
return _cairo_dwrite_error (hr, "TryGetFontTable failed");
@ -1864,7 +1963,7 @@ compare_font_tables (cairo_dwrite_font_face_t *dwface,
cleanup:
free (buffer);
if (dw_tableContext)
dwface->dwriteface->ReleaseFontTable(dw_tableContext);
dwface->dwriteface->ReleaseFontTable(dw_tableContext);
return status;
}
@ -1929,6 +2028,8 @@ _cairo_dwrite_scaled_font_create_win32_scaled_font (cairo_scaled_font_t *scaled_
return CAIRO_INT_STATUS_UNSUPPORTED;
}
/* TODO: use scaled font? */
LOGFONTW logfont;
if (FAILED(gdiInterop->ConvertFontFaceToLOGFONT (dwface->dwriteface, &logfont))) {
return CAIRO_INT_STATUS_UNSUPPORTED;

View file

@ -68,7 +68,8 @@ struct _cairo_dwrite_scaled_font {
cairo_matrix_t mat;
cairo_matrix_t mat_inverse;
cairo_antialias_t antialias_mode;
IDWriteRenderingParams *rendering_params;
IDWriteFontFace *dwriteface; /* Can't use RefPtr because this struct is malloc'd. */
IDWriteRenderingParams *rendering_params; /* Can't use RefPtr because this struct is malloc'd. */
DWRITE_MEASURING_MODE measuring_mode;
};
typedef struct _cairo_dwrite_scaled_font cairo_dwrite_scaled_font_t;