Handle things above U+FFFF in GDI renderer #14292

Open
opened 2026-01-31 04:06:24 +00:00 by claunia · 0 comments
Owner

Originally created by @alabuzhev on GitHub (Jun 21, 2021).

Why not let Windows draw surrogate pairs? It can do that.

Basically, the comment says everything:
c90de69250/src/renderer/gdi/paint.cpp (L346-L347)

However, handling things above U+FFFF doesn't really require extra effort.

Proposed technical implementation details (optional)

  • Put all characters to the output buffer
  • Set the first width to cluster width and the rest to 0
  • Sit back and relax while Windows does the rest
diff --git a/src/renderer/gdi/paint.cpp b/src/renderer/gdi/paint.cpp
index 9295d3d6..e436b156 100644
--- a/src/renderer/gdi/paint.cpp
+++ b/src/renderer/gdi/paint.cpp
@@ -328,11 +328,13 @@ using namespace Microsoft::Console::Render;
 
         const auto pPolyTextLine = &_pPolyText[_cPolyText];
 
-        auto& polyString = _polyStrings.emplace_back(cchLine, UNICODE_NULL);
+        auto& polyString = _polyStrings.emplace_back();
+        polyString.reserve(cchLine);
 
         COORD const coordFontSize = _GetFontSize();
 
-        auto& polyWidth = _polyWidths.emplace_back(cchLine, 0);
+        auto& polyWidth = _polyWidths.emplace_back();
+        polyWidth.reserve(cchLine);
 
         // Sum up the total widths the entire line/run is expected to take while
         // copying the pixel widths into a structure to direct GDI how many pixels to use per character.
@@ -343,11 +345,11 @@ using namespace Microsoft::Console::Render;
         {
             const auto& cluster = til::at(clusters, i);
 
-            // Our GDI renderer hasn't and isn't going to handle things above U+FFFF or sequences.
-            // So replace anything complicated with a replacement character for drawing purposes.
-            polyString[i] = cluster.GetTextAsSingle();
-            polyWidth[i] = gsl::narrow<int>(cluster.GetColumns()) * coordFontSize.X;
-            cchCharWidths += polyWidth[i];
+            const auto text = cluster.GetText();
+            polyString += text;
+            polyWidth.push_back(gsl::narrow<int>(cluster.GetColumns()) * coordFontSize.X);
+            cchCharWidths += polyWidth.back();
+            polyWidth.append(text.size() - 1, 0);
         }
 
         // Detect and convert for raster font...
@@ -396,7 +398,7 @@ using namespace Microsoft::Console::Render;
         const auto bottomOffset = _currentLineRendition == LineRendition::DoubleHeightTop ? halfHeight : 0;
 
         pPolyTextLine->lpstr = polyString.data();
-        pPolyTextLine->n = gsl::narrow<UINT>(clusters.size());
+        pPolyTextLine->n = gsl::narrow<UINT>(polyString.size());
         pPolyTextLine->x = ptDraw.x;
         pPolyTextLine->y = ptDraw.y;
         pPolyTextLine->uiFlags = ETO_OPAQUE | ETO_CLIPPED;
