• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
makeShader(SkTileMode tmx,SkTileMode tmy,SkFilterMode filter,const SkMatrix * localMatrix,const SkRect * tile) const38 sk_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 
46 namespace {
47 static unsigned gImageFromPictureKeyNamespaceLabel;
48 
49 struct ImageFromPictureKey : public SkResourceCache::Key {
50 public:
ImageFromPictureKey__anon52f7fc210111::ImageFromPictureKey51     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 
72 private:
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 
82 struct ImageFromPictureRec : public SkResourceCache::Rec {
ImageFromPictureRec__anon52f7fc210111::ImageFromPictureRec83     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 
getKey__anon52f7fc210111::ImageFromPictureRec90     const Key& getKey() const override { return fKey; }
bytesUsed__anon52f7fc210111::ImageFromPictureRec91     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     }
getCategory__anon52f7fc210111::ImageFromPictureRec95     const char* getCategory() const override { return "bitmap-shader"; }
diagnostic_only_getDiscardable__anon52f7fc210111::ImageFromPictureRec96     SkDiscardableMemory* diagnostic_only_getDiscardable() const override { return nullptr; }
97 
Visitor__anon52f7fc210111::ImageFromPictureRec98     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 
SkPictureShader(sk_sp<SkPicture> picture,SkTileMode tmx,SkTileMode tmy,SkFilterMode filter,const SkMatrix * localMatrix,const SkRect * tile)109 SkPictureShader::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 
Make(sk_sp<SkPicture> picture,SkTileMode tmx,SkTileMode tmy,SkFilterMode filter,const SkMatrix * lm,const SkRect * tile)118 sk_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 
CreateProc(SkReadBuffer & buffer)126 sk_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 
flatten(SkWriteBuffer & buffer) const156 void 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 
ref_or_srgb(SkColorSpace * cs)165 static sk_sp<SkColorSpace> ref_or_srgb(SkColorSpace* cs) {
166     return cs ? sk_ref_sp(cs) : SkColorSpace::MakeSRGB();
167 }
168 
169 struct CachedImageInfo {
170     bool        success;
171     SkSize      tileScale;
172     SkMatrix    matrixForDraw;
173     SkImageInfo imageInfo;
174 
MakeCachedImageInfo175     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 
makeImageCachedImageInfo239     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.
rasterShader(const SkMatrix & viewMatrix,SkTCopyOnFirstWrite<SkMatrix> * localMatrix,SkColorType dstColorType,SkColorSpace * dstColorSpace) const252 sk_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 
onAppendStages(const SkStageRec & rec) const280 bool 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 
onProgram(skvm::Builder * p,skvm::Coord device,skvm::Coord local,skvm::Color paint,const SkMatrixProvider & matrices,const SkMatrix * localM,const SkColorInfo & dst,skvm::Uniforms * uniforms,SkArenaAlloc * alloc) const296 skvm::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
onMakeContext(const ContextRec & rec,SkArenaAlloc * alloc) const319 SkShaderBase::Context* SkPictureShader::onMakeContext(const ContextRec& rec, SkArenaAlloc* alloc)
320 const {
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 
PictureShaderContext(const SkPictureShader & shader,const ContextRec & rec,sk_sp<SkShader> bitmapShader,SkArenaAlloc * alloc)342 SkPictureShader::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 
getFlags() const352 uint32_t SkPictureShader::PictureShaderContext::getFlags() const {
353     SkASSERT(fBitmapShaderContext);
354     return fBitmapShaderContext->getFlags();
355 }
356 
shadeSpan(int x,int y,SkPMColor dstC[],int count)357 void 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 
asFragmentProcessor(const GrFPArgs & args) const366 std::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     return GrTextureEffect::Make(std::move(view), kPremul_SkAlphaType, inv, sampler,
429                                  *ctx->priv().caps());
430 }
431 #endif
432