[PATCH 0/1] MR10013: dwrite/layout: Implement run reordering.
Signed-off-by: Nikolay Sivov <nsivov@codeweavers.com> -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10013
From: Nikolay Sivov <nsivov@codeweavers.com> Signed-off-by: Nikolay Sivov <nsivov@codeweavers.com> --- dlls/dwrite/layout.c | 452 ++++++++++++++++++++++++++++++++----------- 1 file changed, 341 insertions(+), 111 deletions(-) diff --git a/dlls/dwrite/layout.c b/dlls/dwrite/layout.c index 116bd3920f3..e362aba59ef 100644 --- a/dlls/dwrite/layout.c +++ b/dlls/dwrite/layout.c @@ -16,6 +16,8 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ +/* Run reordering logic is adapted from the Pango library (https://www.pango.org/) */ + #define COBJMACROS #include <assert.h> @@ -155,6 +157,7 @@ struct layout_run struct layout_effective_run { struct list draw_entry; + struct list line_entry; struct list entry; /* Common fields */ @@ -166,7 +169,7 @@ struct layout_effective_run float align_dx; /* adjustment from text alignment */ float width; /* run width */ float left; /* Logically left boundary, independent of reading direction. */ - UINT32 line; /* 0-based line index in line metrics array */ + struct layout_line *line; /* Line this run belongs to. */ UINT8 bidi_level; /* For inline objects level is derived from the first character */ bool trimming; /* Run contains trimmed text */ @@ -203,9 +206,16 @@ struct layout_cluster { struct layout_line { - float height; /* height based on content */ - float baseline; /* baseline based on content */ + struct list entry; /* Link in layout list. */ + struct list runs; /* Runs for this line, used mainly for reordering. */ + float height; /* height based on content */ + float baseline; /* baseline based on content */ DWRITE_LINE_METRICS1 metrics; + struct + { + UINT8 mask_or; + UINT8 mask_and; + } bidi; }; enum layout_recompute_mask { @@ -291,9 +301,7 @@ struct dwrite_textlayout UINT32 cluster_count; FLOAT minwidth; - struct layout_line *lines; - size_t lines_size; - + struct list lines; DWRITE_TEXT_METRICS1 metrics; DWRITE_OVERHANG_METRICS overhangs; @@ -387,8 +395,15 @@ static void free_layout_runs(struct dwrite_textlayout *layout) static void free_layout_effective_runs(struct dwrite_textlayout *layout) { struct layout_effective_run *run, *next_run; - struct layout_strikethrough *s, *s2; - struct layout_underline *u, *u2; + struct layout_strikethrough *s, *next_s; + struct layout_line *line, *next_line; + struct layout_underline *u, *next_u; + + LIST_FOR_EACH_ENTRY_SAFE(line, next_line, &layout->lines, struct layout_line, entry) + { + list_remove(&line->entry); + free(line); + } LIST_FOR_EACH_ENTRY_SAFE(run, next_run, &layout->effective_runs, struct layout_effective_run, entry) { @@ -399,13 +414,13 @@ static void free_layout_effective_runs(struct dwrite_textlayout *layout) list_init(&layout->text_runs); list_init(&layout->inlineobjects); - LIST_FOR_EACH_ENTRY_SAFE(u, u2, &layout->underlines, struct layout_underline, entry) + LIST_FOR_EACH_ENTRY_SAFE(u, next_u, &layout->underlines, struct layout_underline, entry) { list_remove(&u->entry); free(u); } - LIST_FOR_EACH_ENTRY_SAFE(s, s2, &layout->strikethrough, struct layout_strikethrough, entry) + LIST_FOR_EACH_ENTRY_SAFE(s, next_s, &layout->strikethrough, struct layout_strikethrough, entry) { list_remove(&s->entry); free(s); @@ -1508,12 +1523,17 @@ static void layout_get_text_run_font_metrics(const struct dwrite_textlayout *lay IDWriteFontFace_GetMetrics(run->run->u.regular.run.fontFace, metrics); } +static void layout_line_update_bidi(struct layout_line *line, UINT8 level) +{ + line->bidi.mask_or |= level; + line->bidi.mask_and &= level; +} + /* Effective run is built from consecutive clusters of a single nominal run, 'first_cluster' is 0 based cluster index, 'cluster_count' indicates how many clusters to add, including first one. */ static HRESULT layout_add_effective_run(struct dwrite_textlayout *layout, const struct layout_run *r, UINT32 first_cluster, - UINT32 cluster_count, UINT32 line, FLOAT origin_x, struct layout_final_splitting_params *params) + UINT32 cluster_count, struct layout_line *line, struct layout_final_splitting_params *params) { - BOOL is_rtl = layout->format.readingdir == DWRITE_READING_DIRECTION_RIGHT_TO_LEFT; struct layout_effective_run *run; UINT32 start, last_cluster; @@ -1529,15 +1549,11 @@ static HRESULT layout_add_effective_run(struct dwrite_textlayout *layout, const run->first_cluster = first_cluster; run->width = get_cluster_range_width(layout, first_cluster, first_cluster + cluster_count); - run->left = is_rtl ? origin_x - run->width : origin_x; if (r->kind == LAYOUT_RUN_INLINE) { run->object = r->u.object.object; run->width = get_cluster_range_width(layout, first_cluster, first_cluster + cluster_count); - run->origin.x = is_rtl ? origin_x - run->width : origin_x; - run->origin.y = 0.0f; /* set after line is built */ - run->align_dx = 0.0f; run->baseline = r->baseline; /* It's not clear how these two are set, possibly directionality @@ -1546,6 +1562,7 @@ static HRESULT layout_add_effective_run(struct dwrite_textlayout *layout, const run->is_sideways = false; run->bidi_level = r->u.object.bidi_level; run->line = line; + layout_line_update_bidi(line, run->bidi_level); /* effect assigned from start position and on is used for inline objects */ run->effect = layout_get_effect_from_pos(layout, layout->clusters[first_cluster].position + @@ -1553,6 +1570,7 @@ static HRESULT layout_add_effective_run(struct dwrite_textlayout *layout, const list_add_tail(&layout->inlineobjects, &run->draw_entry); list_add_tail(&layout->effective_runs, &run->entry); + list_add_tail(&line->runs, &run->line_entry); return S_OK; } @@ -1566,14 +1584,8 @@ static HRESULT layout_add_effective_run(struct dwrite_textlayout *layout, const run->run = r; run->start = start = layout->clusters[first_cluster].position; run->bidi_level = r->u.regular.run.bidiLevel; - - /* Adjust by run width if direction differs. */ - if (is_run_rtl(run) != is_rtl) - run->origin.x = origin_x + (is_rtl ? -run->width : run->width); - else - run->origin.x = origin_x; - run->line = line; + layout_line_update_bidi(line, run->bidi_level); if (r->u.regular.run.glyphCount) { /* Trim leading and trailing clusters. */ @@ -1590,6 +1602,7 @@ static HRESULT layout_add_effective_run(struct dwrite_textlayout *layout, const run->underlined = params->underline; list_add_tail(&layout->text_runs, &run->draw_entry); list_add_tail(&layout->effective_runs, &run->entry); + list_add_tail(&line->runs, &run->line_entry); /* Strikethrough style is guaranteed to be consistent within effective run, its width equals to run width, thickness and offset are derived from @@ -1619,48 +1632,55 @@ static HRESULT layout_add_effective_run(struct dwrite_textlayout *layout, const return S_OK; } -static void layout_apply_line_spacing(struct dwrite_textlayout *layout, UINT32 line) +static void layout_apply_line_spacing(struct dwrite_textlayout *layout, struct layout_line *line) { switch (layout->format.spacing.method) { case DWRITE_LINE_SPACING_METHOD_DEFAULT: - layout->lines[line].metrics.height = layout->lines[line].height; - layout->lines[line].metrics.baseline = layout->lines[line].baseline; + line->metrics.height = line->height; + line->metrics.baseline = line->baseline; break; case DWRITE_LINE_SPACING_METHOD_UNIFORM: - layout->lines[line].metrics.height = layout->format.spacing.height; - layout->lines[line].metrics.baseline = layout->format.spacing.baseline; + line->metrics.height = layout->format.spacing.height; + line->metrics.baseline = layout->format.spacing.baseline; break; case DWRITE_LINE_SPACING_METHOD_PROPORTIONAL: - layout->lines[line].metrics.height = layout->lines[line].height * layout->format.spacing.height; - layout->lines[line].metrics.baseline = layout->lines[line].baseline * layout->format.spacing.baseline; + line->metrics.height = line->height * layout->format.spacing.height; + line->metrics.baseline = line->baseline * layout->format.spacing.baseline; break; default: - ERR("Unknown spacing method %u\n", layout->format.spacing.method); + WARN("Unknown spacing method %u\n", layout->format.spacing.method); } } -static HRESULT layout_set_line_metrics(struct dwrite_textlayout *layout, const DWRITE_LINE_METRICS1 *metrics) +static HRESULT layout_new_line(struct dwrite_textlayout *layout, struct layout_line **ret) { - size_t i = layout->metrics.lineCount; + struct layout_line *line; - if (!dwrite_array_reserve((void **)&layout->lines, &layout->lines_size, layout->metrics.lineCount + 1, - sizeof(*layout->lines))) - { + if (!(line = calloc(1, sizeof(*line)))) return E_OUTOFMEMORY; - } - layout->lines[i].metrics = *metrics; - layout->lines[i].height = metrics->height; - layout->lines[i].baseline = metrics->baseline; + list_init(&line->runs); + line->bidi.mask_and = 1; + list_add_tail(&layout->lines, &line->entry); + ++layout->metrics.lineCount; - if (layout->format.spacing.method != DWRITE_LINE_SPACING_METHOD_DEFAULT) - layout_apply_line_spacing(layout, i); + *ret = line; - layout->metrics.lineCount++; return S_OK; } +static void layout_set_line_metrics(struct dwrite_textlayout *layout, struct layout_line *line, + const DWRITE_LINE_METRICS1 *metrics) +{ + line->metrics = *metrics; + line->height = metrics->height; + line->baseline = metrics->baseline; + + if (layout->format.spacing.method != DWRITE_LINE_SPACING_METHOD_DEFAULT) + layout_apply_line_spacing(layout, line); +} + static inline struct layout_effective_run *layout_get_next_effective_run(const struct dwrite_textlayout *layout, const struct layout_effective_run *cur) { @@ -1711,15 +1731,14 @@ static inline struct layout_effective_run *layout_get_prev_text_run(const struct return LIST_ENTRY(e, struct layout_effective_run, draw_entry); } -static float layout_get_line_width(const struct dwrite_textlayout *layout, - const struct layout_effective_run *run, UINT32 line) +static float layout_get_line_width(struct layout_line *line) { - FLOAT width = 0.0f; + const struct layout_effective_run *run; + float width = 0.0f; - while (run && run->line == line) + LIST_FOR_EACH_ENTRY(run, &line->runs, struct layout_effective_run, line_entry) { width += run->width; - run = layout_get_next_effective_run(layout, run); } return width; @@ -1779,23 +1798,21 @@ static void layout_apply_leading_alignment(struct dwrite_textlayout *layout) static void layout_apply_trailing_alignment(struct dwrite_textlayout *layout) { - BOOL is_rtl = layout->format.readingdir == DWRITE_READING_DIRECTION_RIGHT_TO_LEFT; + bool is_rtl = layout->format.readingdir == DWRITE_READING_DIRECTION_RIGHT_TO_LEFT; struct layout_effective_run *run; - UINT32 line; + struct layout_line *line; - run = layout_get_next_effective_run(layout, NULL); - for (line = 0; line < layout->metrics.lineCount; line++) + LIST_FOR_EACH_ENTRY(line, &layout->lines, struct layout_line, entry) { - float width = layout_get_line_width(layout, run, line); + float width = layout_get_line_width(line); FLOAT shift = layout->metrics.layoutWidth - width; if (is_rtl) shift *= -1.0f; - while (run && run->line == line) + LIST_FOR_EACH_ENTRY(run, &line->runs, struct layout_effective_run, line_entry) { run->align_dx = shift; - run = layout_get_next_effective_run(layout, run); } } @@ -1818,24 +1835,23 @@ static void layout_apply_centered_alignment(struct dwrite_textlayout *layout) { BOOL is_rtl = layout->format.readingdir == DWRITE_READING_DIRECTION_RIGHT_TO_LEFT; struct layout_effective_run *run; + struct layout_line *line; BOOL skiptransform; - UINT32 line; FLOAT det; skiptransform = should_skip_transform(&layout->transform, &det); - run = layout_get_next_effective_run(layout, NULL); - for (line = 0; line < layout->metrics.lineCount; line++) { - float width = layout_get_line_width(layout, run, line); + LIST_FOR_EACH_ENTRY(line, &layout->lines, struct layout_line, entry) + { + float width = layout_get_line_width(line); FLOAT shift = layout_get_centered_shift(layout, skiptransform, width, det); if (is_rtl) shift *= -1.0f; - while (run && run->line == line) + LIST_FOR_EACH_ENTRY(run, &line->runs, struct layout_effective_run, line_entry) { run->align_dx = shift; - run = layout_get_next_effective_run(layout, run); } } @@ -1866,8 +1882,8 @@ static void layout_apply_text_alignment(struct dwrite_textlayout *layout) static void layout_apply_par_alignment(struct dwrite_textlayout *layout) { struct layout_effective_run *run; + struct layout_line *line; FLOAT origin_y = 0.0f; - UINT32 line; /* alignment mode defines origin, after that all run origins are updated the same way */ @@ -1889,19 +1905,17 @@ static void layout_apply_par_alignment(struct dwrite_textlayout *layout) layout->metrics.top = origin_y; - run = layout_get_next_effective_run(layout, NULL); - for (line = 0; line < layout->metrics.lineCount; line++) + LIST_FOR_EACH_ENTRY(line, &layout->lines, struct layout_line, entry) { - float pos_y = origin_y + layout->lines[line].metrics.baseline; + float pos_y = origin_y + line->metrics.baseline; - while (run && run->line == line) + LIST_FOR_EACH_ENTRY(run, &line->runs, struct layout_effective_run, line_entry) { /* Baseline is zero for textual runs */ run->origin.y = pos_y - run->baseline; - run = layout_get_next_effective_run(layout, run); } - origin_y += layout->lines[line].metrics.height; + origin_y += line->metrics.height; } } @@ -1990,7 +2004,7 @@ static HRESULT layout_add_underline(struct dwrite_textlayout *layout, struct lay however Y grows from baseline down for horizontal baseline. */ u->u.offset = -offset; u->u.runHeight = runheight; - u->u.readingDirection = is_run_rtl(cur) ? DWRITE_READING_DIRECTION_RIGHT_TO_LEFT : + u->u.readingDirection = cur->bidi_level & 1 ? DWRITE_READING_DIRECTION_RIGHT_TO_LEFT : DWRITE_READING_DIRECTION_LEFT_TO_RIGHT; u->u.flowDirection = layout->format.flow; u->u.localeName = cur->run->u.regular.descr.localeName; @@ -2025,6 +2039,7 @@ static HRESULT layout_set_dummy_line_metrics(struct dwrite_textlayout *layout) DWRITE_FONT_METRICS fontmetrics; struct regular_layout_run *run; IDWriteFontFace *fontface; + struct layout_line *line; float size; HRESULT hr; @@ -2051,7 +2066,202 @@ static HRESULT layout_set_dummy_line_metrics(struct dwrite_textlayout *layout) layout_get_font_height(size, &fontmetrics, &metrics.baseline, &metrics.height); IDWriteFontFace_Release(fontface); - return layout_set_line_metrics(layout, &metrics); + if (SUCCEEDED(hr = layout_new_line(layout, &line))) + layout_set_line_metrics(layout, line, &metrics); + + return hr; +} + +struct reorder_entry +{ + struct layout_effective_run *run; + struct reorder_entry *next; +}; + +static struct reorder_entry * layout_reorder_last(struct reorder_entry *list) +{ + if (!list) + return NULL; + + while (list->next) + list = list->next; + + return list; +} + +static struct reorder_entry * layout_reorder_append_run(struct reorder_entry *list, struct layout_effective_run *run) +{ + struct reorder_entry *entry; + + entry = malloc(sizeof(*entry)); + entry->run = run; + entry->next = NULL; + + if (list) + { + layout_reorder_last(list)->next = entry; + return list; + } + else + { + return entry; + } +} + +static struct reorder_entry * layout_reorder_prepend_run(struct reorder_entry *list, struct layout_effective_run *run) +{ + struct reorder_entry *entry; + + entry = malloc(sizeof(*entry)); + entry->run = run; + entry->next = list; + + return entry; +} + +static struct reorder_entry * layout_reorder_concat(struct reorder_entry *left, struct reorder_entry *right) +{ + if (right) + { + if (left) + layout_reorder_last(left)->next = right; + else + left = right; + } + + return left; +} + +static int layout_reorder_get_min_level(struct reorder_entry *list, unsigned int count) +{ + int min_level = INT_MAX; + + while (count--) + { + min_level = min(min_level, list->run->bidi_level); + list = list->next; + } + + return min_level; +} + +static struct reorder_entry * layout_reorder_runs_recursive(struct reorder_entry *list, unsigned int count) +{ + struct reorder_entry *result = NULL, *tmp_list, *level_start_node; + int i, level_start_i, min_level; + + if (!count) + return NULL; + + min_level = layout_reorder_get_min_level(list, count); + + level_start_i = 0; + level_start_node = list; + tmp_list = list; + for (i = 0; i < count; ++i) + { + struct layout_effective_run *run = tmp_list->run; + + if (run->bidi_level == min_level) + { + if (min_level % 2) + { + if (i > level_start_i) + result = layout_reorder_concat(layout_reorder_runs_recursive(level_start_node, i - level_start_i), result); + result = layout_reorder_prepend_run(result, run); + } + else + { + if (i > level_start_i) + result = layout_reorder_concat(result, layout_reorder_runs_recursive(level_start_node, i - level_start_i)); + result = layout_reorder_append_run(result, run); + } + + level_start_i = i + 1; + level_start_node = tmp_list->next; + } + + tmp_list = tmp_list->next; + } + + if (min_level % 2) + { + if (i > level_start_i) + result = layout_reorder_concat(layout_reorder_runs_recursive(level_start_node, i - level_start_i), result); + } + else + { + if (i > level_start_i) + result = layout_reorder_concat(result, layout_reorder_runs_recursive(level_start_node, i - level_start_i)); + } + + return result; +} + +static void layout_free_reorder_result(struct reorder_entry *list) +{ + struct reorder_entry *cur; + + while (list) + { + cur = list; + list = list->next; + free(cur); + } +} + +/* Implements same exact reordering algorithm that the Pango library is using. */ +static void layout_reorder_runs(struct layout_line *line) +{ + struct reorder_entry *logical_list = NULL, *result, *cur; + struct layout_effective_run *run, *next; + bool needs_reordering, needs_reversing; + unsigned int count = 0; + bool all_even, all_odd; + + all_even = (line->bidi.mask_or & 1) == 0; + all_odd = (line->bidi.mask_and & 1) == 1; + + needs_reordering = !all_even && !all_odd; + needs_reversing = all_odd; + + if (needs_reordering || needs_reversing) + { + /* Transfer to ordered list */ + LIST_FOR_EACH_ENTRY_SAFE(run, next, &line->runs, struct layout_effective_run, line_entry) + { + logical_list = layout_reorder_append_run(logical_list, run); + list_remove(&run->line_entry); + ++count; + } + + if (needs_reordering) + { + result = layout_reorder_runs_recursive(logical_list, count); + + while (result) + { + cur = result; + result = result->next; + + list_add_tail(&line->runs, &cur->run->line_entry); + free(cur); + } + } + else + { + while (logical_list) + { + cur = logical_list; + logical_list = logical_list->next; + + list_add_head(&line->runs, &cur->run->line_entry); + free(cur); + } + } + + layout_free_reorder_result(logical_list); + } } static HRESULT layout_add_line(struct dwrite_textlayout *layout, UINT32 first_cluster, @@ -2061,14 +2271,18 @@ static HRESULT layout_add_line(struct dwrite_textlayout *layout, UINT32 first_cl struct layout_final_splitting_params params, prev_params; DWRITE_INLINE_OBJECT_METRICS sign_metrics = { 0 }; UINT32 count, length, start, end, pos = *textpos; - UINT32 line = layout->metrics.lineCount; + struct layout_effective_run *line_run; DWRITE_LINE_METRICS1 metrics = { 0 }; FLOAT descent, trailingspacewidth; bool append_trimming_run = false; const struct layout_run *run; float width = 0.0f, origin_x; + struct layout_line *line; HRESULT hr; + if (FAILED(hr = layout_new_line(layout, &line))) + return hr; + /* Set trailing properties for current line */ trailingspacewidth = 0.0f; count = cluster_count; @@ -2159,19 +2373,16 @@ static HRESULT layout_add_line(struct dwrite_textlayout *layout, UINT32 first_cl /* Form runs from a range of clusters; this is what will be reported with DrawGlyphRun() */ length = 0; - origin_x = is_rtl ? layout->metrics.layoutWidth : 0.0f; for (start = first_cluster, end = first_cluster; end < first_cluster + cluster_count; ++end) { layout_splitting_params_from_pos(layout, pos, ¶ms); if (run != layout->clusters[end].run || !is_same_splitting_params(&prev_params, ¶ms)) { - hr = layout_add_effective_run(layout, run, start, end - start, line, origin_x, &prev_params); + hr = layout_add_effective_run(layout, run, start, end - start, line, &prev_params); if (FAILED(hr)) return hr; - origin_x += is_rtl ? -get_cluster_range_width(layout, start, end) : - get_cluster_range_width(layout, start, end); run = layout->clusters[end].run; start = end; } @@ -2184,7 +2395,7 @@ static HRESULT layout_add_line(struct dwrite_textlayout *layout, UINT32 first_cl /* Final run from what's left from cluster range */ if (end - start) { - if (FAILED(hr = layout_add_effective_run(layout, run, start, end - start, line, origin_x, &prev_params))) + if (FAILED(hr = layout_add_effective_run(layout, run, start, end - start, line, &prev_params))) return hr; } @@ -2199,18 +2410,33 @@ static HRESULT layout_add_line(struct dwrite_textlayout *layout, UINT32 first_cl trimming_sign->length = metrics.length - length; trimming_sign->object = layout->format.trimmingsign; trimming_sign->width = sign_metrics.width; - origin_x += is_rtl ? -get_cluster_range_width(layout, start, end) : get_cluster_range_width(layout, start, end); - trimming_sign->origin.x = is_rtl ? origin_x - trimming_sign->width : origin_x; trimming_sign->baseline = sign_metrics.baseline; + trimming_sign->line = line; trimming_sign->bidi_level = layout->clusters[end].run->u.regular.run.bidiLevel; + layout_line_update_bidi(line, trimming_sign->bidi_level); trimming_sign->trimming = true; - trimming_sign->line = line; trimming_sign->effect = layout_get_effect_from_pos(layout, trimming_sign->start_position); list_add_tail(&layout->inlineobjects, &trimming_sign->draw_entry); list_add_tail(&layout->effective_runs, &trimming_sign->entry); + /* TODO: it's possible trimming sign should not appear in reordered list */ + list_add_tail(&line->runs, &trimming_sign->line_entry); + } + + layout_reorder_runs(line); + + /* Position along baseline, and look for max baseline and descent for this line. */ + origin_x = is_rtl ? layout->metrics.layoutWidth - width : 0.0f; + LIST_FOR_EACH_ENTRY(line_run, &line->runs, struct layout_effective_run, line_entry) + { + /* Regardless of the paragraph direction, origin location always matches run direction. + This does not affect inline objects - those are always rendered from visually left side origin. */ + line_run->origin.x = line_run->left = origin_x; + if (!line_run->object && line_run->bidi_level & 1) + line_run->origin.x += line_run->width; + origin_x += line_run->width; } /* Look for max baseline and descent for this line */ @@ -2236,29 +2462,29 @@ static HRESULT layout_add_line(struct dwrite_textlayout *layout, UINT32 first_cl *textpos += metrics.length; - return layout_set_line_metrics(layout, &metrics); + layout_set_line_metrics(layout, line, &metrics); + return hr; } static void layout_set_line_positions(struct dwrite_textlayout *layout) { struct layout_effective_run *run; - FLOAT origin_y; - UINT32 line; + struct layout_line *line; + float origin_y = 0.0f; /* Now all line info is here, update effective runs positions in flow direction */ - run = layout_get_next_effective_run(layout, NULL); - for (line = 0, origin_y = 0.0f; line < layout->metrics.lineCount; line++) + LIST_FOR_EACH_ENTRY(line, &layout->lines, struct layout_line, entry) { - float pos_y = origin_y + layout->lines[line].metrics.baseline; + float pos_y; - while (run && run->line == line) + pos_y = origin_y + line->metrics.baseline; + LIST_FOR_EACH_ENTRY(run, &line->runs, struct layout_effective_run, line_entry) { /* Baseline is zero for textual runs */ run->origin.y = pos_y - run->baseline; - run = layout_get_next_effective_run(layout, run); } - origin_y += layout->lines[line].metrics.height; + origin_y += line->metrics.height; } layout->metrics.height = origin_y; @@ -2365,7 +2591,7 @@ static HRESULT layout_compute_effective_runs(struct dwrite_textlayout *layout) { BOOL is_rtl = layout->format.readingdir == DWRITE_READING_DIRECTION_RIGHT_TO_LEFT; struct layout_effective_run *run, *first_underlined; - DWRITE_LINE_METRICS1 metrics; + struct layout_line *line; HRESULT hr; if (!(layout->recompute & RECOMPUTE_LINES)) @@ -2377,8 +2603,6 @@ static HRESULT layout_compute_effective_runs(struct dwrite_textlayout *layout) return hr; layout->metrics.lineCount = 0; - memset(&metrics, 0, sizeof(metrics)); - layout->metrics.height = 0.0f; layout->metrics.width = 0.0f; layout->metrics.widthIncludingTrailingWhitespace = 0.0f; @@ -2396,7 +2620,7 @@ static HRESULT layout_compute_effective_runs(struct dwrite_textlayout *layout) /* Add explicit underlined runs */ run = layout_get_next_text_run(layout, NULL); first_underlined = run && run->underlined ? run : NULL; - for (unsigned int line = 0; line < layout->metrics.lineCount; line++) + LIST_FOR_EACH_ENTRY(line, &layout->lines, struct layout_line, entry) { while (run && run->line == line) { @@ -3148,7 +3372,6 @@ static ULONG WINAPI dwritetextlayout_Release(IDWriteTextLayout4 *iface) free(layout->actual_breakpoints); free(layout->clustermetrics); free(layout->clusters); - free(layout->lines); free(layout->text); free(layout); } @@ -3874,9 +4097,9 @@ static HRESULT WINAPI dwritetextlayout_GetLineMetrics(IDWriteTextLayout4 *iface, DWRITE_LINE_METRICS *metrics, UINT32 max_count, UINT32 *count) { struct dwrite_textlayout *layout = impl_from_IDWriteTextLayout4(iface); - unsigned int line_count; + unsigned int i = 0, line_count; + struct layout_line *line; HRESULT hr; - size_t i; TRACE("%p, %p, %u, %p.\n", iface, metrics, max_count, count); @@ -3886,8 +4109,11 @@ static HRESULT WINAPI dwritetextlayout_GetLineMetrics(IDWriteTextLayout4 *iface, if (metrics) { line_count = min(max_count, layout->metrics.lineCount); - for (i = 0; i < line_count; ++i) - memcpy(&metrics[i], &layout->lines[i].metrics, sizeof(*metrics)); + LIST_FOR_EACH_ENTRY(line, &layout->lines, struct layout_line, entry) + { + if (i >= line_count) break; + memcpy(&metrics[i++], &line->metrics, sizeof(*metrics)); + } } *count = layout->metrics.lineCount; @@ -4188,7 +4414,7 @@ static HRESULT WINAPI dwritetextlayout_HitTestTextPosition(IDWriteTextLayout4 *i if (position == layout->length) { - const DWRITE_LINE_METRICS1 *line = &layout->lines[layout->metrics.lineCount - 1].metrics; + struct layout_line *line = LIST_ENTRY(list_tail(&layout->lines), struct layout_line, entry); UINT8 level; run = layout_get_trailing_effective_run(layout); @@ -4198,9 +4424,9 @@ static HRESULT WINAPI dwritetextlayout_HitTestTextPosition(IDWriteTextLayout4 *i metrics->length = 0; /* Empty box following the last run */ metrics->left = run->left + run->align_dx + run->width; - metrics->top = layout->metrics.height - line->height; + metrics->top = layout->metrics.height - line->metrics.height; metrics->width = 0.0f; - metrics->height = line->height; + metrics->height = line->metrics.height; metrics->bidiLevel = level; metrics->isText = run->trimming || !run->object; metrics->isTrimmed = run->trimming; @@ -4221,7 +4447,7 @@ static HRESULT WINAPI dwritetextlayout_HitTestTextPosition(IDWriteTextLayout4 *i metrics->textPosition = run->start_position; metrics->length = run->length; metrics->left = run->origin.x + run->align_dx; - metrics->top = run->origin.y - layout->lines[run->line].baseline; + metrics->top = run->origin.y - run->line->baseline; metrics->height = 0.0f; metrics->width = run->width; metrics->bidiLevel = run->bidi_level; @@ -4256,8 +4482,8 @@ static HRESULT WINAPI dwritetextlayout_HitTestTextPosition(IDWriteTextLayout4 *i { metrics->left += get_cluster_range_width(layout, run->first_cluster, cluster); } - metrics->top = run->origin.y - layout->lines[run->line].baseline; - metrics->height = layout->lines[run->line].height; + metrics->top = run->origin.y - run->line->baseline; + metrics->height = run->line->height; metrics->isText = TRUE; metrics->isTrimmed = FALSE; } @@ -4454,6 +4680,7 @@ static HRESULT WINAPI dwritetextlayout3_InvalidateLayout(IDWriteTextLayout4 *ifa static HRESULT WINAPI dwritetextlayout3_SetLineSpacing(IDWriteTextLayout4 *iface, DWRITE_LINE_SPACING const *spacing) { struct dwrite_textlayout *layout = impl_from_IDWriteTextLayout4(iface); + struct layout_line *line; BOOL changed; HRESULT hr; @@ -4467,11 +4694,10 @@ static HRESULT WINAPI dwritetextlayout3_SetLineSpacing(IDWriteTextLayout4 *iface { if (!(layout->recompute & RECOMPUTE_LINES)) { - UINT32 line; - - for (line = 0; line < layout->metrics.lineCount; line++) + LIST_FOR_EACH_ENTRY(line, &layout->lines, struct layout_line, entry) + { layout_apply_line_spacing(layout, line); - + } layout_set_line_positions(layout); } @@ -4495,9 +4721,9 @@ static HRESULT WINAPI dwritetextlayout3_GetLineMetrics(IDWriteTextLayout4 *iface UINT32 max_count, UINT32 *count) { struct dwrite_textlayout *layout = impl_from_IDWriteTextLayout4(iface); - unsigned int line_count; + unsigned int i = 0, line_count; + struct layout_line *line; HRESULT hr; - size_t i; TRACE("%p, %p, %u, %p.\n", iface, metrics, max_count, count); @@ -4507,8 +4733,11 @@ static HRESULT WINAPI dwritetextlayout3_GetLineMetrics(IDWriteTextLayout4 *iface if (metrics) { line_count = min(max_count, layout->metrics.lineCount); - for (i = 0; i < line_count; ++i) - metrics[i] = layout->lines[i].metrics; + LIST_FOR_EACH_ENTRY(line, &layout->lines, struct layout_line, entry) + { + if (i >= line_count) break; + metrics[i++] = line->metrics; + } } *count = layout->metrics.lineCount; @@ -5542,6 +5771,7 @@ static HRESULT init_textlayout(const struct textlayout_desc *desc, struct dwrite list_init(&layout->strikethrough); list_init(&layout->effective_runs); list_init(&layout->runs); + list_init(&layout->lines); list_init(&layout->ranges); list_init(&layout->strike_ranges); list_init(&layout->underline_ranges); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10013
participants (2)
-
Nikolay Sivov -
Nikolay Sivov (@nsivov)