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