1 /*
2  * Copyright 2006 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/SkStrike.h"
9 
10 #include "include/core/SkGraphics.h"
11 #include "include/core/SkPath.h"
12 #include "include/core/SkTypeface.h"
13 #include "include/private/SkMutex.h"
14 #include "include/private/SkOnce.h"
15 #include "include/private/SkTemplates.h"
16 #include "src/core/SkEnumerate.h"
17 #include "src/core/SkMakeUnique.h"
18 #include <cctype>
19 
SkStrike(const SkDescriptor & desc,std::unique_ptr<SkScalerContext> scaler,const SkFontMetrics & fontMetrics)20 SkStrike::SkStrike(
21     const SkDescriptor& desc,
22     std::unique_ptr<SkScalerContext> scaler,
23     const SkFontMetrics& fontMetrics)
24         : fDesc{desc}
25         , fScalerContext{std::move(scaler)}
26         , fFontMetrics{fontMetrics}
27         , fRoundingSpec{fScalerContext->isSubpixel(),
28                         fScalerContext->computeAxisAlignmentForHText()} {
29     SkASSERT(fScalerContext != nullptr);
30     fMemoryUsed = sizeof(*this);
31 }
32 
33 #ifdef SK_DEBUG
34 #define VALIDATE()  AutoValidate av(this)
35 #else
36 #define VALIDATE()
37 #endif
38 
39 // -- glyph creation -------------------------------------------------------------------------------
makeGlyph(SkPackedGlyphID packedGlyphID)40 SkGlyph* SkStrike::makeGlyph(SkPackedGlyphID packedGlyphID) {
41     fMemoryUsed += sizeof(SkGlyph);
42     SkGlyph* glyph = fAlloc.make<SkGlyph>(packedGlyphID);
43     fGlyphMap.set(glyph);
44     return glyph;
45 }
46 
glyph(SkPackedGlyphID packedGlyphID)47 SkGlyph* SkStrike::glyph(SkPackedGlyphID packedGlyphID) {
48     VALIDATE();
49     SkGlyph* glyph = fGlyphMap.findOrNull(packedGlyphID);
50     if (glyph == nullptr) {
51         glyph = this->makeGlyph(packedGlyphID);
52         fScalerContext->getMetrics(glyph);
53     }
54     return glyph;
55 }
56 
glyph(SkGlyphID glyphID)57 SkGlyph* SkStrike::glyph(SkGlyphID glyphID) {
58     return this->glyph(SkPackedGlyphID{glyphID});
59 }
60 
glyph(SkGlyphID glyphID,SkPoint position)61 SkGlyph* SkStrike::glyph(SkGlyphID glyphID, SkPoint position) {
62     SkIPoint mask = fRoundingSpec.ignorePositionMask;
63     SkFixed subX = SkScalarToFixed(position.x()) & mask.x(),
64             subY = SkScalarToFixed(position.y()) & mask.y();
65     return this->glyph(SkPackedGlyphID{glyphID, subX, subY});
66 }
67 
glyphFromPrototype(const SkGlyphPrototype & p,void * image)68 SkGlyph* SkStrike::glyphFromPrototype(const SkGlyphPrototype& p, void* image) {
69     SkGlyph* glyph = fGlyphMap.findOrNull(p.id);
70     if (glyph == nullptr) {
71         fMemoryUsed += sizeof(SkGlyph);
72         glyph = fAlloc.make<SkGlyph>(p);
73         fGlyphMap.set(glyph);
74     }
75     if (glyph->setImage(&fAlloc, image)) {
76         fMemoryUsed += glyph->imageSize();
77     }
78     return glyph;
79 }
80 
glyphOrNull(SkPackedGlyphID id) const81 SkGlyph* SkStrike::glyphOrNull(SkPackedGlyphID id) const {
82     return fGlyphMap.findOrNull(id);
83 }
84 
preparePath(SkGlyph * glyph)85 const SkPath* SkStrike::preparePath(SkGlyph* glyph) {
86     if (glyph->setPath(&fAlloc, fScalerContext.get())) {
87         fMemoryUsed += glyph->path()->approximateBytesUsed();
88     }
89     return glyph->path();
90 }
91 
preparePath(SkGlyph * glyph,const SkPath * path)92 const SkPath* SkStrike::preparePath(SkGlyph* glyph, const SkPath* path) {
93     if (glyph->setPath(&fAlloc, path)) {
94         fMemoryUsed += glyph->path()->approximateBytesUsed();
95     }
96     return glyph->path();
97 }
98 
getDescriptor() const99 const SkDescriptor& SkStrike::getDescriptor() const {
100     return *fDesc.getDesc();
101 }
102 
getGlyphCount() const103 unsigned SkStrike::getGlyphCount() const {
104     return fScalerContext->getGlyphCount();
105 }
106 
countCachedGlyphs() const107 int SkStrike::countCachedGlyphs() const {
108     return fGlyphMap.count();
109 }
110 
internalPrepare(SkSpan<const SkGlyphID> glyphIDs,PathDetail pathDetail,const SkGlyph ** results)111 SkSpan<const SkGlyph*> SkStrike::internalPrepare(
112         SkSpan<const SkGlyphID> glyphIDs, PathDetail pathDetail, const SkGlyph** results) {
113     const SkGlyph** cursor = results;
114     for (auto glyphID : glyphIDs) {
115         SkGlyph* glyphPtr = this->glyph(glyphID);
116         if (pathDetail == kMetricsAndPath) {
117             this->preparePath(glyphPtr);
118         }
119         *cursor++ = glyphPtr;
120     }
121 
122     return {results, glyphIDs.size()};
123 }
124 
prepareImage(SkGlyph * glyph)125 const void* SkStrike::prepareImage(SkGlyph* glyph) {
126     if (glyph->setImage(&fAlloc, fScalerContext.get())) {
127         fMemoryUsed += glyph->imageSize();
128     }
129     return glyph->image();
130 }
131 
mergeGlyphAndImage(SkPackedGlyphID toID,const SkGlyph & from)132 SkGlyph* SkStrike::mergeGlyphAndImage(SkPackedGlyphID toID, const SkGlyph& from) {
133     SkGlyph* glyph = fGlyphMap.findOrNull(toID);
134     if (glyph == nullptr) {
135         glyph = this->makeGlyph(toID);
136     }
137     if (glyph->setMetricsAndImage(&fAlloc, from)) {
138         fMemoryUsed += glyph->imageSize();
139     }
140     return glyph;
141 }
142 
belongsToCache(const SkGlyph * glyph) const143 bool SkStrike::belongsToCache(const SkGlyph* glyph) const {
144     return glyph && fGlyphMap.findOrNull(glyph->getPackedID()) == glyph;
145 }
146 
getCachedGlyphAnySubPix(SkGlyphID glyphID,SkPackedGlyphID vetoID) const147 const SkGlyph* SkStrike::getCachedGlyphAnySubPix(SkGlyphID glyphID,
148                                                      SkPackedGlyphID vetoID) const {
149     for (SkFixed subY = 0; subY < SK_Fixed1; subY += SK_FixedQuarter) {
150         for (SkFixed subX = 0; subX < SK_Fixed1; subX += SK_FixedQuarter) {
151             SkPackedGlyphID packedGlyphID{glyphID, subX, subY};
152             if (packedGlyphID == vetoID) continue;
153             if (SkGlyph* glyphPtr = fGlyphMap.findOrNull(packedGlyphID)) {
154                 return glyphPtr;
155             }
156         }
157     }
158 
159     return nullptr;
160 }
161 
metrics(SkSpan<const SkGlyphID> glyphIDs,const SkGlyph * results[])162 SkSpan<const SkGlyph*> SkStrike::metrics(SkSpan<const SkGlyphID> glyphIDs,
163                                          const SkGlyph* results[]) {
164     return this->internalPrepare(glyphIDs, kMetricsOnly, results);
165 }
166 
preparePaths(SkSpan<const SkGlyphID> glyphIDs,const SkGlyph * results[])167 SkSpan<const SkGlyph*> SkStrike::preparePaths(SkSpan<const SkGlyphID> glyphIDs,
168                                               const SkGlyph* results[]) {
169     return this->internalPrepare(glyphIDs, kMetricsAndPath, results);
170 }
171 
172 SkSpan<const SkGlyph*>
prepareImages(SkSpan<const SkPackedGlyphID> glyphIDs,const SkGlyph * results[])173 SkStrike::prepareImages(SkSpan<const SkPackedGlyphID> glyphIDs, const SkGlyph* results[]) {
174     const SkGlyph** cursor = results;
175     for (auto glyphID : glyphIDs) {
176         SkGlyph* glyphPtr = this->glyph(glyphID);
177         (void)this->prepareImage(glyphPtr);
178         *cursor++ = glyphPtr;
179     }
180 
181     return {results, glyphIDs.size()};
182 }
183 
prepareForDrawingMasksCPU(SkDrawableGlyphBuffer * drawables)184 void SkStrike::prepareForDrawingMasksCPU(SkDrawableGlyphBuffer* drawables) {
185     for (auto t : SkMakeEnumerate(drawables->input())) {
186         size_t i; SkGlyphVariant packedID;
187         std::forward_as_tuple(i, std::tie(packedID, std::ignore)) = t;
188         SkGlyph* glyph = this->glyph(packedID);
189         if (!glyph->isEmpty()) {
190             const void* image = this->prepareImage(glyph);
191             // If the glyph is too large, then no image is created.
192             if (image != nullptr) {
193                 drawables->push_back(glyph, i);
194             }
195         }
196     }
197 }
198 
prepareForDrawingPathsCPU(SkDrawableGlyphBuffer * drawables)199 void SkStrike::prepareForDrawingPathsCPU(SkDrawableGlyphBuffer* drawables) {
200     for (auto t : SkMakeEnumerate(drawables->input())) {
201         size_t i; SkGlyphVariant packedID;
202         std::forward_as_tuple(i, std::tie(packedID, std::ignore)) = t;
203         SkGlyph* glyph = this->glyph(packedID);
204         if (!glyph->isEmpty()) {
205             const SkPath* path = this->preparePath(glyph);
206             // The glyph my not have a path.
207             if (path != nullptr) {
208                 drawables->push_back(path, i);
209             }
210         }
211     }
212 }
213 
214 // N.B. This glyphMetrics call culls all the glyphs which will not display based on a non-finite
215 // position or that there are no mask pixels.
216 SkSpan<const SkGlyphPos>
prepareForDrawingRemoveEmpty(const SkPackedGlyphID packedGlyphIDs[],const SkPoint positions[],size_t n,int maxDimension,SkGlyphPos results[])217 SkStrike::prepareForDrawingRemoveEmpty(const SkPackedGlyphID packedGlyphIDs[],
218                                        const SkPoint positions[],
219                                        size_t n,
220                                        int maxDimension,
221                                        SkGlyphPos results[]) {
222     size_t drawableGlyphCount = 0;
223     for (size_t i = 0; i < n; i++) {
224         SkPoint pos = positions[i];
225         if (SkScalarsAreFinite(pos.x(), pos.y())) {
226             SkGlyph* glyphPtr = this->glyph(packedGlyphIDs[i]);
227             if (!glyphPtr->isEmpty()) {
228                 results[drawableGlyphCount++] = {i, glyphPtr, pos};
229                 if (glyphPtr->maxDimension() <= maxDimension) {
230                     // The glyph fits. Prepare image later.
231                 } else if (!glyphPtr->isColor()) {
232                     // The out of atlas glyph is not color so we can draw it using paths.
233                     this->preparePath(glyphPtr);
234                 } else {
235                     // This will be handled by the fallback strike.
236                     SkASSERT(glyphPtr->maxDimension() > maxDimension && glyphPtr->isColor());
237                 }
238             }
239         }
240     }
241 
242     return SkSpan<const SkGlyphPos>{results, drawableGlyphCount};
243 }
244 
findIntercepts(const SkScalar bounds[2],SkScalar scale,SkScalar xPos,SkGlyph * glyph,SkScalar * array,int * count)245 void SkStrike::findIntercepts(const SkScalar bounds[2], SkScalar scale, SkScalar xPos,
246         SkGlyph* glyph, SkScalar* array, int* count) {
247     glyph->ensureIntercepts(bounds, scale, xPos, array, count, &fAlloc);
248 }
249 
dump() const250 void SkStrike::dump() const {
251     const SkTypeface* face = fScalerContext->getTypeface();
252     const SkScalerContextRec& rec = fScalerContext->getRec();
253     SkMatrix matrix;
254     rec.getSingleMatrix(&matrix);
255     matrix.preScale(SkScalarInvert(rec.fTextSize), SkScalarInvert(rec.fTextSize));
256     SkString name;
257     face->getFamilyName(&name);
258 
259     SkString msg;
260     SkFontStyle style = face->fontStyle();
261     msg.printf("cache typeface:%x %25s:(%d,%d,%d)\n %s glyphs:%3d",
262                face->uniqueID(), name.c_str(), style.weight(), style.width(), style.slant(),
263                rec.dump().c_str(), fGlyphMap.count());
264     SkDebugf("%s\n", msg.c_str());
265 }
266 
onAboutToExitScope()267 void SkStrike::onAboutToExitScope() { }
268 
269 #ifdef SK_DEBUG
forceValidate() const270 void SkStrike::forceValidate() const {
271     size_t memoryUsed = sizeof(*this);
272     fGlyphMap.foreach ([&memoryUsed](const SkGlyph* glyphPtr) {
273         memoryUsed += sizeof(SkGlyph);
274         if (glyphPtr->setImageHasBeenCalled()) {
275             memoryUsed += glyphPtr->imageSize();
276         }
277         if (glyphPtr->setPathHasBeenCalled() && glyphPtr->path() != nullptr) {
278             memoryUsed += glyphPtr->path()->approximateBytesUsed();
279         }
280     });
281     SkASSERT(fMemoryUsed == memoryUsed);
282 }
283 
validate() const284 void SkStrike::validate() const {
285 #ifdef SK_DEBUG_GLYPH_CACHE
286     forceValidate();
287 #endif
288 }
289 #endif  // SK_DEBUG
290 
291 
292