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