Coverage Report

Created: 2024-09-14 07:19

/src/skia/src/shaders/SkPictureShader.cpp
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright 2014 Google Inc.
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/shaders/SkPictureShader.h"
9
10
#include "include/core/SkAlphaType.h"
11
#include "include/core/SkCanvas.h"
12
#include "include/core/SkColorSpace.h"
13
#include "include/core/SkColorType.h"
14
#include "include/core/SkImage.h"
15
#include "include/core/SkPoint.h"
16
#include "include/core/SkSamplingOptions.h"
17
#include "include/core/SkScalar.h"
18
#include "include/core/SkShader.h"
19
#include "include/core/SkSurface.h"
20
#include "include/core/SkTileMode.h"
21
#include "include/private/base/SkDebug.h"
22
#include "include/private/base/SkFloatingPoint.h"
23
#include "src/base/SkArenaAlloc.h"
24
#include "src/core/SkEffectPriv.h"
25
#include "src/core/SkImageInfoPriv.h"
26
#include "src/core/SkMatrixPriv.h"
27
#include "src/core/SkPicturePriv.h"
28
#include "src/core/SkReadBuffer.h"
29
#include "src/core/SkResourceCache.h"
30
#include "src/core/SkWriteBuffer.h"
31
#include "src/shaders/SkLocalMatrixShader.h"
32
33
#include <algorithm>
34
#include <cstddef>
35
#include <cstdint>
36
#include <utility>
37
class SkDiscardableMemory;
38
39
sk_sp<SkShader> SkPicture::makeShader(SkTileMode tmx, SkTileMode tmy, SkFilterMode filter,
40
27.6k
                                      const SkMatrix* localMatrix, const SkRect* tile) const {
41
27.6k
    if (localMatrix && !localMatrix->invert(nullptr)) {
42
10.2k
        return nullptr;
43
10.2k
    }
44
17.3k
    return SkPictureShader::Make(sk_ref_sp(this), tmx, tmy, filter, localMatrix, tile);
45
27.6k
}
46
47
namespace {
48
static unsigned gImageFromPictureKeyNamespaceLabel;
49
50
struct ImageFromPictureKey : public SkResourceCache::Key {
51
public:
52
    ImageFromPictureKey(SkColorSpace* colorSpace, SkColorType colorType,
53
                        uint32_t pictureID, const SkRect& subset,
54
                        SkSize scale, const SkSurfaceProps& surfaceProps)
55
        : fColorSpaceXYZHash(colorSpace->toXYZD50Hash())
56
        , fColorSpaceTransferFnHash(colorSpace->transferFnHash())
57
        , fColorType(static_cast<uint32_t>(colorType))
58
        , fSubset(subset)
59
        , fScale(scale)
60
        , fSurfaceProps(surfaceProps)
61
2.06k
    {
62
2.06k
        static const size_t keySize = sizeof(fColorSpaceXYZHash) +
63
2.06k
                                      sizeof(fColorSpaceTransferFnHash) +
64
2.06k
                                      sizeof(fColorType) +
65
2.06k
                                      sizeof(fSubset) +
66
2.06k
                                      sizeof(fScale) +
67
2.06k
                                      sizeof(fSurfaceProps);
68
        // This better be packed.
69
2.06k
        SkASSERT(sizeof(uint32_t) * (&fEndOfStruct - &fColorSpaceXYZHash) == keySize);
70
2.06k
        this->init(&gImageFromPictureKeyNamespaceLabel,
71
2.06k
                   SkPicturePriv::MakeSharedID(pictureID),
72
2.06k
                   keySize);
73
2.06k
    }
74
75
private:
76
    uint32_t       fColorSpaceXYZHash;
77
    uint32_t       fColorSpaceTransferFnHash;
78
    uint32_t       fColorType;
79
    SkRect         fSubset;
80
    SkSize         fScale;
81
    SkSurfaceProps fSurfaceProps;
82
83
    SkDEBUGCODE(uint32_t fEndOfStruct;)
84
};
85
86
struct ImageFromPictureRec : public SkResourceCache::Rec {
87
    ImageFromPictureRec(const ImageFromPictureKey& key, sk_sp<SkImage> image)
88
        : fKey(key)
89
1.13k
        , fImage(std::move(image)) {}
90
91
    ImageFromPictureKey fKey;
92
    sk_sp<SkImage>  fImage;
93
94
6.52k
    const Key& getKey() const override { return fKey; }
95
2.26k
    size_t bytesUsed() const override {
96
        // Just the record overhead -- the actual pixels are accounted by SkImage_Lazy.
97
2.26k
        return sizeof(fKey) + (size_t)fImage->width() * fImage->height() * 4;
98
2.26k
    }
99
0
    const char* getCategory() const override { return "bitmap-shader"; }
100
0
    SkDiscardableMemory* diagnostic_only_getDiscardable() const override { return nullptr; }
101
102
832
    static bool Visitor(const SkResourceCache::Rec& baseRec, void* contextShader) {
103
832
        const ImageFromPictureRec& rec = static_cast<const ImageFromPictureRec&>(baseRec);
104
832
        sk_sp<SkImage>* result = reinterpret_cast<sk_sp<SkImage>*>(contextShader);
105
106
832
        *result = rec.fImage;
107
832
        return true;
108
832
    }
109
};
110
111
} // namespace
112
113
SkPictureShader::SkPictureShader(sk_sp<SkPicture> picture,
114
                                 SkTileMode tmx,
115
                                 SkTileMode tmy,
116
                                 SkFilterMode filter,
117
                                 const SkRect* tile)
