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