@@ -436,10 +438,23 @@ using namespace Microsoft::Console::Render;
 
     if (_cPolyText > 0)
     {
+#if 1
+        assert(_cPolyText == 1);
+        for (size_t i = 0; i != _cPolyText; ++i)
+        {
+            const auto& t = _pPolyText[i];
+            if (!ExtTextOutW(_hdcMemoryContext, t.x, t.y, t.uiFlags, &t.rcl, t.lpstr, t.n, t.pdx))
+            {
+                hr = E_FAIL;
+                break;
+            }
+        }
+#else
         if (!PolyTextOutW(_hdcMemoryContext, _pPolyText, (UINT)_cPolyText))
         {
             hr = E_FAIL;
         }
+#endif
 
         _polyStrings.clear();
         _polyWidths.clear();
  • This patch also includes PolyTextOutW -> ExtTextOutW replacement, discussed in #10472 - it's needed to show these glyphs in the first place.

Testing

@echo off
chcp 65001

echo 𠜎𠝹𠱓𠱸𠲖𠳏𠳕𠴕𠵼𠵿𠸎
echo 👨👩👧👦

Save this as a UTF-8 cmd file and run.

Before the change

image

After the change

image

Originally created by @alabuzhev on GitHub (Jun 21, 2021). # Why not let Windows draw surrogate pairs? It can do that. Basically, the comment says everything: https://github.com/microsoft/terminal/blob/c90de692509b074bfde191910d67154cfe389911/src/renderer/gdi/paint.cpp#L346-L347 However, handling things above U+FFFF doesn't really require extra effort. # Proposed technical implementation details (optional) - Put *all* characters to the output buffer - Set the first width to cluster width and the rest to 0 - Sit back and relax while Windows does the rest ```DIFF diff --git a/src/renderer/gdi/paint.cpp b/src/renderer/gdi/paint.cpp index 9295d3d6..e436b156 100644 --- a/src/renderer/gdi/paint.cpp +++ b/src/renderer/gdi/paint.cpp @@ -328,11 +328,13 @@ using namespace Microsoft::Console::Render; const auto pPolyTextLine = &_pPolyText[_cPolyText]; - auto& polyString = _polyStrings.emplace_back(cchLine, UNICODE_NULL); + auto& polyString = _polyStrings.emplace_back(); + polyString.reserve(cchLine); COORD const coordFontSize = _GetFontSize(); - auto& polyWidth = _polyWidths.emplace_back(cchLine, 0); + auto& polyWidth = _polyWidths.emplace_back(); + polyWidth.reserve(cchLine); // Sum up the total widths the entire line/run is expected to take while // copying the pixel widths into a structure to direct GDI how many pixels to use per character. @@ -343,11 +345,11 @@ using namespace Microsoft::Console::Render; { const auto& cluster = til::at(clusters, i); - // Our GDI renderer hasn't and isn't going to handle things above U+FFFF or sequences. - // So replace anything complicated with a replacement character for drawing purposes. - polyString[i] = cluster.GetTextAsSingle(); - polyWidth[i] = gsl::narrow<int>(cluster.GetColumns()) * coordFontSize.X; - cchCharWidths += polyWidth[i]; + const auto text = cluster.GetText(); + polyString += text; + polyWidth.push_back(gsl::narrow<int>(cluster.GetColumns()) * coordFontSize.X); + cchCharWidths += polyWidth.back(); + polyWidth.append(text.size() - 1, 0); } // Detect and convert for raster font... @@ -396,7 +398,7 @@ using namespace Microsoft::Console::Render; const auto bottomOffset = _currentLineRendition == LineRendition::DoubleHeightTop ? halfHeight : 0; pPolyTextLine->lpstr = polyString.data(); - pPolyTextLine->n = gsl::narrow<UINT>(clusters.size()); + pPolyTextLine->n = gsl::narrow<UINT>(polyString.size()); pPolyTextLine->x = ptDraw.x; pPolyTextLine->y = ptDraw.y; pPolyTextLine->uiFlags = ETO_OPAQUE | ETO_CLIPPED; @@ -436,10 +438,23 @@ using namespace Microsoft::Console::Render; if (_cPolyText > 0) { +#if 1 + assert(_cPolyText == 1); + for (size_t i = 0; i != _cPolyText; ++i) + { + const auto& t = _pPolyText[i]; + if (!ExtTextOutW(_hdcMemoryContext, t.x, t.y, t.uiFlags, &t.rcl, t.lpstr, t.n, t.pdx)) + { + hr = E_FAIL; + break; + } + } +#else if (!PolyTextOutW(_hdcMemoryContext, _pPolyText, (UINT)_cPolyText)) { hr = E_FAIL; } +#endif _polyStrings.clear(); _polyWidths.clear(); ``` - This patch also includes PolyTextOutW -> ExtTextOutW replacement, discussed in #10472 - it's needed to show these glyphs in the first place. # Testing ```CMD @echo off chcp 65001 echo 𠜎𠝹𠱓𠱸𠲖𠳏𠳕𠴕𠵼𠵿𠸎 echo 👨👩👧👦 ``` Save this as a UTF-8 cmd file and run. # Before the change ![image](https://user-images.githubusercontent.com/11453922/122832196-ed438880-d2e2-11eb-93dd-931954efedbf.png) # After the change ![image](https://user-images.githubusercontent.com/11453922/122832217-f2a0d300-d2e2-11eb-99f0-e129e5544667.png)
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: starred/terminal#14292