Nikolay Sivov : dwrite: Implement DetermineMinWidth() using line breaking info.

Alexandre Julliard julliard at wine.codeweavers.com
Tue Jan 26 10:32:51 CST 2016


Module: wine
Branch: master
Commit: f27b99ec2f4a7cade70e51a5a3e0193ff7499d4c
URL:    http://source.winehq.org/git/wine.git/?a=commit;h=f27b99ec2f4a7cade70e51a5a3e0193ff7499d4c

Author: Nikolay Sivov <nsivov at codeweavers.com>
Date:   Tue Jan 26 15:44:48 2016 +0300

dwrite: Implement DetermineMinWidth() using line breaking info.

Signed-off-by: Nikolay Sivov <nsivov at codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard at winehq.org>

---

 dlls/dwrite/layout.c       | 51 +++++++++++++++++++++++-----------------------
 dlls/dwrite/tests/layout.c | 42 +++++++++++++++++++++++++++++++++-----
 2 files changed, 62 insertions(+), 31 deletions(-)

diff --git a/dlls/dwrite/layout.c b/dlls/dwrite/layout.c
index da08b39..0300166 100644
--- a/dlls/dwrite/layout.c
+++ b/dlls/dwrite/layout.c
@@ -3229,23 +3229,12 @@ static HRESULT WINAPI dwritetextlayout_GetClusterMetrics(IDWriteTextLayout2 *ifa
     return max_count >= This->cluster_count ? S_OK : E_NOT_SUFFICIENT_BUFFER;
 }
 
-/* Only to be used with DetermineMinWidth() to find the longest cluster sequence that we don't want to try
-   too hard to break. */
-static inline BOOL is_terminal_cluster(struct dwrite_textlayout *layout, UINT32 index)
-{
-    if (layout->clustermetrics[index].isWhitespace || layout->clustermetrics[index].isNewline ||
-       (index == layout->cluster_count - 1))
-        return TRUE;
-    /* check next one */
-    return (index < layout->cluster_count - 1) && layout->clustermetrics[index+1].isWhitespace;
-}
-
 static HRESULT WINAPI dwritetextlayout_DetermineMinWidth(IDWriteTextLayout2 *iface, FLOAT* min_width)
 {
     struct dwrite_textlayout *This = impl_from_IDWriteTextLayout2(iface);
+    UINT32 start;
     FLOAT width;
     HRESULT hr;
-    UINT32 i;
 
     TRACE("(%p)->(%p)\n", This, min_width);
 
@@ -3260,20 +3249,30 @@ static HRESULT WINAPI dwritetextlayout_DetermineMinWidth(IDWriteTextLayout2 *ifa
     if (FAILED(hr))
         return hr;
 
-    for (i = 0; i < This->cluster_count;) {
-        if (is_terminal_cluster(This, i)) {
-            width = This->clustermetrics[i].width;
-            i++;
-        }
-        else {
-            width = 0.0f;
-            while (!is_terminal_cluster(This, i)) {
-                width += This->clustermetrics[i].width;
-                i++;
-            }
-            /* count last one too */
-            width += This->clustermetrics[i].width;
-        }
+    /* Find widest word without emergency breaking between clusters, trailing whitespaces
+       preceding breaking point do not contribute to word width. */
+    for (start = 0; start < This->cluster_count;) {
+        UINT32 end = start, j, next;
+
+        /* Last cluster always could be wrapped after. */
+        while (!This->clustermetrics[end].canWrapLineAfter)
+            end++;
+        /* make is so current cluster range that we can wrap after is [start,end) */
+        end++;
+
+        next = end;
+
+        /* Ignore trailing whitespace clusters, in case of single space range will
+           be reduced to empty range, or [start,start+1). */
+        while ((end - 1) >= start && This->clustermetrics[end-1].isWhitespace)
+            end--;
+
+        /* check if cluster range exceeds last minimal width */
+        width = 0.0f;
+        for (j = start; j < end; j++)
+            width += This->clustermetrics[j].width;
+
+        start = next;
 
         if (width > This->minwidth)
             This->minwidth = width;
diff --git a/dlls/dwrite/tests/layout.c b/dlls/dwrite/tests/layout.c
index c04e73a..7d83c1a 100644
--- a/dlls/dwrite/tests/layout.c
+++ b/dlls/dwrite/tests/layout.c
@@ -2347,10 +2347,21 @@ if (hr == S_OK) {
 
 static void test_DetermineMinWidth(void)
 {
+    struct minwidth_test {
+        const WCHAR text[10];    /* text to create a layout for */
+        const WCHAR mintext[10]; /* text that represents sequence of minimal width */
+    } minwidth_tests[] = {
+        { {' ','a','b',' ',0}, {'a','b',0} },
+        { {'a','\n',' ',' ',0}, {'a',0} },
+        { {'a','\n',' ',' ','b',0}, {'b',0} },
+        { {'a','b','c','\n',' ',' ','b',0}, {'a','b','c',0} },
+    };
     static const WCHAR strW[] = {'a','b','c','d',0};
+    DWRITE_CLUSTER_METRICS metrics[10];
     IDWriteTextFormat *format;
     IDWriteTextLayout *layout;
     IDWriteFactory *factory;
+    UINT32 count, i, j;
     FLOAT minwidth;
     HRESULT hr;
 
@@ -2365,13 +2376,34 @@ static void test_DetermineMinWidth(void)
 
     hr = IDWriteTextLayout_DetermineMinWidth(layout, NULL);
     ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
+    IDWriteTextLayout_Release(layout);
 
-    minwidth = 0.0;
-    hr = IDWriteTextLayout_DetermineMinWidth(layout, &minwidth);
-    ok(hr == S_OK, "got 0x%08x\n", hr);
-    ok(minwidth > 0.0, "got %.2f\n", minwidth);
+    for (i = 0; i < sizeof(minwidth_tests)/sizeof(minwidth_tests[0]); i++) {
+        FLOAT width = 0.0f;
+
+        /* measure expected width */
+        hr = IDWriteFactory_CreateTextLayout(factory, minwidth_tests[i].mintext, lstrlenW(minwidth_tests[i].mintext), format, 1000.0f, 1000.0f, &layout);
+        ok(hr == S_OK, "got 0x%08x\n", hr);
+
+        hr = IDWriteTextLayout_GetClusterMetrics(layout, metrics, sizeof(metrics)/sizeof(metrics[0]), &count);
+        ok(hr == S_OK, "got 0x%08x\n", hr);
+
+        for (j = 0; j < count; j++)
+            width += metrics[j].width;
+
+        IDWriteTextLayout_Release(layout);
+
+        hr = IDWriteFactory_CreateTextLayout(factory, minwidth_tests[i].text, lstrlenW(minwidth_tests[i].text), format, 1000.0f, 1000.0f, &layout);
+        ok(hr == S_OK, "got 0x%08x\n", hr);
+
+        minwidth = 0.0f;
+        hr = IDWriteTextLayout_DetermineMinWidth(layout, &minwidth);
+        ok(hr == S_OK, "got 0x%08x\n", hr);
+        ok(minwidth == width, "test %u: expected width %f, got %f\n", i, width, minwidth);
+
+        IDWriteTextLayout_Release(layout);
+    }
 
-    IDWriteTextLayout_Release(layout);
     IDWriteTextFormat_Release(format);
     IDWriteFactory_Release(factory);
 }




More information about the wine-cvs mailing list