118
        : fPicture(std::move(picture))
119
        , fTile(tile ? *tile : fPicture->cullRect())
120
        , fTmx(tmx)
121
        , fTmy(tmy)
122
5.65k
        , fFilter(filter) {}
123
124
sk_sp<SkShader> SkPictureShader::Make(sk_sp<SkPicture> picture, SkTileMode tmx, SkTileMode tmy,
125
17.5k
                                      SkFilterMode filter, const SkMatrix* lm, const SkRect* tile) {
126
17.5k
    if (!picture || picture->cullRect().isEmpty() || (tile && tile->isEmpty())) {
127
11.8k
        return SkShaders::Empty();
128
11.8k
    }
129
5.65k
    return SkLocalMatrixShader::MakeWrapped<SkPictureShader>(lm,
130
5.65k
                                                             std::move(picture),
131
5.65k
                                                             tmx, tmy,
132
5.65k
                                                             filter,
133
5.65k
                                                             tile);
134
17.5k
}
135
136
127
sk_sp<SkFlattenable> SkPictureShader::CreateProc(SkReadBuffer& buffer) {
137
127
    SkMatrix lm;
138
127
    if (buffer.isVersionLT(SkPicturePriv::Version::kNoShaderLocalMatrix)) {
139
10
        buffer.readMatrix(&lm);
140
10
    }
141
127
    auto tmx = buffer.read32LE(SkTileMode::kLastTileMode);
142
127
    auto tmy = buffer.read32LE(SkTileMode::kLastTileMode);
143
127
    SkRect tile = buffer.readRect();
144
145
127
    sk_sp<SkPicture> picture;
146
147
127
    SkFilterMode filter = SkFilterMode::kNearest;
148
127
    if (buffer.isVersionLT(SkPicturePriv::kNoFilterQualityShaders_Version)) {
149
10
        if (buffer.isVersionLT(SkPicturePriv::kPictureShaderFilterParam_Version)) {
150
0
            bool didSerialize = buffer.readBool();
151
0
            if (didSerialize) {
152
0
                picture = SkPicturePriv::MakeFromBuffer(buffer);
153
0
            }
154
10
        } else {
155
10
            unsigned legacyFilter = buffer.read32();
156
10
            if (legacyFilter <= (unsigned)SkFilterMode::kLast) {
157
6
                filter = (SkFilterMode)legacyFilter;
158
6
            }
159
10
            picture = SkPicturePriv::MakeFromBuffer(buffer);
160
10
        }
161
117
    } else {
162
117
        filter = buffer.read32LE(SkFilterMode::kLast);
163
117
        picture = SkPicturePriv::MakeFromBuffer(buffer);
164
117
    }
165
127
    return SkPictureShader::Make(picture, tmx, tmy, filter, &lm, &tile);
166
127
}
167
168
0
void SkPictureShader::flatten(SkWriteBuffer& buffer) const {
169
0
    buffer.write32((unsigned)fTmx);
170
0
    buffer.write32((unsigned)fTmy);
171
0
    buffer.writeRect(fTile);
172
0
    buffer.write32((unsigned)fFilter);
173
0
    SkPicturePriv::Flatten(fPicture, buffer);
174
0
}
175
176
2.26k
static sk_sp<SkColorSpace> ref_or_srgb(SkColorSpace* cs) {
177
2.26k
    return cs ? sk_ref_sp(cs) : SkColorSpace::MakeSRGB();
178
2.26k
}
179
180
SkPictureShader::CachedImageInfo SkPictureShader::CachedImageInfo::Make(
181
        const SkRect& bounds,
182
        const SkMatrix& totalM,
183
        SkColorType dstColorType,
184
        SkColorSpace* dstColorSpace,
185
        const int maxTextureSize,
186
2.79k
        const SkSurfaceProps& propsIn) {
187
2.79k
    SkSurfaceProps props = propsIn.cloneWithPixelGeometry(kUnknown_SkPixelGeometry);
188
189
2.79k
    const SkSize scaledSize = [&]() {
190
2.79k
        SkSize size;
191
        // Use a rotation-invariant scale
192
2.79k
        if (!totalM.decomposeScale(&size, nullptr)) {
193
206
            SkPoint center = {bounds.centerX(), bounds.centerY()};
194
206
            SkScalar area = SkMatrixPriv::DifferentialAreaScale(totalM, center);
195
206
            if (!SkIsFinite(area) || SkScalarNearlyZero(area)) {
196
131
                size = {1, 1};  // ill-conditioned matrix
197
131
            } else {
198
75
                size.fWidth = size.fHeight = SkScalarSqrt(area);
199
75
            }
200
206
        }
201
2.79k
        size.fWidth *= bounds.width();
202
2.79k
        size.fHeight *= bounds.height();
203
204
        // Clamp the tile size to about 4M pixels
205
2.79k
        static const SkScalar kMaxTileArea = 2048 * 2048;
206
2.79k
        SkScalar tileArea = size.width() * size.height();
207
2.79k
        if (tileArea > kMaxTileArea) {
208
475
            SkScalar clampScale = SkScalarSqrt(kMaxTileArea / tileArea);
209
475
            size.set(size.width() * clampScale, size.height() * clampScale);
210
475
        }
211
212
        // Scale down the tile size if larger than maxTextureSize for GPU path
213
        // or it should fail on create texture
214
2.79k
        if (maxTextureSize) {
215
318
            if (size.width() > maxTextureSize || size.height() > maxTextureSize) {
216
111
                SkScalar downScale = maxTextureSize / std::max(size.width(), size.height());
217
111
                size.set(SkScalarFloorToScalar(size.width() * downScale),
218
111
                         SkScalarFloorToScalar(size.height() * downScale));
219
111
            }
220
318
        }
221
2.79k
        return size;
222
2.79k
    }();
223
224
2.79k
    const SkISize tileSize = scaledSize.toCeil();
225
2.79k
    if (tileSize.isEmpty()) {
226
530
        return {false, {}, {}, {}, {}};
227
530
    }
228
229
2.26k
    const SkSize tileScale = {tileSize.width() / bounds.width(),
230
2.26k
                              tileSize.height() / bounds.height()};
231
2.26k
    auto imgCS = ref_or_srgb(dstColorSpace);
232
2.26k
    const SkColorType imgCT = SkColorTypeMaxBitsPerChannel(dstColorType) <= 8
233
2.26k
                                      ? kRGBA_8888_SkColorType
234
2.26k
                                      : kRGBA_F16Norm_SkColorType;
235
236
2.26k
    return {true,
237
2.26k
            tileScale,
238
2.26k
            SkMatrix::RectToRect(bounds, SkRect::MakeIWH(tileSize.width(), tileSize.height())),
239
2.26k
            SkImageInfo::Make(tileSize, imgCT, kPremul_SkAlphaType, imgCS),
240
2.26k
            props};
241
2.79k
}
242
243
sk_sp<SkImage> SkPictureShader::CachedImageInfo::makeImage(sk_sp<SkSurface> surf,
244
1.30k
                                                           const SkPicture* pict) const {
245
1.30k
    if (!surf) {
246
97
        return nullptr;
247
97
    }
248
1.21k
    auto canvas = surf->getCanvas();
249
1.21k
    canvas->concat(matrixForDraw);
250
1.21k
    canvas->drawPicture(pict);
251
1.21k
    return surf->makeImageSnapshot();
252
1.30k
}
253
254
// Returns a cached image shader, which wraps a single picture tile at the given
255
// CTM/local matrix.  Also adjusts the local matrix for tile scaling.
256
sk_sp<SkShader> SkPictureShader::rasterShader(const SkMatrix& totalM,
257
                                              SkColorType dstColorType,
258
                                              SkColorSpace* dstColorSpace,
259
2.48k
                                              const SkSurfaceProps& propsIn) const {
260
2.48k
    const int maxTextureSize_NotUsedForCPU = 0;
261
2.48k
    CachedImageInfo info = CachedImageInfo::Make(fTile,
262
2.48k
                                                 totalM,
263
2.48k
                                                 dstColorType, dstColorSpace,
264
2.48k
                                                 maxTextureSize_NotUsedForCPU,
265
2.48k
                                                 propsIn);
266
2.48k
    if (!info.success) {
267
418
        return nullptr;
268
418
    }
269
270
2.06k
    ImageFromPictureKey key(info.imageInfo.colorSpace(), info.imageInfo.colorType(),
271
2.06k
                            fPicture->uniqueID(), fTile, info.tileScale, info.props);
272
273
2.06k
    sk_sp<SkImage> image;
274
2.06k
    if (!SkResourceCache::Find(key, ImageFromPictureRec::Visitor, &image)) {
275
1.23k
        image = info.makeImage(SkSurfaces::Raster(info.imageInfo, &info.props), fPicture.get());
276
1.23k
        if (!image) {
277
97
            return nullptr;
278
97
        }
279
280
1.13k
        SkResourceCache::Add(new ImageFromPictureRec(key, image));
281
1.13k
        SkPicturePriv::AddedToCache(fPicture.get());
282
1.13k
    }
283
    // Scale the image to the original picture size.
284
1.96k
    auto lm = SkMatrix::Scale(1.f/info.tileScale.width(), 1.f/info.tileScale.height());
285
1.96k
    return image->makeShader(fTmx, fTmy, SkSamplingOptions(fFilter), &lm);
286
2.06k
}
287
288
2.48k
bool SkPictureShader::appendStages(const SkStageRec& rec, const SkShaders::MatrixRec& mRec) const {
289
    // Keep bitmapShader alive by using alloc instead of stack memory
290
2.48k
    auto& bitmapShader = *rec.fAlloc->make<sk_sp<SkShader>>();
291
    // We don't check whether the total local matrix is valid here because we have to assume *some*
292
    // mapping to make an image. It could be wildly wrong if there is a runtime shader transforming
293
    // the coordinates in a manner we don't know about here. However, that is a fundamental problem
294
    // with the technique of converting a picture to an image to implement this shader.
295
2.48k
    bitmapShader = this->rasterShader(mRec.totalMatrix(),
296
2.48k
                                      rec.fDstColorType,
297
2.48k
                                      rec.fDstCS,
298
2.48k
                                      rec.fSurfaceProps);
299
2.48k
    if (!bitmapShader) {
300
515
        return false;
301
515
    }
302
1.96k
    return as_SB(bitmapShader)->appendStages(rec, mRec);
303
2.48k
}
304
305
/////////////////////////////////////////////////////////////////////////////////////////
306
307
#ifdef SK_ENABLE_LEGACY_SHADERCONTEXT
308
SkShaderBase::Context* SkPictureShader::onMakeContext(const ContextRec& rec,
309
0
                                                      SkArenaAlloc* alloc) const {
310
0
    sk_sp<SkShader> bitmapShader = this->rasterShader(
311
0
            rec.fMatrixRec.totalMatrix(), rec.fDstColorType, rec.fDstColorSpace, rec.fProps);
312
0
    if (!bitmapShader) {
313
0
        return nullptr;
314
0
    }
315
316
0
    return as_SB(bitmapShader)->makeContext(rec, alloc);
317
0
}
318
#endif