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/GrSDFTOptions.h" |
19 | #include "src/gpu/text/GrStrikeCache.h" |
20 | #endif |
21 | |
22 | SkStrikeSpec SkStrikeSpec::MakeMask(const SkFont& font, const SkPaint& paint, |
23 | const SkSurfaceProps& surfaceProps, |
24 | SkScalerContextFlags scalerContextFlags, |
25 | const SkMatrix& deviceMatrix) { |
26 | SkStrikeSpec storage; |
27 | |
28 | storage.commonSetup(font, paint, surfaceProps, scalerContextFlags, deviceMatrix); |
29 | |
30 | return storage; |
31 | } |
32 | |
33 | SkStrikeSpec SkStrikeSpec::MakePath(const SkFont& font, const SkPaint& paint, |
34 | const SkSurfaceProps& surfaceProps, |
35 | SkScalerContextFlags scalerContextFlags) { |
36 | SkStrikeSpec storage; |
37 | |
38 | // setup our std runPaint, in hopes of getting hits in the cache |
39 | SkPaint pathPaint{paint}; |
40 | SkFont pathFont{font}; |
41 | |
42 | // The factor to get from the size stored in the strike to the size needed for |
43 | // the source. |
44 | storage.fStrikeToSourceRatio = pathFont.setupForAsPaths(&pathPaint); |
45 | |
46 | // The sub-pixel position will always happen when transforming to the screen. |
47 | pathFont.setSubpixel(false); |
48 | |
49 | storage.commonSetup(pathFont, pathPaint, surfaceProps, scalerContextFlags, SkMatrix::I()); |
50 | |
51 | return storage; |
52 | } |
53 | |
54 | SkStrikeSpec SkStrikeSpec::MakeSourceFallback( |
55 | const SkFont& font, |
56 | const SkPaint& paint, |
57 | const SkSurfaceProps& surfaceProps, |
58 | SkScalerContextFlags scalerContextFlags, |
59 | SkScalar maxSourceGlyphDimension) { |
60 | SkStrikeSpec storage; |
61 | |
62 | // Subtract 2 to account for the bilerp pad around the glyph |
63 | SkScalar maxAtlasDimension = SkStrikeCommon::kSkSideTooBigForAtlas - 2; |
64 | |
65 | SkScalar runFontTextSize = font.getSize(); |
66 | SkScalar fallbackTextSize = runFontTextSize; |
67 | if (maxSourceGlyphDimension > maxAtlasDimension) { |
68 | // Scale the text size down so the long side of all the glyphs will fit in the atlas. |
69 | fallbackTextSize = SkScalarFloorToScalar( |
70 | (maxAtlasDimension / maxSourceGlyphDimension) * runFontTextSize); |
71 | } |
72 | |
73 | SkFont fallbackFont{font}; |
74 | fallbackFont.setSize(fallbackTextSize); |
75 | |
76 | // No sub-pixel needed. The transform to the screen will take care of sub-pixel positioning. |
77 | fallbackFont.setSubpixel(false); |
78 | |
79 | // The scale factor to go from strike size to the source size for glyphs. |
80 | storage.fStrikeToSourceRatio = runFontTextSize / fallbackTextSize; |
81 | |
82 | storage.commonSetup(fallbackFont, paint, surfaceProps, scalerContextFlags, SkMatrix::I()); |
83 | |
84 | return storage; |
85 | } |
86 | |
87 | SkStrikeSpec SkStrikeSpec::MakeCanonicalized(const SkFont& font, const SkPaint* paint) { |
88 | SkStrikeSpec storage; |
89 | |
90 | SkPaint canonicalizedPaint; |
91 | if (paint != nullptr) { |
92 | canonicalizedPaint = *paint; |
93 | } |
94 | |
95 | const SkFont* canonicalizedFont = &font; |
96 | SkTLazy<SkFont> pathFont; |
97 | if (ShouldDrawAsPath(canonicalizedPaint, font, SkMatrix::I())) { |
98 | canonicalizedFont = pathFont.set(font); |
99 | storage.fStrikeToSourceRatio = pathFont->setupForAsPaths(nullptr); |
100 | canonicalizedPaint.reset(); |
101 | } |
102 | |
103 | storage.commonSetup(*canonicalizedFont, |
104 | canonicalizedPaint, |
105 | SkSurfaceProps(SkSurfaceProps::kLegacyFontHost_InitType), |
106 | kFakeGammaAndBoostContrast, |
107 | SkMatrix::I()); |
108 | return storage; |
109 | } |
110 | |
111 | SkStrikeSpec SkStrikeSpec::MakeWithNoDevice(const SkFont& font, const SkPaint* paint) { |
112 | SkStrikeSpec storage; |
113 | |
114 | SkPaint setupPaint; |
115 | if (paint != nullptr) { |
116 | setupPaint = *paint; |
117 | } |
118 | |
119 | storage.commonSetup(font, |
120 | setupPaint, |
121 | SkSurfaceProps(SkSurfaceProps::kLegacyFontHost_InitType), |
122 | kFakeGammaAndBoostContrast, |
123 | SkMatrix::I()); |
124 | |
125 | return storage; |
126 | |
127 | } |
128 | |
129 | SkStrikeSpec SkStrikeSpec::MakeDefault() { |
130 | SkFont defaultFont; |
131 | return MakeCanonicalized(defaultFont); |
132 | } |
133 | |
134 | bool SkStrikeSpec::ShouldDrawAsPath( |
135 | const SkPaint& paint, const SkFont& font, const SkMatrix& viewMatrix) { |
136 | |
137 | // hairline glyphs are fast enough so we don't need to cache them |
138 | if (SkPaint::kStroke_Style == paint.getStyle() && 0 == paint.getStrokeWidth()) { |
139 | return true; |
140 | } |
141 | |
142 | // we don't cache perspective |
143 | if (viewMatrix.hasPerspective()) { |
144 | return true; |
145 | } |
146 | |
147 | SkMatrix textMatrix = SkFontPriv::MakeTextMatrix(font); |
148 | textMatrix.postConcat(viewMatrix); |
149 | |
150 | // we have a self-imposed maximum, just to limit memory-usage |
151 | SkScalar limit = std::min(SkGraphics::GetFontCachePointSizeLimit(), 1024); |
152 | SkScalar maxSizeSquared = limit * limit; |
153 | |
154 | auto distance = [&textMatrix](int XIndex, int YIndex) { |
155 | return textMatrix[XIndex] * textMatrix[XIndex] + textMatrix[YIndex] * textMatrix[YIndex]; |
156 | }; |
157 | |
158 | return distance(SkMatrix::kMScaleX, SkMatrix::kMSkewY ) > maxSizeSquared |
159 | || distance(SkMatrix::kMSkewX, SkMatrix::kMScaleY) > maxSizeSquared; |
160 | } |
161 | |
162 | SkStrikeSpec SkStrikeSpec::MakePDFVector(const SkTypeface& typeface, int* size) { |
163 | SkFont font; |
164 | font.setHinting(SkFontHinting::kNone); |
165 | font.setEdging(SkFont::Edging::kAlias); |
166 | font.setTypeface(sk_ref_sp(&typeface)); |
167 | int unitsPerEm = typeface.getUnitsPerEm(); |
168 | if (unitsPerEm <= 0) { |
169 | unitsPerEm = 1024; |
170 | } |
171 | if (size) { |
172 | *size = unitsPerEm; |
173 | } |
174 | font.setSize((SkScalar)unitsPerEm); |
175 | |
176 | SkStrikeSpec storage; |
177 | storage.commonSetup(font, |
178 | SkPaint(), |
179 | SkSurfaceProps(0, kUnknown_SkPixelGeometry), |
180 | kFakeGammaAndBoostContrast, |
181 | SkMatrix::I()); |
182 | |
183 | return storage; |
184 | } |
185 | |
186 | #if SK_SUPPORT_GPU |
187 | std::tuple<SkStrikeSpec, SkScalar, SkScalar> |
188 | SkStrikeSpec::MakeSDFT(const SkFont& font, const SkPaint& paint, |
189 | const SkSurfaceProps& surfaceProps, const SkMatrix& deviceMatrix, |
190 | const GrSDFTOptions& options) { |
191 | SkStrikeSpec storage; |
192 | |
193 | SkPaint dfPaint{paint}; |
194 | dfPaint.setMaskFilter(GrSDFMaskFilter::Make()); |
195 | SkFont dfFont = options.getSDFFont(font, deviceMatrix, &storage.fStrikeToSourceRatio); |
196 | |
197 | // Fake-gamma and subpixel antialiasing are applied in the shader, so we ignore the |
198 | // passed-in scaler context flags. (It's only used when we fall-back to bitmap text). |
199 | SkScalerContextFlags flags = SkScalerContextFlags::kNone; |
200 | |
201 | SkScalar minScale, maxScale; |
202 | std::tie(minScale, maxScale) = options.computeSDFMinMaxScale(font.getSize(), deviceMatrix); |
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->findOrCreateStrike(*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 | |