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/SkScalerCache.h" 9 10#include "include/core/SkGraphics.h" 11#include "include/core/SkPath.h" 12#include "include/core/SkTypeface.h" 13#include "src/core/SkEnumerate.h" 14#include "src/core/SkScalerContext.h" 15 16static SkFontMetrics use_or_generate_metrics( 17 const SkFontMetrics* metrics, SkScalerContext* context) { 18 SkFontMetrics answer; 19 if (metrics) { 20 answer = *metrics; 21 } else { 22 context->getFontMetrics(&answer); 23 } 24 return answer; 25} 26 27SkScalerCache::SkScalerCache( 28 std::unique_ptr<SkScalerContext> scaler, 29 const SkFontMetrics* fontMetrics) 30 : fScalerContext{std::move(scaler)} 31 , fFontMetrics{use_or_generate_metrics(fontMetrics, fScalerContext.get())} 32 , fRoundingSpec{fScalerContext->isSubpixel(), 33 fScalerContext->computeAxisAlignmentForHText()} { 34 SkASSERT(fScalerContext != nullptr); 35} 36 37std::tuple<SkGlyph*, size_t> SkScalerCache::glyph(SkPackedGlyphID packedGlyphID) { 38 auto [digest, size] = this->digest(packedGlyphID); 39 return {fGlyphForIndex[digest.index()], size}; 40} 41 42std::tuple<SkGlyphDigest, size_t> SkScalerCache::digest(SkPackedGlyphID packedGlyphID) { 43 SkGlyphDigest* digest = fDigestForPackedGlyphID.find(packedGlyphID); 44 45 if (digest != nullptr) { 46 return {*digest, 0}; 47 } 48 49 SkGlyph* glyph = fAlloc.make<SkGlyph>(fScalerContext->makeGlyph(packedGlyphID)); 50 return {this->addGlyph(glyph), sizeof(SkGlyph)}; 51} 52 53SkGlyphDigest SkScalerCache::addGlyph(SkGlyph* glyph) { 54 size_t index = fGlyphForIndex.size(); 55 SkGlyphDigest digest = SkGlyphDigest{index, *glyph}; 56 fDigestForPackedGlyphID.set(glyph->getPackedID(), digest); 57 fGlyphForIndex.push_back(glyph); 58 return digest; 59} 60 61std::tuple<const SkPath*, size_t> SkScalerCache::preparePath(SkGlyph* glyph) { 62 size_t delta = 0; 63 if (glyph->setPath(&fAlloc, fScalerContext.get())) { 64 delta = glyph->path()->approximateBytesUsed(); 65 } 66 return {glyph->path(), delta}; 67} 68 69std::tuple<const SkPath*, size_t> SkScalerCache::mergePath(SkGlyph* glyph, const SkPath* path) { 70 SkAutoMutexExclusive lock{fMu}; 71 size_t pathDelta = 0; 72 if (glyph->setPath(&fAlloc, path)) { 73 pathDelta = glyph->path()->approximateBytesUsed(); 74 } 75 return {glyph->path(), pathDelta}; 76} 77 78int SkScalerCache::countCachedGlyphs() const { 79 SkAutoMutexExclusive lock(fMu); 80 return fDigestForPackedGlyphID.count(); 81} 82 83std::tuple<SkSpan<const SkGlyph*>, size_t> SkScalerCache::internalPrepare( 84 SkSpan<const SkGlyphID> glyphIDs, PathDetail pathDetail, const SkGlyph** results) { 85 const SkGlyph** cursor = results; 86 size_t delta = 0; 87 for (auto glyphID : glyphIDs) { 88 auto [glyph, size] = this->glyph(SkPackedGlyphID{glyphID}); 89 delta += size; 90 if (pathDetail == kMetricsAndPath) { 91 auto [_, pathSize] = this->preparePath(glyph); 92 delta += pathSize; 93 } 94 *cursor++ = glyph; 95 } 96 97 return {{results, glyphIDs.size()}, delta}; 98} 99 100std::tuple<const void*, size_t> SkScalerCache::prepareImage(SkGlyph* glyph) { 101 size_t delta = 0; 102 if (glyph->setImage(&fAlloc, fScalerContext.get())) { 103 delta = glyph->imageSize(); 104 } 105 return {glyph->image(), delta}; 106} 107 108std::tuple<SkGlyph*, size_t> SkScalerCache::mergeGlyphAndImage( 109 SkPackedGlyphID toID, const SkGlyph& from) { 110 SkAutoMutexExclusive lock{fMu}; 111 // TODO(herb): remove finding the glyph when we are sure there are no glyph collisions. 112 SkGlyphDigest* digest = fDigestForPackedGlyphID.find(toID); 113 if (digest != nullptr) { 114 // Since there is no search for replacement glyphs, this glyph should not exist yet. 115 SkDEBUGFAIL("This implies adding to an existing glyph. This should not happen."); 116 117 // Just return what we have. The invariants have already been cast in stone. 118 return {fGlyphForIndex[digest->index()], 0}; 119 } else { 120 SkGlyph* glyph = fAlloc.make<SkGlyph>(toID); 121 size_t delta = glyph->setMetricsAndImage(&fAlloc, from); 122 (void)this->addGlyph(glyph); 123 return {glyph, sizeof(SkGlyph) + delta}; 124 } 125} 126 127std::tuple<SkSpan<const SkGlyph*>, size_t> SkScalerCache::metrics( 128 SkSpan<const SkGlyphID> glyphIDs, const SkGlyph* results[]) { 129 SkAutoMutexExclusive lock{fMu}; 130 auto [glyphs, delta] = this->internalPrepare(glyphIDs, kMetricsOnly, results); 131 return {glyphs, delta}; 132} 133 134std::tuple<SkSpan<const SkGlyph*>, size_t> SkScalerCache::preparePaths( 135 SkSpan<const SkGlyphID> glyphIDs, const SkGlyph* results[]) { 136 SkAutoMutexExclusive lock{fMu}; 137 auto [glyphs, delta] = this->internalPrepare(glyphIDs, kMetricsAndPath, results); 138 return {glyphs, delta}; 139} 140 141std::tuple<SkSpan<const SkGlyph*>, size_t> SkScalerCache::prepareImages( 142 SkSpan<const SkPackedGlyphID> glyphIDs, const SkGlyph* results[]) { 143 const SkGlyph** cursor = results; 144 SkAutoMutexExclusive lock{fMu}; 145 size_t delta = 0; 146 for (auto glyphID : glyphIDs) { 147 auto[glyph, glyphSize] = this->glyph(glyphID); 148 auto[_, imageSize] = this->prepareImage(glyph); 149 delta += glyphSize + imageSize; 150 *cursor++ = glyph; 151 } 152 153 return {{results, glyphIDs.size()}, delta}; 154} 155 156template <typename Fn> 157size_t SkScalerCache::commonFilterLoop(SkDrawableGlyphBuffer* drawables, Fn&& fn) { 158 size_t total = 0; 159 for (auto [i, packedID, pos] : SkMakeEnumerate(drawables->input())) { 160 if (SkScalarsAreFinite(pos.x(), pos.y())) { 161 auto [digest, size] = this->digest(packedID); 162 total += size; 163 if (!digest.isEmpty()) { 164 fn(i, digest, pos); 165 } 166 } 167 } 168 return total; 169} 170 171size_t SkScalerCache::prepareForDrawingMasksCPU(SkDrawableGlyphBuffer* drawables) { 172 SkAutoMutexExclusive lock{fMu}; 173 size_t imageDelta = 0; 174 size_t delta = this->commonFilterLoop(drawables, 175 [&](size_t i, SkGlyphDigest digest, SkPoint pos) SK_REQUIRES(fMu) { 176 // If the glyph is too large, then no image is created. 177 SkGlyph* glyph = fGlyphForIndex[digest.index()]; 178 auto [image, imageSize] = this->prepareImage(glyph); 179 if (image != nullptr) { 180 drawables->push_back(glyph, i); 181 imageDelta += imageSize; 182 } 183 }); 184 185 return delta + imageDelta; 186} 187 188// Note: this does not actually fill out the image. That happens at atlas building time. 189size_t SkScalerCache::prepareForMaskDrawing( 190 SkDrawableGlyphBuffer* drawables, SkSourceGlyphBuffer* rejects) { 191 SkAutoMutexExclusive lock{fMu}; 192 size_t delta = this->commonFilterLoop(drawables, 193 [&](size_t i, SkGlyphDigest digest, SkPoint pos) SK_REQUIRES(fMu) { 194 if (digest.canDrawAsMask()) { 195 drawables->push_back(fGlyphForIndex[digest.index()], i); 196 } else { 197 rejects->reject(i); 198 } 199 }); 200 201 return delta; 202} 203 204size_t SkScalerCache::prepareForSDFTDrawing( 205 SkDrawableGlyphBuffer* drawables, SkSourceGlyphBuffer* rejects) { 206 SkAutoMutexExclusive lock{fMu}; 207 size_t delta = this->commonFilterLoop(drawables, 208 [&](size_t i, SkGlyphDigest digest, SkPoint pos) SK_REQUIRES(fMu) { 209 if (digest.canDrawAsSDFT()) { 210 drawables->push_back(fGlyphForIndex[digest.index()], i); 211 } else { 212 rejects->reject(i); 213 } 214 }); 215 216 return delta; 217} 218 219size_t SkScalerCache::prepareForPathDrawing( 220 SkDrawableGlyphBuffer* drawables, SkSourceGlyphBuffer* rejects) { 221 SkAutoMutexExclusive lock{fMu}; 222 size_t pathDelta = 0; 223 size_t delta = this->commonFilterLoop(drawables, 224 [&](size_t i, SkGlyphDigest digest, SkPoint pos) SK_REQUIRES(fMu) { 225 SkGlyph* glyph = fGlyphForIndex[digest.index()]; 226 if (!digest.isColor()) { 227 auto [path, pathSize] = this->preparePath(glyph); 228 pathDelta += pathSize; 229 if (path != nullptr) { 230 // Save off the path to draw later. 231 drawables->push_back(path, i); 232 } else { 233 // Glyph does not have a path. It is probably bitmap only. 234 rejects->reject(i, glyph->maxDimension()); 235 } 236 } else { 237 // Glyph is color. 238 rejects->reject(i, glyph->maxDimension()); 239 } 240 }); 241 242 return delta + pathDelta; 243} 244 245void SkScalerCache::findIntercepts(const SkScalar bounds[2], SkScalar scale, SkScalar xPos, 246 SkGlyph* glyph, SkScalar* array, int* count) { 247 SkAutoMutexExclusive lock{fMu}; 248 glyph->ensureIntercepts(bounds, scale, xPos, array, count, &fAlloc); 249} 250 251void SkScalerCache::dump() const { 252 SkAutoMutexExclusive lock{fMu}; 253 const SkTypeface* face = fScalerContext->getTypeface(); 254 const SkScalerContextRec& rec = fScalerContext->getRec(); 255 SkMatrix matrix; 256 rec.getSingleMatrix(&matrix); 257 matrix.preScale(SkScalarInvert(rec.fTextSize), SkScalarInvert(rec.fTextSize)); 258 SkString name; 259 face->getFamilyName(&name); 260 261 SkString msg; 262 SkFontStyle style = face->fontStyle(); 263 msg.printf("cache typeface:%x %25s:(%d,%d,%d)\n %s glyphs:%3d", 264 face->uniqueID(), name.c_str(), style.weight(), style.width(), style.slant(), 265 rec.dump().c_str(), fDigestForPackedGlyphID.count()); 266 SkDebugf("%s\n", msg.c_str()); 267} 268 269