blob: 531a421a49eda90f5a4d28ac920cfb972eb92a50 [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/SkStrike.h"
#include "include/core/SkDrawable.h"
#include "include/core/SkGraphics.h"
#include "include/core/SkPath.h"
#include "include/core/SkTraceMemoryDump.h"
#include "include/core/SkTypeface.h"
#include "src/core/SkDistanceFieldGen.h"
#include "src/core/SkEnumerate.h"
#include "src/core/SkGlyphBuffer.h"
#include "src/core/SkScalerContext.h"
#include "src/core/SkStrikeCache.h"
#include "src/text/StrikeForGPU.h"
#if SK_SUPPORT_GPU
#include "src/text/gpu/StrikeCache.h"
#endif
static SkFontMetrics use_or_generate_metrics(
const SkFontMetrics* metrics, SkScalerContext* context) {
SkFontMetrics answer;
if (metrics) {
answer = *metrics;
} else {
context->getFontMetrics(&answer);
}
return answer;
}
SkStrike::SkStrike(SkStrikeCache* strikeCache,
const SkStrikeSpec& strikeSpec,
std::unique_ptr<SkScalerContext> scaler,
const SkFontMetrics* metrics,
std::unique_ptr<SkStrikePinner> pinner)
: fFontMetrics{use_or_generate_metrics(metrics, scaler.get())}
, fRoundingSpec{scaler->isSubpixel(),
scaler->computeAxisAlignmentForHText()}
, fStrikeSpec{strikeSpec}
, fStrikeCache{strikeCache}
, fScalerContext{std::move(scaler)}
, fPinner{std::move(pinner)} {
SkASSERT(fScalerContext != nullptr);
}
SkGlyph* SkStrike::mergeGlyphAndImage(SkPackedGlyphID toID, const SkGlyph& fromGlyph) {
size_t increase = 0;
SkGlyph* glyph;
{
SkAutoMutexExclusive lock{fStrikeLock};
// TODO(herb): remove finding the glyph when setting the metrics and image are separated
SkGlyphDigest* digest = fDigestForPackedGlyphID.find(toID);
if (digest != nullptr) {
glyph = fGlyphForIndex[digest->index()];
if (fromGlyph.setImageHasBeenCalled()) {
if (glyph->setImageHasBeenCalled()) {
// Should never set an image on a glyph which already has an image.
SkDEBUGFAIL("Re-adding image to existing glyph. This should not happen.");
}
// TODO: assert that any metrics on fromGlyph are the same.
increase = glyph->setMetricsAndImage(&fAlloc, fromGlyph);
}
} else {
glyph = fAlloc.make<SkGlyph>(toID);
increase = glyph->setMetricsAndImage(&fAlloc, fromGlyph) + sizeof(SkGlyph);
(void)this->addGlyph(glyph);
}
}
this->updateDelta(increase);
return glyph;
}
const SkPath* SkStrike::mergePath(SkGlyph* glyph, const SkPath* path, bool hairline) {
size_t increase = 0;
{
SkAutoMutexExclusive lock{fStrikeLock};
if (glyph->setPathHasBeenCalled()) {
SkDEBUGFAIL("Re-adding path to existing glyph. This should not happen.");
}
if (glyph->setPath(&fAlloc, path, hairline)) {
increase = glyph->path()->approximateBytesUsed();
}
}
this->updateDelta(increase);
return glyph->path();
}
const SkDrawable* SkStrike::mergeDrawable(SkGlyph* glyph, sk_sp<SkDrawable> drawable) {
size_t increase = 0;
{
SkAutoMutexExclusive lock{fStrikeLock};
if (glyph->setDrawableHasBeenCalled()) {
SkDEBUGFAIL("Re-adding drawable to existing glyph. This should not happen.");
}
if (glyph->setDrawable(&fAlloc, std::move(drawable))) {
increase = glyph->drawable()->approximateBytesUsed();
SkASSERT(increase > 0);
}
}
this->updateDelta(increase);
return glyph->drawable();
}
void SkStrike::findIntercepts(const SkScalar bounds[2], SkScalar scale, SkScalar xPos,
SkGlyph* glyph, SkScalar* array, int* count) {
SkAutoMutexExclusive lock{fStrikeLock};
glyph->ensureIntercepts(bounds, scale, xPos, array, count, &fAlloc);
}
SkSpan<const SkGlyph*> SkStrike::metrics(
SkSpan<const SkGlyphID> glyphIDs, const SkGlyph* results[]) {
size_t increase = 0;
SkSpan<const SkGlyph*> glyphs;
{
SkAutoMutexExclusive lock{fStrikeLock};
std::tie(glyphs, increase)= this->internalPrepare(glyphIDs, kMetricsOnly, results);
}
this->updateDelta(increase);
return glyphs;
}
SkSpan<const SkGlyph*> SkStrike::preparePaths(
SkSpan<const SkGlyphID> glyphIDs, const SkGlyph* results[]) {
size_t increase = 0;
SkSpan<const SkGlyph*> glyphs;
{
SkAutoMutexExclusive lock{fStrikeLock};
std::tie(glyphs, increase) = this->internalPrepare(glyphIDs, kMetricsAndPath, results);
}
this->updateDelta(increase);
return glyphs;
}
SkSpan<const SkGlyph*> SkStrike::prepareImages(
SkSpan<const SkPackedGlyphID> glyphIDs, const SkGlyph* results[]) {
const SkGlyph** cursor = results;
size_t increase = 0;
{
SkAutoMutexExclusive lock{fStrikeLock};
for (auto glyphID : glyphIDs) {
auto[glyph, glyphSize] = this->glyph(glyphID);
auto[_, imageSize] = this->prepareImage(glyph);
increase += glyphSize + imageSize;
*cursor++ = glyph;
}
}
this->updateDelta(increase);
return {results, glyphIDs.size()};
}
SkSpan<const SkGlyph*> SkStrike::prepareDrawables(
SkSpan<const SkGlyphID> glyphIDs, const SkGlyph* results[]) {
const SkGlyph** cursor = results;
size_t increase = 0;
{
SkAutoMutexExclusive lock{fStrikeLock};
for (auto glyphID : glyphIDs) {
auto[glyph, glyphSize] = this->glyph(SkPackedGlyphID{glyphID});
size_t drawableSize = this->prepareDrawable(glyph);
increase += glyphSize + drawableSize;
*cursor++ = glyph;
}
}
this->updateDelta(increase);
return {results, glyphIDs.size()};
}
void SkStrike::prepareForDrawingMasksCPU(SkDrawableGlyphBuffer* accepted) {
size_t increase = 0;
{
SkAutoMutexExclusive lock{fStrikeLock};
increase += this->commonFilterLoop(
accepted,
[&](size_t i, SkGlyphDigest digest, SkPoint pos) SK_REQUIRES(fStrikeLock) {
// If the glyph is too large, then no image is created.
SkGlyph* glyph = fGlyphForIndex[digest.index()];
auto [image, imageSize] = this->prepareImage(glyph);
if (image != nullptr) {
accepted->accept(glyph, i);
increase += imageSize;
}
});
}
this->updateDelta(increase);
}
// Note: this does not actually fill out the image. That happens at atlas building time.
SkRect SkStrike::prepareForMaskDrawing(SkDrawableGlyphBuffer* accepted,
SkSourceGlyphBuffer* rejected) {
SkGlyphRect boundingRect = skglyph::empty_rect();
size_t increase = 0;
{
SkAutoMutexExclusive lock{fStrikeLock};
for (auto [i, packedID, pos] : SkMakeEnumerate(accepted->input())) {
if (SkScalarsAreFinite(pos.x(), pos.y())) {
auto [digest, glyphIncrease] = this->digest(packedID);
increase += glyphIncrease;
// N.B. this must have the same behavior as RemoteStrike::prepareForMaskDrawing.
if (!digest.isEmpty()) {
if (digest.canDrawAsMask()) {
const SkGlyphRect glyphBounds = digest.bounds().offset(pos);
boundingRect = skglyph::rect_union(boundingRect, glyphBounds);
accepted->accept(packedID, glyphBounds.leftTop(), digest.maskFormat());
} else {
rejected->reject(i);
}
}
}
}
}
this->updateDelta(increase);
return boundingRect.rect();
}
#if !defined(SK_DISABLE_SDF_TEXT)
SkRect SkStrike::prepareForSDFTDrawing(SkDrawableGlyphBuffer* accepted,
SkSourceGlyphBuffer* rejected) {
SkGlyphRect boundingRect = skglyph::empty_rect();
size_t increase = 0;
{
SkAutoMutexExclusive lock{fStrikeLock};
for (auto [i, packedID, pos] : SkMakeEnumerate(accepted->input())) {
if (SkScalarsAreFinite(pos.x(), pos.y())) {
auto [digest, glyphIncrease] = this->digest(packedID);
increase += glyphIncrease;
// N.B. this must have the same behavior as RemoteStrike::prepareForSDFTDrawing.
if (!digest.isEmpty()) {
if (digest.canDrawAsSDFT()) {
const SkGlyphRect glyphBounds =
digest.bounds()
// The SDFT glyphs have 2-pixel wide padding that should
// not be used in calculating the source rectangle.
.inset(SK_DistanceFieldInset, SK_DistanceFieldInset)
.offset(pos);
boundingRect = skglyph::rect_union(boundingRect, glyphBounds);
accepted->accept(packedID, glyphBounds.leftTop(), digest.maskFormat());
} else {
// Assume whatever follows SDF doesn't care about the maximum rejected size.
rejected->reject(i);
}
}
}
}
}
this->updateDelta(increase);
return boundingRect.rect();
}
#endif
void SkStrike::prepareForPathDrawing(SkDrawableGlyphBuffer* accepted,
SkSourceGlyphBuffer* rejected) {
size_t increase = 0;
{
SkAutoMutexExclusive lock{fStrikeLock};
for (auto [i, packedID, pos] : SkMakeEnumerate(accepted->input())) {
if (SkScalarsAreFinite(pos.x(), pos.y())) {
auto [digest, glyphIncrease] = this->digest(packedID);
increase += glyphIncrease;
if (!digest.isEmpty()) {
SkGlyph* glyph = fGlyphForIndex[digest.index()];
increase += this->preparePath(glyph);
if (glyph->path() != nullptr) {
// Save off the path to draw later.
accepted->accept(packedID, pos);
} else {
// Glyph does not have a path.
rejected->reject(i);
}
}
}
}
}
this->updateDelta(increase);
}
void SkStrike::prepareForDrawableDrawing(SkDrawableGlyphBuffer* accepted,
SkSourceGlyphBuffer* rejected) {
size_t increase = 0;
{
SkAutoMutexExclusive lock{fStrikeLock};
for (auto [i, packedID, pos] : SkMakeEnumerate(accepted->input())) {
if (SkScalarsAreFinite(pos.x(), pos.y())) {
auto [digest, glyphIncrease] = this->digest(packedID);
increase += glyphIncrease;
if (!digest.isEmpty()) {
SkGlyph* glyph = fGlyphForIndex[digest.index()];
increase += this->prepareDrawable(glyph);
if (glyph->drawable() != nullptr) {
// Save off the drawable to draw later.
accepted->accept(packedID, pos);
} else {
// Glyph does not have a drawable.
rejected->reject(i);
}
}
}
}
}
this->updateDelta(increase);
}
SkScalar SkStrike::findMaximumGlyphDimension(SkSpan<const SkGlyphID> glyphs) {
size_t increase = 0;
SkScalar maxDimension = 0;
{
SkAutoMutexExclusive lock{fStrikeLock};
for (SkGlyphID glyphID : glyphs) {
auto [digest, glyphIncrease] = this->digest(SkPackedGlyphID{glyphID});
increase += glyphIncrease;
maxDimension = std::max(static_cast<SkScalar>(digest.maxDimension()), maxDimension);
}
}
this->updateDelta(increase);
return maxDimension;
}
void SkStrike::glyphIDsToPaths(SkSpan<sktext::IDOrPath> idsOrPaths) {
size_t increase = 0;
{
SkAutoMutexExclusive lock{fStrikeLock};
for (sktext::IDOrPath& idOrPath : idsOrPaths) {
auto [glyph, size] = this->glyph(SkPackedGlyphID{idOrPath.fGlyphID});
increase += size;
increase += this->preparePath(glyph);
new (&idOrPath.fPath) SkPath{*glyph->path()};
}
}
this->updateDelta(increase);
}
void SkStrike::glyphIDsToDrawables(SkSpan<sktext::IDOrDrawable> idsOrDrawables) {
size_t increase = 0;
{
SkAutoMutexExclusive lock{fStrikeLock};
for (sktext::IDOrDrawable& idOrDrawable : idsOrDrawables) {
auto [glyph, size] = this->glyph(SkPackedGlyphID{idOrDrawable.fGlyphID});
increase += size;
increase += this->prepareDrawable(glyph);
SkASSERT(glyph->drawable() != nullptr);
idOrDrawable.fDrawable = glyph->drawable();
}
}
this->updateDelta(increase);
}
void SkStrike::dump() const {
SkAutoMutexExclusive lock{fStrikeLock};
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());
}
void SkStrike::dumpMemoryStatistics(SkTraceMemoryDump* dump) const {
SkAutoMutexExclusive lock{fStrikeLock};
const SkTypeface* face = fScalerContext->getTypeface();
const SkScalerContextRec& rec = fScalerContext->getRec();
SkString fontName;
face->getFamilyName(&fontName);
// Replace all special characters with '_'.
for (size_t index = 0; index < fontName.size(); ++index) {
if (!std::isalnum(fontName[index])) {
fontName[index] = '_';
}
}
SkString dumpName = SkStringPrintf("%s/%s_%d/%p",
SkStrikeCache::kGlyphCacheDumpName,
fontName.c_str(),
rec.fTypefaceID,
this);
dump->dumpNumericValue(dumpName.c_str(), "size", "bytes", fMemoryUsed);
dump->dumpNumericValue(dumpName.c_str(),
"glyph_count", "objects",
fDigestForPackedGlyphID.count());
dump->setMemoryBacking(dumpName.c_str(), "malloc", nullptr);
}
template <typename Fn>
size_t SkStrike::commonFilterLoop(SkDrawableGlyphBuffer* accepted, Fn&& fn) {
size_t total = 0;
for (auto [i, packedID, pos] : SkMakeEnumerate(accepted->input())) {
if (SkScalarsAreFinite(pos.x(), pos.y())) {
auto [digest, size] = this->digest(packedID);
total += size;
if (!digest.isEmpty()) {
fn(i, digest, pos);
}
}
}
return total;
}
std::tuple<SkGlyph*, size_t> SkStrike::glyph(SkPackedGlyphID packedGlyphID) {
auto [digest, size] = this->digest(packedGlyphID);
return {fGlyphForIndex[digest.index()], size};
}
std::tuple<SkGlyphDigest, size_t> SkStrike::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 SkStrike::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 void*, size_t> SkStrike::prepareImage(SkGlyph* glyph) {
size_t delta = 0;
if (glyph->setImage(&fAlloc, fScalerContext.get())) {
delta = glyph->imageSize();
}
return {glyph->image(), delta};
}
size_t SkStrike::preparePath(SkGlyph* glyph) {
size_t delta = 0;
if (glyph->setPath(&fAlloc, fScalerContext.get())) {
delta = glyph->path()->approximateBytesUsed();
}
return delta;
}
size_t SkStrike::prepareDrawable(SkGlyph* glyph) {
size_t delta = 0;
if (glyph->setDrawable(&fAlloc, fScalerContext.get())) {
delta = glyph->drawable()->approximateBytesUsed();
SkASSERT(delta > 0);
}
return delta;
}
std::tuple<SkSpan<const SkGlyph*>, size_t> SkStrike::internalPrepare(
SkSpan<const SkGlyphID> glyphIDs, PathDetail pathDetail, const SkGlyph** results) {
const SkGlyph** cursor = results;
size_t delta = 0;
for (auto glyphID : glyphIDs) {
auto [glyph, size] = this->glyph(SkPackedGlyphID{glyphID});
delta += size;
if (pathDetail == kMetricsAndPath) {
size_t pathSize = this->preparePath(glyph);
delta += pathSize;
}
*cursor++ = glyph;
}
return {{results, glyphIDs.size()}, delta};
}
void SkStrike::updateDelta(size_t increase) {
if (increase != 0) {
SkAutoMutexExclusive lock{fStrikeCache->fLock};
fMemoryUsed += increase;
if (!fRemoved) {
fStrikeCache->fTotalMemoryUsed += increase;
}
}
}