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