blob: b049f1e2aa9f270763035e43c3f57320fb1764ff [file] [log] [blame]
/*
* Copyright 2018 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "include/private/chromium/SkChromeRemoteGlyphCache.h"
#include "include/core/SkCanvas.h"
#include "include/core/SkColorSpace.h"
#include "include/core/SkData.h"
#include "include/core/SkDrawable.h"
#include "include/core/SkFontMetrics.h"
#include "include/core/SkImageInfo.h"
#include "include/core/SkMatrix.h"
#include "include/core/SkPicture.h"
#include "include/core/SkRect.h"
#include "include/core/SkSize.h"
#include "include/core/SkString.h"
#include "include/core/SkSurfaceProps.h"
#include "include/core/SkTypeface.h"
#include "include/private/base/SkAssert.h"
#include "include/private/base/SkDebug.h"
#include "include/private/base/SkPoint_impl.h"
#include "include/private/base/SkTFitsIn.h"
#include "include/private/base/SkTo.h"
#include "include/private/chromium/Slug.h"
#include "src/base/SkArenaAlloc.h"
#include "src/core/SkDescriptor.h"
#include "src/core/SkDevice.h"
#include "src/core/SkFontMetricsPriv.h"
#include "src/core/SkGlyph.h"
#include "src/core/SkReadBuffer.h"
#include "src/core/SkScalerContext.h"
#include "src/core/SkStrike.h"
#include "src/core/SkStrikeCache.h"
#include "src/core/SkStrikeSpec.h"
#include "src/core/SkTHash.h"
#include "src/core/SkTraceEvent.h"
#include "src/core/SkTypeface_remote.h"
#include "src/core/SkWriteBuffer.h"
#include "src/text/GlyphRun.h"
#include "src/text/StrikeForGPU.h"
#include "src/text/gpu/SDFTControl.h"
#include "src/text/gpu/SubRunAllocator.h"
#include "src/text/gpu/SubRunContainer.h"
#include "src/text/gpu/TextBlob.h"
#include <cstring>
#include <memory>
#include <optional>
#include <unordered_map>
#include <utility>
class SkPaint;
using namespace skia_private;
using namespace sktext;
using namespace sktext::gpu;
using namespace skglyph;
namespace {
// -- StrikeSpec -----------------------------------------------------------------------------------
struct StrikeSpec {
StrikeSpec() = default;
StrikeSpec(SkTypefaceID typefaceID, SkDiscardableHandleId discardableHandleId)
: fTypefaceID{typefaceID}, fDiscardableHandleId(discardableHandleId) {}
SkTypefaceID fTypefaceID = 0u;
SkDiscardableHandleId fDiscardableHandleId = 0u;
};
// -- RemoteStrike ----------------------------------------------------------------------------
class RemoteStrike final : public sktext::StrikeForGPU {
public:
// N.B. RemoteStrike is not valid until ensureScalerContext is called.
RemoteStrike(const SkStrikeSpec& strikeSpec,
std::unique_ptr<SkScalerContext> context,
SkDiscardableHandleId discardableHandleId);
~RemoteStrike() override = default;
void lock() override {}
void unlock() override {}
SkGlyphDigest digestFor(skglyph::ActionType, SkPackedGlyphID) override;
bool prepareForImage(SkGlyph* glyph) override {
this->ensureScalerContext();
glyph->setImage(&fAlloc, fContext.get());
return glyph->image() != nullptr;
}
bool prepareForPath(SkGlyph* glyph) override {
this->ensureScalerContext();
glyph->setPath(&fAlloc, fContext.get());
return glyph->path() != nullptr;
}
bool prepareForDrawable(SkGlyph* glyph) override {
this->ensureScalerContext();
glyph->setDrawable(&fAlloc, fContext.get());
return glyph->drawable() != nullptr;
}
void writePendingGlyphs(SkWriteBuffer& buffer);
SkDiscardableHandleId discardableHandleId() const { return fDiscardableHandleId; }
const SkDescriptor& getDescriptor() const override {
return *fDescriptor.getDesc();
}
void setStrikeSpec(const SkStrikeSpec& strikeSpec);
const SkGlyphPositionRoundingSpec& roundingSpec() const override {
return fRoundingSpec;
}
sktext::SkStrikePromise strikePromise() override;
bool hasPendingGlyphs() const {
return !fMasksToSend.empty() || !fPathsToSend.empty() || !fDrawablesToSend.empty();
}
void resetScalerContext();
private:
void ensureScalerContext();
const SkAutoDescriptor fDescriptor;
const SkDiscardableHandleId fDiscardableHandleId;
const SkGlyphPositionRoundingSpec fRoundingSpec;
// The context built using fDescriptor
std::unique_ptr<SkScalerContext> fContext;
// fStrikeSpec is set every time getOrCreateCache is called. This allows the code to maintain
// the fContext as lazy as possible.
const SkStrikeSpec* fStrikeSpec;
// Have the metrics been sent for this strike. Only send them once.
bool fHaveSentFontMetrics{false};
// The masks and paths that currently reside in the GPU process.
THashTable<SkGlyphDigest, SkPackedGlyphID, SkGlyphDigest> fSentGlyphs;
// 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 and drawables, Cleared after diffs are serialized.
SkArenaAllocWithReset fAlloc{256};
};
RemoteStrike::RemoteStrike(
const SkStrikeSpec& strikeSpec,
std::unique_ptr<SkScalerContext> context,
uint32_t discardableHandleId)
: fDescriptor{strikeSpec.descriptor()}
, fDiscardableHandleId(discardableHandleId)
, fRoundingSpec{context->isSubpixel(), context->computeAxisAlignmentForHText()}
// N.B. context must come last because it is used above.
, fContext{std::move(context)} {
SkASSERT(fDescriptor.getDesc() != nullptr);
SkASSERT(fContext != nullptr);
}
void RemoteStrike::writePendingGlyphs(SkWriteBuffer& buffer) {
SkASSERT(this->hasPendingGlyphs());
buffer.writeUInt(fContext->getTypeface()->uniqueID());
buffer.writeUInt(fDiscardableHandleId);
fDescriptor.getDesc()->flatten(buffer);
buffer.writeBool(fHaveSentFontMetrics);
if (!fHaveSentFontMetrics) {
// Write FontMetrics if not sent before.
SkFontMetrics fontMetrics;
fContext->getFontMetrics(&fontMetrics);
SkFontMetricsPriv::Flatten(buffer, fontMetrics);
fHaveSentFontMetrics = true;
}
// Make sure to install all the mask data into the glyphs before sending.
for (SkGlyph& glyph: fMasksToSend) {
this->prepareForImage(&glyph);
}
// Make sure to install all the path data into the glyphs before sending.
for (SkGlyph& glyph: fPathsToSend) {
this->prepareForPath(&glyph);
}
// Make sure to install all the drawable data into the glyphs before sending.
for (SkGlyph& glyph: fDrawablesToSend) {
this->prepareForDrawable(&glyph);
}
// Send all the pending glyph information.
SkStrike::FlattenGlyphsByType(buffer, fMasksToSend, fPathsToSend, fDrawablesToSend);
// Reset all the sending data.
fMasksToSend.clear();
fPathsToSend.clear();
fDrawablesToSend.clear();
fAlloc.reset();
}
void RemoteStrike::ensureScalerContext() {
if (fContext == nullptr) {
fContext = fStrikeSpec->createScalerContext();
}
}
void RemoteStrike::resetScalerContext() {
fContext = nullptr;
fStrikeSpec = nullptr;
}
void RemoteStrike::setStrikeSpec(const SkStrikeSpec& strikeSpec) {
fStrikeSpec = &strikeSpec;
}
SkGlyphDigest RemoteStrike::digestFor(ActionType actionType, SkPackedGlyphID packedGlyphID) {
SkGlyphDigest* digestPtr = fSentGlyphs.find(packedGlyphID);
if (digestPtr != nullptr && digestPtr->actionFor(actionType) != GlyphAction::kUnset) {
return *digestPtr;
}
SkGlyph* glyph;
this->ensureScalerContext();
switch (actionType) {
case skglyph::kPath: {
fPathsToSend.emplace_back(fContext->makeGlyph(packedGlyphID, &fAlloc));
glyph = &fPathsToSend.back();
break;
}
case skglyph::kDrawable: {
fDrawablesToSend.emplace_back(fContext->makeGlyph(packedGlyphID, &fAlloc));
glyph = &fDrawablesToSend.back();
break;
}
default: {
fMasksToSend.emplace_back(fContext->makeGlyph(packedGlyphID, &fAlloc));
glyph = &fMasksToSend.back();
break;
}
}
if (digestPtr == nullptr) {
digestPtr = fSentGlyphs.set(SkGlyphDigest{0, *glyph});
}
digestPtr->setActionFor(actionType, glyph, this);
return *digestPtr;
}
sktext::SkStrikePromise RemoteStrike::strikePromise() {
return sktext::SkStrikePromise{*this->fStrikeSpec};
}
} // namespace
// -- SkStrikeServerImpl ---------------------------------------------------------------------------
class SkStrikeServerImpl final : public sktext::StrikeForGPUCacheInterface {
public:
explicit SkStrikeServerImpl(
SkStrikeServer::DiscardableHandleManager* discardableHandleManager);
// SkStrikeServer API methods
void writeStrikeData(std::vector<uint8_t>* memory);
sk_sp<sktext::StrikeForGPU> findOrCreateScopedStrike(const SkStrikeSpec& strikeSpec) override;
// Methods for testing
void setMaxEntriesInDescriptorMapForTesting(size_t count);
size_t remoteStrikeMapSizeForTesting() const;
private:
inline static constexpr size_t kMaxEntriesInDescriptorMap = 2000u;
void checkForDeletedEntries();
sk_sp<RemoteStrike> getOrCreateCache(const SkStrikeSpec& strikeSpec);
struct MapOps {
size_t operator()(const SkDescriptor* key) const {
return key->getChecksum();
}
bool operator()(const SkDescriptor* lhs, const SkDescriptor* rhs) const {
return *lhs == *rhs;
}
};
using DescToRemoteStrike =
std::unordered_map<const SkDescriptor*, sk_sp<RemoteStrike>, MapOps, MapOps>;
DescToRemoteStrike fDescToRemoteStrike;
SkStrikeServer::DiscardableHandleManager* const fDiscardableHandleManager;
THashSet<SkTypefaceID> fCachedTypefaces;
size_t fMaxEntriesInDescriptorMap = kMaxEntriesInDescriptorMap;
// State cached until the next serialization.
THashSet<RemoteStrike*> fRemoteStrikesToSend;
std::vector<SkTypefaceProxyPrototype> fTypefacesToSend;
};
SkStrikeServerImpl::SkStrikeServerImpl(SkStrikeServer::DiscardableHandleManager* dhm)
: fDiscardableHandleManager(dhm) {
SkASSERT(fDiscardableHandleManager);
}
void SkStrikeServerImpl::setMaxEntriesInDescriptorMapForTesting(size_t count) {
fMaxEntriesInDescriptorMap = count;
}
size_t SkStrikeServerImpl::remoteStrikeMapSizeForTesting() const {
return fDescToRemoteStrike.size();
}
void SkStrikeServerImpl::writeStrikeData(std::vector<uint8_t>* memory) {
// We can use the default SkSerialProcs because we do not currently need to encode any SkImages.
SkBinaryWriteBuffer buffer{nullptr, 0, {}};
// Gather statistics about what needs to be sent.
size_t strikesToSend = 0;
fRemoteStrikesToSend.foreach([&](RemoteStrike* strike) {
if (strike->hasPendingGlyphs()) {
strikesToSend++;
} else {
// This strike has nothing to send, so drop its scaler context to reduce memory.
strike->resetScalerContext();
}
});
// If there are no strikes or typefaces to send, then cleanup and return.
if (strikesToSend == 0 && fTypefacesToSend.empty()) {
fRemoteStrikesToSend.reset();
return;
}
// Send newly seen typefaces.
SkASSERT_RELEASE(SkTFitsIn<int>(fTypefacesToSend.size()));
buffer.writeInt(fTypefacesToSend.size());
for (const auto& typeface: fTypefacesToSend) {
SkTypefaceProxyPrototype proto{typeface};
proto.flatten(buffer);
}
fTypefacesToSend.clear();
buffer.writeInt(strikesToSend);
fRemoteStrikesToSend.foreach(
[&](RemoteStrike* strike) {
if (strike->hasPendingGlyphs()) {
strike->writePendingGlyphs(buffer);
strike->resetScalerContext();
}
}
);
fRemoteStrikesToSend.reset();
// Copy data into the vector.
auto data = buffer.snapshotAsData();
memory->assign(data->bytes(), data->bytes() + data->size());
}
sk_sp<StrikeForGPU> SkStrikeServerImpl::findOrCreateScopedStrike(
const SkStrikeSpec& strikeSpec) {
return this->getOrCreateCache(strikeSpec);
}
void SkStrikeServerImpl::checkForDeletedEntries() {
auto it = fDescToRemoteStrike.begin();
while (fDescToRemoteStrike.size() > fMaxEntriesInDescriptorMap &&
it != fDescToRemoteStrike.end()) {
RemoteStrike* strike = it->second.get();
if (fDiscardableHandleManager->isHandleDeleted(strike->discardableHandleId())) {
// If we are trying to send the strike, then do not erase it.
if (!fRemoteStrikesToSend.contains(strike)) {
// Erase returns the iterator following the removed element.
it = fDescToRemoteStrike.erase(it);
continue;
}
}
++it;
}
}
sk_sp<RemoteStrike> SkStrikeServerImpl::getOrCreateCache(const SkStrikeSpec& strikeSpec) {
// In cases where tracing is turned off, make sure not to get an unused function warning.
// Lambdaize the function.
TRACE_EVENT1("skia", "RecForDesc", "rec",
TRACE_STR_COPY(
[&strikeSpec](){
auto ptr =
strikeSpec.descriptor().findEntry(kRec_SkDescriptorTag, nullptr);
SkScalerContextRec rec;
std::memcpy((void*)&rec, ptr, sizeof(rec));
return rec.dump();
}().c_str()
)
);
if (auto it = fDescToRemoteStrike.find(&strikeSpec.descriptor());
it != fDescToRemoteStrike.end())
{
// We have processed the RemoteStrike before. Reuse it.
sk_sp<RemoteStrike> strike = it->second;
strike->setStrikeSpec(strikeSpec);
if (fRemoteStrikesToSend.contains(strike.get())) {
// Already tracking
return strike;
}
// Strike is in unknown state on GPU. Start tracking strike on GPU by locking it.
bool locked = fDiscardableHandleManager->lockHandle(it->second->discardableHandleId());
if (locked) {
fRemoteStrikesToSend.add(strike.get());
return strike;
}
// If it wasn't locked, then forget this strike, and build it anew below.
fDescToRemoteStrike.erase(it);
}
const SkTypeface& typeface = strikeSpec.typeface();
// Create a new RemoteStrike. Start by processing the typeface.
const SkTypefaceID typefaceId = typeface.uniqueID();
if (!fCachedTypefaces.contains(typefaceId)) {
fCachedTypefaces.add(typefaceId);
fTypefacesToSend.emplace_back(typeface);
}
auto context = strikeSpec.createScalerContext();
auto newHandle = fDiscardableHandleManager->createHandle(); // Locked on creation
auto remoteStrike = sk_make_sp<RemoteStrike>(strikeSpec, std::move(context), newHandle);
remoteStrike->setStrikeSpec(strikeSpec);
fRemoteStrikesToSend.add(remoteStrike.get());
auto d = &remoteStrike->getDescriptor();
fDescToRemoteStrike[d] = remoteStrike;
checkForDeletedEntries();
return remoteStrike;
}
// -- GlyphTrackingDevice --------------------------------------------------------------------------
class GlyphTrackingDevice final : public SkNoPixelsDevice {
public:
GlyphTrackingDevice(
const SkISize& dimensions, const SkSurfaceProps& props, SkStrikeServerImpl* server,
sk_sp<SkColorSpace> colorSpace, sktext::gpu::SDFTControl SDFTControl)
: SkNoPixelsDevice(SkIRect::MakeSize(dimensions), props, std::move(colorSpace))
, fStrikeServerImpl(server)
, fSDFTControl(SDFTControl) {
SkASSERT(fStrikeServerImpl != nullptr);
}
sk_sp<SkDevice> createDevice(const CreateInfo& cinfo, const SkPaint*) override {
const SkSurfaceProps surfaceProps =
this->surfaceProps().cloneWithPixelGeometry(cinfo.fPixelGeometry);
return sk_make_sp<GlyphTrackingDevice>(cinfo.fInfo.dimensions(),
surfaceProps,
fStrikeServerImpl,
cinfo.fInfo.refColorSpace(),
fSDFTControl);
}
SkStrikeDeviceInfo strikeDeviceInfo() const override {
return {this->surfaceProps(), this->scalerContextFlags(), &fSDFTControl};
}
protected:
void onDrawGlyphRunList(SkCanvas*,
const sktext::GlyphRunList& glyphRunList,
const SkPaint& paint) override {
SkMatrix drawMatrix = this->localToDevice();
drawMatrix.preTranslate(glyphRunList.origin().x(), glyphRunList.origin().y());
// Just ignore the resulting SubRunContainer. Since we're passing in a null SubRunAllocator
// no SubRuns will be produced.
STSubRunAllocator<sizeof(SubRunContainer), alignof(SubRunContainer)> tempAlloc;
auto container = SubRunContainer::MakeInAlloc(glyphRunList,
drawMatrix,
paint,
this->strikeDeviceInfo(),
fStrikeServerImpl,
&tempAlloc,
SubRunContainer::kStrikeCalculationsOnly,
"Cache Diff");
// Calculations only. No SubRuns.
SkASSERT(container->isEmpty());
}
sk_sp<sktext::gpu::Slug> convertGlyphRunListToSlug(const sktext::GlyphRunList& glyphRunList,
const SkPaint& paint) override {
// Full matrix for placing glyphs.
SkMatrix positionMatrix = this->localToDevice();
positionMatrix.preTranslate(glyphRunList.origin().x(), glyphRunList.origin().y());
// Use the SkStrikeServer's strike cache to generate the Slug.
return sktext::gpu::MakeSlug(this->localToDevice(),
glyphRunList,
paint,
this->strikeDeviceInfo(),
fStrikeServerImpl);
}
private:
SkStrikeServerImpl* const fStrikeServerImpl;
const sktext::gpu::SDFTControl fSDFTControl;
};
// -- SkStrikeServer -------------------------------------------------------------------------------
SkStrikeServer::SkStrikeServer(DiscardableHandleManager* dhm)
: fImpl(new SkStrikeServerImpl{dhm}) { }
SkStrikeServer::~SkStrikeServer() = default;
std::unique_ptr<SkCanvas> SkStrikeServer::makeAnalysisCanvas(int width, int height,
const SkSurfaceProps& props,
sk_sp<SkColorSpace> colorSpace,
bool DFTSupport,
bool DFTPerspSupport) {
#if !defined(SK_DISABLE_SDF_TEXT)
// These are copied from the defaults in GrContextOptions for historical reasons.
// TODO(herb, jvanverth) pipe in parameters that can be used for both Ganesh and Graphite
// backends instead of just using the defaults.
constexpr float kMinDistanceFieldFontSize = 18.f;
#if defined(SK_BUILD_FOR_ANDROID)
constexpr float kGlyphsAsPathsFontSize = 384.f;
#elif defined(SK_BUILD_FOR_MAC)
constexpr float kGlyphsAsPathsFontSize = 256.f;
#else
constexpr float kGlyphsAsPathsFontSize = 324.f;
#endif
auto control = sktext::gpu::SDFTControl{DFTSupport,
props.isUseDeviceIndependentFonts(),
DFTPerspSupport,
kMinDistanceFieldFontSize,
kGlyphsAsPathsFontSize};
#else
auto control = sktext::gpu::SDFTControl{};
#endif
sk_sp<SkDevice> trackingDevice = sk_make_sp<GlyphTrackingDevice>(
SkISize::Make(width, height),
props, this->impl(),
std::move(colorSpace),
control);
return std::make_unique<SkCanvas>(std::move(trackingDevice));
}
void SkStrikeServer::writeStrikeData(std::vector<uint8_t>* memory) {
fImpl->writeStrikeData(memory);
}
SkStrikeServerImpl* SkStrikeServer::impl() { return fImpl.get(); }
void SkStrikeServer::setMaxEntriesInDescriptorMapForTesting(size_t count) {
fImpl->setMaxEntriesInDescriptorMapForTesting(count);
}
size_t SkStrikeServer::remoteStrikeMapSizeForTesting() const {
return fImpl->remoteStrikeMapSizeForTesting();
}
// -- DiscardableStrikePinner ----------------------------------------------------------------------
class DiscardableStrikePinner : public SkStrikePinner {
public:
DiscardableStrikePinner(SkDiscardableHandleId discardableHandleId,
sk_sp<SkStrikeClient::DiscardableHandleManager> manager)
: fDiscardableHandleId(discardableHandleId), fManager(std::move(manager)) {}
~DiscardableStrikePinner() override = default;
bool canDelete() override { return fManager->deleteHandle(fDiscardableHandleId); }
void assertValid() override { fManager->assertHandleValid(fDiscardableHandleId); }
private:
const SkDiscardableHandleId fDiscardableHandleId;
sk_sp<SkStrikeClient::DiscardableHandleManager> fManager;
};
// -- SkStrikeClientImpl ---------------------------------------------------------------------------
class SkStrikeClientImpl {
public:
explicit SkStrikeClientImpl(sk_sp<SkStrikeClient::DiscardableHandleManager>,
bool isLogging = true,
SkStrikeCache* strikeCache = nullptr);
bool readStrikeData(const volatile void* memory, size_t memorySize);
bool translateTypefaceID(SkAutoDescriptor* descriptor) const;
sk_sp<SkTypeface> retrieveTypefaceUsingServerID(SkTypefaceID) const;
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); }
};
sk_sp<SkTypeface> addTypeface(const SkTypefaceProxyPrototype& typefaceProto);
THashMap<SkTypefaceID, sk_sp<SkTypeface>> fServerTypefaceIdToTypeface;
sk_sp<SkStrikeClient::DiscardableHandleManager> fDiscardableHandleManager;
SkStrikeCache* const fStrikeCache;
const bool fIsLogging;
};
SkStrikeClientImpl::SkStrikeClientImpl(
sk_sp<SkStrikeClient::DiscardableHandleManager>
discardableManager,
bool isLogging,
SkStrikeCache* strikeCache)
: fDiscardableHandleManager(std::move(discardableManager)),
fStrikeCache{strikeCache ? strikeCache : SkStrikeCache::GlobalStrikeCache()},
fIsLogging{isLogging} {}
// Change the path count to track the line number of the failing read.
// TODO: change __LINE__ back to glyphPathsCount when bug chromium:1287356 is closed.
#define READ_FAILURE \
{ \
SkDebugf("Bad font data serialization line: %d", __LINE__); \
SkStrikeClient::DiscardableHandleManager::ReadFailureData data = { \
memorySize, deserializer.bytesRead(), typefaceSize, \
strikeCount, glyphImagesCount, __LINE__}; \
fDiscardableHandleManager->notifyReadFailure(data); \
return false; \
}
bool SkStrikeClientImpl::readStrikeData(const volatile void* memory, size_t memorySize) {
SkASSERT(memorySize != 0);
SkASSERT(memory != nullptr);
// We do not need to set any SkDeserialProcs here because SkStrikeServerImpl::writeStrikeData
// did not encode any SkImages.
SkReadBuffer buffer{const_cast<const void*>(memory), memorySize};
// Limit the kinds of effects that appear in a glyph's drawable (crbug.com/1442140):
buffer.setAllowSkSL(false);
int curTypeface = 0,
curStrike = 0;
auto postError = [&](int line) {
SkDebugf("Read Error Posted %s : %d", __FILE__, line);
SkStrikeClient::DiscardableHandleManager::ReadFailureData data{
memorySize,
buffer.offset(),
SkTo<uint64_t>(curTypeface),
SkTo<uint64_t>(curStrike),
SkTo<uint64_t>(0),
SkTo<uint64_t>(0)};
fDiscardableHandleManager->notifyReadFailure(data);
};
// Read the number of typefaces sent.
const int typefaceCount = buffer.readInt();
for (curTypeface = 0; curTypeface < typefaceCount; ++curTypeface) {
auto proto = SkTypefaceProxyPrototype::MakeFromBuffer(buffer);
if (proto) {
this->addTypeface(proto.value());
} else {
postError(__LINE__);
return false;
}
}
// Read the number of strikes sent.
const int stirkeCount = buffer.readInt();
for (curStrike = 0; curStrike < stirkeCount; ++curStrike) {
const SkTypefaceID serverTypefaceID = buffer.readUInt();
if (serverTypefaceID == 0 && !buffer.isValid()) {
postError(__LINE__);
return false;
}
const SkDiscardableHandleId discardableHandleID = buffer.readUInt();
if (discardableHandleID == 0 && !buffer.isValid()) {
postError(__LINE__);
return false;
}
std::optional<SkAutoDescriptor> serverDescriptor = SkAutoDescriptor::MakeFromBuffer(buffer);
if (!buffer.validate(serverDescriptor.has_value())) {
postError(__LINE__);
return false;
}
const bool fontMetricsInitialized = buffer.readBool();
if (!fontMetricsInitialized && !buffer.isValid()) {
postError(__LINE__);
return false;
}
std::optional<SkFontMetrics> fontMetrics;
if (!fontMetricsInitialized) {
fontMetrics = SkFontMetricsPriv::MakeFromBuffer(buffer);
if (!fontMetrics || !buffer.isValid()) {
postError(__LINE__);
return false;
}
}
auto* clientTypeface = fServerTypefaceIdToTypeface.find(serverTypefaceID);
if (clientTypeface == nullptr) {
postError(__LINE__);
return false;
}
if (!this->translateTypefaceID(&serverDescriptor.value())) {
postError(__LINE__);
return false;
}
SkDescriptor* clientDescriptor = serverDescriptor->getDesc();
auto strike = fStrikeCache->findStrike(*clientDescriptor);
if (strike == nullptr) {
// Metrics are only sent the first time. If creating a new strike, then the metrics
// are not initialized.
if (fontMetricsInitialized) {
postError(__LINE__);
return false;
}
SkStrikeSpec strikeSpec{*clientDescriptor, *clientTypeface};
strike = fStrikeCache->createStrike(
strikeSpec, &fontMetrics.value(),
std::make_unique<DiscardableStrikePinner>(
discardableHandleID, fDiscardableHandleManager));
}
// Make sure this strike is pinned on the GPU side.
strike->verifyPinnedStrike();
if (!strike->mergeFromBuffer(buffer)) {
postError(__LINE__);
return false;
}
}
return true;
}
bool SkStrikeClientImpl::translateTypefaceID(SkAutoDescriptor* toChange) const {
SkDescriptor& descriptor = *toChange->getDesc();
// Rewrite the typefaceID in the rec.
{
uint32_t size;
// findEntry returns a const void*, remove the const in order to update in place.
void* ptr = const_cast<void *>(descriptor.findEntry(kRec_SkDescriptorTag, &size));
SkScalerContextRec rec;
if (!ptr || size != sizeof(rec)) { return false; }
std::memcpy((void*)&rec, ptr, size);
// Get the local typeface from remote typefaceID.
auto* tfPtr = fServerTypefaceIdToTypeface.find(rec.fTypefaceID);
// Received a strike for a typeface which doesn't exist.
if (!tfPtr) { return false; }
// Update the typeface id to work with the client side.
rec.fTypefaceID = tfPtr->get()->uniqueID();
std::memcpy(ptr, &rec, size);
}
descriptor.computeChecksum();
return true;
}
sk_sp<SkTypeface> SkStrikeClientImpl::retrieveTypefaceUsingServerID(SkTypefaceID typefaceID) const {
auto* tfPtr = fServerTypefaceIdToTypeface.find(typefaceID);
return tfPtr != nullptr ? *tfPtr : nullptr;
}
sk_sp<SkTypeface> SkStrikeClientImpl::addTypeface(const SkTypefaceProxyPrototype& typefaceProto) {
sk_sp<SkTypeface>* typeface =
fServerTypefaceIdToTypeface.find(typefaceProto.serverTypefaceID());
// We already have the typeface.
if (typeface != nullptr) {
return *typeface;
}
auto newTypeface = sk_make_sp<SkTypefaceProxy>(
typefaceProto, fDiscardableHandleManager, fIsLogging);
fServerTypefaceIdToTypeface.set(typefaceProto.serverTypefaceID(), newTypeface);
return newTypeface;
}
// SkStrikeClient ----------------------------------------------------------------------------------
SkStrikeClient::SkStrikeClient(sk_sp<DiscardableHandleManager> discardableManager,
bool isLogging,
SkStrikeCache* strikeCache)
: fImpl{new SkStrikeClientImpl{std::move(discardableManager), isLogging, strikeCache}} {}
SkStrikeClient::~SkStrikeClient() = default;
bool SkStrikeClient::readStrikeData(const volatile void* memory, size_t memorySize) {
return fImpl->readStrikeData(memory, memorySize);
}
sk_sp<SkTypeface> SkStrikeClient::retrieveTypefaceUsingServerIDForTest(
SkTypefaceID typefaceID) const {
return fImpl->retrieveTypefaceUsingServerID(typefaceID);
}
bool SkStrikeClient::translateTypefaceID(SkAutoDescriptor* descriptor) const {
return fImpl->translateTypefaceID(descriptor);
}
sk_sp<sktext::gpu::Slug> SkStrikeClient::deserializeSlugForTest(const void* data,
size_t size) const {
return sktext::gpu::Slug::Deserialize(data, size, this);
}