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