[PATCH 5/5] d3dx9: Handle tabs in ID3DXFont_DrawText.

Sven Baars sbaars at codeweavers.com
Tue Mar 10 05:21:42 CDT 2020


Signed-off-by: Sven Baars <sbaars at codeweavers.com>
---
 dlls/d3dx9_36/font.c       | 212 +++++++++++++++++++++++++------------
 dlls/d3dx9_36/tests/core.c |   8 +-
 2 files changed, 151 insertions(+), 69 deletions(-)

diff --git a/dlls/d3dx9_36/font.c b/dlls/d3dx9_36/font.c
index 550ff2c0fe..dc0a72c197 100644
--- a/dlls/d3dx9_36/font.c
+++ b/dlls/d3dx9_36/font.c
@@ -512,7 +512,8 @@ static INT WINAPI ID3DXFontImpl_DrawTextA(ID3DXFont *iface, ID3DXSprite *sprite,
 }
 
 static void word_break(HDC hdc, const WCHAR *str, unsigned int *str_len,
-        unsigned int chars_fit, unsigned int *chars_used, DWORD format, SIZE *size)
+        unsigned int chars_fit, unsigned int *chars_used, BOOL line_start,
+        DWORD format, SIZE *size)
 {
     SCRIPT_LOGATTR *sla;
     SCRIPT_ANALYSIS sa;
@@ -535,7 +536,7 @@ static void word_break(HDC hdc, const WCHAR *str, unsigned int *str_len,
         --i;
 
     /* If the there is no word that fits put in all characters that do fit */
-    if (!sla[i].fSoftBreak || (format & DT_SINGLELINE))
+    if ((!sla[i].fSoftBreak && line_start) || (format & DT_SINGLELINE))
         i = chars_fit;
 
     *chars_used = i;
@@ -553,47 +554,95 @@ static void word_break(HDC hdc, const WCHAR *str, unsigned int *str_len,
 }
 
 static const WCHAR *read_line(HDC hdc, const WCHAR *str, int *count,
-        WCHAR *dest, unsigned int *dest_len, int width, DWORD format, SIZE *size)
+        WCHAR *dest, unsigned int *dest_len, int width, unsigned int tab_width,
+        DWORD format, SIZE *size)
 {
+    int num_fit, current_width = 0;
     unsigned int i = 0;
-    int orig_count = *count;
-    int num_fit;
 
+    size->cy = 0;
     *dest_len = 0;
-    while (*count && (str[i] != '\n' || (format & DT_SINGLELINE)))
+    while (*count)
     {
-        --(*count);
-        if (str[i] != '\r' && str[i] != '\n')
-            dest[(*dest_len)++] = str[i];
-        ++i;
-    }
+        unsigned int seg_i, seg_len, prev_dest_len;
+        int seg_count, max_seg_width;
+        BOOL word_broken = FALSE;
+        SIZE seg_size = {0};
 
-    num_fit = 0;
-    GetTextExtentExPointW(hdc, dest, *dest_len, width, &num_fit, NULL, size);
+        /* Create line segments until the next tab. If we don't have to expand */
+        /* tabs, just skip the tab character. */
 
-    if (num_fit < *dest_len)
-    {
-        if (format & DT_WORDBREAK)
+        max_seg_width = width - current_width;
+        if (max_seg_width < 0 && (format & DT_WORDBREAK))
+            break;
+
+        if (str[i] == '\t')
         {
-            unsigned int chars_used;
+            if (tab_width)
+                current_width = ((current_width / tab_width) + 1) * tab_width;
 
-            word_break(hdc, dest, dest_len, num_fit, &chars_used, format, size);
-            *count = orig_count - chars_used;
-            i = chars_used;
+            --(*count);
+            if (format & DT_EXPANDTABS)
+                dest[(*dest_len)++] = str[i];
+            ++i;
+            continue;
         }
-        else if (format & DT_SINGLELINE)
+
+        seg_i = i;
+        seg_count = *count;
+        prev_dest_len = *dest_len;
+
+        while (*count && (str[i] != '\n' || (format & DT_SINGLELINE)) && str[i] != '\t')
         {
-            *dest_len = num_fit;
-            *count = 0;
+            --(*count);
+            if (str[i] != '\r' && str[i] != '\n')
+                dest[(*dest_len)++] = str[i];
+            ++i;
         }
-    }
 
-    if (*count && str[i] == '\n')
-    {
-        --(*count);
-        ++i;
+        num_fit = 0;
+        seg_len = *(dest_len) - prev_dest_len;
+        GetTextExtentExPointW(hdc, dest + prev_dest_len, seg_len, max_seg_width,
+                              &num_fit, NULL, &seg_size);
+
+        if (num_fit < seg_len)
+        {
+            if (format & DT_WORDBREAK)
+            {
+                unsigned int chars_used;
+
+                word_break(hdc, dest + prev_dest_len, &seg_len, num_fit, &chars_used,
+                           !current_width, format, &seg_size);
+                *count = seg_count - chars_used;
+                i = seg_i + chars_used;
+                word_broken = TRUE;
+            }
+            else if (format & DT_SINGLELINE)
+            {
+                seg_len = num_fit;
+                *count = 0;
+                word_broken = TRUE;
+            }
+            *dest_len = prev_dest_len + seg_len;
+        }
+
+        current_width += seg_size.cx;
+        if (seg_size.cy > size->cy)
+            size->cy = seg_size.cy;
+
+        if (word_broken)
+        {
+            break;
+        }
+        else if (*count && str[i] == '\n')
+        {
+            --(*count);
+            ++i;
+            break;
+        }
     }
 
+    size->cx = current_width;
     if (*count)
         return str + i;
     return NULL;
@@ -605,6 +654,7 @@ static INT WINAPI ID3DXFontImpl_DrawTextW(ID3DXFont *iface, ID3DXSprite *sprite,
     struct d3dx_font *font = impl_from_ID3DXFont(iface);
     RECT calcrect, textrect = {0};
     ID3DXSprite *target = sprite;
+    unsigned int tab_width = 0;
     int lh, x, y, width;
     int max_width = 0;
     int ret = 0;
@@ -669,6 +719,9 @@ static INT WINAPI ID3DXFontImpl_DrawTextW(ID3DXFont *iface, ID3DXSprite *sprite,
     if (!line)
         return 0;
 
+    if (format & DT_EXPANDTABS)
+        tab_width = font->metrics.tmAveCharWidth * 8;
+
     if (!(format & DT_CALCRECT) && !sprite)
     {
         D3DXCreateSprite(font->device, &target);
@@ -679,13 +732,12 @@ static INT WINAPI ID3DXFontImpl_DrawTextW(ID3DXFont *iface, ID3DXSprite *sprite,
     {
         unsigned int line_len;
 
-        string = read_line(font->hdc, string, &count, line, &line_len, width, format, &size);
+        string = read_line(font->hdc, string, &count, line, &line_len,
+                           width, tab_width, format, &size);
 
         if (!(format & DT_CALCRECT))
         {
-            GCP_RESULTSW results;
-            D3DXVECTOR3 pos;
-            unsigned int i;
+            const WCHAR *line_seg = line;
 
             if (format & DT_CENTER)
                 x = (calcrect.left + calcrect.right - size.cx) / 2;
@@ -694,42 +746,72 @@ static INT WINAPI ID3DXFontImpl_DrawTextW(ID3DXFont *iface, ID3DXSprite *sprite,
             else
                 x = calcrect.left;
 
-            memset(&results, 0, sizeof(results));
-            results.nGlyphs = line_len;
-
-            results.lpCaretPos = heap_alloc(line_len * sizeof(*results.lpCaretPos));
-            if (!results.lpCaretPos)
-                goto cleanup;
-
-            results.lpGlyphs = heap_alloc(line_len * sizeof(*results.lpGlyphs));
-            if (!results.lpGlyphs)
+            while (line_len)
             {
-                heap_free(results.lpCaretPos);
-                goto cleanup;
-            }
-
-            GetCharacterPlacementW(font->hdc, line, line_len, 0, &results, 0);
-
-            for (i = 0; i < results.nGlyphs; ++i)
-            {
-                IDirect3DTexture9 *texture;
-                POINT cell_inc;
-                RECT black_box;
-
-                ID3DXFont_GetGlyphData(iface, results.lpGlyphs[i], &texture, &black_box, &cell_inc);
+                unsigned int i, seg_len = line_len;
+                GCP_RESULTSW results;
+                D3DXVECTOR3 pos;
+
+                if (format & DT_EXPANDTABS)
+                {
+                    /* Iterate until the next tab to create a line segment */
+                    i = 0;
+                    while (i < line_len && line_seg[i] != '\t')
+                        ++i;
+                    seg_len = i;
+                    if (seg_len != line_len && !GetTextExtentPointW(
+                            font->hdc, line_seg, seg_len, &size))
+                        goto cleanup;
+                }
+
+                memset(&results, 0, sizeof(results));
+                results.nGlyphs = seg_len;
+
+                results.lpCaretPos = heap_alloc(seg_len * sizeof(*results.lpCaretPos));
+                if (!results.lpCaretPos)
+                    goto cleanup;
+
+                results.lpGlyphs = heap_alloc(seg_len * sizeof(*results.lpGlyphs));
+                if (!results.lpGlyphs)
+                {
+                    heap_free(results.lpCaretPos);
+                    goto cleanup;
+                }
+
+                GetCharacterPlacementW(font->hdc, line_seg, seg_len, 0, &results, 0);
+
+                for (i = 0; i < results.nGlyphs; ++i)
+                {
+                    IDirect3DTexture9 *texture;
+                    POINT cell_inc;
+                    RECT black_box;
+
+                    ID3DXFont_GetGlyphData(iface, results.lpGlyphs[i], &texture,
+                                           &black_box, &cell_inc);
+
+                    if (!texture)
+                        continue;
+
+                    pos.x = cell_inc.x + x + results.lpCaretPos[i];
+                    pos.y = cell_inc.y + y;
+
+                    ID3DXSprite_Draw(target, texture, &black_box, NULL, &pos, color);
+                    IDirect3DTexture9_Release(texture);
+                }
+
+                line_len -= seg_len;
+                line_seg += seg_len;
+                if (line_len)
+                {
+                    /* If there is anything left, this character is a tab */
+                    --line_len;
+                    ++line_seg;
+                    x += ((size.cx / tab_width) + 1) * tab_width;
+                }
 
-                if (!texture)
-                    continue;
-
-                pos.x = cell_inc.x + x + results.lpCaretPos[i];
-                pos.y = cell_inc.y + y;
-
-                ID3DXSprite_Draw(target, texture, &black_box, NULL, &pos, color);
-                IDirect3DTexture9_Release(texture);
+                heap_free(results.lpCaretPos);
+                heap_free(results.lpGlyphs);
             }
-
-            heap_free(results.lpCaretPos);
-            heap_free(results.lpGlyphs);
         }
         else if (size.cx > max_width)
         {
diff --git a/dlls/d3dx9_36/tests/core.c b/dlls/d3dx9_36/tests/core.c
index c2244b1a97..65d4c66708 100644
--- a/dlls/d3dx9_36/tests/core.c
+++ b/dlls/d3dx9_36/tests/core.c
@@ -798,10 +798,10 @@ static void test_ID3DXFont(IDirect3DDevice9 *device)
     todo_wine ok(height == 0, "Got unexpected height %d.\n", height);
 
     height = ID3DXFont_DrawTextW(font, NULL, L"\t\t\t\t\t\t\t\t\t\ta", -1, &rect, DT_WORDBREAK, 0xff00ff);
-    todo_wine ok(height == 12, "Got unexpected height %d.\n", height);
+    ok(height == 12, "Got unexpected height %d.\n", height);
 
     height = ID3DXFont_DrawTextW(font, NULL, L"\taaaaaaaaaa", -1, &rect, DT_WORDBREAK, 0xff00ff);
-    todo_wine ok(height == 24, "Got unexpected height %d.\n", height);
+    ok(height == 24, "Got unexpected height %d.\n", height);
 
     height = ID3DXFont_DrawTextW(font, NULL, L"\taaaaaaaaaa", -1, &rect, DT_EXPANDTABS | DT_WORDBREAK, 0xff00ff);
     ok(height == 36, "Got unexpected height %d.\n", height);
@@ -810,7 +810,7 @@ static void test_ID3DXFont(IDirect3DDevice9 *device)
     ok(height == 24, "Got unexpected height %d.\n", height);
 
     height = ID3DXFont_DrawTextW(font, NULL, L"\taaa\taaa\taaa", -1, &rect, DT_EXPANDTABS | DT_WORDBREAK, 0xff00ff);
-    todo_wine ok(height == 48, "Got unexpected height %d.\n", height);
+    ok(height == 48, "Got unexpected height %d.\n", height);
 
     height = ID3DXFont_DrawTextW(font, NULL, L"\t\t\t\t\t\t\t\t\t\t", -1, &rect, DT_EXPANDTABS | DT_WORDBREAK, 0xff00ff);
     todo_wine ok(height == 60, "Got unexpected height %d.\n", height);
@@ -819,7 +819,7 @@ static void test_ID3DXFont(IDirect3DDevice9 *device)
     ok(height == 12, "Got unexpected height %d.\n", height);
 
     height = ID3DXFont_DrawTextW(font, NULL, L"a\ta\ta", -1, &rect, DT_EXPANDTABS | DT_WORDBREAK, 0xff00ff);
-    todo_wine ok(height == 24, "Got unexpected height %d.\n", height);
+    ok(height == 24, "Got unexpected height %d.\n", height);
 
     height = ID3DXFont_DrawTextW(font, NULL, L"aaaaaaaaaaaaaaaaaaaa", -1, &rect, DT_WORDBREAK, 0xff00ff);
     ok(height == 36, "Got unexpected height %d.\n", height);
-- 
2.24.0




More information about the wine-devel mailing list