Nikolay Sivov : dwrite: Fix cluster width and length calculation.

Alexandre Julliard julliard at wine.codeweavers.com
Fri Apr 3 07:14:59 CDT 2015


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

Author: Nikolay Sivov <nsivov at codeweavers.com>
Date:   Fri Apr  3 11:38:23 2015 +0300

dwrite: Fix cluster width and length calculation.

---

 dlls/dwrite/layout.c       | 76 ++++++++++++++++++++++++++++------------------
 dlls/dwrite/tests/layout.c | 26 +++++++++++-----
 2 files changed, 64 insertions(+), 38 deletions(-)

diff --git a/dlls/dwrite/layout.c b/dlls/dwrite/layout.c
index 28889a7..f28214f 100644
--- a/dlls/dwrite/layout.c
+++ b/dlls/dwrite/layout.c
@@ -362,14 +362,34 @@ static HRESULT layout_update_breakpoints_range(struct dwrite_textlayout *layout,
 
 static struct layout_range *get_layout_range_by_pos(struct dwrite_textlayout *layout, UINT32 pos);
 
-static void init_cluster_metrics(const struct layout_run *run, DWRITE_CLUSTER_METRICS *metrics)
+static inline DWRITE_LINE_BREAKPOINT get_effective_breakpoint(const struct dwrite_textlayout *layout, UINT32 pos)
 {
+    if (layout->actual_breakpoints)
+        return layout->actual_breakpoints[pos];
+    return layout->nominal_breakpoints[pos];
+}
+
+static inline void init_cluster_metrics(const struct dwrite_textlayout *layout, const struct layout_run *run,
+    UINT16 start_glyph, UINT16 stop_glyph, UINT32 stop_position, DWRITE_CLUSTER_METRICS *metrics)
+{
+    UINT8 breakcondition;
+    UINT16 j;
+
     metrics->width = 0.0;
-    metrics->length = 1;
-    metrics->canWrapLineAfter = FALSE;
-    metrics->isWhitespace = FALSE;
-    metrics->isNewline = FALSE;
-    metrics->isSoftHyphen = FALSE;
+    for (j = start_glyph; j < stop_glyph; j++)
+        metrics->width += run->run.glyphAdvances[j];
+    metrics->length = 0;
+
+    if (stop_glyph == run->run.glyphCount)
+        breakcondition = get_effective_breakpoint(layout, stop_position).breakConditionAfter;
+    else
+        breakcondition = get_effective_breakpoint(layout, stop_position).breakConditionBefore;
+
+    metrics->canWrapLineAfter = breakcondition == DWRITE_BREAK_CONDITION_CAN_BREAK ||
+                                breakcondition == DWRITE_BREAK_CONDITION_MUST_BREAK;
+    metrics->isWhitespace = FALSE; /* FIXME */
+    metrics->isNewline = FALSE;    /* FIXME */
+    metrics->isSoftHyphen = FALSE; /* FIXME */
     metrics->isRightToLeft = run->run.bidiLevel & 1;
     metrics->padding = 0;
 }
@@ -378,42 +398,34 @@ static void init_cluster_metrics(const struct layout_run *run, DWRITE_CLUSTER_ME
 
   All clusters in a 'run' will be added to 'layout' data, starting at index pointed to by 'cluster'.
   On return 'cluster' is updated to point to next metrics struct to be filled in on next call.
+  Note that there's no need to reallocate anything at this point as we allocate one cluster per
+  codepoint initially.
 
 */
 static void layout_set_cluster_metrics(struct dwrite_textlayout *layout, const struct layout_run *run, UINT32 *cluster)
 {
     DWRITE_CLUSTER_METRICS *metrics = &layout->clusters[*cluster];
-    UINT16 glyph;
-    UINT32 i;
-
-    glyph = run->descr.clusterMap[0];
-    init_cluster_metrics(run, metrics);
+    UINT32 i, start = 0;
 
     for (i = 0; i < run->descr.stringLength; i++) {
-        BOOL newcluster = glyph != run->descr.clusterMap[i];
+        BOOL end = i == run->descr.stringLength - 1;
 
-        /* add new cluster on starting glyph change or simply when run is over */
-        if (newcluster || i == run->descr.stringLength - 1) {
-            UINT8 breakcondition;
-            UINT16 j;
+        if (run->descr.clusterMap[start] != run->descr.clusterMap[i]) {
+            init_cluster_metrics(layout, run, run->descr.clusterMap[start], run->descr.clusterMap[i], i, metrics);
+            metrics->length = i - start;
 
-            for (j = glyph; j < run->descr.clusterMap[i]; j++)
-                metrics->width += run->run.glyphAdvances[j];
+            *cluster += 1;
+            metrics++;
+            start = i;
+        }
 
-            /* FIXME: also set isWhitespace, isNewline and isSoftHyphen */
-            breakcondition = newcluster ? layout->nominal_breakpoints[i].breakConditionBefore :
-                                          layout->nominal_breakpoints[i].breakConditionAfter;
-            metrics->canWrapLineAfter = breakcondition == DWRITE_BREAK_CONDITION_CAN_BREAK ||
-                                        breakcondition == DWRITE_BREAK_CONDITION_MUST_BREAK;
+        if (end) {
+            init_cluster_metrics(layout, run, run->descr.clusterMap[start], run->run.glyphCount, i, metrics);
+            metrics->length = i - start + 1;
 
-            /* advance to next cluster */
-            glyph = run->descr.clusterMap[i];
             *cluster += 1;
-            metrics++;
-            init_cluster_metrics(run, metrics);
+            return;
         }
-        else
-            metrics->length++;
     }
 }
 
@@ -583,7 +595,7 @@ static HRESULT layout_compute_runs(struct dwrite_textlayout *layout)
     }
 
     if (hr == S_OK)
-        layout->clusters_count = cluster + 1;
+        layout->clusters_count = cluster;
 
     IDWriteTextAnalyzer_Release(analyzer);
     return hr;
@@ -613,6 +625,10 @@ static HRESULT layout_compute(struct dwrite_textlayout *layout)
             0, layout->len, &layout->IDWriteTextAnalysisSink_iface);
         IDWriteTextAnalyzer_Release(analyzer);
     }
