dwrite: Implement single adjustment, GPOS lookup 1.
Signed-off-by: Nikolay Sivov <nsivov@codeweavers.com> Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
parent
1d8a2ba4f7
commit
623f059b07
|
@ -1316,7 +1316,9 @@ static HRESULT WINAPI dwritetextanalyzer_GetGlyphPlacements(IDWriteTextAnalyzer2
|
||||||
context.u.pos.glyphs = glyphs;
|
context.u.pos.glyphs = glyphs;
|
||||||
context.u.pos.glyph_props = glyph_props;
|
context.u.pos.glyph_props = glyph_props;
|
||||||
context.glyph_count = glyph_count;
|
context.glyph_count = glyph_count;
|
||||||
|
context.emsize = emSize;
|
||||||
context.advances = advances;
|
context.advances = advances;
|
||||||
|
context.offsets = offsets;
|
||||||
context.language_tag = get_opentype_language(locale);
|
context.language_tag = get_opentype_language(locale);
|
||||||
|
|
||||||
hr = shape_get_positions(&context, scriptprops->scripttags, scriptprops->ops->gpos_features);
|
hr = shape_get_positions(&context, scriptprops->scripttags, scriptprops->ops->gpos_features);
|
||||||
|
@ -1378,7 +1380,9 @@ static HRESULT WINAPI dwritetextanalyzer_GetGdiCompatibleGlyphPlacements(IDWrite
|
||||||
context.u.pos.glyphs = glyphs;
|
context.u.pos.glyphs = glyphs;
|
||||||
context.u.pos.glyph_props = glyph_props;
|
context.u.pos.glyph_props = glyph_props;
|
||||||
context.glyph_count = glyph_count;
|
context.glyph_count = glyph_count;
|
||||||
|
context.emsize = emSize * ppdip;
|
||||||
context.advances = advances;
|
context.advances = advances;
|
||||||
|
context.offsets = offsets;
|
||||||
context.language_tag = get_opentype_language(locale);
|
context.language_tag = get_opentype_language(locale);
|
||||||
|
|
||||||
hr = shape_get_positions(&context, scriptprops->scripttags, scriptprops->ops->gpos_features);
|
hr = shape_get_positions(&context, scriptprops->scripttags, scriptprops->ops->gpos_features);
|
||||||
|
|
|
@ -393,7 +393,9 @@ struct scriptshaping_context
|
||||||
} u;
|
} u;
|
||||||
|
|
||||||
unsigned int glyph_count;
|
unsigned int glyph_count;
|
||||||
|
float emsize;
|
||||||
float *advances;
|
float *advances;
|
||||||
|
DWRITE_GLYPH_OFFSET *offsets;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct shaping_font_ops
|
struct shaping_font_ops
|
||||||
|
|
|
@ -489,6 +489,18 @@ enum gpos_lookup_type
|
||||||
GPOS_LOOKUP_EXTENSION_POSITION = 9,
|
GPOS_LOOKUP_EXTENSION_POSITION = 9,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum gpos_value_format
|
||||||
|
{
|
||||||
|
GPOS_VALUE_X_PLACEMENT = 0x1,
|
||||||
|
GPOS_VALUE_Y_PLACEMENT = 0x2,
|
||||||
|
GPOS_VALUE_X_ADVANCE = 0x4,
|
||||||
|
GPOS_VALUE_Y_ADVANCE = 0x8,
|
||||||
|
GPOS_VALUE_X_PLACEMENT_DEVICE = 0x10,
|
||||||
|
GPOS_VALUE_Y_PLACEMENT_DEVICE = 0x20,
|
||||||
|
GPOS_VALUE_X_ADVANCE_DEVICE = 0x40,
|
||||||
|
GPOS_VALUE_Y_ADVANCE_DEVICE = 0x80,
|
||||||
|
};
|
||||||
|
|
||||||
enum OPENTYPE_PLATFORM_ID
|
enum OPENTYPE_PLATFORM_ID
|
||||||
{
|
{
|
||||||
OPENTYPE_PLATFORM_UNICODE = 0,
|
OPENTYPE_PLATFORM_UNICODE = 0,
|
||||||
|
@ -526,6 +538,54 @@ struct ot_lookup_table
|
||||||
WORD subtable[1];
|
WORD subtable[1];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#define GLYPH_NOT_COVERED ((unsigned int)~0u)
|
||||||
|
|
||||||
|
struct ot_coverage_format1
|
||||||
|
{
|
||||||
|
WORD format;
|
||||||
|
WORD glyph_count;
|
||||||
|
WORD glyphs[1];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ot_coverage_range
|
||||||
|
{
|
||||||
|
WORD start_glyph;
|
||||||
|
WORD end_glyph;
|
||||||
|
WORD startcoverage_index;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ot_coverage_format2
|
||||||
|
{
|
||||||
|
WORD format;
|
||||||
|
WORD range_count;
|
||||||
|
struct ot_coverage_range ranges[1];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ot_gpos_device_table
|
||||||
|
{
|
||||||
|
WORD start_size;
|
||||||
|
WORD end_size;
|
||||||
|
WORD format;
|
||||||
|
WORD values[1];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ot_gpos_singlepos_format1
|
||||||
|
{
|
||||||
|
WORD format;
|
||||||
|
WORD coverage;
|
||||||
|
WORD value_format;
|
||||||
|
WORD value[1];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ot_gpos_singlepos_format2
|
||||||
|
{
|
||||||
|
WORD format;
|
||||||
|
WORD coverage;
|
||||||
|
WORD value_format;
|
||||||
|
WORD value_count;
|
||||||
|
WORD values[1];
|
||||||
|
};
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
WORD SubstFormat;
|
WORD SubstFormat;
|
||||||
WORD Coverage;
|
WORD Coverage;
|
||||||
|
@ -2635,6 +2695,201 @@ static unsigned int opentype_layout_get_glyph_class(const struct dwrite_fonttabl
|
||||||
return glyph_class;
|
return glyph_class;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct coverage_compare_format1_context
|
||||||
|
{
|
||||||
|
UINT16 glyph;
|
||||||
|
const UINT16 *table_base;
|
||||||
|
unsigned int *coverage_index;
|
||||||
|
};
|
||||||
|
|
||||||
|
static int coverage_compare_format1(const void *left, const void *right)
|
||||||
|
{
|
||||||
|
const struct coverage_compare_format1_context *context = left;
|
||||||
|
UINT16 glyph = GET_BE_WORD(*(UINT16 *)right);
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = context->glyph - glyph;
|
||||||
|
if (!ret)
|
||||||
|
*context->coverage_index = (UINT16 *)right - context->table_base;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int coverage_compare_format2(const void *g, const void *r)
|
||||||
|
{
|
||||||
|
const struct ot_coverage_range *range = r;
|
||||||
|
UINT16 glyph = *(UINT16 *)g;
|
||||||
|
|
||||||
|
if (glyph < GET_BE_WORD(range->start_glyph))
|
||||||
|
return -1;
|
||||||
|
else if (glyph > GET_BE_WORD(range->end_glyph))
|
||||||
|
return 1;
|
||||||
|
else
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsigned int opentype_layout_is_glyph_covered(const struct dwrite_fonttable *table, DWORD coverage,
|
||||||
|
UINT16 glyph)
|
||||||
|
{
|
||||||
|
WORD format = table_read_be_word(table, coverage), count;
|
||||||
|
|
||||||
|
count = table_read_be_word(table, coverage + 2);
|
||||||
|
|
||||||
|
if (format == 1)
|
||||||
|
{
|
||||||
|
const struct ot_coverage_format1 *format1 = table_read_ensure(table, coverage,
|
||||||
|
FIELD_OFFSET(struct ot_coverage_format1, glyphs[count]));
|
||||||
|
struct coverage_compare_format1_context context;
|
||||||
|
unsigned int coverage_index = GLYPH_NOT_COVERED;
|
||||||
|
|
||||||
|
if (format1)
|
||||||
|
{
|
||||||
|
context.glyph = glyph;
|
||||||
|
context.table_base = format1->glyphs;
|
||||||
|
context.coverage_index = &coverage_index;
|
||||||
|
|
||||||
|
bsearch(&context, format1->glyphs, count, sizeof(glyph), coverage_compare_format1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return coverage_index;
|
||||||
|
}
|
||||||
|
else if (format == 2)
|
||||||
|
{
|
||||||
|
const struct ot_coverage_format2 *format2 = table_read_ensure(table, coverage,
|
||||||
|
FIELD_OFFSET(struct ot_coverage_format2, ranges[count]));
|
||||||
|
if (format2)
|
||||||
|
{
|
||||||
|
const struct ot_coverage_range *range = bsearch(&glyph, format2->ranges, count,
|
||||||
|
sizeof(struct ot_coverage_range), coverage_compare_format2);
|
||||||
|
return range && glyph <= GET_BE_WORD(range->end_glyph) ?
|
||||||
|
GET_BE_WORD(range->startcoverage_index) + glyph - GET_BE_WORD(range->start_glyph) :
|
||||||
|
GLYPH_NOT_COVERED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
WARN("Unknown coverage format %u.\n", format);
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline unsigned int dwrite_popcount(unsigned int x)
|
||||||
|
{
|
||||||
|
#ifdef HAVE___BUILTIN_POPCOUNT
|
||||||
|
return __builtin_popcount(x);
|
||||||
|
#else
|
||||||
|
x -= x >> 1 & 0x55555555;
|
||||||
|
x = (x & 0x33333333) + (x >> 2 & 0x33333333);
|
||||||
|
return ((x + (x >> 4)) & 0x0f0f0f0f) * 0x01010101 >> 24;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static float opentype_scale_gpos_be_value(WORD value, float emsize, UINT16 upem)
|
||||||
|
{
|
||||||
|
return (short)GET_BE_WORD(value) * emsize / upem;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int opentype_layout_gpos_get_dev_value(const struct scriptshaping_context *context, unsigned int offset)
|
||||||
|
{
|
||||||
|
const struct scriptshaping_cache *cache = context->cache;
|
||||||
|
unsigned int start_size, end_size, format, value_word;
|
||||||
|
unsigned int index, ppem, mask;
|
||||||
|
int value;
|
||||||
|
|
||||||
|
if (!offset)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
start_size = table_read_be_word(&cache->gpos.table, offset);
|
||||||
|
end_size = table_read_be_word(&cache->gpos.table, offset + FIELD_OFFSET(struct ot_gpos_device_table, end_size));
|
||||||
|
|
||||||
|
ppem = context->emsize;
|
||||||
|
if (ppem < start_size || ppem > end_size)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
format = table_read_be_word(&cache->gpos.table, offset + FIELD_OFFSET(struct ot_gpos_device_table, format));
|
||||||
|
|
||||||
|
if (format < 1 || format > 3)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
index = ppem - start_size;
|
||||||
|
|
||||||
|
value_word = table_read_be_word(&cache->gpos.table, offset +
|
||||||
|
FIELD_OFFSET(struct ot_gpos_device_table, values[index >> (4 - format)]));
|
||||||
|
mask = 0xffff >> (16 - (1 << format));
|
||||||
|
|
||||||
|
value = (value_word >> ((index % (4 - format)) * (1 << format))) & mask;
|
||||||
|
|
||||||
|
if ((unsigned int)value >= ((mask + 1) >> 1))
|
||||||
|
value -= mask + 1;
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void opentype_layout_apply_gpos_value(struct scriptshaping_context *context, unsigned int table_offset,
|
||||||
|
WORD value_format, const WORD *values, unsigned int glyph)
|
||||||
|
{
|
||||||
|
const struct scriptshaping_cache *cache = context->cache;
|
||||||
|
DWRITE_GLYPH_OFFSET *offset = &context->offsets[glyph];
|
||||||
|
float *advance = &context->advances[glyph];
|
||||||
|
|
||||||
|
if (!value_format)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (value_format & GPOS_VALUE_X_PLACEMENT)
|
||||||
|
{
|
||||||
|
offset->advanceOffset += opentype_scale_gpos_be_value(*values, context->emsize, cache->upem);
|
||||||
|
values++;
|
||||||
|
}
|
||||||
|
if (value_format & GPOS_VALUE_Y_PLACEMENT)
|
||||||
|
{
|
||||||
|
offset->ascenderOffset += opentype_scale_gpos_be_value(*values, context->emsize, cache->upem);
|
||||||
|
values++;
|
||||||
|
}
|
||||||
|
if (value_format & GPOS_VALUE_X_ADVANCE)
|
||||||
|
{
|
||||||
|
*advance += opentype_scale_gpos_be_value(*values, context->emsize, cache->upem);
|
||||||
|
values++;
|
||||||
|
}
|
||||||
|
if (value_format & GPOS_VALUE_Y_ADVANCE)
|
||||||
|
{
|
||||||
|
values++;
|
||||||
|
}
|
||||||
|
if (value_format & GPOS_VALUE_X_PLACEMENT_DEVICE)
|
||||||
|
{
|
||||||
|
offset->advanceOffset += opentype_layout_gpos_get_dev_value(context, table_offset + GET_BE_WORD(*values));
|
||||||
|
values++;
|
||||||
|
}
|
||||||
|
if (value_format & GPOS_VALUE_Y_PLACEMENT_DEVICE)
|
||||||
|
{
|
||||||
|
offset->ascenderOffset += opentype_layout_gpos_get_dev_value(context, table_offset + GET_BE_WORD(*values));
|
||||||
|
values++;
|
||||||
|
}
|
||||||
|
if (value_format & GPOS_VALUE_X_ADVANCE_DEVICE)
|
||||||
|
{
|
||||||
|
*advance += opentype_layout_gpos_get_dev_value(context, table_offset + GET_BE_WORD(*values));
|
||||||
|
values++;
|
||||||
|
}
|
||||||
|
if (value_format & GPOS_VALUE_Y_ADVANCE_DEVICE)
|
||||||
|
{
|
||||||
|
values++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsigned int opentype_layout_get_gpos_subtable(const struct scriptshaping_cache *cache,
|
||||||
|
unsigned int lookup_offset, unsigned int subtable)
|
||||||
|
{
|
||||||
|
WORD lookup_type = table_read_be_word(&cache->gpos.table, lookup_offset);
|
||||||
|
unsigned int subtable_offset = table_read_be_word(&cache->gpos.table, lookup_offset +
|
||||||
|
FIELD_OFFSET(struct ot_lookup_table, subtable[subtable]));
|
||||||
|
if (lookup_type == GPOS_LOOKUP_EXTENSION_POSITION)
|
||||||
|
{
|
||||||
|
const struct ot_gpos_extensionpos_format1 *format1 = table_read_ensure(&cache->gpos.table,
|
||||||
|
lookup_offset + subtable_offset, sizeof(*format1));
|
||||||
|
subtable_offset += GET_BE_DWORD(format1->extension_offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
return lookup_offset + subtable_offset;
|
||||||
|
}
|
||||||
|
|
||||||
struct lookup
|
struct lookup
|
||||||
{
|
{
|
||||||
unsigned int offset;
|
unsigned int offset;
|
||||||
|
@ -2677,6 +2932,56 @@ static BOOL glyph_iterator_match(const struct glyph_iterator *iter)
|
||||||
static BOOL opentype_layout_apply_gpos_single_adjustment(struct scriptshaping_context *context,
|
static BOOL opentype_layout_apply_gpos_single_adjustment(struct scriptshaping_context *context,
|
||||||
struct glyph_iterator *iter, const struct lookup *lookup)
|
struct glyph_iterator *iter, const struct lookup *lookup)
|
||||||
{
|
{
|
||||||
|
struct scriptshaping_cache *cache = context->cache;
|
||||||
|
WORD format, value_format, value_len, coverage;
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
|
for (i = 0; i < lookup->subtable_count; ++i)
|
||||||
|
{
|
||||||
|
unsigned int subtable_offset = opentype_layout_get_gpos_subtable(cache, lookup->offset, i);
|
||||||
|
unsigned int coverage_index;
|
||||||
|
|
||||||
|
format = table_read_be_word(&cache->gpos.table, subtable_offset);
|
||||||
|
|
||||||
|
coverage = table_read_be_word(&cache->gpos.table, subtable_offset +
|
||||||
|
FIELD_OFFSET(struct ot_gpos_singlepos_format1, coverage));
|
||||||
|
value_format = table_read_be_word(&cache->gpos.table, subtable_offset +
|
||||||
|
FIELD_OFFSET(struct ot_gpos_singlepos_format1, value_format));
|
||||||
|
value_len = dwrite_popcount(value_format);
|
||||||
|
|
||||||
|
if (format == 1)
|
||||||
|
{
|
||||||
|
const struct ot_gpos_singlepos_format1 *format1 = table_read_ensure(&cache->gpos.table, subtable_offset,
|
||||||
|
FIELD_OFFSET(struct ot_gpos_singlepos_format1, value[value_len]));
|
||||||
|
|
||||||
|
coverage_index = opentype_layout_is_glyph_covered(&cache->gpos.table, subtable_offset + coverage,
|
||||||
|
context->u.pos.glyphs[iter->pos]);
|
||||||
|
if (coverage_index == GLYPH_NOT_COVERED)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
opentype_layout_apply_gpos_value(context, subtable_offset, value_format, format1->value, iter->pos);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else if (format == 2)
|
||||||
|
{
|
||||||
|
WORD value_count = table_read_be_word(&cache->gpos.table, subtable_offset +
|
||||||
|
FIELD_OFFSET(struct ot_gpos_singlepos_format2, value_count));
|
||||||
|
const struct ot_gpos_singlepos_format2 *format2 = table_read_ensure(&cache->gpos.table, subtable_offset,
|
||||||
|
FIELD_OFFSET(struct ot_gpos_singlepos_format2, values) + value_count * value_len * sizeof(WORD));
|
||||||
|
|
||||||
|
coverage_index = opentype_layout_is_glyph_covered(&cache->gpos.table, subtable_offset + coverage,
|
||||||
|
context->u.pos.glyphs[iter->pos]);
|
||||||
|
if (coverage_index == GLYPH_NOT_COVERED || coverage_index >= value_count)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
opentype_layout_apply_gpos_value(context, subtable_offset, value_format,
|
||||||
|
&format2->values[coverage_index * value_len], iter->pos);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
WARN("Unknown single adjustment format %u.\n", format);
|
||||||
|
}
|
||||||
|
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue