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