+    if (layout->actual_breakpoints) {
+        heap_free(layout->actual_breakpoints);
+        layout->actual_breakpoints = NULL;
+    }
 
     hr = layout_compute_runs(layout);
 
diff --git a/dlls/dwrite/tests/layout.c b/dlls/dwrite/tests/layout.c
index f36c01e..cf0e1f8 100644
--- a/dlls/dwrite/tests/layout.c
+++ b/dlls/dwrite/tests/layout.c
@@ -993,13 +993,13 @@ static void test_GetClusterMetrics(void)
 {
     static const WCHAR strW[] = {'a','b','c','d',0};
     DWRITE_INLINE_OBJECT_METRICS inline_metrics;
-    DWRITE_CLUSTER_METRICS metrics;
+    DWRITE_CLUSTER_METRICS metrics[4];
     IDWriteInlineObject *trimm;
     IDWriteTextFormat *format;
     IDWriteTextLayout *layout;
     DWRITE_TEXT_RANGE range;
     IDWriteFactory *factory;
-    UINT32 count;
+    UINT32 count, i;
     HRESULT hr;
 
     factory = create_factory();
@@ -1016,6 +1016,16 @@ static void test_GetClusterMetrics(void)
     ok(hr == E_NOT_SUFFICIENT_BUFFER, "got 0x%08x\n", hr);
     ok(count == 4, "got %u\n", count);
 
+    /* check every cluster width */
+    count = 0;
+    hr = IDWriteTextLayout_GetClusterMetrics(layout, metrics, sizeof(metrics)/sizeof(metrics[0]), &count);
+    ok(hr == S_OK, "got 0x%08x\n", hr);
+    ok(count == 4, "got %u\n", count);
+    for (i = 0; i < count; i++) {
+        ok(metrics[i].width > 0.0, "%u: got width %.2f\n", i, metrics[i].width);
+        ok(metrics[i].length == 1, "%u: got length %u\n", i, metrics[i].length);
+    }
+
     hr = IDWriteFactory_CreateEllipsisTrimmingSign(factory, format, &trimm);
     ok(hr == S_OK, "got 0x%08x\n", hr);
 
@@ -1033,17 +1043,17 @@ todo_wine
 
     count = 0;
     memset(&metrics, 0, sizeof(metrics));
-    hr = IDWriteTextLayout_GetClusterMetrics(layout, &metrics, 1, &count);
+    hr = IDWriteTextLayout_GetClusterMetrics(layout, metrics, 1, &count);
     ok(hr == E_NOT_SUFFICIENT_BUFFER, "got 0x%08x\n", hr);
-todo_wine
+todo_wine {
     ok(count == 3, "got %u\n", count);
-    ok(metrics.length == 2, "got %u\n", metrics.length);
-
+    ok(metrics[0].length == 2, "got %u\n", metrics[0].length);
+}
     hr = IDWriteInlineObject_GetMetrics(trimm, &inline_metrics);
 todo_wine {
     ok(hr == S_OK, "got 0x%08x\n", hr);
-    ok(inline_metrics.width == metrics.width, "got %.2f, expected %.2f\n", inline_metrics.width,
-        metrics.width);
+    ok(inline_metrics.width == metrics[0].width, "got %.2f, expected %.2f\n", inline_metrics.width,
+        metrics[0].width);
 }
     IDWriteInlineObject_Release(trimm);
     IDWriteTextLayout_Release(layout);




More information about the wine-cvs mailing list