• 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 "SkPictureShader.h"
9 
10 #include "SkArenaAlloc.h"
11 #include "SkBitmap.h"
12 #include "SkBitmapProcShader.h"
13 #include "SkCanvas.h"
14 #include "SkImage.h"
15 #include "SkImageShader.h"
16 #include "SkMatrixUtils.h"
17 #include "SkPicture.h"
18 #include "SkPictureImageGenerator.h"
19 #include "SkReadBuffer.h"
20 #include "SkResourceCache.h"
21 
22 #if SK_SUPPORT_GPU
23 #include "GrContext.h"
24 #include "GrCaps.h"
25 #include "GrFragmentProcessor.h"
26 #endif
27 
28 namespace {
29 static unsigned gBitmapSkaderKeyNamespaceLabel;
30 
31 struct BitmapShaderKey : public SkResourceCache::Key {
32 public:
BitmapShaderKey__anon838458180111::BitmapShaderKey33     BitmapShaderKey(uint32_t pictureID,
34                     const SkRect& tile,
35                     SkShader::TileMode tmx,
36                     SkShader::TileMode tmy,
37                     const SkSize& scale,
38                     const SkMatrix& localMatrix)
39         : fPictureID(pictureID)
40         , fTile(tile)
41         , fTmx(tmx)
42         , fTmy(tmy)
43         , fScale(scale) {
44 
45         for (int i = 0; i < 9; ++i) {
46             fLocalMatrixStorage[i] = localMatrix[i];
47         }
48 
49         static const size_t keySize = sizeof(fPictureID) +
50                                       sizeof(fTile) +
51                                       sizeof(fTmx) + sizeof(fTmy) +
52                                       sizeof(fScale) +
53                                       sizeof(fLocalMatrixStorage);
54         // This better be packed.
55         SkASSERT(sizeof(uint32_t) * (&fEndOfStruct - &fPictureID) == keySize);
56         this->init(&gBitmapSkaderKeyNamespaceLabel, 0, keySize);
57     }
58 
59 private:
60     uint32_t           fPictureID;
61     SkRect             fTile;
62     SkShader::TileMode fTmx, fTmy;
63     SkSize             fScale;
64     SkScalar           fLocalMatrixStorage[9];
65 
66     SkDEBUGCODE(uint32_t fEndOfStruct;)
67 };
68 
69 struct BitmapShaderRec : public SkResourceCache::Rec {
BitmapShaderRec__anon838458180111::BitmapShaderRec70     BitmapShaderRec(const BitmapShaderKey& key, SkShader* tileShader)
71         : fKey(key)
72         , fShader(SkRef(tileShader)) {}
73 
74     BitmapShaderKey fKey;
75     sk_sp<SkShader> fShader;
76     size_t          fBitmapBytes;
77 
getKey__anon838458180111::BitmapShaderRec78     const Key& getKey() const override { return fKey; }
bytesUsed__anon838458180111::BitmapShaderRec79     size_t bytesUsed() const override {
80         // Just the record overhead -- the actual pixels are accounted by SkImageCacherator.
81         return sizeof(fKey) + sizeof(SkImageShader);
82     }
getCategory__anon838458180111::BitmapShaderRec83     const char* getCategory() const override { return "bitmap-shader"; }
diagnostic_only_getDiscardable__anon838458180111::BitmapShaderRec84     SkDiscardableMemory* diagnostic_only_getDiscardable() const override { return nullptr; }
85 
Visitor__anon838458180111::BitmapShaderRec86     static bool Visitor(const SkResourceCache::Rec& baseRec, void* contextShader) {
87         const BitmapShaderRec& rec = static_cast<const BitmapShaderRec&>(baseRec);
88         sk_sp<SkShader>* result = reinterpret_cast<sk_sp<SkShader>*>(contextShader);
89 
90         *result = rec.fShader;
91 
92         // The bitmap shader is backed by an image generator, thus it can always re-generate its
93         // pixels if discarded.
94         return true;
95     }
96 };
97 
98 } // namespace
99 
SkPictureShader(sk_sp<SkPicture> picture,TileMode tmx,TileMode tmy,const SkMatrix * localMatrix,const SkRect * tile)100 SkPictureShader::SkPictureShader(sk_sp<SkPicture> picture, TileMode tmx, TileMode tmy,
101                                  const SkMatrix* localMatrix, const SkRect* tile)
102     : INHERITED(localMatrix)
103     , fPicture(std::move(picture))
104     , fTile(tile ? *tile : fPicture->cullRect())
105     , fTmx(tmx)
106     , fTmy(tmy) {
107 }
108 
Make(sk_sp<SkPicture> picture,TileMode tmx,TileMode tmy,const SkMatrix * localMatrix,const SkRect * tile)109 sk_sp<SkShader> SkPictureShader::Make(sk_sp<SkPicture> picture, TileMode tmx, TileMode tmy,
110                                       const SkMatrix* localMatrix, const SkRect* tile) {
111     if (!picture || picture->cullRect().isEmpty() || (tile && tile->isEmpty())) {
112         return SkShader::MakeEmptyShader();
113     }
114     return sk_sp<SkShader>(new SkPictureShader(std::move(picture), tmx, tmy, localMatrix, tile));
115 }
116 
CreateProc(SkReadBuffer & buffer)117 sk_sp<SkFlattenable> SkPictureShader::CreateProc(SkReadBuffer& buffer) {
118     SkMatrix lm;
119     buffer.readMatrix(&lm);
120     TileMode mx = (TileMode)buffer.read32();
121     TileMode my = (TileMode)buffer.read32();
122     SkRect tile;
123     buffer.readRect(&tile);
124 
125     sk_sp<SkPicture> picture;
126 
127     if (buffer.isCrossProcess() && SkPicture::PictureIOSecurityPrecautionsEnabled()) {
128         if (buffer.isVersionLT(SkReadBuffer::kPictureShaderHasPictureBool_Version)) {
129             // Older code blindly serialized pictures.  We don't trust them.
130             buffer.validate(false);
131             return nullptr;
132         }
133         // Newer code won't serialize pictures in disallow-cross-process-picture mode.
134         // Assert that they didn't serialize anything except a false here.
135         buffer.validate(!buffer.readBool());
136     } else {
137         // Old code always serialized the picture.  New code writes a 'true' first if it did.
138         if (buffer.isVersionLT(SkReadBuffer::kPictureShaderHasPictureBool_Version) ||
139             buffer.readBool()) {
140             picture = SkPicture::MakeFromBuffer(buffer);
141         }
142     }
143     return SkPictureShader::Make(picture, mx, my, &lm, &tile);
144 }
145 
flatten(SkWriteBuffer & buffer) const146 void SkPictureShader::flatten(SkWriteBuffer& buffer) const {
147     buffer.writeMatrix(this->getLocalMatrix());
148     buffer.write32(fTmx);
149     buffer.write32(fTmy);
150     buffer.writeRect(fTile);
151 
152     // The deserialization code won't trust that our serialized picture is safe to deserialize.
153     // So write a 'false' telling it that we're not serializing a picture.
154     if (buffer.isCrossProcess() && SkPicture::PictureIOSecurityPrecautionsEnabled()) {
155         buffer.writeBool(false);
156     } else {
157         buffer.writeBool(true);
158         fPicture->flatten(buffer);
159     }
160 }
161 
refBitmapShader(const SkMatrix & viewMatrix,const SkMatrix * localM,SkColorSpace * dstColorSpace,const int maxTextureSize) const162 sk_sp<SkShader> SkPictureShader::refBitmapShader(const SkMatrix& viewMatrix, const SkMatrix* localM,
163                                                  SkColorSpace* dstColorSpace,
164                                                  const int maxTextureSize) const {
165     SkASSERT(fPicture && !fPicture->cullRect().isEmpty());
166 
167     SkMatrix m;
168     m.setConcat(viewMatrix, this->getLocalMatrix());
169     if (localM) {
170         m.preConcat(*localM);
171     }
172 
173     // Use a rotation-invariant scale
174     SkPoint scale;
175     //
176     // TODO: replace this with decomposeScale() -- but beware LayoutTest rebaselines!
177     //
178     if (!SkDecomposeUpper2x2(m, nullptr, &scale, nullptr)) {
179         // Decomposition failed, use an approximation.
180         scale.set(SkScalarSqrt(m.getScaleX() * m.getScaleX() + m.getSkewX() * m.getSkewX()),
181                   SkScalarSqrt(m.getScaleY() * m.getScaleY() + m.getSkewY() * m.getSkewY()));
182     }
183     SkSize scaledSize = SkSize::Make(SkScalarAbs(scale.x() * fTile.width()),
184                                      SkScalarAbs(scale.y() * fTile.height()));
185 
186     // Clamp the tile size to about 4M pixels
187     static const SkScalar kMaxTileArea = 2048 * 2048;
188     SkScalar tileArea = scaledSize.width() * scaledSize.height();
189     if (tileArea > kMaxTileArea) {
190         SkScalar clampScale = SkScalarSqrt(kMaxTileArea / tileArea);
191         scaledSize.set(scaledSize.width() * clampScale,
192                        scaledSize.height() * clampScale);
193     }
194 #if SK_SUPPORT_GPU
195     // Scale down the tile size if larger than maxTextureSize for GPU Path or it should fail on create texture
196     if (maxTextureSize) {
197         if (scaledSize.width() > maxTextureSize || scaledSize.height() > maxTextureSize) {
198             SkScalar downScale = maxTextureSize / SkMaxScalar(scaledSize.width(), scaledSize.height());
199             scaledSize.set(SkScalarFloorToScalar(scaledSize.width() * downScale),
200                            SkScalarFloorToScalar(scaledSize.height() * downScale));
201         }
202     }
203 #endif
204 
205 #ifdef SK_SUPPORT_LEGACY_PICTURESHADER_ROUNDING
206     const SkISize tileSize = scaledSize.toRound();
207 #else
208     const SkISize tileSize = scaledSize.toCeil();
209 #endif
210     if (tileSize.isEmpty()) {
211         return SkShader::MakeEmptyShader();
212     }
213 
214     // The actual scale, compensating for rounding & clamping.
215     const SkSize tileScale = SkSize::Make(SkIntToScalar(tileSize.width()) / fTile.width(),
216                                           SkIntToScalar(tileSize.height()) / fTile.height());
217 
218     sk_sp<SkShader> tileShader;
219     BitmapShaderKey key(fPicture->uniqueID(),
220                         fTile,
221                         fTmx,
222                         fTmy,
223                         tileScale,
224                         this->getLocalMatrix());
225 
226     if (!SkResourceCache::Find(key, BitmapShaderRec::Visitor, &tileShader)) {
227         SkMatrix tileMatrix;
228         tileMatrix.setRectToRect(fTile, SkRect::MakeIWH(tileSize.width(), tileSize.height()),
229                                  SkMatrix::kFill_ScaleToFit);
230 
231         sk_sp<SkImage> tileImage = SkImage::MakeFromGenerator(
232                 SkPictureImageGenerator::Make(tileSize, fPicture, &tileMatrix, nullptr,
233                                               SkImage::BitDepth::kU8, sk_ref_sp(dstColorSpace)));
234         if (!tileImage) {
235             return nullptr;
236         }
237 
238         SkMatrix shaderMatrix = this->getLocalMatrix();
239         shaderMatrix.preScale(1 / tileScale.width(), 1 / tileScale.height());
240         tileShader = tileImage->makeShader(fTmx, fTmy, &shaderMatrix);
241 
242         SkResourceCache::Add(new BitmapShaderRec(key, tileShader.get()));
243     }
244 
245     return tileShader;
246 }
247 
onAppendStages(SkRasterPipeline * p,SkColorSpace * cs,SkArenaAlloc * alloc,const SkMatrix & ctm,const SkPaint & paint,const SkMatrix * localMatrix) const248 bool SkPictureShader::onAppendStages(SkRasterPipeline* p, SkColorSpace* cs, SkArenaAlloc* alloc,
249                                      const SkMatrix& ctm, const SkPaint& paint,
250                                      const SkMatrix* localMatrix) const {
251     // Keep bitmapShader alive by using alloc instead of stack memory
252     auto& bitmapShader = *alloc->make<sk_sp<SkShader>>();
253     bitmapShader = this->refBitmapShader(ctm, localMatrix, cs);
254     return bitmapShader && bitmapShader->appendStages(p, cs, alloc, ctm, paint);
255 }
256 
257 /////////////////////////////////////////////////////////////////////////////////////////
onMakeContext(const ContextRec & rec,SkArenaAlloc * alloc) const258 SkShader::Context* SkPictureShader::onMakeContext(const ContextRec& rec, SkArenaAlloc* alloc)
259 const {
260     sk_sp<SkShader> bitmapShader(this->refBitmapShader(*rec.fMatrix, rec.fLocalMatrix,
261                                                        rec.fDstColorSpace));
262     if (!bitmapShader) {
263         return nullptr;
264     }
265 
266     PictureShaderContext* ctx =
267         alloc->make<PictureShaderContext>(*this, rec, std::move(bitmapShader), alloc);
268     if (nullptr == ctx->fBitmapShaderContext) {
269         ctx = nullptr;
270     }
271     return ctx;
272 }
273 
274 /////////////////////////////////////////////////////////////////////////////////////////
275 
PictureShaderContext(const SkPictureShader & shader,const ContextRec & rec,sk_sp<SkShader> bitmapShader,SkArenaAlloc * alloc)276 SkPictureShader::PictureShaderContext::PictureShaderContext(
277         const SkPictureShader& shader, const ContextRec& rec, sk_sp<SkShader> bitmapShader,
278         SkArenaAlloc* alloc)
279     : INHERITED(shader, rec)
280     , fBitmapShader(std::move(bitmapShader))
281 {
282     fBitmapShaderContext = fBitmapShader->makeContext(rec, alloc);
283     //if fBitmapShaderContext is null, we are invalid
284 }
285 
getFlags() const286 uint32_t SkPictureShader::PictureShaderContext::getFlags() const {
287     SkASSERT(fBitmapShaderContext);
288     return fBitmapShaderContext->getFlags();
289 }
290 
asAShadeProc(void ** ctx)291 SkShader::Context::ShadeProc SkPictureShader::PictureShaderContext::asAShadeProc(void** ctx) {
292     SkASSERT(fBitmapShaderContext);
293     return fBitmapShaderContext->asAShadeProc(ctx);
294 }
295 
shadeSpan(int x,int y,SkPMColor dstC[],int count)296 void SkPictureShader::PictureShaderContext::shadeSpan(int x, int y, SkPMColor dstC[], int count) {
297     SkASSERT(fBitmapShaderContext);
298     fBitmapShaderContext->shadeSpan(x, y, dstC, count);
299 }
300 
301 #ifndef SK_IGNORE_TO_STRING
toString(SkString * str) const302 void SkPictureShader::toString(SkString* str) const {
303     static const char* gTileModeName[SkShader::kTileModeCount] = {
304         "clamp", "repeat", "mirror"
305     };
306 
307     str->appendf("PictureShader: [%f:%f:%f:%f] ",
308                  fPicture->cullRect().fLeft,
309                  fPicture->cullRect().fTop,
310                  fPicture->cullRect().fRight,
311                  fPicture->cullRect().fBottom);
312 
313     str->appendf("(%s, %s)", gTileModeName[fTmx], gTileModeName[fTmy]);
314 
315     this->INHERITED::toString(str);
316 }
317 #endif
318 
319 #if SK_SUPPORT_GPU
asFragmentProcessor(const AsFPArgs & args) const320 sk_sp<GrFragmentProcessor> SkPictureShader::asFragmentProcessor(const AsFPArgs& args) const {
321     int maxTextureSize = 0;
322     if (args.fContext) {
323         maxTextureSize = args.fContext->caps()->maxTextureSize();
324     }
325     sk_sp<SkShader> bitmapShader(this->refBitmapShader(*args.fViewMatrix, args.fLocalMatrix,
326                                                        args.fDstColorSpace, maxTextureSize));
327     if (!bitmapShader) {
328         return nullptr;
329     }
330     return bitmapShader->asFragmentProcessor(SkShader::AsFPArgs(
331         args.fContext, args.fViewMatrix, nullptr, args.fFilterQuality, args.fDstColorSpace));
332 }
333 #endif
334