[PATCH 0/4] MR9950: dwrite/layout: Add a list to keep textual and inline runs together.
From: Nikolay Sivov <nsivov@codeweavers.com> --- dlls/dwrite/tests/layout.c | 496 +++++++++++++++++++++++++++++++++++++ 1 file changed, 496 insertions(+) diff --git a/dlls/dwrite/tests/layout.c b/dlls/dwrite/tests/layout.c index 4f5b88d8e8c..6e620603c72 100644 --- a/dlls/dwrite/tests/layout.c +++ b/dlls/dwrite/tests/layout.c @@ -7089,6 +7089,501 @@ if (SUCCEEDED(hr)) IDWriteFactory_Release(factory); } +static void test_HitTestTextPosition(void) +{ + IDWriteInlineObject *trimming_sign; + DWRITE_TEXT_METRICS layout_metrics; + DWRITE_HIT_TEST_METRICS metrics; + IDWriteTextFormat *format; + IDWriteTextLayout *layout; + DWRITE_TRIMMING trimming; + DWRITE_TEXT_RANGE range; + IDWriteFactory *factory; + float posx, posy; + HRESULT hr; + + factory = create_factory(); + + hr = IDWriteFactory_CreateTextFormat(factory, L"Tahoma", NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL, + DWRITE_FONT_STRETCH_NORMAL, 10.0f, L"en-US", &format); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IDWriteFactory_CreateEllipsisTrimmingSign(factory, format, &trimming_sign); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IDWriteFactory_CreateTextLayout(factory, L"string", 6, format, 100.0f, 100.0f, &layout); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IDWriteTextLayout_GetMetrics(layout, &layout_metrics); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + /* Out of bounds */ + hr = IDWriteTextLayout_HitTestTextPosition(layout, 10, 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 == 6, "Unexpected text position %u.\n", metrics.textPosition); + ok(metrics.length == 0, "Unexpected length %u.\n", metrics.length); + ok(metrics.left == layout_metrics.width, "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, 10, TRUE, &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 == 6, "Unexpected text position %u.\n", metrics.textPosition); + ok(metrics.length == 0, "Unexpected length %u.\n", metrics.length); + ok(metrics.left == layout_metrics.width, "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); +} + /* Text-only, simple clusters, one line. */ + 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 == 0.0f && 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_HitTestTextPosition(layout, 0, TRUE, &posx, &posy, &metrics); + todo_wine + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); +if (hr == S_OK) +{ + ok(posx == metrics.width && 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_HitTestTextPosition(layout, 5, 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 == 5, "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_HitTestTextPosition(layout, 5, TRUE, &posx, &posy, &metrics); + todo_wine + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); +if (hr == S_OK) +{ + ok(posx == metrics.left + metrics.width && posy == 0.0f, "Unexpected position {%.8e,%.8e}.\n", posx, posy); + ok(metrics.textPosition == 5, "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); +} + /* Right-to-left direction. */ + hr = IDWriteTextLayout_SetReadingDirection(layout, DWRITE_READING_DIRECTION_RIGHT_TO_LEFT); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IDWriteTextLayout_HitTestTextPosition(layout, 10, 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 == 6, "Unexpected text position %u.\n", metrics.textPosition); + ok(metrics.length == 0, "Unexpected length %u.\n", metrics.length); + ok(metrics.left == layout_metrics.layoutWidth, "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 == 2, "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, 10, TRUE, &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 == 6, "Unexpected text position %u.\n", metrics.textPosition); + ok(metrics.length == 0, "Unexpected length %u.\n", metrics.length); + ok(metrics.left == layout_metrics.layoutWidth, "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 == 2, "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 == 2, "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, TRUE, &posx, &posy, &metrics); + todo_wine + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); +if (hr == S_OK) +{ + ok(posx == metrics.left + metrics.width && 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 == 2, "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, 5, 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 == 5, "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 == 2, "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, 5, TRUE, &posx, &posy, &metrics); + todo_wine + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); +if (hr == S_OK) +{ + ok(posx == layout_metrics.layoutWidth && posy == 0.0f, "Unexpected position {%.8e,%.8e}.\n", posx, posy); + ok(metrics.textPosition == 5, "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 == 2, "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); +} + /* Trimming */ + hr = IDWriteTextLayout_SetWordWrapping(layout, DWRITE_WORD_WRAPPING_NO_WRAP); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + trimming.granularity = DWRITE_TRIMMING_GRANULARITY_CHARACTER; + trimming.delimiter = 0; + trimming.delimiterCount = 0; + hr = IDWriteTextLayout_SetTrimming(layout, &trimming, trimming_sign); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IDWriteTextLayout_SetMaxWidth(layout, layout_metrics.width / 2.0f); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + 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 == 2, "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, TRUE, &posx, &posy, &metrics); + todo_wine + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); +if (hr == S_OK) +{ + ok(posx == metrics.left + metrics.width && 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 == 2, "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, 5, TRUE, &posx, &posy, &metrics); + todo_wine + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); +if (hr == S_OK) +{ + ok(posx == metrics.left + metrics.width && 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 == 2, "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_SetMaxWidth(layout, 100.0f); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + /* Now with inline object. */ + range.startPosition = 0; + range.length = 2; + hr = IDWriteTextLayout_SetInlineObject(layout, trimming_sign, range); + 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, 0, FALSE, &posx, &posy, &metrics); + todo_wine + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); +if (hr == S_OK) +{ + ok(posx == 0.0f && posy == metrics.top, "Unexpected position {%.8e,%.8e}.\n", posx, posy); + ok(metrics.textPosition == 0, "Unexpected text position %u.\n", metrics.textPosition); + ok(metrics.length == 2, "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, TRUE, &posx, &posy, &metrics); + todo_wine + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); +if (hr == S_OK) +{ + ok(posx == metrics.width && posy == metrics.top, "Unexpected position {%.8e,%.8e}.\n", posx, posy); + ok(metrics.textPosition == 0, "Unexpected text position %u.\n", metrics.textPosition); + ok(metrics.length == 2, "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); +} + /* Inline object, RTL */ + hr = IDWriteTextLayout_SetReadingDirection(layout, DWRITE_READING_DIRECTION_RIGHT_TO_LEFT); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + 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 == metrics.top, "Unexpected position {%.8e,%.8e}.\n", posx, posy); + ok(metrics.textPosition == 0, "Unexpected text position %u.\n", metrics.textPosition); + ok(metrics.length == 2, "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 == 2, "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, TRUE, &posx, &posy, &metrics); + todo_wine + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); +if (hr == S_OK) +{ + ok(posx == metrics.left + metrics.width && posy == metrics.top, "Unexpected position {%.8e,%.8e}.\n", posx, posy); + ok(metrics.textPosition == 0, "Unexpected text position %u.\n", metrics.textPosition); + ok(metrics.length == 2, "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 == 2, "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); +} + IDWriteTextLayout_Release(layout); + + /* Non-trivial clusters */ + hr = IDWriteFactory_CreateTextLayout(factory, L"\u0627\u0644str\u0644", 6, format, 100.0f, 100.0f, &layout); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IDWriteTextLayout_SetReadingDirection(layout, DWRITE_READING_DIRECTION_RIGHT_TO_LEFT); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + 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 == 100.0f && 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 == 1, "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, TRUE, &posx, &posy, &metrics); + todo_wine + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); +if (hr == S_OK) +{ + ok(posx == metrics.left && posy == metrics.top, "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 == 1, "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, 10, 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 == 6, "Unexpected text position %u.\n", metrics.textPosition); + ok(metrics.length == 0, "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 == 1, "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, 10, TRUE, &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 == 6, "Unexpected text position %u.\n", metrics.textPosition); + ok(metrics.length == 0, "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 == 1, "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); +} + + /* Set inline object to contain both Arabic and Latin, Arabic first */ + range.startPosition = 1; + range.length = 2; + hr = IDWriteTextLayout_SetInlineObject(layout, trimming_sign, range); + 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 == metrics.left + metrics.width && posy == metrics.top, "Unexpected position {%.8e,%.8e}.\n", posx, posy); + ok(metrics.textPosition == 1, "Unexpected text position %u.\n", metrics.textPosition); + ok(metrics.length == 2, "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 == 1, "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_SetInlineObject(layout, NULL, range); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + /* Starting with Latin now. */ + range.startPosition = 4; + range.length = 2; + hr = IDWriteTextLayout_SetInlineObject(layout, trimming_sign, range); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IDWriteTextLayout_HitTestTextPosition(layout, 4, FALSE, &posx, &posy, &metrics); + todo_wine + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); +if (hr == S_OK) +{ + ok(posx == metrics.left && posy == metrics.top, "Unexpected position {%.8e,%.8e}.\n", posx, posy); + ok(metrics.textPosition == 4, "Unexpected text position %u.\n", metrics.textPosition); + ok(metrics.length == 2, "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 == 2, "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); +} + + IDWriteTextLayout_Release(layout); + + IDWriteInlineObject_Release(trimming_sign); + IDWriteTextFormat_Release(format); + IDWriteFactory_Release(factory); +} + START_TEST(layout) { IDWriteFactory *factory; @@ -7145,6 +7640,7 @@ START_TEST(layout) test_text_format_axes(); test_layout_range_length(); test_HitTestTextRange(); + test_HitTestTextPosition(); IDWriteFactory_Release(factory); } -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/9950
From: Nikolay Sivov <nsivov@codeweavers.com> --- dlls/dwrite/layout.c | 390 +++++++++++++++++++++++++++++-------------- 1 file changed, 265 insertions(+), 125 deletions(-) diff --git a/dlls/dwrite/layout.c b/dlls/dwrite/layout.c index aefb49296b3..5998c3d2787 100644 --- a/dlls/dwrite/layout.c +++ b/dlls/dwrite/layout.c @@ -213,6 +213,33 @@ enum layout_recompute_mask { RECOMPUTE_EVERYTHING = 0xffff }; +#define SCRIPT_ID_MASK 0x7fff +#define SCRIPT_NO_VISUAL 0x8000 + +struct layout_scripts +{ + struct + { + UINT32 start; + UINT32 end; + UINT16 script; + } *ranges; + size_t count; + size_t capacity; +}; + +struct layout_bidi_levels +{ + struct + { + UINT32 start; + UINT32 end; + UINT8 level; + } *ranges; + size_t count; + size_t capacity; +}; + struct dwrite_textlayout { IDWriteTextLayout4 IDWriteTextLayout4_iface; @@ -224,13 +251,15 @@ struct dwrite_textlayout IDWriteFactory7 *factory; IDWriteFontCollection *system_collection; - WCHAR *str; - UINT32 len; + WCHAR *text; + UINT32 length; struct { unsigned int offset; unsigned int length; + struct layout_scripts scripts; + struct layout_bidi_levels levels; } text_source; struct dwrite_textformat_data format; @@ -401,38 +430,30 @@ static inline DWRITE_BREAK_CONDITION override_break_condition(DWRITE_BREAK_CONDI return existingbreak; } -/* This helper should be used to get effective range length, in other words it returns number of text - positions from range starting point to the end of the range, limited by layout text length */ -static inline UINT32 get_clipped_range_length(const struct dwrite_textlayout *layout, const struct layout_range *range) -{ - if (range->h.range.startPosition + range->h.range.length <= layout->len) - return range->h.range.length; - return layout->len - range->h.range.startPosition; -} - -/* Actual breakpoint data gets updated with break condition required by inline object set for range 'cur'. */ -static HRESULT layout_update_breakpoints_range(struct dwrite_textlayout *layout, const struct layout_range *cur) +/* Actual breakpoint data gets updated with break condition required by the inline object. */ +static HRESULT layout_update_breakpoints_range(struct dwrite_textlayout *layout, IDWriteInlineObject *object, + UINT32 start, UINT32 length) { DWRITE_BREAK_CONDITION before, after; - UINT32 i, length; HRESULT hr; /* ignore returned conditions if failed */ - hr = IDWriteInlineObject_GetBreakConditions(cur->object, &before, &after); + hr = IDWriteInlineObject_GetBreakConditions(object, &before, &after); if (FAILED(hr)) after = before = DWRITE_BREAK_CONDITION_NEUTRAL; if (!layout->actual_breakpoints) { - if (!(layout->actual_breakpoints = calloc(layout->len, sizeof(*layout->actual_breakpoints)))) + if (!(layout->actual_breakpoints = calloc(layout->length, sizeof(*layout->actual_breakpoints)))) return E_OUTOFMEMORY; - memcpy(layout->actual_breakpoints, layout->nominal_breakpoints, sizeof(DWRITE_LINE_BREAKPOINT)*layout->len); + memcpy(layout->actual_breakpoints, layout->nominal_breakpoints, sizeof(DWRITE_LINE_BREAKPOINT)*layout->length); } - length = get_clipped_range_length(layout, cur); - for (i = cur->h.range.startPosition; i < length + cur->h.range.startPosition; i++) { - /* for first codepoint check if there's anything before it and update accordingly */ - if (i == cur->h.range.startPosition) { + for (UINT32 i = start; i < length + start; i++) + { + /* For the first codepoint check if there's anything before it and update accordingly */ + if (i == start) + { if (i > 0) layout->actual_breakpoints[i].breakConditionBefore = layout->actual_breakpoints[i-1].breakConditionAfter = override_break_condition(layout->actual_breakpoints[i-1].breakConditionAfter, before); @@ -440,9 +461,10 @@ static HRESULT layout_update_breakpoints_range(struct dwrite_textlayout *layout, layout->actual_breakpoints[i].breakConditionBefore = before; layout->actual_breakpoints[i].breakConditionAfter = DWRITE_BREAK_CONDITION_MAY_NOT_BREAK; } - /* similar check for last codepoint */ - else if (i == cur->h.range.startPosition + length - 1) { - if (i == layout->len - 1) + /* similar check for the last codepoint */ + else if (i == start + length - 1) + { + if (i == layout->length - 1) layout->actual_breakpoints[i].breakConditionAfter = after; else layout->actual_breakpoints[i].breakConditionAfter = layout->actual_breakpoints[i+1].breakConditionBefore = @@ -526,7 +548,7 @@ static inline void init_cluster_metrics(const struct dwrite_textlayout *layout, if (metrics->length == 1) { DWRITE_LINE_BREAKPOINT bp = get_effective_breakpoint(layout, position); metrics->isWhitespace = bp.isWhitespace; - metrics->isNewline = metrics->canWrapLineAfter && lb_is_newline_char(layout->str[position]); + metrics->isNewline = metrics->canWrapLineAfter && lb_is_newline_char(layout->text[position]); metrics->isSoftHyphen = bp.isSoftHyphen; } else { @@ -605,55 +627,200 @@ static inline void layout_get_font_height(float emsize, const DWRITE_FONT_METRIC static inline void layout_initialize_text_source(struct dwrite_textlayout *layout, unsigned int offset, unsigned int length) { + memset(&layout->text_source, 0, sizeof(layout->text_source)); layout->text_source.offset = offset; layout->text_source.length = length; } -static HRESULT layout_itemize(struct dwrite_textlayout *layout) +static void layout_cleanup_text_source(struct dwrite_textlayout *layout) { - IDWriteTextAnalyzer2 *analyzer; - struct layout_range *range; - struct layout_run *r; - HRESULT hr = S_OK; + free(layout->text_source.scripts.ranges); + free(layout->text_source.levels.ranges); + memset(&layout->text_source, 0, sizeof(layout->text_source)); +} - analyzer = get_text_analyzer(); +struct itemization_context +{ + struct dwrite_textlayout *layout; + unsigned int run_start, run_end; - layout_initialize_text_source(layout, 0, layout->len); - 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; + struct + { + UINT8 value; + unsigned int index, end; + } level; - /* Inline objects override actual text in range. */ - if (range->object) { - hr = layout_update_breakpoints_range(layout, range); - if (FAILED(hr)) - return hr; + struct + { + UINT16 script; + unsigned int index, end; + } script; - if (FAILED(hr = alloc_layout_run(LAYOUT_RUN_INLINE, range->h.range.startPosition, &r))) - return hr; + struct + { + struct layout_range *value; + unsigned int end; + } range; +}; - r->u.object.object = range->object; - r->u.object.length = get_clipped_range_length(layout, range); - list_add_tail(&layout->runs, &r->entry); - continue; +static void layout_itemize_next_level(struct itemization_context *context) +{ + const struct dwrite_textlayout *layout = context->layout; + unsigned int index = context->level.index++; + + context->level.value = layout->text_source.levels.ranges[index].level; + context->level.end = layout->text_source.levels.ranges[index].end; +} + +static void layout_itemize_next_script(struct itemization_context *context) +{ + const struct dwrite_textlayout *layout = context->layout; + unsigned int index = context->script.index++; + + context->script.script = layout->text_source.scripts.ranges[index].script; + context->script.end = layout->text_source.scripts.ranges[index].end; +} + +static void layout_itemize_next_range(struct itemization_context *context) +{ + const struct dwrite_textlayout *layout = context->layout; + + context->range.value = LIST_ENTRY(context->range.value ? list_next(&layout->ranges, &context->range.value->h.entry) + : list_head(&layout->ranges), struct layout_range, h.entry); + context->range.end = min(context->layout->length, context->range.value->h.range.startPosition + + context->range.value->h.range.length); +} + +static void layout_itemize_set_run_end(struct itemization_context *context) +{ + /* Inline objects take precedence, skip level and script ranges accordingly. */ + + 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 + { + context->run_end = context->level.end; + context->run_end = min(context->run_end, context->script.end); + context->run_end = min(context->run_end, context->range.end); + } +} + +static void layout_itemize_context_init(struct dwrite_textlayout *layout, + struct itemization_context *context) +{ + memset(context, 0, sizeof(*context)); + + context->layout = layout; + layout_itemize_next_level(context); + layout_itemize_next_script(context); + layout_itemize_next_range(context); + layout_itemize_set_run_end(context); +} + +static bool layout_itemize_get_next(struct itemization_context *context) +{ + if (context->run_end == context->layout->length) + return false; + + context->run_start = context->run_end; + + if (context->run_end == context->level.end) + layout_itemize_next_level(context); + + if (context->run_end == context->script.end) + layout_itemize_next_script(context); + + if (context->run_end == context->range.end) + layout_itemize_next_range(context); + + layout_itemize_set_run_end(context); + return true; +} + +static HRESULT layout_itemize_add_run(struct itemization_context *context) +{ + UINT32 length = context->run_end - context->run_start; + struct layout_run *run; + HRESULT hr; + + if (FAILED(hr = alloc_layout_run(context->range.value->object ? LAYOUT_RUN_INLINE : LAYOUT_RUN_REGULAR, + context->run_start, &run))) + return hr; + + if (context->range.value->object) + { + if (FAILED(hr = layout_update_breakpoints_range(context->layout, context->range.value->object, + context->run_start, length))) + { + free(run); + return hr; } - /* Initial splitting by script. */ - hr = IDWriteTextAnalyzer2_AnalyzeScript(analyzer, (IDWriteTextAnalysisSource *)&layout->IDWriteTextAnalysisSource1_iface, - range->h.range.startPosition, get_clipped_range_length(layout, range), - (IDWriteTextAnalysisSink *)&layout->IDWriteTextAnalysisSink1_iface); - if (FAILED(hr)) - break; + run->u.object.object = context->range.value->object; + run->u.object.length = length; + } + else + { + run->u.regular.descr.string = &context->layout->text[context->run_start]; + run->u.regular.descr.stringLength = length; + run->u.regular.descr.textPosition = context->run_start; + run->u.regular.sa.script = context->script.script & SCRIPT_ID_MASK; + run->u.regular.sa.shapes = context->script.script & SCRIPT_NO_VISUAL ? + DWRITE_SCRIPT_SHAPES_NO_VISUAL : DWRITE_SCRIPT_SHAPES_DEFAULT; + run->u.regular.run.bidiLevel = context->level.value; + } + list_add_tail(&context->layout->runs, &run->entry); + + return hr; +} + +static HRESULT layout_itemize(struct dwrite_textlayout *layout) +{ + struct itemization_context context; + IDWriteTextAnalyzer2 *analyzer; + HRESULT hr; - /* Splitting further by bidi levels. */ + if (layout->length == 0) + return S_OK; + + analyzer = get_text_analyzer(); + + layout_initialize_text_source(layout, 0, layout->length); + + hr = IDWriteTextAnalyzer2_AnalyzeScript(analyzer, (IDWriteTextAnalysisSource *)&layout->IDWriteTextAnalysisSource1_iface, + 0, layout->length, (IDWriteTextAnalysisSink *)&layout->IDWriteTextAnalysisSink1_iface); + if (SUCCEEDED(hr)) + { hr = IDWriteTextAnalyzer2_AnalyzeBidi(analyzer, (IDWriteTextAnalysisSource *)&layout->IDWriteTextAnalysisSource1_iface, - range->h.range.startPosition, get_clipped_range_length(layout, range), - (IDWriteTextAnalysisSink *)&layout->IDWriteTextAnalysisSink1_iface); - if (FAILED(hr)) - break; + 0, layout->length, (IDWriteTextAnalysisSink *)&layout->IDWriteTextAnalysisSink1_iface); } + if (FAILED(hr)) + { + layout_cleanup_text_source(layout); + return hr; + } + + layout_itemize_context_init(layout, &context); + do + { + hr = layout_itemize_add_run(&context); + } while (hr == S_OK && layout_itemize_get_next(&context)); + + layout_cleanup_text_source(layout); return hr; } @@ -723,7 +890,7 @@ static HRESULT layout_map_run_characters(struct dwrite_textlayout *layout, struc nextrun->run.fontFace = NULL; nextrun->descr.textPosition = nextr->start_position; nextrun->descr.stringLength = run->descr.stringLength - mapped_length; - nextrun->descr.string = &layout->str[nextrun->descr.textPosition]; + nextrun->descr.string = &layout->text[nextrun->descr.textPosition]; run->descr.stringLength = mapped_length; list_add_after(&r->entry, &nextr->entry); r = nextr; @@ -1157,10 +1324,10 @@ static HRESULT layout_compute_runs(struct dwrite_textlayout *layout) free_layout_runs(layout); /* Cluster data arrays are allocated once, assuming one text position per cluster. */ - if (!layout->clustermetrics && layout->len) + if (!layout->clustermetrics && layout->length) { - layout->clustermetrics = calloc(layout->len, sizeof(*layout->clustermetrics)); - layout->clusters = calloc(layout->len, sizeof(*layout->clusters)); + layout->clustermetrics = calloc(layout->length, sizeof(*layout->clustermetrics)); + layout->clusters = calloc(layout->length, sizeof(*layout->clusters)); if (!layout->clustermetrics || !layout->clusters) { free(layout->clustermetrics); @@ -1249,15 +1416,15 @@ static HRESULT layout_compute(struct dwrite_textlayout *layout) { IDWriteTextAnalyzer2 *analyzer; - if (!(layout->nominal_breakpoints = calloc(layout->len, sizeof(*layout->nominal_breakpoints)))) + if (!(layout->nominal_breakpoints = calloc(layout->length, sizeof(*layout->nominal_breakpoints)))) return E_OUTOFMEMORY; analyzer = get_text_analyzer(); - layout_initialize_text_source(layout, 0, layout->len); + layout_initialize_text_source(layout, 0, layout->length); if (FAILED(hr = IDWriteTextAnalyzer2_AnalyzeLineBreakpoints(analyzer, (IDWriteTextAnalysisSource *)&layout->IDWriteTextAnalysisSource1_iface, - 0, layout->len, (IDWriteTextAnalysisSink *)&layout->IDWriteTextAnalysisSink1_iface))) + 0, layout->length, (IDWriteTextAnalysisSink *)&layout->IDWriteTextAnalysisSink1_iface))) WARN("Line breakpoints analysis failed, hr %#lx.\n", hr); } @@ -2955,7 +3122,7 @@ static ULONG WINAPI dwritetextlayout_Release(IDWriteTextLayout4 *iface) free(layout->clustermetrics); free(layout->clusters); free(layout->lines); - free(layout->str); + free(layout->text); free(layout); } @@ -4918,19 +5085,25 @@ static HRESULT WINAPI dwritetextlayout_sink_SetScriptAnalysis(IDWriteTextAnalysi UINT32 position, UINT32 length, DWRITE_SCRIPT_ANALYSIS const* sa) { struct dwrite_textlayout *layout = impl_from_IDWriteTextAnalysisSink1(iface); - struct layout_run *run; - HRESULT hr; + struct layout_scripts *scripts = &layout->text_source.scripts; + UINT32 script; TRACE("[%u,%u) script=%u:%s\n", position, position + length, sa->script, debugstr_sa_script(sa->script)); - if (FAILED(hr = alloc_layout_run(LAYOUT_RUN_REGULAR, position, &run))) - return hr; + if (!dwrite_array_reserve((void **)&scripts->ranges, &scripts->capacity, scripts->count + 1, + sizeof(*scripts->ranges))) + { + return E_OUTOFMEMORY; + } + + script = sa->script; + if (sa->shapes == DWRITE_SCRIPT_SHAPES_NO_VISUAL) + script |= SCRIPT_NO_VISUAL; + scripts->ranges[scripts->count].script = script; + scripts->ranges[scripts->count].start = position; + scripts->ranges[scripts->count].end = position + length; + ++scripts->count; - run->u.regular.descr.string = &layout->str[position]; - run->u.regular.descr.stringLength = length; - run->u.regular.descr.textPosition = position; - run->u.regular.sa = *sa; - list_add_tail(&layout->runs, &run->entry); return S_OK; } @@ -4939,7 +5112,7 @@ static HRESULT WINAPI dwritetextlayout_sink_SetLineBreakpoints(IDWriteTextAnalys { struct dwrite_textlayout *layout = impl_from_IDWriteTextAnalysisSink1(iface); - if (position + length > layout->len) + if (position + length > layout->length) return E_FAIL; memcpy(&layout->nominal_breakpoints[position], breakpoints, length*sizeof(DWRITE_LINE_BREAKPOINT)); @@ -4950,55 +5123,21 @@ static HRESULT WINAPI dwritetextlayout_sink_SetBidiLevel(IDWriteTextAnalysisSink UINT32 length, UINT8 explicitLevel, UINT8 resolvedLevel) { struct dwrite_textlayout *layout = impl_from_IDWriteTextAnalysisSink1(iface); - struct layout_run *cur_run; - HRESULT hr; + struct layout_bidi_levels *levels = &layout->text_source.levels; TRACE("[%u,%u) %u %u\n", position, position + length, explicitLevel, resolvedLevel); - LIST_FOR_EACH_ENTRY(cur_run, &layout->runs, struct layout_run, entry) { - struct regular_layout_run *cur = &cur_run->u.regular; - struct layout_run *run; - - if (cur_run->kind == LAYOUT_RUN_INLINE) - continue; - - /* FIXME: levels are reported in a natural forward direction, so start loop from a run we ended on */ - if (position < cur->descr.textPosition || position >= cur->descr.textPosition + cur->descr.stringLength) - continue; - - /* full hit - just set run level */ - if (cur->descr.textPosition == position && cur->descr.stringLength == length) { - cur->run.bidiLevel = resolvedLevel; - break; - } - - /* current run is fully covered, move to next one */ - if (cur->descr.textPosition == position && cur->descr.stringLength < length) { - cur->run.bidiLevel = resolvedLevel; - position += cur->descr.stringLength; - length -= cur->descr.stringLength; - continue; - } - - /* all fully covered runs are processed at this point, reuse existing run for remaining - reported bidi range and add another run for the rest of original one */ - - if (FAILED(hr = alloc_layout_run(LAYOUT_RUN_REGULAR, position + length, &run))) - return hr; - - *run = *cur_run; - run->u.regular.descr.textPosition = position + length; - run->u.regular.descr.stringLength = cur->descr.stringLength - length; - run->u.regular.descr.string = &layout->str[position + length]; - - /* reduce existing run */ - cur->run.bidiLevel = resolvedLevel; - cur->descr.stringLength = length; - - list_add_after(&cur_run->entry, &run->entry); - break; + if (!dwrite_array_reserve((void **)&levels->ranges, &levels->capacity, levels->count + 1, + sizeof(*levels->ranges))) + { + return E_OUTOFMEMORY; } + levels->ranges[levels->count].level = resolvedLevel; + levels->ranges[levels->count].start = position; + levels->ranges[levels->count].end = position + length; + ++levels->count; + return S_OK; } @@ -5065,7 +5204,7 @@ static HRESULT WINAPI dwritetextlayout_source_GetTextAtPosition(IDWriteTextAnaly if (position < layout->text_source.length) { - *text = &layout->str[position + layout->text_source.offset]; + *text = &layout->text[position + layout->text_source.offset]; *text_len = layout->text_source.length - position; } else @@ -5086,7 +5225,7 @@ static HRESULT WINAPI dwritetextlayout_source_GetTextBeforePosition(IDWriteTextA if (position && position < layout->text_source.length) { - *text = &layout->str[layout->text_source.offset]; + *text = &layout->text[layout->text_source.offset]; *text_len = position; } else @@ -5272,7 +5411,7 @@ static HRESULT init_textlayout(const struct textlayout_desc *desc, struct dwrite layout->IDWriteTextAnalysisSink1_iface.lpVtbl = &dwritetextlayoutsinkvtbl; layout->IDWriteTextAnalysisSource1_iface.lpVtbl = &dwritetextlayoutsourcevtbl; layout->refcount = 1; - layout->len = desc->length; + layout->length = desc->length; layout->recompute = RECOMPUTE_EVERYTHING; list_init(&layout->eruns); list_init(&layout->inlineobjects); @@ -5288,8 +5427,9 @@ static HRESULT init_textlayout(const struct textlayout_desc *desc, struct dwrite layout->metrics.layoutWidth = desc->max_width; layout->metrics.layoutHeight = desc->max_height; - layout->str = heap_strdupnW(desc->string, desc->length); - if (desc->length && !layout->str) { + layout->text = heap_strdupnW(desc->string, desc->length); + if (desc->length && !layout->text) + { hr = E_OUTOFMEMORY; goto fail; } -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/9950
From: Nikolay Sivov <nsivov@codeweavers.com> --- dlls/dwrite/layout.c | 253 ++++++++++++++++++++++--------------------- 1 file changed, 128 insertions(+), 125 deletions(-) diff --git a/dlls/dwrite/layout.c b/dlls/dwrite/layout.c index 5998c3d2787..7d3aa134e9b 100644 --- a/dlls/dwrite/layout.c +++ b/dlls/dwrite/layout.c @@ -151,33 +151,31 @@ struct layout_run unsigned int start_position; /* run text position in range [0, layout-text-length) */ }; -struct layout_effective_run { - struct list entry; - const struct layout_run *run; /* nominal run this one is based on */ - UINT32 start; /* relative text position, 0 means first text position of a nominal run */ - UINT32 length; /* length in codepoints that this run covers */ - UINT32 glyphcount; /* total glyph count in this run */ - IUnknown *effect; /* original reference is kept only at range level */ - D2D1_POINT_2F origin; /* baseline origin */ - FLOAT align_dx; /* adjustment from text alignment */ - FLOAT width; /* run width */ - UINT16 *clustermap; /* effective clustermap, allocated separately, is not reused from nominal map */ - UINT32 line; /* 0-based line index in line metrics array */ - BOOL underlined; /* set if this run is underlined */ - D2D1_RECT_F bbox; /* ink run box, top == bottom means it wasn't estimated yet */ -}; +struct layout_effective_run +{ + struct list draw_entry; -struct layout_effective_inline { - struct list entry; - IDWriteInlineObject *object; /* inline object, set explicitly or added when trimming a line */ + /* Common fields */ + UINT32 start; /* relative text position, 0 means first text position of a nominal run */ + UINT32 length; /* length in codepoints that this run covers */ IUnknown *effect; /* original reference is kept only at range level */ - FLOAT baseline; - D2D1_POINT_2F origin; /* left top corner */ - FLOAT align_dx; /* adjustment from text alignment */ - FLOAT width; /* object width as it's reported it */ - BOOL is_sideways; /* vertical flow direction flag passed to Draw */ - BOOL is_rtl; /* bidi flag passed to Draw */ + D2D1_POINT_2F origin; /* baseline origin or left top corner */ + float align_dx; /* adjustment from text alignment */ + float width; /* run width */ UINT32 line; /* 0-based line index in line metrics array */ + + /* Text run fields */ + const struct layout_run *run; /* nominal run this one is based on */ + UINT32 glyphcount; /* total glyph count in this run */ + UINT16 *clustermap; /* Clustermap allocated separately, not reused from nominal map. */ + bool underlined; /* Set if this run is underlined */ + D2D1_RECT_F bbox; /* ink run box, top == bottom means it wasn't estimated yet */ + + /* Inline object fields */ + 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 { @@ -380,22 +378,21 @@ static void free_layout_runs(struct dwrite_textlayout *layout) static void free_layout_eruns(struct dwrite_textlayout *layout) { - struct layout_effective_inline *in, *in2; struct layout_effective_run *cur, *cur2; struct layout_strikethrough *s, *s2; struct layout_underline *u, *u2; - LIST_FOR_EACH_ENTRY_SAFE(cur, cur2, &layout->eruns, struct layout_effective_run, entry) + LIST_FOR_EACH_ENTRY_SAFE(cur, cur2, &layout->eruns, struct layout_effective_run, draw_entry) { - list_remove(&cur->entry); + list_remove(&cur->draw_entry); free(cur->clustermap); free(cur); } - LIST_FOR_EACH_ENTRY_SAFE(in, in2, &layout->inlineobjects, struct layout_effective_inline, entry) + LIST_FOR_EACH_ENTRY_SAFE(cur, cur2, &layout->inlineobjects, struct layout_effective_run, draw_entry) { - list_remove(&in->entry); - free(in); + list_remove(&cur->draw_entry); + free(cur); } LIST_FOR_EACH_ENTRY_SAFE(u, u2, &layout->underlines, struct layout_underline, entry) @@ -1530,9 +1527,9 @@ static HRESULT layout_add_effective_run(struct dwrite_textlayout *layout, const if (r->kind == LAYOUT_RUN_INLINE) { - struct layout_effective_inline *inlineobject; + struct layout_effective_run *inlineobject; - if (!(inlineobject = malloc(sizeof(*inlineobject)))) + if (!(inlineobject = calloc(1, sizeof(*inlineobject)))) return E_OUTOFMEMORY; inlineobject->object = r->u.object.object; @@ -1545,15 +1542,15 @@ static HRESULT layout_add_effective_run(struct dwrite_textlayout *layout, const /* 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->is_sideways = false; + inlineobject->is_rtl = false; inlineobject->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 + layout->clusters[first_cluster].run->start_position); - list_add_tail(&layout->inlineobjects, &inlineobject->entry); + list_add_tail(&layout->inlineobjects, &inlineobject->draw_entry); return S_OK; } @@ -1603,7 +1600,7 @@ static HRESULT layout_add_effective_run(struct dwrite_textlayout *layout, const run->effect = params->effect; run->underlined = params->underline; - list_add_tail(&layout->eruns, &run->entry); + list_add_tail(&layout->eruns, &run->draw_entry); /* Strikethrough style is guaranteed to be consistent within effective run, its width equals to run width, thickness and offset are derived from @@ -1683,10 +1680,10 @@ static inline struct layout_effective_run *layout_get_next_erun(const struct dwr if (!cur) e = list_head(&layout->eruns); else - e = list_next(&layout->eruns, &cur->entry); + e = list_next(&layout->eruns, &cur->draw_entry); if (!e) return NULL; - return LIST_ENTRY(e, struct layout_effective_run, entry); + return LIST_ENTRY(e, struct layout_effective_run, draw_entry); } static inline struct layout_effective_run *layout_get_prev_erun(const struct dwrite_textlayout *layout, @@ -1697,42 +1694,44 @@ static inline struct layout_effective_run *layout_get_prev_erun(const struct dwr if (!cur) e = list_tail(&layout->eruns); else - e = list_prev(&layout->eruns, &cur->entry); + e = list_prev(&layout->eruns, &cur->draw_entry); if (!e) return NULL; - return LIST_ENTRY(e, struct layout_effective_run, entry); + return LIST_ENTRY(e, struct layout_effective_run, draw_entry); } -static inline struct layout_effective_inline *layout_get_next_inline_run(const struct dwrite_textlayout *layout, - const struct layout_effective_inline *cur) +static inline struct layout_effective_run *layout_get_next_inline_run(const struct dwrite_textlayout *layout, + const struct layout_effective_run *cur) { struct list *e; if (!cur) e = list_head(&layout->inlineobjects); else - e = list_next(&layout->inlineobjects, &cur->entry); + e = list_next(&layout->inlineobjects, &cur->draw_entry); if (!e) return NULL; - return LIST_ENTRY(e, struct layout_effective_inline, entry); + 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 *erun, - const struct layout_effective_inline *inrun, UINT32 line) +static float layout_get_line_width(const struct dwrite_textlayout *layout, const struct layout_effective_run *text_run, + const struct layout_effective_run *inline_run, UINT32 line) { FLOAT width = 0.0f; - while (erun && erun->line == line) { - width += erun->width; - erun = layout_get_next_erun(layout, erun); - if (!erun) + while (text_run && text_run->line == line) + { + width += text_run->width; + text_run = layout_get_next_erun(layout, text_run); + if (!text_run) break; } - while (inrun && inrun->line == line) { - width += inrun->width; - inrun = layout_get_next_inline_run(layout, inrun); - if (!inrun) + while (inline_run && inline_run->line == line) + { + width += inline_run->width; + inline_run = layout_get_next_inline_run(layout, inline_run); + if (!inline_run) break; } @@ -1779,20 +1778,21 @@ static inline void layout_apply_snapping(D2D1_POINT_2F *vec, BOOL skiptransform, static void layout_apply_leading_alignment(struct dwrite_textlayout *layout) { BOOL is_rtl = layout->format.readingdir == DWRITE_READING_DIRECTION_RIGHT_TO_LEFT; - struct layout_effective_inline *inrun; - struct layout_effective_run *erun; + struct layout_effective_run *text_run, *inline_run; - erun = layout_get_next_erun(layout, NULL); - inrun = layout_get_next_inline_run(layout, NULL); + text_run = layout_get_next_erun(layout, NULL); + inline_run = layout_get_next_inline_run(layout, NULL); - while (erun) { - erun->align_dx = 0.0f; - erun = layout_get_next_erun(layout, erun); + while (text_run) + { + text_run->align_dx = 0.0f; + text_run = layout_get_next_erun(layout, text_run); } - while (inrun) { - inrun->align_dx = 0.0f; - inrun = layout_get_next_inline_run(layout, inrun); + while (inline_run) + { + inline_run->align_dx = 0.0f; + inline_run = layout_get_next_inline_run(layout, inline_run); } layout->metrics.left = is_rtl ? layout->metrics.layoutWidth - layout->metrics.width : 0.0f; @@ -1801,28 +1801,29 @@ 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; - struct layout_effective_inline *inrun; - struct layout_effective_run *erun; + struct layout_effective_run *text_run, *inline_run; UINT32 line; - erun = layout_get_next_erun(layout, NULL); - inrun = layout_get_next_inline_run(layout, NULL); + text_run = layout_get_next_erun(layout, NULL); + inline_run = layout_get_next_inline_run(layout, NULL); for (line = 0; line < layout->metrics.lineCount; line++) { - FLOAT width = layout_get_line_width(layout, erun, inrun, line); + FLOAT width = layout_get_line_width(layout, text_run, inline_run, line); FLOAT shift = layout->metrics.layoutWidth - width; if (is_rtl) shift *= -1.0f; - while (erun && erun->line == line) { - erun->align_dx = shift; - erun = layout_get_next_erun(layout, erun); + while (text_run && text_run->line == line) + { + text_run->align_dx = shift; + text_run = layout_get_next_erun(layout, text_run); } - while (inrun && inrun->line == line) { - inrun->align_dx = shift; - inrun = layout_get_next_inline_run(layout, inrun); + while (inline_run && inline_run->line == line) + { + inline_run->align_dx = shift; + inline_run = layout_get_next_inline_run(layout, inline_run); } } @@ -1844,32 +1845,33 @@ static inline float layout_get_centered_shift(const struct dwrite_textlayout *la 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_inline *inrun; - struct layout_effective_run *erun; + struct layout_effective_run *text_run, *inline_run; BOOL skiptransform; UINT32 line; FLOAT det; - erun = layout_get_next_erun(layout, NULL); - inrun = layout_get_next_inline_run(layout, NULL); + text_run = layout_get_next_erun(layout, NULL); + inline_run = layout_get_next_inline_run(layout, NULL); skiptransform = should_skip_transform(&layout->transform, &det); for (line = 0; line < layout->metrics.lineCount; line++) { - FLOAT width = layout_get_line_width(layout, erun, inrun, line); + FLOAT width = layout_get_line_width(layout, text_run, inline_run, line); FLOAT shift = layout_get_centered_shift(layout, skiptransform, width, det); if (is_rtl) shift *= -1.0f; - while (erun && erun->line == line) { - erun->align_dx = shift; - erun = layout_get_next_erun(layout, erun); + while (text_run && text_run->line == line) + { + text_run->align_dx = shift; + text_run = layout_get_next_erun(layout, text_run); } - while (inrun && inrun->line == line) { - inrun->align_dx = shift; - inrun = layout_get_next_inline_run(layout, inrun); + while (inline_run && inline_run->line == line) + { + inline_run->align_dx = shift; + inline_run = layout_get_next_inline_run(layout, inline_run); } } @@ -1899,8 +1901,7 @@ static void layout_apply_text_alignment(struct dwrite_textlayout *layout) static void layout_apply_par_alignment(struct dwrite_textlayout *layout) { - struct layout_effective_inline *inrun; - struct layout_effective_run *erun; + struct layout_effective_run *text_run, *inline_run; FLOAT origin_y = 0.0f; UINT32 line; @@ -1924,20 +1925,22 @@ static void layout_apply_par_alignment(struct dwrite_textlayout *layout) layout->metrics.top = origin_y; - erun = layout_get_next_erun(layout, NULL); - inrun = layout_get_next_inline_run(layout, NULL); + text_run = layout_get_next_erun(layout, NULL); + inline_run = layout_get_next_inline_run(layout, NULL); for (line = 0; line < layout->metrics.lineCount; line++) { float pos_y = origin_y + layout->lines[line].metrics.baseline; - while (erun && erun->line == line) { - erun->origin.y = pos_y; - erun = layout_get_next_erun(layout, erun); + while (text_run && text_run->line == line) + { + text_run->origin.y = pos_y; + text_run = layout_get_next_erun(layout, text_run); } - while (inrun && inrun->line == line) { - inrun->origin.y = pos_y - inrun->baseline; - inrun = layout_get_next_inline_run(layout, inrun); + while (inline_run && inline_run->line == line) + { + inline_run->origin.y = pos_y - inline_run->baseline; + inline_run = layout_get_next_inline_run(layout, inline_run); } origin_y += layout->lines[line].metrics.height; @@ -2205,8 +2208,9 @@ static void layout_add_line(struct dwrite_textlayout *layout, UINT32 first_clust if (get_cluster_range_width(layout, start, i) + sign_metrics.width > layout->metrics.layoutWidth) append_trimming_run = FALSE; - if (append_trimming_run) { - struct layout_effective_inline *trimming_sign; + if (append_trimming_run) + { + struct layout_effective_run *trimming_sign; if (!(trimming_sign = calloc(1, sizeof(*trimming_sign)))) return; @@ -2219,14 +2223,14 @@ static void layout_add_line(struct dwrite_textlayout *layout, UINT32 first_clust trimming_sign->align_dx = 0.0f; trimming_sign->baseline = sign_metrics.baseline; - trimming_sign->is_sideways = FALSE; - trimming_sign->is_rtl = FALSE; + 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[i].position + layout->clusters[i].run->start_position); - list_add_tail(&layout->inlineobjects, &trimming_sign->entry); + list_add_tail(&layout->inlineobjects, &trimming_sign->draw_entry); } /* Look for max baseline and descent for this line */ @@ -2253,29 +2257,30 @@ static void layout_add_line(struct dwrite_textlayout *layout, UINT32 first_clust static void layout_set_line_positions(struct dwrite_textlayout *layout) { - struct layout_effective_inline *inrun; - struct layout_effective_run *erun; + struct layout_effective_run *text_run, *inline_run; FLOAT origin_y; UINT32 line; /* Now all line info is here, update effective runs positions in flow direction */ - erun = layout_get_next_erun(layout, NULL); - inrun = layout_get_next_inline_run(layout, NULL); + text_run = layout_get_next_erun(layout, NULL); + inline_run = layout_get_next_inline_run(layout, NULL); for (line = 0, origin_y = 0.0f; line < layout->metrics.lineCount; line++) { float pos_y = origin_y + layout->lines[line].metrics.baseline; /* For all runs on this line */ - while (erun && erun->line == line) { - erun->origin.y = pos_y; - erun = layout_get_next_erun(layout, erun); + while (text_run && text_run->line == line) + { + text_run->origin.y = pos_y; + text_run = layout_get_next_erun(layout, text_run); } /* Same for inline runs */ - while (inrun && inrun->line == line) { - inrun->origin.y = pos_y - inrun->baseline; - inrun = layout_get_next_inline_run(layout, inrun); + while (inline_run && inline_run->line == line) + { + inline_run->origin.y = pos_y - inline_run->baseline; + inline_run = layout_get_next_inline_run(layout, inline_run); } origin_y += layout->lines[line].metrics.height; @@ -3730,7 +3735,6 @@ static HRESULT WINAPI dwritetextlayout_Draw(IDWriteTextLayout4 *iface, { struct dwrite_textlayout *layout = impl_from_IDWriteTextLayout4(iface); BOOL disabled = FALSE, skiptransform = FALSE; - struct layout_effective_inline *inlineobject; struct layout_effective_run *run; struct layout_strikethrough *s; struct layout_underline *u; @@ -3768,7 +3772,7 @@ static HRESULT WINAPI dwritetextlayout_Draw(IDWriteTextLayout4 *iface, #define SNAP_COORD(x) (disabled ? (x) : renderer_apply_snapping((x), skiptransform, ppdip, det, &m)) /* 1. Regular runs */ - LIST_FOR_EACH_ENTRY(run, &layout->eruns, struct layout_effective_run, entry) + LIST_FOR_EACH_ENTRY(run, &layout->eruns, struct layout_effective_run, draw_entry) { const struct regular_layout_run *regular = &run->run->u.regular; UINT32 start_glyph = regular->clustermap[run->start]; @@ -3805,16 +3809,16 @@ static HRESULT WINAPI dwritetextlayout_Draw(IDWriteTextLayout4 *iface, } /* 2. Inline objects */ - LIST_FOR_EACH_ENTRY(inlineobject, &layout->inlineobjects, struct layout_effective_inline, entry) + LIST_FOR_EACH_ENTRY(run, &layout->inlineobjects, struct layout_effective_run, draw_entry) { IDWriteTextRenderer_DrawInlineObject(renderer, context, - inlineobject->origin.x + inlineobject->align_dx + origin_x, - SNAP_COORD(inlineobject->origin.y + origin_y), - inlineobject->object, - inlineobject->is_sideways, - inlineobject->is_rtl, - inlineobject->effect); + run->origin.x + run->align_dx + origin_x, + SNAP_COORD(run->origin.y + origin_y), + run->object, + run->is_sideways, + run->is_rtl, + run->effect); } /* 3. Underlines */ @@ -3978,7 +3982,7 @@ static void layout_get_erun_bbox(struct dwrite_textlayout *layout, struct layout d2d_rect_offset(bbox, run->origin.x + run->align_dx, run->origin.y); } -static void layout_get_inlineobj_bbox(const struct layout_effective_inline *run, D2D1_RECT_F *bbox) +static void layout_get_inlineobj_bbox(const struct layout_effective_run *run, D2D1_RECT_F *bbox) { DWRITE_OVERHANG_METRICS overhang_metrics = { 0 }; DWRITE_INLINE_OBJECT_METRICS metrics = { 0 }; @@ -4007,7 +4011,6 @@ static HRESULT WINAPI dwritetextlayout_GetOverhangMetrics(IDWriteTextLayout4 *if DWRITE_OVERHANG_METRICS *overhangs) { struct dwrite_textlayout *layout = impl_from_IDWriteTextLayout4(iface); - struct layout_effective_inline *inline_run; struct layout_effective_run *run; D2D1_RECT_F bbox = { 0 }; HRESULT hr; @@ -4026,7 +4029,7 @@ static HRESULT WINAPI dwritetextlayout_GetOverhangMetrics(IDWriteTextLayout4 *if if (FAILED(hr)) return hr; - LIST_FOR_EACH_ENTRY(run, &layout->eruns, struct layout_effective_run, entry) + LIST_FOR_EACH_ENTRY(run, &layout->eruns, struct layout_effective_run, draw_entry) { D2D1_RECT_F run_bbox; @@ -4034,11 +4037,11 @@ static HRESULT WINAPI dwritetextlayout_GetOverhangMetrics(IDWriteTextLayout4 *if d2d_rect_union(&bbox, &run_bbox); } - LIST_FOR_EACH_ENTRY(inline_run, &layout->inlineobjects, struct layout_effective_inline, entry) + LIST_FOR_EACH_ENTRY(run, &layout->inlineobjects, struct layout_effective_run, draw_entry) { D2D1_RECT_F object_bbox; - layout_get_inlineobj_bbox(inline_run, &object_bbox); + layout_get_inlineobj_bbox(run, &object_bbox); d2d_rect_union(&bbox, &object_bbox); } -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/9950
From: Nikolay Sivov <nsivov@codeweavers.com> --- dlls/dwrite/layout.c | 271 ++++++++++++++++++------------------------- 1 file changed, 111 insertions(+), 160 deletions(-) diff --git a/dlls/dwrite/layout.c b/dlls/dwrite/layout.c index 7d3aa134e9b..932be5551ac 100644 --- a/dlls/dwrite/layout.c +++ b/dlls/dwrite/layout.c @@ -154,6 +154,7 @@ struct layout_run struct layout_effective_run { struct list draw_entry; + struct list entry; /* Common fields */ UINT32 start; /* relative text position, 0 means first text position of a nominal run */ @@ -268,11 +269,13 @@ struct dwrite_textlayout struct list spacing; struct list ranges; struct list runs; - /* lists ready to use by Draw() */ - struct list eruns; + /* Lists ready to use with text renderer callbacks. */ + struct list text_runs; struct list inlineobjects; struct list underlines; struct list strikethrough; + /* Combined list of textual runs and inline objects */ + struct list effective_runs; USHORT recompute; DWRITE_LINE_BREAKPOINT *nominal_breakpoints; @@ -376,24 +379,20 @@ static void free_layout_runs(struct dwrite_textlayout *layout) } } -static void free_layout_eruns(struct dwrite_textlayout *layout) +static void free_layout_effective_runs(struct dwrite_textlayout *layout) { - struct layout_effective_run *cur, *cur2; + struct layout_effective_run *run, *next_run; struct layout_strikethrough *s, *s2; struct layout_underline *u, *u2; - LIST_FOR_EACH_ENTRY_SAFE(cur, cur2, &layout->eruns, struct layout_effective_run, draw_entry) - { - list_remove(&cur->draw_entry); - free(cur->clustermap); - free(cur); - } - - LIST_FOR_EACH_ENTRY_SAFE(cur, cur2, &layout->inlineobjects, struct layout_effective_run, draw_entry) + LIST_FOR_EACH_ENTRY_SAFE(run, next_run, &layout->effective_runs, struct layout_effective_run, entry) { - list_remove(&cur->draw_entry); - free(cur); + list_remove(&run->entry); + free(run->clustermap); + free(run); } + list_init(&layout->text_runs); + list_init(&layout->inlineobjects); LIST_FOR_EACH_ENTRY_SAFE(u, u2, &layout->underlines, struct layout_underline, entry) { @@ -1317,7 +1316,7 @@ static HRESULT layout_compute_runs(struct dwrite_textlayout *layout) UINT32 cluster = 0; HRESULT hr; - free_layout_eruns(layout); + free_layout_effective_runs(layout); free_layout_runs(layout); /* Cluster data arrays are allocated once, assuming one text position per cluster. */ @@ -1498,14 +1497,14 @@ static BOOL is_same_splitting_params(const struct layout_final_splitting_params left->effect == right->effect; } -static void layout_get_erun_font_metrics(const struct dwrite_textlayout *layout, const struct layout_effective_run *erun, +static void layout_get_text_run_font_metrics(const struct dwrite_textlayout *layout, const struct layout_effective_run *run, DWRITE_FONT_METRICS *metrics) { memset(metrics, 0, sizeof(*metrics)); if (is_layout_gdi_compatible(layout)) { HRESULT hr = IDWriteFontFace_GetGdiCompatibleMetrics( - erun->run->u.regular.run.fontFace, - erun->run->u.regular.run.fontEmSize, + run->run->u.regular.run.fontFace, + run->run->u.regular.run.fontEmSize, layout->ppdip, &layout->transform, metrics); @@ -1513,7 +1512,7 @@ static void layout_get_erun_font_metrics(const struct dwrite_textlayout *layout, WARN("failed to get font metrics, 0x%08lx\n", hr); } else - IDWriteFontFace_GetMetrics(erun->run->u.regular.run.fontFace, metrics); + IDWriteFontFace_GetMetrics(run->run->u.regular.run.fontFace, metrics); } /* Effective run is built from consecutive clusters of a single nominal run, 'first_cluster' is 0 based cluster index, @@ -1551,10 +1550,11 @@ static HRESULT layout_add_effective_run(struct dwrite_textlayout *layout, const layout->clusters[first_cluster].run->start_position); list_add_tail(&layout->inlineobjects, &inlineobject->draw_entry); + list_add_tail(&layout->effective_runs, &inlineobject->entry); return S_OK; } - if (!(run = malloc(sizeof(*run)))) + if (!(run = calloc(1, sizeof(*run)))) return E_OUTOFMEMORY; /* No need to iterate for that, use simple fact that: @@ -1573,7 +1573,6 @@ 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); - memset(&run->bbox, 0, sizeof(run->bbox)); /* Adjust by run width if direction differs. */ if (is_run_rtl(run) != is_rtl) @@ -1581,8 +1580,6 @@ static HRESULT layout_add_effective_run(struct dwrite_textlayout *layout, const else run->origin.x = origin_x; - run->origin.y = 0.0f; /* set after line is built */ - run->align_dx = 0.0f; run->line = line; if (r->u.regular.run.glyphCount) { @@ -1591,8 +1588,6 @@ static HRESULT layout_add_effective_run(struct dwrite_textlayout *layout, const if (start + length < r->u.regular.descr.stringLength) run->glyphcount -= r->u.regular.run.glyphCount - r->u.regular.clustermap[start + length]; } - else - run->glyphcount = 0; /* cluster map needs to be shifted */ for (i = 0; i < length; i++) @@ -1600,7 +1595,8 @@ static HRESULT layout_add_effective_run(struct dwrite_textlayout *layout, const run->effect = params->effect; run->underlined = params->underline; - list_add_tail(&layout->eruns, &run->draw_entry); + list_add_tail(&layout->text_runs, &run->draw_entry); + list_add_tail(&layout->effective_runs, &run->entry); /* Strikethrough style is guaranteed to be consistent within effective run, its width equals to run width, thickness and offset are derived from @@ -1613,7 +1609,7 @@ static HRESULT layout_add_effective_run(struct dwrite_textlayout *layout, const if (!(s = malloc(sizeof(*s)))) return E_OUTOFMEMORY; - layout_get_erun_font_metrics(layout, run, &metrics); + layout_get_text_run_font_metrics(layout, run, &metrics); s->s.width = get_cluster_range_width(layout, first_cluster, first_cluster + cluster_count); s->s.thickness = SCALE_FONT_METRIC(metrics.strikethroughThickness, r->u.regular.run.fontEmSize, &metrics); /* Negative offset moves it above baseline as Y coordinate grows downward. */ @@ -1672,67 +1668,57 @@ static HRESULT layout_set_line_metrics(struct dwrite_textlayout *layout, DWRITE_ return S_OK; } -static inline struct layout_effective_run *layout_get_next_erun(const struct dwrite_textlayout *layout, - const struct layout_effective_run *cur) +static inline struct layout_effective_run *layout_get_next_effective_run(const struct dwrite_textlayout *layout, + const struct layout_effective_run *cur) { struct list *e; if (!cur) - e = list_head(&layout->eruns); + e = list_head(&layout->effective_runs); else - e = list_next(&layout->eruns, &cur->draw_entry); + e = list_next(&layout->effective_runs, &cur->entry); if (!e) return NULL; - return LIST_ENTRY(e, struct layout_effective_run, draw_entry); + return LIST_ENTRY(e, struct layout_effective_run, entry); } -static inline struct layout_effective_run *layout_get_prev_erun(const struct dwrite_textlayout *layout, +static inline struct layout_effective_run *layout_get_next_text_run(const struct dwrite_textlayout *layout, const struct layout_effective_run *cur) { struct list *e; if (!cur) - e = list_tail(&layout->eruns); + e = list_head(&layout->text_runs); else - e = list_prev(&layout->eruns, &cur->draw_entry); + e = list_next(&layout->text_runs, &cur->draw_entry); if (!e) return NULL; return LIST_ENTRY(e, struct layout_effective_run, draw_entry); } -static inline struct layout_effective_run *layout_get_next_inline_run(const struct dwrite_textlayout *layout, +static inline struct layout_effective_run *layout_get_prev_text_run(const struct dwrite_textlayout *layout, const struct layout_effective_run *cur) { struct list *e; if (!cur) - e = list_head(&layout->inlineobjects); + e = list_tail(&layout->text_runs); else - e = list_next(&layout->inlineobjects, &cur->draw_entry); + e = list_prev(&layout->text_runs, &cur->draw_entry); if (!e) return NULL; 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 *text_run, - const struct layout_effective_run *inline_run, UINT32 line) +static float layout_get_line_width(const struct dwrite_textlayout *layout, + const struct layout_effective_run *run, UINT32 line) { FLOAT width = 0.0f; - while (text_run && text_run->line == line) + while (run && run->line == line) { - width += text_run->width; - text_run = layout_get_next_erun(layout, text_run); - if (!text_run) - break; - } - - while (inline_run && inline_run->line == line) - { - width += inline_run->width; - inline_run = layout_get_next_inline_run(layout, inline_run); - if (!inline_run) - break; + width += run->width; + run = layout_get_next_effective_run(layout, run); } return width; @@ -1778,21 +1764,13 @@ static inline void layout_apply_snapping(D2D1_POINT_2F *vec, BOOL skiptransform, static void layout_apply_leading_alignment(struct dwrite_textlayout *layout) { BOOL is_rtl = layout->format.readingdir == DWRITE_READING_DIRECTION_RIGHT_TO_LEFT; - struct layout_effective_run *text_run, *inline_run; - - text_run = layout_get_next_erun(layout, NULL); - inline_run = layout_get_next_inline_run(layout, NULL); - - while (text_run) - { - text_run->align_dx = 0.0f; - text_run = layout_get_next_erun(layout, text_run); - } + struct layout_effective_run *run; - while (inline_run) + run = layout_get_next_effective_run(layout, NULL); + while (run) { - inline_run->align_dx = 0.0f; - inline_run = layout_get_next_inline_run(layout, inline_run); + run->align_dx = 0.0f; + run = layout_get_next_effective_run(layout, run); } layout->metrics.left = is_rtl ? layout->metrics.layoutWidth - layout->metrics.width : 0.0f; @@ -1801,29 +1779,22 @@ 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; - struct layout_effective_run *text_run, *inline_run; + struct layout_effective_run *run; UINT32 line; - text_run = layout_get_next_erun(layout, NULL); - inline_run = layout_get_next_inline_run(layout, NULL); - - for (line = 0; line < layout->metrics.lineCount; line++) { - FLOAT width = layout_get_line_width(layout, text_run, inline_run, line); + 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); FLOAT shift = layout->metrics.layoutWidth - width; if (is_rtl) shift *= -1.0f; - while (text_run && text_run->line == line) - { - text_run->align_dx = shift; - text_run = layout_get_next_erun(layout, text_run); - } - - while (inline_run && inline_run->line == line) + while (run && run->line == line) { - inline_run->align_dx = shift; - inline_run = layout_get_next_inline_run(layout, inline_run); + run->align_dx = shift; + run = layout_get_next_effective_run(layout, run); } } @@ -1845,33 +1816,25 @@ static inline float layout_get_centered_shift(const struct dwrite_textlayout *la 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 *text_run, *inline_run; + struct layout_effective_run *run; BOOL skiptransform; UINT32 line; FLOAT det; - text_run = layout_get_next_erun(layout, NULL); - inline_run = layout_get_next_inline_run(layout, NULL); - 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, text_run, inline_run, line); + float width = layout_get_line_width(layout, run, line); FLOAT shift = layout_get_centered_shift(layout, skiptransform, width, det); if (is_rtl) shift *= -1.0f; - while (text_run && text_run->line == line) + while (run && run->line == line) { - text_run->align_dx = shift; - text_run = layout_get_next_erun(layout, text_run); - } - - while (inline_run && inline_run->line == line) - { - inline_run->align_dx = shift; - inline_run = layout_get_next_inline_run(layout, inline_run); + run->align_dx = shift; + run = layout_get_next_effective_run(layout, run); } } @@ -1901,7 +1864,7 @@ static void layout_apply_text_alignment(struct dwrite_textlayout *layout) static void layout_apply_par_alignment(struct dwrite_textlayout *layout) { - struct layout_effective_run *text_run, *inline_run; + struct layout_effective_run *run; FLOAT origin_y = 0.0f; UINT32 line; @@ -1925,22 +1888,16 @@ static void layout_apply_par_alignment(struct dwrite_textlayout *layout) layout->metrics.top = origin_y; - text_run = layout_get_next_erun(layout, NULL); - inline_run = layout_get_next_inline_run(layout, NULL); + run = layout_get_next_effective_run(layout, NULL); for (line = 0; line < layout->metrics.lineCount; line++) { float pos_y = origin_y + layout->lines[line].metrics.baseline; - while (text_run && text_run->line == line) - { - text_run->origin.y = pos_y; - text_run = layout_get_next_erun(layout, text_run); - } - - while (inline_run && inline_run->line == line) + while (run && run->line == line) { - inline_run->origin.y = pos_y - inline_run->baseline; - inline_run = layout_get_next_inline_run(layout, inline_run); + /* 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; @@ -1952,11 +1909,11 @@ struct layout_underline_splitting_params { IUnknown *effect; /* does not hold another reference */ }; -static void init_u_splitting_params_from_erun(const struct layout_effective_run *erun, +static void init_u_splitting_params_from_text_run(const struct layout_effective_run *run, struct layout_underline_splitting_params *params) { - params->locale = erun->run->u.regular.descr.localeName; - params->effect = erun->effect; + params->locale = run->run->u.regular.descr.localeName; + params->effect = run->effect; } static BOOL is_same_u_splitting(const struct layout_underline_splitting_params *left, @@ -1972,8 +1929,9 @@ static HRESULT layout_add_underline(struct dwrite_textlayout *layout, struct lay struct layout_effective_run *cur; DWRITE_FONT_METRICS metrics; - if (first == layout_get_prev_erun(layout, last)) { - layout_get_erun_font_metrics(layout, first, &metrics); + if (first == layout_get_prev_text_run(layout, last)) + { + layout_get_text_run_font_metrics(layout, first, &metrics); thickness = SCALE_FONT_METRIC(metrics.underlineThickness, first->run->u.regular.run.fontEmSize, &metrics); offset = SCALE_FONT_METRIC(metrics.underlinePosition, first->run->u.regular.run.fontEmSize, &metrics); runheight = SCALE_FONT_METRIC(metrics.capHeight, first->run->u.regular.run.fontEmSize, &metrics); @@ -1985,15 +1943,16 @@ static HRESULT layout_add_underline(struct dwrite_textlayout *layout, struct lay calculated as weighted average, where run width acts as a weight. */ thickness = offset = runheight = 0.0f; cur = first; - do { - layout_get_erun_font_metrics(layout, cur, &metrics); + do + { + layout_get_text_run_font_metrics(layout, cur, &metrics); thickness += SCALE_FONT_METRIC(metrics.underlineThickness, cur->run->u.regular.run.fontEmSize, &metrics) * cur->width; offset += SCALE_FONT_METRIC(metrics.underlinePosition, cur->run->u.regular.run.fontEmSize, &metrics) * cur->width; runheight = max(SCALE_FONT_METRIC(metrics.capHeight, cur->run->u.regular.run.fontEmSize, &metrics), runheight); width += cur->width; - cur = layout_get_next_erun(layout, cur); + cur = layout_get_next_text_run(layout, cur); } while (cur != last); thickness /= width; @@ -2006,9 +1965,10 @@ static HRESULT layout_add_underline(struct dwrite_textlayout *layout, struct lay struct layout_effective_run *next, *w; struct layout_underline *u; - init_u_splitting_params_from_erun(cur, &prev_params); - while ((next = layout_get_next_erun(layout, cur)) != last) { - init_u_splitting_params_from_erun(next, ¶ms); + init_u_splitting_params_from_text_run(cur, &prev_params); + while ((next = layout_get_next_text_run(layout, cur)) != last) + { + init_u_splitting_params_from_text_run(next, ¶ms); if (!is_same_u_splitting(&prev_params, ¶ms)) break; cur = next; @@ -2021,7 +1981,7 @@ static HRESULT layout_add_underline(struct dwrite_textlayout *layout, struct lay u->u.width = 0.0f; while (w != next) { u->u.width += w->width; - w = layout_get_next_erun(layout, w); + w = layout_get_next_text_run(layout, w); } u->u.thickness = thickness; @@ -2231,6 +2191,7 @@ static void layout_add_line(struct dwrite_textlayout *layout, UINT32 first_clust layout->clusters[i].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 */ @@ -2257,30 +2218,21 @@ static void layout_add_line(struct dwrite_textlayout *layout, UINT32 first_clust static void layout_set_line_positions(struct dwrite_textlayout *layout) { - struct layout_effective_run *text_run, *inline_run; + struct layout_effective_run *run; FLOAT origin_y; UINT32 line; /* Now all line info is here, update effective runs positions in flow direction */ - text_run = layout_get_next_erun(layout, NULL); - inline_run = layout_get_next_inline_run(layout, NULL); - + run = layout_get_next_effective_run(layout, NULL); for (line = 0, origin_y = 0.0f; line < layout->metrics.lineCount; line++) { float pos_y = origin_y + layout->lines[line].metrics.baseline; - /* For all runs on this line */ - while (text_run && text_run->line == line) + while (run && run->line == line) { - text_run->origin.y = pos_y; - text_run = layout_get_next_erun(layout, text_run); - } - - /* Same for inline runs */ - while (inline_run && inline_run->line == line) - { - inline_run->origin.y = pos_y - inline_run->baseline; - inline_run = layout_get_next_inline_run(layout, inline_run); + /* 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; @@ -2304,7 +2256,7 @@ static BOOL layout_can_wrap_after(const struct dwrite_textlayout *layout, UINT32 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 *erun, *first_underlined; + struct layout_effective_run *run, *first_underlined; UINT32 i, start, textpos, last_breaking_point; DWRITE_LINE_METRICS1 metrics; FLOAT width; @@ -2314,7 +2266,7 @@ static HRESULT layout_compute_effective_runs(struct dwrite_textlayout *layout) if (!(layout->recompute & RECOMPUTE_LINES)) return S_OK; - free_layout_eruns(layout); + free_layout_effective_runs(layout); hr = layout_compute(layout); if (FAILED(hr)) @@ -2375,18 +2327,21 @@ static HRESULT layout_compute_effective_runs(struct dwrite_textlayout *layout) layout->metrics.maxBidiReorderingDepth = 1; /* FIXME */ /* Add explicit underlined runs */ - erun = layout_get_next_erun(layout, NULL); - first_underlined = erun && erun->underlined ? erun : NULL; - for (line = 0; line < layout->metrics.lineCount; line++) { - while (erun && erun->line == line) { - erun = layout_get_next_erun(layout, erun); + run = layout_get_next_text_run(layout, NULL); + first_underlined = run && run->underlined ? run : NULL; + for (line = 0; line < layout->metrics.lineCount; line++) + { + while (run && run->line == line) + { + run = layout_get_next_text_run(layout, run); - if (first_underlined && (!erun || !erun->underlined)) { - layout_add_underline(layout, first_underlined, erun); + if (first_underlined && (!run || !run->underlined)) + { + layout_add_underline(layout, first_underlined, run); first_underlined = NULL; } - else if (!first_underlined && erun && erun->underlined) - first_underlined = erun; + else if (!first_underlined && run && run->underlined) + first_underlined = run; } } @@ -3119,7 +3074,7 @@ static ULONG WINAPI dwritetextlayout_Release(IDWriteTextLayout4 *iface) if (layout->system_collection) IDWriteFontCollection_Release(layout->system_collection); free_layout_ranges_list(layout); - free_layout_eruns(layout); + free_layout_effective_runs(layout); free_layout_runs(layout); release_format_data(&layout->format); free(layout->nominal_breakpoints); @@ -3771,8 +3726,8 @@ static HRESULT WINAPI dwritetextlayout_Draw(IDWriteTextLayout4 *iface, } #define SNAP_COORD(x) (disabled ? (x) : renderer_apply_snapping((x), skiptransform, ppdip, det, &m)) - /* 1. Regular runs */ - LIST_FOR_EACH_ENTRY(run, &layout->eruns, struct layout_effective_run, draw_entry) + /* 1. Textual runs */ + LIST_FOR_EACH_ENTRY(run, &layout->text_runs, struct layout_effective_run, draw_entry) { const struct regular_layout_run *regular = &run->run->u.regular; UINT32 start_glyph = regular->clustermap[run->start]; @@ -3924,7 +3879,7 @@ static void d2d_rect_union(D2D1_RECT_F *dst, const D2D1_RECT_F *src) } } -static void layout_get_erun_bbox(struct dwrite_textlayout *layout, struct layout_effective_run *run, D2D1_RECT_F *bbox) +static void layout_get_text_run_bbox(struct dwrite_textlayout *layout, struct layout_effective_run *run, D2D1_RECT_F *bbox) { const struct regular_layout_run *regular = &run->run->u.regular; UINT32 start_glyph = regular->clustermap[run->start]; @@ -4029,22 +3984,17 @@ static HRESULT WINAPI dwritetextlayout_GetOverhangMetrics(IDWriteTextLayout4 *if if (FAILED(hr)) return hr; - LIST_FOR_EACH_ENTRY(run, &layout->eruns, struct layout_effective_run, draw_entry) + LIST_FOR_EACH_ENTRY(run, &layout->effective_runs, struct layout_effective_run, entry) { D2D1_RECT_F run_bbox; - layout_get_erun_bbox(layout, run, &run_bbox); + if (run->object) + layout_get_inlineobj_bbox(run, &run_bbox); + else + layout_get_text_run_bbox(layout, run, &run_bbox); d2d_rect_union(&bbox, &run_bbox); } - LIST_FOR_EACH_ENTRY(run, &layout->inlineobjects, struct layout_effective_run, draw_entry) - { - D2D1_RECT_F object_bbox; - - layout_get_inlineobj_bbox(run, &object_bbox); - d2d_rect_union(&bbox, &object_bbox); - } - /* Deltas from layout box. */ layout->overhangs.left = -bbox.left; layout->overhangs.top = -bbox.top; @@ -5416,10 +5366,11 @@ static HRESULT init_textlayout(const struct textlayout_desc *desc, struct dwrite layout->refcount = 1; layout->length = desc->length; layout->recompute = RECOMPUTE_EVERYTHING; - list_init(&layout->eruns); + list_init(&layout->text_runs); list_init(&layout->inlineobjects); list_init(&layout->underlines); list_init(&layout->strikethrough); + list_init(&layout->effective_runs); list_init(&layout->runs); list_init(&layout->ranges); list_init(&layout->strike_ranges); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/9950
participants (2)
-
Nikolay Sivov -
Nikolay Sivov (@nsivov)