• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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