[PATCH v3 3/4] d3dx9: Handle tabs in ID3DXFont_DrawText.

Sven Baars sbaars at codeweavers.com
Fri Mar 27 11:31:31 CDT 2020


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

diff --git a/dlls/d3dx9_36/font.c b/dlls/d3dx9_36/font.c
index 54aef71913..4835732931 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,53 +554,104 @@ 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)
+        if (str[i] == '\t')
         {
-            unsigned int chars_used;
+            if (tab_width)
+                current_width = ((current_width / tab_width) + 1) * tab_width;
+
+            --(*count);
+            if (format & DT_EXPANDTABS)
+                dest[(*dest_len)++] = str[i];
+            ++i;
 
-            word_break(hdc, dest, dest_len, num_fit, &chars_used, format, size);
-            *count = orig_count - chars_used;
-            i = chars_used;
+            max_seg_width = width - current_width;
+            if (max_seg_width < 0 && (format & DT_WORDBREAK))
+                break;
+
+            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;
+        max_seg_width = width - current_width;
+        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;
 }
 
-static int compute_rect(struct d3dx_font *font, const WCHAR *string, INT count, WCHAR *line, RECT *rect, DWORD format)
+static int compute_rect(struct d3dx_font *font, const WCHAR *string, INT count,
+        WCHAR *line, RECT *rect, DWORD format, unsigned int tab_width)
 {
     int y, lh, width, top = rect->top;
     int max_width = 0;
@@ -613,7 +665,8 @@ static int compute_rect(struct d3dx_font *font, const WCHAR *string, INT count,
     {
         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 (size.cx > max_width)
             max_width = size.cx;
@@ -660,6 +713,7 @@ static INT WINAPI ID3DXFontImpl_DrawTextW(ID3DXFont *iface, ID3DXSprite *sprite,
     struct d3dx_font *font = impl_from_ID3DXFont(iface);
     int lh, x, y, width, top, ret = 0;
     ID3DXSprite *target = sprite;
+    unsigned int tab_width = 0;
     RECT r = {0};
     WCHAR *line;
     SIZE size;
@@ -682,6 +736,9 @@ static INT WINAPI ID3DXFontImpl_DrawTextW(ID3DXFont *iface, ID3DXSprite *sprite,
     if (format & DT_SINGLELINE)
         format &= ~DT_WORDBREAK;
 
+    if (format & DT_EXPANDTABS)
+        tab_width = font->metrics.tmAveCharWidth * 8;
+
     line = heap_alloc(count * sizeof(*line));
     if (!line)
         return 0;
@@ -701,7 +758,7 @@ static INT WINAPI ID3DXFontImpl_DrawTextW(ID3DXFont *iface, ID3DXSprite *sprite,
 
         top = rect->top;
 
-        ret = compute_rect(font, string, count, line, rect, format);
+        ret = compute_rect(font, string, count, line, rect, format, tab_width);
 
         if (format & DT_CALCRECT)
             goto cleanup;
@@ -721,13 +778,14 @@ static INT WINAPI ID3DXFontImpl_DrawTextW(ID3DXFont *iface, ID3DXSprite *sprite,
         ID3DXSprite_Begin(target, 0);
     }
 
+    /* Iterate over all lines */
     while (string)
     {
-        unsigned int line_len, i;
-        GCP_RESULTSW results;
-        D3DXVECTOR3 pos;
+        const WCHAR *line_seg = line;
+        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_CENTER)
             x = (rect->left + rect->right - size.cx) / 2;
@@ -736,42 +794,73 @@ static INT WINAPI ID3DXFontImpl_DrawTextW(ID3DXFont *iface, ID3DXSprite *sprite,
         else
             x = rect->left;
 
-        memset(&results, 0, sizeof(results));
-        results.nGlyphs = line_len;
+        /* Iterate over all line segments separated by tabs */
+        while (line_len)
+        {
+            unsigned int i, seg_len = line_len;
+            GCP_RESULTSW results;
+            D3DXVECTOR3 pos;
 
-        results.lpCaretPos = heap_alloc(line_len * sizeof(*results.lpCaretPos));
-        if (!results.lpCaretPos)
-            goto cleanup;
+            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;
+            }
 
-        results.lpGlyphs = heap_alloc(line_len * sizeof(*results.lpGlyphs));
-        if (!results.lpGlyphs)
-        {
-            heap_free(results.lpCaretPos);
-            goto cleanup;
-        }
+            memset(&results, 0, sizeof(results));
+            results.nGlyphs = seg_len;
 
-        GetCharacterPlacementW(font->hdc, line, line_len, 0, &results, 0);
+            results.lpCaretPos = heap_alloc(seg_len * sizeof(*results.lpCaretPos));
+            if (!results.lpCaretPos)
+                goto cleanup;
 
-        for (i = 0; i < results.nGlyphs; ++i)
-        {
-            IDirect3DTexture9 *texture;
-            POINT cell_inc;
-            RECT black_box;
+            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);
 
-            ID3DXFont_GetGlyphData(iface, results.lpGlyphs[i], &texture, &black_box, &cell_inc);
+            for (i = 0; i < results.nGlyphs; ++i)
+            {
+                IDirect3DTexture9 *texture;
+                POINT cell_inc;
+                RECT black_box;
 
-            if (!texture)
-                continue;
+                ID3DXFont_GetGlyphData(iface, results.lpGlyphs[i], &texture,
+                                       &black_box, &cell_inc);
 
-            pos.x = cell_inc.x + x + results.lpCaretPos[i];
-            pos.y = cell_inc.y + y;
+                if (!texture)
+                    continue;
 
-            ID3DXSprite_Draw(target, texture, &black_box, NULL, &pos, color);
-            IDirect3DTexture9_Release(texture);
-        }
+                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;
+            }
 
-        heap_free(results.lpCaretPos);
-        heap_free(results.lpGlyphs);
+            heap_free(results.lpCaretPos);
+            heap_free(results.lpGlyphs);
+        }
 
         y += lh;
         if (!(DT_NOCLIP & format) && (y > rect->bottom))
diff --git a/dlls/d3dx9_36/tests/core.c b/dlls/d3dx9_36/tests/core.c
index 020f18e622..9b942e1535 100644
--- a/dlls/d3dx9_36/tests/core.c
+++ b/dlls/d3dx9_36/tests/core.c
@@ -807,10 +807,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);
@@ -819,7 +819,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);
@@ -828,7 +828,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