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/core/SkArenaAlloc.h"
14#include "src/core/SkMatrixUtils.h"
15#include "src/core/SkPicturePriv.h"
16#include "src/core/SkReadBuffer.h"
17#include "src/core/SkResourceCache.h"
18#include "src/core/SkVM.h"
19#include "src/shaders/SkBitmapProcShader.h"
20#include "src/shaders/SkImageShader.h"
21#include <atomic>
22
23#if SK_SUPPORT_GPU
24#include "include/private/GrRecordingContext.h"
25#include "src/gpu/GrCaps.h"
26#include "src/gpu/GrColorInfo.h"
27#include "src/gpu/GrFragmentProcessor.h"
28#include "src/gpu/GrRecordingContextPriv.h"
29#include "src/gpu/SkGr.h"
30#endif
31
32sk_sp<SkShader> SkPicture::makeShader(SkTileMode tmx, SkTileMode tmy, const SkMatrix* localMatrix,
33 const SkRect* tile) const {
34 if (localMatrix && !localMatrix->invert(nullptr)) {
35 return nullptr;
36 }
37 return SkPictureShader::Make(sk_ref_sp(this), tmx, tmy, localMatrix, tile);
38}
39
40sk_sp<SkShader> SkPicture::makeShader(SkTileMode tmx, SkTileMode tmy,
41 const SkMatrix* localMatrix) const {
42 return this->makeShader(tmx, tmy, localMatrix, nullptr);
43}
44
45namespace {
46static unsigned gBitmapShaderKeyNamespaceLabel;
47
48struct BitmapShaderKey : public SkResourceCache::Key {
49public:
50 BitmapShaderKey(SkColorSpace* colorSpace,
51 SkImage::BitDepth bitDepth,
52 uint32_t shaderID,
53 const SkSize& scale)
54 : fColorSpaceXYZHash(colorSpace->toXYZD50Hash())
55 , fColorSpaceTransferFnHash(colorSpace->transferFnHash())
56 , fBitDepth(bitDepth)
57 , fScale(scale) {
58
59 static const size_t keySize = sizeof(fColorSpaceXYZHash) +
60 sizeof(fColorSpaceTransferFnHash) +
61 sizeof(fBitDepth) +
62 sizeof(fScale);
63 // This better be packed.
64 SkASSERT(sizeof(uint32_t) * (&fEndOfStruct - &fColorSpaceXYZHash) == keySize);
65 this->init(&gBitmapShaderKeyNamespaceLabel, MakeSharedID(shaderID), keySize);
66 }
67
68 static uint64_t MakeSharedID(uint32_t shaderID) {
69 uint64_t sharedID = SkSetFourByteTag('p', 's', 'd', 'r');
70 return (sharedID << 32) | shaderID;
71 }
72
73private:
74 uint32_t fColorSpaceXYZHash;
75 uint32_t fColorSpaceTransferFnHash;
76 SkImage::BitDepth fBitDepth;
77 SkSize fScale;
78
79 SkDEBUGCODE(uint32_t fEndOfStruct;)
80};
81
82struct BitmapShaderRec : public SkResourceCache::Rec {
83 BitmapShaderRec(const BitmapShaderKey& key, SkShader* tileShader)
84 : fKey(key)
85 , fShader(SkRef(tileShader)) {}
86
87 BitmapShaderKey fKey;
88 sk_sp<SkShader> fShader;
89
90 const Key& getKey() const override { return fKey; }
91 size_t bytesUsed() const override {
92 // Just the record overhead -- the actual pixels are accounted by SkImage_Lazy.
93 return sizeof(fKey) + sizeof(SkImageShader);
94 }
95 const char* getCategory() const override { return "bitmap-shader"; }
96 SkDiscardableMemory* diagnostic_only_getDiscardable() const override { return nullptr; }
97
98 static bool Visitor(const SkResourceCache::Rec& baseRec, void* contextShader) {
99 const BitmapShaderRec& rec = static_cast<const BitmapShaderRec&>(baseRec);
100 sk_sp<SkShader>* result = reinterpret_cast<sk_sp<SkShader>*>(contextShader);
101
102 *result = rec.fShader;
103
104 // The bitmap shader is backed by an image generator, thus it can always re-generate its
105 // pixels if discarded.
106 return true;
107 }
108};
109
110uint32_t next_id() {
111 static std::atomic<uint32_t> nextID{1};
112
113 uint32_t id;
114 do {
115 id = nextID++;
116 } while (id == SK_InvalidGenID);
117 return id;
118}
119
120} // namespace
121
122SkPictureShader::SkPictureShader(sk_sp<SkPicture> picture, SkTileMode tmx, SkTileMode tmy,
123 const SkMatrix* localMatrix, const SkRect* tile)
124 : INHERITED(localMatrix)
125 , fPicture(std::move(picture))
126 , fTile(tile ? *tile : fPicture->cullRect())
127 , fTmx(tmx)
128 , fTmy(tmy)
129 , fUniqueID(next_id())
130 , fAddedToCache(false) {}
131
132SkPictureShader::~SkPictureShader() {
133 if (fAddedToCache.load()) {
134 SkResourceCache::PostPurgeSharedID(BitmapShaderKey::MakeSharedID(fUniqueID));
135 }
136}
137
138sk_sp<SkShader> SkPictureShader::Make(sk_sp<SkPicture> picture, SkTileMode tmx, SkTileMode tmy,
139 const SkMatrix* localMatrix, const SkRect* tile) {
140 if (!picture || picture->cullRect().isEmpty() || (tile && tile->isEmpty())) {
141 return SkShaders::Empty();
142 }
143 return sk_sp<SkShader>(new SkPictureShader(std::move(picture), tmx, tmy, localMatrix, tile));
144}
145
146sk_sp<SkFlattenable> SkPictureShader::CreateProc(SkReadBuffer& buffer) {
147 SkMatrix lm;
148 buffer.readMatrix(&lm);
149 auto tmx = buffer.read32LE(SkTileMode::kLastTileMode);
150 auto tmy = buffer.read32LE(SkTileMode::kLastTileMode);
151 SkRect tile;
152 buffer.readRect(&tile);
153
154 sk_sp<SkPicture> picture;
155
156 bool didSerialize = buffer.readBool();
157 if (didSerialize) {
158 picture = SkPicturePriv::MakeFromBuffer(buffer);
159 }
160 return SkPictureShader::Make(picture, tmx, tmy, &lm, &tile);
161}
162
163void SkPictureShader::flatten(SkWriteBuffer& buffer) const {
164 buffer.writeMatrix(this->getLocalMatrix());
165 buffer.write32((unsigned)fTmx);
166 buffer.write32((unsigned)fTmy);
167 buffer.writeRect(fTile);
168
169 buffer.writeBool(true);
170 SkPicturePriv::Flatten(fPicture, buffer);
171}
172
173// Returns a cached image shader, which wraps a single picture tile at the given
174// CTM/local matrix. Also adjusts the local matrix for tile scaling.
175sk_sp<SkShader> SkPictureShader::refBitmapShader(const SkMatrix& viewMatrix,
176 SkTCopyOnFirstWrite<SkMatrix>* localMatrix,
177 SkColorType dstColorType,
178 SkColorSpace* dstColorSpace,
179 const int maxTextureSize) const {
180 SkASSERT(fPicture && !fPicture->cullRect().isEmpty());
181
182 const SkMatrix m = SkMatrix::Concat(viewMatrix, **localMatrix);
183
184 // Use a rotation-invariant scale
185 SkPoint scale;
186 //
187 // TODO: replace this with decomposeScale() -- but beware LayoutTest rebaselines!
188 //
189 if (!SkDecomposeUpper2x2(m, nullptr, &scale, nullptr)) {
190 // Decomposition failed, use an approximation.
191 scale.set(SkScalarSqrt(m.getScaleX() * m.getScaleX() + m.getSkewX() * m.getSkewX()),
192 SkScalarSqrt(m.getScaleY() * m.getScaleY() + m.getSkewY() * m.getSkewY()));
193 }
194 SkSize scaledSize = SkSize::Make(SkScalarAbs(scale.x() * fTile.width()),
195 SkScalarAbs(scale.y() * fTile.height()));
196
197 // Clamp the tile size to about 4M pixels
198 static const SkScalar kMaxTileArea = 2048 * 2048;
199 SkScalar tileArea = scaledSize.width() * scaledSize.height();
200 if (tileArea > kMaxTileArea) {
201 SkScalar clampScale = SkScalarSqrt(kMaxTileArea / tileArea);
202 scaledSize.set(scaledSize.width() * clampScale,
203 scaledSize.height() * clampScale);
204 }
205#if SK_SUPPORT_GPU
206 // Scale down the tile size if larger than maxTextureSize for GPU Path or it should fail on create texture
207 if (maxTextureSize) {
208 if (scaledSize.width() > maxTextureSize || scaledSize.height() > maxTextureSize) {
209 SkScalar downScale = maxTextureSize / std::max(scaledSize.width(), scaledSize.height());
210 scaledSize.set(SkScalarFloorToScalar(scaledSize.width() * downScale),
211 SkScalarFloorToScalar(scaledSize.height() * downScale));
212 }
213 }
214#endif
215
216 const SkISize tileSize = scaledSize.toCeil();
217 if (tileSize.isEmpty()) {
218 return SkShaders::Empty();
219 }
220
221 // The actual scale, compensating for rounding & clamping.
222 const SkSize tileScale = SkSize::Make(SkIntToScalar(tileSize.width()) / fTile.width(),
223 SkIntToScalar(tileSize.height()) / fTile.height());
224
225
226 sk_sp<SkColorSpace> imgCS = dstColorSpace ? sk_ref_sp(dstColorSpace): SkColorSpace::MakeSRGB();
227 SkImage::BitDepth bitDepth =
228 dstColorType >= kRGBA_F16Norm_SkColorType
229 ? SkImage::BitDepth::kF16 : SkImage::BitDepth::kU8;
230
231 BitmapShaderKey key(imgCS.get(), bitDepth, fUniqueID, tileScale);
232
233 sk_sp<SkShader> tileShader;
234 if (!SkResourceCache::Find(key, BitmapShaderRec::Visitor, &tileShader)) {
235 SkMatrix tileMatrix;
236 tileMatrix.setRectToRect(fTile, SkRect::MakeIWH(tileSize.width(), tileSize.height()),
237 SkMatrix::kFill_ScaleToFit);
238
239 sk_sp<SkImage> tileImage = SkImage::MakeFromPicture(fPicture, tileSize, &tileMatrix,
240 nullptr, bitDepth, std::move(imgCS));
241 if (!tileImage) {
242 return nullptr;
243 }
244
245 tileShader = tileImage->makeShader(fTmx, fTmy);
246
247 SkResourceCache::Add(new BitmapShaderRec(key, tileShader.get()));
248 fAddedToCache.store(true);
249 }
250
251 if (tileScale.width() != 1 || tileScale.height() != 1) {
252 localMatrix->writable()->preScale(1 / tileScale.width(), 1 / tileScale.height());
253 }
254
255 return tileShader;
256}
257
258bool SkPictureShader::onAppendStages(const SkStageRec& rec) const {
259 auto lm = this->totalLocalMatrix(rec.fLocalM);
260
261 // Keep bitmapShader alive by using alloc instead of stack memory
262 auto& bitmapShader = *rec.fAlloc->make<sk_sp<SkShader>>();
263 bitmapShader = this->refBitmapShader(rec.fCTM, &lm, rec.fDstColorType, rec.fDstCS);
264
265 if (!bitmapShader) {
266 return false;
267 }
268
269 SkStageRec localRec = rec;
270 localRec.fLocalM = lm->isIdentity() ? nullptr : lm.get();
271
272 return as_SB(bitmapShader)->appendStages(localRec);
273}
274
275skvm::Color SkPictureShader::onProgram(skvm::Builder* p,
276 skvm::F32 x, skvm::F32 y, skvm::Color paint,
277 const SkMatrix& ctm, const SkMatrix* localM,
278 SkFilterQuality quality, const SkColorInfo& dst,
279 skvm::Uniforms* uniforms, SkArenaAlloc* alloc) const {
280 auto lm = this->totalLocalMatrix(localM);
281
282 // Keep bitmapShader alive by using alloc instead of stack memory
283 auto& bitmapShader = *alloc->make<sk_sp<SkShader>>();
284 bitmapShader = this->refBitmapShader(ctm, &lm, dst.colorType(), dst.colorSpace());
285 if (!bitmapShader) {
286 return {};
287 }
288
289 return as_SB(bitmapShader)->program(p, x,y, paint, ctm, lm, quality, dst, uniforms, alloc);
290}
291
292/////////////////////////////////////////////////////////////////////////////////////////
293
294#ifdef SK_ENABLE_LEGACY_SHADERCONTEXT
295SkShaderBase::Context* SkPictureShader::onMakeContext(const ContextRec& rec, SkArenaAlloc* alloc)
296const {
297 auto lm = this->totalLocalMatrix(rec.fLocalMatrix);
298 sk_sp<SkShader> bitmapShader = this->refBitmapShader(*rec.fMatrix, &lm, rec.fDstColorType,
299 rec.fDstColorSpace);
300 if (!bitmapShader) {
301 return nullptr;
302 }
303
304 ContextRec localRec = rec;
305 localRec.fLocalMatrix = lm->isIdentity() ? nullptr : lm.get();
306
307 PictureShaderContext* ctx =
308 alloc->make<PictureShaderContext>(*this, localRec, std::move(bitmapShader), alloc);
309 if (nullptr == ctx->fBitmapShaderContext) {
310 ctx = nullptr;
311 }
312 return ctx;
313}
314#endif
315
316/////////////////////////////////////////////////////////////////////////////////////////
317
318SkPictureShader::PictureShaderContext::PictureShaderContext(
319 const SkPictureShader& shader, const ContextRec& rec, sk_sp<SkShader> bitmapShader,
320 SkArenaAlloc* alloc)
321 : INHERITED(shader, rec)
322 , fBitmapShader(std::move(bitmapShader))
323{
324 fBitmapShaderContext = as_SB(fBitmapShader)->makeContext(rec, alloc);
325 //if fBitmapShaderContext is null, we are invalid
326}
327
328uint32_t SkPictureShader::PictureShaderContext::getFlags() const {
329 SkASSERT(fBitmapShaderContext);
330 return fBitmapShaderContext->getFlags();
331}
332
333void SkPictureShader::PictureShaderContext::shadeSpan(int x, int y, SkPMColor dstC[], int count) {
334 SkASSERT(fBitmapShaderContext);
335 fBitmapShaderContext->shadeSpan(x, y, dstC, count);
336}
337
338#if SK_SUPPORT_GPU
339#include "include/gpu/GrContext.h"
340#include "src/gpu/GrContextPriv.h"
341
342std::unique_ptr<GrFragmentProcessor> SkPictureShader::asFragmentProcessor(
343 const GrFPArgs& args) const {
344 int maxTextureSize = 0;
345 if (args.fContext) {
346 maxTextureSize = args.fContext->priv().caps()->maxTextureSize();
347 }
348
349 auto lm = this->totalLocalMatrix(args.fPreLocalMatrix);
350 SkColorType dstColorType = GrColorTypeToSkColorType(args.fDstColorInfo->colorType());
351 if (dstColorType == kUnknown_SkColorType) {
352 dstColorType = kRGBA_8888_SkColorType;
353 }
354 sk_sp<SkShader> bitmapShader(this->refBitmapShader(*args.fViewMatrix, &lm, dstColorType,
355 args.fDstColorInfo->colorSpace(),
356 maxTextureSize));
357 if (!bitmapShader) {
358 return nullptr;
359 }
360
361 // We want to *reset* args.fPreLocalMatrix, not compose it.
362 GrFPArgs newArgs(args.fContext, args.fViewMatrix, args.fFilterQuality, args.fDstColorInfo);
363 newArgs.fPreLocalMatrix = lm.get();
364
365 return as_SB(bitmapShader)->asFragmentProcessor(newArgs);
366}
367#endif
368