[PATCH 4/5] dwrite: Implement color glyph run decomposition
Nikolay Sivov
bunglehead at gmail.com
Mon Feb 8 03:31:35 CST 2016
---
-------------- next part --------------
From 28119d9325dcd7a183883ad6f4db111a6c551e5a Mon Sep 17 00:00:00 2001
From: Nikolay Sivov <nsivov at codeweavers.com>
Date: Mon, 8 Feb 2016 12:07:34 +0300
Subject: [PATCH 4/5] dwrite: Implement color glyph run decomposition
Signed-off-by: Nikolay Sivov <nsivov at codeweavers.com>
---
dlls/dwrite/dwrite_private.h | 13 +++
dlls/dwrite/font.c | 249 ++++++++++++++++++++++++++++++++++++++++---
dlls/dwrite/opentype.c | 84 +++++++++++++++
dlls/dwrite/tests/font.c | 5 +-
4 files changed, 334 insertions(+), 17 deletions(-)
diff --git a/dlls/dwrite/dwrite_private.h b/dlls/dwrite/dwrite_private.h
index ce08572..fd6d3c7 100644
--- a/dlls/dwrite/dwrite_private.h
+++ b/dlls/dwrite/dwrite_private.h
@@ -165,6 +165,19 @@ extern UINT32 opentype_get_cpal_palettecount(const void*) DECLSPEC_HIDDEN;
extern UINT32 opentype_get_cpal_paletteentrycount(const void*) DECLSPEC_HIDDEN;
extern HRESULT opentype_get_cpal_entries(const void*,UINT32,UINT32,UINT32,DWRITE_COLOR_F*) DECLSPEC_HIDDEN;
+struct dwrite_colorglyph {
+ USHORT layer; /* [0, num_layers) index indicating current layer */
+ /* base glyph record data, set once on initialization */
+ USHORT first_layer;
+ USHORT num_layers;
+ /* current layer record data, updated every time glyph is switched to next layer */
+ UINT16 glyph;
+ UINT16 palette_index;
+};
+
+extern HRESULT opentype_get_colr_glyph(const void*,UINT16,struct dwrite_colorglyph*) DECLSPEC_HIDDEN;
+extern void opentype_colr_next_glyph(const void*,struct dwrite_colorglyph*) DECLSPEC_HIDDEN;
+
enum gasp_flags {
GASP_GRIDFIT = 0x0001,
GASP_DOGRAY = 0x0002,
diff --git a/dlls/dwrite/font.c b/dlls/dwrite/font.c
index a7d28f9..6cea760 100644
--- a/dlls/dwrite/font.c
+++ b/dlls/dwrite/font.c
@@ -181,6 +181,22 @@ struct dwrite_glyphrunanalysis {
struct dwrite_colorglyphenum {
IDWriteColorGlyphRunEnumerator IDWriteColorGlyphRunEnumerator_iface;
LONG ref;
+
+ FLOAT origin_x; /* original run origin */
+ FLOAT origin_y;
+
+ IDWriteFontFace2 *fontface; /* for convenience */
+ DWRITE_COLOR_GLYPH_RUN colorrun; /* returned with GetCurrentRun() */
+ DWRITE_GLYPH_RUN run; /* base run */
+ UINT32 palette; /* palette index to get layer color from */
+ FLOAT *advances; /* original or measured advances for base glyphs */
+ FLOAT *color_advances; /* returned color run points to this */
+ UINT16 *glyphindices; /* returned color run points to this */
+ struct dwrite_colorglyph *glyphs; /* current glyph color info */
+ BOOL has_regular_glyphs; /* TRUE if there's any glyph without a color */
+ UINT16 current_layer; /* enumerator position, updated with MoveNext */
+ UINT16 max_layer_num; /* max number of layers for this run */
+ struct dwrite_fonttable colr; /* used to access layers */
};
#define GLYPH_BLOCK_SHIFT 8
@@ -4610,24 +4626,147 @@ static ULONG WINAPI colorglyphenum_Release(IDWriteColorGlyphRunEnumerator *iface
TRACE("(%p)->(%u)\n", This, ref);
- if (!ref)
+ if (!ref) {
+ heap_free(This->advances);
+ heap_free(This->color_advances);
+ heap_free(This->glyphindices);
+ heap_free(This->glyphs);
+ if (This->colr.context)
+ IDWriteFontFace2_ReleaseFontTable(This->fontface, This->colr.context);
+ IDWriteFontFace2_Release(This->fontface);
heap_free(This);
+ }
return ref;
}
+static FLOAT get_glyph_origin(const struct dwrite_colorglyphenum *glyphenum, UINT32 g)
+{
+ BOOL is_rtl = glyphenum->run.bidiLevel & 1;
+ FLOAT origin = 0.0f;
+
+ if (g == 0)
+ return 0.0f;
+
+ while (g--)
+ origin += is_rtl ? -glyphenum->advances[g] : glyphenum->advances[g];
+ return origin;
+}
+
+static BOOL colorglyphenum_build_color_run(struct dwrite_colorglyphenum *glyphenum)
+{
+ DWRITE_COLOR_GLYPH_RUN *colorrun = &glyphenum->colorrun;
+ FLOAT advance_adj = 0.0f;
+ BOOL got_palette_index;
+ UINT32 g;
+
+ /* start with regular glyphs */
+ if (glyphenum->current_layer == 0 && glyphenum->has_regular_glyphs) {
+ UINT32 first_glyph = 0;
+
+ for (g = 0; g < glyphenum->run.glyphCount; g++) {
+ if (glyphenum->glyphs[g].num_layers == 0) {
+ glyphenum->glyphindices[g] = glyphenum->glyphs[g].glyph;
+ first_glyph = min(first_glyph, g);
+ }
+ else
+ glyphenum->glyphindices[g] = 1;
+ glyphenum->color_advances[g] = glyphenum->advances[g];
+ }
+
+ colorrun->baselineOriginX = glyphenum->origin_x + get_glyph_origin(glyphenum, first_glyph);
+ colorrun->baselineOriginY = glyphenum->origin_y;
+ colorrun->glyphRun.glyphCount = glyphenum->run.glyphCount;
+ colorrun->paletteIndex = 0xffff;
+ memset(&colorrun->runColor, 0, sizeof(colorrun->runColor));
+ glyphenum->has_regular_glyphs = FALSE;
+ return TRUE;
+ }
+ else {
+ colorrun->glyphRun.glyphCount = 0;
+ got_palette_index = FALSE;
+ }
+
+ advance_adj = 0.0f;
+ for (g = 0; g < glyphenum->run.glyphCount; g++) {
+
+ glyphenum->glyphindices[g] = 1;
+
+ /* all glyph layers were returned */
+ if (glyphenum->glyphs[g].layer == glyphenum->glyphs[g].num_layers) {
+ advance_adj += glyphenum->advances[g];
+ continue;
+ }
+
+ if (glyphenum->current_layer == glyphenum->glyphs[g].layer && (!got_palette_index || colorrun->paletteIndex == glyphenum->glyphs[g].palette_index)) {
+ UINT32 index = colorrun->glyphRun.glyphCount;
+ if (!got_palette_index) {
+ colorrun->paletteIndex = glyphenum->glyphs[g].palette_index;
+ /* use foreground color or request one from the font */
+ if (colorrun->paletteIndex == 0xffff)
+ memset(&colorrun->runColor, 0, sizeof(colorrun->runColor));
+ else
+ IDWriteFontFace2_GetPaletteEntries(glyphenum->fontface, glyphenum->palette, colorrun->paletteIndex,
+ 1, &colorrun->runColor);
+ /* found a glyph position new color run starts from, origin is "original origin + distance to this glyph" */
+ colorrun->baselineOriginX = glyphenum->origin_x + get_glyph_origin(glyphenum, g);
+ colorrun->baselineOriginY = glyphenum->origin_y;
+ glyphenum->color_advances[index] = glyphenum->advances[g];
+ got_palette_index = TRUE;
+ }
+
+ glyphenum->glyphindices[index] = glyphenum->glyphs[g].glyph;
+ opentype_colr_next_glyph(glyphenum->colr.data, glyphenum->glyphs + g);
+ if (index)
+ glyphenum->color_advances[index-1] += advance_adj;
+ colorrun->glyphRun.glyphCount++;
+ advance_adj = 0.0f;
+ }
+ else
+ advance_adj += glyphenum->advances[g];
+ }
+
+ /* reset last advance */
+ if (colorrun->glyphRun.glyphCount)
+ glyphenum->color_advances[colorrun->glyphRun.glyphCount-1] = 0.0f;
+
+ return colorrun->glyphRun.glyphCount > 0;
+}
+
static HRESULT WINAPI colorglyphenum_MoveNext(IDWriteColorGlyphRunEnumerator *iface, BOOL *has_run)
{
struct dwrite_colorglyphenum *This = impl_from_IDWriteColorGlyphRunEnumerator(iface);
- FIXME("(%p)->(%p): stub\n", This, has_run);
- return E_NOTIMPL;
+
+ TRACE("(%p)->(%p)\n", This, has_run);
+
+ *has_run = FALSE;
+
+ This->colorrun.glyphRun.glyphCount = 0;
+ while (This->current_layer < This->max_layer_num) {
+ if (colorglyphenum_build_color_run(This))
+ break;
+ else
+ This->current_layer++;
+ }
+
+ *has_run = This->colorrun.glyphRun.glyphCount > 0;
+
+ return S_OK;
}
static HRESULT WINAPI colorglyphenum_GetCurrentRun(IDWriteColorGlyphRunEnumerator *iface, DWRITE_COLOR_GLYPH_RUN const **run)
{
struct dwrite_colorglyphenum *This = impl_from_IDWriteColorGlyphRunEnumerator(iface);
- FIXME("(%p)->(%p): stub\n", This, run);
- return E_NOTIMPL;
+
+ TRACE("(%p)->(%p)\n", This, run);
+
+ if (This->colorrun.glyphRun.glyphCount == 0) {
+ *run = NULL;
+ return E_NOT_VALID_STATE;
+ }
+
+ *run = &This->colorrun;
+ return S_OK;
}
static const IDWriteColorGlyphRunEnumeratorVtbl colorglyphenumvtbl = {
@@ -4639,12 +4778,13 @@ static const IDWriteColorGlyphRunEnumeratorVtbl colorglyphenumvtbl = {
};
HRESULT create_colorglyphenum(FLOAT originX, FLOAT originY, const DWRITE_GLYPH_RUN *run, const DWRITE_GLYPH_RUN_DESCRIPTION *rundescr,
- DWRITE_MEASURING_MODE mode, const DWRITE_MATRIX *transform, UINT32 palette, IDWriteColorGlyphRunEnumerator **ret)
+ DWRITE_MEASURING_MODE measuring_mode, const DWRITE_MATRIX *transform, UINT32 palette, IDWriteColorGlyphRunEnumerator **ret)
{
struct dwrite_colorglyphenum *colorglyphenum;
+ BOOL colorfont, has_colored_glyph;
IDWriteFontFace2 *fontface2;
- BOOL colorfont;
HRESULT hr;
+ UINT32 i;
*ret = NULL;
@@ -4655,17 +4795,100 @@ HRESULT create_colorglyphenum(FLOAT originX, FLOAT originY, const DWRITE_GLYPH_R
}
colorfont = IDWriteFontFace2_IsColorFont(fontface2) && IDWriteFontFace2_GetColorPaletteCount(fontface2) > palette;
- IDWriteFontFace2_Release(fontface2);
- if (!colorfont)
- return DWRITE_E_NOCOLOR;
+ if (!colorfont) {
+ hr = DWRITE_E_NOCOLOR;
+ goto failed;
+ }
- colorglyphenum = heap_alloc(sizeof(*colorglyphenum));
- if (!colorglyphenum)
- return E_OUTOFMEMORY;
+ colorglyphenum = heap_alloc_zero(sizeof(*colorglyphenum));
+ if (!colorglyphenum) {
+ hr = E_OUTOFMEMORY;
+ goto failed;
+ }
colorglyphenum->IDWriteColorGlyphRunEnumerator_iface.lpVtbl = &colorglyphenumvtbl;
colorglyphenum->ref = 1;
+ colorglyphenum->origin_x = originX;
+ colorglyphenum->origin_y = originY;
+ colorglyphenum->fontface = fontface2;
+ colorglyphenum->glyphs = NULL;
+ colorglyphenum->run = *run;
+ colorglyphenum->run.glyphIndices = NULL;
+ colorglyphenum->run.glyphAdvances = NULL;
+ colorglyphenum->run.glyphOffsets = NULL;
+ colorglyphenum->palette = palette;
+ memset(&colorglyphenum->colr, 0, sizeof(colorglyphenum->colr));
+ colorglyphenum->colr.exists = TRUE;
+ get_fontface_table(fontface2, MS_COLR_TAG, &colorglyphenum->colr);
+ colorglyphenum->current_layer = 0;
+ colorglyphenum->max_layer_num = 0;
+
+ colorglyphenum->glyphs = heap_alloc_zero(run->glyphCount * sizeof(*colorglyphenum->glyphs));
+
+ has_colored_glyph = FALSE;
+ colorglyphenum->has_regular_glyphs = FALSE;
+ for (i = 0; i < run->glyphCount; i++) {
+ if (opentype_get_colr_glyph(colorglyphenum->colr.data, run->glyphIndices[i], colorglyphenum->glyphs + i) == S_OK) {
+ colorglyphenum->max_layer_num = max(colorglyphenum->max_layer_num, colorglyphenum->glyphs[i].num_layers);
+ has_colored_glyph = TRUE;
+ }
+ if (colorglyphenum->glyphs[i].num_layers == 0)
+ colorglyphenum->has_regular_glyphs = TRUE;
+ }
+
+ /* It's acceptable to have a subset of glyphs mapped to color layers, for regular runs client
+ is supposed to proceed normally, like if font had no color info at all. */
+ if (!has_colored_glyph) {
+ IDWriteColorGlyphRunEnumerator_Release(&colorglyphenum->IDWriteColorGlyphRunEnumerator_iface);
+ return DWRITE_E_NOCOLOR;
+ }
+
+ colorglyphenum->advances = heap_alloc(run->glyphCount * sizeof(FLOAT));
+ colorglyphenum->color_advances = heap_alloc(run->glyphCount * sizeof(FLOAT));
+ colorglyphenum->glyphindices = heap_alloc(run->glyphCount * sizeof(UINT16));
+
+ colorglyphenum->colorrun.glyphRun.glyphIndices = colorglyphenum->glyphindices;
+ colorglyphenum->colorrun.glyphRun.glyphAdvances = colorglyphenum->color_advances;
+ colorglyphenum->colorrun.glyphRun.glyphOffsets = NULL; /* FIXME */
+ colorglyphenum->colorrun.glyphRunDescription = NULL; /* FIXME */
+
+ if (run->glyphAdvances)
+ memcpy(colorglyphenum->advances, run->glyphAdvances, run->glyphCount * sizeof(FLOAT));
+ else {
+ DWRITE_FONT_METRICS metrics;
+
+ IDWriteFontFace_GetMetrics(run->fontFace, &metrics);
+ for (i = 0; i < run->glyphCount; i++) {
+ HRESULT hr;
+ INT32 a;
+
+ switch (measuring_mode)
+ {
+ case DWRITE_MEASURING_MODE_NATURAL:
+ hr = IDWriteFontFace2_GetDesignGlyphAdvances(fontface2, 1, run->glyphIndices + i, &a, run->isSideways);
+ if (FAILED(hr))
+ a = 0;
+ colorglyphenum->advances[i] = get_scaled_advance_width(a, run->fontEmSize, &metrics);
+ break;
+ case DWRITE_MEASURING_MODE_GDI_CLASSIC:
+ case DWRITE_MEASURING_MODE_GDI_NATURAL:
+ hr = IDWriteFontFace2_GetGdiCompatibleGlyphAdvances(fontface2, run->fontEmSize, 1.0f, transform,
+ measuring_mode == DWRITE_MEASURING_MODE_GDI_NATURAL, run->isSideways, 1, run->glyphIndices + i, &a);
+ if (FAILED(hr))
+ colorglyphenum->advances[i] = 0.0f;
+ else
+ colorglyphenum->advances[i] = floorf(a * run->fontEmSize / metrics.designUnitsPerEm + 0.5f);
+ break;
+ default:
+ ;
+ }
+ }
+ }
*ret = &colorglyphenum->IDWriteColorGlyphRunEnumerator_iface;
return S_OK;
+
+failed:
+ IDWriteFontFace2_Release(fontface2);
+ return hr;
}
diff --git a/dlls/dwrite/opentype.c b/dlls/dwrite/opentype.c
index 0d3482d..a913dc9 100644
--- a/dlls/dwrite/opentype.c
+++ b/dlls/dwrite/opentype.c
@@ -707,6 +707,29 @@ struct CPAL_ColorRecord
BYTE alpha;
};
+/* COLR table */
+struct COLR_Header
+{
+ USHORT version;
+ USHORT numBaseGlyphRecords;
+ ULONG offsetBaseGlyphRecord;
+ ULONG offsetLayerRecord;
+ USHORT numLayerRecords;
+};
+
+struct COLR_BaseGlyphRecord
+{
+ USHORT GID;
+ USHORT firstLayerIndex;
+ USHORT numLayers;
+};
+
+struct COLR_LayerRecord
+{
+ USHORT GID;
+ USHORT paletteIndex;
+};
+
BOOL is_face_type_supported(DWRITE_FONT_FACE_TYPE type)
{
return (type == DWRITE_FONT_FACE_TYPE_CFF) ||
@@ -1692,3 +1715,64 @@ HRESULT opentype_get_cpal_entries(const void *cpal, UINT32 palette, UINT32 first
return S_OK;
}
+
+static int colr_compare_gid(const void *g, const void *r)
+{
+ const struct COLR_BaseGlyphRecord *record = r;
+ UINT16 glyph = *(UINT16*)g, GID = GET_BE_WORD(record->GID);
+ int ret = 0;
+
+ if (glyph > GID)
+ ret = 1;
+ else if (glyph < GID)
+ ret = -1;
+
+ return ret;
+}
+
+HRESULT opentype_get_colr_glyph(const void *colr, UINT16 glyph, struct dwrite_colorglyph *ret)
+{
+ const struct COLR_BaseGlyphRecord *record;
+ const struct COLR_Header *header = colr;
+ const struct COLR_LayerRecord *layer;
+ DWORD layerrecordoffset = GET_BE_DWORD(header->offsetLayerRecord);
+ DWORD baserecordoffset = GET_BE_DWORD(header->offsetBaseGlyphRecord);
+ WORD numbaserecords = GET_BE_WORD(header->numBaseGlyphRecords);
+
+ record = bsearch(&glyph, (BYTE*)colr + baserecordoffset, numbaserecords, sizeof(struct COLR_BaseGlyphRecord),
+ colr_compare_gid);
+ if (!record) {
+ ret->layer = 0;
+ ret->first_layer = 0;
+ ret->num_layers = 0;
+ ret->glyph = glyph;
+ ret->palette_index = 0xffff;
+ return S_FALSE;
+ }
+
+ ret->layer = 0;
+ ret->first_layer = GET_BE_WORD(record->firstLayerIndex);
+ ret->num_layers = GET_BE_WORD(record->numLayers);
+
+ layer = (struct COLR_LayerRecord*)((BYTE*)colr + layerrecordoffset) + ret->first_layer + ret->layer;
+ ret->glyph = GET_BE_WORD(layer->GID);
+ ret->palette_index = GET_BE_WORD(layer->paletteIndex);
+
+ return S_OK;
+}
+
+void opentype_colr_next_glyph(const void *colr, struct dwrite_colorglyph *glyph)
+{
+ const struct COLR_Header *header = colr;
+ const struct COLR_LayerRecord *layer;
+ DWORD layerrecordoffset = GET_BE_DWORD(header->offsetLayerRecord);
+
+ /* iterated all the way through */
+ if (glyph->layer == glyph->num_layers)
+ return;
+
+ glyph->layer++;
+ layer = (struct COLR_LayerRecord*)((BYTE*)colr + layerrecordoffset) + glyph->first_layer + glyph->layer;
+ glyph->glyph = GET_BE_WORD(layer->GID);
+ glyph->palette_index = GET_BE_WORD(layer->paletteIndex);
+}
diff --git a/dlls/dwrite/tests/font.c b/dlls/dwrite/tests/font.c
index a396723..2d31541 100644
--- a/dlls/dwrite/tests/font.c
+++ b/dlls/dwrite/tests/font.c
@@ -5207,7 +5207,6 @@ static void test_TranslateColorGlyphRun(void)
while (1) {
hasrun = FALSE;
hr = IDWriteColorGlyphRunEnumerator_MoveNext(layers, &hasrun);
- todo_wine
ok(hr == S_OK, "got 0x%08x\n", hr);
if (!hasrun)
@@ -5216,7 +5215,6 @@ static void test_TranslateColorGlyphRun(void)
/* iterated all way through */
hr = IDWriteColorGlyphRunEnumerator_GetCurrentRun(layers, &colorrun);
-todo_wine
ok(hr == E_NOT_VALID_STATE, "got 0x%08x\n", hr);
IDWriteColorGlyphRunEnumerator_Release(layers);
@@ -5247,10 +5245,9 @@ todo_wine
layers = (void*)0xdeadbeef;
hr = IDWriteFactory2_TranslateColorGlyphRun(factory2, 0.0, 0.0, &run, NULL,
DWRITE_MEASURING_MODE_NATURAL, NULL, 0, &layers);
-todo_wine {
ok(hr == DWRITE_E_NOCOLOR, "got 0x%08x\n", hr);
ok(layers == NULL, "got %p\n", layers);
-}
+
/* one glyph with, one without */
codepoints[0] = 'A';
codepoints[1] = 0x26c4;
--
2.7.0
More information about the wine-patches
mailing list