diff --git a/dlls/dwrite/dwrite_private.h b/dlls/dwrite/dwrite_private.h index 2757b69a758..f79763c5c95 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 fe0c1bedc56..4640de96d78 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 6c2f6a1c9ee..f4b824caf13 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; + 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; } -static short get_outline_data(const FT_Outline *outline, struct glyph_outline *ret) +static int decompose_cubic_to(const FT_Vector *control1, const FT_Vector *control2, + const FT_Vector *to, void *user) { - short contour, point = 0, first_pt, count; + struct decompose_context *ctxt = (struct decompose_context*)user; + D2D1_POINT_2F points[3]; - 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; - } - - point++; - count++; - - 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; - } - - 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++; - } - } - } - - if (ret) - ret->tags[count-1] |= OUTLINE_POINT_END; - - return count; + 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; } -HRESULT freetype_get_glyph_outline(IDWriteFontFace2 *fontface, FLOAT emSize, UINT16 index, USHORT simulations, struct glyph_outline **ret) +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; + + 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; + + pFT_Outline_Decompose(outline, &decompose_funcs, &context); + + if (!context.figure_closed && outline->n_points) + ID2D1SimplifiedGeometrySink_EndFigure(sink, D2D1_FIGURE_END_CLOSED); +} + +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; + FLOAT advance = 0.0f; + UINT32 g; - 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 */ + 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; - pFT_Outline_Transform(outline, &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 */ - 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; - } - } + 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; }