• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "include/core/SkPaint.h"
12 #include "include/core/SkPathEffect.h"
13 #include "include/effects/SkDashPathEffect.h"
14 #include "src/base/SkTLazy.h"
15 #include "src/core/SkDraw.h"
16 #include "src/core/SkFontPriv.h"
17 #include "src/core/SkGlyphBuffer.h"
18 #include "src/core/SkStrike.h"
19 #include "src/core/SkStrikeCache.h"
20 
21 #if defined(SK_GANESH) || defined(SK_GRAPHITE)
22 #include "src/text/gpu/SDFMaskFilter.h"
23 #include "src/text/gpu/SDFTControl.h"
24 #include "src/text/gpu/StrikeCache.h"
25 #endif
26 
SkStrikeSpec(const SkDescriptor & descriptor,sk_sp<SkTypeface> typeface)27 SkStrikeSpec::SkStrikeSpec(const SkDescriptor& descriptor, sk_sp<SkTypeface> typeface)
28     : fAutoDescriptor{descriptor}
29     , fTypeface{std::move(typeface)} {}
30 
31 SkStrikeSpec::SkStrikeSpec(const SkStrikeSpec&) = default;
32 SkStrikeSpec::SkStrikeSpec(SkStrikeSpec&&) = default;
33 SkStrikeSpec::~SkStrikeSpec() = default;
34 
MakeMask(const SkFont & font,const SkPaint & paint,const SkSurfaceProps & surfaceProps,SkScalerContextFlags scalerContextFlags,const SkMatrix & deviceMatrix)35 SkStrikeSpec SkStrikeSpec::MakeMask(const SkFont& font, const SkPaint& paint,
36                                     const SkSurfaceProps& surfaceProps,
37                                     SkScalerContextFlags scalerContextFlags,
38                                     const SkMatrix& deviceMatrix) {
39 
40     return SkStrikeSpec(font, paint, surfaceProps, scalerContextFlags, deviceMatrix);
41 }
42 
MakeTransformMask(const SkFont & font,const SkPaint & paint,const SkSurfaceProps & surfaceProps,SkScalerContextFlags scalerContextFlags,const SkMatrix & deviceMatrix)43 SkStrikeSpec SkStrikeSpec::MakeTransformMask(const SkFont& font,
44                                              const SkPaint& paint,
45                                              const SkSurfaceProps& surfaceProps,
46                                              SkScalerContextFlags scalerContextFlags,
47                                              const SkMatrix& deviceMatrix) {
48     SkFont sourceFont{font};
49     sourceFont.setSubpixel(false);
50     return SkStrikeSpec(sourceFont, paint, surfaceProps, scalerContextFlags, deviceMatrix);
51 }
52 
MakePath(const SkFont & font,const SkPaint & paint,const SkSurfaceProps & surfaceProps,SkScalerContextFlags scalerContextFlags)53 std::tuple<SkStrikeSpec, SkScalar> SkStrikeSpec::MakePath(
54         const SkFont& font, const SkPaint& paint,
55         const SkSurfaceProps& surfaceProps,
56         SkScalerContextFlags scalerContextFlags) {
57 
58     // setup our std runPaint, in hopes of getting hits in the cache
59     SkPaint pathPaint{paint};
60     SkFont pathFont{font};
61 
62     // The sub-pixel position will always happen when transforming to the screen.
63     pathFont.setSubpixel(false);
64 
65     // The factor to get from the size stored in the strike to the size needed for
66     // the source.
67     SkScalar strikeToSourceScale = pathFont.setupForAsPaths(&pathPaint);
68 
69     return {SkStrikeSpec(pathFont, pathPaint, surfaceProps, scalerContextFlags, SkMatrix::I()),
70             strikeToSourceScale};
71 }
72 
MakeCanonicalized(const SkFont & font,const SkPaint * paint)73 std::tuple<SkStrikeSpec, SkScalar> SkStrikeSpec::MakeCanonicalized(
74         const SkFont& font, const SkPaint* paint) {
75     SkPaint canonicalizedPaint;
76     if (paint != nullptr) {
77         canonicalizedPaint = *paint;
78     }
79 
80     const SkFont* canonicalizedFont = &font;
81     SkTLazy<SkFont> pathFont;
82     SkScalar strikeToSourceScale = 1;
83     if (ShouldDrawAsPath(canonicalizedPaint, font, SkMatrix::I())) {
84         canonicalizedFont = pathFont.set(font);
85         strikeToSourceScale = pathFont->setupForAsPaths(nullptr);
86         canonicalizedPaint.reset();
87     }
88 
89     return {SkStrikeSpec(*canonicalizedFont, canonicalizedPaint, SkSurfaceProps(),
90                          SkScalerContextFlags::kFakeGammaAndBoostContrast, SkMatrix::I()),
91             strikeToSourceScale};
92 }
93 
MakeWithNoDevice(const SkFont & font,const SkPaint * paint)94 SkStrikeSpec SkStrikeSpec::MakeWithNoDevice(const SkFont& font, const SkPaint* paint) {
95     SkPaint setupPaint;
96     if (paint != nullptr) {
97         setupPaint = *paint;
98     }
99 
100     return SkStrikeSpec(font, setupPaint, SkSurfaceProps(),
101                         SkScalerContextFlags::kFakeGammaAndBoostContrast, SkMatrix::I());
102 }
103 
ShouldDrawAsPath(const SkPaint & paint,const SkFont & font,const SkMatrix & viewMatrix)104 bool SkStrikeSpec::ShouldDrawAsPath(
105         const SkPaint& paint, const SkFont& font, const SkMatrix& viewMatrix) {
106 
107     // hairline glyphs are fast enough, so we don't need to cache them
108     if (SkPaint::kStroke_Style == paint.getStyle() && 0 == paint.getStrokeWidth()) {
109         return true;
110     }
111 
112     // we don't cache perspective
113     if (viewMatrix.hasPerspective()) {
114         return true;
115     }
116 
117     SkMatrix textMatrix = SkFontPriv::MakeTextMatrix(font);
118     textMatrix.postConcat(viewMatrix);
119 
120     // we have a self-imposed maximum, just to limit memory-usage
121     constexpr SkScalar memoryLimit = 256;
122     constexpr SkScalar maxSizeSquared = memoryLimit * memoryLimit;
123 
124     auto distance = [&textMatrix](int XIndex, int YIndex) {
125         return textMatrix[XIndex] * textMatrix[XIndex] + textMatrix[YIndex] * textMatrix[YIndex];
126     };
127 
128     return distance(SkMatrix::kMScaleX, SkMatrix::kMSkewY ) > maxSizeSquared
129         || distance(SkMatrix::kMSkewX,  SkMatrix::kMScaleY) > maxSizeSquared;
130 }
131 
dump() const132 SkString SkStrikeSpec::dump() const {
133     return fAutoDescriptor.getDesc()->dumpRec();
134 }
135 
MakePDFVector(const SkTypeface & typeface,int * size)136 SkStrikeSpec SkStrikeSpec::MakePDFVector(const SkTypeface& typeface, int* size) {
137     SkFont font;
138     font.setHinting(SkFontHinting::kNone);
139     font.setEdging(SkFont::Edging::kAlias);
140     font.setTypeface(sk_ref_sp(&typeface));
141     int unitsPerEm = typeface.getUnitsPerEm();
142     if (unitsPerEm <= 0) {
143         unitsPerEm = 1024;
144     }
145     if (size) {
146         *size = unitsPerEm;
147     }
148     font.setSize((SkScalar)unitsPerEm);
149 
150     return SkStrikeSpec(font,
151                         SkPaint(),
152                         SkSurfaceProps(0, kUnknown_SkPixelGeometry),
153                         SkScalerContextFlags::kFakeGammaAndBoostContrast,
154                         SkMatrix::I());
155 }
156 
157 #if (defined(SK_GANESH) || defined(SK_GRAPHITE)) && !defined(SK_DISABLE_SDF_TEXT)
158 std::tuple<SkStrikeSpec, SkScalar, sktext::gpu::SDFTMatrixRange>
MakeSDFT(const SkFont & font,const SkPaint & paint,const SkSurfaceProps & surfaceProps,const SkMatrix & deviceMatrix,const SkPoint & textLocation,const sktext::gpu::SDFTControl & control)159 SkStrikeSpec::MakeSDFT(const SkFont& font, const SkPaint& paint,
160                        const SkSurfaceProps& surfaceProps, const SkMatrix& deviceMatrix,
161                        const SkPoint& textLocation, const sktext::gpu::SDFTControl& control) {
162     // Add filter to the paint which creates the SDFT data for A8 masks.
163     SkPaint dfPaint{paint};
164     dfPaint.setMaskFilter(sktext::gpu::SDFMaskFilter::Make());
165 
166     auto [dfFont, strikeToSourceScale, matrixRange] = control.getSDFFont(font, deviceMatrix,
167                                                                          textLocation);
168 
169     // Adjust the stroke width by the scale factor for drawing the SDFT.
170     dfPaint.setStrokeWidth(paint.getStrokeWidth() / strikeToSourceScale);
171 
172     // Check for dashing and adjust the intervals.
173     if (SkPathEffect* pathEffect = paint.getPathEffect(); pathEffect != nullptr) {
174         SkPathEffect::DashInfo dashInfo;
175         if (pathEffect->asADash(&dashInfo) == SkPathEffect::kDash_DashType) {
176             if (dashInfo.fCount > 0) {
177                 // Allocate the intervals.
178                 std::vector<SkScalar> scaledIntervals(dashInfo.fCount);
179                 dashInfo.fIntervals = scaledIntervals.data();
180                 // Call again to get the interval data.
181                 (void)pathEffect->asADash(&dashInfo);
182                 for (SkScalar& interval : scaledIntervals) {
183                     interval /= strikeToSourceScale;
184                 }
185                 auto scaledDashes = SkDashPathEffect::Make(scaledIntervals.data(),
186                                                            scaledIntervals.size(),
187                                                            dashInfo.fPhase / strikeToSourceScale);
188                 dfPaint.setPathEffect(scaledDashes);
189             }
190         }
191     }
192 
193     // Fake-gamma and subpixel antialiasing are applied in the shader, so we ignore the
194     // passed-in scaler context flags. (It's only used when we fall-back to bitmap text).
195     SkScalerContextFlags flags = SkScalerContextFlags::kNone;
196     SkStrikeSpec strikeSpec(dfFont, dfPaint, surfaceProps, flags, SkMatrix::I());
197 
198     return std::make_tuple(std::move(strikeSpec), strikeToSourceScale, matrixRange);
199 }
200 #endif
201 
SkStrikeSpec(const SkFont & font,const SkPaint & paint,const SkSurfaceProps & surfaceProps,SkScalerContextFlags scalerContextFlags,const SkMatrix & deviceMatrix)202 SkStrikeSpec::SkStrikeSpec(const SkFont& font, const SkPaint& paint,
203                            const SkSurfaceProps& surfaceProps,
204                            SkScalerContextFlags scalerContextFlags,
205                            const SkMatrix& deviceMatrix) {
206     SkScalerContextEffects effects;
207 
208     SkScalerContext::CreateDescriptorAndEffectsUsingPaint(
209             font, paint, surfaceProps, scalerContextFlags, deviceMatrix,
210             &fAutoDescriptor, &effects);
211 
212     fMaskFilter = sk_ref_sp(effects.fMaskFilter);
213     fPathEffect = sk_ref_sp(effects.fPathEffect);
214     fTypeface = font.refTypefaceOrDefault();
215 }
216 
findOrCreateScopedStrike(sktext::StrikeForGPUCacheInterface * cache) const217 sk_sp<sktext::StrikeForGPU> SkStrikeSpec::findOrCreateScopedStrike(
218         sktext::StrikeForGPUCacheInterface* cache) const {
219     return cache->findOrCreateScopedStrike(*this);
220 }
221 
findOrCreateStrike() const222 sk_sp<SkStrike> SkStrikeSpec::findOrCreateStrike() const {
223     SkScalerContextEffects effects{fPathEffect.get(), fMaskFilter.get()};
224     return SkStrikeCache::GlobalStrikeCache()->findOrCreateStrike(*this);
225 }
226 
findOrCreateStrike(SkStrikeCache * cache) const227 sk_sp<SkStrike> SkStrikeSpec::findOrCreateStrike(SkStrikeCache* cache) const {
228     SkScalerContextEffects effects{fPathEffect.get(), fMaskFilter.get()};
229     return cache->findOrCreateStrike(*this);
230 }
231 
SkBulkGlyphMetrics(const SkStrikeSpec & spec)232 SkBulkGlyphMetrics::SkBulkGlyphMetrics(const SkStrikeSpec& spec)
233     : fStrike{spec.findOrCreateStrike()} { }
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 
glyph(SkGlyphID glyphID)240 const SkGlyph* SkBulkGlyphMetrics::glyph(SkGlyphID glyphID) {
241     return this->glyphs(SkSpan<const SkGlyphID>{&glyphID, 1})[0];
242 }
243 
SkBulkGlyphMetricsAndPaths(const SkStrikeSpec & spec)244 SkBulkGlyphMetricsAndPaths::SkBulkGlyphMetricsAndPaths(const SkStrikeSpec& spec)
245     : fStrike{spec.findOrCreateStrike()} { }
246 
SkBulkGlyphMetricsAndPaths(sk_sp<SkStrike> && strike)247 SkBulkGlyphMetricsAndPaths::SkBulkGlyphMetricsAndPaths(sk_sp<SkStrike>&& strike)
248         : fStrike{std::move(strike)} { }
249 
250 SkBulkGlyphMetricsAndPaths::~SkBulkGlyphMetricsAndPaths() = default;
251 
glyphs(SkSpan<const SkGlyphID> glyphIDs)252 SkSpan<const SkGlyph*> SkBulkGlyphMetricsAndPaths::glyphs(SkSpan<const SkGlyphID> glyphIDs) {
253     fGlyphs.reset(glyphIDs.size());
254     return fStrike->preparePaths(glyphIDs, fGlyphs.get());
255 }
256 
glyph(SkGlyphID glyphID)257 const SkGlyph* SkBulkGlyphMetricsAndPaths::glyph(SkGlyphID glyphID) {
258     return this->glyphs(SkSpan<const SkGlyphID>{&glyphID, 1})[0];
259 }
260 
findIntercepts(const SkScalar * bounds,SkScalar scale,SkScalar xPos,const SkGlyph * glyph,SkScalar * array,int * count)261 void SkBulkGlyphMetricsAndPaths::findIntercepts(
262     const SkScalar* bounds, SkScalar scale, SkScalar xPos,
263     const SkGlyph* glyph, SkScalar* array, int* count) {
264     // TODO(herb): remove this abominable const_cast. Do the intercepts really need to be on the
265     //  glyph?
266     fStrike->findIntercepts(bounds, scale, xPos, const_cast<SkGlyph*>(glyph), array, count);
267 }
268 
SkBulkGlyphMetricsAndDrawables(const SkStrikeSpec & spec)269 SkBulkGlyphMetricsAndDrawables::SkBulkGlyphMetricsAndDrawables(const SkStrikeSpec& spec)
270         : fStrike{spec.findOrCreateStrike()} { }
271 
SkBulkGlyphMetricsAndDrawables(sk_sp<SkStrike> && strike)272 SkBulkGlyphMetricsAndDrawables::SkBulkGlyphMetricsAndDrawables(sk_sp<SkStrike>&& strike)
273         : fStrike{std::move(strike)} { }
274 
275 SkBulkGlyphMetricsAndDrawables::~SkBulkGlyphMetricsAndDrawables() = default;
276 
glyphs(SkSpan<const SkGlyphID> glyphIDs)277 SkSpan<const SkGlyph*> SkBulkGlyphMetricsAndDrawables::glyphs(SkSpan<const SkGlyphID> glyphIDs) {
278     fGlyphs.reset(glyphIDs.size());
279     return fStrike->prepareDrawables(glyphIDs, fGlyphs.get());
280 }
281 
glyph(SkGlyphID glyphID)282 const SkGlyph* SkBulkGlyphMetricsAndDrawables::glyph(SkGlyphID glyphID) {
283     return this->glyphs(SkSpan<const SkGlyphID>{&glyphID, 1})[0];
284 }
285 
SkBulkGlyphMetricsAndImages(const SkStrikeSpec & spec)286 SkBulkGlyphMetricsAndImages::SkBulkGlyphMetricsAndImages(const SkStrikeSpec& spec)
287         : fStrike{spec.findOrCreateStrike()} { }
288 
SkBulkGlyphMetricsAndImages(sk_sp<SkStrike> && strike)289 SkBulkGlyphMetricsAndImages::SkBulkGlyphMetricsAndImages(sk_sp<SkStrike>&& strike)
290         : fStrike{std::move(strike)} { }
291 
292 SkBulkGlyphMetricsAndImages::~SkBulkGlyphMetricsAndImages() = default;
293 
glyphs(SkSpan<const SkPackedGlyphID> glyphIDs)294 SkSpan<const SkGlyph*> SkBulkGlyphMetricsAndImages::glyphs(SkSpan<const SkPackedGlyphID> glyphIDs) {
295     fGlyphs.reset(glyphIDs.size());
296     return fStrike->prepareImages(glyphIDs, fGlyphs.get());
297 }
298 
glyph(SkPackedGlyphID packedID)299 const SkGlyph* SkBulkGlyphMetricsAndImages::glyph(SkPackedGlyphID packedID) {
300     return this->glyphs(SkSpan<const SkPackedGlyphID>{&packedID, 1})[0];
301 }
302 
descriptor() const303 const SkDescriptor& SkBulkGlyphMetricsAndImages::descriptor() const {
304     return fStrike->getDescriptor();
305 }
306