dwrite: Split initial cluster computation by stage.
Signed-off-by: Nikolay Sivov <nsivov@codeweavers.com> Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
parent
5a70c58acd
commit
072a9b00cf
|
@ -1832,10 +1832,9 @@ static const struct IDWriteTextAnalyzer2Vtbl textanalyzervtbl = {
|
||||||
|
|
||||||
static IDWriteTextAnalyzer2 textanalyzer = { &textanalyzervtbl };
|
static IDWriteTextAnalyzer2 textanalyzer = { &textanalyzervtbl };
|
||||||
|
|
||||||
HRESULT get_textanalyzer(IDWriteTextAnalyzer **ret)
|
IDWriteTextAnalyzer *get_text_analyzer(void)
|
||||||
{
|
{
|
||||||
*ret = (IDWriteTextAnalyzer*)&textanalyzer;
|
return (IDWriteTextAnalyzer *)&textanalyzer;
|
||||||
return S_OK;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static HRESULT WINAPI dwritenumbersubstitution_QueryInterface(IDWriteNumberSubstitution *iface, REFIID riid, void **obj)
|
static HRESULT WINAPI dwritenumbersubstitution_QueryInterface(IDWriteNumberSubstitution *iface, REFIID riid, void **obj)
|
||||||
|
|
|
@ -175,7 +175,7 @@ extern HRESULT clone_localizedstring(IDWriteLocalizedStrings *iface, IDWriteLoca
|
||||||
extern void set_en_localizedstring(IDWriteLocalizedStrings*,const WCHAR*) DECLSPEC_HIDDEN;
|
extern void set_en_localizedstring(IDWriteLocalizedStrings*,const WCHAR*) DECLSPEC_HIDDEN;
|
||||||
extern HRESULT get_system_fontcollection(IDWriteFactory5*,IDWriteFontCollection1**) DECLSPEC_HIDDEN;
|
extern HRESULT get_system_fontcollection(IDWriteFactory5*,IDWriteFontCollection1**) DECLSPEC_HIDDEN;
|
||||||
extern HRESULT get_eudc_fontcollection(IDWriteFactory5*,IDWriteFontCollection1**) DECLSPEC_HIDDEN;
|
extern HRESULT get_eudc_fontcollection(IDWriteFactory5*,IDWriteFontCollection1**) DECLSPEC_HIDDEN;
|
||||||
extern HRESULT get_textanalyzer(IDWriteTextAnalyzer**) DECLSPEC_HIDDEN;
|
extern IDWriteTextAnalyzer *get_text_analyzer(void) DECLSPEC_HIDDEN;
|
||||||
extern HRESULT create_font_file(IDWriteFontFileLoader *loader, const void *reference_key, UINT32 key_size, IDWriteFontFile **font_file) DECLSPEC_HIDDEN;
|
extern HRESULT create_font_file(IDWriteFontFileLoader *loader, const void *reference_key, UINT32 key_size, IDWriteFontFile **font_file) DECLSPEC_HIDDEN;
|
||||||
extern HRESULT create_localfontfileloader(IDWriteLocalFontFileLoader** iface) DECLSPEC_HIDDEN;
|
extern HRESULT create_localfontfileloader(IDWriteLocalFontFileLoader** iface) DECLSPEC_HIDDEN;
|
||||||
extern HRESULT create_fontface(const struct fontface_desc*,struct list*,IDWriteFontFace4**) DECLSPEC_HIDDEN;
|
extern HRESULT create_fontface(const struct fontface_desc*,struct list*,IDWriteFontFace4**) DECLSPEC_HIDDEN;
|
||||||
|
|
|
@ -746,11 +746,284 @@ static void layout_get_font_height(FLOAT emsize, DWRITE_FONT_METRICS *fontmetric
|
||||||
*height = SCALE_FONT_METRIC(fontmetrics->ascent + fontmetrics->descent + fontmetrics->lineGap, emsize, fontmetrics);
|
*height = SCALE_FONT_METRIC(fontmetrics->ascent + fontmetrics->descent + fontmetrics->lineGap, emsize, fontmetrics);
|
||||||
}
|
}
|
||||||
|
|
||||||
static HRESULT layout_compute_runs(struct dwrite_textlayout *layout)
|
static HRESULT layout_itemize(struct dwrite_textlayout *layout)
|
||||||
{
|
{
|
||||||
IDWriteFontFallback *fallback;
|
|
||||||
IDWriteTextAnalyzer *analyzer;
|
IDWriteTextAnalyzer *analyzer;
|
||||||
struct layout_range *range;
|
struct layout_range *range;
|
||||||
|
struct layout_run *r;
|
||||||
|
HRESULT hr = S_OK;
|
||||||
|
|
||||||
|
analyzer = get_text_analyzer();
|
||||||
|
|
||||||
|
LIST_FOR_EACH_ENTRY(range, &layout->ranges, struct layout_range, h.entry) {
|
||||||
|
/* We don't care about ranges that don't contain any text. */
|
||||||
|
if (range->h.range.startPosition >= layout->len)
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* Inline objects override actual text in range. */
|
||||||
|
if (range->object) {
|
||||||
|
hr = layout_update_breakpoints_range(layout, range);
|
||||||
|
if (FAILED(hr))
|
||||||
|
return hr;
|
||||||
|
|
||||||
|
r = alloc_layout_run(LAYOUT_RUN_INLINE, range->h.range.startPosition);
|
||||||
|
if (!r)
|
||||||
|
return E_OUTOFMEMORY;
|
||||||
|
|
||||||
|
r->u.object.object = range->object;
|
||||||
|
r->u.object.length = get_clipped_range_length(layout, range);
|
||||||
|
list_add_tail(&layout->runs, &r->entry);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Initial splitting by script. */
|
||||||
|
hr = IDWriteTextAnalyzer_AnalyzeScript(analyzer, (IDWriteTextAnalysisSource *)&layout->IDWriteTextAnalysisSource1_iface,
|
||||||
|
range->h.range.startPosition, get_clipped_range_length(layout, range),
|
||||||
|
(IDWriteTextAnalysisSink *)&layout->IDWriteTextAnalysisSink1_iface);
|
||||||
|
if (FAILED(hr))
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* Splitting further by bidi levels. */
|
||||||
|
hr = IDWriteTextAnalyzer_AnalyzeBidi(analyzer, (IDWriteTextAnalysisSource *)&layout->IDWriteTextAnalysisSource1_iface,
|
||||||
|
range->h.range.startPosition, get_clipped_range_length(layout, range),
|
||||||
|
(IDWriteTextAnalysisSink *)&layout->IDWriteTextAnalysisSink1_iface);
|
||||||
|
if (FAILED(hr))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return hr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static HRESULT layout_resolve_fonts(struct dwrite_textlayout *layout)
|
||||||
|
{
|
||||||
|
IDWriteFontCollection *sys_collection;
|
||||||
|
IDWriteFontFallback *fallback = NULL;
|
||||||
|
struct layout_range *range;
|
||||||
|
struct layout_run *r;
|
||||||
|
HRESULT hr;
|
||||||
|
|
||||||
|
if (FAILED(hr = IDWriteFactory5_GetSystemFontCollection(layout->factory, FALSE,
|
||||||
|
(IDWriteFontCollection1 **)&sys_collection, FALSE))) {
|
||||||
|
WARN("Failed to get system collection, hr %#x.\n", hr);
|
||||||
|
return hr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (layout->format.fallback) {
|
||||||
|
fallback = layout->format.fallback;
|
||||||
|
IDWriteFontFallback_AddRef(fallback);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (FAILED(hr = IDWriteFactory5_GetSystemFontFallback(layout->factory, &fallback))) {
|
||||||
|
WARN("Failed to get system fallback, hr %#x.\n", hr);
|
||||||
|
goto fatal;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LIST_FOR_EACH_ENTRY(r, &layout->runs, struct layout_run, entry) {
|
||||||
|
struct regular_layout_run *run = &r->u.regular;
|
||||||
|
IDWriteFont *font;
|
||||||
|
UINT32 length;
|
||||||
|
|
||||||
|
if (r->kind == LAYOUT_RUN_INLINE)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
range = get_layout_range_by_pos(layout, run->descr.textPosition);
|
||||||
|
|
||||||
|
if (run->sa.shapes == DWRITE_SCRIPT_SHAPES_NO_VISUAL) {
|
||||||
|
IDWriteFontCollection *collection;
|
||||||
|
|
||||||
|
collection = range->collection ? range->collection : sys_collection;
|
||||||
|
|
||||||
|
if (FAILED(hr = create_matching_font(collection, range->fontfamily, range->weight, range->style,
|
||||||
|
range->stretch, &font))) {
|
||||||
|
WARN("%s: failed to create matching font for non visual run, family %s, collection %p\n",
|
||||||
|
debugstr_rundescr(&run->descr), debugstr_w(range->fontfamily), range->collection);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
hr = IDWriteFont_CreateFontFace(font, &run->run.fontFace);
|
||||||
|
IDWriteFont_Release(font);
|
||||||
|
if (FAILED(hr)) {
|
||||||
|
WARN("Failed to create font face, hr %#x.\n", hr);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
run->run.fontEmSize = range->fontsize;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
length = run->descr.stringLength;
|
||||||
|
|
||||||
|
while (length) {
|
||||||
|
UINT32 mapped_length;
|
||||||
|
FLOAT scale;
|
||||||
|
|
||||||
|
run = &r->u.regular;
|
||||||
|
|
||||||
|
hr = IDWriteFontFallback_MapCharacters(fallback,
|
||||||
|
(IDWriteTextAnalysisSource *)&layout->IDWriteTextAnalysisSource1_iface,
|
||||||
|
run->descr.textPosition,
|
||||||
|
run->descr.stringLength,
|
||||||
|
range->collection,
|
||||||
|
range->fontfamily,
|
||||||
|
range->weight,
|
||||||
|
range->style,
|
||||||
|
range->stretch,
|
||||||
|
&mapped_length,
|
||||||
|
&font,
|
||||||
|
&scale);
|
||||||
|
if (FAILED(hr)) {
|
||||||
|
WARN("%s: failed to map family %s, collection %p, hr %#x.\n", debugstr_rundescr(&run->descr),
|
||||||
|
debugstr_w(range->fontfamily), range->collection, hr);
|
||||||
|
goto fatal;
|
||||||
|
}
|
||||||
|
|
||||||
|
hr = IDWriteFont_CreateFontFace(font, &run->run.fontFace);
|
||||||
|
IDWriteFont_Release(font);
|
||||||
|
if (FAILED(hr)) {
|
||||||
|
WARN("Failed to create font face, hr %#x.\n", hr);
|
||||||
|
goto fatal;
|
||||||
|
}
|
||||||
|
|
||||||
|
run->run.fontEmSize = range->fontsize * scale;
|
||||||
|
|
||||||
|
if (mapped_length < length) {
|
||||||
|
struct regular_layout_run *nextrun;
|
||||||
|
struct layout_run *nextr;
|
||||||
|
|
||||||
|
/* keep mapped part for current run, add another run for the rest */
|
||||||
|
nextr = alloc_layout_run(LAYOUT_RUN_REGULAR, 0);
|
||||||
|
if (!nextr) {
|
||||||
|
hr = E_OUTOFMEMORY;
|
||||||
|
goto fatal;
|
||||||
|
}
|
||||||
|
|
||||||
|
*nextr = *r;
|
||||||
|
nextr->start_position = run->descr.textPosition + mapped_length;
|
||||||
|
nextrun = &nextr->u.regular;
|
||||||
|
nextrun->descr.textPosition = nextr->start_position;
|
||||||
|
nextrun->descr.stringLength = run->descr.stringLength - mapped_length;
|
||||||
|
nextrun->descr.string = &layout->str[nextrun->descr.textPosition];
|
||||||
|
run->descr.stringLength = mapped_length;
|
||||||
|
list_add_after(&r->entry, &nextr->entry);
|
||||||
|
r = nextr;
|
||||||
|
}
|
||||||
|
|
||||||
|
length -= mapped_length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fatal:
|
||||||
|
IDWriteFontCollection_Release(sys_collection);
|
||||||
|
if (fallback)
|
||||||
|
IDWriteFontFallback_Release(fallback);
|
||||||
|
|
||||||
|
return hr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static HRESULT layout_shape_run(struct dwrite_textlayout *layout, struct regular_layout_run *run)
|
||||||
|
{
|
||||||
|
DWRITE_SHAPING_GLYPH_PROPERTIES *glyph_props;
|
||||||
|
DWRITE_SHAPING_TEXT_PROPERTIES *text_props;
|
||||||
|
IDWriteTextAnalyzer *analyzer;
|
||||||
|
struct layout_range *range;
|
||||||
|
UINT32 max_count;
|
||||||
|
HRESULT hr;
|
||||||
|
|
||||||
|
range = get_layout_range_by_pos(layout, run->descr.textPosition);
|
||||||
|
run->descr.localeName = range->locale;
|
||||||
|
run->clustermap = heap_alloc(run->descr.stringLength * sizeof(*run->clustermap));
|
||||||
|
|
||||||
|
max_count = 3 * run->descr.stringLength / 2 + 16;
|
||||||
|
run->glyphs = heap_alloc(max_count * sizeof(*run->glyphs));
|
||||||
|
if (!run->clustermap || !run->glyphs)
|
||||||
|
return E_OUTOFMEMORY;
|
||||||
|
|
||||||
|
text_props = heap_alloc(run->descr.stringLength * sizeof(*text_props));
|
||||||
|
glyph_props = heap_alloc(max_count * sizeof(*glyph_props));
|
||||||
|
if (!text_props || !glyph_props) {
|
||||||
|
heap_free(text_props);
|
||||||
|
heap_free(glyph_props);
|
||||||
|
return E_OUTOFMEMORY;
|
||||||
|
}
|
||||||
|
|
||||||
|
analyzer = get_text_analyzer();
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
hr = IDWriteTextAnalyzer_GetGlyphs(analyzer, run->descr.string, run->descr.stringLength, run->run.fontFace,
|
||||||
|
run->run.isSideways, run->run.bidiLevel & 1, &run->sa, run->descr.localeName, NULL /* FIXME */, NULL,
|
||||||
|
NULL, 0, max_count, run->clustermap, text_props, run->glyphs, glyph_props, &run->glyphcount);
|
||||||
|
if (hr == E_NOT_SUFFICIENT_BUFFER) {
|
||||||
|
heap_free(run->glyphs);
|
||||||
|
heap_free(glyph_props);
|
||||||
|
|
||||||
|
max_count = run->glyphcount;
|
||||||
|
|
||||||
|
run->glyphs = heap_alloc(max_count * sizeof(*run->glyphs));
|
||||||
|
glyph_props = heap_alloc(max_count * sizeof(*glyph_props));
|
||||||
|
if (!run->glyphs || !glyph_props) {
|
||||||
|
hr = E_OUTOFMEMORY;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (FAILED(hr)) {
|
||||||
|
heap_free(text_props);
|
||||||
|
heap_free(glyph_props);
|
||||||
|
WARN("%s: shaping failed, hr %#x.\n", debugstr_rundescr(&run->descr), hr);
|
||||||
|
return hr;
|
||||||
|
}
|
||||||
|
|
||||||
|
run->run.glyphIndices = run->glyphs;
|
||||||
|
run->descr.clusterMap = run->clustermap;
|
||||||
|
|
||||||
|
run->advances = heap_alloc(run->glyphcount * sizeof(*run->advances));
|
||||||
|
run->offsets = heap_alloc(run->glyphcount * sizeof(*run->offsets));
|
||||||
|
if (!run->advances || !run->offsets)
|
||||||
|
return E_OUTOFMEMORY;
|
||||||
|
|
||||||
|
/* Get advances and offsets. */
|
||||||
|
if (is_layout_gdi_compatible(layout))
|
||||||
|
hr = IDWriteTextAnalyzer_GetGdiCompatibleGlyphPlacements(analyzer, run->descr.string, run->descr.clusterMap,
|
||||||
|
text_props, run->descr.stringLength, run->run.glyphIndices, glyph_props, run->glyphcount,
|
||||||
|
run->run.fontFace, run->run.fontEmSize, layout->ppdip, &layout->transform,
|
||||||
|
layout->measuringmode == DWRITE_MEASURING_MODE_GDI_NATURAL, run->run.isSideways, run->run.bidiLevel & 1,
|
||||||
|
&run->sa, run->descr.localeName, NULL, NULL, 0, run->advances, run->offsets);
|
||||||
|
else
|
||||||
|
hr = IDWriteTextAnalyzer_GetGlyphPlacements(analyzer, run->descr.string, run->descr.clusterMap, text_props,
|
||||||
|
run->descr.stringLength, run->run.glyphIndices, glyph_props, run->glyphcount, run->run.fontFace,
|
||||||
|
run->run.fontEmSize, run->run.isSideways, run->run.bidiLevel & 1, &run->sa, run->descr.localeName,
|
||||||
|
NULL, NULL, 0, run->advances, run->offsets);
|
||||||
|
|
||||||
|
heap_free(text_props);
|
||||||
|
heap_free(glyph_props);
|
||||||
|
if (FAILED(hr)) {
|
||||||
|
memset(run->advances, 0, run->glyphcount * sizeof(*run->advances));
|
||||||
|
memset(run->offsets, 0, run->glyphcount * sizeof(*run->offsets));
|
||||||
|
WARN("%s: failed to get glyph placement info, hr %#x.\n", debugstr_rundescr(&run->descr), hr);
|
||||||
|
}
|
||||||
|
|
||||||
|
run->run.glyphAdvances = run->advances;
|
||||||
|
run->run.glyphOffsets = run->offsets;
|
||||||
|
|
||||||
|
/* Special treatment for runs that don't produce visual output, shaping code adds normal glyphs for them,
|
||||||
|
with valid cluster map and potentially with non-zero advances; layout code exposes those as zero
|
||||||
|
width clusters. */
|
||||||
|
if (run->sa.shapes == DWRITE_SCRIPT_SHAPES_NO_VISUAL)
|
||||||
|
run->run.glyphCount = 0;
|
||||||
|
else
|
||||||
|
run->run.glyphCount = run->glyphcount;
|
||||||
|
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static HRESULT layout_compute_runs(struct dwrite_textlayout *layout)
|
||||||
|
{
|
||||||
struct layout_run *r;
|
struct layout_run *r;
|
||||||
UINT32 cluster = 0;
|
UINT32 cluster = 0;
|
||||||
HRESULT hr;
|
HRESULT hr;
|
||||||
|
@ -770,159 +1043,20 @@ static HRESULT layout_compute_runs(struct dwrite_textlayout *layout)
|
||||||
}
|
}
|
||||||
layout->cluster_count = 0;
|
layout->cluster_count = 0;
|
||||||
|
|
||||||
hr = get_textanalyzer(&analyzer);
|
if (FAILED(hr = layout_itemize(layout))) {
|
||||||
if (FAILED(hr))
|
WARN("Itemization failed, hr %#x.\n", hr);
|
||||||
return hr;
|
return hr;
|
||||||
|
|
||||||
LIST_FOR_EACH_ENTRY(range, &layout->ranges, struct layout_range, h.entry) {
|
|
||||||
/* we don't care about ranges that don't contain any text */
|
|
||||||
if (range->h.range.startPosition >= layout->len)
|
|
||||||
break;
|
|
||||||
|
|
||||||
/* inline objects override actual text in a range */
|
|
||||||
if (range->object) {
|
|
||||||
hr = layout_update_breakpoints_range(layout, range);
|
|
||||||
if (FAILED(hr))
|
|
||||||
return hr;
|
|
||||||
|
|
||||||
r = alloc_layout_run(LAYOUT_RUN_INLINE, range->h.range.startPosition);
|
|
||||||
if (!r)
|
|
||||||
return E_OUTOFMEMORY;
|
|
||||||
|
|
||||||
r->u.object.object = range->object;
|
|
||||||
r->u.object.length = get_clipped_range_length(layout, range);
|
|
||||||
list_add_tail(&layout->runs, &r->entry);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* initial splitting by script */
|
|
||||||
hr = IDWriteTextAnalyzer_AnalyzeScript(analyzer, (IDWriteTextAnalysisSource*)&layout->IDWriteTextAnalysisSource1_iface,
|
|
||||||
range->h.range.startPosition, get_clipped_range_length(layout, range), (IDWriteTextAnalysisSink*)&layout->IDWriteTextAnalysisSink1_iface);
|
|
||||||
if (FAILED(hr))
|
|
||||||
break;
|
|
||||||
|
|
||||||
/* this splits it further */
|
|
||||||
hr = IDWriteTextAnalyzer_AnalyzeBidi(analyzer, (IDWriteTextAnalysisSource*)&layout->IDWriteTextAnalysisSource1_iface,
|
|
||||||
range->h.range.startPosition, get_clipped_range_length(layout, range), (IDWriteTextAnalysisSink*)&layout->IDWriteTextAnalysisSink1_iface);
|
|
||||||
if (FAILED(hr))
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (layout->format.fallback) {
|
if (FAILED(hr = layout_resolve_fonts(layout))) {
|
||||||
fallback = layout->format.fallback;
|
WARN("Failed to resolve layout fonts, hr %#x.\n", hr);
|
||||||
IDWriteFontFallback_AddRef(fallback);
|
return hr;
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
hr = IDWriteFactory5_GetSystemFontFallback(layout->factory, &fallback);
|
|
||||||
if (FAILED(hr))
|
|
||||||
return hr;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* resolve run fonts */
|
|
||||||
LIST_FOR_EACH_ENTRY(r, &layout->runs, struct layout_run, entry) {
|
|
||||||
struct regular_layout_run *run = &r->u.regular;
|
|
||||||
IDWriteFont *font;
|
|
||||||
UINT32 length;
|
|
||||||
|
|
||||||
if (r->kind == LAYOUT_RUN_INLINE)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
range = get_layout_range_by_pos(layout, run->descr.textPosition);
|
|
||||||
|
|
||||||
if (run->sa.shapes == DWRITE_SCRIPT_SHAPES_NO_VISUAL) {
|
|
||||||
IDWriteFontCollection *collection;
|
|
||||||
|
|
||||||
if (range->collection) {
|
|
||||||
collection = range->collection;
|
|
||||||
IDWriteFontCollection_AddRef(collection);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
IDWriteFactory5_GetSystemFontCollection(layout->factory, FALSE, (IDWriteFontCollection1 **)&collection, FALSE);
|
|
||||||
|
|
||||||
hr = create_matching_font(collection, range->fontfamily, range->weight,
|
|
||||||
range->style, range->stretch, &font);
|
|
||||||
|
|
||||||
IDWriteFontCollection_Release(collection);
|
|
||||||
|
|
||||||
if (FAILED(hr)) {
|
|
||||||
WARN("%s: failed to create a font for non visual run, %s, collection %p\n", debugstr_rundescr(&run->descr),
|
|
||||||
debugstr_w(range->fontfamily), range->collection);
|
|
||||||
return hr;
|
|
||||||
}
|
|
||||||
|
|
||||||
hr = IDWriteFont_CreateFontFace(font, &run->run.fontFace);
|
|
||||||
IDWriteFont_Release(font);
|
|
||||||
if (FAILED(hr))
|
|
||||||
return hr;
|
|
||||||
|
|
||||||
run->run.fontEmSize = range->fontsize;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
length = run->descr.stringLength;
|
|
||||||
|
|
||||||
while (length) {
|
|
||||||
UINT32 mapped_length;
|
|
||||||
FLOAT scale;
|
|
||||||
|
|
||||||
run = &r->u.regular;
|
|
||||||
|
|
||||||
hr = IDWriteFontFallback_MapCharacters(fallback,
|
|
||||||
(IDWriteTextAnalysisSource*)&layout->IDWriteTextAnalysisSource1_iface,
|
|
||||||
run->descr.textPosition,
|
|
||||||
run->descr.stringLength,
|
|
||||||
range->collection,
|
|
||||||
range->fontfamily,
|
|
||||||
range->weight,
|
|
||||||
range->style,
|
|
||||||
range->stretch,
|
|
||||||
&mapped_length,
|
|
||||||
&font,
|
|
||||||
&scale);
|
|
||||||
if (FAILED(hr)) {
|
|
||||||
WARN("%s: failed to map family %s, collection %p\n", debugstr_rundescr(&run->descr), debugstr_w(range->fontfamily), range->collection);
|
|
||||||
return hr;
|
|
||||||
}
|
|
||||||
|
|
||||||
hr = IDWriteFont_CreateFontFace(font, &run->run.fontFace);
|
|
||||||
IDWriteFont_Release(font);
|
|
||||||
if (FAILED(hr))
|
|
||||||
return hr;
|
|
||||||
run->run.fontEmSize = range->fontsize * scale;
|
|
||||||
|
|
||||||
if (mapped_length < length) {
|
|
||||||
struct regular_layout_run *nextrun;
|
|
||||||
struct layout_run *nextr;
|
|
||||||
|
|
||||||
/* keep mapped part for current run, add another run for the rest */
|
|
||||||
nextr = alloc_layout_run(LAYOUT_RUN_REGULAR, 0);
|
|
||||||
if (!nextr)
|
|
||||||
return E_OUTOFMEMORY;
|
|
||||||
|
|
||||||
*nextr = *r;
|
|
||||||
nextr->start_position = run->descr.textPosition + mapped_length;
|
|
||||||
nextrun = &nextr->u.regular;
|
|
||||||
nextrun->descr.textPosition = nextr->start_position;
|
|
||||||
nextrun->descr.stringLength = run->descr.stringLength - mapped_length;
|
|
||||||
nextrun->descr.string = &layout->str[nextrun->descr.textPosition];
|
|
||||||
run->descr.stringLength = mapped_length;
|
|
||||||
list_add_after(&r->entry, &nextr->entry);
|
|
||||||
r = nextr;
|
|
||||||
}
|
|
||||||
|
|
||||||
length -= mapped_length;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
IDWriteFontFallback_Release(fallback);
|
|
||||||
|
|
||||||
/* fill run info */
|
/* fill run info */
|
||||||
LIST_FOR_EACH_ENTRY(r, &layout->runs, struct layout_run, entry) {
|
LIST_FOR_EACH_ENTRY(r, &layout->runs, struct layout_run, entry) {
|
||||||
DWRITE_SHAPING_GLYPH_PROPERTIES *glyph_props = NULL;
|
|
||||||
DWRITE_SHAPING_TEXT_PROPERTIES *text_props = NULL;
|
|
||||||
struct regular_layout_run *run = &r->u.regular;
|
struct regular_layout_run *run = &r->u.regular;
|
||||||
DWRITE_FONT_METRICS fontmetrics = { 0 };
|
DWRITE_FONT_METRICS fontmetrics = { 0 };
|
||||||
UINT32 max_count;
|
|
||||||
|
|
||||||
/* we need to do very little in case of inline objects */
|
/* we need to do very little in case of inline objects */
|
||||||
if (r->kind == LAYOUT_RUN_INLINE) {
|
if (r->kind == LAYOUT_RUN_INLINE) {
|
||||||
|
@ -957,104 +1091,14 @@ static HRESULT layout_compute_runs(struct dwrite_textlayout *layout)
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
range = get_layout_range_by_pos(layout, run->descr.textPosition);
|
if (FAILED(hr = layout_shape_run(layout, run)))
|
||||||
run->descr.localeName = range->locale;
|
WARN("%s: shaping failed, hr %#x.\n", debugstr_rundescr(&run->descr), hr);
|
||||||
run->clustermap = heap_alloc(run->descr.stringLength*sizeof(UINT16));
|
|
||||||
|
|
||||||
max_count = 3*run->descr.stringLength/2 + 16;
|
|
||||||
run->glyphs = heap_alloc(max_count*sizeof(UINT16));
|
|
||||||
if (!run->clustermap || !run->glyphs)
|
|
||||||
goto memerr;
|
|
||||||
|
|
||||||
text_props = heap_alloc(run->descr.stringLength*sizeof(DWRITE_SHAPING_TEXT_PROPERTIES));
|
|
||||||
glyph_props = heap_alloc(max_count*sizeof(DWRITE_SHAPING_GLYPH_PROPERTIES));
|
|
||||||
if (!text_props || !glyph_props)
|
|
||||||
goto memerr;
|
|
||||||
|
|
||||||
while (1) {
|
|
||||||
hr = IDWriteTextAnalyzer_GetGlyphs(analyzer, run->descr.string, run->descr.stringLength,
|
|
||||||
run->run.fontFace, run->run.isSideways, run->run.bidiLevel & 1, &run->sa, run->descr.localeName,
|
|
||||||
NULL /* FIXME */, NULL, NULL, 0, max_count, run->clustermap, text_props, run->glyphs, glyph_props,
|
|
||||||
&run->glyphcount);
|
|
||||||
if (hr == E_NOT_SUFFICIENT_BUFFER) {
|
|
||||||
heap_free(run->glyphs);
|
|
||||||
heap_free(glyph_props);
|
|
||||||
|
|
||||||
max_count = run->glyphcount;
|
|
||||||
|
|
||||||
run->glyphs = heap_alloc(max_count*sizeof(UINT16));
|
|
||||||
glyph_props = heap_alloc(max_count*sizeof(DWRITE_SHAPING_GLYPH_PROPERTIES));
|
|
||||||
if (!run->glyphs || !glyph_props)
|
|
||||||
goto memerr;
|
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (FAILED(hr)) {
|
|
||||||
heap_free(text_props);
|
|
||||||
heap_free(glyph_props);
|
|
||||||
WARN("%s: shaping failed 0x%08x\n", debugstr_rundescr(&run->descr), hr);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
run->run.glyphIndices = run->glyphs;
|
|
||||||
run->descr.clusterMap = run->clustermap;
|
|
||||||
|
|
||||||
run->advances = heap_alloc(run->glyphcount*sizeof(FLOAT));
|
|
||||||
run->offsets = heap_alloc(run->glyphcount*sizeof(DWRITE_GLYPH_OFFSET));
|
|
||||||
if (!run->advances || !run->offsets)
|
|
||||||
goto memerr;
|
|
||||||
|
|
||||||
/* now set advances and offsets */
|
|
||||||
if (is_layout_gdi_compatible(layout))
|
|
||||||
hr = IDWriteTextAnalyzer_GetGdiCompatibleGlyphPlacements(analyzer, run->descr.string, run->descr.clusterMap,
|
|
||||||
text_props, run->descr.stringLength, run->run.glyphIndices, glyph_props, run->glyphcount,
|
|
||||||
run->run.fontFace, run->run.fontEmSize, layout->ppdip, &layout->transform,
|
|
||||||
layout->measuringmode == DWRITE_MEASURING_MODE_GDI_NATURAL, run->run.isSideways,
|
|
||||||
run->run.bidiLevel & 1, &run->sa, run->descr.localeName, NULL, NULL, 0, run->advances, run->offsets);
|
|
||||||
else
|
|
||||||
hr = IDWriteTextAnalyzer_GetGlyphPlacements(analyzer, run->descr.string, run->descr.clusterMap, text_props,
|
|
||||||
run->descr.stringLength, run->run.glyphIndices, glyph_props, run->glyphcount, run->run.fontFace,
|
|
||||||
run->run.fontEmSize, run->run.isSideways, run->run.bidiLevel & 1, &run->sa, run->descr.localeName,
|
|
||||||
NULL, NULL, 0, run->advances, run->offsets);
|
|
||||||
|
|
||||||
heap_free(text_props);
|
|
||||||
heap_free(glyph_props);
|
|
||||||
if (FAILED(hr))
|
|
||||||
WARN("%s: failed to get glyph placement info, 0x%08x\n", debugstr_rundescr(&run->descr), hr);
|
|
||||||
|
|
||||||
run->run.glyphAdvances = run->advances;
|
|
||||||
run->run.glyphOffsets = run->offsets;
|
|
||||||
|
|
||||||
/* Special treatment for runs that don't produce visual output, shaping code adds normal glyphs for them,
|
|
||||||
with valid cluster map and potentially with non-zero advances; layout code exposes those as zero width clusters. */
|
|
||||||
if (run->sa.shapes == DWRITE_SCRIPT_SHAPES_NO_VISUAL)
|
|
||||||
run->run.glyphCount = 0;
|
|
||||||
else
|
|
||||||
run->run.glyphCount = run->glyphcount;
|
|
||||||
|
|
||||||
/* baseline derived from font metrics */
|
/* baseline derived from font metrics */
|
||||||
layout_get_font_metrics(layout, run->run.fontFace, run->run.fontEmSize, &fontmetrics);
|
layout_get_font_metrics(layout, run->run.fontFace, run->run.fontEmSize, &fontmetrics);
|
||||||
layout_get_font_height(run->run.fontEmSize, &fontmetrics, &r->baseline, &r->height);
|
layout_get_font_height(run->run.fontEmSize, &fontmetrics, &r->baseline, &r->height);
|
||||||
|
|
||||||
layout_set_cluster_metrics(layout, r, &cluster);
|
layout_set_cluster_metrics(layout, r, &cluster);
|
||||||
continue;
|
|
||||||
|
|
||||||
memerr:
|
|
||||||
heap_free(text_props);
|
|
||||||
heap_free(glyph_props);
|
|
||||||
heap_free(run->clustermap);
|
|
||||||
heap_free(run->glyphs);
|
|
||||||
heap_free(run->advances);
|
|
||||||
heap_free(run->offsets);
|
|
||||||
run->advances = NULL;
|
|
||||||
run->offsets = NULL;
|
|
||||||
run->clustermap = run->glyphs = NULL;
|
|
||||||
hr = E_OUTOFMEMORY;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hr == S_OK) {
|
if (hr == S_OK) {
|
||||||
|
@ -1063,7 +1107,6 @@ static HRESULT layout_compute_runs(struct dwrite_textlayout *layout)
|
||||||
layout->clustermetrics[cluster-1].canWrapLineAfter = 1;
|
layout->clustermetrics[cluster-1].canWrapLineAfter = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
IDWriteTextAnalyzer_Release(analyzer);
|
|
||||||
return hr;
|
return hr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1077,25 +1120,22 @@ static HRESULT layout_compute(struct dwrite_textlayout *layout)
|
||||||
/* nominal breakpoints are evaluated only once, because string never changes */
|
/* nominal breakpoints are evaluated only once, because string never changes */
|
||||||
if (!layout->nominal_breakpoints) {
|
if (!layout->nominal_breakpoints) {
|
||||||
IDWriteTextAnalyzer *analyzer;
|
IDWriteTextAnalyzer *analyzer;
|
||||||
HRESULT hr;
|
|
||||||
|
|
||||||
layout->nominal_breakpoints = heap_alloc(sizeof(DWRITE_LINE_BREAKPOINT)*layout->len);
|
layout->nominal_breakpoints = heap_alloc(layout->len * sizeof(*layout->nominal_breakpoints));
|
||||||
if (!layout->nominal_breakpoints)
|
if (!layout->nominal_breakpoints)
|
||||||
return E_OUTOFMEMORY;
|
return E_OUTOFMEMORY;
|
||||||
|
|
||||||
hr = get_textanalyzer(&analyzer);
|
analyzer = get_text_analyzer();
|
||||||
if (FAILED(hr))
|
|
||||||
return hr;
|
|
||||||
|
|
||||||
hr = IDWriteTextAnalyzer_AnalyzeLineBreakpoints(analyzer, (IDWriteTextAnalysisSource*)&layout->IDWriteTextAnalysisSource1_iface,
|
if (FAILED(hr = IDWriteTextAnalyzer_AnalyzeLineBreakpoints(analyzer,
|
||||||
0, layout->len, (IDWriteTextAnalysisSink*)&layout->IDWriteTextAnalysisSink1_iface);
|
(IDWriteTextAnalysisSource *)&layout->IDWriteTextAnalysisSource1_iface,
|
||||||
IDWriteTextAnalyzer_Release(analyzer);
|
0, layout->len, (IDWriteTextAnalysisSink *)&layout->IDWriteTextAnalysisSink1_iface)))
|
||||||
}
|
WARN("Line breakpoints analysis failed, hr %#x.\n", hr);
|
||||||
if (layout->actual_breakpoints) {
|
|
||||||
heap_free(layout->actual_breakpoints);
|
|
||||||
layout->actual_breakpoints = NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
heap_free(layout->actual_breakpoints);
|
||||||
|
layout->actual_breakpoints = NULL;
|
||||||
|
|
||||||
hr = layout_compute_runs(layout);
|
hr = layout_compute_runs(layout);
|
||||||
|
|
||||||
if (TRACE_ON(dwrite)) {
|
if (TRACE_ON(dwrite)) {
|
||||||
|
|
|
@ -1192,8 +1192,12 @@ static HRESULT WINAPI dwritefactory_CreateEllipsisTrimmingSign(IDWriteFactory5 *
|
||||||
static HRESULT WINAPI dwritefactory_CreateTextAnalyzer(IDWriteFactory5 *iface, IDWriteTextAnalyzer **analyzer)
|
static HRESULT WINAPI dwritefactory_CreateTextAnalyzer(IDWriteFactory5 *iface, IDWriteTextAnalyzer **analyzer)
|
||||||
{
|
{
|
||||||
struct dwritefactory *This = impl_from_IDWriteFactory5(iface);
|
struct dwritefactory *This = impl_from_IDWriteFactory5(iface);
|
||||||
|
|
||||||
TRACE("(%p)->(%p)\n", This, analyzer);
|
TRACE("(%p)->(%p)\n", This, analyzer);
|
||||||
return get_textanalyzer(analyzer);
|
|
||||||
|
*analyzer = get_text_analyzer();
|
||||||
|
|
||||||
|
return S_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
static HRESULT WINAPI dwritefactory_CreateNumberSubstitution(IDWriteFactory5 *iface,
|
static HRESULT WINAPI dwritefactory_CreateNumberSubstitution(IDWriteFactory5 *iface,
|
||||||
|
|
Loading…
Reference in New Issue