• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/SkMakeUnique.h"
17  #include <cctype>
18  
SkStrike(const SkDescriptor & desc,std::unique_ptr<SkScalerContext> scaler,const SkFontMetrics & fontMetrics)19  SkStrike::SkStrike(
20      const SkDescriptor& desc,
21      std::unique_ptr<SkScalerContext> scaler,
22      const SkFontMetrics& fontMetrics)
23      : fDesc{desc}
24      , fScalerContext{std::move(scaler)}
25      , fFontMetrics{fontMetrics}
26      , fIsSubpixel{fScalerContext->isSubpixel()}
27      , fAxisAlignment{fScalerContext->computeAxisAlignmentForHText()}
28  {
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      const SkFixed maskX = (!fIsSubpixel || fAxisAlignment == kY_SkAxisAlignment) ? 0 : ~0;
63      const SkFixed maskY = (!fIsSubpixel || fAxisAlignment == kX_SkAxisAlignment) ? 0 : ~0;
64      SkFixed subX = SkScalarToFixed(position.x()) & maskX,
65              subY = SkScalarToFixed(position.y()) & maskY;
66      return this->glyph(SkPackedGlyphID{glyphID, subX, subY});
67  }
68  
glyphFromPrototype(const SkGlyphPrototype & p,void * image)69  SkGlyph* SkStrike::glyphFromPrototype(const SkGlyphPrototype& p, void* image) {
70      SkGlyph* glyph = fGlyphMap.findOrNull(p.id);
71      if (glyph == nullptr) {
72          fMemoryUsed += sizeof(SkGlyph);
73          glyph = fAlloc.make<SkGlyph>(p);
74          fGlyphMap.set(glyph);
75      }
76      if (glyph->setImage(&fAlloc, image)) {
77          fMemoryUsed += glyph->imageSize();
78      }
79      return glyph;
80  }
81  
glyphOrNull(SkPackedGlyphID id) const82  SkGlyph* SkStrike::glyphOrNull(SkPackedGlyphID id) const {
83      return fGlyphMap.findOrNull(id);
84  }
85  
preparePath(SkGlyph * glyph)86  const SkPath* SkStrike::preparePath(SkGlyph* glyph) {
87      if (glyph->setPath(&fAlloc, fScalerContext.get())) {
88          fMemoryUsed += glyph->path()->approximateBytesUsed();
89      }
90      return glyph->path();
91  }
92  
preparePath(SkGlyph * glyph,const SkPath * path)93  const SkPath* SkStrike::preparePath(SkGlyph* glyph, const SkPath* path) {
94      if (glyph->setPath(&fAlloc, path)) {
95          fMemoryUsed += glyph->path()->approximateBytesUsed();
96      }
97      return glyph->path();
98  }
99  
getDescriptor() const100  const SkDescriptor& SkStrike::getDescriptor() const {
101      return *fDesc.getDesc();
102  }
103  
getGlyphCount() const104  unsigned SkStrike::getGlyphCount() const {
105      return fScalerContext->getGlyphCount();
106  }
107  
countCachedGlyphs() const108  int SkStrike::countCachedGlyphs() const {
109      return fGlyphMap.count();
110  }
111  
internalPrepare(SkSpan<const SkGlyphID> glyphIDs,PathDetail pathDetail,const SkGlyph ** results)112  SkSpan<const SkGlyph*> SkStrike::internalPrepare(
113          SkSpan<const SkGlyphID> glyphIDs, PathDetail pathDetail, const SkGlyph** results) {
114      const SkGlyph** cursor = results;
115      for (auto glyphID : glyphIDs) {
116          SkGlyph* glyphPtr = this->glyph(glyphID);
117          if (pathDetail == kMetricsAndPath) {
118              this->preparePath(glyphPtr);
119          }
120          *cursor++ = glyphPtr;
121      }
122  
123      return {results, glyphIDs.size()};
124  }
125  
prepareImage(SkGlyph * glyph)126  const void* SkStrike::prepareImage(SkGlyph* glyph) {
127      if (glyph->setImage(&fAlloc, fScalerContext.get())) {
128          fMemoryUsed += glyph->imageSize();
129      }
130      return glyph->image();
131  }
132  
mergeGlyphAndImage(SkPackedGlyphID toID,const SkGlyph & from)133  SkGlyph* SkStrike::mergeGlyphAndImage(SkPackedGlyphID toID, const SkGlyph& from) {
134      SkGlyph* glyph = fGlyphMap.findOrNull(toID);
135      if (glyph == nullptr) {
136          glyph = this->makeGlyph(toID);
137      }
138      if (glyph->setMetricsAndImage(&fAlloc, from)) {
139          fMemoryUsed += glyph->imageSize();
140      }
141      return glyph;
142  }
143  
belongsToCache(const SkGlyph * glyph) const144  bool SkStrike::belongsToCache(const SkGlyph* glyph) const {
145      return glyph && fGlyphMap.findOrNull(glyph->getPackedID()) == glyph;
146  }
147  
getCachedGlyphAnySubPix(SkGlyphID glyphID,SkPackedGlyphID vetoID) const148  const SkGlyph* SkStrike::getCachedGlyphAnySubPix(SkGlyphID glyphID,
149                                                       SkPackedGlyphID vetoID) const {
150      for (SkFixed subY = 0; subY < SK_Fixed1; subY += SK_FixedQuarter) {
151          for (SkFixed subX = 0; subX < SK_Fixed1; subX += SK_FixedQuarter) {
152              SkPackedGlyphID packedGlyphID{glyphID, subX, subY};
153              if (packedGlyphID == vetoID) continue;
154              if (SkGlyph* glyphPtr = fGlyphMap.findOrNull(packedGlyphID)) {
155                  return glyphPtr;
156              }
157          }
158      }
159  
160      return nullptr;
161  }
162  
rounding() const163  SkVector SkStrike::rounding() const {
164      return SkStrikeCommon::PixelRounding(fIsSubpixel, fAxisAlignment);
165  }
166  
metrics(SkSpan<const SkGlyphID> glyphIDs,const SkGlyph * results[])167  SkSpan<const SkGlyph*> SkStrike::metrics(SkSpan<const SkGlyphID> glyphIDs,
168                                           const SkGlyph* results[]) {
169      return this->internalPrepare(glyphIDs, kMetricsOnly, results);
170  }
171  
preparePaths(SkSpan<const SkGlyphID> glyphIDs,const SkGlyph * results[])172  SkSpan<const SkGlyph*> SkStrike::preparePaths(SkSpan<const SkGlyphID> glyphIDs,
173                                                const SkGlyph* results[]) {
174      return this->internalPrepare(glyphIDs, kMetricsAndPath, results);
175  }
176  
177  SkSpan<const SkGlyph*>
prepareImages(SkSpan<const SkPackedGlyphID> glyphIDs,const SkGlyph * results[])178  SkStrike::prepareImages(SkSpan<const SkPackedGlyphID> glyphIDs, const SkGlyph* results[]) {
179      const SkGlyph** cursor = results;
180      for (auto glyphID : glyphIDs) {
181          SkGlyph* glyphPtr = this->glyph(glyphID);
182          (void)this->prepareImage(glyphPtr);
183          *cursor++ = glyphPtr;
184      }
185  
186      return {results, glyphIDs.size()};
187  }
188  
189  // N.B. This glyphMetrics call culls all the glyphs which will not display based on a non-finite
190  // position or that there are no mask pixels.
191  SkSpan<const SkGlyphPos>
prepareForDrawingRemoveEmpty(const SkPackedGlyphID packedGlyphIDs[],const SkPoint positions[],size_t n,int maxDimension,PreparationDetail detail,SkGlyphPos results[])192  SkStrike::prepareForDrawingRemoveEmpty(const SkPackedGlyphID packedGlyphIDs[],
193                                         const SkPoint positions[],
194                                         size_t n,
195                                         int maxDimension,
196                                         PreparationDetail detail,
197                                         SkGlyphPos results[]) {
198      size_t drawableGlyphCount = 0;
199      for (size_t i = 0; i < n; i++) {
200          SkPoint pos = positions[i];
201          if (SkScalarsAreFinite(pos.x(), pos.y())) {
202              SkGlyph* glyphPtr = this->glyph(packedGlyphIDs[i]);
203              if (!glyphPtr->isEmpty()) {
204                  results[drawableGlyphCount++] = {i, glyphPtr, pos};
205                  if (glyphPtr->maxDimension() <= maxDimension) {
206                      // The glyph fits; ensure the image if needed.
207                      if (detail == SkStrikeInterface::kImageIfNeeded) {
208                          this->prepareImage(glyphPtr);
209                      }
210                  } else if (!glyphPtr->isColor()) {
211                      // The out of atlas glyph is not color so we can draw it using paths.
212                      this->preparePath(glyphPtr);
213                  } else {
214                      // This will be handled by the fallback strike.
215                      SkASSERT(glyphPtr->maxDimension() > maxDimension && glyphPtr->isColor());
216                  }
217              }
218          }
219      }
220  
221      return SkSpan<const SkGlyphPos>{results, drawableGlyphCount};
222  }
223  
findIntercepts(const SkScalar bounds[2],SkScalar scale,SkScalar xPos,SkGlyph * glyph,SkScalar * array,int * count)224  void SkStrike::findIntercepts(const SkScalar bounds[2], SkScalar scale, SkScalar xPos,
225          SkGlyph* glyph, SkScalar* array, int* count) {
226      glyph->ensureIntercepts(bounds, scale, xPos, array, count, &fAlloc);
227  }
228  
dump() const229  void SkStrike::dump() const {
230      const SkTypeface* face = fScalerContext->getTypeface();
231      const SkScalerContextRec& rec = fScalerContext->getRec();
232      SkMatrix matrix;
233      rec.getSingleMatrix(&matrix);
234      matrix.preScale(SkScalarInvert(rec.fTextSize), SkScalarInvert(rec.fTextSize));
235      SkString name;
236      face->getFamilyName(&name);
237  
238      SkString msg;
239      SkFontStyle style = face->fontStyle();
240      msg.printf("cache typeface:%x %25s:(%d,%d,%d)\n %s glyphs:%3d",
241                 face->uniqueID(), name.c_str(), style.weight(), style.width(), style.slant(),
242                 rec.dump().c_str(), fGlyphMap.count());
243      SkDebugf("%s\n", msg.c_str());
244  }
245  
onAboutToExitScope()246  void SkStrike::onAboutToExitScope() { }
247  
248  #ifdef SK_DEBUG
forceValidate() const249  void SkStrike::forceValidate() const {
250      size_t memoryUsed = sizeof(*this);
251      fGlyphMap.foreach ([&memoryUsed](const SkGlyph* glyphPtr) {
252          memoryUsed += sizeof(SkGlyph);
253          if (glyphPtr->setImageHasBeenCalled()) {
254              memoryUsed += glyphPtr->imageSize();
255          }
256          if (glyphPtr->setPathHasBeenCalled() && glyphPtr->path() != nullptr) {
257              memoryUsed += glyphPtr->path()->approximateBytesUsed();
258          }
259      });
260      SkASSERT(fMemoryUsed == memoryUsed);
261  }
262  
validate() const263  void SkStrike::validate() const {
264  #ifdef SK_DEBUG_GLYPH_CACHE
265      forceValidate();
266  #endif
267  }
268  #endif  // SK_DEBUG
269  
270  
271