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