blob: 6575e327cc2e2985b3cba2af8f8f756630805e49 [file] [log] [blame]
/*
* Copyright 2006 The Android Open Source Project
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "src/core/SkScalerCache.h"
#include "include/core/SkGraphics.h"
#include "include/core/SkPath.h"
#include "include/core/SkTypeface.h"
#include "src/core/SkEnumerate.h"
#include "src/core/SkScalerContext.h"
static SkFontMetrics use_or_generate_metrics(
const SkFontMetrics* metrics, SkScalerContext* context) {
SkFontMetrics answer;
if (metrics) {
answer = *metrics;
} else {
context->getFontMetrics(&answer);
}
return answer;
}
SkScalerCache::SkScalerCache(
const SkDescriptor& desc,
std::unique_ptr<SkScalerContext> scaler,
const SkFontMetrics* fontMetrics)
: fDesc{desc}
, fScalerContext{std::move(scaler)}
, fFontMetrics{use_or_generate_metrics(fontMetrics, fScalerContext.get())}
, fRoundingSpec{fScalerContext->isSubpixel(),
fScalerContext->computeAxisAlignmentForHText()} {
SkASSERT(fScalerContext != nullptr);
}
std::tuple<SkGlyph*, size_t> SkScalerCache::glyph(SkPackedGlyphID packedGlyphID) {
#ifndef SKIA_STRUCTURED_BINDINGS_BACKPORT
auto [digest, size] = this->digest(packedGlyphID);
#else
STRUCTURED_BINDING_2(digest, size, this->digest(packedGlyphID));
#endif
return {fGlyphForIndex[digest.index()], size};
}
std::tuple<SkGlyphDigest, size_t> SkScalerCache::digest(SkPackedGlyphID packedGlyphID) {
SkGlyphDigest* digest = fDigestForPackedGlyphID.find(packedGlyphID);
if (digest != nullptr) {
return {*digest, 0};
}
SkGlyph* glyph = fAlloc.make<SkGlyph>(fScalerContext->makeGlyph(packedGlyphID, &fAlloc));
return {this->addGlyph(glyph), sizeof(SkGlyph)};
}
SkGlyphDigest SkScalerCache::addGlyph(SkGlyph* glyph) {
size_t index = fGlyphForIndex.size();
SkGlyphDigest digest = SkGlyphDigest{index, *glyph};
fDigestForPackedGlyphID.set(glyph->getPackedID(), digest);
fGlyphForIndex.push_back(glyph);
return digest;
}
std::tuple<const SkPath*, size_t> SkScalerCache::preparePath(SkGlyph* glyph) {
size_t delta = 0;
if (glyph->setPath(&fAlloc, fScalerContext.get())) {
delta = glyph->path()->approximateBytesUsed();
}
return {glyph->path(), delta};
}
std::tuple<const SkPath*, size_t> SkScalerCache::mergePath(
SkGlyph* glyph, const SkPath* path, bool hairline) {
SkAutoMutexExclusive lock{fMu};
size_t pathDelta = 0;
if (glyph->setPath(&fAlloc, path, hairline)) {
pathDelta = glyph->path()->approximateBytesUsed();
}
return {glyph->path(), pathDelta};
}
const SkDescriptor& SkScalerCache::getDescriptor() const {
return *fDesc.getDesc();
}
int SkScalerCache::countCachedGlyphs() const {
SkAutoMutexExclusive lock(fMu);
return fDigestForPackedGlyphID.count();
}
std::tuple<SkSpan<const SkGlyph*>, size_t> SkScalerCache::internalPrepare(
SkSpan<const SkGlyphID> glyphIDs, PathDetail pathDetail, const SkGlyph** results) {
const SkGlyph** cursor = results;
size_t delta = 0;
for (auto glyphID : glyphIDs) {
#ifndef SKIA_STRUCTURED_BINDINGS_BACKPORT
auto [glyph, size] = this->glyph(SkPackedGlyphID{glyphID});
#else
STRUCTURED_BINDING_2(glyph, size, this->glyph(SkPackedGlyphID{glyphID}));
#endif
delta += size;
if (pathDetail == kMetricsAndPath) {
#ifndef SKIA_STRUCTURED_BINDINGS_BACKPORT
auto [_, pathSize] = this->preparePath(glyph);
#else
STRUCTURED_BINDING_2(_, pathSize, this->preparePath(glyph));
#endif
delta += pathSize;
}
*cursor++ = glyph;
}
return {{results, glyphIDs.size()}, delta};
}
std::tuple<const void*, size_t> SkScalerCache::prepareImage(SkGlyph* glyph) {
size_t delta = 0;
if (glyph->setImage(&fAlloc, fScalerContext.get())) {
delta = glyph->imageSize();
}
return {glyph->image(), delta};
}
std::tuple<SkGlyph*, size_t> SkScalerCache::mergeGlyphAndImage(
SkPackedGlyphID toID, const SkGlyph& from) {
SkAutoMutexExclusive lock{fMu};
// TODO(herb): remove finding the glyph when we are sure there are no glyph collisions.
SkGlyphDigest* digest = fDigestForPackedGlyphID.find(toID);
if (digest != nullptr) {
// Since there is no search for replacement glyphs, this glyph should not exist yet.
SkDEBUGFAIL("This implies adding to an existing glyph. This should not happen.");
// Just return what we have. The invariants have already been cast in stone.
return {fGlyphForIndex[digest->index()], 0};
} else {
SkGlyph* glyph = fAlloc.make<SkGlyph>(toID);
size_t delta = glyph->setMetricsAndImage(&fAlloc, from);
(void)this->addGlyph(glyph);
return {glyph, sizeof(SkGlyph) + delta};
}
}
std::tuple<SkSpan<const SkGlyph*>, size_t> SkScalerCache::metrics(
SkSpan<const SkGlyphID> glyphIDs, const SkGlyph* results[]) {
SkAutoMutexExclusive lock{fMu};
#ifndef SKIA_STRUCTURED_BINDINGS_BACKPORT
auto [glyphs, delta] = this->internalPrepare(glyphIDs, kMetricsOnly, results);
#else
STRUCTURED_BINDING_2(glyphs, delta, this->internalPrepare(glyphIDs, kMetricsOnly, results));
#endif
return {glyphs, delta};
}
std::tuple<SkSpan<const SkGlyph*>, size_t> SkScalerCache::preparePaths(
SkSpan<const SkGlyphID> glyphIDs, const SkGlyph* results[]) {
SkAutoMutexExclusive lock{fMu};
#ifndef SKIA_STRUCTURED_BINDINGS_BACKPORT
auto [glyphs, delta] = this->internalPrepare(glyphIDs, kMetricsAndPath, results);
#else
STRUCTURED_BINDING_2(glyphs, delta, this->internalPrepare(glyphIDs, kMetricsAndPath, results));
#endif
return {glyphs, delta};
}
std::tuple<SkSpan<const SkGlyph*>, size_t> SkScalerCache::prepareImages(
SkSpan<const SkPackedGlyphID> glyphIDs, const SkGlyph* results[]) {
const SkGlyph** cursor = results;
SkAutoMutexExclusive lock{fMu};
size_t delta = 0;
for (auto glyphID : glyphIDs) {
#ifndef SKIA_STRUCTURED_BINDINGS_BACKPORT
auto[glyph, glyphSize] = this->glyph(glyphID);
#else
STRUCTURED_BINDING_2(glyph, glyphSize, this->glyph(glyphID));
#endif
#ifndef SKIA_STRUCTURED_BINDINGS_BACKPORT
auto[_, imageSize] = this->prepareImage(glyph);
#else
STRUCTURED_BINDING_2(_, imageSize, this->prepareImage(glyph));
#endif
delta += glyphSize + imageSize;
*cursor++ = glyph;
}
return {{results, glyphIDs.size()}, delta};
}
template <typename Fn>
size_t SkScalerCache::commonFilterLoop(SkDrawableGlyphBuffer* drawables, Fn&& fn) {
size_t total = 0;
#ifndef SKIA_STRUCTURED_BINDINGS_BACKPORT
for (auto [i, packedID, pos] : SkMakeEnumerate(drawables->input())) {
#else
for (auto item : SkMakeEnumerate(drawables->input())) {
STRUCTURED_BINDING_3(i, packedID, pos, std::move(item));
#endif
if (SkScalarsAreFinite(pos.x(), pos.y())) {
#ifndef SKIA_STRUCTURED_BINDINGS_BACKPORT
auto [digest, size] = this->digest(packedID);
#else
STRUCTURED_BINDING_2(digest, size, this->digest(packedID));
#endif
total += size;
if (!digest.isEmpty()) {
fn(i, digest, pos);
}
}
}
return total;
}
size_t SkScalerCache::prepareForDrawingMasksCPU(SkDrawableGlyphBuffer* drawables) {
SkAutoMutexExclusive lock{fMu};
size_t imageDelta = 0;
size_t delta = this->commonFilterLoop(drawables,
[&](size_t i, SkGlyphDigest digest, SkPoint pos) SK_REQUIRES(fMu) {
// If the glyph is too large, then no image is created.
SkGlyph* glyph = fGlyphForIndex[digest.index()];
#ifndef SKIA_STRUCTURED_BINDINGS_BACKPORT
auto [image, imageSize] = this->prepareImage(glyph);
#else
STRUCTURED_BINDING_2(image, imageSize, this->prepareImage(glyph));
#endif
if (image != nullptr) {
drawables->push_back(glyph, i);
imageDelta += imageSize;
}
});
return delta + imageDelta;
}
// Note: this does not actually fill out the image. That happens at atlas building time.
size_t SkScalerCache::prepareForMaskDrawing(
SkDrawableGlyphBuffer* drawables, SkSourceGlyphBuffer* rejects) {
SkAutoMutexExclusive lock{fMu};
size_t delta = this->commonFilterLoop(drawables,
[&](size_t i, SkGlyphDigest digest, SkPoint pos) SK_REQUIRES(fMu) {
if (digest.canDrawAsMask()) {
drawables->push_back(fGlyphForIndex[digest.index()], i);
} else {
rejects->reject(i);
}
});
return delta;
}
size_t SkScalerCache::prepareForSDFTDrawing(
SkDrawableGlyphBuffer* drawables, SkSourceGlyphBuffer* rejects) {
SkAutoMutexExclusive lock{fMu};
size_t delta = this->commonFilterLoop(drawables,
[&](size_t i, SkGlyphDigest digest, SkPoint pos) SK_REQUIRES(fMu) {
if (digest.canDrawAsSDFT()) {
drawables->push_back(fGlyphForIndex[digest.index()], i);
} else {
rejects->reject(i);
}
});
return delta;
}
size_t SkScalerCache::prepareForPathDrawing(
SkDrawableGlyphBuffer* drawables, SkSourceGlyphBuffer* rejects) {
SkAutoMutexExclusive lock{fMu};
size_t pathDelta = 0;
size_t delta = this->commonFilterLoop(drawables,
[&](size_t i, SkGlyphDigest digest, SkPoint pos) SK_REQUIRES(fMu) {
SkGlyph* glyph = fGlyphForIndex[digest.index()];
if (!digest.isColor()) {
#ifndef SKIA_STRUCTURED_BINDINGS_BACKPORT
auto [path, pathSize] = this->preparePath(glyph);
#else
STRUCTURED_BINDING_2(path, pathSize, this->preparePath(glyph));
#endif
pathDelta += pathSize;
if (path != nullptr) {
// Save off the path to draw later.
drawables->push_back(path, i);
} else {
// Glyph does not have a path. It is probably bitmap only.
rejects->reject(i, glyph->maxDimension());
}
} else {
// Glyph is color.
rejects->reject(i, glyph->maxDimension());
}
});
return delta + pathDelta;
}
void SkScalerCache::findIntercepts(const SkScalar bounds[2], SkScalar scale, SkScalar xPos,
SkGlyph* glyph, SkScalar* array, int* count) {
SkAutoMutexExclusive lock{fMu};
glyph->ensureIntercepts(bounds, scale, xPos, array, count, &fAlloc);
}
void SkScalerCache::dump() const {
SkAutoMutexExclusive lock{fMu};
const SkTypeface* face = fScalerContext->getTypeface();
const SkScalerContextRec& rec = fScalerContext->getRec();
SkMatrix matrix;
rec.getSingleMatrix(&matrix);
matrix.preScale(SkScalarInvert(rec.fTextSize), SkScalarInvert(rec.fTextSize));
SkString name;
face->getFamilyName(&name);
SkString msg;
SkFontStyle style = face->fontStyle();
msg.printf("cache typeface:%x %25s:(%d,%d,%d)\n %s glyphs:%3d",
face->uniqueID(), name.c_str(), style.weight(), style.width(), style.slant(),
rec.dump().c_str(), fDigestForPackedGlyphID.count());
SkDebugf("%s\n", msg.c_str());
}