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/SkBitmap.h"
11#include "include/core/SkCanvas.h"
12#include "include/core/SkImage.h"
13#include "include/private/SkImageInfoPriv.h"
14#include "src/core/SkArenaAlloc.h"
15#include "src/core/SkImagePriv.h"
16#include "src/core/SkMatrixProvider.h"
17#include "src/core/SkMatrixUtils.h"
18#include "src/core/SkPicturePriv.h"
19#include "src/core/SkReadBuffer.h"
20#include "src/core/SkResourceCache.h"
21#include "src/core/SkVM.h"
22#include "src/shaders/SkBitmapProcShader.h"
23#include "src/shaders/SkImageShader.h"
24#include <atomic>
25
26#if SK_SUPPORT_GPU
27#include "include/gpu/GrDirectContext.h"
28#include "include/gpu/GrRecordingContext.h"
29#include "src/gpu/GrCaps.h"
30#include "src/gpu/GrColorInfo.h"
31#include "src/gpu/GrFragmentProcessor.h"
32#include "src/gpu/GrRecordingContextPriv.h"
33#include "src/gpu/SkGr.h"
34#include "src/gpu/effects/GrTextureEffect.h"
35#include "src/image/SkImage_Base.h"
36#endif
37
38sk_sp<SkShader> SkPicture::makeShader(SkTileMode tmx, SkTileMode tmy, SkFilterMode filter,
39                                      const SkMatrix* localMatrix, const SkRect* tile) const {
40    if (localMatrix && !localMatrix->invert(nullptr)) {
41        return nullptr;
42    }
43    return SkPictureShader::Make(sk_ref_sp(this), tmx, tmy, filter, localMatrix, tile);
44}
45
46namespace {
47static unsigned gImageFromPictureKeyNamespaceLabel;
48
49struct ImageFromPictureKey : public SkResourceCache::Key {
50public:
51    ImageFromPictureKey(SkColorSpace* colorSpace, SkColorType colorType,
52                        uint32_t pictureID, const SkRect& subset,
53                        SkSize scale)
54        : fColorSpaceXYZHash(colorSpace->toXYZD50Hash())
55        , fColorSpaceTransferFnHash(colorSpace->transferFnHash())
56        , fColorType(static_cast<uint32_t>(colorType))
57        , fSubset(subset)
58        , fScale(scale)
59    {
60        static const size_t keySize = sizeof(fColorSpaceXYZHash) +
61                                      sizeof(fColorSpaceTransferFnHash) +
62                                      sizeof(fColorType) +
63                                      sizeof(fSubset) +
64                                      sizeof(fScale);
65        // This better be packed.
66        SkASSERT(sizeof(uint32_t) * (&fEndOfStruct - &fColorSpaceXYZHash) == keySize);
67        this->init(&gImageFromPictureKeyNamespaceLabel,
68                   SkPicturePriv::MakeSharedID(pictureID),
69                   keySize);
70    }
71
72private:
73    uint32_t    fColorSpaceXYZHash;
74    uint32_t    fColorSpaceTransferFnHash;
75    uint32_t    fColorType;
76    SkRect      fSubset;
77    SkSize      fScale;
78
79    SkDEBUGCODE(uint32_t fEndOfStruct;)
80};
81
82struct ImageFromPictureRec : public SkResourceCache::Rec {
83    ImageFromPictureRec(const ImageFromPictureKey& key, sk_sp<SkImage> image)
84        : fKey(key)
85        , fImage(std::move(image)) {}
86
87    ImageFromPictureKey fKey;
88    sk_sp<SkImage>  fImage;
89
90    const Key& getKey() const override { return fKey; }
91    size_t bytesUsed() const override {
92        // Just the record overhead -- the actual pixels are accounted by SkImage_Lazy.
93        return sizeof(fKey) + (size_t)fImage->width() * fImage->height() * 4;
94    }
95    const char* getCategory() const override { return "bitmap-shader"; }
96    SkDiscardableMemory* diagnostic_only_getDiscardable() const override { return nullptr; }
97
98    static bool Visitor(const SkResourceCache::Rec& baseRec, void* contextShader) {
99        const ImageFromPictureRec& rec = static_cast<const ImageFromPictureRec&>(baseRec);
100        sk_sp<SkImage>* result = reinterpret_cast<sk_sp<SkImage>*>(contextShader);
101
102        *result = rec.fImage;
103        return true;
104    }
105};
106
107} // namespace
108
109SkPictureShader::SkPictureShader(sk_sp<SkPicture> picture, SkTileMode tmx, SkTileMode tmy,
110                                 SkFilterMode filter, const SkMatrix* localMatrix, const SkRect* tile)
111    : INHERITED(localMatrix)
112    , fPicture(std::move(picture))
113    , fTile(tile ? *tile : fPicture->cullRect())
114    , fTmx(tmx)
115    , fTmy(tmy)
116    , fFilter(filter) {}
117
118sk_sp<SkShader> SkPictureShader::Make(sk_sp<SkPicture> picture, SkTileMode tmx, SkTileMode tmy,
119                                      SkFilterMode filter, const SkMatrix* lm, const SkRect* tile) {
120    if (!picture || picture->cullRect().isEmpty() || (tile && tile->isEmpty())) {
121        return SkShaders::Empty();
122    }
123    return sk_sp<SkShader>(new SkPictureShader(std::move(picture), tmx, tmy, filter, lm, tile));
124}
125
126sk_sp<SkFlattenable> SkPictureShader::CreateProc(SkReadBuffer& buffer) {
127    SkMatrix lm;
128    buffer.readMatrix(&lm);
129    auto tmx = buffer.read32LE(SkTileMode::kLastTileMode);
130    auto tmy = buffer.read32LE(SkTileMode::kLastTileMode);
131    SkRect tile = buffer.readRect();
132
133    sk_sp<SkPicture> picture;
134
135    SkFilterMode filter = SkFilterMode::kNearest;
136    if (buffer.isVersionLT(SkPicturePriv::kNoFilterQualityShaders_Version)) {
137        if (buffer.isVersionLT(SkPicturePriv::kPictureShaderFilterParam_Version)) {
138            bool didSerialize = buffer.readBool();
139            if (didSerialize) {
140                picture = SkPicturePriv::MakeFromBuffer(buffer);
141            }
142        } else {
143            unsigned legacyFilter = buffer.read32();
144            if (legacyFilter <= (unsigned)SkFilterMode::kLast) {
145                filter = (SkFilterMode)legacyFilter;
146            }
147            picture = SkPicturePriv::MakeFromBuffer(buffer);
148        }
149    } else {
150        filter = buffer.read32LE(SkFilterMode::kLast);
151        picture = SkPicturePriv::MakeFromBuffer(buffer);
152    }
153    return SkPictureShader::Make(picture, tmx, tmy, filter, &lm, &tile);
154}
155
156void SkPictureShader::flatten(SkWriteBuffer& buffer) const {
157    buffer.writeMatrix(this->getLocalMatrix());
158    buffer.write32((unsigned)fTmx);
159    buffer.write32((unsigned)fTmy);
160    buffer.writeRect(fTile);
161    buffer.write32((unsigned)fFilter);
162    SkPicturePriv::Flatten(fPicture, buffer);
163}
164
165static sk_sp<SkColorSpace> ref_or_srgb(SkColorSpace* cs) {
166    return cs ? sk_ref_sp(cs) : SkColorSpace::MakeSRGB();
167}
168
169struct CachedImageInfo {
170    bool        success;
171    SkSize      tileScale;
172    SkMatrix    matrixForDraw;
173    SkImageInfo imageInfo;
174
175    static CachedImageInfo Make(const SkRect& bounds,
176                                const SkMatrix& viewMatrix,
177                                SkTCopyOnFirstWrite<SkMatrix>* localMatrix,     // in/out
178                                SkColorType dstColorType,
179                                SkColorSpace* dstColorSpace,
180                                const int maxTextureSize) {
181        const SkMatrix m = SkMatrix::Concat(viewMatrix, **localMatrix);
182
183        const SkSize scaledSize = [&]() {
184            SkSize size;
185            // Use a rotation-invariant scale
186            if (!m.decomposeScale(&size, nullptr)) {
187                size = {1, 1};
188            }
189            size.fWidth  *= bounds.width();
190            size.fHeight *= bounds.height();
191
192            // Clamp the tile size to about 4M pixels
193            static const SkScalar kMaxTileArea = 2048 * 2048;
194            SkScalar tileArea = size.width() * size.height();
195            if (tileArea > kMaxTileArea) {
196                SkScalar clampScale = SkScalarSqrt(kMaxTileArea / tileArea);
197                size.set(size.width() * clampScale, size.height() * clampScale);
198            }
199
200            // Scale down the tile size if larger than maxTextureSize for GPU Path
201            // or it should fail on create texture
202            if (maxTextureSize) {
203                if (size.width() > maxTextureSize || size.height() > maxTextureSize) {
204                    SkScalar downScale = maxTextureSize / std::max(size.width(),
205                                                                   size.height());
206                    size.set(SkScalarFloorToScalar(size.width() * downScale),
207                             SkScalarFloorToScalar(size.height() * downScale));
208                }
209            }
210            return size;
211        }();
212
213        const SkISize tileSize = scaledSize.toCeil();
214        if (tileSize.isEmpty()) {
215            return {false, {}, {}, {}};
216        }
217
218        const SkSize tileScale = {
219            tileSize.width() / bounds.width(), tileSize.height() / bounds.height()
220        };
221        auto imgCS = ref_or_srgb(dstColorSpace);
222        const SkColorType imgCT = SkColorTypeMaxBitsPerChannel(dstColorType) <= 8
223                                ? kRGBA_8888_SkColorType
224                                : kRGBA_F16Norm_SkColorType;
225
226        if (tileScale.width() != 1 || tileScale.height() != 1) {
227            localMatrix->writable()->preScale(1 / tileScale.width(), 1 / tileScale.height());
228        }
229
230        return {
231            true,
232            tileScale,
233            SkMatrix::RectToRect(bounds, SkRect::MakeIWH(tileSize.width(), tileSize.height())),
234            SkImageInfo::Make(tileSize.width(), tileSize.height(),
235                              imgCT, kPremul_SkAlphaType, imgCS),
236        };
237    }
238
239    sk_sp<SkImage> makeImage(sk_sp<SkSurface> surf, const SkPicture* pict) const {
240        if (!surf) {
241            return nullptr;
242        }
243        auto canvas = surf->getCanvas();
244        canvas->concat(matrixForDraw);
245        canvas->drawPicture(pict);
246        return surf->makeImageSnapshot();
247    }
248};
249
250// Returns a cached image shader, which wraps a single picture tile at the given
251// CTM/local matrix.  Also adjusts the local matrix for tile scaling.
252sk_sp<SkShader> SkPictureShader::rasterShader(const SkMatrix& viewMatrix,
253                                              SkTCopyOnFirstWrite<SkMatrix>* localMatrix,
254                                              SkColorType dstColorType,
255                                              SkColorSpace* dstColorSpace) const {
256    const int maxTextureSize_NotUsedForCPU = 0;
257    CachedImageInfo info = CachedImageInfo::Make(fTile, viewMatrix, localMatrix,
258                                                 dstColorType, dstColorSpace,
259                                                 maxTextureSize_NotUsedForCPU);
260    if (!info.success) {
261        return nullptr;
262    }
263
264    ImageFromPictureKey key(info.imageInfo.colorSpace(), info.imageInfo.colorType(),
265                        fPicture->uniqueID(), fTile, info.tileScale);
266
267    sk_sp<SkImage> image;
268    if (!SkResourceCache::Find(key, ImageFromPictureRec::Visitor, &image)) {
269        image = info.makeImage(SkSurface::MakeRaster(info.imageInfo), fPicture.get());
270        if (!image) {
271            return nullptr;
272        }
273
274        SkResourceCache::Add(new ImageFromPictureRec(key, image));
275        SkPicturePriv::AddedToCache(fPicture.get());
276    }
277    return image->makeShader(fTmx, fTmy, SkSamplingOptions(fFilter), nullptr);
278}
279
280bool SkPictureShader::onAppendStages(const SkStageRec& rec) const {
281    auto lm = this->totalLocalMatrix(rec.fLocalM);
282    // Keep bitmapShader alive by using alloc instead of stack memory
283    auto& bitmapShader = *rec.fAlloc->make<sk_sp<SkShader>>();
284    bitmapShader = this->rasterShader(rec.fMatrixProvider.localToDevice(), &lm,
285                                      rec.fDstColorType, rec.fDstCS);
286    if (!bitmapShader) {
287        return false;
288    }
289
290    SkStageRec localRec = rec;
291    localRec.fLocalM = lm->isIdentity() ? nullptr : lm.get();
292
293    return as_SB(bitmapShader)->appendStages(localRec);
294}
295
296skvm::Color SkPictureShader::onProgram(skvm::Builder* p,
297                                       skvm::Coord device, skvm::Coord local, skvm::Color paint,
298                                       const SkMatrixProvider& matrices, const SkMatrix* localM,
299                                       const SkColorInfo& dst,
300                                       skvm::Uniforms* uniforms, SkArenaAlloc* alloc) const {
301    auto lm = this->totalLocalMatrix(localM);
302
303    // Keep bitmapShader alive by using alloc instead of stack memory
304    auto& bitmapShader = *alloc->make<sk_sp<SkShader>>();
305    bitmapShader = this->rasterShader(matrices.localToDevice(), &lm,
306                                      dst.colorType(), dst.colorSpace());
307    if (!bitmapShader) {
308        return {};
309    }
310
311    return as_SB(bitmapShader)->program(p, device,local, paint,
312                                        matrices,lm, dst,
313                                        uniforms,alloc);
314}
315
316/////////////////////////////////////////////////////////////////////////////////////////
317
318#ifdef SK_ENABLE_LEGACY_SHADERCONTEXT
319SkShaderBase::Context* SkPictureShader::onMakeContext(const ContextRec& rec, SkArenaAlloc* alloc)
320const {
321    auto lm = this->totalLocalMatrix(rec.fLocalMatrix);
322    sk_sp<SkShader> bitmapShader = this->rasterShader(*rec.fMatrix, &lm, rec.fDstColorType,
323                                                      rec.fDstColorSpace);
324    if (!bitmapShader) {
325        return nullptr;
326    }
327
328    ContextRec localRec = rec;
329    localRec.fLocalMatrix = lm->isIdentity() ? nullptr : lm.get();
330
331    PictureShaderContext* ctx =
332        alloc->make<PictureShaderContext>(*this, localRec, std::move(bitmapShader), alloc);
333    if (nullptr == ctx->fBitmapShaderContext) {
334        ctx = nullptr;
335    }
336    return ctx;
337}
338#endif
339
340/////////////////////////////////////////////////////////////////////////////////////////
341
342SkPictureShader::PictureShaderContext::PictureShaderContext(
343        const SkPictureShader& shader, const ContextRec& rec, sk_sp<SkShader> bitmapShader,
344        SkArenaAlloc* alloc)
345    : INHERITED(shader, rec)
346    , fBitmapShader(std::move(bitmapShader))
347{
348    fBitmapShaderContext = as_SB(fBitmapShader)->makeContext(rec, alloc);
349    //if fBitmapShaderContext is null, we are invalid
350}
351
352uint32_t SkPictureShader::PictureShaderContext::getFlags() const {
353    SkASSERT(fBitmapShaderContext);
354    return fBitmapShaderContext->getFlags();
355}
356
357void SkPictureShader::PictureShaderContext::shadeSpan(int x, int y, SkPMColor dstC[], int count) {
358    SkASSERT(fBitmapShaderContext);
359    fBitmapShaderContext->shadeSpan(x, y, dstC, count);
360}
361
362#if SK_SUPPORT_GPU
363
364#include "src/gpu/GrProxyProvider.h"
365
366std::unique_ptr<GrFragmentProcessor> SkPictureShader::asFragmentProcessor(
367        const GrFPArgs& args) const {
368
369    auto ctx = args.fContext;
370    auto lm = this->totalLocalMatrix(args.fPreLocalMatrix);
371    SkColorType dstColorType = GrColorTypeToSkColorType(args.fDstColorInfo->colorType());
372    if (dstColorType == kUnknown_SkColorType) {
373        dstColorType = kRGBA_8888_SkColorType;
374    }
375
376    auto dstCS = ref_or_srgb(args.fDstColorInfo->colorSpace());
377    auto info = CachedImageInfo::Make(fTile, args.fMatrixProvider.localToDevice(), &lm,
378                                      dstColorType, dstCS.get(),
379                                      ctx->priv().caps()->maxTextureSize());
380    SkMatrix inv;
381    if (!info.success || !(*lm).invert(&inv)) {
382        return nullptr;
383    }
384
385    // Gotta be sure the GPU can support our requested colortype (might be FP16)
386    if (!ctx->colorTypeSupportedAsSurface(info.imageInfo.colorType())) {
387        info.imageInfo = info.imageInfo.makeColorType(kRGBA_8888_SkColorType);
388    }
389
390    static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain();
391    GrUniqueKey key;
392    GrUniqueKey::Builder builder(&key, kDomain, 10, "Picture Shader Image");
393    builder[0] = dstCS->toXYZD50Hash();
394    builder[1] = dstCS->transferFnHash();
395    builder[2] = static_cast<uint32_t>(dstColorType);
396    builder[3] = fPicture->uniqueID();
397    memcpy(&builder[4], &fTile, sizeof(fTile));                     // 4,5,6,7
398    memcpy(&builder[8], &info.tileScale, sizeof(info.tileScale));   // 8,9
399    builder.finish();
400
401    GrProxyProvider* provider = ctx->priv().proxyProvider();
402    GrSurfaceProxyView view;
403    if (auto proxy = provider->findOrCreateProxyByUniqueKey(key)) {
404        view = GrSurfaceProxyView(proxy, kTopLeft_GrSurfaceOrigin, GrSwizzle());
405    } else {
406        const int msaaSampleCount = 0;
407        const SkSurfaceProps* props = nullptr;
408        const bool createWithMips = false;
409        auto image = info.makeImage(SkSurface::MakeRenderTarget(ctx,
410                                                                SkBudgeted::kYes,
411                                                                info.imageInfo,
412                                                                msaaSampleCount,
413                                                                kTopLeft_GrSurfaceOrigin,
414                                                                props,
415                                                                createWithMips),
416                                    fPicture.get());
417        if (!image) {
418            return nullptr;
419        }
420        auto [v, ct] = as_IB(image)->asView(ctx, GrMipmapped::kNo);
421        view = std::move(v);
422        provider->assignUniqueKeyToProxy(key, view.asTextureProxy());
423    }
424
425    const GrSamplerState sampler(static_cast<GrSamplerState::WrapMode>(fTmx),
426                                 static_cast<GrSamplerState::WrapMode>(fTmy),
427                                 fFilter);
428
429    return GrTextureEffect::Make(
430            std::move(view), kPremul_SkAlphaType, inv, sampler, *ctx->priv().caps());
431}
432#endif
433