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 "src/base/SkArenaAlloc.h"
14 #include "src/core/SkImageInfoPriv.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/SkRasterPipeline.h"
21 #include "src/core/SkReadBuffer.h"
22 #include "src/core/SkResourceCache.h"
23 #include "src/core/SkVM.h"
24 #include "src/shaders/SkBitmapProcShader.h"
25 #include "src/shaders/SkImageShader.h"
26 #include "src/shaders/SkLocalMatrixShader.h"
27 
28 #if defined(SK_GANESH)
29 #include "include/gpu/GrDirectContext.h"
30 #include "include/gpu/GrRecordingContext.h"
31 #include "src/gpu/ganesh/GrCaps.h"
32 #include "src/gpu/ganesh/GrColorInfo.h"
33 #include "src/gpu/ganesh/GrFPArgs.h"
34 #include "src/gpu/ganesh/GrFragmentProcessor.h"
35 #include "src/gpu/ganesh/GrRecordingContextPriv.h"
36 #include "src/gpu/ganesh/SkGr.h"
37 #include "src/gpu/ganesh/effects/GrTextureEffect.h"
38 #include "src/image/SkImage_Base.h"
39 #include "src/shaders/SkLocalMatrixShader.h"
40 #endif
41 
42 #if defined(SK_GRAPHITE)
43 #include "src/gpu/graphite/Caps.h"
44 #include "src/gpu/graphite/KeyContext.h"
45 #include "src/gpu/graphite/KeyHelpers.h"
46 #include "src/gpu/graphite/PaintParamsKey.h"
47 #include "src/gpu/graphite/RecorderPriv.h"
48 #endif
49 
makeShader(SkTileMode tmx,SkTileMode tmy,SkFilterMode filter,const SkMatrix * localMatrix,const SkRect * tile) const50 sk_sp<SkShader> SkPicture::makeShader(SkTileMode tmx, SkTileMode tmy, SkFilterMode filter,
51                                       const SkMatrix* localMatrix, const SkRect* tile) const {
52     if (localMatrix && !localMatrix->invert(nullptr)) {
53         return nullptr;
54     }
55     return SkPictureShader::Make(sk_ref_sp(this), tmx, tmy, filter, localMatrix, tile);
56 }
57 
58 namespace {
59 static unsigned gImageFromPictureKeyNamespaceLabel;
60 
61 struct ImageFromPictureKey : public SkResourceCache::Key {
62 public:
ImageFromPictureKey__anondb89c27f0111::ImageFromPictureKey63     ImageFromPictureKey(SkColorSpace* colorSpace, SkColorType colorType,
64                         uint32_t pictureID, const SkRect& subset,
65                         SkSize scale, const SkSurfaceProps& surfaceProps)
66         : fColorSpaceXYZHash(colorSpace->toXYZD50Hash())
67         , fColorSpaceTransferFnHash(colorSpace->transferFnHash())
68         , fColorType(static_cast<uint32_t>(colorType))
69         , fSubset(subset)
70         , fScale(scale)
71         , fSurfaceProps(surfaceProps)
72     {
73         static const size_t keySize = sizeof(fColorSpaceXYZHash) +
74                                       sizeof(fColorSpaceTransferFnHash) +
75                                       sizeof(fColorType) +
76                                       sizeof(fSubset) +
77                                       sizeof(fScale) +
78                                       sizeof(fSurfaceProps);
79         // This better be packed.
80         SkASSERT(sizeof(uint32_t) * (&fEndOfStruct - &fColorSpaceXYZHash) == keySize);
81         this->init(&gImageFromPictureKeyNamespaceLabel,
82                    SkPicturePriv::MakeSharedID(pictureID),
83                    keySize);
84     }
85 
86 private:
87     uint32_t       fColorSpaceXYZHash;
88     uint32_t       fColorSpaceTransferFnHash;
89     uint32_t       fColorType;
90     SkRect         fSubset;
91     SkSize         fScale;
92     SkSurfaceProps fSurfaceProps;
93 
94     SkDEBUGCODE(uint32_t fEndOfStruct;)
95 };
96 
97 struct ImageFromPictureRec : public SkResourceCache::Rec {
ImageFromPictureRec__anondb89c27f0111::ImageFromPictureRec98     ImageFromPictureRec(const ImageFromPictureKey& key, sk_sp<SkImage> image)
99         : fKey(key)
100         , fImage(std::move(image)) {}
101 
102     ImageFromPictureKey fKey;
103     sk_sp<SkImage>  fImage;
104 
getKey__anondb89c27f0111::ImageFromPictureRec105     const Key& getKey() const override { return fKey; }
bytesUsed__anondb89c27f0111::ImageFromPictureRec106     size_t bytesUsed() const override {
107         // Just the record overhead -- the actual pixels are accounted by SkImage_Lazy.
108         return sizeof(fKey) + (size_t)fImage->width() * fImage->height() * 4;
109     }
getCategory__anondb89c27f0111::ImageFromPictureRec110     const char* getCategory() const override { return "bitmap-shader"; }
diagnostic_only_getDiscardable__anondb89c27f0111::ImageFromPictureRec111     SkDiscardableMemory* diagnostic_only_getDiscardable() const override { return nullptr; }
112 
Visitor__anondb89c27f0111::ImageFromPictureRec113     static bool Visitor(const SkResourceCache::Rec& baseRec, void* contextShader) {
114         const ImageFromPictureRec& rec = static_cast<const ImageFromPictureRec&>(baseRec);
115         sk_sp<SkImage>* result = reinterpret_cast<sk_sp<SkImage>*>(contextShader);
116 
117         *result = rec.fImage;
118         return true;
119     }
120 };
121 
122 } // namespace
123 
SkPictureShader(sk_sp<SkPicture> picture,SkTileMode tmx,SkTileMode tmy,SkFilterMode filter,const SkRect * tile)124 SkPictureShader::SkPictureShader(sk_sp<SkPicture> picture,
125                                  SkTileMode tmx,
126                                  SkTileMode tmy,
127                                  SkFilterMode filter,
128                                  const SkRect* tile)
129         : fPicture(std::move(picture))
130         , fTile(tile ? *tile : fPicture->cullRect())
131         , fTmx(tmx)
132         , fTmy(tmy)
133         , fFilter(filter) {}
134 
Make(sk_sp<SkPicture> picture,SkTileMode tmx,SkTileMode tmy,SkFilterMode filter,const SkMatrix * lm,const SkRect * tile)135 sk_sp<SkShader> SkPictureShader::Make(sk_sp<SkPicture> picture, SkTileMode tmx, SkTileMode tmy,
136                                       SkFilterMode filter, const SkMatrix* lm, const SkRect* tile) {
137     if (!picture || picture->cullRect().isEmpty() || (tile && tile->isEmpty())) {
138         return SkShaders::Empty();
139     }
140     return SkLocalMatrixShader::MakeWrapped<SkPictureShader>(lm,
141                                                              std::move(picture),
142                                                              tmx, tmy,
143                                                              filter,
144                                                              tile);
145 }
146 
CreateProc(SkReadBuffer & buffer)147 sk_sp<SkFlattenable> SkPictureShader::CreateProc(SkReadBuffer& buffer) {
148     SkMatrix lm;
149     if (buffer.isVersionLT(SkPicturePriv::Version::kNoShaderLocalMatrix)) {
150         buffer.readMatrix(&lm);
151     }
152     auto tmx = buffer.read32LE(SkTileMode::kLastTileMode);
153     auto tmy = buffer.read32LE(SkTileMode::kLastTileMode);
154     SkRect tile = buffer.readRect();
155 
156     sk_sp<SkPicture> picture;
157 
158     SkFilterMode filter = SkFilterMode::kNearest;
159     if (buffer.isVersionLT(SkPicturePriv::kNoFilterQualityShaders_Version)) {
160         if (buffer.isVersionLT(SkPicturePriv::kPictureShaderFilterParam_Version)) {
161             bool didSerialize = buffer.readBool();
162             if (didSerialize) {
163                 picture = SkPicturePriv::MakeFromBuffer(buffer);
164             }
165         } else {
166             unsigned legacyFilter = buffer.read32();
167             if (legacyFilter <= (unsigned)SkFilterMode::kLast) {
168                 filter = (SkFilterMode)legacyFilter;
169             }
170             picture = SkPicturePriv::MakeFromBuffer(buffer);
171         }
172     } else {
173         filter = buffer.read32LE(SkFilterMode::kLast);
174         picture = SkPicturePriv::MakeFromBuffer(buffer);
175     }
176     return SkPictureShader::Make(picture, tmx, tmy, filter, &lm, &tile);
177 }
178 
flatten(SkWriteBuffer & buffer) const179 void SkPictureShader::flatten(SkWriteBuffer& buffer) const {
180     buffer.write32((unsigned)fTmx);
181     buffer.write32((unsigned)fTmy);
182     buffer.writeRect(fTile);
183     buffer.write32((unsigned)fFilter);
184     SkPicturePriv::Flatten(fPicture, buffer);
185 }
186 
ref_or_srgb(SkColorSpace * cs)187 static sk_sp<SkColorSpace> ref_or_srgb(SkColorSpace* cs) {
188     return cs ? sk_ref_sp(cs) : SkColorSpace::MakeSRGB();
189 }
190 
191 struct CachedImageInfo {
192     bool           success;
193     SkSize         tileScale;      // Additional scale factors to apply when sampling image.
194     SkMatrix       matrixForDraw;  // Matrix used to produce an image from the picture
195     SkImageInfo    imageInfo;
196     SkSurfaceProps props;
197 
MakeCachedImageInfo198     static CachedImageInfo Make(const SkRect& bounds,
199                                 const SkMatrix& totalM,
200                                 SkColorType dstColorType,
201                                 SkColorSpace* dstColorSpace,
202                                 const int maxTextureSize,
203                                 const SkSurfaceProps& propsIn) {
204         SkSurfaceProps props = propsIn.cloneWithPixelGeometry(kUnknown_SkPixelGeometry);
205 
206         const SkSize scaledSize = [&]() {
207             SkSize size;
208             // Use a rotation-invariant scale
209             if (!totalM.decomposeScale(&size, nullptr)) {
210                 SkPoint center = {bounds.centerX(), bounds.centerY()};
211                 SkScalar area = SkMatrixPriv::DifferentialAreaScale(totalM, center);
212                 if (!SkScalarIsFinite(area) || SkScalarNearlyZero(area)) {
213                     size = {1, 1}; // ill-conditioned matrix
214                 } else {
215                     size.fWidth = size.fHeight = SkScalarSqrt(area);
216                 }
217             }
218             size.fWidth  *= bounds.width();
219             size.fHeight *= bounds.height();
220 
221             // Clamp the tile size to about 4M pixels
222             static const SkScalar kMaxTileArea = 2048 * 2048;
223             SkScalar tileArea = size.width() * size.height();
224             if (tileArea > kMaxTileArea) {
225                 SkScalar clampScale = SkScalarSqrt(kMaxTileArea / tileArea);
226                 size.set(size.width() * clampScale, size.height() * clampScale);
227             }
228 
229             // Scale down the tile size if larger than maxTextureSize for GPU path
230             // or it should fail on create texture
231             if (maxTextureSize) {
232                 if (size.width() > maxTextureSize || size.height() > maxTextureSize) {
233                     SkScalar downScale = maxTextureSize / std::max(size.width(),
234                                                                    size.height());
235                     size.set(SkScalarFloorToScalar(size.width() * downScale),
236                              SkScalarFloorToScalar(size.height() * downScale));
237                 }
238             }
239             return size;
240         }();
241 
242         const SkISize tileSize = scaledSize.toCeil();
243         if (tileSize.isEmpty()) {
244             return {false, {}, {}, {}, {}};
245         }
246 
247         const SkSize tileScale = {
248             tileSize.width() / bounds.width(), tileSize.height() / bounds.height()
249         };
250         auto imgCS = ref_or_srgb(dstColorSpace);
251         const SkColorType imgCT = SkColorTypeMaxBitsPerChannel(dstColorType) <= 8
252                                 ? kRGBA_8888_SkColorType
253                                 : kRGBA_F16Norm_SkColorType;
254 
255         return {true,
256                 tileScale,
257                 SkMatrix::RectToRect(bounds, SkRect::MakeIWH(tileSize.width(), tileSize.height())),
258                 SkImageInfo::Make(tileSize, imgCT, kPremul_SkAlphaType, imgCS),
259                 props};
260     }
261 
makeImageCachedImageInfo262     sk_sp<SkImage> makeImage(sk_sp<SkSurface> surf, const SkPicture* pict) const {
263         if (!surf) {
264             return nullptr;
265         }
266         auto canvas = surf->getCanvas();
267         canvas->concat(matrixForDraw);
268         canvas->drawPicture(pict);
269         return surf->makeImageSnapshot();
270     }
271 };
272 
273 // Returns a cached image shader, which wraps a single picture tile at the given
274 // CTM/local matrix.  Also adjusts the local matrix for tile scaling.
rasterShader(const SkMatrix & totalM,SkColorType dstColorType,SkColorSpace * dstColorSpace,const SkSurfaceProps & propsIn) const275 sk_sp<SkShader> SkPictureShader::rasterShader(const SkMatrix& totalM,
276                                               SkColorType dstColorType,
277                                               SkColorSpace* dstColorSpace,
278                                               const SkSurfaceProps& propsIn) const {
279     const int maxTextureSize_NotUsedForCPU = 0;
280     CachedImageInfo info = CachedImageInfo::Make(fTile,
281                                                  totalM,
282                                                  dstColorType, dstColorSpace,
283                                                  maxTextureSize_NotUsedForCPU,
284                                                  propsIn);
285     if (!info.success) {
286         return nullptr;
287     }
288 
289     ImageFromPictureKey key(info.imageInfo.colorSpace(), info.imageInfo.colorType(),
290                             fPicture->uniqueID(), fTile, info.tileScale, info.props);
291 
292     sk_sp<SkImage> image;
293     if (!SkResourceCache::Find(key, ImageFromPictureRec::Visitor, &image)) {
294         image = info.makeImage(SkSurface::MakeRaster(info.imageInfo, &info.props), fPicture.get());
295         if (!image) {
296             return nullptr;
297         }
298 
299         SkResourceCache::Add(new ImageFromPictureRec(key, image));
300         SkPicturePriv::AddedToCache(fPicture.get());
301     }
302     // Scale the image to the original picture size.
303     auto lm = SkMatrix::Scale(1.f/info.tileScale.width(), 1.f/info.tileScale.height());
304     return image->makeShader(fTmx, fTmy, SkSamplingOptions(fFilter), &lm);
305 }
306 
appendStages(const SkStageRec & rec,const MatrixRec & mRec) const307 bool SkPictureShader::appendStages(const SkStageRec& rec, const MatrixRec& mRec) const {
308     // Keep bitmapShader alive by using alloc instead of stack memory
309     auto& bitmapShader = *rec.fAlloc->make<sk_sp<SkShader>>();
310     // We don't check whether the total local matrix is valid here because we have to assume *some*
311     // mapping to make an image. It could be wildly wrong if there is a runtime shader transforming
312     // the coordinates in a manner we don't know about here. However, that is a fundamental problem
313     // with the technique of converting a picture to an image to implement this shader.
314     bitmapShader = this->rasterShader(mRec.totalMatrix(),
315                                       rec.fDstColorType,
316                                       rec.fDstCS,
317                                       rec.fSurfaceProps);
318     if (!bitmapShader) {
319         return false;
320     }
321     return as_SB(bitmapShader)->appendStages(rec, mRec);
322 }
323 
program(skvm::Builder * p,skvm::Coord device,skvm::Coord local,skvm::Color paint,const MatrixRec & mRec,const SkColorInfo & dst,skvm::Uniforms * uniforms,SkArenaAlloc * alloc) const324 skvm::Color SkPictureShader::program(skvm::Builder* p,
325                                      skvm::Coord device,
326                                      skvm::Coord local,
327                                      skvm::Color paint,
328                                      const MatrixRec& mRec,
329                                      const SkColorInfo& dst,
330                                      skvm::Uniforms* uniforms,
331                                      SkArenaAlloc* alloc) const {
332     // TODO: We'll need additional plumbing to get the correct props from our callers.
333     SkSurfaceProps props{};
334 
335     // Keep bitmapShader alive by using alloc instead of stack memory
336     auto& bitmapShader = *alloc->make<sk_sp<SkShader>>();
337     bitmapShader = this->rasterShader(mRec.totalMatrix(), dst.colorType(), dst.colorSpace(), props);
338     if (!bitmapShader) {
339         return {};
340     }
341 
342     return as_SB(bitmapShader)->program(p, device, local, paint, mRec, dst, uniforms, alloc);
343 }
344 
345 /////////////////////////////////////////////////////////////////////////////////////////
346 
347 #ifdef SK_ENABLE_LEGACY_SHADERCONTEXT
onMakeContext(const ContextRec & rec,SkArenaAlloc * alloc) const348 SkShaderBase::Context* SkPictureShader::onMakeContext(const ContextRec& rec, SkArenaAlloc* alloc)
349 const {
350     const auto& vm     = *rec.fMatrix;
351     const auto* lm     = rec.fLocalMatrix;
352     const auto  totalM = lm ? SkMatrix::Concat(vm, *lm) : vm;
353     sk_sp<SkShader> bitmapShader = this->rasterShader(totalM, rec.fDstColorType,
354                                                       rec.fDstColorSpace, rec.fProps);
355     if (!bitmapShader) {
356         return nullptr;
357     }
358 
359     return as_SB(bitmapShader)->makeContext(rec, alloc);
360 }
361 #endif
362 
363 /////////////////////////////////////////////////////////////////////////////////////////
364 
365 #if defined(SK_GANESH)
366 
367 #include "src/gpu/ganesh/GrProxyProvider.h"
368 
asFragmentProcessor(const GrFPArgs & args,const MatrixRec & mRec) const369 std::unique_ptr<GrFragmentProcessor> SkPictureShader::asFragmentProcessor(
370         const GrFPArgs& args, const MatrixRec& mRec) const {
371     auto ctx = args.fContext;
372     SkColorType dstColorType = GrColorTypeToSkColorType(args.fDstColorInfo->colorType());
373     if (dstColorType == kUnknown_SkColorType) {
374         dstColorType = kRGBA_8888_SkColorType;
375     }
376 
377     auto dstCS = ref_or_srgb(args.fDstColorInfo->colorSpace());
378 
379     auto info = CachedImageInfo::Make(fTile,
380                                       mRec.totalMatrix(),
381                                       dstColorType,
382                                       dstCS.get(),
383                                       ctx->priv().caps()->maxTextureSize(),
384                                       args.fSurfaceProps);
385     if (!info.success) {
386         return nullptr;
387     }
388 
389     // Gotta be sure the GPU can support our requested colortype (might be FP16)
390     if (!ctx->colorTypeSupportedAsSurface(info.imageInfo.colorType())) {
391         info.imageInfo = info.imageInfo.makeColorType(kRGBA_8888_SkColorType);
392     }
393 
394     static const skgpu::UniqueKey::Domain kDomain = skgpu::UniqueKey::GenerateDomain();
395     skgpu::UniqueKey key;
396     std::tuple keyData = {
397         dstCS->toXYZD50Hash(),
398         dstCS->transferFnHash(),
399         static_cast<uint32_t>(dstColorType),
400         fPicture->uniqueID(),
401         fTile,
402         info.tileScale,
403         info.props
404     };
405     skgpu::UniqueKey::Builder builder(&key, kDomain, sizeof(keyData)/sizeof(uint32_t),
406                                       "Picture Shader Image");
407     memcpy(&builder[0], &keyData, sizeof(keyData));
408     builder.finish();
409 
410     GrProxyProvider* provider = ctx->priv().proxyProvider();
411     GrSurfaceProxyView view;
412     if (auto proxy = provider->findOrCreateProxyByUniqueKey(key)) {
413         view = GrSurfaceProxyView(proxy, kTopLeft_GrSurfaceOrigin, skgpu::Swizzle());
414     } else {
415         const int msaaSampleCount = 0;
416         const bool createWithMips = false;
417         auto image = info.makeImage(SkSurface::MakeRenderTarget(ctx,
418                                                                 skgpu::Budgeted::kYes,
419                                                                 info.imageInfo,
420                                                                 msaaSampleCount,
421                                                                 kTopLeft_GrSurfaceOrigin,
422                                                                 &info.props,
423                                                                 createWithMips),
424                                     fPicture.get());
425         if (!image) {
426             return nullptr;
427         }
428         auto [v, ct] = as_IB(image)->asView(ctx, GrMipmapped::kNo);
429         view = std::move(v);
430         provider->assignUniqueKeyToProxy(key, view.asTextureProxy());
431     }
432 
433     const GrSamplerState sampler(static_cast<GrSamplerState::WrapMode>(fTmx),
434                                  static_cast<GrSamplerState::WrapMode>(fTmy),
435                                  fFilter);
436     auto fp = GrTextureEffect::Make(std::move(view),
437                                     kPremul_SkAlphaType,
438                                     SkMatrix::I(),
439                                     sampler,
440                                     *ctx->priv().caps());
441     SkMatrix scale = SkMatrix::Scale(info.tileScale.width(), info.tileScale.height());
442     bool success;
443     std::tie(success, fp) = mRec.apply(std::move(fp), scale);
444     return success ? std::move(fp) : nullptr;
445 }
446 #endif
447 
448 #if defined(SK_GRAPHITE)
addToKey(const skgpu::graphite::KeyContext & keyContext,skgpu::graphite::PaintParamsKeyBuilder * builder,skgpu::graphite::PipelineDataGatherer * gatherer) const449 void SkPictureShader::addToKey(const skgpu::graphite::KeyContext& keyContext,
450                                skgpu::graphite::PaintParamsKeyBuilder* builder,
451                                skgpu::graphite::PipelineDataGatherer* gatherer) const {
452 
453     using namespace skgpu::graphite;
454 
455     Recorder* recorder = keyContext.recorder();
456     const Caps* caps = recorder->priv().caps();
457 
458     // TODO: We'll need additional plumbing to get the correct props from our callers. In
459     // particular we'll need to expand the keyContext to have the surfaceProps, the dstColorType
460     // and dstColorSpace.
461     SkSurfaceProps props{};
462 
463     SkMatrix totalM = keyContext.local2Dev().asM33();
464     if (keyContext.localMatrix()) {
465         totalM.preConcat(*keyContext.localMatrix());
466     }
467     CachedImageInfo info = CachedImageInfo::Make(fTile,
468                                                  totalM,
469                                                  /* dstColorType= */ kRGBA_8888_SkColorType,
470                                                  /* dstColorSpace= */ nullptr,
471                                                  caps->maxTextureSize(),
472                                                  props);
473     if (!info.success) {
474         SolidColorShaderBlock::BeginBlock(keyContext, builder, gatherer, {1, 0, 0, 1});
475         builder->endBlock();
476         return;
477     }
478 
479     // TODO: right now we're explicitly not caching here. We could expand the ImageProvider
480     // API to include already Graphite-backed images, add a Recorder-local cache or add
481     // rendered-picture images to the global cache.
482     sk_sp<SkImage> img = info.makeImage(SkSurface::MakeGraphite(recorder, info.imageInfo,
483                                                                 skgpu::Mipmapped::kNo, &info.props),
484                                         fPicture.get());
485     if (!img) {
486         SolidColorShaderBlock::BeginBlock(keyContext, builder, gatherer, {1, 0, 0, 1});
487         builder->endBlock();
488         return;
489     }
490 
491     const auto shaderLM = SkMatrix::Scale(1.f/info.tileScale.width(), 1.f/info.tileScale.height());
492     sk_sp<SkShader> shader = img->makeShader(fTmx, fTmy, SkSamplingOptions(fFilter), &shaderLM);
493     if (!shader) {
494         SolidColorShaderBlock::BeginBlock(keyContext, builder, gatherer, {1, 0, 0, 1});
495         builder->endBlock();
496         return;
497     }
498 
499     as_SB(shader)->addToKey(keyContext, builder, gatherer);
500 }
501 #endif // SK_GRAPHITE
502