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