Coverage Report

Created: 2025-06-24 08:20

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