[PATCH] dwrite: Use callback freetype API to implement GetGlyphRunOutline()

Nikolay Sivov nsivov at codeweavers.com
Mon Jan 18 15:47:09 CST 2016


Signed-off-by: Nikolay Sivov <nsivov at codeweavers.com>
---
 dlls/dwrite/dwrite_private.h |  19 +--
 dlls/dwrite/font.c           | 110 +----------------
 dlls/dwrite/freetype.c       | 281 +++++++++++++++++++++++++------------------
 3 files changed, 169 insertions(+), 241 deletions(-)

diff --git a/dlls/dwrite/dwrite_private.h b/dlls/dwrite/dwrite_private.h
index 2757b69..f79763c 100644
--- a/dlls/dwrite/dwrite_private.h
+++ b/dlls/dwrite/dwrite_private.h
@@ -175,22 +175,6 @@ extern WORD opentype_get_gasp_flags(const WORD*,UINT32,INT) DECLSPEC_HIDDEN;
 extern HRESULT bidi_computelevels(const WCHAR*,UINT32,UINT8,UINT8*,UINT8*) DECLSPEC_HIDDEN;
 extern WCHAR bidi_get_mirrored_char(WCHAR) DECLSPEC_HIDDEN;
 
-enum outline_point_tag {
-    OUTLINE_POINT_START  = 1 << 0,
-    OUTLINE_POINT_END    = 1 << 1,
-    OUTLINE_POINT_BEZIER = 1 << 2,
-    OUTLINE_POINT_LINE   = 1 << 3
-};
-
-struct glyph_outline {
-    D2D1_POINT_2F *points;
-    UINT8 *tags;
-    UINT16 count;
-    FLOAT  advance;
-};
-
-extern HRESULT new_glyph_outline(UINT32,struct glyph_outline**) DECLSPEC_HIDDEN;
-
 /* FreeType integration */
 struct dwrite_glyphbitmap {
     IDWriteFontFace2 *fontface;
@@ -209,7 +193,8 @@ extern void release_freetype(void) DECLSPEC_HIDDEN;
 extern HRESULT freetype_get_design_glyph_metrics(IDWriteFontFace2*,UINT16,UINT16,DWRITE_GLYPH_METRICS*) DECLSPEC_HIDDEN;
 extern void freetype_notify_cacheremove(IDWriteFontFace2*) DECLSPEC_HIDDEN;
 extern BOOL freetype_is_monospaced(IDWriteFontFace2*) DECLSPEC_HIDDEN;
-extern HRESULT freetype_get_glyph_outline(IDWriteFontFace2*,FLOAT,UINT16,USHORT,struct glyph_outline**) DECLSPEC_HIDDEN;
+extern HRESULT freetype_get_glyphrun_outline(IDWriteFontFace2*,FLOAT,UINT16 const*,FLOAT const*, DWRITE_GLYPH_OFFSET const*,
+    UINT32,BOOL,IDWriteGeometrySink*) DECLSPEC_HIDDEN;
 extern UINT16 freetype_get_glyphcount(IDWriteFontFace2*) DECLSPEC_HIDDEN;
 extern UINT16 freetype_get_glyphindex(IDWriteFontFace2*,UINT32,INT) DECLSPEC_HIDDEN;
 extern BOOL freetype_has_kerning_pairs(IDWriteFontFace2*) DECLSPEC_HIDDEN;
diff --git a/dlls/dwrite/font.c b/dlls/dwrite/font.c
index fe0c1be..4640de9 100644
--- a/dlls/dwrite/font.c
+++ b/dlls/dwrite/font.c
@@ -584,84 +584,11 @@ static void WINAPI dwritefontface_ReleaseFontTable(IDWriteFontFace2 *iface, void
     IDWriteFontFileStream_ReleaseFileFragment(This->streams[0], table_context);
 }
 
-HRESULT new_glyph_outline(UINT32 count, struct glyph_outline **ret)
-{
-    struct glyph_outline *outline;
-    D2D1_POINT_2F *points;
-    UINT8 *tags;
-
-    *ret = NULL;
-
-    outline = heap_alloc(sizeof(*outline));
-    if (!outline)
-        return E_OUTOFMEMORY;
-
-    points = heap_alloc(count*sizeof(D2D1_POINT_2F));
-    tags = heap_alloc_zero(count*sizeof(UINT8));
-    if (!points || !tags) {
-        heap_free(points);
-        heap_free(tags);
-        heap_free(outline);
-        return E_OUTOFMEMORY;
-    }
-
-    outline->points = points;
-    outline->tags = tags;
-    outline->count = count;
-    outline->advance = 0.0f;
-
-    *ret = outline;
-    return S_OK;
-}
-
-static void free_glyph_outline(struct glyph_outline *outline)
-{
-    heap_free(outline->points);
-    heap_free(outline->tags);
-    heap_free(outline);
-}
-
-static void report_glyph_outline(const struct glyph_outline *outline, IDWriteGeometrySink *sink)
-{
-    UINT16 p;
-
-    for (p = 0; p < outline->count; p++) {
-        if (outline->tags[p] & OUTLINE_POINT_START) {
-            ID2D1SimplifiedGeometrySink_BeginFigure(sink, outline->points[p], D2D1_FIGURE_BEGIN_FILLED);
-            continue;
-        }
-
-        if (outline->tags[p] & OUTLINE_POINT_LINE)
-            ID2D1SimplifiedGeometrySink_AddLines(sink, outline->points+p, 1);
-        else if (outline->tags[p] & OUTLINE_POINT_BEZIER) {
-            static const UINT16 segment_length = 3;
-            ID2D1SimplifiedGeometrySink_AddBeziers(sink, (D2D1_BEZIER_SEGMENT*)&outline->points[p], 1);
-            p += segment_length - 1;
-        }
-
-        if (outline->tags[p] & OUTLINE_POINT_END)
-            ID2D1SimplifiedGeometrySink_EndFigure(sink, D2D1_FIGURE_END_CLOSED);
-    }
-}
-
-static inline void translate_glyph_outline(struct glyph_outline *outline, FLOAT xoffset, FLOAT yoffset)
-{
-    UINT16 p;
-
-    for (p = 0; p < outline->count; p++) {
-        outline->points[p].x += xoffset;
-        outline->points[p].y += yoffset;
-    }
-}
-
 static HRESULT WINAPI dwritefontface_GetGlyphRunOutline(IDWriteFontFace2 *iface, FLOAT emSize,
     UINT16 const *glyphs, FLOAT const* advances, DWRITE_GLYPH_OFFSET const *offsets,
     UINT32 count, BOOL is_sideways, BOOL is_rtl, IDWriteGeometrySink *sink)
 {
     struct dwrite_fontface *This = impl_from_IDWriteFontFace2(iface);
-    FLOAT advance = 0.0f;
-    HRESULT hr;
-    UINT32 g;
 
     TRACE("(%p)->(%.2f %p %p %p %u %d %d %p)\n", This, emSize, glyphs, advances, offsets,
         count, is_sideways, is_rtl, sink);
@@ -672,42 +599,7 @@ static HRESULT WINAPI dwritefontface_GetGlyphRunOutline(IDWriteFontFace2 *iface,
     if (is_sideways)
         FIXME("sideways mode is not supported.\n");
 
-    if (count)
-        ID2D1SimplifiedGeometrySink_SetFillMode(sink, D2D1_FILL_MODE_WINDING);
-
-    for (g = 0; g < count; g++) {
-        FLOAT xoffset = 0.0f, yoffset = 0.0f;
-        struct glyph_outline *outline;
-
-        /* FIXME: cache outlines */
-
-        hr = freetype_get_glyph_outline(iface, emSize, glyphs[g], This->simulations, &outline);
-        if (FAILED(hr))
-            return hr;
-
-        /* glyph offsets act as current glyph adjustment */
-        if (offsets) {
-            xoffset += is_rtl ? -offsets[g].advanceOffset : offsets[g].advanceOffset;
-            yoffset -= offsets[g].ascenderOffset;
-        }
-
-        if (g == 0)
-            advance = is_rtl ? -outline->advance : 0.0f;
-
-        xoffset += advance;
-        translate_glyph_outline(outline, xoffset, yoffset);
-
-        /* update advance to next glyph */
-        if (advances)
-            advance += is_rtl ? -advances[g] : advances[g];
-        else
-            advance += is_rtl ? -outline->advance : outline->advance;
-
-        report_glyph_outline(outline, sink);
-        free_glyph_outline(outline);
-    }
-
-    return S_OK;
+    return freetype_get_glyphrun_outline(iface, emSize, glyphs, advances, offsets, count, is_rtl, sink);
 }
 
 static DWRITE_RENDERING_MODE fontface_renderingmode_from_measuringmode(DWRITE_MEASURING_MODE measuring,
diff --git a/dlls/dwrite/freetype.c b/dlls/dwrite/freetype.c
index 6c2f6a1..f4b824c 100644
--- a/dlls/dwrite/freetype.c
+++ b/dlls/dwrite/freetype.c
@@ -77,6 +77,7 @@ MAKE_FUNCPTR(FT_Library_Version);
 MAKE_FUNCPTR(FT_Load_Glyph);
 MAKE_FUNCPTR(FT_New_Memory_Face);
 MAKE_FUNCPTR(FT_Outline_Copy);
+MAKE_FUNCPTR(FT_Outline_Decompose);
 MAKE_FUNCPTR(FT_Outline_Done);
 MAKE_FUNCPTR(FT_Outline_Get_Bitmap);
 MAKE_FUNCPTR(FT_Outline_New);
@@ -163,6 +164,7 @@ BOOL init_freetype(void)
     LOAD_FUNCPTR(FT_Load_Glyph)
     LOAD_FUNCPTR(FT_New_Memory_Face)
     LOAD_FUNCPTR(FT_Outline_Copy)
+    LOAD_FUNCPTR(FT_Outline_Decompose)
     LOAD_FUNCPTR(FT_Outline_Done)
     LOAD_FUNCPTR(FT_Outline_Get_Bitmap)
     LOAD_FUNCPTR(FT_Outline_New)
@@ -267,16 +269,69 @@ BOOL freetype_is_monospaced(IDWriteFontFace2 *fontface)
     return is_monospaced;
 }
 
-static inline void ft_vector_to_d2d_point(const FT_Vector *v, D2D1_POINT_2F *p)
+struct decompose_context {
+    IDWriteGeometrySink *sink;
+    FLOAT xoffset;
+    FLOAT yoffset;
+    BOOL figure_started;
+    BOOL figure_closed;
+    BOOL move_to;     /* last call was 'move_to' */
+    FT_Vector origin; /* 'pen' position from last call */
+};
+
+static inline void ft_vector_to_d2d_point(const FT_Vector *v, FLOAT xoffset, FLOAT yoffset, D2D1_POINT_2F *p)
 {
-    p->x = v->x / 64.0f;
-    p->y = v->y / 64.0f;
+    p->x = (v->x / 64.0f) + xoffset;
+    p->y = (v->y / 64.0f) + yoffset;
 }
 
-/* Convert the quadratic Beziers to cubic Beziers. */
-static void get_cubic_glyph_outline(const FT_Outline *outline, short point, short first_pt,
-    short contour, FT_Vector *cubic_control)
+static int decompose_move_to(const FT_Vector *to, void *user)
 {
+    struct decompose_context *ctxt = (struct decompose_context*)user;
+    D2D1_POINT_2F point;
+
+    if (ctxt->figure_started) {
+        ID2D1SimplifiedGeometrySink_EndFigure(ctxt->sink, D2D1_FIGURE_END_CLOSED);
+        ctxt->figure_closed = TRUE;
+    }
+    else
+        ctxt->figure_closed = FALSE;
+    ctxt->figure_started = TRUE;
+
+    ft_vector_to_d2d_point(to, ctxt->xoffset, ctxt->yoffset, &point);
+    ID2D1SimplifiedGeometrySink_BeginFigure(ctxt->sink, point, D2D1_FIGURE_BEGIN_FILLED);
+    ctxt->move_to = TRUE;
+    ctxt->origin = *to;
+    return 0;
+}
+
+static int decompose_line_to(const FT_Vector *to, void *user)
+{
+    struct decompose_context *ctxt = (struct decompose_context*)user;
+    /* special case for empty contours, in a way freetype returns them */
+    if (ctxt->move_to && !memcmp(to, &ctxt->origin, sizeof(*to))) {
+        ID2D1SimplifiedGeometrySink_EndFigure(ctxt->sink, D2D1_FIGURE_END_CLOSED);
+        ctxt->figure_closed = TRUE;
+    }
+    else {
+        D2D1_POINT_2F point;
+        ft_vector_to_d2d_point(to, ctxt->xoffset, ctxt->yoffset, &point);
+        ID2D1SimplifiedGeometrySink_AddLines(ctxt->sink, &point, 1);
+        ctxt->figure_closed = FALSE;
+    }
+    ctxt->move_to = FALSE;
+    ctxt->origin = *to;
+    return 0;
+}
+
+static int decompose_conic_to(const FT_Vector *control, const FT_Vector *to, void *user)
+{
+    struct decompose_context *ctxt = (struct decompose_context*)user;
+    D2D1_POINT_2F points[3];
+    FT_Vector cubic[3];
+
+    /* convert from quadratic to cubic */
+
     /*
        The parametric eqn for a cubic Bezier is, from PLRM:
        r(t) = at^3 + bt^2 + ct + r0
@@ -294,108 +349,85 @@ static void get_cubic_glyph_outline(const FT_Outline *outline, short point, shor
        and of course r0 = p0, r3 = p2
     */
 
-    /* FIXME: Possible optimization in endpoint calculation
-       if there are two consecutive curves */
-    cubic_control[0] = outline->points[point-1];
-    if (!(outline->tags[point-1] & FT_Curve_Tag_On)) {
-        cubic_control[0].x += outline->points[point].x + 1;
-        cubic_control[0].y += outline->points[point].y + 1;
-        cubic_control[0].x >>= 1;
-        cubic_control[0].y >>= 1;
-    }
-    if (point+1 > outline->contours[contour])
-        cubic_control[3] = outline->points[first_pt];
-    else {
-        cubic_control[3] = outline->points[point+1];
-        if (!(outline->tags[point+1] & FT_Curve_Tag_On)) {
-            cubic_control[3].x += outline->points[point].x + 1;
-            cubic_control[3].y += outline->points[point].y + 1;
-            cubic_control[3].x >>= 1;
-            cubic_control[3].y >>= 1;
-        }
-    }
-
     /* r1 = 1/3 p0 + 2/3 p1
        r2 = 1/3 p2 + 2/3 p1 */
-    cubic_control[1].x = (2 * outline->points[point].x + 1) / 3;
-    cubic_control[1].y = (2 * outline->points[point].y + 1) / 3;
-    cubic_control[2] = cubic_control[1];
-    cubic_control[1].x += (cubic_control[0].x + 1) / 3;
-    cubic_control[1].y += (cubic_control[0].y + 1) / 3;
-    cubic_control[2].x += (cubic_control[3].x + 1) / 3;
-    cubic_control[2].y += (cubic_control[3].y + 1) / 3;
-}
-
-static short get_outline_data(const FT_Outline *outline, struct glyph_outline *ret)
-{
-    short contour, point = 0, first_pt, count;
-
-    for (contour = 0, count = 0; contour < outline->n_contours; contour++) {
-        first_pt = point;
-        if (ret) {
-            ft_vector_to_d2d_point(&outline->points[point], &ret->points[count]);
-            ret->tags[count] = OUTLINE_POINT_START;
-            if (count)
-                ret->tags[count-1] |= OUTLINE_POINT_END;
-        }
+    cubic[0].x = (2 * control->x + 1) / 3;
+    cubic[0].y = (2 * control->y + 1) / 3;
+    cubic[1] = cubic[0];
+    cubic[0].x += (ctxt->origin.x + 1) / 3;
+    cubic[0].y += (ctxt->origin.y + 1) / 3;
+    cubic[1].x += (to->x + 1) / 3;
+    cubic[1].y += (to->y + 1) / 3;
+    cubic[2] = *to;
+
+    ft_vector_to_d2d_point(cubic, ctxt->xoffset, ctxt->yoffset, points);
+    ft_vector_to_d2d_point(cubic + 1, ctxt->xoffset, ctxt->yoffset, points + 1);
+    ft_vector_to_d2d_point(cubic + 2, ctxt->xoffset, ctxt->yoffset, points + 2);
+    ID2D1SimplifiedGeometrySink_AddBeziers(ctxt->sink, (D2D1_BEZIER_SEGMENT*)points, 1);
+    ctxt->figure_closed = FALSE;
+    ctxt->move_to = FALSE;
+    ctxt->origin = *to;
+    return 0;
+}
 
-        point++;
-        count++;
+static int decompose_cubic_to(const FT_Vector *control1, const FT_Vector *control2,
+    const FT_Vector *to, void *user)
+{
+    struct decompose_context *ctxt = (struct decompose_context*)user;
+    D2D1_POINT_2F points[3];
 
-        while (point <= outline->contours[contour]) {
-            do {
-                if (outline->tags[point] & FT_Curve_Tag_On) {
-                    if (ret) {
-                        ft_vector_to_d2d_point(&outline->points[point], &ret->points[count]);
-                        ret->tags[count] |= OUTLINE_POINT_LINE;
-                    }
+    ft_vector_to_d2d_point(control1, ctxt->xoffset, ctxt->yoffset, points);
+    ft_vector_to_d2d_point(control2, ctxt->xoffset, ctxt->yoffset, points + 1);
+    ft_vector_to_d2d_point(to, ctxt->xoffset, ctxt->yoffset, points + 2);
+    ID2D1SimplifiedGeometrySink_AddBeziers(ctxt->sink, (D2D1_BEZIER_SEGMENT*)points, 1);
+    ctxt->figure_closed = FALSE;
+    ctxt->move_to = FALSE;
+    ctxt->origin = *to;
+    return 0;
+}
 
-                    point++;
-                    count++;
-                }
-                else {
-
-                    if (ret) {
-                        FT_Vector cubic_control[4];
-
-                        get_cubic_glyph_outline(outline, point, first_pt, contour, cubic_control);
-                        ft_vector_to_d2d_point(&cubic_control[1], &ret->points[count]);
-                        ft_vector_to_d2d_point(&cubic_control[2], &ret->points[count+1]);
-                        ft_vector_to_d2d_point(&cubic_control[3], &ret->points[count+2]);
-                        ret->tags[count] = OUTLINE_POINT_BEZIER;
-                        ret->tags[count+1] = OUTLINE_POINT_BEZIER;
-                        ret->tags[count+2] = OUTLINE_POINT_BEZIER;
-                    }
-
-                    count += 3;
-                    point++;
-                }
-            } while (point <= outline->contours[contour] &&
-                    (outline->tags[point] & FT_Curve_Tag_On) ==
-                    (outline->tags[point-1] & FT_Curve_Tag_On));
-
-            if (point <= outline->contours[contour] &&
-               outline->tags[point] & FT_Curve_Tag_On)
-            {
-                /* This is the closing pt of a bezier, but we've already
-                   added it, so just inc point and carry on */
-                point++;
-            }
-        }
-    }
+static void decompose_outline(FT_Outline *outline, FLOAT xoffset, FLOAT yoffset, IDWriteGeometrySink *sink)
+{
+    static const FT_Outline_Funcs decompose_funcs = {
+        decompose_move_to,
+        decompose_line_to,
+        decompose_conic_to,
+        decompose_cubic_to,
+        0,
+        0
+    };
+    struct decompose_context context;
 
-    if (ret)
-        ret->tags[count-1] |= OUTLINE_POINT_END;
+    context.sink = sink;
+    context.xoffset = xoffset;
+    context.yoffset = yoffset;
+    context.figure_started = FALSE;
+    context.figure_closed = FALSE;
+    context.move_to = FALSE;
+    context.origin.x = 0;
+    context.origin.y = 0;
 
-    return count;
+    pFT_Outline_Decompose(outline, &decompose_funcs, &context);
+
+    if (!context.figure_closed && outline->n_points)
+        ID2D1SimplifiedGeometrySink_EndFigure(sink, D2D1_FIGURE_END_CLOSED);
 }
 
-HRESULT freetype_get_glyph_outline(IDWriteFontFace2 *fontface, FLOAT emSize, UINT16 index, USHORT simulations, struct glyph_outline **ret)
+HRESULT freetype_get_glyphrun_outline(IDWriteFontFace2 *fontface, FLOAT emSize, UINT16 const *glyphs, FLOAT const *advances,
+    DWRITE_GLYPH_OFFSET const *offsets, UINT32 count, BOOL is_rtl, IDWriteGeometrySink *sink)
 {
     FTC_ScalerRec scaler;
+    USHORT simulations;
     HRESULT hr = S_OK;
     FT_Size size;
 
+    if (!count)
+        return S_OK;
+
+    ID2D1SimplifiedGeometrySink_SetFillMode(sink, D2D1_FILL_MODE_WINDING);
+
+    simulations = IDWriteFontFace2_GetSimulations(fontface);
+
     scaler.face_id = fontface;
     scaler.width  = emSize;
     scaler.height = emSize;
@@ -405,26 +437,45 @@ HRESULT freetype_get_glyph_outline(IDWriteFontFace2 *fontface, FLOAT emSize, UIN
 
     EnterCriticalSection(&freetype_cs);
     if (pFTC_Manager_LookupSize(cache_manager, &scaler, &size) == 0) {
-         if (pFT_Load_Glyph(size->face, index, FT_LOAD_NO_BITMAP) == 0) {
-             FT_Outline *outline = &size->face->glyph->outline;
-             short count;
-             FT_Matrix m;
-
-             m.xx = 1 << 16;
-             m.xy = simulations & DWRITE_FONT_SIMULATIONS_OBLIQUE ? (1 << 16) / 3 : 0;
-             m.yx = 0;
-             m.yy = -(1 << 16); /* flip Y axis */
-
-             pFT_Outline_Transform(outline, &m);
-
-             count = get_outline_data(outline, NULL);
-             hr = new_glyph_outline(count, ret);
-             if (hr == S_OK) {
-                 get_outline_data(outline, *ret);
-                 (*ret)->advance = size->face->glyph->metrics.horiAdvance >> 6;
-             }
-         }
+        FLOAT advance = 0.0f;
+        UINT32 g;
+
+        for (g = 0; g < count; g++) {
+            if (pFT_Load_Glyph(size->face, glyphs[g], FT_LOAD_NO_BITMAP) == 0) {
+                FLOAT ft_advance = size->face->glyph->metrics.horiAdvance >> 6;
+                FT_Outline *outline = &size->face->glyph->outline;
+                FLOAT xoffset = 0.0f, yoffset = 0.0f;
+                FT_Matrix m;
+
+                m.xx = 1 << 16;
+                m.xy = simulations & DWRITE_FONT_SIMULATIONS_OBLIQUE ? (1 << 16) / 3 : 0;
+                m.yx = 0;
+                m.yy = -(1 << 16); /* flip Y axis */
+
+                pFT_Outline_Transform(outline, &m);
+
+                /* glyph offsets act as current glyph adjustment */
+                if (offsets) {
+                    xoffset += is_rtl ? -offsets[g].advanceOffset : offsets[g].advanceOffset;
+                    yoffset -= offsets[g].ascenderOffset;
+                }
+
+                if (g == 0 && is_rtl)
+                    advance = advances ? -advances[g] : -ft_advance;
+
+                xoffset += advance;
+                decompose_outline(outline, xoffset, yoffset, sink);
+
+                /* update advance to next glyph */
+                if (advances)
+                    advance += is_rtl ? -advances[g] : advances[g];
+                else
+                    advance += is_rtl ? -ft_advance : ft_advance;
+            }
+        }
     }
+    else
+        hr = E_FAIL;
     LeaveCriticalSection(&freetype_cs);
 
     return hr;
@@ -775,9 +826,9 @@ BOOL freetype_is_monospaced(IDWriteFontFace2 *fontface)
     return FALSE;
 }
 
-HRESULT freetype_get_glyph_outline(IDWriteFontFace2 *fontface, FLOAT emSize, UINT16 index, USHORT simulations, struct glyph_outline **ret)
+HRESULT freetype_get_glyphrun_outline(IDWriteFontFace2 *fontface, FLOAT emSize, UINT16 const *glyphs, FLOAT const *advances,
+    DWRITE_GLYPH_OFFSET const *offsets, UINT32 count, BOOL is_rtl, IDWriteGeometrySink *sink)
 {
-    *ret = NULL;
     return E_NOTIMPL;
 }
 
-- 
2.7.0.rc3




More information about the wine-patches mailing list