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
MakeMask(const SkFont & font,const SkPaint & paint,const SkSurfaceProps & surfaceProps,SkScalerContextFlags scalerContextFlags,const SkMatrix & deviceMatrix)16 SkStrikeSpec SkStrikeSpec::MakeMask(const SkFont& font, const SkPaint& paint,
17 const SkSurfaceProps& surfaceProps,
18 SkScalerContextFlags scalerContextFlags,
19 const SkMatrix& deviceMatrix) {
20 SkStrikeSpec storage;
21
22 storage.commonSetup(font, paint, surfaceProps, scalerContextFlags, deviceMatrix);
23
24 return storage;
25 }
26
MakePath(const SkFont & font,const SkPaint & paint,const SkSurfaceProps & surfaceProps,SkScalerContextFlags scalerContextFlags)27 SkStrikeSpec SkStrikeSpec::MakePath(const SkFont& font, const SkPaint& paint,
28 const SkSurfaceProps& surfaceProps,
29 SkScalerContextFlags scalerContextFlags) {
30 SkStrikeSpec storage;
31
32 // setup our std runPaint, in hopes of getting hits in the cache
33 SkPaint pathPaint{paint};
34 SkFont pathFont{font};
35
36 // The factor to get from the size stored in the strike to the size needed for
37 // the source.
38 storage.fStrikeToSourceRatio = pathFont.setupForAsPaths(&pathPaint);
39
40 // The sub-pixel position will always happen when transforming to the screen.
41 pathFont.setSubpixel(false);
42
43 storage.commonSetup(pathFont, pathPaint, surfaceProps, scalerContextFlags, SkMatrix::I());
44
45 return storage;
46 }
47
MakeSourceFallback(const SkFont & font,const SkPaint & paint,const SkSurfaceProps & surfaceProps,SkScalerContextFlags scalerContextFlags,SkScalar maxSourceGlyphDimension)48 SkStrikeSpec SkStrikeSpec::MakeSourceFallback(
49 const SkFont& font,
50 const SkPaint& paint,
51 const SkSurfaceProps& surfaceProps,
52 SkScalerContextFlags scalerContextFlags,
53 SkScalar maxSourceGlyphDimension) {
54 SkStrikeSpec storage;
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
61 // Scale the text size down so the long side of all the glyphs will fit in the atlas.
62 SkScalar fallbackTextSize = SkScalarFloorToScalar(
63 (maxAtlasDimension / maxSourceGlyphDimension) * runFontTextSize);
64
65 SkFont fallbackFont{font};
66 fallbackFont.setSize(fallbackTextSize);
67
68 // No sub-pixel needed. The transform to the screen will take care of sub-pixel positioning.
69 fallbackFont.setSubpixel(false);
70
71 // The scale factor to go from strike size to the source size for glyphs.
72 storage.fStrikeToSourceRatio = runFontTextSize / fallbackTextSize;
73
74 storage.commonSetup(fallbackFont, paint, surfaceProps, scalerContextFlags, SkMatrix::I());
75
76 return storage;
77 }
78
MakeCanonicalized(const SkFont & font,const SkPaint * paint)79 SkStrikeSpec SkStrikeSpec::MakeCanonicalized(const SkFont& font, const SkPaint* paint) {
80 SkStrikeSpec storage;
81
82 SkPaint canonicalizedPaint;
83 if (paint != nullptr) {
84 canonicalizedPaint = *paint;
85 }
86
87 const SkFont* canonicalizedFont = &font;
88 SkTLazy<SkFont> pathFont;
89 if (ShouldDrawAsPath(canonicalizedPaint, font, SkMatrix::I())) {
90 canonicalizedFont = pathFont.set(font);
91 storage.fStrikeToSourceRatio = pathFont->setupForAsPaths(nullptr);
92 canonicalizedPaint.reset();
93 }
94
95 storage.commonSetup(*canonicalizedFont,
96 canonicalizedPaint,
97 SkSurfaceProps(SkSurfaceProps::kLegacyFontHost_InitType),
98 kFakeGammaAndBoostContrast,
99 SkMatrix::I());
100 return storage;
101 }
102
MakeWithNoDevice(const SkFont & font,const SkPaint * paint)103 SkStrikeSpec SkStrikeSpec::MakeWithNoDevice(const SkFont& font, const SkPaint* paint) {
104 SkStrikeSpec storage;
105
106 SkPaint setupPaint;
107 if (paint != nullptr) {
108 setupPaint = *paint;
109 }
110
111 storage.commonSetup(font,
112 setupPaint,
113 SkSurfaceProps(SkSurfaceProps::kLegacyFontHost_InitType),
114 kFakeGammaAndBoostContrast,
115 SkMatrix::I());
116
117 return storage;
118
119 }
120
MakeDefault()121 SkStrikeSpec SkStrikeSpec::MakeDefault() {
122 SkFont defaultFont;
123 return MakeCanonicalized(defaultFont);
124 }
125
ShouldDrawAsPath(const SkPaint & paint,const SkFont & font,const SkMatrix & viewMatrix)126 bool SkStrikeSpec::ShouldDrawAsPath(
127 const SkPaint& paint, const SkFont& font, const SkMatrix& viewMatrix) {
128
129 // hairline glyphs are fast enough so we don't need to cache them
130 if (SkPaint::kStroke_Style == paint.getStyle() && 0 == paint.getStrokeWidth()) {
131 return true;
132 }
133
134 // we don't cache perspective
135 if (viewMatrix.hasPerspective()) {
136 return true;
137 }
138
139 SkMatrix textMatrix = SkFontPriv::MakeTextMatrix(font);
140 textMatrix.postConcat(viewMatrix);
141
142 // we have a self-imposed maximum, just for memory-usage sanity
143 SkScalar limit = SkMinScalar(SkGraphics::GetFontCachePointSizeLimit(), 1024);
144 SkScalar maxSizeSquared = limit * limit;
145
146 auto distance = [&textMatrix](int XIndex, int YIndex) {
147 return textMatrix[XIndex] * textMatrix[XIndex] + textMatrix[YIndex] * textMatrix[YIndex];
148 };
149
150 return distance(SkMatrix::kMScaleX, SkMatrix::kMSkewY ) > maxSizeSquared
151 || distance(SkMatrix::kMSkewX, SkMatrix::kMScaleY) > maxSizeSquared;
152 }
153
MakePDFVector(const SkTypeface & typeface,int * size)154 SkStrikeSpec SkStrikeSpec::MakePDFVector(const SkTypeface& typeface, int* size) {
155 SkFont font;
156 font.setHinting(SkFontHinting::kNone);
157 font.setEdging(SkFont::Edging::kAlias);
158 font.setTypeface(sk_ref_sp(&typeface));
159 int unitsPerEm = typeface.getUnitsPerEm();
160 if (unitsPerEm <= 0) {
161 unitsPerEm = 1024;
162 }
163 if (size) {
164 *size = unitsPerEm;
165 }
166 font.setSize((SkScalar)unitsPerEm);
167
168 SkStrikeSpec storage;
169 storage.commonSetup(font,
170 SkPaint(),
171 SkSurfaceProps(0, kUnknown_SkPixelGeometry),
172 kFakeGammaAndBoostContrast,
173 SkMatrix::I());
174
175 return storage;
176 }
177
178 #if SK_SUPPORT_GPU
179 std::tuple<SkStrikeSpec, SkScalar, SkScalar>
MakeSDFT(const SkFont & font,const SkPaint & paint,const SkSurfaceProps & surfaceProps,const SkMatrix & deviceMatrix,const GrTextContext::Options & options)180 SkStrikeSpec::MakeSDFT(const SkFont& font, const SkPaint& paint,
181 const SkSurfaceProps& surfaceProps, const SkMatrix& deviceMatrix,
182 const GrTextContext::Options& options) {
183 SkStrikeSpec storage;
184
185 SkPaint dfPaint = GrTextContext::InitDistanceFieldPaint(paint);
186 SkFont dfFont = GrTextContext::InitDistanceFieldFont(
187 font, deviceMatrix, options, &storage.fStrikeToSourceRatio);
188
189 // Fake-gamma and subpixel antialiasing are applied in the shader, so we ignore the
190 // passed-in scaler context flags. (It's only used when we fall-back to bitmap text).
191 SkScalerContextFlags flags = SkScalerContextFlags::kNone;
192
193 SkScalar minScale, maxScale;
194 std::tie(minScale, maxScale) = GrTextContext::InitDistanceFieldMinMaxScale(
195 font.getSize(), deviceMatrix, options);
196
197 storage.commonSetup(dfFont, dfPaint, surfaceProps, flags, SkMatrix::I());
198
199 return std::tie(storage, minScale, maxScale);
200 }
201
findOrCreateGrStrike(GrStrikeCache * cache) const202 sk_sp<GrTextStrike> SkStrikeSpec::findOrCreateGrStrike(GrStrikeCache* cache) const {
203 return cache->getStrike(*fAutoDescriptor.getDesc());
204 }
205 #endif
206
commonSetup(const SkFont & font,const SkPaint & paint,const SkSurfaceProps & surfaceProps,SkScalerContextFlags scalerContextFlags,const SkMatrix & deviceMatrix)207 void SkStrikeSpec::commonSetup(const SkFont& font, const SkPaint& paint,
208 const SkSurfaceProps& surfaceProps,
209 SkScalerContextFlags scalerContextFlags,
210 const SkMatrix& deviceMatrix) {
211 SkScalerContextEffects effects;
212
213 SkScalerContext::CreateDescriptorAndEffectsUsingPaint(
214 font, paint, surfaceProps, scalerContextFlags, deviceMatrix,
215 &fAutoDescriptor, &effects);
216
217 fMaskFilter = sk_ref_sp(effects.fMaskFilter);
218 fPathEffect = sk_ref_sp(effects.fPathEffect);
219 fTypeface = font.refTypefaceOrDefault();
220 }
221
findOrCreateScopedStrike(SkStrikeCacheInterface * cache) const222 SkScopedStrike SkStrikeSpec::findOrCreateScopedStrike(SkStrikeCacheInterface* cache) const {
223 SkScalerContextEffects effects{fPathEffect.get(), fMaskFilter.get()};
224 return cache->findOrCreateScopedStrike(*fAutoDescriptor.getDesc(), effects, *fTypeface);
225 }
226
findOrCreateExclusiveStrike(SkStrikeCache * cache) const227 SkExclusiveStrikePtr SkStrikeSpec::findOrCreateExclusiveStrike(SkStrikeCache* cache) const {
228 SkScalerContextEffects effects{fPathEffect.get(), fMaskFilter.get()};
229 return cache->findOrCreateStrikeExclusive(*fAutoDescriptor.getDesc(), effects, *fTypeface);
230 }
231
SkBulkGlyphMetrics(const SkStrikeSpec & spec)232 SkBulkGlyphMetrics::SkBulkGlyphMetrics(const SkStrikeSpec& spec)
233 : fStrike{spec.findOrCreateExclusiveStrike()} { }
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
SkBulkGlyphMetricsAndPaths(const SkStrikeSpec & spec)240 SkBulkGlyphMetricsAndPaths::SkBulkGlyphMetricsAndPaths(const SkStrikeSpec& spec)
241 : fStrike{spec.findOrCreateExclusiveStrike()} { }
242
glyphs(SkSpan<const SkGlyphID> glyphIDs)243 SkSpan<const SkGlyph*> SkBulkGlyphMetricsAndPaths::glyphs(SkSpan<const SkGlyphID> glyphIDs) {
244 fGlyphs.reset(glyphIDs.size());
245 return fStrike->preparePaths(glyphIDs, fGlyphs.get());
246 }
247
SkBulkGlyphMetricsAndImages(const SkStrikeSpec & spec)248 SkBulkGlyphMetricsAndImages::SkBulkGlyphMetricsAndImages(const SkStrikeSpec& spec)
249 : fStrike{spec.findOrCreateExclusiveStrike()} { }
250
glyphs(SkSpan<const SkPackedGlyphID> glyphIDs)251 SkSpan<const SkGlyph*> SkBulkGlyphMetricsAndImages::glyphs(SkSpan<const SkPackedGlyphID> glyphIDs) {
252 fGlyphs.reset(glyphIDs.size());
253 return fStrike->prepareImages(glyphIDs, fGlyphs.get());
254 }
255