1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this
5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 #include "ScaledFontBase.h"
8 
9 #include "PathSkia.h"
10 #include "skia/include/core/SkFont.h"
11 
12 #ifdef USE_CAIRO
13 #  include "PathCairo.h"
14 #  include "DrawTargetCairo.h"
15 #  include "HelpersCairo.h"
16 #endif
17 
18 #include <vector>
19 #include <cmath>
20 
21 namespace mozilla {
22 namespace gfx {
23 
24 Atomic<uint32_t> UnscaledFont::sDeletionCounter(0);
25 
~UnscaledFont()26 UnscaledFont::~UnscaledFont() { sDeletionCounter++; }
27 
28 Atomic<uint32_t> ScaledFont::sDeletionCounter(0);
29 
~ScaledFont()30 ScaledFont::~ScaledFont() { sDeletionCounter++; }
31 
~ScaledFontBase()32 ScaledFontBase::~ScaledFontBase() {
33   SkSafeUnref<SkTypeface>(mTypeface);
34   cairo_scaled_font_destroy(mScaledFont);
35 }
36 
ScaledFontBase(const RefPtr<UnscaledFont> & aUnscaledFont,Float aSize)37 ScaledFontBase::ScaledFontBase(const RefPtr<UnscaledFont>& aUnscaledFont,
38                                Float aSize)
39     : ScaledFont(aUnscaledFont),
40       mTypeface(nullptr),
41       mScaledFont(nullptr),
42       mSize(aSize) {}
43 
GetSkTypeface()44 SkTypeface* ScaledFontBase::GetSkTypeface() {
45   if (!mTypeface) {
46     SkTypeface* typeface = CreateSkTypeface();
47     if (!mTypeface.compareExchange(nullptr, typeface)) {
48       SkSafeUnref(typeface);
49     }
50   }
51   return mTypeface;
52 }
53 
GetCairoScaledFont()54 cairo_scaled_font_t* ScaledFontBase::GetCairoScaledFont() {
55   if (mScaledFont) {
56     return mScaledFont;
57   }
58 
59   cairo_font_options_t* fontOptions = cairo_font_options_create();
60   cairo_font_face_t* fontFace = CreateCairoFontFace(fontOptions);
61   if (!fontFace) {
62     cairo_font_options_destroy(fontOptions);
63     return nullptr;
64   }
65 
66   cairo_matrix_t sizeMatrix;
67   cairo_matrix_t identityMatrix;
68 
69   cairo_matrix_init_scale(&sizeMatrix, mSize, mSize);
70   cairo_matrix_init_identity(&identityMatrix);
71 
72   cairo_scaled_font_t* scaledFont = cairo_scaled_font_create(
73       fontFace, &sizeMatrix, &identityMatrix, fontOptions);
74 
75   cairo_font_options_destroy(fontOptions);
76   cairo_font_face_destroy(fontFace);
77 
78   if (cairo_scaled_font_status(scaledFont) != CAIRO_STATUS_SUCCESS) {
79     cairo_scaled_font_destroy(scaledFont);
80     return nullptr;
81   }
82 
83   PrepareCairoScaledFont(scaledFont);
84   mScaledFont = scaledFont;
85   return mScaledFont;
86 }
87 
GetSkiaPathForGlyphs(const GlyphBuffer & aBuffer)88 SkPath ScaledFontBase::GetSkiaPathForGlyphs(const GlyphBuffer& aBuffer) {
89   SkTypeface* typeFace = GetSkTypeface();
90   MOZ_ASSERT(typeFace);
91 
92   SkFont font(sk_ref_sp(typeFace), SkFloatToScalar(mSize));
93 
94   std::vector<uint16_t> indices;
95   indices.resize(aBuffer.mNumGlyphs);
96   for (unsigned int i = 0; i < aBuffer.mNumGlyphs; i++) {
97     indices[i] = aBuffer.mGlyphs[i].mIndex;
98   }
99 
100   struct Context {
101     const Glyph* mGlyph;
102     SkPath mPath;
103   } ctx = {aBuffer.mGlyphs};
104 
105   font.getPaths(
106       indices.data(), indices.size(),
107       [](const SkPath* glyphPath, const SkMatrix& scaleMatrix, void* ctxPtr) {
108         Context& ctx = *reinterpret_cast<Context*>(ctxPtr);
109         if (glyphPath) {
110           SkMatrix transMatrix(scaleMatrix);
111           transMatrix.postTranslate(SkFloatToScalar(ctx.mGlyph->mPosition.x),
112                                     SkFloatToScalar(ctx.mGlyph->mPosition.y));
113           ctx.mPath.addPath(*glyphPath, transMatrix);
114         }
115         ++ctx.mGlyph;
116       },
117       &ctx);
118 
119   return ctx.mPath;
120 }
121 
GetPathForGlyphs(const GlyphBuffer & aBuffer,const DrawTarget * aTarget)122 already_AddRefed<Path> ScaledFontBase::GetPathForGlyphs(
123     const GlyphBuffer& aBuffer, const DrawTarget* aTarget) {
124   if (aTarget->GetBackendType() == BackendType::SKIA) {
125     SkPath path = GetSkiaPathForGlyphs(aBuffer);
126     return MakeAndAddRef<PathSkia>(path, FillRule::FILL_WINDING);
127   }
128 #ifdef USE_CAIRO
129   if (aTarget->GetBackendType() == BackendType::CAIRO) {
130     MOZ_ASSERT(mScaledFont);
131 
132     DrawTarget* dt = const_cast<DrawTarget*>(aTarget);
133     cairo_t* ctx = static_cast<cairo_t*>(
134         dt->GetNativeSurface(NativeSurfaceType::CAIRO_CONTEXT));
135 
136     bool isNewContext = !ctx;
137     if (!ctx) {
138       ctx = cairo_create(DrawTargetCairo::GetDummySurface());
139       cairo_matrix_t mat;
140       GfxMatrixToCairoMatrix(aTarget->GetTransform(), mat);
141       cairo_set_matrix(ctx, &mat);
142     }
143 
144     cairo_set_scaled_font(ctx, mScaledFont);
145 
146     // Convert our GlyphBuffer into an array of Cairo glyphs.
147     std::vector<cairo_glyph_t> glyphs(aBuffer.mNumGlyphs);
148     for (uint32_t i = 0; i < aBuffer.mNumGlyphs; ++i) {
149       glyphs[i].index = aBuffer.mGlyphs[i].mIndex;
150       glyphs[i].x = aBuffer.mGlyphs[i].mPosition.x;
151       glyphs[i].y = aBuffer.mGlyphs[i].mPosition.y;
152     }
153 
154     cairo_new_path(ctx);
155 
156     cairo_glyph_path(ctx, &glyphs[0], aBuffer.mNumGlyphs);
157 
158     RefPtr<PathCairo> newPath = new PathCairo(ctx);
159     if (isNewContext) {
160       cairo_destroy(ctx);
161     }
162 
163     return newPath.forget();
164   }
165 #endif
166   RefPtr<PathBuilder> builder = aTarget->CreatePathBuilder();
167   SkPath skPath = GetSkiaPathForGlyphs(aBuffer);
168   RefPtr<Path> path = MakeAndAddRef<PathSkia>(skPath, FillRule::FILL_WINDING);
169   path->StreamToSink(builder);
170   return builder->Finish();
171 }
172 
CopyGlyphsToBuilder(const GlyphBuffer & aBuffer,PathBuilder * aBuilder,const Matrix * aTransformHint)173 void ScaledFontBase::CopyGlyphsToBuilder(const GlyphBuffer& aBuffer,
174                                          PathBuilder* aBuilder,
175                                          const Matrix* aTransformHint) {
176   BackendType backendType = aBuilder->GetBackendType();
177   if (backendType == BackendType::SKIA) {
178     PathBuilderSkia* builder = static_cast<PathBuilderSkia*>(aBuilder);
179     builder->AppendPath(GetSkiaPathForGlyphs(aBuffer));
180     return;
181   }
182 #ifdef USE_CAIRO
183   if (backendType == BackendType::CAIRO) {
184     MOZ_ASSERT(mScaledFont);
185 
186     PathBuilderCairo* builder = static_cast<PathBuilderCairo*>(aBuilder);
187     cairo_t* ctx = cairo_create(DrawTargetCairo::GetDummySurface());
188 
189     if (aTransformHint) {
190       cairo_matrix_t mat;
191       GfxMatrixToCairoMatrix(*aTransformHint, mat);
192       cairo_set_matrix(ctx, &mat);
193     }
194 
195     // Convert our GlyphBuffer into an array of Cairo glyphs.
196     std::vector<cairo_glyph_t> glyphs(aBuffer.mNumGlyphs);
197     for (uint32_t i = 0; i < aBuffer.mNumGlyphs; ++i) {
198       glyphs[i].index = aBuffer.mGlyphs[i].mIndex;
199       glyphs[i].x = aBuffer.mGlyphs[i].mPosition.x;
200       glyphs[i].y = aBuffer.mGlyphs[i].mPosition.y;
201     }
202 
203     cairo_set_scaled_font(ctx, mScaledFont);
204     cairo_glyph_path(ctx, &glyphs[0], aBuffer.mNumGlyphs);
205 
206     RefPtr<PathCairo> cairoPath = new PathCairo(ctx);
207     cairo_destroy(ctx);
208 
209     cairoPath->AppendPathToBuilder(builder);
210     return;
211   }
212 #endif
213   if (backendType == BackendType::RECORDING) {
214     SkPath skPath = GetSkiaPathForGlyphs(aBuffer);
215     RefPtr<Path> path = MakeAndAddRef<PathSkia>(skPath, FillRule::FILL_WINDING);
216     path->StreamToSink(aBuilder);
217     return;
218   }
219   MOZ_ASSERT(false, "Path not being copied");
220 }
221 
222 }  // namespace gfx
223 }  // namespace mozilla
224