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 "src/core/SkDraw.h"
12 #include "src/core/SkFontPriv.h"
13 #include "src/core/SkStrikeCache.h"
14 #include "src/core/SkTLazy.h"
15
16 #if SK_SUPPORT_GPU
17 #include "src/gpu/text/GrSDFMaskFilter.h"
18 #include "src/gpu/text/GrSDFTControl.h"
19 #include "src/gpu/text/GrStrikeCache.h"
20 #endif
21
SkStrikeSpec(const SkDescriptor & descriptor,sk_sp<SkTypeface> typeface)22 SkStrikeSpec::SkStrikeSpec(const SkDescriptor& descriptor, sk_sp<SkTypeface> typeface)
23 : fAutoDescriptor{descriptor}
24 , fTypeface{std::move(typeface)} {}
25
26 SkStrikeSpec::SkStrikeSpec(const SkStrikeSpec&) = default;
27 SkStrikeSpec::SkStrikeSpec(SkStrikeSpec&&) = default;
28 SkStrikeSpec::~SkStrikeSpec() = default;
29
MakeMask(const SkFont & font,const SkPaint & paint,const SkSurfaceProps & surfaceProps,SkScalerContextFlags scalerContextFlags,const SkMatrix & deviceMatrix)30 SkStrikeSpec SkStrikeSpec::MakeMask(const SkFont& font, const SkPaint& paint,
31 const SkSurfaceProps& surfaceProps,
32 SkScalerContextFlags scalerContextFlags,
33 const SkMatrix& deviceMatrix) {
34
35 return SkStrikeSpec(font, paint, surfaceProps, scalerContextFlags, deviceMatrix);
36 }
37
MakePath(const SkFont & font,const SkPaint & paint,const SkSurfaceProps & surfaceProps,SkScalerContextFlags scalerContextFlags)38 std::tuple<SkStrikeSpec, SkScalar> SkStrikeSpec::MakePath(
39 const SkFont& font, const SkPaint& paint,
40 const SkSurfaceProps& surfaceProps,
41 SkScalerContextFlags scalerContextFlags) {
42
43 // setup our std runPaint, in hopes of getting hits in the cache
44 SkPaint pathPaint{paint};
45 SkFont pathFont{font};
46
47 // The sub-pixel position will always happen when transforming to the screen.
48 pathFont.setSubpixel(false);
49
50 // The factor to get from the size stored in the strike to the size needed for
51 // the source.
52 SkScalar strikeToSourceScale = pathFont.setupForAsPaths(&pathPaint);
53
54 return {SkStrikeSpec(pathFont, pathPaint, surfaceProps, scalerContextFlags, SkMatrix::I()),
55 strikeToSourceScale};
56 }
57
MakeSourceFallback(const SkFont & font,const SkPaint & paint,const SkSurfaceProps & surfaceProps,SkScalerContextFlags scalerContextFlags,SkScalar maxSourceGlyphDimension)58 std::tuple<SkStrikeSpec, SkScalar> SkStrikeSpec::MakeSourceFallback(
59 const SkFont& font,
60 const SkPaint& paint,
61 const SkSurfaceProps& surfaceProps,
62 SkScalerContextFlags scalerContextFlags,
63 SkScalar maxSourceGlyphDimension) {
64
65 // Subtract 2 to account for the bilerp pad around the glyph
66 SkScalar maxAtlasDimension = SkStrikeCommon::kSkSideTooBigForAtlas - 2;
67
68 SkScalar runFontTextSize = font.getSize();
69 SkScalar fallbackTextSize = runFontTextSize;
70 if (maxSourceGlyphDimension > maxAtlasDimension) {
71 // Scale the text size down so the long side of all the glyphs will fit in the atlas.
72 fallbackTextSize = SkScalarFloorToScalar(
73 (maxAtlasDimension / maxSourceGlyphDimension) * runFontTextSize);
74 }
75
76 SkFont fallbackFont{font};
77 fallbackFont.setSize(fallbackTextSize);
78
79 // No sub-pixel needed. The transform to the screen will take care of sub-pixel positioning.
80 fallbackFont.setSubpixel(false);
81
82 // The scale factor to go from strike size to the source size for glyphs.
83 SkScalar strikeToSourceScale = runFontTextSize / fallbackTextSize;
84
85 return {SkStrikeSpec(fallbackFont, paint, surfaceProps, scalerContextFlags, SkMatrix::I()),
86 strikeToSourceScale};
87 }
88
MakeCanonicalized(const SkFont & font,const SkPaint * paint)89 std::tuple<SkStrikeSpec, SkScalar> SkStrikeSpec::MakeCanonicalized(
90 const SkFont& font, const SkPaint* paint) {
91 SkPaint canonicalizedPaint;
92 if (paint != nullptr) {
93 canonicalizedPaint = *paint;
94 }
95
96 const SkFont* canonicalizedFont = &font;
97 SkTLazy<SkFont> pathFont;
98 SkScalar strikeToSourceScale = 1;
99 if (ShouldDrawAsPath(canonicalizedPaint, font, SkMatrix::I())) {
100 canonicalizedFont = pathFont.set(font);
101 strikeToSourceScale = pathFont->setupForAsPaths(nullptr);
102 canonicalizedPaint.reset();
103 }
104
105 return {SkStrikeSpec(*canonicalizedFont, canonicalizedPaint,
106 SkSurfaceProps(), kFakeGammaAndBoostContrast, SkMatrix::I()),
107 strikeToSourceScale};
108 }
109
MakeWithNoDevice(const SkFont & font,const SkPaint * paint)110 SkStrikeSpec SkStrikeSpec::MakeWithNoDevice(const SkFont& font, const SkPaint* paint) {
111 SkPaint setupPaint;
112 if (paint != nullptr) {
113 setupPaint = *paint;
114 }
115
116 return SkStrikeSpec(
117 font, setupPaint, SkSurfaceProps(), kFakeGammaAndBoostContrast, SkMatrix::I());
118 }
119
ShouldDrawAsPath(const SkPaint & paint,const SkFont & font,const SkMatrix & viewMatrix)120 bool SkStrikeSpec::ShouldDrawAsPath(
121 const SkPaint& paint, const SkFont& font, const SkMatrix& viewMatrix) {
122
123 // hairline glyphs are fast enough, so we don't need to cache them
124 if (SkPaint::kStroke_Style == paint.getStyle() && 0 == paint.getStrokeWidth()) {
125 return true;
126 }
127
128 // we don't cache perspective
129 if (viewMatrix.hasPerspective()) {
130 return true;
131 }
132
133 SkMatrix textMatrix = SkFontPriv::MakeTextMatrix(font);
134 textMatrix.postConcat(viewMatrix);
135
136 // we have a self-imposed maximum, just to limit memory-usage
137 constexpr SkScalar memoryLimit = 256;
138 constexpr SkScalar maxSizeSquared = memoryLimit * memoryLimit;
139
140 auto distance = [&textMatrix](int XIndex, int YIndex) {
141 return textMatrix[XIndex] * textMatrix[XIndex] + textMatrix[YIndex] * textMatrix[YIndex];
142 };
143
144 return distance(SkMatrix::kMScaleX, SkMatrix::kMSkewY ) > maxSizeSquared
145 || distance(SkMatrix::kMSkewX, SkMatrix::kMScaleY) > maxSizeSquared;
146 }
147
dump() const148 SkString SkStrikeSpec::dump() const {
149 return fAutoDescriptor.getDesc()->dumpRec();
150 }
151
MakePDFVector(const SkTypeface & typeface,int * size)152 SkStrikeSpec SkStrikeSpec::MakePDFVector(const SkTypeface& typeface, int* size) {
153 SkFont font;
154 font.setHinting(SkFontHinting::kNone);
155 font.setEdging(SkFont::Edging::kAlias);
156 font.setTypeface(sk_ref_sp(&typeface));
157 int unitsPerEm = typeface.getUnitsPerEm();
158 if (unitsPerEm <= 0) {
159 unitsPerEm = 1024;
160 }
161 if (size) {
162 *size = unitsPerEm;
163 }
164 font.setSize((SkScalar)unitsPerEm);
165
166 return SkStrikeSpec(font,
167 SkPaint(),
168 SkSurfaceProps(0, kUnknown_SkPixelGeometry),
169 kFakeGammaAndBoostContrast,
170 SkMatrix::I());
171 }
172
173 #if SK_SUPPORT_GPU
174 std::tuple<SkStrikeSpec, SkScalar, SkScalar, SkScalar>
MakeSDFT(const SkFont & font,const SkPaint & paint,const SkSurfaceProps & surfaceProps,const SkMatrix & deviceMatrix,const GrSDFTControl & control)175 SkStrikeSpec::MakeSDFT(const SkFont& font, const SkPaint& paint,
176 const SkSurfaceProps& surfaceProps, const SkMatrix& deviceMatrix,
177 const GrSDFTControl& control) {
178 SkPaint dfPaint{paint};
179 dfPaint.setMaskFilter(GrSDFMaskFilter::Make());
180 SkScalar strikeToSourceScale;
181 SkFont dfFont = control.getSDFFont(font, deviceMatrix, &strikeToSourceScale);
182
183 // Fake-gamma and subpixel antialiasing are applied in the shader, so we ignore the
184 // passed-in scaler context flags. (It's only used when we fall-back to bitmap text).
185 SkScalerContextFlags flags = SkScalerContextFlags::kNone;
186
187 SkScalar minScale, maxScale;
188 std::tie(minScale, maxScale) = control.computeSDFMinMaxScale(font.getSize(), deviceMatrix);
189
190 SkStrikeSpec strikeSpec(dfFont, dfPaint, surfaceProps, flags, SkMatrix::I());
191
192 return std::make_tuple(std::move(strikeSpec), strikeToSourceScale, minScale, maxScale);
193 }
194
findOrCreateGrStrike(GrStrikeCache * cache) const195 sk_sp<GrTextStrike> SkStrikeSpec::findOrCreateGrStrike(GrStrikeCache* cache) const {
196 return cache->findOrCreateStrike(*this);
197 }
198 #endif
199
SkStrikeSpec(const SkFont & font,const SkPaint & paint,const SkSurfaceProps & surfaceProps,SkScalerContextFlags scalerContextFlags,const SkMatrix & deviceMatrix)200 SkStrikeSpec::SkStrikeSpec(const SkFont& font, const SkPaint& paint,
201 const SkSurfaceProps& surfaceProps,
202 SkScalerContextFlags scalerContextFlags,
203 const SkMatrix& deviceMatrix) {
204 SkScalerContextEffects effects;
205
206 SkScalerContext::CreateDescriptorAndEffectsUsingPaint(
207 font, paint, surfaceProps, scalerContextFlags, deviceMatrix,
208 &fAutoDescriptor, &effects);
209
210 fMaskFilter = sk_ref_sp(effects.fMaskFilter);
211 fPathEffect = sk_ref_sp(effects.fPathEffect);
212 fTypeface = font.refTypefaceOrDefault();
213 }
214
findOrCreateScopedStrike(SkStrikeForGPUCacheInterface * cache) const215 SkScopedStrikeForGPU SkStrikeSpec::findOrCreateScopedStrike(
216 SkStrikeForGPUCacheInterface* cache) const {
217 return cache->findOrCreateScopedStrike(*this);
218 }
219
findOrCreateStrike() const220 sk_sp<SkStrike> SkStrikeSpec::findOrCreateStrike() const {
221 SkScalerContextEffects effects{fPathEffect.get(), fMaskFilter.get()};
222 return SkStrikeCache::GlobalStrikeCache()->findOrCreateStrike(*this);
223 }
224
findOrCreateStrike(SkStrikeCache * cache) const225 sk_sp<SkStrike> SkStrikeSpec::findOrCreateStrike(SkStrikeCache* cache) const {
226 SkScalerContextEffects effects{fPathEffect.get(), fMaskFilter.get()};
227 return cache->findOrCreateStrike(*this);
228 }
229
SkBulkGlyphMetrics(const SkStrikeSpec & spec)230 SkBulkGlyphMetrics::SkBulkGlyphMetrics(const SkStrikeSpec& spec)
231 : fStrike{spec.findOrCreateStrike()} { }
232
glyphs(SkSpan<const SkGlyphID> glyphIDs)233 SkSpan<const SkGlyph*> SkBulkGlyphMetrics::glyphs(SkSpan<const SkGlyphID> glyphIDs) {
234 fGlyphs.reset(glyphIDs.size());
235 return fStrike->metrics(glyphIDs, fGlyphs.get());
236 }
237
glyph(SkGlyphID glyphID)238 const SkGlyph* SkBulkGlyphMetrics::glyph(SkGlyphID glyphID) {
239 return this->glyphs(SkSpan<const SkGlyphID>{&glyphID, 1})[0];
240 }
241
SkBulkGlyphMetricsAndPaths(const SkStrikeSpec & spec)242 SkBulkGlyphMetricsAndPaths::SkBulkGlyphMetricsAndPaths(const SkStrikeSpec& spec)
243 : fStrike{spec.findOrCreateStrike()} { }
244
SkBulkGlyphMetricsAndPaths(sk_sp<SkStrike> && strike)245 SkBulkGlyphMetricsAndPaths::SkBulkGlyphMetricsAndPaths(sk_sp<SkStrike>&& strike)
246 : fStrike{std::move(strike)} { }
247
248 SkBulkGlyphMetricsAndPaths::~SkBulkGlyphMetricsAndPaths() = default;
249
glyphs(SkSpan<const SkGlyphID> glyphIDs)250 SkSpan<const SkGlyph*> SkBulkGlyphMetricsAndPaths::glyphs(SkSpan<const SkGlyphID> glyphIDs) {
251 fGlyphs.reset(glyphIDs.size());
252 return fStrike->preparePaths(glyphIDs, fGlyphs.get());
253 }
254
glyph(SkGlyphID glyphID)255 const SkGlyph* SkBulkGlyphMetricsAndPaths::glyph(SkGlyphID glyphID) {
256 return this->glyphs(SkSpan<const SkGlyphID>{&glyphID, 1})[0];
257 }
258
findIntercepts(const SkScalar * bounds,SkScalar scale,SkScalar xPos,const SkGlyph * glyph,SkScalar * array,int * count)259 void SkBulkGlyphMetricsAndPaths::findIntercepts(
260 const SkScalar* bounds, SkScalar scale, SkScalar xPos,
261 const SkGlyph* glyph, SkScalar* array, int* count) {
262 // TODO(herb): remove this abominable const_cast. Do the intercepts really need to be on the
263 // glyph?
264 fStrike->findIntercepts(bounds, scale, xPos, const_cast<SkGlyph*>(glyph), array, count);
265 }
266
SkBulkGlyphMetricsAndImages(const SkStrikeSpec & spec)267 SkBulkGlyphMetricsAndImages::SkBulkGlyphMetricsAndImages(const SkStrikeSpec& spec)
268 : fStrike{spec.findOrCreateStrike()} { }
269
SkBulkGlyphMetricsAndImages(sk_sp<SkStrike> && strike)270 SkBulkGlyphMetricsAndImages::SkBulkGlyphMetricsAndImages(sk_sp<SkStrike>&& strike)
271 : fStrike{std::move(strike)} { }
272
273 SkBulkGlyphMetricsAndImages::~SkBulkGlyphMetricsAndImages() = default;
274
glyphs(SkSpan<const SkPackedGlyphID> glyphIDs)275 SkSpan<const SkGlyph*> SkBulkGlyphMetricsAndImages::glyphs(SkSpan<const SkPackedGlyphID> glyphIDs) {
276 fGlyphs.reset(glyphIDs.size());
277 return fStrike->prepareImages(glyphIDs, fGlyphs.get());
278 }
279
glyph(SkPackedGlyphID packedID)280 const SkGlyph* SkBulkGlyphMetricsAndImages::glyph(SkPackedGlyphID packedID) {
281 return this->glyphs(SkSpan<const SkPackedGlyphID>{&packedID, 1})[0];
282 }
283
descriptor() const284 const SkDescriptor& SkBulkGlyphMetricsAndImages::descriptor() const {
285 return fStrike->getDescriptor();
286 }
287