xref: /aosp_15_r20/external/skia/tools/gpu/TestCanvas.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
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