/* * Copyright 2023 Google LLC * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #include "tools/gpu/TestCanvas.h" #include "include/core/SkCanvas.h" #include "include/core/SkColorSpace.h" // IWYU pragma: keep #include "include/core/SkData.h" #include "include/core/SkImageInfo.h" #include "include/core/SkRect.h" #include "include/core/SkTypes.h" #include "include/private/base/SkDebug.h" #include "include/private/chromium/SkChromeRemoteGlyphCache.h" #include "include/private/chromium/Slug.h" #include "src/core/SkCanvasPriv.h" #include "src/core/SkDevice.h" #include "src/text/GlyphRun.h" #include #include #include #include class SkPaint; namespace skiatest { TestCanvas::TestCanvas(SkCanvas* canvas) : SkCanvas(sk_ref_sp(canvas->rootDevice())) {} void TestCanvas::onDrawGlyphRunList( const sktext::GlyphRunList& glyphRunList, const SkPaint& paint) { SkRect bounds = glyphRunList.sourceBoundsWithOrigin(); if (this->internalQuickReject(bounds, paint)) { return; } auto layer = this->aboutToDraw(paint, &bounds); if (layer) { if (glyphRunList.hasRSXForm()) { this->SkCanvas::onDrawGlyphRunList(glyphRunList, layer->paint()); } else { auto slug = this->onConvertGlyphRunListToSlug(glyphRunList, layer->paint()); this->drawSlug(slug.get(), layer->paint()); } } } TestCanvas::TestCanvas(SkCanvas* canvas) : SkCanvas(sk_ref_sp(canvas->rootDevice())) {} void TestCanvas::onDrawGlyphRunList( const sktext::GlyphRunList& glyphRunList, const SkPaint& paint) { SkRect bounds = glyphRunList.sourceBoundsWithOrigin(); if (this->internalQuickReject(bounds, paint)) { return; } auto layer = this->aboutToDraw(paint, &bounds); if (layer) { if (glyphRunList.hasRSXForm()) { this->SkCanvas::onDrawGlyphRunList(glyphRunList, layer->paint()); } else { sk_sp bytes; { auto slug = this->onConvertGlyphRunListToSlug(glyphRunList, layer->paint()); if (slug != nullptr) { bytes = slug->serialize(); } } { if (bytes != nullptr) { auto slug = sktext::gpu::Slug::Deserialize(bytes->data(), bytes->size()); this->drawSlug(slug.get(), layer->paint()); } } } } } // A do nothing handle manager for the remote strike server. class ServerHandleManager : public SkStrikeServer::DiscardableHandleManager { public: SkDiscardableHandleId createHandle() override { return 0; } bool lockHandle(SkDiscardableHandleId id) override { return true; } bool isHandleDeleted(SkDiscardableHandleId id) override { return false; } }; // Lock the strikes into the cache for the length of the test. This handler is tied to the lifetime // of the canvas used to render the entire test. class ClientHandleManager : public SkStrikeClient::DiscardableHandleManager { public: bool deleteHandle(SkDiscardableHandleId id) override { return fIsLocked; } void assertHandleValid(SkDiscardableHandleId id) override { DiscardableHandleManager::assertHandleValid(id); } void notifyCacheMiss(SkStrikeClient::CacheMissType type, int fontSize) override { } void notifyReadFailure(const ReadFailureData& data) override { DiscardableHandleManager::notifyReadFailure(data); } void unlock() { fIsLocked = true; } private: bool fIsLocked{false}; }; TestCanvas::TestCanvas(SkCanvas* canvas) : SkCanvas(sk_ref_sp(canvas->rootDevice())) , fServerHandleManager(new ServerHandleManager{}) , fClientHandleManager(new ClientHandleManager{}) , fStrikeServer(fServerHandleManager.get()) , fStrikeClient(fClientHandleManager) {} // Allow the strikes to be freed from the strike cache after the test has been drawn. TestCanvas::~TestCanvas() { static_cast(fClientHandleManager.get())->unlock(); } void TestCanvas::onDrawGlyphRunList( const sktext::GlyphRunList& glyphRunList, const SkPaint& paint) { SkRect bounds = glyphRunList.sourceBoundsWithOrigin(); if (this->internalQuickReject(bounds, paint)) { return; } auto layer = this->aboutToDraw(paint, &bounds); if (layer) { if (glyphRunList.hasRSXForm()) { this->SkCanvas::onDrawGlyphRunList(glyphRunList, layer->paint()); } else { sk_sp slugBytes; std::vector glyphBytes; { auto analysisCanvas = fStrikeServer.makeAnalysisCanvas( this->topDevice()->width(), this->topDevice()->height(), this->fProps, this->topDevice()->imageInfo().refColorSpace(), // TODO: Where should we get this value from? /*DFTSupport=*/ true); // TODO: Move the analysis canvas processing up to the via to handle a whole // document at a time. This is not the correct way to handle the CTM; it doesn't // work for layers. analysisCanvas->setMatrix(this->getLocalToDevice()); auto slug = analysisCanvas->onConvertGlyphRunListToSlug(glyphRunList, layer->paint()); if (slug != nullptr) { slugBytes = slug->serialize(); } fStrikeServer.writeStrikeData(&glyphBytes); } { if (!glyphBytes.empty()) { fStrikeClient.readStrikeData(glyphBytes.data(), glyphBytes.size()); } if (slugBytes != nullptr) { auto slug = sktext::gpu::Slug::Deserialize( slugBytes->data(), slugBytes->size(), &fStrikeClient); this->drawSlug(slug.get(), layer->paint()); } } } } } } // namespace skiatest