1 /*
2 * Copyright 2019 The Android Open Source Project
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8 #include "src/core/SkStrikeSpec.h"
9
10 #include "include/core/SkGraphics.h"
11 #include "include/core/SkPaint.h"
12 #include "include/core/SkPathEffect.h"
13 #include "include/effects/SkDashPathEffect.h"
14 #include "src/base/SkTLazy.h"
15 #include "src/core/SkDraw.h"
16 #include "src/core/SkFontPriv.h"
17 #include "src/core/SkGlyphBuffer.h"
18 #include "src/core/SkStrike.h"
19 #include "src/core/SkStrikeCache.h"
20
21 #if defined(SK_GANESH) || defined(SK_GRAPHITE)
22 #include "src/text/gpu/SDFMaskFilter.h"
23 #include "src/text/gpu/SDFTControl.h"
24 #include "src/text/gpu/StrikeCache.h"
25 #endif
26
SkStrikeSpec(const SkDescriptor & descriptor,sk_sp<SkTypeface> typeface)27 SkStrikeSpec::SkStrikeSpec(const SkDescriptor& descriptor, sk_sp<SkTypeface> typeface)
28 : fAutoDescriptor{descriptor}
29 , fTypeface{std::move(typeface)} {}
30
31 SkStrikeSpec::SkStrikeSpec(const SkStrikeSpec&) = default;
32 SkStrikeSpec::SkStrikeSpec(SkStrikeSpec&&) = default;
33 SkStrikeSpec::~SkStrikeSpec() = default;
34
MakeMask(const SkFont & font,const SkPaint & paint,const SkSurfaceProps & surfaceProps,SkScalerContextFlags scalerContextFlags,const SkMatrix & deviceMatrix)35 SkStrikeSpec SkStrikeSpec::MakeMask(const SkFont& font, const SkPaint& paint,
36 const SkSurfaceProps& surfaceProps,
37 SkScalerContextFlags scalerContextFlags,
38 const SkMatrix& deviceMatrix) {
39
40 return SkStrikeSpec(font, paint, surfaceProps, scalerContextFlags, deviceMatrix);
41 }
42
MakeTransformMask(const SkFont & font,const SkPaint & paint,const SkSurfaceProps & surfaceProps,SkScalerContextFlags scalerContextFlags,const SkMatrix & deviceMatrix)43 SkStrikeSpec SkStrikeSpec::MakeTransformMask(const SkFont& font,
44 const SkPaint& paint,
45 const SkSurfaceProps& surfaceProps,
46 SkScalerContextFlags scalerContextFlags,
47 const SkMatrix& deviceMatrix) {
48 SkFont sourceFont{font};
49 sourceFont.setSubpixel(false);
50 return SkStrikeSpec(sourceFont, paint, surfaceProps, scalerContextFlags, deviceMatrix);
51 }
52
MakePath(const SkFont & font,const SkPaint & paint,const SkSurfaceProps & surfaceProps,SkScalerContextFlags scalerContextFlags)53 std::tuple<SkStrikeSpec, SkScalar> SkStrikeSpec::MakePath(
54 const SkFont& font, const SkPaint& paint,
55 const SkSurfaceProps& surfaceProps,
56 SkScalerContextFlags scalerContextFlags) {
57
58 // setup our std runPaint, in hopes of getting hits in the cache
59 SkPaint pathPaint{paint};
60 SkFont pathFont{font};
61
62 // The sub-pixel position will always happen when transforming to the screen.
63 pathFont.setSubpixel(false);
64
65 // The factor to get from the size stored in the strike to the size needed for
66 // the source.
67 SkScalar strikeToSourceScale = pathFont.setupForAsPaths(&pathPaint);
68
69 return {SkStrikeSpec(pathFont, pathPaint, surfaceProps, scalerContextFlags, SkMatrix::I()),
70 strikeToSourceScale};
71 }
72
MakeCanonicalized(const SkFont & font,const SkPaint * paint)73 std::tuple<SkStrikeSpec, SkScalar> SkStrikeSpec::MakeCanonicalized(
74 const SkFont& font, const SkPaint* paint) {
75 SkPaint canonicalizedPaint;
76 if (paint != nullptr) {
77 canonicalizedPaint = *paint;
78 }
79
80 const SkFont* canonicalizedFont = &font;
81 SkTLazy<SkFont> pathFont;
82 SkScalar strikeToSourceScale = 1;
83 if (ShouldDrawAsPath(canonicalizedPaint, font, SkMatrix::I())) {
84 canonicalizedFont = pathFont.set(font);
85 strikeToSourceScale = pathFont->setupForAsPaths(nullptr);
86 canonicalizedPaint.reset();
87 }
88
89 return {SkStrikeSpec(*canonicalizedFont, canonicalizedPaint, SkSurfaceProps(),
90 SkScalerContextFlags::kFakeGammaAndBoostContrast, SkMatrix::I()),
91 strikeToSourceScale};
92 }
93
MakeWithNoDevice(const SkFont & font,const SkPaint * paint)94 SkStrikeSpec SkStrikeSpec::MakeWithNoDevice(const SkFont& font, const SkPaint* paint) {
95 SkPaint setupPaint;
96 if (paint != nullptr) {
97 setupPaint = *paint;
98 }
99
100 return SkStrikeSpec(font, setupPaint, SkSurfaceProps(),
101 SkScalerContextFlags::kFakeGammaAndBoostContrast, SkMatrix::I());
102 }
103
ShouldDrawAsPath(const SkPaint & paint,const SkFont & font,const SkMatrix & viewMatrix)104 bool SkStrikeSpec::ShouldDrawAsPath(
105 const SkPaint& paint, const SkFont& font, const SkMatrix& viewMatrix) {
106
107 // hairline glyphs are fast enough, so we don't need to cache them
108 if (SkPaint::kStroke_Style == paint.getStyle() && 0 == paint.getStrokeWidth()) {
109 return true;
110 }
111
112 // we don't cache perspective
113 if (viewMatrix.hasPerspective()) {
114 return true;
115 }
116
117 SkMatrix textMatrix = SkFontPriv::MakeTextMatrix(font);
118 textMatrix.postConcat(viewMatrix);
119
120 // we have a self-imposed maximum, just to limit memory-usage
121 constexpr SkScalar memoryLimit = 256;
122 constexpr SkScalar maxSizeSquared = memoryLimit * memoryLimit;
123
124 auto distance = [&textMatrix](int XIndex, int YIndex) {
125 return textMatrix[XIndex] * textMatrix[XIndex] + textMatrix[YIndex] * textMatrix[YIndex];
126 };
127
128 return distance(SkMatrix::kMScaleX, SkMatrix::kMSkewY ) > maxSizeSquared
129 || distance(SkMatrix::kMSkewX, SkMatrix::kMScaleY) > maxSizeSquared;
130 }
131
dump() const132 SkString SkStrikeSpec::dump() const {
133 return fAutoDescriptor.getDesc()->dumpRec();
134 }
135
MakePDFVector(const SkTypeface & typeface,int * size)136 SkStrikeSpec SkStrikeSpec::MakePDFVector(const SkTypeface& typeface, int* size) {
137 SkFont font;
138 font.setHinting(SkFontHinting::kNone);
139 font.setEdging(SkFont::Edging::kAlias);
140 font.setTypeface(sk_ref_sp(&typeface));
141 int unitsPerEm = typeface.getUnitsPerEm();
142 if (unitsPerEm <= 0) {
143 unitsPerEm = 1024;
144 }
145 if (size) {
146 *size = unitsPerEm;
147 }
148 font.setSize((SkScalar)unitsPerEm);
149
150 return SkStrikeSpec(font,
151 SkPaint(),
152 SkSurfaceProps(0, kUnknown_SkPixelGeometry),
153 SkScalerContextFlags::kFakeGammaAndBoostContrast,
154 SkMatrix::I());
155 }
156
157 #if (defined(SK_GANESH) || defined(SK_GRAPHITE)) && !defined(SK_DISABLE_SDF_TEXT)
158 std::tuple<SkStrikeSpec, SkScalar, sktext::gpu::SDFTMatrixRange>
MakeSDFT(const SkFont & font,const SkPaint & paint,const SkSurfaceProps & surfaceProps,const SkMatrix & deviceMatrix,const SkPoint & textLocation,const sktext::gpu::SDFTControl & control)159 SkStrikeSpec::MakeSDFT(const SkFont& font, const SkPaint& paint,
160 const SkSurfaceProps& surfaceProps, const SkMatrix& deviceMatrix,
161 const SkPoint& textLocation, const sktext::gpu::SDFTControl& control) {
162 // Add filter to the paint which creates the SDFT data for A8 masks.
163 SkPaint dfPaint{paint};
164 dfPaint.setMaskFilter(sktext::gpu::SDFMaskFilter::Make());
165
166 auto [dfFont, strikeToSourceScale, matrixRange] = control.getSDFFont(font, deviceMatrix,
167 textLocation);
168
169 // Adjust the stroke width by the scale factor for drawing the SDFT.
170 dfPaint.setStrokeWidth(paint.getStrokeWidth() / strikeToSourceScale);
171
172 // Check for dashing and adjust the intervals.
173 if (SkPathEffect* pathEffect = paint.getPathEffect(); pathEffect != nullptr) {
174 SkPathEffect::DashInfo dashInfo;
175 if (pathEffect->asADash(&dashInfo) == SkPathEffect::kDash_DashType) {
176 if (dashInfo.fCount > 0) {
177 // Allocate the intervals.
178 std::vector<SkScalar> scaledIntervals(dashInfo.fCount);
179 dashInfo.fIntervals = scaledIntervals.data();
180 // Call again to get the interval data.
181 (void)pathEffect->asADash(&dashInfo);
182 for (SkScalar& interval : scaledIntervals) {
183 interval /= strikeToSourceScale;
184 }
185 auto scaledDashes = SkDashPathEffect::Make(scaledIntervals.data(),
186 scaledIntervals.size(),
187 dashInfo.fPhase / strikeToSourceScale);
188 dfPaint.setPathEffect(scaledDashes);
189 }
190 }
191 }
192
193 // Fake-gamma and subpixel antialiasing are applied in the shader, so we ignore the
194 // passed-in scaler context flags. (It's only used when we fall-back to bitmap text).
195 SkScalerContextFlags flags = SkScalerContextFlags::kNone;
196 SkStrikeSpec strikeSpec(dfFont, dfPaint, surfaceProps, flags, SkMatrix::I());
197
198 return std::make_tuple(std::move(strikeSpec), strikeToSourceScale, matrixRange);
199 }
200 #endif
201
SkStrikeSpec(const SkFont & font,const SkPaint & paint,const SkSurfaceProps & surfaceProps,SkScalerContextFlags scalerContextFlags,const SkMatrix & deviceMatrix)202 SkStrikeSpec::SkStrikeSpec(const SkFont& font, const SkPaint& paint,
203 const SkSurfaceProps& surfaceProps,
204 SkScalerContextFlags scalerContextFlags,
205 const SkMatrix& deviceMatrix) {
206 SkScalerContextEffects effects;
207
208 SkScalerContext::CreateDescriptorAndEffectsUsingPaint(
209 font, paint, surfaceProps, scalerContextFlags, deviceMatrix,
210 &fAutoDescriptor, &effects);
211
212 fMaskFilter = sk_ref_sp(effects.fMaskFilter);
213 fPathEffect = sk_ref_sp(effects.fPathEffect);
214 fTypeface = font.refTypefaceOrDefault();
215 }
216
findOrCreateScopedStrike(sktext::StrikeForGPUCacheInterface * cache) const217 sk_sp<sktext::StrikeForGPU> SkStrikeSpec::findOrCreateScopedStrike(
218 sktext::StrikeForGPUCacheInterface* cache) const {
219 return cache->findOrCreateScopedStrike(*this);
220 }
221
findOrCreateStrike() const222 sk_sp<SkStrike> SkStrikeSpec::findOrCreateStrike() const {
223 SkScalerContextEffects effects{fPathEffect.get(), fMaskFilter.get()};
224 return SkStrikeCache::GlobalStrikeCache()->findOrCreateStrike(*this);
225 }
226
findOrCreateStrike(SkStrikeCache * cache) const227 sk_sp<SkStrike> SkStrikeSpec::findOrCreateStrike(SkStrikeCache* cache) const {
228 SkScalerContextEffects effects{fPathEffect.get(), fMaskFilter.get()};
229 return cache->findOrCreateStrike(*this);
230 }
231
SkBulkGlyphMetrics(const SkStrikeSpec & spec)232 SkBulkGlyphMetrics::SkBulkGlyphMetrics(const SkStrikeSpec& spec)
233 : fStrike{spec.findOrCreateStrike()} { }
234
glyphs(SkSpan<const SkGlyphID> glyphIDs)235 SkSpan<const SkGlyph*> SkBulkGlyphMetrics::glyphs(SkSpan<const SkGlyphID> glyphIDs) {
236 fGlyphs.reset(glyphIDs.size());
237 return fStrike->metrics(glyphIDs, fGlyphs.get());
238 }
239
glyph(SkGlyphID glyphID)240 const SkGlyph* SkBulkGlyphMetrics::glyph(SkGlyphID glyphID) {
241 return this->glyphs(SkSpan<const SkGlyphID>{&glyphID, 1})[0];
242 }
243
SkBulkGlyphMetricsAndPaths(const SkStrikeSpec & spec)244 SkBulkGlyphMetricsAndPaths::SkBulkGlyphMetricsAndPaths(const SkStrikeSpec& spec)
245 : fStrike{spec.findOrCreateStrike()} { }
246
SkBulkGlyphMetricsAndPaths(sk_sp<SkStrike> && strike)247 SkBulkGlyphMetricsAndPaths::SkBulkGlyphMetricsAndPaths(sk_sp<SkStrike>&& strike)
248 : fStrike{std::move(strike)} { }
249
250 SkBulkGlyphMetricsAndPaths::~SkBulkGlyphMetricsAndPaths() = default;
251
glyphs(SkSpan<const SkGlyphID> glyphIDs)252 SkSpan<const SkGlyph*> SkBulkGlyphMetricsAndPaths::glyphs(SkSpan<const SkGlyphID> glyphIDs) {
253 fGlyphs.reset(glyphIDs.size());
254 return fStrike->preparePaths(glyphIDs, fGlyphs.get());
255 }
256
glyph(SkGlyphID glyphID)257 const SkGlyph* SkBulkGlyphMetricsAndPaths::glyph(SkGlyphID glyphID) {
258 return this->glyphs(SkSpan<const SkGlyphID>{&glyphID, 1})[0];
259 }
260
findIntercepts(const SkScalar * bounds,SkScalar scale,SkScalar xPos,const SkGlyph * glyph,SkScalar * array,int * count)261 void SkBulkGlyphMetricsAndPaths::findIntercepts(
262 const SkScalar* bounds, SkScalar scale, SkScalar xPos,
263 const SkGlyph* glyph, SkScalar* array, int* count) {
264 // TODO(herb): remove this abominable const_cast. Do the intercepts really need to be on the
265 // glyph?
266 fStrike->findIntercepts(bounds, scale, xPos, const_cast<SkGlyph*>(glyph), array, count);
267 }
268
SkBulkGlyphMetricsAndDrawables(const SkStrikeSpec & spec)269 SkBulkGlyphMetricsAndDrawables::SkBulkGlyphMetricsAndDrawables(const SkStrikeSpec& spec)
270 : fStrike{spec.findOrCreateStrike()} { }
271
SkBulkGlyphMetricsAndDrawables(sk_sp<SkStrike> && strike)272 SkBulkGlyphMetricsAndDrawables::SkBulkGlyphMetricsAndDrawables(sk_sp<SkStrike>&& strike)
273 : fStrike{std::move(strike)} { }
274
275 SkBulkGlyphMetricsAndDrawables::~SkBulkGlyphMetricsAndDrawables() = default;
276
glyphs(SkSpan<const SkGlyphID> glyphIDs)277 SkSpan<const SkGlyph*> SkBulkGlyphMetricsAndDrawables::glyphs(SkSpan<const SkGlyphID> glyphIDs) {
278 fGlyphs.reset(glyphIDs.size());
279 return fStrike->prepareDrawables(glyphIDs, fGlyphs.get());
280 }
281
glyph(SkGlyphID glyphID)282 const SkGlyph* SkBulkGlyphMetricsAndDrawables::glyph(SkGlyphID glyphID) {
283 return this->glyphs(SkSpan<const SkGlyphID>{&glyphID, 1})[0];
284 }
285
SkBulkGlyphMetricsAndImages(const SkStrikeSpec & spec)286 SkBulkGlyphMetricsAndImages::SkBulkGlyphMetricsAndImages(const SkStrikeSpec& spec)
287 : fStrike{spec.findOrCreateStrike()} { }
288
SkBulkGlyphMetricsAndImages(sk_sp<SkStrike> && strike)289 SkBulkGlyphMetricsAndImages::SkBulkGlyphMetricsAndImages(sk_sp<SkStrike>&& strike)
290 : fStrike{std::move(strike)} { }
291
292 SkBulkGlyphMetricsAndImages::~SkBulkGlyphMetricsAndImages() = default;
293
glyphs(SkSpan<const SkPackedGlyphID> glyphIDs)294 SkSpan<const SkGlyph*> SkBulkGlyphMetricsAndImages::glyphs(SkSpan<const SkPackedGlyphID> glyphIDs) {
295 fGlyphs.reset(glyphIDs.size());
296 return fStrike->prepareImages(glyphIDs, fGlyphs.get());
297 }
298
glyph(SkPackedGlyphID packedID)299 const SkGlyph* SkBulkGlyphMetricsAndImages::glyph(SkPackedGlyphID packedID) {
300 return this->glyphs(SkSpan<const SkPackedGlyphID>{&packedID, 1})[0];
301 }
302
descriptor() const303 const SkDescriptor& SkBulkGlyphMetricsAndImages::descriptor() const {
304 return fStrike->getDescriptor();
305 }
306