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__anon266b298d0111::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__anon266b298d0111::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__anon266b298d0111::ImageFromPictureRec90 const Key& getKey() const override { return fKey; }
bytesUsed__anon266b298d0111::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__anon266b298d0111::ImageFromPictureRec95 const char* getCategory() const override { return "bitmap-shader"; }
diagnostic_only_getDiscardable__anon266b298d0111::ImageFromPictureRec96 SkDiscardableMemory* diagnostic_only_getDiscardable() const override { return nullptr; }
97
Visitor__anon266b298d0111::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
429 return GrTextureEffect::Make(
430 std::move(view), kPremul_SkAlphaType, inv, sampler, *ctx->priv().caps());
431 }
432 #endif
433