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