blob: 41c989491688d2dd276156427c46067bb540eeea [file] [log] [blame]
/*
* 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 "src/utils/SkTestCanvas.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 <cstdint>
#include <memory>
#include <optional>
#include <vector>
class SkPaint;
SkTestCanvas<SkSlugTestKey>::SkTestCanvas(SkCanvas* canvas)
: SkCanvas(sk_ref_sp(canvas->rootDevice())) {}
void SkTestCanvas<SkSlugTestKey>::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());
}
}
}
SkTestCanvas<SkSerializeSlugTestKey>::SkTestCanvas(SkCanvas* canvas)
: SkCanvas(sk_ref_sp(canvas->rootDevice())) {}
void SkTestCanvas<SkSerializeSlugTestKey>::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<SkData> 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};
};
SkTestCanvas<SkRemoteSlugTestKey>::SkTestCanvas(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.
SkTestCanvas<SkRemoteSlugTestKey>::~SkTestCanvas() {
static_cast<ClientHandleManager*>(fClientHandleManager.get())->unlock();
}
void SkTestCanvas<SkRemoteSlugTestKey>::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<SkData> slugBytes;
std::vector<uint8_t> 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());
}
}
}
}
}