dwrite: Implement color glyph run decomposition.

Signed-off-by: Nikolay Sivov <nsivov@codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
Nikolay Sivov 2016-02-08 12:31:35 +03:00 committed by Alexandre Julliard
parent 2e927af328
commit 8a4db9fd90
4 changed files with 334 additions and 17 deletions

View file

@ -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,

View file

@ -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;
}

View file

@ -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);
}

View file

@ -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;