/* * 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 "src/core/SkRemoteGlyphCache.h" #include #include #include #include #include #include "src/core/SkDevice.h" #include "src/core/SkDraw.h" #include "src/core/SkGlyphRun.h" #include "src/core/SkRemoteGlyphCacheImpl.h" #include "src/core/SkStrike.h" #include "src/core/SkStrikeCache.h" #include "src/core/SkTLazy.h" #include "src/core/SkTraceEvent.h" #include "src/core/SkTypeface_remote.h" #if SK_SUPPORT_GPU #include "src/gpu/GrDrawOpAtlas.h" #include "src/gpu/text/GrTextContext.h" #endif static SkDescriptor* auto_descriptor_from_desc(const SkDescriptor* source_desc, SkFontID font_id, SkAutoDescriptor* ad) { ad->reset(source_desc->getLength()); auto* desc = ad->getDesc(); desc->init(); // Rec. { uint32_t size; auto ptr = source_desc->findEntry(kRec_SkDescriptorTag, &size); SkScalerContextRec rec; std::memcpy(&rec, ptr, size); rec.fFontID = font_id; desc->addEntry(kRec_SkDescriptorTag, sizeof(rec), &rec); } // Effects. { uint32_t size; auto ptr = source_desc->findEntry(kEffects_SkDescriptorTag, &size); if (ptr) { desc->addEntry(kEffects_SkDescriptorTag, size, ptr); } } desc->computeChecksum(); return desc; } static const SkDescriptor* create_descriptor( const SkPaint& paint, const SkFont& font, const SkMatrix& m, const SkSurfaceProps& props, SkScalerContextFlags flags, SkAutoDescriptor* ad, SkScalerContextEffects* effects) { SkScalerContextRec rec; SkScalerContext::MakeRecAndEffects(font, paint, props, flags, m, &rec, effects); return SkScalerContext::AutoDescriptorGivenRecAndEffects(rec, *effects, ad); } // -- Serializer ---------------------------------------------------------------------------------- size_t pad(size_t size, size_t alignment) { return (size + (alignment - 1)) & ~(alignment - 1); } // Alignment between x86 and x64 differs for some types, in particular // int64_t and doubles have 4 and 8-byte alignment, respectively. // Be consistent even when writing and reading across different architectures. template size_t serialization_alignment() { return sizeof(T) == 8 ? 8 : alignof(T); } class Serializer { public: Serializer(std::vector* buffer) : fBuffer{buffer} { } template T* emplace(Args&&... args) { auto result = allocate(sizeof(T), serialization_alignment()); return new (result) T{std::forward(args)...}; } template void write(const T& data) { T* result = (T*)allocate(sizeof(T), serialization_alignment()); memcpy(result, &data, sizeof(T)); } template T* allocate() { T* result = (T*)allocate(sizeof(T), serialization_alignment()); return result; } void writeDescriptor(const SkDescriptor& desc) { write(desc.getLength()); auto result = allocate(desc.getLength(), alignof(SkDescriptor)); memcpy(result, &desc, desc.getLength()); } void* allocate(size_t size, size_t alignment) { size_t aligned = pad(fBuffer->size(), alignment); fBuffer->resize(aligned + size); return &(*fBuffer)[aligned]; } private: std::vector* fBuffer; }; // -- Deserializer ------------------------------------------------------------------------------- // Note that the Deserializer is reading untrusted data, we need to guard against invalid data. class Deserializer { public: Deserializer(const volatile char* memory, size_t memorySize) : fMemory(memory), fMemorySize(memorySize) {} template bool read(T* val) { auto* result = this->ensureAtLeast(sizeof(T), serialization_alignment()); if (!result) return false; memcpy(val, const_cast(result), sizeof(T)); return true; } bool readDescriptor(SkAutoDescriptor* ad) { uint32_t descLength = 0u; if (!read(&descLength)) return false; if (descLength < sizeof(SkDescriptor)) return false; if (descLength != SkAlign4(descLength)) return false; auto* result = this->ensureAtLeast(descLength, alignof(SkDescriptor)); if (!result) return false; ad->reset(descLength); memcpy(ad->getDesc(), const_cast(result), descLength); if (ad->getDesc()->getLength() > descLength) return false; return ad->getDesc()->isValid(); } const volatile void* read(size_t size, size_t alignment) { return this->ensureAtLeast(size, alignment); } size_t bytesRead() const { return fBytesRead; } private: const volatile char* ensureAtLeast(size_t size, size_t alignment) { size_t padded = pad(fBytesRead, alignment); // Not enough data. if (padded > fMemorySize) return nullptr; if (size > fMemorySize - padded) return nullptr; auto* result = fMemory + padded; fBytesRead = padded + size; return result; } // Note that we read each piece of memory only once to guard against TOCTOU violations. const volatile char* fMemory; size_t fMemorySize; size_t fBytesRead = 0u; }; // Paths use a SkWriter32 which requires 4 byte alignment. static const size_t kPathAlignment = 4u; size_t SkDescriptorMapOperators::operator()(const SkDescriptor* key) const { return key->getChecksum(); } bool SkDescriptorMapOperators::operator()(const SkDescriptor* lhs, const SkDescriptor* rhs) const { return *lhs == *rhs; } struct StrikeSpec { StrikeSpec() {} StrikeSpec(SkFontID typefaceID_, SkDiscardableHandleId discardableHandleId_) : typefaceID{typefaceID_}, discardableHandleId(discardableHandleId_) {} SkFontID typefaceID = 0u; SkDiscardableHandleId discardableHandleId = 0u; /* desc */ /* n X (glyphs ids) */ }; // -- TrackLayerDevice ----------------------------------------------------------------------------- SkTextBlobCacheDiffCanvas::TrackLayerDevice::TrackLayerDevice( const SkIRect& bounds, const SkSurfaceProps& props, SkStrikeServer* server, sk_sp colorSpace, const SkTextBlobCacheDiffCanvas::Settings& settings) : SkNoPixelsDevice(bounds, props, std::move(colorSpace)) , fStrikeServer(server) , fSettings(settings) , fPainter{props, kUnknown_SkColorType, imageInfo().colorSpace(), fStrikeServer} { SkASSERT(fStrikeServer); } SkBaseDevice* SkTextBlobCacheDiffCanvas::TrackLayerDevice::onCreateDevice( const CreateInfo& cinfo, const SkPaint*) { const SkSurfaceProps surfaceProps(this->surfaceProps().flags(), cinfo.fPixelGeometry); return new TrackLayerDevice(this->getGlobalBounds(), surfaceProps, fStrikeServer, cinfo.fInfo.refColorSpace(), fSettings); } void SkTextBlobCacheDiffCanvas::TrackLayerDevice::drawGlyphRunList( const SkGlyphRunList& glyphRunList) { #if SK_SUPPORT_GPU GrTextContext::Options options; options.fMinDistanceFieldFontSize = fSettings.fMinDistanceFieldFontSize; options.fMaxDistanceFieldFontSize = fSettings.fMaxDistanceFieldFontSize; GrTextContext::SanitizeOptions(&options); fPainter.processGlyphRunList(glyphRunList, this->ctm(), this->surfaceProps(), fSettings.fContextSupportsDistanceFieldText, options, nullptr); #endif // SK_SUPPORT_GPU } // -- SkTextBlobCacheDiffCanvas ------------------------------------------------------------------- SkTextBlobCacheDiffCanvas::Settings::Settings() = default; SkTextBlobCacheDiffCanvas::SkTextBlobCacheDiffCanvas( int width, int height, const SkSurfaceProps& props, SkStrikeServer* strikeServer, Settings settings) : SkNoDrawCanvas{sk_make_sp( SkIRect::MakeWH(width, height), props, strikeServer, nullptr, settings)} {} SkTextBlobCacheDiffCanvas::SkTextBlobCacheDiffCanvas(int width, int height, const SkSurfaceProps& props, SkStrikeServer* strikeServer, sk_sp colorSpace, Settings settings) : SkNoDrawCanvas{sk_make_sp(SkIRect::MakeWH(width, height), props, strikeServer, std::move(colorSpace), settings)} {} SkTextBlobCacheDiffCanvas::~SkTextBlobCacheDiffCanvas() = default; SkCanvas::SaveLayerStrategy SkTextBlobCacheDiffCanvas::getSaveLayerStrategy( const SaveLayerRec& rec) { return kFullLayer_SaveLayerStrategy; } bool SkTextBlobCacheDiffCanvas::onDoSaveBehind(const SkRect*) { return false; } void SkTextBlobCacheDiffCanvas::onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y, const SkPaint& paint) { /// SkCanvas::onDrawTextBlob(blob, x, y, paint); } struct WireTypeface { WireTypeface() = default; WireTypeface(SkFontID typeface_id, int glyph_count, SkFontStyle style, bool is_fixed) : typefaceID(typeface_id), glyphCount(glyph_count), style(style), isFixed(is_fixed) {} SkFontID typefaceID; int glyphCount; SkFontStyle style; bool isFixed; }; // SkStrikeServer ----------------------------------------- SkStrikeServer::SkStrikeServer(DiscardableHandleManager* discardableHandleManager) : fDiscardableHandleManager(discardableHandleManager) { SkASSERT(fDiscardableHandleManager); } SkStrikeServer::~SkStrikeServer() = default; sk_sp SkStrikeServer::serializeTypeface(SkTypeface* tf) { auto* data = fSerializedTypefaces.find(SkTypeface::UniqueID(tf)); if (data) { return *data; } WireTypeface wire(SkTypeface::UniqueID(tf), tf->countGlyphs(), tf->fontStyle(), tf->isFixedPitch()); data = fSerializedTypefaces.set(SkTypeface::UniqueID(tf), SkData::MakeWithCopy(&wire, sizeof(wire))); return *data; } void SkStrikeServer::writeStrikeData(std::vector* memory) { if (fLockedDescs.empty() && fTypefacesToSend.empty()) { return; } Serializer serializer(memory); serializer.emplace(fTypefacesToSend.size()); for (const auto& tf : fTypefacesToSend) serializer.write(tf); fTypefacesToSend.clear(); serializer.emplace(fLockedDescs.size()); for (const auto* desc : fLockedDescs) { auto it = fRemoteGlyphStateMap.find(desc); SkASSERT(it != fRemoteGlyphStateMap.end()); it->second->writePendingGlyphs(&serializer); } fLockedDescs.clear(); } SkStrikeServer::SkGlyphCacheState* SkStrikeServer::getOrCreateCache( const SkPaint& paint, const SkFont& font, const SkSurfaceProps& props, const SkMatrix& matrix, SkScalerContextFlags flags, SkScalerContextEffects* effects) { SkAutoDescriptor descStorage; auto desc = create_descriptor(paint, font, matrix, props, flags, &descStorage, effects); return this->getOrCreateCache(*desc, *font.getTypefaceOrDefault(), *effects); } SkScopedStrike SkStrikeServer::findOrCreateScopedStrike(const SkDescriptor& desc, const SkScalerContextEffects& effects, const SkTypeface& typeface) { return SkScopedStrike{this->getOrCreateCache(desc, typeface, effects)}; } void SkStrikeServer::checkForDeletedEntries() { auto it = fRemoteGlyphStateMap.begin(); while (fRemoteGlyphStateMap.size() > fMaxEntriesInDescriptorMap && it != fRemoteGlyphStateMap.end()) { if (fDiscardableHandleManager->isHandleDeleted(it->second->discardableHandleId())) { it = fRemoteGlyphStateMap.erase(it); } else { ++it; } } } SkStrikeServer::SkGlyphCacheState* SkStrikeServer::getOrCreateCache( const SkDescriptor& desc, const SkTypeface& typeface, SkScalerContextEffects effects) { // 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( [&desc](){ auto ptr = desc.findEntry(kRec_SkDescriptorTag, nullptr); SkScalerContextRec rec; std::memcpy(&rec, ptr, sizeof(rec)); return rec.dump(); }().c_str() ) ); // Already locked. if (fLockedDescs.find(&desc) != fLockedDescs.end()) { auto it = fRemoteGlyphStateMap.find(&desc); SkASSERT(it != fRemoteGlyphStateMap.end()); SkGlyphCacheState* cache = it->second.get(); cache->setTypefaceAndEffects(&typeface, effects); return cache; } // Try to lock. auto it = fRemoteGlyphStateMap.find(&desc); if (it != fRemoteGlyphStateMap.end()) { SkGlyphCacheState* cache = it->second.get(); bool locked = fDiscardableHandleManager->lockHandle(it->second->discardableHandleId()); if (locked) { fLockedDescs.insert(it->first); cache->setTypefaceAndEffects(&typeface, effects); return cache; } // If the lock failed, the entry was deleted on the client. Remove our // tracking. fRemoteGlyphStateMap.erase(it); } const SkFontID typefaceId = typeface.uniqueID(); if (!fCachedTypefaces.contains(typefaceId)) { fCachedTypefaces.add(typefaceId); fTypefacesToSend.emplace_back(typefaceId, typeface.countGlyphs(), typeface.fontStyle(), typeface.isFixedPitch()); } auto context = typeface.createScalerContext(effects, &desc); // Create a new cache state and insert it into the map. auto newHandle = fDiscardableHandleManager->createHandle(); auto cacheState = skstd::make_unique(desc, std::move(context), newHandle); auto* cacheStatePtr = cacheState.get(); fLockedDescs.insert(&cacheStatePtr->getDescriptor()); fRemoteGlyphStateMap[&cacheStatePtr->getDescriptor()] = std::move(cacheState); checkForDeletedEntries(); cacheStatePtr->setTypefaceAndEffects(&typeface, effects); return cacheStatePtr; } // -- SkGlyphCacheState ---------------------------------------------------------------------------- SkStrikeServer::SkGlyphCacheState::SkGlyphCacheState( const SkDescriptor& descriptor, std::unique_ptr context, uint32_t discardableHandleId) : fDescriptor{descriptor} , fDiscardableHandleId(discardableHandleId) , fIsSubpixel{context->isSubpixel()} , fAxisAlignment{context->computeAxisAlignmentForHText()} // N.B. context must come last because it is used above. , fContext{std::move(context)} { SkASSERT(fDescriptor.getDesc() != nullptr); SkASSERT(fContext != nullptr); } SkStrikeServer::SkGlyphCacheState::~SkGlyphCacheState() = default; void SkStrikeServer::SkGlyphCacheState::addGlyph(SkPackedGlyphID glyph, bool asPath) { auto* cache = asPath ? &fCachedGlyphPaths : &fCachedGlyphImages; auto* pending = asPath ? &fPendingGlyphPaths : &fPendingGlyphImages; // Already cached. if (cache->contains(glyph)) { return; } // A glyph is going to be sent. Make sure we have a scaler context to send it. this->ensureScalerContext(); // Serialize and cache. Also create the scalar context to use when serializing // this glyph. cache->add(glyph); pending->push_back(glyph); } // No need to write fForceBW because it is a flag private to SkScalerContext_DW, which will never // be called on the GPU side. static void writeGlyph(SkGlyph* glyph, Serializer* serializer) { serializer->write(glyph->getPackedID()); serializer->write(glyph->advanceX()); serializer->write(glyph->advanceY()); serializer->write(glyph->width()); serializer->write(glyph->height()); serializer->write(glyph->top()); serializer->write(glyph->left()); serializer->write(glyph->maskFormat()); } void SkStrikeServer::SkGlyphCacheState::writePendingGlyphs(Serializer* serializer) { // TODO(khushalsagar): Write a strike only if it has any pending glyphs. serializer->emplace(this->hasPendingGlyphs()); if (!this->hasPendingGlyphs()) { this->resetScalerContext(); return; } // Write the desc. serializer->emplace(fContext->getTypeface()->uniqueID(), fDiscardableHandleId); serializer->writeDescriptor(*fDescriptor.getDesc()); // Write FontMetrics. // TODO(khushalsagar): Do we need to re-send each time? SkFontMetrics fontMetrics; fContext->getFontMetrics(&fontMetrics); serializer->write(fontMetrics); // Write glyphs images. serializer->emplace(fPendingGlyphImages.size()); for (const auto& glyphID : fPendingGlyphImages) { SkGlyph glyph{glyphID}; fContext->getMetrics(&glyph); SkASSERT(SkMask::IsValidFormat(glyph.fMaskFormat)); writeGlyph(&glyph, serializer); auto imageSize = glyph.imageSize(); if (imageSize == 0u) continue; glyph.fImage = serializer->allocate(imageSize, glyph.formatAlignment()); fContext->getImage(glyph); // TODO: Generating the image can change the mask format, do we need to update it in the // serialized glyph? } fPendingGlyphImages.clear(); // Write glyphs paths. serializer->emplace(fPendingGlyphPaths.size()); for (const auto& glyphID : fPendingGlyphPaths) { SkGlyph glyph{glyphID}; fContext->getMetrics(&glyph); SkASSERT(SkMask::IsValidFormat(glyph.fMaskFormat)); writeGlyph(&glyph, serializer); writeGlyphPath(glyphID, serializer); } fPendingGlyphPaths.clear(); this->resetScalerContext(); } void SkStrikeServer::SkGlyphCacheState::ensureScalerContext() { if (fContext == nullptr) { fContext = fTypeface->createScalerContext(fEffects, fDescriptor.getDesc()); } } void SkStrikeServer::SkGlyphCacheState::resetScalerContext() { fContext.reset(); fTypeface = nullptr; } void SkStrikeServer::SkGlyphCacheState::setTypefaceAndEffects( const SkTypeface* typeface, SkScalerContextEffects effects) { fTypeface = typeface; fEffects = effects; } SkVector SkStrikeServer::SkGlyphCacheState::rounding() const { return SkStrikeCommon::PixelRounding(fIsSubpixel, fAxisAlignment); } void SkStrikeServer::SkGlyphCacheState::writeGlyphPath(const SkPackedGlyphID& glyphID, Serializer* serializer) const { SkPath path; if (!fContext->getPath(glyphID, &path)) { serializer->write(0u); return; } size_t pathSize = path.writeToMemory(nullptr); serializer->write(pathSize); path.writeToMemory(serializer->allocate(pathSize, kPathAlignment)); } // Be sure to read and understand the comment for prepareForDrawing in SkStrikeInterface.h before // working on this code. SkSpan SkStrikeServer::SkGlyphCacheState::prepareForDrawing(const SkPackedGlyphID packedGlyphIDs[], const SkPoint positions[], size_t n, int maxDimension, PreparationDetail detail, SkGlyphPos results[]) { for (size_t i = 0; i < n; i++) { SkPoint glyphPos = positions[i]; // Check the cache for the glyph. SkGlyph* glyphPtr = fGlyphMap.findOrNull(packedGlyphIDs[i]); // Has this glyph ever been seen before? if (glyphPtr == nullptr) { // Never seen before. Make a new glyph. glyphPtr = fAlloc.make(packedGlyphIDs[i]); fGlyphMap.set(glyphPtr); this->ensureScalerContext(); fContext->getMetrics(glyphPtr); if (glyphPtr->maxDimension() <= maxDimension) { // do nothing } else if (!glyphPtr->isColor()) { // The glyph is too big for the atlas, but it is not color, so it is handled with a // path. if (glyphPtr->setPath(&fAlloc, fContext.get())) { // Always send the path data, even if its not available, to make sure empty // paths are not incorrectly assumed to be cache misses. fCachedGlyphPaths.add(glyphPtr->getPackedID()); fPendingGlyphPaths.push_back(glyphPtr->getPackedID()); } } else { // This will be handled by the fallback strike. SkASSERT(glyphPtr->maxDimension() > maxDimension && glyphPtr->isColor()); } // Make sure to send the glyph to the GPU because we always send the image for a glyph. fCachedGlyphImages.add(packedGlyphIDs[i]); fPendingGlyphImages.push_back(packedGlyphIDs[i]); } // Each glyph needs to be added as per the contract for prepareForDrawing. // TODO(herb): check if the empty glyphs need to be added here. They certainly need to be // sent, but do the need to be processed by the painter? results[i] = {i, glyphPtr, glyphPos}; } return SkSpan{results, n}; } // SkStrikeClient ----------------------------------------- class SkStrikeClient::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); } private: const SkDiscardableHandleId fDiscardableHandleId; sk_sp fManager; }; SkStrikeClient::SkStrikeClient(sk_sp discardableManager, bool isLogging, SkStrikeCache* strikeCache) : fDiscardableHandleManager(std::move(discardableManager)) , fStrikeCache{strikeCache ? strikeCache : SkStrikeCache::GlobalStrikeCache()} , fIsLogging{isLogging} {} SkStrikeClient::~SkStrikeClient() = default; #define READ_FAILURE \ { \ SkDebugf("Bad font data serialization line: %d", __LINE__); \ DiscardableHandleManager::ReadFailureData data = { \ memorySize, deserializer.bytesRead(), typefaceSize, \ strikeCount, glyphImagesCount, glyphPathsCount}; \ fDiscardableHandleManager->notifyReadFailure(data); \ return false; \ } // No need to read fForceBW because it is a flag private to SkScalerContext_DW, which will never // be called on the GPU side. bool SkStrikeClient::ReadGlyph(SkTLazy& glyph, Deserializer* deserializer) { SkPackedGlyphID glyphID; if (!deserializer->read(&glyphID)) return false; glyph.init(glyphID); if (!deserializer->read(&glyph->fAdvanceX)) return false; if (!deserializer->read(&glyph->fAdvanceY)) return false; if (!deserializer->read(&glyph->fWidth)) return false; if (!deserializer->read(&glyph->fHeight)) return false; if (!deserializer->read(&glyph->fTop)) return false; if (!deserializer->read(&glyph->fLeft)) return false; if (!deserializer->read(&glyph->fMaskFormat)) return false; if (!SkMask::IsValidFormat(glyph->fMaskFormat)) return false; return true; } bool SkStrikeClient::readStrikeData(const volatile void* memory, size_t memorySize) { SkASSERT(memorySize != 0u); Deserializer deserializer(static_cast(memory), memorySize); uint64_t typefaceSize = 0u; uint64_t strikeCount = 0u; uint64_t glyphImagesCount = 0u; uint64_t glyphPathsCount = 0u; if (!deserializer.read(&typefaceSize)) READ_FAILURE for (size_t i = 0; i < typefaceSize; ++i) { WireTypeface wire; if (!deserializer.read(&wire)) READ_FAILURE // TODO(khushalsagar): The typeface no longer needs a reference to the // SkStrikeClient, since all needed glyphs must have been pushed before // raster. addTypeface(wire); } if (!deserializer.read(&strikeCount)) READ_FAILURE for (size_t i = 0; i < strikeCount; ++i) { bool has_glyphs = false; if (!deserializer.read(&has_glyphs)) READ_FAILURE if (!has_glyphs) continue; StrikeSpec spec; if (!deserializer.read(&spec)) READ_FAILURE SkAutoDescriptor sourceAd; if (!deserializer.readDescriptor(&sourceAd)) READ_FAILURE SkFontMetrics fontMetrics; if (!deserializer.read(&fontMetrics)) READ_FAILURE // Get the local typeface from remote fontID. auto* tfPtr = fRemoteFontIdToTypeface.find(spec.typefaceID); // Received strikes for a typeface which doesn't exist. if (!tfPtr) READ_FAILURE auto* tf = tfPtr->get(); // Replace the ContextRec in the desc from the server to create the client // side descriptor. // TODO: Can we do this in-place and re-compute checksum? Instead of a complete copy. SkAutoDescriptor ad; auto* client_desc = auto_descriptor_from_desc(sourceAd.getDesc(), tf->uniqueID(), &ad); auto strike = fStrikeCache->findStrikeExclusive(*client_desc); if (strike == nullptr) { // Note that we don't need to deserialize the effects since we won't be generating any // glyphs here anyway, and the desc is still correct since it includes the serialized // effects. SkScalerContextEffects effects; auto scaler = SkStrikeCache::CreateScalerContext(*client_desc, effects, *tf); strike = fStrikeCache->createStrikeExclusive( *client_desc, std::move(scaler), &fontMetrics, skstd::make_unique(spec.discardableHandleId, fDiscardableHandleManager)); auto proxyContext = static_cast(strike->getScalerContext()); proxyContext->initCache(strike.get(), fStrikeCache); } if (!deserializer.read(&glyphImagesCount)) READ_FAILURE for (size_t j = 0; j < glyphImagesCount; j++) { SkTLazy glyph; if (!ReadGlyph(glyph, &deserializer)) READ_FAILURE if (!glyph->isEmpty()) { const volatile void* image = deserializer.read(glyph->imageSize(), glyph->formatAlignment()); if (!image) READ_FAILURE glyph->fImage = (void*)image; } strike->mergeGlyphAndImage(glyph->getPackedID(), *glyph); } if (!deserializer.read(&glyphPathsCount)) READ_FAILURE for (size_t j = 0; j < glyphPathsCount; j++) { SkTLazy glyph; if (!ReadGlyph(glyph, &deserializer)) READ_FAILURE SkGlyph* allocatedGlyph = strike->mergeGlyphAndImage(glyph->getPackedID(), *glyph); SkPath* pathPtr = nullptr; SkPath path; uint64_t pathSize = 0u; if (!deserializer.read(&pathSize)) READ_FAILURE if (pathSize > 0) { auto* pathData = deserializer.read(pathSize, kPathAlignment); if (!pathData) READ_FAILURE if (!path.readFromMemory(const_cast(pathData), pathSize)) READ_FAILURE pathPtr = &path; } strike->preparePath(allocatedGlyph, pathPtr); } } return true; } sk_sp SkStrikeClient::deserializeTypeface(const void* buf, size_t len) { WireTypeface wire; if (len != sizeof(wire)) return nullptr; memcpy(&wire, buf, sizeof(wire)); return this->addTypeface(wire); } sk_sp SkStrikeClient::addTypeface(const WireTypeface& wire) { auto* typeface = fRemoteFontIdToTypeface.find(wire.typefaceID); if (typeface) return *typeface; auto newTypeface = sk_make_sp( wire.typefaceID, wire.glyphCount, wire.style, wire.isFixed, fDiscardableHandleManager, fIsLogging); fRemoteFontIdToTypeface.set(wire.typefaceID, newTypeface); return std::move(newTypeface); }