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:
parent
2e927af328
commit
8a4db9fd90
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue