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