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