1 /*
2 * Copyright 2023 Google LLC
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8 #include "tools/gpu/TestCanvas.h"
9
10 #include "include/core/SkCanvas.h"
11 #include "include/core/SkColorSpace.h" // IWYU pragma: keep
12 #include "include/core/SkData.h"
13 #include "include/core/SkImageInfo.h"
14 #include "include/core/SkRect.h"
15 #include "include/core/SkTypes.h"
16 #include "include/private/base/SkDebug.h"
17 #include "include/private/chromium/SkChromeRemoteGlyphCache.h"
18 #include "include/private/chromium/Slug.h"
19 #include "src/core/SkCanvasPriv.h"
20 #include "src/core/SkDevice.h"
21 #include "src/text/GlyphRun.h"
22
23 #include <cstdint>
24 #include <memory>
25 #include <optional>
26 #include <vector>
27
28 class SkPaint;
29
30 namespace skiatest {
31
TestCanvas(SkCanvas * canvas)32 TestCanvas<SkSlugTestKey>::TestCanvas(SkCanvas* canvas)
33 : SkCanvas(sk_ref_sp(canvas->rootDevice())) {}
34
onDrawGlyphRunList(const sktext::GlyphRunList & glyphRunList,const SkPaint & paint)35 void TestCanvas<SkSlugTestKey>::onDrawGlyphRunList(
36 const sktext::GlyphRunList& glyphRunList, const SkPaint& paint) {
37 SkRect bounds = glyphRunList.sourceBoundsWithOrigin();
38 if (this->internalQuickReject(bounds, paint)) {
39 return;
40 }
41 auto layer = this->aboutToDraw(paint, &bounds);
42 if (layer) {
43 if (glyphRunList.hasRSXForm()) {
44 this->SkCanvas::onDrawGlyphRunList(glyphRunList, layer->paint());
45 } else {
46 auto slug = this->onConvertGlyphRunListToSlug(glyphRunList, layer->paint());
47 this->drawSlug(slug.get(), layer->paint());
48 }
49 }
50 }
51
TestCanvas(SkCanvas * canvas)52 TestCanvas<SkSerializeSlugTestKey>::TestCanvas(SkCanvas* canvas)
53 : SkCanvas(sk_ref_sp(canvas->rootDevice())) {}
54
onDrawGlyphRunList(const sktext::GlyphRunList & glyphRunList,const SkPaint & paint)55 void TestCanvas<SkSerializeSlugTestKey>::onDrawGlyphRunList(
56 const sktext::GlyphRunList& glyphRunList, const SkPaint& paint) {
57 SkRect bounds = glyphRunList.sourceBoundsWithOrigin();
58 if (this->internalQuickReject(bounds, paint)) {
59 return;
60 }
61 auto layer = this->aboutToDraw(paint, &bounds);
62 if (layer) {
63 if (glyphRunList.hasRSXForm()) {
64 this->SkCanvas::onDrawGlyphRunList(glyphRunList, layer->paint());
65 } else {
66 sk_sp<SkData> bytes;
67 {
68 auto slug = this->onConvertGlyphRunListToSlug(glyphRunList, layer->paint());
69 if (slug != nullptr) {
70 bytes = slug->serialize();
71 }
72 }
73 {
74 if (bytes != nullptr) {
75 auto slug = sktext::gpu::Slug::Deserialize(bytes->data(), bytes->size());
76 this->drawSlug(slug.get(), layer->paint());
77 }
78 }
79 }
80 }
81 }
82
83
84 // A do nothing handle manager for the remote strike server.
85 class ServerHandleManager : public SkStrikeServer::DiscardableHandleManager {
86 public:
createHandle()87 SkDiscardableHandleId createHandle() override {
88 return 0;
89 }
90
lockHandle(SkDiscardableHandleId id)91 bool lockHandle(SkDiscardableHandleId id) override {
92 return true;
93 }
94
isHandleDeleted(SkDiscardableHandleId id)95 bool isHandleDeleted(SkDiscardableHandleId id) override {
96 return false;
97 }
98 };
99
100 // Lock the strikes into the cache for the length of the test. This handler is tied to the lifetime
101 // of the canvas used to render the entire test.
102 class ClientHandleManager : public SkStrikeClient::DiscardableHandleManager {
103 public:
deleteHandle(SkDiscardableHandleId id)104 bool deleteHandle(SkDiscardableHandleId id) override {
105 return fIsLocked;
106 }
107
assertHandleValid(SkDiscardableHandleId id)108 void assertHandleValid(SkDiscardableHandleId id) override {
109 DiscardableHandleManager::assertHandleValid(id);
110 }
111
notifyCacheMiss(SkStrikeClient::CacheMissType type,int fontSize)112 void notifyCacheMiss(SkStrikeClient::CacheMissType type, int fontSize) override {
113
114 }
115
notifyReadFailure(const ReadFailureData & data)116 void notifyReadFailure(const ReadFailureData& data) override {
117 DiscardableHandleManager::notifyReadFailure(data);
118 }
119
unlock()120 void unlock() {
121 fIsLocked = true;
122 }
123
124 private:
125 bool fIsLocked{false};
126 };
127
TestCanvas(SkCanvas * canvas)128 TestCanvas<SkRemoteSlugTestKey>::TestCanvas(SkCanvas* canvas)
129 : SkCanvas(sk_ref_sp(canvas->rootDevice()))
130 , fServerHandleManager(new ServerHandleManager{})
131 , fClientHandleManager(new ClientHandleManager{})
132 , fStrikeServer(fServerHandleManager.get())
133 , fStrikeClient(fClientHandleManager) {}
134
135 // Allow the strikes to be freed from the strike cache after the test has been drawn.
~TestCanvas()136 TestCanvas<SkRemoteSlugTestKey>::~TestCanvas() {
137 static_cast<ClientHandleManager*>(fClientHandleManager.get())->unlock();
138 }
139
onDrawGlyphRunList(const sktext::GlyphRunList & glyphRunList,const SkPaint & paint)140 void TestCanvas<SkRemoteSlugTestKey>::onDrawGlyphRunList(
141 const sktext::GlyphRunList& glyphRunList, const SkPaint& paint) {
142 SkRect bounds = glyphRunList.sourceBoundsWithOrigin();
143 if (this->internalQuickReject(bounds, paint)) {
144 return;
145 }
146 auto layer = this->aboutToDraw(paint, &bounds);
147 if (layer) {
148 if (glyphRunList.hasRSXForm()) {
149 this->SkCanvas::onDrawGlyphRunList(glyphRunList, layer->paint());
150 } else {
151 sk_sp<SkData> slugBytes;
152 std::vector<uint8_t> glyphBytes;
153 {
154 auto analysisCanvas = fStrikeServer.makeAnalysisCanvas(
155 this->topDevice()->width(),
156 this->topDevice()->height(),
157 this->fProps,
158 this->topDevice()->imageInfo().refColorSpace(),
159 // TODO: Where should we get this value from?
160 /*DFTSupport=*/ true);
161
162 // TODO: Move the analysis canvas processing up to the via to handle a whole
163 // document at a time. This is not the correct way to handle the CTM; it doesn't
164 // work for layers.
165 analysisCanvas->setMatrix(this->getLocalToDevice());
166 auto slug = analysisCanvas->onConvertGlyphRunListToSlug(glyphRunList,
167 layer->paint());
168 if (slug != nullptr) {
169 slugBytes = slug->serialize();
170 }
171 fStrikeServer.writeStrikeData(&glyphBytes);
172 }
173 {
174 if (!glyphBytes.empty()) {
175 fStrikeClient.readStrikeData(glyphBytes.data(), glyphBytes.size());
176 }
177 if (slugBytes != nullptr) {
178 auto slug = sktext::gpu::Slug::Deserialize(
179 slugBytes->data(), slugBytes->size(), &fStrikeClient);
180 this->drawSlug(slug.get(), layer->paint());
181 }
182 }
183 }
184 }
185 }
186
187 } // namespace skiatest
188