Plumbing for glyph drawable

The plumbing necessary to allow glyphs to have an associated drawable.
The TestSVGTypeface is updated to produce drawables for testing.

Bug: skia:12121
Change-Id: I475a1bfc27bf11e732e18bed3c1a9593e7c901cd
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/413438
Reviewed-by: Derek Sollenberger <djsollen@google.com>
Reviewed-by: Herb Derby <herb@google.com>
Commit-Queue: Ben Wagner <bungeman@google.com>
This commit is contained in:
Ben Wagner 2021-05-27 17:42:58 -04:00 committed by SkCQ
parent ada59c148e
commit 35d25ac79f
17 changed files with 577 additions and 15 deletions

View File

@ -103,6 +103,12 @@ public:
*/
SkRect getBounds();
/**
* Return approximately how many bytes would be freed if this drawable is destroyed.
* The base implementation returns 0 to indicate that this is unknown.
*/
size_t approximateBytesUsed();
/**
* Calling this invalidates the previous generation ID, and causes a new one to be computed
* the next time getGenerationID() is called. Typically this is called by the object itself,
@ -132,6 +138,7 @@ protected:
SkDrawable();
virtual SkRect onGetBounds() = 0;
virtual size_t onApproximateBytesUsed();
virtual void onDraw(SkCanvas*) = 0;
virtual std::unique_ptr<GpuDrawHandler> onSnapGpuDrawHandler(GrBackendApi, const SkMatrix&,

View File

@ -14,6 +14,7 @@
#include <string>
#include <tuple>
#include "include/core/SkDrawable.h"
#include "include/core/SkSpan.h"
#include "include/core/SkTypeface.h"
#include "include/private/SkChecksum.h"
@ -171,7 +172,8 @@ private:
};
// Paths use a SkWriter32 which requires 4 byte alignment.
static const size_t kPathAlignment = 4u;
static const size_t kPathAlignment = 4u;
static const size_t kDrawableAlignment = 8u;
// -- StrikeSpec -----------------------------------------------------------------------------------
struct StrikeSpec {
@ -245,12 +247,15 @@ public:
void prepareForPathDrawing(
SkDrawableGlyphBuffer* accepted, SkSourceGlyphBuffer* rejected) override;
void prepareForDrawableDrawing(
SkDrawableGlyphBuffer* accepted, SkSourceGlyphBuffer* rejected) override;
void onAboutToExitScope() override {}
sk_sp<SkStrike> getUnderlyingStrike() const override { return nullptr; }
bool hasPendingGlyphs() const {
return !fMasksToSend.empty() || !fPathsToSend.empty();
return !fMasksToSend.empty() || !fPathsToSend.empty() || !fDrawablesToSend.empty();
}
void resetScalerContext();
@ -279,7 +284,27 @@ private:
}
};
// Same thing as MaskSummary, but for drawables.
struct DrawableSummary {
constexpr static uint16_t kIsDrawable = 0;
SkGlyphID glyphID;
// If drawing glyphID can be done with a drawable, this is 0, otherwise it is the max
// dimension of the glyph.
uint16_t maxDimensionOrDrawable;
};
struct DrawableSummaryTraits {
static SkGlyphID GetKey(DrawableSummary summary) {
return summary.glyphID;
}
static uint32_t Hash(SkGlyphID packedID) {
return SkChecksum::CheapMix(packedID);
}
};
void writeGlyphPath(const SkGlyph& glyph, Serializer* serializer) const;
void writeGlyphDrawable(const SkGlyph& glyph, Serializer* serializer) const;
void ensureScalerContext();
const SkAutoDescriptor fDescriptor;
@ -302,14 +327,16 @@ private:
// The masks and paths that currently reside in the GPU process.
SkTHashTable<SkGlyphDigest, uint32_t, SkGlyphDigest> fSentGlyphs;
SkTHashTable<PathSummary, SkPackedGlyphID, PathSummaryTraits> fSentPaths;
SkTHashTable<DrawableSummary, SkGlyphID, DrawableSummaryTraits> fSentDrawables;
// The Masks, SDFT Mask, and Paths that need to be sent to the GPU task for the processed
// TextBlobs. Cleared after diffs are serialized.
std::vector<SkGlyph> fMasksToSend;
std::vector<SkGlyph> fPathsToSend;
std::vector<SkGlyph> fDrawablesToSend;
// Alloc for storing bits and pieces of paths, Cleared after diffs are serialized.
SkArenaAllocWithReset fPathAlloc{256};
// Alloc for storing bits and pieces of paths and drawables, Cleared after diffs are serialized.
SkArenaAllocWithReset fAlloc{256};
};
RemoteStrike::RemoteStrike(
@ -378,7 +405,17 @@ void RemoteStrike::writePendingGlyphs(Serializer* serializer) {
this->writeGlyphPath(glyph, serializer);
}
fPathsToSend.clear();
fPathAlloc.reset();
// Write glyphs drawables.
serializer->emplace<uint64_t>(fDrawablesToSend.size());
for (SkGlyph& glyph : fDrawablesToSend) {
SkASSERT(SkMask::IsValidFormat(glyph.maskFormat()));
write_glyph(glyph, serializer);
writeGlyphDrawable(glyph, serializer);
}
fDrawablesToSend.clear();
fAlloc.reset();
}
void RemoteStrike::ensureScalerContext() {
@ -396,8 +433,7 @@ void RemoteStrike::setStrikeSpec(const SkStrikeSpec& strikeSpec) {
fStrikeSpec = &strikeSpec;
}
void RemoteStrike::writeGlyphPath(
const SkGlyph& glyph, Serializer* serializer) const {
void RemoteStrike::writeGlyphPath(const SkGlyph& glyph, Serializer* serializer) const {
const SkPath* path = glyph.path();
if (path == nullptr) {
@ -412,6 +448,25 @@ void RemoteStrike::writeGlyphPath(
serializer->write<bool>(glyph.pathIsHairline());
}
void RemoteStrike::writeGlyphDrawable(const SkGlyph& glyph, Serializer* serializer) const {
if (glyph.isEmpty()) {
serializer->write<uint64_t>(0u);
return;
}
SkDrawable* drawable = glyph.drawable();
if (drawable == nullptr) {
serializer->write<uint64_t>(0u);
return;
}
sk_sp<SkPicture> picture(drawable->newPictureSnapshot());
sk_sp<SkData> data = picture->serialize();
serializer->write<uint64_t>(data->size());
memcpy(serializer->allocate(data->size(), kDrawableAlignment), data->data(), data->size());
}
template <typename Rejector>
void RemoteStrike::commonMaskLoop(
SkDrawableGlyphBuffer* accepted, SkSourceGlyphBuffer* rejected, Rejector&& reject) {
@ -421,7 +476,7 @@ void RemoteStrike::commonMaskLoop(
if (digest == nullptr) {
// Put the new SkGlyph in the glyphs to send.
this->ensureScalerContext();
fMasksToSend.emplace_back(fContext->makeGlyph(packedID, &fPathAlloc));
fMasksToSend.emplace_back(fContext->makeGlyph(packedID, &fAlloc));
SkGlyph* glyph = &fMasksToSend.back();
SkGlyphDigest newDigest{0, *glyph};
@ -453,7 +508,7 @@ void RemoteStrike::prepareForMaskDrawing(
// Put the new SkGlyph in the glyphs to send.
this->ensureScalerContext();
fMasksToSend.emplace_back(fContext->makeGlyph(packedID, &fPathAlloc));
fMasksToSend.emplace_back(fContext->makeGlyph(packedID, &fAlloc));
SkGlyph* glyph = &fMasksToSend.back();
SkGlyphDigest newDigest{0, *glyph};
@ -488,11 +543,11 @@ void RemoteStrike::prepareForPathDrawing(
// Put the new SkGlyph in the glyphs to send.
this->ensureScalerContext();
fPathsToSend.emplace_back(fContext->makeGlyph(packedID, &fPathAlloc));
fPathsToSend.emplace_back(fContext->makeGlyph(packedID, &fAlloc));
SkGlyph* glyph = &fPathsToSend.back();
uint16_t maxDimensionOrPath = glyph->maxDimension();
glyph->setPath(&fPathAlloc, fContext.get());
glyph->setPath(&fAlloc, fContext.get());
if (glyph->path() != nullptr) {
maxDimensionOrPath = PathSummary::kIsPath;
}
@ -507,6 +562,35 @@ void RemoteStrike::prepareForPathDrawing(
});
}
void RemoteStrike::prepareForDrawableDrawing(
SkDrawableGlyphBuffer* accepted, SkSourceGlyphBuffer* rejected) {
accepted->forEachInput(
[&](size_t i, SkPackedGlyphID packedID, SkPoint position) {
SkGlyphID glyphID = packedID.glyphID();
DrawableSummary* summary = fSentDrawables.find(glyphID);
if (summary == nullptr) {
// Put the new SkGlyph in the glyphs to send.
this->ensureScalerContext();
fDrawablesToSend.emplace_back(fContext->makeGlyph(packedID, &fAlloc));
SkGlyph* glyph = &fDrawablesToSend.back();
uint16_t maxDimensionOrDrawable = glyph->maxDimension();
glyph->setDrawable(&fAlloc, fContext.get());
if (glyph->drawable() != nullptr) {
maxDimensionOrDrawable = DrawableSummary::kIsDrawable;
}
DrawableSummary newSummary = {glyph->getGlyphID(), maxDimensionOrDrawable};
summary = fSentDrawables.set(newSummary);
}
if (summary->maxDimensionOrDrawable != DrawableSummary::kIsDrawable) {
rejected->reject(i, (int)summary->maxDimensionOrDrawable);
}
});
}
// -- WireTypeface ---------------------------------------------------------------------------------
struct WireTypeface {
WireTypeface() = default;
@ -866,6 +950,18 @@ public:
bool readStrikeData(const volatile void* memory, size_t memorySize);
private:
class PictureBackedGlyphDrawable final : public SkDrawable {
public:
PictureBackedGlyphDrawable(sk_sp<SkPicture> self) : fSelf(std::move(self)) {}
private:
sk_sp<SkPicture> fSelf;
SkRect onGetBounds() override { return fSelf->cullRect(); }
size_t onApproximateBytesUsed() override {
return sizeof(PictureBackedGlyphDrawable) + fSelf->approximateBytesUsed();
}
void onDraw(SkCanvas* canvas) override { canvas->drawPicture(fSelf); }
};
static bool ReadGlyph(SkTLazy<SkGlyph>& glyph, Deserializer* deserializer);
sk_sp<SkTypeface> addTypeface(const WireTypeface& wire);
@ -923,6 +1019,7 @@ bool SkStrikeClientImpl::readStrikeData(const volatile void* memory, size_t memo
uint64_t strikeCount = 0;
uint64_t glyphImagesCount = 0;
uint64_t glyphPathsCount = 0;
uint64_t glyphDrawablesCount = 0;
if (!deserializer.read<uint64_t>(&typefaceSize)) READ_FAILURE
for (size_t i = 0; i < typefaceSize; ++i) {
@ -1028,6 +1125,30 @@ bool SkStrikeClientImpl::readStrikeData(const volatile void* memory, size_t memo
strike->mergePath(allocatedGlyph, pathPtr, hairline);
}
if (!deserializer.read<uint64_t>(&glyphDrawablesCount)) READ_FAILURE
for (size_t j = 0; j < glyphDrawablesCount; j++) {
SkTLazy<SkGlyph> glyph;
if (!ReadGlyph(glyph, &deserializer)) READ_FAILURE
SkGlyph* allocatedGlyph = strike->mergeGlyphAndImage(glyph->getPackedID(), *glyph);
sk_sp<SkDrawable> drawable;
uint64_t drawableSize = 0u;
if (!deserializer.read<uint64_t>(&drawableSize)) READ_FAILURE
if (drawableSize > 0) {
auto* drawableData = deserializer.read(drawableSize, kDrawableAlignment);
if (!drawableData) READ_FAILURE
sk_sp<SkPicture> picture(SkPicture::MakeFromData(
const_cast<const void*>(drawableData), drawableSize));
if (!picture) READ_FAILURE
drawable = sk_make_sp<PictureBackedGlyphDrawable>(std::move(picture));
}
strike->mergeDrawable(allocatedGlyph, std::move(drawable));
}
}
#if defined(SK_TRACE_GLYPH_RUN_PROCESS)

View File

@ -62,6 +62,13 @@ SkRect SkDrawable::getBounds() {
return this->onGetBounds();
}
size_t SkDrawable::approximateBytesUsed() {
return this->onApproximateBytesUsed();
}
size_t SkDrawable::onApproximateBytesUsed() {
return 0;
}
void SkDrawable::notifyDrawingChanged() {
fGenerationID = 0;
}

View File

@ -7,11 +7,18 @@
#include "src/core/SkGlyph.h"
#include "include/core/SkDrawable.h"
#include "src/core/SkArenaAlloc.h"
#include "src/core/SkScalerContext.h"
#include "src/pathops/SkPathOpsCubic.h"
#include "src/pathops/SkPathOpsQuad.h"
SkGlyph::SkGlyph(const SkGlyph&) = default;
SkGlyph& SkGlyph::operator=(const SkGlyph&) = default;
SkGlyph::SkGlyph(SkGlyph&&) = default;
SkGlyph& SkGlyph::operator=(SkGlyph&&) = default;
SkGlyph::~SkGlyph() = default;
SkMask SkGlyph::mask() const {
SkMask mask;
mask.fImage = (uint8_t*)fImage;
@ -189,6 +196,43 @@ bool SkGlyph::pathIsHairline() const {
return fPathData->fHairline;
}
void SkGlyph::installDrawable(SkArenaAlloc* alloc, sk_sp<SkDrawable> drawable) {
SkASSERT(fDrawableData == nullptr);
SkASSERT(!this->setDrawableHasBeenCalled());
fDrawableData = alloc->make<SkGlyph::DrawableData>();
if (drawable != nullptr) {
fDrawableData->fDrawable = std::move(drawable);
fDrawableData->fDrawable->getGenerationID();
fDrawableData->fHasDrawable = true;
}
}
bool SkGlyph::setDrawable(SkArenaAlloc* alloc, SkScalerContext* scalerContext) {
if (!this->setDrawableHasBeenCalled()) {
sk_sp<SkDrawable> drawable = scalerContext->getDrawable(*this);
this->installDrawable(alloc, std::move(drawable));
return this->drawable() != nullptr;
}
return false;
}
bool SkGlyph::setDrawable(SkArenaAlloc* alloc, sk_sp<SkDrawable> drawable) {
if (!this->setDrawableHasBeenCalled()) {
this->installDrawable(alloc, std::move(drawable));
return this->drawable() != nullptr;
}
return false;
}
SkDrawable* SkGlyph::drawable() const {
// setDrawable must have been called previously.
SkASSERT(this->setDrawableHasBeenCalled());
if (fDrawableData->fHasDrawable) {
return fDrawableData->fDrawable.get();
}
return nullptr;
}
static std::tuple<SkScalar, SkScalar> calculate_path_gap(
SkScalar topOffset, SkScalar bottomOffset, const SkPath& path) {

View File

@ -19,6 +19,7 @@
#include "src/core/SkStrikeForGPU.h"
class SkArenaAlloc;
class SkDrawable;
class SkScalerContext;
// A combination of SkGlyphID and sub-pixel position information.
@ -280,6 +281,11 @@ class SkGlyph {
public:
// SkGlyph() is used for testing.
constexpr SkGlyph() : SkGlyph{SkPackedGlyphID()} { }
SkGlyph(const SkGlyph&);
SkGlyph& operator=(const SkGlyph&);
SkGlyph(SkGlyph&&);
SkGlyph& operator=(SkGlyph&&);
~SkGlyph();
constexpr explicit SkGlyph(SkPackedGlyphID id) : fID{id} { }
SkVector advanceVector() const { return SkVector{fAdvanceX, fAdvanceY}; }
@ -349,6 +355,11 @@ public:
const SkPath* path() const;
bool pathIsHairline() const;
bool setDrawable(SkArenaAlloc* alloc, SkScalerContext* scalerContext);
bool setDrawable(SkArenaAlloc* alloc, sk_sp<SkDrawable> drawable);
bool setDrawableHasBeenCalled() const { return fDrawableData != nullptr; }
SkDrawable* drawable() const;
// Format
bool isColor() const { return fMaskFormat == SkMask::kARGB32_Format; }
SkMask::Format maskFormat() const { return fMaskFormat; }
@ -428,11 +439,20 @@ private:
bool fHairline{false};
};
struct DrawableData {
Intercept* fIntercept{nullptr};
sk_sp<SkDrawable> fDrawable;
bool fHasDrawable{false};
};
size_t allocImage(SkArenaAlloc* alloc);
// path == nullptr indicates that there is no path.
void installPath(SkArenaAlloc* alloc, const SkPath* path, bool hairline);
// drawable == nullptr indicates that there is no path.
void installDrawable(SkArenaAlloc* alloc, sk_sp<SkDrawable> drawable);
// The width and height of the glyph mask.
uint16_t fWidth = 0,
fHeight = 0;
@ -448,6 +468,7 @@ private:
// else if fPathData is not null, then a path has been requested. The fPath field of fPathData
// may still be null after the request meaning that there is no path for this glyph.
PathData* fPathData = nullptr;
DrawableData* fDrawableData = nullptr;
// The advance for this glyph.
float fAdvanceX = 0,

View File

@ -16,6 +16,8 @@
class SkStrikeForGPU;
struct SkGlyphPositionRoundingSpec;
class SkPath;
class SkDrawable;
// SkSourceGlyphBuffer is the source of glyphs between the different stages of glyph drawing.
// It starts with the glyphs and positions from the SkGlyphRun as the first source. When glyphs
@ -110,6 +112,11 @@ public:
SkDEBUGCODE(fTag = kPath);
return *this;
}
SkGlyphVariant& operator= (SkDrawable* drawable) {
fV.drawable = drawable;
SkDEBUGCODE(fTag = kDrawable);
return *this;
}
SkGlyph* glyph() const {
SkASSERT(fTag == kGlyph);
@ -119,19 +126,25 @@ public:
SkASSERT(fTag == kPath);
return fV.path;
}
SkDrawable* drawable() const {
SkASSERT(fTag == kDrawable);
return fV.drawable;
}
SkPackedGlyphID packedID() const {
SkASSERT(fTag == kPackedID);
return fV.packedID;
}
operator SkPackedGlyphID() const { return this->packedID(); }
operator SkGlyph*() const { return this->glyph(); }
operator const SkPath*() const { return this->path(); }
operator SkPackedGlyphID() const { return this->packedID(); }
operator SkGlyph*() const { return this->glyph(); }
operator const SkPath*() const { return this->path(); }
operator const SkDrawable*()const { return this->drawable(); }
private:
union {
SkGlyph* glyph;
const SkPath* path;
SkDrawable* drawable;
SkPackedGlyphID packedID;
} fV;
@ -140,7 +153,8 @@ private:
kEmpty,
kPackedID,
kGlyph,
kPath
kPath,
kDrawable,
} fTag{kEmpty};
#endif
};
@ -208,6 +222,15 @@ public:
fAcceptedSize++;
}
// Store drawable in the next slot, using the position information located at index from.
void accept(SkDrawable* drawable, size_t from) {
SkASSERT(fPhase == kProcess);
SkASSERT(fAcceptedSize <= from);
fPositions[fAcceptedSize] = fPositions[from];
fMultiBuffer[fAcceptedSize] = drawable;
fAcceptedSize++;
}
// The result after a series of `accept` of accepted SkGlyph* or SkPath*.
SkZip<SkGlyphVariant, SkPoint> accepted() {
SkASSERT(fPhase == kProcess);

View File

@ -146,6 +146,23 @@ void SkGlyphRunListPainter::drawForBitmapDevice(
canvas->drawPath(deviceOutline, pathPaint);
}
}
fAccepted.startSource(fRejected.source());
strike->prepareForDrawableDrawing(&fAccepted, &fRejected);
fRejected.flipRejectsToSource();
for (auto [variant, pos] : fAccepted.accepted()) {
SkDrawable* drawable = variant.drawable();
SkMatrix m;
SkPoint translate = drawOrigin + pos;
m.setScaleTranslate(strikeToSourceScale, strikeToSourceScale,
translate.x(), translate.y());
SkAutoCanvasRestore acr(canvas, false);
SkRect drawableBounds = drawable->getBounds();
m.mapRect(&drawableBounds);
canvas->saveLayer(&drawableBounds, &paint);
drawable->draw(canvas, &m);
}
}
if (!fRejected.source().empty() && !deviceMatrix.hasPerspective()) {
SkStrikeSpec strikeSpec = SkStrikeSpec::MakeMask(
@ -349,6 +366,34 @@ void SkGlyphRunListPainter::processGlyphRun(SkGlyphRunPainterInterface* process,
// maxDimensionInSourceSpace is used to calculate the factor from strike space to source
// space.
SkScalar maxDimensionInSourceSpace = 0.0;
if (!fRejected.source().empty()) {
// Drawable case - handle big things with that have a drawable.
auto [strikeSpec, strikeToSourceScale] =
SkStrikeSpec::MakePath(runFont, runPaint, fDeviceProps, fScalerContextFlags);
#if defined(SK_TRACE_GLYPH_RUN_PROCESS)
msg.appendf(" Drawable case:\n%s", strikeSpec.dump().c_str());
#endif
if (!SkScalarNearlyZero(strikeToSourceScale)) {
SkScopedStrikeForGPU strike = strikeSpec.findOrCreateScopedStrike(fStrikeCache);
fAccepted.startSource(fRejected.source());
#if defined(SK_TRACE_GLYPH_RUN_PROCESS)
msg.appendf(" glyphs:(x,y):\n %s\n", fAccepted.dumpInput().c_str());
#endif
strike->prepareForDrawableDrawing(&fAccepted, &fRejected);
fRejected.flipRejectsToSource();
auto [minHint, maxHint] = fRejected.maxDimensionHint();
maxDimensionInSourceSpace = SkScalarCeilToScalar(maxHint * strikeToSourceScale);
if (process && !fAccepted.empty()) {
// processSourceDrawables must be called even if there are no glyphs to make sure
// runs are set correctly.
process->processSourceDrawables(fAccepted.accepted(), runFont, strikeToSourceScale);
}
}
}
if (!fRejected.source().empty()) {
// Path case - handle big things without color and that have a path.
auto [strikeSpec, strikeToSourceScale] =

View File

@ -146,6 +146,10 @@ public:
const SkFont& runFont,
SkScalar strikeToSourceScale) = 0;
virtual void processSourceDrawables(const SkZip<SkGlyphVariant, SkPoint>& accepted,
const SkFont& runFont,
SkScalar strikeToSourceScale) = 0;
virtual void processSourceSDFT(const SkZip<SkGlyphVariant, SkPoint>& accepted,
sk_sp<SkStrike>&& strike,
SkScalar strikeToSourceScale,

View File

@ -7,6 +7,7 @@
#include "src/core/SkScalerCache.h"
#include "include/core/SkDrawable.h"
#include "include/core/SkGraphics.h"
#include "include/core/SkPath.h"
#include "include/core/SkTypeface.h"
@ -79,6 +80,28 @@ std::tuple<const SkPath*, size_t> SkScalerCache::mergePath(
return {glyph->path(), pathDelta};
}
std::tuple<SkDrawable*, size_t> SkScalerCache::prepareDrawable(SkGlyph* glyph) {
size_t delta = 0;
if (glyph->setDrawable(&fAlloc, fScalerContext.get())) {
delta = glyph->drawable()->approximateBytesUsed();
}
return {glyph->drawable(), delta};
}
std::tuple<SkDrawable*, size_t> SkScalerCache::mergeDrawable(SkGlyph* glyph,
sk_sp<SkDrawable> drawable) {
SkAutoMutexExclusive lock{fMu};
size_t drawableDelta = 0;
if (glyph->setDrawableHasBeenCalled()) {
SkDEBUGFAIL("Re-adding drawable to existing glyph. This should not happen.");
}
if (glyph->setDrawable(&fAlloc, std::move(drawable))) {
drawableDelta = glyph->drawable()->approximateBytesUsed();
SkASSERT(drawableDelta > 0);
}
return {glyph->drawable(), drawableDelta};
}
int SkScalerCache::countCachedGlyphs() const {
SkAutoMutexExclusive lock(fMu);
return fDigestForPackedGlyphID.count();
@ -249,6 +272,27 @@ size_t SkScalerCache::prepareForPathDrawing(
return delta + pathDelta;
}
size_t SkScalerCache::prepareForDrawableDrawing(
SkDrawableGlyphBuffer* accepted, SkSourceGlyphBuffer* rejected) {
SkAutoMutexExclusive lock{fMu};
size_t drawableDelta = 0;
size_t delta = this->commonFilterLoop(accepted,
[&](size_t i, SkGlyphDigest digest, SkPoint pos) SK_REQUIRES(fMu) {
SkGlyph* glyph = fGlyphForIndex[digest.index()];
auto [drawable, drawableSize] = this->prepareDrawable(glyph);
drawableDelta += drawableSize;
if (drawable != nullptr) {
// Save off the drawable to draw later.
accepted->accept(drawable, i);
} else {
// Glyph does not have a drawable.
rejected->reject(i, glyph->maxDimension());
}
});
return delta + drawableDelta;
}
void SkScalerCache::findIntercepts(const SkScalar bounds[2], SkScalar scale, SkScalar xPos,
SkGlyph* glyph, SkScalar* array, int* count) {
SkAutoMutexExclusive lock{fMu};

View File

@ -39,6 +39,10 @@ public:
std::tuple<const SkPath*, size_t> mergePath(
SkGlyph* glyph, const SkPath* path, bool hairline) SK_EXCLUDES(fMu);
// If the drawable has never been set, then add a drawble to glyph.
std::tuple<SkDrawable*, size_t> mergeDrawable(
SkGlyph* glyph, sk_sp<SkDrawable> drawable) SK_EXCLUDES(fMu);
/** Return the number of glyphs currently cached. */
int countCachedGlyphs() const SK_EXCLUDES(fMu);
@ -77,6 +81,9 @@ public:
size_t prepareForPathDrawing(
SkDrawableGlyphBuffer* accepted, SkSourceGlyphBuffer* rejected) SK_EXCLUDES(fMu);
size_t prepareForDrawableDrawing(
SkDrawableGlyphBuffer* accepted, SkSourceGlyphBuffer* rejected) SK_EXCLUDES(fMu);
void dump() const SK_EXCLUDES(fMu);
SkScalerContext* getScalerContext() const { return fScalerContext.get(); }
@ -99,6 +106,9 @@ private:
// If the path has never been set, then use the scaler context to add the glyph.
std::tuple<const SkPath*, size_t> preparePath(SkGlyph*) SK_REQUIRES(fMu);
// If the drawable has never been set, then use the scaler context to add the glyph.
std::tuple<SkDrawable*, size_t> prepareDrawable(SkGlyph*) SK_REQUIRES(fMu);
enum PathDetail {
kMetricsOnly,
kMetricsAndPath

View File

@ -8,6 +8,7 @@
#include "include/core/SkPaint.h"
#include "src/core/SkScalerContext.h"
#include "include/core/SkDrawable.h"
#include "include/core/SkFontMetrics.h"
#include "include/core/SkMaskFilter.h"
#include "include/core/SkPathEffect.h"
@ -695,6 +696,14 @@ void SkScalerContext::getPath(SkGlyph& glyph, SkArenaAlloc* alloc) {
this->internalGetPath(glyph, alloc);
}
sk_sp<SkDrawable> SkScalerContext::getDrawable(SkGlyph& glyph) {
return this->generateDrawable(glyph);
}
//TODO: make pure virtual
sk_sp<SkDrawable> SkScalerContext::generateDrawable(const SkGlyph&) {
return nullptr;
}
void SkScalerContext::getFontMetrics(SkFontMetrics* fm) {
SkASSERT(fm);
this->generateFontMetrics(fm);

View File

@ -295,6 +295,7 @@ public:
SkGlyph makeGlyph(SkPackedGlyphID, SkArenaAlloc*);
void getImage(const SkGlyph&);
void getPath(SkGlyph&, SkArenaAlloc*);
sk_sp<SkDrawable> getDrawable(SkGlyph&);
void getFontMetrics(SkFontMetrics*);
/** Return the size in bytes of the associated gamma lookup table
@ -396,6 +397,16 @@ protected:
*/
virtual bool SK_WARN_UNUSED_RESULT generatePath(const SkGlyph&, SkPath*) = 0;
/** Returns the drawable for the glyph (if any).
*
* The generated drawable will be lifetime scoped to the lifetime of this scaler context.
* This means the drawable may refer to the scaler context and associated font data.
*
* The drawable does not need to be flattenable (e.g. implement getFactory and getTypeName).
* Any necessary serialization will be done with newPictureSnapshot.
*/
virtual sk_sp<SkDrawable> generateDrawable(const SkGlyph&); // TODO: = 0
/** Retrieves font metrics. */
virtual void generateFontMetrics(SkFontMetrics*) = 0;

View File

@ -11,6 +11,7 @@
#include <unordered_map>
#include <unordered_set>
#include "include/core/SkDrawable.h"
#include "include/private/SkSpinlock.h"
#include "include/private/SkTemplates.h"
#include "src/core/SkDescriptor.h"
@ -61,6 +62,12 @@ public:
return glyphPath;
}
const SkDrawable* mergeDrawable(SkGlyph* glyph, sk_sp<SkDrawable> drawable) {
auto [glyphDrawable, increase] = fScalerCache.mergeDrawable(glyph, std::move(drawable));
this->updateDelta(increase);
return glyphDrawable;
}
// [[deprecated]]
SkScalerContext* getScalerContext() const {
return fScalerCache.getScalerContext();
@ -135,6 +142,12 @@ public:
this->updateDelta(increase);
}
void prepareForDrawableDrawing(
SkDrawableGlyphBuffer* accepted, SkSourceGlyphBuffer* rejected) override {
size_t increase = fScalerCache.prepareForDrawableDrawing(accepted, rejected);
this->updateDelta(increase);
}
void onAboutToExitScope() override {
this->unref();
}

View File

@ -41,6 +41,9 @@ public:
virtual void prepareForPathDrawing(
SkDrawableGlyphBuffer* accepted, SkSourceGlyphBuffer* rejected) = 0;
virtual void prepareForDrawableDrawing(
SkDrawableGlyphBuffer* accepted, SkSourceGlyphBuffer* rejected) = 0;
virtual const SkGlyphPositionRoundingSpec& roundingSpec() const = 0;
// Used with SkScopedStrikeForGPU to take action at the end of a scope.

View File

@ -510,6 +510,142 @@ private:
PathOpSubmitter fPathDrawing;
};
// -- DrawableOpSubmitter --------------------------------------------------------------------------
// Shared code for submitting GPU ops for drawing glyphs as drawables.
class DrawableOpSubmitter {
struct DrawableAndPosition;
public:
DrawableOpSubmitter(bool isAntiAliased,
SkScalar strikeToSourceScale,
SkSpan<DrawableAndPosition> drawables,
std::unique_ptr<DrawableAndPosition[],
GrSubRunAllocator::ArrayDestroyer> drawableData);
DrawableOpSubmitter(DrawableOpSubmitter&& that);
static DrawableOpSubmitter Make(const SkZip<SkGlyphVariant, SkPoint>& accepted,
bool isAntiAliased,
SkScalar strikeToSourceScale,
GrSubRunAllocator* alloc);
void submitOps(SkCanvas*,
const GrClip* clip,
const SkMatrixProvider& viewMatrix,
SkPoint drawOrigin,
const SkPaint& paint,
skgpu::v1::SurfaceDrawContext* sdc) const;
private:
struct DrawableAndPosition {
sk_sp<SkDrawable> fDrawable;
SkPoint fPosition;
};
const bool fIsAntiAliased;
const SkScalar fStrikeToSourceScale;
const SkSpan<const DrawableAndPosition> fDrawables;
std::unique_ptr<DrawableAndPosition[], GrSubRunAllocator::ArrayDestroyer> fDrawableData;
};
DrawableOpSubmitter::DrawableOpSubmitter(
bool isAntiAliased,
SkScalar strikeToSourceScale,
SkSpan<DrawableAndPosition> drawables,
std::unique_ptr<DrawableAndPosition[], GrSubRunAllocator::ArrayDestroyer> drawableData)
: fIsAntiAliased{isAntiAliased}
, fStrikeToSourceScale{strikeToSourceScale}
, fDrawables{drawables}
, fDrawableData{std::move(drawableData)} {
SkASSERT(!fDrawables.empty());
}
DrawableOpSubmitter::DrawableOpSubmitter(DrawableOpSubmitter&& that)
: fIsAntiAliased{that.fIsAntiAliased}
, fStrikeToSourceScale{that.fStrikeToSourceScale}
, fDrawables{that.fDrawables}
, fDrawableData{std::move(that.fDrawableData)} {}
DrawableOpSubmitter DrawableOpSubmitter::Make(const SkZip<SkGlyphVariant, SkPoint>& accepted,
bool isAntiAliased,
SkScalar strikeToSourceScale,
GrSubRunAllocator* alloc) {
auto drawableData = alloc->makeUniqueArray<DrawableAndPosition>(
accepted.size(),
[&](int i){
auto [variant, pos] = accepted[i];
return DrawableAndPosition{sk_ref_sp(variant.drawable()), pos};
});
SkSpan<DrawableAndPosition> drawables{drawableData.get(), accepted.size()};
return DrawableOpSubmitter{isAntiAliased, strikeToSourceScale,
drawables, std::move(drawableData)};
}
void DrawableOpSubmitter::submitOps(SkCanvas* canvas,
const GrClip* clip,
const SkMatrixProvider& viewMatrix,
SkPoint drawOrigin,
const SkPaint& paint,
skgpu::v1::SurfaceDrawContext* sdc) const {
// Calculate the matrix that maps the path glyphs from their size in the strike to
// the graphics source space.
SkMatrix strikeToSource = SkMatrix::Scale(fStrikeToSourceScale, fStrikeToSourceScale);
strikeToSource.postTranslate(drawOrigin.x(), drawOrigin.y());
// Transform the path to device because the deviceMatrix must be unchanged to
// draw effect, filter or shader paths.
for (const auto& pathPos : fDrawables) {
const sk_sp<SkDrawable>& drawable = pathPos.fDrawable;
const SkPoint pos = pathPos.fPosition;
// Transform the glyph to source space.
SkMatrix pathMatrix = strikeToSource;
pathMatrix.postTranslate(pos.x(), pos.y());
SkAutoCanvasRestore acr(canvas, false);
SkRect drawableBounds = drawable->getBounds();
pathMatrix.mapRect(&drawableBounds);
canvas->saveLayer(&drawableBounds, &paint);
drawable->draw(canvas, &pathMatrix);
}
}
template <typename SubRun>
GrSubRunOwner make_drawable_sub_run(const SkZip<SkGlyphVariant, SkPoint>& drawables,
bool isAntiAliased,
SkScalar strikeToSourceScale,
GrSubRunAllocator* alloc) {
return alloc->makeUnique<SubRun>(
DrawableOpSubmitter::Make(drawables, isAntiAliased, strikeToSourceScale, alloc));
}
// -- DrawableSubRunSlug ---------------------------------------------------------------------------
class DrawableSubRunSlug : public GrSubRun {
public:
DrawableSubRunSlug(DrawableOpSubmitter&& drawingDrawing) : fDrawingDrawing(std::move(drawingDrawing)) {}
void draw(SkCanvas* canvas,
const GrClip* clip,
const SkMatrixProvider& viewMatrix,
SkPoint drawOrigin,
const SkPaint& paint,
skgpu::v1::SurfaceDrawContext* sdc) const override {
fDrawingDrawing.submitOps(canvas, clip, viewMatrix, drawOrigin, paint, sdc);
}
private:
DrawableOpSubmitter fDrawingDrawing;
};
// -- DrawableSubRun -------------------------------------------------------------------------------
class DrawableSubRun final : public DrawableSubRunSlug, public GrBlobSubRun {
public:
using DrawableSubRunSlug::DrawableSubRunSlug;
const GrBlobSubRun* blobCast() const override { return this; }
bool canReuse(const SkPaint& paint, const SkMatrix& positionMatrix) const override {
return true;
}
const GrAtlasSubRun* testingOnly_atlasSubRun() const override { return nullptr; }
};
// -- GlyphVector ----------------------------------------------------------------------------------
// GlyphVector provides a way to delay the lookup of GrGlyphs until the code is running on the
// GPU in single threaded mode. The GlyphVector is created in a multi-threaded environment, but
@ -1825,6 +1961,13 @@ void GrTextBlob::processSourcePaths(const SkZip<SkGlyphVariant, SkPoint>& accept
accepted, has_some_antialiasing(runFont), strikeToSourceScale, &fAlloc));
}
void GrTextBlob::processSourceDrawables(const SkZip<SkGlyphVariant, SkPoint>& accepted,
const SkFont& runFont,
SkScalar strikeToSourceScale) {
fSubRunList.append(make_drawable_sub_run<DrawableSubRun>(
accepted, has_some_antialiasing(runFont), strikeToSourceScale, &fAlloc));
}
void GrTextBlob::processSourceSDFT(const SkZip<SkGlyphVariant, SkPoint>& accepted,
sk_sp<SkStrike>&& strike,
SkScalar strikeToSourceScale,
@ -2533,6 +2676,18 @@ void GrSubRunNoCachePainter::processSourcePaths(const SkZip<SkGlyphVariant, SkPo
pathDrawing.submitOps(fCanvas, fClip, fViewMatrix, fGlyphRunList.origin(), fPaint, fSDC);
}
void GrSubRunNoCachePainter::processSourceDrawables(const SkZip<SkGlyphVariant, SkPoint>& accepted,
const SkFont& runFont,
SkScalar strikeToSourceScale) {
DrawableOpSubmitter drawableDrawing =
DrawableOpSubmitter::Make(accepted,
has_some_antialiasing(runFont),
strikeToSourceScale,
fAlloc);
drawableDrawing.submitOps(fCanvas, fClip, fViewMatrix, fGlyphRunList.origin(), fPaint, fSDC);
}
void GrSubRunNoCachePainter::processSourceSDFT(const SkZip<SkGlyphVariant, SkPoint>& accepted,
sk_sp<SkStrike>&& strike,
SkScalar strikeToSourceScale,
@ -2590,6 +2745,9 @@ public:
void processSourcePaths(
const SkZip<SkGlyphVariant, SkPoint>& accepted, const SkFont& runFont,
SkScalar strikeToSourceScale) override;
void processSourceDrawables(
const SkZip<SkGlyphVariant, SkPoint>& drawables, const SkFont& runFont,
SkScalar strikeToSourceScale) override;
void processSourceSDFT(
const SkZip<SkGlyphVariant, SkPoint>& accepted, sk_sp<SkStrike>&& strike,
SkScalar strikeToSourceScale, const SkFont& runFont,
@ -3072,6 +3230,13 @@ void Slug::processSourcePaths(const SkZip<SkGlyphVariant,
accepted, has_some_antialiasing(runFont), strikeToSourceScale, &fAlloc));
}
void Slug::processSourceDrawables(const SkZip<SkGlyphVariant, SkPoint>& accepted,
const SkFont& runFont,
SkScalar strikeToSourceScale) {
fSubRuns.append(make_drawable_sub_run<DrawableSubRunSlug>(
accepted, has_some_antialiasing(runFont), strikeToSourceScale, &fAlloc));
}
void Slug::processSourceSDFT(const SkZip<SkGlyphVariant, SkPoint>& accepted,
sk_sp<SkStrike>&& strike,
SkScalar strikeToSourceScale,

View File

@ -249,6 +249,9 @@ private:
void processSourcePaths(const SkZip<SkGlyphVariant, SkPoint>& accepted,
const SkFont& runFont,
SkScalar strikeToSourceScale) override;
void processSourceDrawables(const SkZip<SkGlyphVariant, SkPoint>& accepted,
const SkFont& runFont,
SkScalar strikeToSourceScale) override;
void processSourceSDFT(const SkZip<SkGlyphVariant, SkPoint>& accepted,
sk_sp<SkStrike>&& strike,
SkScalar strikeToSourceScale,
@ -299,6 +302,9 @@ public:
void processSourcePaths(const SkZip<SkGlyphVariant, SkPoint>& accepted,
const SkFont& runFont,
SkScalar strikeToSourceScale) override;
void processSourceDrawables(const SkZip<SkGlyphVariant, SkPoint>& accepted,
const SkFont& runFont,
SkScalar strikeToSourceScale) override;
void processSourceSDFT(const SkZip<SkGlyphVariant, SkPoint>& accepted,
sk_sp<SkStrike>&& strike,
SkScalar strikeToSourceScale,

View File

@ -13,6 +13,7 @@
#include "include/core/SkCanvas.h"
#include "include/core/SkColor.h"
#include "include/core/SkData.h"
#include "include/core/SkDrawable.h"
#include "include/core/SkEncodedImageFormat.h"
#include "include/core/SkFontStyle.h"
#include "include/core/SkImage.h"
@ -255,6 +256,34 @@ protected:
return false;
}
struct SVGGlyphDrawable : public SkDrawable {
SkTestSVGScalerContext* fSelf;
SkGlyph fGlyph;
SVGGlyphDrawable(SkTestSVGScalerContext* self, const SkGlyph& glyph)
: fSelf(self), fGlyph(glyph) {}
SkRect onGetBounds() override { return fGlyph.rect(); }
size_t onApproximateBytesUsed() override { return sizeof(SVGGlyphDrawable); }
void onDraw(SkCanvas* canvas) override {
SkGlyphID glyphID = fGlyph.getGlyphID();
glyphID = glyphID < fSelf->getTestSVGTypeface()->fGlyphCount ? glyphID : 0;
TestSVGTypeface::Glyph& glyphData = fSelf->getTestSVGTypeface()->fGlyphs[glyphID];
SkScalar dx = SkFixedToScalar(fGlyph.getSubXFixed());
SkScalar dy = SkFixedToScalar(fGlyph.getSubYFixed());
canvas->translate(dx, dy);
canvas->concat(fSelf->fMatrix);
canvas->translate(glyphData.fOrigin.fX, -glyphData.fOrigin.fY);
glyphData.render(canvas);
}
};
sk_sp<SkDrawable> generateDrawable(const SkGlyph& glyph) override {
return sk_sp<SVGGlyphDrawable>(new SVGGlyphDrawable(this, glyph));
}
void generateFontMetrics(SkFontMetrics* metrics) override {
this->getTestSVGTypeface()->getFontMetrics(metrics);
SkFontPriv::ScaleFontMetrics(metrics, fMatrix.getScaleY());