• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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