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