[PATCH 0/11] MR9974: dwrite/tests: Add more tests for GetClusterMetrics().
Signed-off-by: Nikolay Sivov <nsivov@codeweavers.com> -- https://gitlab.winehq.org/wine/wine/-/merge_requests/9974
From: Nikolay Sivov <nsivov@codeweavers.com> Signed-off-by: Nikolay Sivov <nsivov@codeweavers.com> --- dlls/dwrite/tests/layout.c | 99 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 99 insertions(+) diff --git a/dlls/dwrite/tests/layout.c b/dlls/dwrite/tests/layout.c index 6e620603c72..8fc02a0fe9f 100644 --- a/dlls/dwrite/tests/layout.c +++ b/dlls/dwrite/tests/layout.c @@ -2416,6 +2416,7 @@ static void test_GetClusterMetrics(void) 'g',0x0085,'h',0x2028,'i',0x2029,0xad,0xa,0}; static const WCHAR str3W[] = {0x2066,')',')',0x661,'(',0x627,')',0}; static const WCHAR str2W[] = {0x202a,0x202c,'a',0}; + static const WCHAR str6W[] = {0x0646,0x0627,'a',0x0646,0x0627,0}; struct test_inline_obj *testinlineobj, *testinlineobj2, *testinlineobj3; DWRITE_INLINE_OBJECT_METRICS inline_metrics; @@ -2590,6 +2591,104 @@ static void test_GetClusterMetrics(void) IDWriteTextLayout_Release(layout); + /* RTL text with RTL reading direction. Inline object covers some characters. */ + hr = IDWriteFactory_CreateTextLayout(factory, str6W, 5, format, 1000.0, 1000.0, &layout); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + count = 0; + memset(metrics, 0, sizeof(metrics)); + hr = IDWriteTextLayout_GetClusterMetrics(layout, metrics, 5, &count); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(count == 5, "Unexpected count %u.\n", count); + + ok(metrics[0].isRightToLeft == 1, "Unexpected value %d.\n", metrics[0].isRightToLeft); + ok(metrics[1].isRightToLeft == 1, "Unexpected value %d.\n", metrics[1].isRightToLeft); + ok(metrics[2].isRightToLeft == 0, "Unexpected value %d.\n", metrics[2].isRightToLeft); + ok(metrics[3].isRightToLeft == 1, "Unexpected value %d.\n", metrics[3].isRightToLeft); + ok(metrics[4].isRightToLeft == 1, "Unexpected value %d.\n", metrics[4].isRightToLeft); + + range.startPosition = 1; + range.length = 1; + hr = IDWriteTextLayout_SetInlineObject(layout, &testinlineobj->IDWriteInlineObject_iface, range); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + count = 0; + memset(metrics, 0, sizeof(metrics)); + hr = IDWriteTextLayout_GetClusterMetrics(layout, metrics, 5, &count); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(count == 5, "Unexpected count %u.\n", count); + + ok(metrics[0].isRightToLeft == 1, "Unexpected value %d.\n", metrics[0].isRightToLeft); + todo_wine + ok(metrics[1].isRightToLeft == 1, "Unexpected value %d.\n", metrics[1].isRightToLeft); + ok(metrics[2].isRightToLeft == 0, "Unexpected value %d.\n", metrics[2].isRightToLeft); + ok(metrics[3].isRightToLeft == 1, "Unexpected value %d.\n", metrics[3].isRightToLeft); + ok(metrics[4].isRightToLeft == 1, "Unexpected value %d.\n", metrics[4].isRightToLeft); + + range.startPosition = 1; + range.length = 2; + hr = IDWriteTextLayout_SetInlineObject(layout, &testinlineobj->IDWriteInlineObject_iface, range); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + count = 0; + memset(metrics, 0, sizeof(metrics)); + hr = IDWriteTextLayout_GetClusterMetrics(layout, metrics, 5, &count); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(count == 4, "Unexpected count %u.\n", count); + + ok(metrics[0].isRightToLeft == 1, "Unexpected value %d.\n", metrics[0].isRightToLeft); + todo_wine + ok(metrics[1].isRightToLeft == 1, "Unexpected value %d.\n", metrics[1].isRightToLeft); + ok(metrics[1].length == 2, "Unexpected value %d.\n", metrics[1].length); + ok(metrics[2].isRightToLeft == 1, "Unexpected value %d.\n", metrics[2].isRightToLeft); + ok(metrics[2].length == 1, "Unexpected value %d.\n", metrics[2].length); + ok(metrics[3].isRightToLeft == 1, "Unexpected value %d.\n", metrics[3].isRightToLeft); + ok(metrics[3].length == 1, "Unexpected value %d.\n", metrics[3].length); + + range.startPosition = 1; + range.length = 2; + hr = IDWriteTextLayout_SetInlineObject(layout, NULL, range); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + range.startPosition = 2; + range.length = 1; + hr = IDWriteTextLayout_SetInlineObject(layout, &testinlineobj->IDWriteInlineObject_iface, range); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + count = 0; + memset(metrics, 0, sizeof(metrics)); + hr = IDWriteTextLayout_GetClusterMetrics(layout, metrics, 5, &count); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(count == 5, "Unexpected count %u.\n", count); + + ok(metrics[0].isRightToLeft == 1, "Unexpected value %d.\n", metrics[0].isRightToLeft); + ok(metrics[1].isRightToLeft == 1, "Unexpected value %d.\n", metrics[1].isRightToLeft); + ok(metrics[2].isRightToLeft == 0, "Unexpected value %d.\n", metrics[2].isRightToLeft); + ok(metrics[3].isRightToLeft == 1, "Unexpected value %d.\n", metrics[3].isRightToLeft); + ok(metrics[4].isRightToLeft == 1, "Unexpected value %d.\n", metrics[4].isRightToLeft); + + range.startPosition = 2; + range.length = 2; + hr = IDWriteTextLayout_SetInlineObject(layout, &testinlineobj->IDWriteInlineObject_iface, range); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + count = 0; + memset(metrics, 0, sizeof(metrics)); + hr = IDWriteTextLayout_GetClusterMetrics(layout, metrics, 5, &count); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(count == 4, "Unexpected count %u.\n", count); + + ok(metrics[0].isRightToLeft == 1, "Unexpected value %d.\n", metrics[0].isRightToLeft); + ok(metrics[1].isRightToLeft == 1, "Unexpected value %d.\n", metrics[1].isRightToLeft); + ok(metrics[1].length == 1, "Unexpected value %d.\n", metrics[1].length); + ok(metrics[2].isRightToLeft == 0, "Unexpected value %d.\n", metrics[2].isRightToLeft); + ok(metrics[2].length == 2, "Unexpected value %d.\n", metrics[2].length); + todo_wine + ok(metrics[3].isRightToLeft == 1, "Unexpected value %d.\n", metrics[3].isRightToLeft); + ok(metrics[3].length == 1, "Unexpected value %d.\n", metrics[3].length); + + IDWriteTextLayout_Release(layout); + /* single inline object that fails to report its metrics */ hr = IDWriteFactory_CreateTextLayout(factory, L"abcd", 4, format, 1000.0f, 1000.0f, &layout); ok(hr == S_OK, "Failed to create text layout, hr %#lx.\n", hr); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/9974
From: Nikolay Sivov <nsivov@codeweavers.com> Signed-off-by: Nikolay Sivov <nsivov@codeweavers.com> --- dlls/dwrite/layout.c | 4 +++- dlls/dwrite/tests/layout.c | 2 -- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/dlls/dwrite/layout.c b/dlls/dwrite/layout.c index 932be5551ac..949b81baf67 100644 --- a/dlls/dwrite/layout.c +++ b/dlls/dwrite/layout.c @@ -124,6 +124,7 @@ enum layout_run_kind { struct inline_object_run { IDWriteInlineObject *object; UINT16 length; + UINT8 bidi_level; }; struct regular_layout_run { @@ -767,6 +768,7 @@ static HRESULT layout_itemize_add_run(struct itemization_context *context) run->u.object.object = context->range.value->object; run->u.object.length = length; + run->u.object.bidi_level = context->level.value; } else { @@ -1360,7 +1362,7 @@ static HRESULT layout_compute_runs(struct dwrite_textlayout *layout) metrics->isWhitespace = 0; metrics->isNewline = 0; metrics->isSoftHyphen = 0; - metrics->isRightToLeft = 0; + metrics->isRightToLeft = r->u.object.bidi_level & 1; metrics->padding = 0; c->run = r; c->position = 0; /* there's always one cluster per inline object, so 0 is valid value */ diff --git a/dlls/dwrite/tests/layout.c b/dlls/dwrite/tests/layout.c index 8fc02a0fe9f..b7f3e1a5018 100644 --- a/dlls/dwrite/tests/layout.c +++ b/dlls/dwrite/tests/layout.c @@ -2619,7 +2619,6 @@ static void test_GetClusterMetrics(void) ok(count == 5, "Unexpected count %u.\n", count); ok(metrics[0].isRightToLeft == 1, "Unexpected value %d.\n", metrics[0].isRightToLeft); - todo_wine ok(metrics[1].isRightToLeft == 1, "Unexpected value %d.\n", metrics[1].isRightToLeft); ok(metrics[2].isRightToLeft == 0, "Unexpected value %d.\n", metrics[2].isRightToLeft); ok(metrics[3].isRightToLeft == 1, "Unexpected value %d.\n", metrics[3].isRightToLeft); @@ -2637,7 +2636,6 @@ static void test_GetClusterMetrics(void) ok(count == 4, "Unexpected count %u.\n", count); ok(metrics[0].isRightToLeft == 1, "Unexpected value %d.\n", metrics[0].isRightToLeft); - todo_wine ok(metrics[1].isRightToLeft == 1, "Unexpected value %d.\n", metrics[1].isRightToLeft); ok(metrics[1].length == 2, "Unexpected value %d.\n", metrics[1].length); ok(metrics[2].isRightToLeft == 1, "Unexpected value %d.\n", metrics[2].isRightToLeft); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/9974
From: Nikolay Sivov <nsivov@codeweavers.com> Signed-off-by: Nikolay Sivov <nsivov@codeweavers.com> --- dlls/dwrite/layout.c | 17 +++-------------- dlls/dwrite/tests/layout.c | 1 - 2 files changed, 3 insertions(+), 15 deletions(-) diff --git a/dlls/dwrite/layout.c b/dlls/dwrite/layout.c index 949b81baf67..f6684097234 100644 --- a/dlls/dwrite/layout.c +++ b/dlls/dwrite/layout.c @@ -690,22 +690,11 @@ static void layout_itemize_next_range(struct itemization_context *context) static void layout_itemize_set_run_end(struct itemization_context *context) { - /* Inline objects take precedence, skip level and script ranges accordingly. */ + /* Inline objects take precedence. */ if (context->range.value->object) { - UINT8 level = context->level.value; - UINT16 script = context->script.script; - context->run_end = context->range.end; - - while (context->level.end < context->run_end) - layout_itemize_next_level(context); - while (context->script.end < context->run_end) - layout_itemize_next_script(context); - - context->level.value = level; - context->script.script = script; } else { @@ -734,10 +723,10 @@ static bool layout_itemize_get_next(struct itemization_context *context) context->run_start = context->run_end; - if (context->run_end == context->level.end) + while (context->level.end <= context->run_end) layout_itemize_next_level(context); - if (context->run_end == context->script.end) + while (context->script.end <= context->run_end) layout_itemize_next_script(context); if (context->run_end == context->range.end) diff --git a/dlls/dwrite/tests/layout.c b/dlls/dwrite/tests/layout.c index b7f3e1a5018..02a9cfe37aa 100644 --- a/dlls/dwrite/tests/layout.c +++ b/dlls/dwrite/tests/layout.c @@ -2681,7 +2681,6 @@ static void test_GetClusterMetrics(void) ok(metrics[1].length == 1, "Unexpected value %d.\n", metrics[1].length); ok(metrics[2].isRightToLeft == 0, "Unexpected value %d.\n", metrics[2].isRightToLeft); ok(metrics[2].length == 2, "Unexpected value %d.\n", metrics[2].length); - todo_wine ok(metrics[3].isRightToLeft == 1, "Unexpected value %d.\n", metrics[3].isRightToLeft); ok(metrics[3].length == 1, "Unexpected value %d.\n", metrics[3].length); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/9974
From: Nikolay Sivov <nsivov@codeweavers.com> Signed-off-by: Nikolay Sivov <nsivov@codeweavers.com> --- dlls/dwrite/tests/layout.c | 52 ++++++++++++++++++++++++++++++++++---- 1 file changed, 47 insertions(+), 5 deletions(-) diff --git a/dlls/dwrite/tests/layout.c b/dlls/dwrite/tests/layout.c index 02a9cfe37aa..17a60692cf9 100644 --- a/dlls/dwrite/tests/layout.c +++ b/dlls/dwrite/tests/layout.c @@ -2752,18 +2752,59 @@ static void test_GetClusterMetrics(void) IDWriteTextLayout_Release(layout); /* Whitespace */ - hr = IDWriteFactory_CreateTextLayout(factory, L"a ", 2, format, 1000.0f, 1000.0f, &layout); + hr = IDWriteFactory_CreateTextLayout(factory, L"a b ", 4, format, 1000.0f, 1000.0f, &layout); ok(hr == S_OK, "Failed to create text layout, hr %#lx.\n", hr); count = 0; memset(metrics, 0, sizeof(metrics)); - hr = IDWriteTextLayout_GetClusterMetrics(layout, metrics, 2, &count); + hr = IDWriteTextLayout_GetClusterMetrics(layout, metrics, 4, &count); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - ok(count == 2, "got %u\n", count); + ok(count == 4, "got %u\n", count); ok(metrics[0].isWhitespace == 0, "got %d\n", metrics[0].isWhitespace); - ok(metrics[0].canWrapLineAfter == 0, "got %d\n", metrics[0].canWrapLineAfter); + ok(!metrics[0].canWrapLineAfter, "Unexpected value %d.\n", metrics[0].canWrapLineAfter); ok(metrics[1].isWhitespace == 1, "got %d\n", metrics[1].isWhitespace); - ok(metrics[1].canWrapLineAfter == 1, "got %d\n", metrics[1].canWrapLineAfter); + ok(metrics[1].canWrapLineAfter == 1, "Unexpected value %d.\n", metrics[1].canWrapLineAfter); + ok(metrics[2].isWhitespace == 0, "got %d\n", metrics[0].isWhitespace); + ok(!metrics[2].canWrapLineAfter, "Unexpected value %d.\n", metrics[2].canWrapLineAfter); + ok(metrics[3].isWhitespace == 1, "got %d\n", metrics[1].isWhitespace); + ok(metrics[3].canWrapLineAfter == 1, "Unexpected value %d.\n", metrics[3].canWrapLineAfter); + + range.startPosition = 1; + range.length = 1; + hr = IDWriteTextLayout_SetInlineObject(layout, &testinlineobj3->IDWriteInlineObject_iface, range); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + count = 0; + memset(metrics, 0, sizeof(metrics)); + hr = IDWriteTextLayout_GetClusterMetrics(layout, metrics, 4, &count); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(count == 4, "Unexpected count %u.\n", count); + ok(!metrics[0].isWhitespace, "Unexpected value %d.\n", metrics[0].isWhitespace); + todo_wine + ok(metrics[1].isWhitespace == 1, "Unexpected value %d.\n", metrics[1].isWhitespace); + ok(!metrics[2].isWhitespace, "Unexpected value %d.\n", metrics[2].isWhitespace); + ok(metrics[3].isWhitespace == 1, "Unexpected value %d.\n", metrics[3].isWhitespace); + + range.startPosition = 0; + range.length = 2; + hr = IDWriteTextLayout_SetInlineObject(layout, &testinlineobj3->IDWriteInlineObject_iface, range); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + count = 0; + memset(metrics, 0, sizeof(metrics)); + hr = IDWriteTextLayout_GetClusterMetrics(layout, metrics, 4, &count); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(count == 3, "Unexpected count %u.\n", count); + ok(!metrics[0].isWhitespace, "Unexpected value %d.\n", metrics[0].isWhitespace); + ok(!metrics[0].canWrapLineAfter, "Unexpected value %d.\n", metrics[0].canWrapLineAfter); + ok(metrics[0].length == 2, "Unexpected length %u.\n", metrics[0].length); + ok(!metrics[1].isWhitespace, "Unexpected value %d.\n", metrics[1].isWhitespace); + ok(!metrics[1].canWrapLineAfter, "Unexpected value %d.\n", metrics[1].canWrapLineAfter); + ok(metrics[1].length == 1, "Unexpected length %u.\n", metrics[1].length); + ok(metrics[2].isWhitespace == 1, "Unexpected value %d.\n", metrics[2].isWhitespace); + ok(metrics[2].canWrapLineAfter == 1, "Unexpected value %d.\n", metrics[2].canWrapLineAfter); + ok(metrics[2].length == 1, "Unexpected length %u.\n", metrics[2].length); + IDWriteTextLayout_Release(layout); /* Layout is fully covered by inline object with after condition DWRITE_BREAK_CONDITION_MAY_NOT_BREAK. */ @@ -2771,6 +2812,7 @@ static void test_GetClusterMetrics(void) ok(hr == S_OK, "Failed to create text layout, hr %#lx.\n", hr); range.startPosition = 0; + range.length = 2; hr = IDWriteTextLayout_SetInlineObject(layout, &testinlineobj3->IDWriteInlineObject_iface, range); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/9974
From: Nikolay Sivov <nsivov@codeweavers.com> Signed-off-by: Nikolay Sivov <nsivov@codeweavers.com> --- dlls/dwrite/layout.c | 132 +++++++++++++++++++++---------------- dlls/dwrite/tests/layout.c | 39 ++++++++++- 2 files changed, 113 insertions(+), 58 deletions(-) diff --git a/dlls/dwrite/layout.c b/dlls/dwrite/layout.c index f6684097234..2ada34f2d38 100644 --- a/dlls/dwrite/layout.c +++ b/dlls/dwrite/layout.c @@ -1638,7 +1638,7 @@ static void layout_apply_line_spacing(struct dwrite_textlayout *layout, UINT32 l } } -static HRESULT layout_set_line_metrics(struct dwrite_textlayout *layout, DWRITE_LINE_METRICS1 *metrics) +static HRESULT layout_set_line_metrics(struct dwrite_textlayout *layout, const DWRITE_LINE_METRICS1 *metrics) { size_t i = layout->metrics.lineCount; @@ -2044,7 +2044,7 @@ static HRESULT layout_set_dummy_line_metrics(struct dwrite_textlayout *layout) return layout_set_line_metrics(layout, &metrics); } -static void layout_add_line(struct dwrite_textlayout *layout, UINT32 first_cluster, UINT32 last_cluster, +static HRESULT layout_add_line(struct dwrite_textlayout *layout, UINT32 first_cluster, UINT32 last_cluster, UINT32 *textpos) { BOOL is_rtl = layout->format.readingdir == DWRITE_READING_DIRECTION_RIGHT_TO_LEFT; @@ -2139,7 +2139,7 @@ static void layout_add_line(struct dwrite_textlayout *layout, UINT32 first_clust if (run != layout->clusters[i].run || !is_same_splitting_params(&prev_params, ¶ms)) { hr = layout_add_effective_run(layout, run, start, i - start, line, origin_x, &prev_params); if (FAILED(hr)) - return; + return hr; origin_x += is_rtl ? -get_cluster_range_width(layout, start, i) : get_cluster_range_width(layout, start, i); @@ -2152,9 +2152,8 @@ static void layout_add_line(struct dwrite_textlayout *layout, UINT32 first_clust } /* Final run from what's left from cluster range */ - hr = layout_add_effective_run(layout, run, start, i - start, line, origin_x, &prev_params); - if (FAILED(hr)) - return; + if (FAILED(hr = layout_add_effective_run(layout, run, start, i - start, line, origin_x, &prev_params))) + return hr; if (get_cluster_range_width(layout, start, i) + sign_metrics.width > layout->metrics.layoutWidth) append_trimming_run = FALSE; @@ -2164,7 +2163,7 @@ static void layout_add_line(struct dwrite_textlayout *layout, UINT32 first_clust struct layout_effective_run *trimming_sign; if (!(trimming_sign = calloc(1, sizeof(*trimming_sign)))) - return; + return E_OUTOFMEMORY; trimming_sign->object = layout->format.trimmingsign; trimming_sign->width = sign_metrics.width; @@ -2202,9 +2201,10 @@ static void layout_add_line(struct dwrite_textlayout *layout, UINT32 first_clust metrics.height = descent + metrics.baseline; metrics.isTrimmed = append_trimming_run || width > layout->metrics.layoutWidth; - layout_set_line_metrics(layout, &metrics); *textpos += metrics.length; + + return layout_set_line_metrics(layout, &metrics); } static void layout_set_line_positions(struct dwrite_textlayout *layout) @@ -2236,22 +2236,82 @@ static void layout_set_line_positions(struct dwrite_textlayout *layout) layout_apply_par_alignment(layout); } -static BOOL layout_can_wrap_after(const struct dwrite_textlayout *layout, UINT32 cluster) +static bool layout_can_wrap_after(const struct dwrite_textlayout *layout, UINT32 cluster) { if (layout->format.wrapping == DWRITE_WORD_WRAPPING_CHARACTER) - return TRUE; + return true; return layout->clustermetrics[cluster].canWrapLineAfter; } +static HRESULT layout_compute_lines(struct dwrite_textlayout *layout) +{ + bool wraps = layout->format.wrapping != DWRITE_WORD_WRAPPING_NO_WRAP; + UINT32 remaining_clusters = layout->cluster_count; + UINT32 start_cluster, end_cluster, break_cluster; + UINT32 text_position = 0; + float width, max_width; + HRESULT hr = S_OK; + + start_cluster = 0; + break_cluster = layout->cluster_count; + max_width = layout->metrics.layoutWidth; + while (remaining_clusters) + { + width = 0.0f; + for (end_cluster = start_cluster; end_cluster < layout->cluster_count; ++end_cluster) + { + if (layout->clustermetrics[end_cluster].isNewline) break; + width += layout->clustermetrics[end_cluster].width; + if (wraps && width > max_width) break; + + if (layout_can_wrap_after(layout, end_cluster)) + break_cluster = end_cluster; + } + end_cluster = min(end_cluster, layout->cluster_count - 1); + + /* Adjust end cluster for wrapping. */ + if (wraps && width > max_width) + { + /* Use the most recent breaking cluster, or look forward for the next breaking point. */ + if (!(layout->clustermetrics[end_cluster].isWhitespace && layout_can_wrap_after(layout, end_cluster))) + { + if (break_cluster < layout->cluster_count) + { + end_cluster = break_cluster; + break_cluster = layout->cluster_count; + } + else + { + for (; end_cluster < layout->cluster_count; ++end_cluster) + { + if (layout_can_wrap_after(layout, end_cluster) + || layout->clustermetrics[end_cluster].isNewline) + { + break; + } + } + end_cluster = min(end_cluster, layout->cluster_count - 1); + } + } + } + + if (FAILED(hr = layout_add_line(layout, start_cluster, end_cluster, &text_position))) + break; + + remaining_clusters -= end_cluster - start_cluster + 1; + + start_cluster = end_cluster + 1; + } + + return hr; +} + 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; - UINT32 i, start, textpos, last_breaking_point; DWRITE_LINE_METRICS1 metrics; - FLOAT width; - UINT32 line; HRESULT hr; if (!(layout->recompute & RECOMPUTE_LINES)) @@ -2259,8 +2319,7 @@ static HRESULT layout_compute_effective_runs(struct dwrite_textlayout *layout) free_layout_effective_runs(layout); - hr = layout_compute(layout); - if (FAILED(hr)) + if (FAILED(hr = layout_compute(layout))) return hr; layout->metrics.lineCount = 0; @@ -2270,45 +2329,8 @@ static HRESULT layout_compute_effective_runs(struct dwrite_textlayout *layout) layout->metrics.width = 0.0f; layout->metrics.widthIncludingTrailingWhitespace = 0.0f; - last_breaking_point = ~0u; - - for (i = 0, start = 0, width = 0.0f, textpos = 0; i < layout->cluster_count; i++) { - BOOL overflow = FALSE; - - while (i < layout->cluster_count && !layout->clustermetrics[i].isNewline) { - /* Check for overflow */ - overflow = ((width + layout->clustermetrics[i].width > layout->metrics.layoutWidth) && - (layout->format.wrapping != DWRITE_WORD_WRAPPING_NO_WRAP)); - if (overflow) - break; - - if (layout_can_wrap_after(layout, i)) - last_breaking_point = i; - width += layout->clustermetrics[i].width; - i++; - } - i = min(i, layout->cluster_count - 1); - - /* Ignore if overflown on whitespace */ - if (overflow && !(layout->clustermetrics[i].isWhitespace && layout_can_wrap_after(layout, i))) { - /* Use most recently found breaking point */ - if (last_breaking_point != ~0u) { - i = last_breaking_point; - last_breaking_point = ~0u; - } - else { - /* Otherwise proceed forward to next newline or breaking point */ - for (; i < layout->cluster_count; i++) - if (layout_can_wrap_after(layout, i) || layout->clustermetrics[i].isNewline) - break; - } - } - i = min(i, layout->cluster_count - 1); - - layout_add_line(layout, start, i, &textpos); - start = i + 1; - width = 0.0f; - } + if (FAILED(hr = layout_compute_lines(layout))) + return hr; if (FAILED(hr = layout_set_dummy_line_metrics(layout))) return hr; @@ -2320,7 +2342,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 (line = 0; line < layout->metrics.lineCount; line++) + for (unsigned int line = 0; line < layout->metrics.lineCount; line++) { while (run && run->line == line) { diff --git a/dlls/dwrite/tests/layout.c b/dlls/dwrite/tests/layout.c index 17a60692cf9..6b6b567c37b 100644 --- a/dlls/dwrite/tests/layout.c +++ b/dlls/dwrite/tests/layout.c @@ -2467,9 +2467,15 @@ static void test_GetClusterMetrics(void) hr = IDWriteTextLayout_GetClusterMetrics(layout, metrics, ARRAY_SIZE(metrics), &count); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); ok(count == 4, "got %u\n", count); - for (i = 0; i < count; i++) { - ok(metrics[i].width > 0.0, "%u: got width %.2f\n", i, metrics[i].width); - ok(metrics[i].length == 1, "%u: got length %u\n", i, metrics[i].length); + for (i = 0; i < count; i++) + { + winetest_push_context("Test %#x", i); + + ok(metrics[i].width > 0.0, "Unexpected width %.8e.\n", metrics[i].width); + ok(metrics[i].length == 1, "Unexpected length %u.\n", metrics[i].length); + ok(!metrics[i].padding, "Unexpected padding %#x.\n", metrics[i].padding); + + winetest_pop_context(); } /* apply spacing and check widths again */ @@ -2546,6 +2552,7 @@ static void test_GetClusterMetrics(void) ok(hr == E_NOT_SUFFICIENT_BUFFER, "Unexpected hr %#lx.\n", hr); ok(count == 3, "got %u\n", count); ok(metrics[0].length == 2, "got %u\n", metrics[0].length); + ok(!metrics[0].padding, "Unexpected value %#x.\n", metrics[0].padding); hr = IDWriteInlineObject_GetMetrics(trimm, &inline_metrics); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); @@ -2950,6 +2957,7 @@ static void test_GetClusterMetrics(void) hr = IDWriteTextLayout_GetClusterMetrics(layout, metrics, 4, &count); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); ok(count == 4, "got %u\n", count); + ok(!metrics[3].padding, "Unexpected padding %#x.\n", metrics[3].padding); hr = IDWriteTextLayout_GetLineMetrics(layout, &line, 1, &count); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); @@ -3016,6 +3024,18 @@ static void test_GetClusterMetrics(void) IDWriteTextLayout_Release(layout); + /* No text */ + hr = IDWriteFactory_CreateTextLayout(factory, L"", 0, format, 1000.0f, 200.0f, &layout); + ok(hr == S_OK, "Failed to create text layout, hr %#lx.\n", hr); + + count = 1; + memset(metrics, 0, sizeof(metrics)); + hr = IDWriteTextLayout_GetClusterMetrics(layout, metrics, ARRAYSIZE(metrics), &count); + ok(hr == S_OK, "Failed to get cluster metrics, hr %#lx.\n", hr); + ok(!count, "Unexpected cluster count %u.\n", count); + + IDWriteTextLayout_Release(layout); + IDWriteInlineObject_Release(&testinlineobj->IDWriteInlineObject_iface); IDWriteInlineObject_Release(&testinlineobj2->IDWriteInlineObject_iface); IDWriteInlineObject_Release(&testinlineobj3->IDWriteInlineObject_iface); @@ -4428,6 +4448,19 @@ static void test_GetLineMetrics(void) else win_skip("Proportional spacing is not supported.\n"); + /* No text */ + hr = IDWriteFactory_CreateTextLayout(factory, L"", 0, format, 1000.0f, 200.0f, &layout); + ok(hr == S_OK, "Failed to create text layout, hr %#lx.\n", hr); + + count = 0; + memset(metrics, 0, sizeof(metrics)); + hr = IDWriteTextLayout_GetLineMetrics(layout, metrics, ARRAYSIZE(metrics), &count); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(count == 1, "Unexpected line count %u.\n", count); + ok(metrics[0].height > 0.0f, "Unexpected line height %.8e.\n", metrics[0].height); + + IDWriteTextLayout_Release(layout); + IDWriteTextFormat_Release(format); IDWriteFontFace_Release(fontface); IDWriteFactory_Release(factory); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/9974
From: Nikolay Sivov <nsivov@codeweavers.com> Signed-off-by: Nikolay Sivov <nsivov@codeweavers.com> --- dlls/dwrite/tests/layout.c | 116 ++++++++++++++++++++++++++++++++++++- 1 file changed, 115 insertions(+), 1 deletion(-) diff --git a/dlls/dwrite/tests/layout.c b/dlls/dwrite/tests/layout.c index 6b6b567c37b..e3a15944ccb 100644 --- a/dlls/dwrite/tests/layout.c +++ b/dlls/dwrite/tests/layout.c @@ -3317,10 +3317,13 @@ static void test_DetermineMinWidth(void) { {'a','\n',' ',' ','b',0}, {'b',0} }, { {'a','b','c','\n',' ',' ','b',0}, {'a','b','c',0} }, }; + DWRITE_INLINE_OBJECT_METRICS inline_metrics; DWRITE_CLUSTER_METRICS metrics[10]; + IDWriteInlineObject *sign; IDWriteTextFormat *format; IDWriteTextLayout *layout; IDWriteFactory *factory; + DWRITE_TEXT_RANGE range; UINT32 count, i, j; FLOAT minwidth; HRESULT hr; @@ -3351,6 +3354,8 @@ static void test_DetermineMinWidth(void) for (i = 0; i < ARRAY_SIZE(minwidth_tests); i++) { FLOAT width = 0.0f; + winetest_push_context("Test %u", i); + /* measure expected width */ hr = IDWriteFactory_CreateTextLayout(factory, minwidth_tests[i].mintext, lstrlenW(minwidth_tests[i].mintext), format, 1000.0f, 1000.0f, &layout); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); @@ -3369,11 +3374,65 @@ static void test_DetermineMinWidth(void) minwidth = 0.0f; hr = IDWriteTextLayout_DetermineMinWidth(layout, &minwidth); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - ok(minwidth == width, "test %u: expected width %f, got %f\n", i, width, minwidth); + ok(minwidth == width, "Unexpected width %.8e, got %.8e.\n", width, minwidth); IDWriteTextLayout_Release(layout); + + winetest_pop_context(); } + /* Trailing white space, replaced with by an inline object. */ + hr = IDWriteFactory_CreateTextLayout(factory, L"a ", 2, format, 1000.0f, 1000.0f, &layout); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IDWriteFactory_CreateEllipsisTrimmingSign(factory, format, &sign); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IDWriteInlineObject_GetMetrics(sign, &inline_metrics); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(inline_metrics.width > 0.0f, "Unexpected width %.8e.\n", inline_metrics.width); + + range.startPosition = 1; + range.length = 1; + hr = IDWriteTextLayout_SetInlineObject(layout, sign, range); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + count = 0; + hr = IDWriteTextLayout_GetClusterMetrics(layout, metrics, ARRAYSIZE(metrics), &count); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(count == 2, "Unexpected count %u.\n", count); + todo_wine + ok(metrics[1].isWhitespace, "Unexpected value %d.\n", metrics[1].isWhitespace); + + hr = IDWriteTextLayout_SetWordWrapping(layout, DWRITE_WORD_WRAPPING_NO_WRAP); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + minwidth = 0.0f; + hr = IDWriteTextLayout_DetermineMinWidth(layout, &minwidth); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + todo_wine + ok(minwidth == metrics[0].width, "Unexpected width %.8e.\n", minwidth); + + IDWriteTextLayout_Release(layout); + + hr = IDWriteFactory_CreateTextLayout(factory, L" ", 1, format, 1000.0f, 1000.0f, &layout); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + range.startPosition = 0; + range.length = 1; + hr = IDWriteTextLayout_SetInlineObject(layout, sign, range); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + minwidth = 0.0f; + hr = IDWriteTextLayout_DetermineMinWidth(layout, &minwidth); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + todo_wine + ok(minwidth == 0.0f, "Unexpected width %.8e.\n", minwidth); + + IDWriteTextLayout_Release(layout); + + IDWriteInlineObject_Release(sign); + IDWriteTextFormat_Release(format); IDWriteFactory_Release(factory); } @@ -4127,7 +4186,10 @@ static void test_GetLineMetrics(void) static const WCHAR strW[] = {'a','b','c','d',' ',0}; static const WCHAR str2W[] = {'a','b','\r','c','d',0}; static const WCHAR str4W[] = {'a','\r',0}; + struct test_inline_obj *inline_object; IDWriteFontCollection *syscollection; + DWRITE_CLUSTER_METRICS clusters[16]; + DWRITE_TEXT_METRICS text_metrics; DWRITE_FONT_METRICS fontmetrics; DWRITE_LINE_METRICS metrics[6]; UINT32 count, i, familycount; @@ -4461,6 +4523,58 @@ static void test_GetLineMetrics(void) IDWriteTextLayout_Release(layout); + /* Trailing spaces, replaced by an inline object. */ + hr = IDWriteFactory_CreateTextLayout(factory, L"a ", 2, format, 1000.0f, 200.0f, &layout); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + count = 0; + hr = IDWriteTextLayout_GetClusterMetrics(layout, clusters, ARRAYSIZE(clusters), &count); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(count == 2, "Unexpected count %u.\n", count); + ok(!clusters[0].isWhitespace, "Unexpected value %d.\n", clusters[0].isWhitespace); + ok(clusters[1].isWhitespace, "Unexpected value %d.\n", clusters[1].isWhitespace); + + inline_object = create_test_inline_object(&testinlineobjvtbl); + + count = 0; + memset(metrics, 0, sizeof(metrics)); + hr = IDWriteTextLayout_GetLineMetrics(layout, metrics, ARRAYSIZE(metrics), &count); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(count == 1, "Unexpected line count %u.\n", count); + ok(metrics[0].trailingWhitespaceLength == 1, "Unexpected length %u.\n", metrics[0].trailingWhitespaceLength); + ok(metrics[0].length == 2, "Unexpected length %u.\n", metrics[0].length); + + range.startPosition = 1; + range.length = 1; + hr = IDWriteTextLayout_SetInlineObject(layout, &inline_object->IDWriteInlineObject_iface, range); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + count = 0; + hr = IDWriteTextLayout_GetClusterMetrics(layout, clusters, ARRAYSIZE(clusters), &count); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(count == 2, "Unexpected count %u.\n", count); + ok(!clusters[0].isWhitespace, "Unexpected value %d.\n", clusters[0].isWhitespace); + todo_wine + ok(clusters[1].isWhitespace, "Unexpected value %d.\n", clusters[1].isWhitespace); + + count = 0; + memset(metrics, 0, sizeof(metrics)); + hr = IDWriteTextLayout_GetLineMetrics(layout, metrics, ARRAYSIZE(metrics), &count); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(count == 1, "Unexpected line count %u.\n", count); + todo_wine + ok(metrics[0].trailingWhitespaceLength == 1, "Unexpected length %u.\n", metrics[0].trailingWhitespaceLength); + ok(metrics[0].length == 2, "Unexpected length %u.\n", metrics[0].length); + + hr = IDWriteTextLayout_GetMetrics(layout, &text_metrics); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(text_metrics.width == clusters[0].width, "Unexpected width %.8e.\n", text_metrics.width); + ok(text_metrics.widthIncludingTrailingWhitespace == (clusters[0].width + clusters[1].width), + "Unexpected width %.8e.\n", text_metrics.widthIncludingTrailingWhitespace); + + IDWriteInlineObject_Release(&inline_object->IDWriteInlineObject_iface); + IDWriteTextLayout_Release(layout); + IDWriteTextFormat_Release(format); IDWriteFontFace_Release(fontface); IDWriteFactory_Release(factory); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/9974
From: Nikolay Sivov <nsivov@codeweavers.com> Signed-off-by: Nikolay Sivov <nsivov@codeweavers.com> --- dlls/dwrite/layout.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dlls/dwrite/layout.c b/dlls/dwrite/layout.c index 2ada34f2d38..753c7bdab18 100644 --- a/dlls/dwrite/layout.c +++ b/dlls/dwrite/layout.c @@ -2072,7 +2072,7 @@ static HRESULT layout_add_line(struct dwrite_textlayout *layout, UINT32 first_cl /* Every isNewline cluster is also isWhitespace, but not every newline character cluster has isNewline set, so go back to original string. */ - ch = lc->run->u.regular.descr.string[lc->position]; + ch = layout->text[lc->run->start_position + lc->position]; if (cluster->length == 1 && lb_is_newline_char(ch)) metrics.newlineLength += cluster->length; -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/9974
From: Nikolay Sivov <nsivov@codeweavers.com> Signed-off-by: Nikolay Sivov <nsivov@codeweavers.com> --- dlls/dwrite/layout.c | 14 +++++--------- dlls/dwrite/tests/layout.c | 6 ------ 2 files changed, 5 insertions(+), 15 deletions(-) diff --git a/dlls/dwrite/layout.c b/dlls/dwrite/layout.c index 753c7bdab18..9cc0394bffb 100644 --- a/dlls/dwrite/layout.c +++ b/dlls/dwrite/layout.c @@ -432,11 +432,8 @@ static HRESULT layout_update_breakpoints_range(struct dwrite_textlayout *layout, UINT32 start, UINT32 length) { DWRITE_BREAK_CONDITION before, after; - HRESULT hr; - /* ignore returned conditions if failed */ - hr = IDWriteInlineObject_GetBreakConditions(object, &before, &after); - if (FAILED(hr)) + if (FAILED(IDWriteInlineObject_GetBreakConditions(object, &before, &after))) after = before = DWRITE_BREAK_CONDITION_NEUTRAL; if (!layout->actual_breakpoints) @@ -474,7 +471,6 @@ static HRESULT layout_update_breakpoints_range(struct dwrite_textlayout *layout, layout->actual_breakpoints[i].breakConditionAfter = DWRITE_BREAK_CONDITION_MAY_NOT_BREAK; } - layout->actual_breakpoints[i].isWhitespace = 0; layout->actual_breakpoints[i].isSoftHyphen = 0; } @@ -1340,7 +1336,9 @@ static HRESULT layout_compute_runs(struct dwrite_textlayout *layout) DWRITE_FONT_METRICS fontmetrics = { 0 }; /* we need to do very little in case of inline objects */ - if (r->kind == LAYOUT_RUN_INLINE) { + if (r->kind == LAYOUT_RUN_INLINE) + { + DWRITE_LINE_BREAKPOINT bp = get_effective_breakpoint(layout, r->start_position); DWRITE_CLUSTER_METRICS *metrics = &layout->clustermetrics[cluster]; struct layout_cluster *c = &layout->clusters[cluster]; DWRITE_INLINE_OBJECT_METRICS inlinemetrics; @@ -1348,7 +1346,7 @@ static HRESULT layout_compute_runs(struct dwrite_textlayout *layout) metrics->width = 0.0f; metrics->length = r->u.object.length; metrics->canWrapLineAfter = 0; - metrics->isWhitespace = 0; + metrics->isWhitespace = bp.isWhitespace; metrics->isNewline = 0; metrics->isSoftHyphen = 0; metrics->isRightToLeft = r->u.object.bidi_level & 1; @@ -2065,8 +2063,6 @@ static HRESULT layout_add_line(struct dwrite_textlayout *layout, UINT32 first_cl struct layout_cluster *lc = &layout->clusters[index]; WCHAR ch; - /* This also filters out clusters added from inline objects, those are never - treated as a white space. */ if (!cluster->isWhitespace) break; diff --git a/dlls/dwrite/tests/layout.c b/dlls/dwrite/tests/layout.c index e3a15944ccb..9c5630b4ac6 100644 --- a/dlls/dwrite/tests/layout.c +++ b/dlls/dwrite/tests/layout.c @@ -2787,7 +2787,6 @@ static void test_GetClusterMetrics(void) ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); ok(count == 4, "Unexpected count %u.\n", count); ok(!metrics[0].isWhitespace, "Unexpected value %d.\n", metrics[0].isWhitespace); - todo_wine ok(metrics[1].isWhitespace == 1, "Unexpected value %d.\n", metrics[1].isWhitespace); ok(!metrics[2].isWhitespace, "Unexpected value %d.\n", metrics[2].isWhitespace); ok(metrics[3].isWhitespace == 1, "Unexpected value %d.\n", metrics[3].isWhitespace); @@ -3401,7 +3400,6 @@ static void test_DetermineMinWidth(void) hr = IDWriteTextLayout_GetClusterMetrics(layout, metrics, ARRAYSIZE(metrics), &count); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); ok(count == 2, "Unexpected count %u.\n", count); - todo_wine ok(metrics[1].isWhitespace, "Unexpected value %d.\n", metrics[1].isWhitespace); hr = IDWriteTextLayout_SetWordWrapping(layout, DWRITE_WORD_WRAPPING_NO_WRAP); @@ -3410,7 +3408,6 @@ static void test_DetermineMinWidth(void) minwidth = 0.0f; hr = IDWriteTextLayout_DetermineMinWidth(layout, &minwidth); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - todo_wine ok(minwidth == metrics[0].width, "Unexpected width %.8e.\n", minwidth); IDWriteTextLayout_Release(layout); @@ -3426,7 +3423,6 @@ static void test_DetermineMinWidth(void) minwidth = 0.0f; hr = IDWriteTextLayout_DetermineMinWidth(layout, &minwidth); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - todo_wine ok(minwidth == 0.0f, "Unexpected width %.8e.\n", minwidth); IDWriteTextLayout_Release(layout); @@ -4554,7 +4550,6 @@ static void test_GetLineMetrics(void) ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); ok(count == 2, "Unexpected count %u.\n", count); ok(!clusters[0].isWhitespace, "Unexpected value %d.\n", clusters[0].isWhitespace); - todo_wine ok(clusters[1].isWhitespace, "Unexpected value %d.\n", clusters[1].isWhitespace); count = 0; @@ -4562,7 +4557,6 @@ static void test_GetLineMetrics(void) hr = IDWriteTextLayout_GetLineMetrics(layout, metrics, ARRAYSIZE(metrics), &count); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); ok(count == 1, "Unexpected line count %u.\n", count); - todo_wine ok(metrics[0].trailingWhitespaceLength == 1, "Unexpected length %u.\n", metrics[0].trailingWhitespaceLength); ok(metrics[0].length == 2, "Unexpected length %u.\n", metrics[0].length); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/9974
From: Nikolay Sivov <nsivov@codeweavers.com> Signed-off-by: Nikolay Sivov <nsivov@codeweavers.com> --- dlls/dwrite/tests/layout.c | 48 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/dlls/dwrite/tests/layout.c b/dlls/dwrite/tests/layout.c index 9c5630b4ac6..7a5c0d26952 100644 --- a/dlls/dwrite/tests/layout.c +++ b/dlls/dwrite/tests/layout.c @@ -7370,6 +7370,7 @@ if (SUCCEEDED(hr)) static void test_HitTestTextPosition(void) { + DWRITE_CLUSTER_METRICS clusters[10]; IDWriteInlineObject *trimming_sign; DWRITE_TEXT_METRICS layout_metrics; DWRITE_HIT_TEST_METRICS metrics; @@ -7379,6 +7380,7 @@ static void test_HitTestTextPosition(void) DWRITE_TEXT_RANGE range; IDWriteFactory *factory; float posx, posy; + UINT32 count; HRESULT hr; factory = create_factory(); @@ -7654,6 +7656,52 @@ if (hr == S_OK) ok(metrics.isText, "Unexpected isText %d.\n", metrics.isText); ok(!!metrics.isTrimmed, "Unexpected isTrimmed %d.\n", metrics.isTrimmed); } + /* Width can't fit a single cluster. */ + hr = IDWriteTextLayout_GetClusterMetrics(layout, clusters, ARRAYSIZE(clusters), &count); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(count == 6, "Unexpected count %u.\n", count); + + hr = IDWriteTextLayout_SetMaxWidth(layout, clusters[0].width / 2.0f); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IDWriteTextLayout_SetReadingDirection(layout, DWRITE_READING_DIRECTION_LEFT_TO_RIGHT); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IDWriteTextLayout_HitTestTextPosition(layout, 1, FALSE, &posx, &posy, &metrics); + todo_wine + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); +if (hr == S_OK) +{ + ok(posx > 0.0f && posy == 0.0f, "Unexpected position {%.8e,%.8e}.\n", posx, posy); + ok(metrics.textPosition == 1, "Unexpected text position %u.\n", metrics.textPosition); + ok(metrics.length == 5, "Unexpected length %u.\n", metrics.length); + ok(metrics.left != 0.0f, "Unexpected left %.8e.\n", metrics.left); + ok(metrics.top == 0.0f, "Unexpected top %.8e.\n", metrics.top); + ok(metrics.width > 0.0f, "Unexpected width %.8e.\n", metrics.width); + ok(metrics.height > 0.0f, "Unexpected height %.8e.\n", metrics.height); + ok(!metrics.bidiLevel, "Unexpected bidi level %u.\n", metrics.bidiLevel); + ok(metrics.isText, "Unexpected isText %d.\n", metrics.isText); + ok(metrics.isTrimmed, "Unexpected isTrimmed %d.\n", metrics.isTrimmed); +} + hr = IDWriteTextLayout_HitTestTextPosition(layout, 0, FALSE, &posx, &posy, &metrics); + todo_wine + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); +if (hr == S_OK) +{ + ok(posx == metrics.left && posy == 0.0f, "Unexpected position {%.8e,%.8e}.\n", posx, posy); + ok(metrics.textPosition == 0, "Unexpected text position %u.\n", metrics.textPosition); + ok(metrics.length == 1, "Unexpected length %u.\n", metrics.length); + ok(metrics.left == 0.0f, "Unexpected left %.8e.\n", metrics.left); + ok(metrics.top == 0.0f, "Unexpected top %.8e.\n", metrics.top); + ok(metrics.width > 0.0f, "Unexpected width %.8e.\n", metrics.width); + ok(metrics.height > 0.0f, "Unexpected height %.8e.\n", metrics.height); + ok(!metrics.bidiLevel, "Unexpected bidi level %u.\n", metrics.bidiLevel); + ok(metrics.isText, "Unexpected isText %d.\n", metrics.isText); + ok(!metrics.isTrimmed, "Unexpected isTrimmed %d.\n", metrics.isTrimmed); +} + + hr = IDWriteTextLayout_SetReadingDirection(layout, DWRITE_READING_DIRECTION_RIGHT_TO_LEFT); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); hr = IDWriteTextLayout_SetMaxWidth(layout, 100.0f); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/9974
From: Nikolay Sivov <nsivov@codeweavers.com> Signed-off-by: Nikolay Sivov <nsivov@codeweavers.com> --- dlls/dwrite/layout.c | 130 ++++++++++++++++++++++++------------------- 1 file changed, 72 insertions(+), 58 deletions(-) diff --git a/dlls/dwrite/layout.c b/dlls/dwrite/layout.c index 9cc0394bffb..2cdf41369bb 100644 --- a/dlls/dwrite/layout.c +++ b/dlls/dwrite/layout.c @@ -2042,25 +2042,28 @@ static HRESULT layout_set_dummy_line_metrics(struct dwrite_textlayout *layout) return layout_set_line_metrics(layout, &metrics); } -static HRESULT layout_add_line(struct dwrite_textlayout *layout, UINT32 first_cluster, UINT32 last_cluster, - UINT32 *textpos) +static HRESULT layout_add_line(struct dwrite_textlayout *layout, UINT32 first_cluster, + UINT32 cluster_count, UINT32 *textpos) { BOOL is_rtl = layout->format.readingdir == DWRITE_READING_DIRECTION_RIGHT_TO_LEFT; struct layout_final_splitting_params params, prev_params; DWRITE_INLINE_OBJECT_METRICS sign_metrics = { 0 }; - UINT32 line = layout->metrics.lineCount, i; + UINT32 count, start, end, pos = *textpos; + UINT32 line = layout->metrics.lineCount; DWRITE_LINE_METRICS1 metrics = { 0 }; - UINT32 index, start, pos = *textpos; FLOAT descent, trailingspacewidth; - BOOL append_trimming_run = FALSE; + bool append_trimming_run = false; const struct layout_run *run; float width = 0.0f, origin_x; HRESULT hr; - /* Take a look at clusters we got for this line in reverse order to set trailing properties for current line */ - for (index = last_cluster, trailingspacewidth = 0.0f; index >= first_cluster; index--) { - DWRITE_CLUSTER_METRICS *cluster = &layout->clustermetrics[index]; - struct layout_cluster *lc = &layout->clusters[index]; + /* Set trailing properties for current line */ + trailingspacewidth = 0.0f; + count = cluster_count; + while (count--) + { + DWRITE_CLUSTER_METRICS *cluster = &layout->clustermetrics[first_cluster + count]; + struct layout_cluster *lc = &layout->clusters[first_cluster + count]; WCHAR ch; if (!cluster->isWhitespace) @@ -2074,48 +2077,54 @@ static HRESULT layout_add_line(struct dwrite_textlayout *layout, UINT32 first_cl metrics.trailingWhitespaceLength += cluster->length; trailingspacewidth += cluster->width; - - if (index == 0) - break; } - /* Line metrics length includes trailing whitespace length too */ - for (i = first_cluster; i <= last_cluster; i++) + /* Line length includes every cluster. */ + for (UINT32 i = first_cluster; i < first_cluster + cluster_count; ++i) metrics.length += layout->clustermetrics[i].length; /* Ignore trailing whitespaces */ - while (last_cluster > first_cluster) { - if (!layout->clustermetrics[last_cluster].isWhitespace) - break; - - last_cluster--; - } + while (cluster_count && layout->clustermetrics[first_cluster + cluster_count - 1].isWhitespace) + --cluster_count; /* Does not include trailing space width */ - if (!layout->clustermetrics[last_cluster].isWhitespace) - width = get_cluster_range_width(layout, first_cluster, last_cluster + 1); + width = get_cluster_range_width(layout, first_cluster, first_cluster + cluster_count); /* Append trimming run if necessary */ - if (width > layout->metrics.layoutWidth && layout->format.trimmingsign != NULL && - layout->format.trimming.granularity != DWRITE_TRIMMING_GRANULARITY_NONE) { + + if (width > layout->metrics.layoutWidth + && cluster_count > 1 + && layout->format.trimmingsign != NULL + && layout->format.trimming.granularity != DWRITE_TRIMMING_GRANULARITY_NONE) + { FLOAT trimmed_width = width; hr = IDWriteInlineObject_GetMetrics(layout->format.trimmingsign, &sign_metrics); - if (SUCCEEDED(hr)) { - while (last_cluster > first_cluster) { + if (SUCCEEDED(hr)) + { + while (cluster_count) + { if (trimmed_width + sign_metrics.width <= layout->metrics.layoutWidth) break; + if (layout->format.trimming.granularity == DWRITE_TRIMMING_GRANULARITY_CHARACTER) - trimmed_width -= layout->clustermetrics[last_cluster--].width; - else { - while (last_cluster > first_cluster) { - trimmed_width -= layout->clustermetrics[last_cluster].width; - if (layout->clustermetrics[last_cluster--].canWrapLineAfter) + { + --cluster_count; + trimmed_width -= layout->clustermetrics[first_cluster + cluster_count].width; + } + else /* DWRITE_TRIMMING_GRANULARITY_WORD */ + { + while (cluster_count) + { + --cluster_count; + trimmed_width -= layout->clustermetrics[first_cluster + cluster_count].width; + if (layout->clustermetrics[first_cluster + cluster_count].canWrapLineAfter) break; } } } - append_trimming_run = TRUE; + + append_trimming_run = true; } else WARN("Failed to get trimming sign metrics, lines won't be trimmed, hr %#lx.\n", hr); @@ -2129,30 +2138,32 @@ 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() */ origin_x = is_rtl ? layout->metrics.layoutWidth : 0.0f; - for (start = first_cluster, i = first_cluster; i <= last_cluster; i++) { + 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[i].run || !is_same_splitting_params(&prev_params, ¶ms)) { - hr = layout_add_effective_run(layout, run, start, i - start, line, origin_x, &prev_params); + 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); if (FAILED(hr)) return hr; - origin_x += is_rtl ? -get_cluster_range_width(layout, start, i) : - get_cluster_range_width(layout, start, i); - run = layout->clusters[i].run; - start = i; + 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; } prev_params = params; - pos += layout->clustermetrics[i].length; + pos += layout->clustermetrics[end].length; } /* Final run from what's left from cluster range */ - if (FAILED(hr = layout_add_effective_run(layout, run, start, i - start, line, origin_x, &prev_params))) - return hr; - - if (get_cluster_range_width(layout, start, i) + sign_metrics.width > layout->metrics.layoutWidth) - append_trimming_run = FALSE; + if (end - start) + { + if (FAILED(hr = layout_add_effective_run(layout, run, start, end - start, line, origin_x, &prev_params))) + return hr; + } if (append_trimming_run) { @@ -2163,7 +2174,7 @@ static HRESULT layout_add_line(struct dwrite_textlayout *layout, UINT32 first_cl trimming_sign->object = layout->format.trimmingsign; trimming_sign->width = sign_metrics.width; - origin_x += is_rtl ? -get_cluster_range_width(layout, start, i) : get_cluster_range_width(layout, start, i); + 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->origin.y = 0.0f; /* set after line is built */ trimming_sign->align_dx = 0.0f; @@ -2173,22 +2184,24 @@ static HRESULT layout_add_line(struct dwrite_textlayout *layout, UINT32 first_cl trimming_sign->is_rtl = false; trimming_sign->line = line; - trimming_sign->effect = layout_get_effect_from_pos(layout, layout->clusters[i].position + - layout->clusters[i].run->start_position); + trimming_sign->effect = layout_get_effect_from_pos(layout, layout->clusters[end].position + + layout->clusters[end].run->start_position); list_add_tail(&layout->inlineobjects, &trimming_sign->draw_entry); list_add_tail(&layout->effective_runs, &trimming_sign->entry); } /* Look for max baseline and descent for this line */ - for (index = first_cluster, metrics.baseline = 0.0f, descent = 0.0f; index <= last_cluster; index++) { - const struct layout_run *cur = layout->clusters[index].run; - FLOAT cur_descent = cur->height - cur->baseline; + run = layout->clusters[first_cluster].run; + metrics.baseline = run->baseline; + descent = run->height - run->baseline; + + for (UINT32 cluster = first_cluster + 1; cluster < first_cluster + cluster_count; ++cluster) + { + run = layout->clusters[cluster].run; - if (cur->baseline > metrics.baseline) - metrics.baseline = cur->baseline; - if (cur_descent > descent) - descent = cur_descent; + metrics.baseline = max(metrics.baseline, run->baseline); + descent = max(descent, run->height - run->baseline); } layout->metrics.width = max(width, layout->metrics.width); @@ -2243,7 +2256,7 @@ static bool layout_can_wrap_after(const struct dwrite_textlayout *layout, UINT32 static HRESULT layout_compute_lines(struct dwrite_textlayout *layout) { bool wraps = layout->format.wrapping != DWRITE_WORD_WRAPPING_NO_WRAP; - UINT32 remaining_clusters = layout->cluster_count; + UINT32 remaining_clusters = layout->cluster_count, cluster_count; UINT32 start_cluster, end_cluster, break_cluster; UINT32 text_position = 0; float width, max_width; @@ -2292,10 +2305,11 @@ static HRESULT layout_compute_lines(struct dwrite_textlayout *layout) } } - if (FAILED(hr = layout_add_line(layout, start_cluster, end_cluster, &text_position))) + cluster_count = end_cluster - start_cluster + 1; + if (FAILED(hr = layout_add_line(layout, start_cluster, cluster_count, &text_position))) break; - remaining_clusters -= end_cluster - start_cluster + 1; + remaining_clusters -= cluster_count; start_cluster = end_cluster + 1; } -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/9974
From: Nikolay Sivov <nsivov@codeweavers.com> Signed-off-by: Nikolay Sivov <nsivov@codeweavers.com> --- dlls/dwrite/layout.c | 42 +++++++++++++++++++----------------------- 1 file changed, 19 insertions(+), 23 deletions(-) diff --git a/dlls/dwrite/layout.c b/dlls/dwrite/layout.c index 2cdf41369bb..63c0fd05bea 100644 --- a/dlls/dwrite/layout.c +++ b/dlls/dwrite/layout.c @@ -165,6 +165,7 @@ struct layout_effective_run float align_dx; /* adjustment from text alignment */ float width; /* run width */ UINT32 line; /* 0-based line index in line metrics array */ + UINT8 bidi_level; /* For inline objects level is derived from the first character */ /* Text run fields */ const struct layout_run *run; /* nominal run this one is based on */ @@ -177,7 +178,6 @@ struct layout_effective_run IDWriteInlineObject *object; /* User object or automatically added trimming sign. */ float baseline; bool is_sideways; /* Vertical flow direction flag passed to DrawInlineObject() */ - bool is_rtl; /* Reading direction flag passed to DrawInlineObject() */ }; struct layout_underline { @@ -1513,39 +1513,35 @@ static HRESULT layout_add_effective_run(struct dwrite_textlayout *layout, const UINT32 i, start, length, last_cluster; struct layout_effective_run *run; + if (!(run = calloc(1, sizeof(*run)))) + return E_OUTOFMEMORY; + if (r->kind == LAYOUT_RUN_INLINE) { - struct layout_effective_run *inlineobject; - - if (!(inlineobject = calloc(1, sizeof(*inlineobject)))) - return E_OUTOFMEMORY; - - inlineobject->object = r->u.object.object; - inlineobject->width = get_cluster_range_width(layout, first_cluster, first_cluster + cluster_count); - inlineobject->origin.x = is_rtl ? origin_x - inlineobject->width : origin_x; - inlineobject->origin.y = 0.0f; /* set after line is built */ - inlineobject->align_dx = 0.0f; - inlineobject->baseline = r->baseline; + 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 is derived from surrounding text (replaced text could have different ranges which differ in reading direction). */ - inlineobject->is_sideways = false; - inlineobject->is_rtl = false; - inlineobject->line = line; + run->is_sideways = false; + run->bidi_level = r->u.object.bidi_level; + run->line = line; /* effect assigned from start position and on is used for inline objects */ - inlineobject->effect = layout_get_effect_from_pos(layout, layout->clusters[first_cluster].position + + run->effect = layout_get_effect_from_pos(layout, layout->clusters[first_cluster].position + layout->clusters[first_cluster].run->start_position); - list_add_tail(&layout->inlineobjects, &inlineobject->draw_entry); - list_add_tail(&layout->effective_runs, &inlineobject->entry); + list_add_tail(&layout->inlineobjects, &run->draw_entry); + list_add_tail(&layout->effective_runs, &run->entry); + return S_OK; } - if (!(run = calloc(1, sizeof(*run)))) - return E_OUTOFMEMORY; - /* No need to iterate for that, use simple fact that: <last cluster position> = <first cluster position> + <sum of cluster lengths not including last one> */ last_cluster = first_cluster + cluster_count - 1; @@ -1562,6 +1558,7 @@ static HRESULT layout_add_effective_run(struct dwrite_textlayout *layout, const run->start = start = layout->clusters[first_cluster].position; run->length = length; run->width = get_cluster_range_width(layout, first_cluster, first_cluster + cluster_count); + run->bidi_level = r->u.regular.run.bidiLevel; /* Adjust by run width if direction differs. */ if (is_run_rtl(run) != is_rtl) @@ -2181,7 +2178,6 @@ static HRESULT layout_add_line(struct dwrite_textlayout *layout, UINT32 first_cl trimming_sign->baseline = sign_metrics.baseline; trimming_sign->is_sideways = false; - trimming_sign->is_rtl = false; trimming_sign->line = line; trimming_sign->effect = layout_get_effect_from_pos(layout, layout->clusters[end].position + @@ -3795,7 +3791,7 @@ static HRESULT WINAPI dwritetextlayout_Draw(IDWriteTextLayout4 *iface, SNAP_COORD(run->origin.y + origin_y), run->object, run->is_sideways, - run->is_rtl, + run->bidi_level & 1, run->effect); } -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/9974
participants (2)
-
Nikolay Sivov -
Nikolay Sivov (@nsivov)