/* * 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/SubRunAllocator.h" #include "src/text/gpu/SubRunContainer.h" #include "src/text/gpu/SubRunControl.h" #include "src/text/gpu/TextBlob.h" #include #include #include #include #include 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 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 fContext; SkTypefaceID fStrikeSpecTypefaceId; // 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 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 fMasksToSend; std::vector fPathsToSend; std::vector 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 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)} , fStrikeSpecTypefaceId(strikeSpec.typeface().uniqueID()) { SkASSERT(fDescriptor.getDesc() != nullptr); SkASSERT(fContext != nullptr); } void RemoteStrike::writePendingGlyphs(SkWriteBuffer& buffer) { SkASSERT(this->hasPendingGlyphs()); // ScalerContext should not hold to the typeface, so we should not use its ID. // We should use StrikeSpec typeface and its ID instead. buffer.writeUInt(fStrikeSpecTypefaceId); 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* memory); sk_sp 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 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, MapOps, MapOps>; DescToRemoteStrike fDescToRemoteStrike; SkStrikeServer::DiscardableHandleManager* const fDiscardableHandleManager; THashSet fCachedTypefaces; size_t fMaxEntriesInDescriptorMap = kMaxEntriesInDescriptorMap; // State cached until the next serialization. THashSet fRemoteStrikesToSend; std::vector 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* 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(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 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 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 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(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 colorSpace, sktext::gpu::SubRunControl SubRunControl) : SkNoPixelsDevice(SkIRect::MakeSize(dimensions), props, std::move(colorSpace)) , fStrikeServerImpl(server) , fSubRunControl(SubRunControl) { SkASSERT(fStrikeServerImpl != nullptr); } sk_sp createDevice(const CreateInfo& cinfo, const SkPaint*) override { const SkSurfaceProps surfaceProps = this->surfaceProps().cloneWithPixelGeometry(cinfo.fPixelGeometry); return sk_make_sp(cinfo.fInfo.dimensions(), surfaceProps, fStrikeServerImpl, cinfo.fInfo.refColorSpace(), fSubRunControl); } SkStrikeDeviceInfo strikeDeviceInfo() const override { return {this->surfaceProps(), this->scalerContextFlags(), &fSubRunControl}; } 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 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 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::SubRunControl fSubRunControl; }; // -- SkStrikeServer ------------------------------------------------------------------------------- SkStrikeServer::SkStrikeServer(DiscardableHandleManager* dhm) : fImpl(new SkStrikeServerImpl{dhm}) { } SkStrikeServer::~SkStrikeServer() = default; std::unique_ptr SkStrikeServer::makeAnalysisCanvas(int width, int height, const SkSurfaceProps& props, sk_sp 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 // There is no need to set forcePathAA for the remote glyph cache as that control impacts // *how* the glyphs are rendered as paths, not *when* they are rendered as paths. auto control = sktext::gpu::SubRunControl{DFTSupport, props.isUseDeviceIndependentFonts(), DFTPerspSupport, kMinDistanceFieldFontSize, kGlyphsAsPathsFontSize}; #else auto control = sktext::gpu::SubRunControl{}; #endif sk_sp trackingDevice = sk_make_sp( SkISize::Make(width, height), props, this->impl(), std::move(colorSpace), control); return std::make_unique(std::move(trackingDevice)); } void SkStrikeServer::writeStrikeData(std::vector* 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 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 fManager; }; // -- SkStrikeClientImpl --------------------------------------------------------------------------- class SkStrikeClientImpl { public: explicit SkStrikeClientImpl(sk_sp, bool isLogging = true, SkStrikeCache* strikeCache = nullptr); bool readStrikeData(const volatile void* memory, size_t memorySize); bool translateTypefaceID(SkAutoDescriptor* descriptor) const; sk_sp retrieveTypefaceUsingServerID(SkTypefaceID) const; private: class PictureBackedGlyphDrawable final : public SkDrawable { public: PictureBackedGlyphDrawable(sk_sp self) : fSelf(std::move(self)) {} private: sk_sp 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 addTypeface(const SkTypefaceProxyPrototype& typefaceProto); THashMap> fServerTypefaceIdToTypeface; sk_sp fDiscardableHandleManager; SkStrikeCache* const fStrikeCache; const bool fIsLogging; }; SkStrikeClientImpl::SkStrikeClientImpl( sk_sp 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(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(curTypeface), SkTo(curStrike), SkTo(0), SkTo(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 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 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( 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(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 SkStrikeClientImpl::retrieveTypefaceUsingServerID(SkTypefaceID typefaceID) const { auto* tfPtr = fServerTypefaceIdToTypeface.find(typefaceID); return tfPtr != nullptr ? *tfPtr : nullptr; } sk_sp SkStrikeClientImpl::addTypeface(const SkTypefaceProxyPrototype& typefaceProto) { sk_sp* typeface = fServerTypefaceIdToTypeface.find(typefaceProto.serverTypefaceID()); // We already have the typeface. if (typeface != nullptr) { return *typeface; } auto newTypeface = sk_make_sp( typefaceProto, fDiscardableHandleManager, fIsLogging); fServerTypefaceIdToTypeface.set(typefaceProto.serverTypefaceID(), newTypeface); return newTypeface; } // SkStrikeClient ---------------------------------------------------------------------------------- SkStrikeClient::SkStrikeClient(sk_sp 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 SkStrikeClient::retrieveTypefaceUsingServerIDForTest( SkTypefaceID typefaceID) const { return fImpl->retrieveTypefaceUsingServerID(typefaceID); } bool SkStrikeClient::translateTypefaceID(SkAutoDescriptor* descriptor) const { return fImpl->translateTypefaceID(descriptor); } sk_sp SkStrikeClient::deserializeSlugForTest(const void* data, size_t size) const { return sktext::gpu::Slug::Deserialize(data, size, this); }