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