From 1ca454f0d2c58d4bc431c4f2e48a18d0deaae840 Mon Sep 17 00:00:00 2001 From: skyline75489 Date: Tue, 26 May 2020 22:22:35 +0800 Subject: [PATCH 1/6] Skip glyph shaping analysis when the entire text is simple --- src/renderer/dx/CustomTextLayout.cpp | 60 ++++++++++++++++++++++++---- src/renderer/dx/CustomTextLayout.h | 6 +++ 2 files changed, 58 insertions(+), 8 deletions(-) diff --git a/src/renderer/dx/CustomTextLayout.cpp b/src/renderer/dx/CustomTextLayout.cpp index 7593e17d7de..11f40da8428 100644 --- a/src/renderer/dx/CustomTextLayout.cpp +++ b/src/renderer/dx/CustomTextLayout.cpp @@ -63,6 +63,19 @@ CustomTextLayout::CustomTextLayout(gsl::not_null const factory _text += text; } + + const auto textLength = gsl::narrow(_text.size()); + + BOOL isTextSimple = FALSE; + UINT32 uiLengthRead = 0; + UINT32 glyphStart = 0; + + HRESULT hr = S_OK; + + _glyphIndices.resize(textLength); + + hr = _analyzer->GetTextComplexity(_text.c_str(), textLength, _font.Get(), &isTextSimple, &uiLengthRead, &_glyphIndices.at(glyphStart)); + _isEntireTextSimple = isTextSimple && uiLengthRead == textLength; } // Routine Description: @@ -149,11 +162,7 @@ CustomTextLayout::CustomTextLayout(gsl::not_null const factory // Allocate enough room to have one breakpoint per code unit. _breakpoints.resize(_text.size()); - BOOL isTextSimple = FALSE; - UINT32 uiLengthRead = 0; - RETURN_IF_FAILED(_analyzer->GetTextComplexity(_text.c_str(), textLength, _font.Get(), &isTextSimple, &uiLengthRead, NULL)); - - if (!(isTextSimple && uiLengthRead == _text.size())) + if (!_isEntireTextSimple) { // Call each of the analyzers in sequence, recording their results. RETURN_IF_FAILED(_analyzer->AnalyzeLineBreakpoints(this, 0, textLength, this)); @@ -274,6 +283,39 @@ CustomTextLayout::CustomTextLayout(gsl::not_null const factory _glyphIndices.resize(totalGlyphsArrayCount); } + if (_isEntireTextSimple) + { + // When the entire text is simple, we can skip GetGlyphs and directly retrieve glyph indices and + // advances(in font design unit). With the help of font metrics, we can calculate the actual glyph + // advances without the need of GetGlyphPlacements. This shortcut will significantly reduce the time + // needed for text analysis. + DWRITE_FONT_METRICS1 metrics; + run.fontFace->GetMetrics(&metrics); + + // With simple text, there's only one run. The actual glyph count is the same as textLength. + _glyphDesignUnitAdvances.resize(textLength); + _glyphAdvances.resize(textLength); + _glyphOffsets.resize(textLength); + + USHORT designUnitsPerEm = metrics.designUnitsPerEm; + + RETURN_IF_FAILED(_font->GetDesignGlyphAdvances( + textLength, + &_glyphIndices.at(glyphStart), + &_glyphDesignUnitAdvances.at(glyphStart), + run.isSideways)); + + for (size_t i = glyphStart; i < _glyphAdvances.size(); i++) + { + _glyphAdvances.at(i) = (float)_glyphDesignUnitAdvances.at(i) / designUnitsPerEm * _format->GetFontSize() * run.fontScale; + } + + run.glyphCount = textLength; + glyphStart += textLength; + + return S_OK; + } + std::vector textProps(textLength); std::vector glyphProps(maxGlyphCount); @@ -369,6 +411,11 @@ CustomTextLayout::CustomTextLayout(gsl::not_null const factory // - S_OK or suitable DirectWrite or STL error code [[nodiscard]] HRESULT CustomTextLayout::_CorrectGlyphRuns() noexcept { + if (_isEntireTextSimple) + { + return S_OK; + } + try { // Correct each run separately. This is needed whenever script, locale, @@ -508,9 +555,6 @@ try // We're going to walk through and check for advances that don't match the space that we expect to give out. - DWRITE_FONT_METRICS1 metrics; - run.fontFace->GetMetrics(&metrics); - // Glyph Indices represents the number inside the selected font where the glyph image/paths are found. // Text represents the original text we gave in. // Glyph Clusters represents the map between Text and Glyph Indices. diff --git a/src/renderer/dx/CustomTextLayout.h b/src/renderer/dx/CustomTextLayout.h index 37097227416..064a37f8581 100644 --- a/src/renderer/dx/CustomTextLayout.h +++ b/src/renderer/dx/CustomTextLayout.h @@ -180,6 +180,9 @@ namespace Microsoft::Console::Render // Glyph shaping results + // Whether the entire text is determined to be simple and does not require full script shaping. + bool _isEntireTextSimple; + std::vector _glyphOffsets; // Clusters are complicated. They're in respect to each individual run. @@ -191,6 +194,9 @@ namespace Microsoft::Console::Render // This appears to be the index of the glyph inside each font. std::vector _glyphIndices; + // This is used when the entire text is simple. + std::vector _glyphDesignUnitAdvances; + std::vector _glyphAdvances; struct ScaleCorrection From fa789b34f3952bb93fc7b76f59e7fffc1d3f71b1 Mon Sep 17 00:00:00 2001 From: skyline75489 Date: Wed, 27 May 2020 19:40:32 +0800 Subject: [PATCH 2/6] Resolve comments --- src/renderer/dx/CustomTextLayout.cpp | 15 ++++++++++----- src/renderer/dx/CustomTextLayout.h | 2 +- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/src/renderer/dx/CustomTextLayout.cpp b/src/renderer/dx/CustomTextLayout.cpp index 11f40da8428..6261e71e971 100644 --- a/src/renderer/dx/CustomTextLayout.cpp +++ b/src/renderer/dx/CustomTextLayout.cpp @@ -68,14 +68,19 @@ CustomTextLayout::CustomTextLayout(gsl::not_null const factory BOOL isTextSimple = FALSE; UINT32 uiLengthRead = 0; - UINT32 glyphStart = 0; - - HRESULT hr = S_OK; + const UINT32 glyphStart = 0; _glyphIndices.resize(textLength); - hr = _analyzer->GetTextComplexity(_text.c_str(), textLength, _font.Get(), &isTextSimple, &uiLengthRead, &_glyphIndices.at(glyphStart)); - _isEntireTextSimple = isTextSimple && uiLengthRead == textLength; + HRESULT hr = _analyzer->GetTextComplexity( + _text.c_str(), + textLength, + _font.Get(), + &isTextSimple, + &uiLengthRead, + &_glyphIndices.at(glyphStart)); + + _isEntireTextSimple = SUCCEEDED(hr) && isTextSimple && uiLengthRead == textLength; } // Routine Description: diff --git a/src/renderer/dx/CustomTextLayout.h b/src/renderer/dx/CustomTextLayout.h index 064a37f8581..ee65d3564ff 100644 --- a/src/renderer/dx/CustomTextLayout.h +++ b/src/renderer/dx/CustomTextLayout.h @@ -194,7 +194,7 @@ namespace Microsoft::Console::Render // This appears to be the index of the glyph inside each font. std::vector _glyphIndices; - // This is used when the entire text is simple. + // This is for calculating glyph advances when the entire text is simple. std::vector _glyphDesignUnitAdvances; std::vector _glyphAdvances; From 44436527de4e3f9e2c0ee31e47e89876e0fd10bd Mon Sep 17 00:00:00 2001 From: skyline75489 Date: Wed, 27 May 2020 22:12:47 +0800 Subject: [PATCH 3/6] Oh Mr.Static --- src/renderer/dx/CustomTextLayout.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/renderer/dx/CustomTextLayout.cpp b/src/renderer/dx/CustomTextLayout.cpp index 6261e71e971..13ddfc5f8e8 100644 --- a/src/renderer/dx/CustomTextLayout.cpp +++ b/src/renderer/dx/CustomTextLayout.cpp @@ -72,7 +72,7 @@ CustomTextLayout::CustomTextLayout(gsl::not_null const factory _glyphIndices.resize(textLength); - HRESULT hr = _analyzer->GetTextComplexity( + const HRESULT hr = _analyzer->GetTextComplexity( _text.c_str(), textLength, _font.Get(), From 5b2d4cb1ab0a817fd5593100663af8de4c1af98d Mon Sep 17 00:00:00 2001 From: skyline75489 Date: Thu, 28 May 2020 21:36:34 +0800 Subject: [PATCH 4/6] Resolve comments --- src/renderer/dx/CustomTextLayout.cpp | 69 ++++++++++++++++++---------- src/renderer/dx/CustomTextLayout.h | 1 + 2 files changed, 47 insertions(+), 23 deletions(-) diff --git a/src/renderer/dx/CustomTextLayout.cpp b/src/renderer/dx/CustomTextLayout.cpp index 13ddfc5f8e8..c06cc62b2e5 100644 --- a/src/renderer/dx/CustomTextLayout.cpp +++ b/src/renderer/dx/CustomTextLayout.cpp @@ -63,24 +63,6 @@ CustomTextLayout::CustomTextLayout(gsl::not_null const factory _text += text; } - - const auto textLength = gsl::narrow(_text.size()); - - BOOL isTextSimple = FALSE; - UINT32 uiLengthRead = 0; - const UINT32 glyphStart = 0; - - _glyphIndices.resize(textLength); - - const HRESULT hr = _analyzer->GetTextComplexity( - _text.c_str(), - textLength, - _font.Get(), - &isTextSimple, - &uiLengthRead, - &_glyphIndices.at(glyphStart)); - - _isEntireTextSimple = SUCCEEDED(hr) && isTextSimple && uiLengthRead == textLength; } // Routine Description: @@ -94,6 +76,7 @@ CustomTextLayout::CustomTextLayout(gsl::not_null const factory RETURN_HR_IF_NULL(E_INVALIDARG, columns); *columns = 0; + RETURN_IF_FAILED(_AnalyzeTextComplexity()); RETURN_IF_FAILED(_AnalyzeRuns()); RETURN_IF_FAILED(_ShapeGlyphRuns()); @@ -124,6 +107,7 @@ CustomTextLayout::CustomTextLayout(gsl::not_null const factory FLOAT originX, FLOAT originY) noexcept { + RETURN_IF_FAILED(_AnalyzeTextComplexity()); RETURN_IF_FAILED(_AnalyzeRuns()); RETURN_IF_FAILED(_ShapeGlyphRuns()); RETURN_IF_FAILED(_CorrectGlyphRuns()); @@ -137,6 +121,44 @@ CustomTextLayout::CustomTextLayout(gsl::not_null const factory return S_OK; } +// Routine Description: +// - Uses the internal text information and the analyzers/font information from construction +// to determine the complexity of the text. If the text is determined to be entirely simple, +// we'll have more chances to optimize the layout process. +// Arguments: +// - - Uses internal state +// Return Value: +// - S_OK or suitable DirectWrite or STL error code +[[nodiscard]] HRESULT CustomTextLayout::_AnalyzeTextComplexity() noexcept +{ + try + { + const auto textLength = gsl::narrow(_text.size()); + + BOOL isTextSimple = FALSE; + UINT32 uiLengthRead = 0; + + // Start from the beginning. + const UINT32 glyphStart = 0; + + _glyphIndices.resize(textLength); + + const HRESULT hr = _analyzer->GetTextComplexity( + _text.c_str(), + textLength, + _font.Get(), + &isTextSimple, + &uiLengthRead, + &_glyphIndices.at(glyphStart)); + + RETURN_IF_FAILED(hr); + + _isEntireTextSimple = isTextSimple && uiLengthRead == textLength; + } + CATCH_RETURN(); + return S_OK; +} + // Routine Description: // - Uses the internal text information and the analyzers/font information from construction // to determine the complexity of the text inside this layout, compute the subsections (or runs) @@ -416,13 +438,14 @@ CustomTextLayout::CustomTextLayout(gsl::not_null const factory // - S_OK or suitable DirectWrite or STL error code [[nodiscard]] HRESULT CustomTextLayout::_CorrectGlyphRuns() noexcept { - if (_isEntireTextSimple) - { - return S_OK; - } - try { + // For simple text, there is no need to correct runs. + if (_isEntireTextSimple) + { + return S_OK; + } + // Correct each run separately. This is needed whenever script, locale, // or reading direction changes. for (UINT32 runIndex = 0; runIndex < _runs.size(); ++runIndex) diff --git a/src/renderer/dx/CustomTextLayout.h b/src/renderer/dx/CustomTextLayout.h index ee65d3564ff..f3916419dcc 100644 --- a/src/renderer/dx/CustomTextLayout.h +++ b/src/renderer/dx/CustomTextLayout.h @@ -134,6 +134,7 @@ namespace Microsoft::Console::Render [[nodiscard]] HRESULT STDMETHODCALLTYPE _AnalyzeBoxDrawing(gsl::not_null const source, UINT32 textPosition, UINT32 textLength); [[nodiscard]] HRESULT STDMETHODCALLTYPE _SetBoxEffect(UINT32 textPosition, UINT32 textLength); + [[nodiscard]] HRESULT _AnalyzeTextComplexity() noexcept; [[nodiscard]] HRESULT _AnalyzeRuns() noexcept; [[nodiscard]] HRESULT _ShapeGlyphRuns() noexcept; [[nodiscard]] HRESULT _ShapeGlyphRun(const UINT32 runIndex, UINT32& glyphStart) noexcept; From b450173786271edcb924337b7b2a1a42f0a09fae Mon Sep 17 00:00:00 2001 From: Chester Liu Date: Fri, 29 May 2020 10:13:57 +0800 Subject: [PATCH 5/6] Static --- src/renderer/dx/CustomTextLayout.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/renderer/dx/CustomTextLayout.cpp b/src/renderer/dx/CustomTextLayout.cpp index c06cc62b2e5..f7bc6b00986 100644 --- a/src/renderer/dx/CustomTextLayout.cpp +++ b/src/renderer/dx/CustomTextLayout.cpp @@ -41,7 +41,8 @@ CustomTextLayout::CustomTextLayout(gsl::not_null const factory _runs{}, _breakpoints{}, _runIndex{ 0 }, - _width{ width } + _width{ width }, + _isEntireTextSimple { false } { // Fetch the locale name out once now from the format _localeName.resize(gsl::narrow_cast(format->GetLocaleNameLength()) + 1); // +1 for null From 5046746d656d5eba7fbd576d9020cd160f65d949 Mon Sep 17 00:00:00 2001 From: Chester Liu Date: Fri, 29 May 2020 10:44:17 +0800 Subject: [PATCH 6/6] Format --- src/renderer/dx/CustomTextLayout.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/renderer/dx/CustomTextLayout.cpp b/src/renderer/dx/CustomTextLayout.cpp index f7bc6b00986..d19d2d0c338 100644 --- a/src/renderer/dx/CustomTextLayout.cpp +++ b/src/renderer/dx/CustomTextLayout.cpp @@ -42,7 +42,7 @@ CustomTextLayout::CustomTextLayout(gsl::not_null const factory _breakpoints{}, _runIndex{ 0 }, _width{ width }, - _isEntireTextSimple { false } + _isEntireTextSimple{ false } { // Fetch the locale name out once now from the format _localeName.resize(gsl::narrow_cast(format->GetLocaleNameLength()) + 1); // +1 for null