Line data Source code
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:
33 0 : 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 0 : : fPictureID(pictureID)
40 : , fTile(tile)
41 : , fTmx(tmx)
42 : , fTmy(tmy)
43 0 : , fScale(scale) {
44 :
45 0 : for (int i = 0; i < 9; ++i) {
46 0 : 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 0 : this->init(&gBitmapSkaderKeyNamespaceLabel, 0, keySize);
57 0 : }
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 0 : struct BitmapShaderRec : public SkResourceCache::Rec {
70 0 : BitmapShaderRec(const BitmapShaderKey& key, SkShader* tileShader)
71 0 : : fKey(key)
72 0 : , fShader(SkRef(tileShader)) {}
73 :
74 : BitmapShaderKey fKey;
75 : sk_sp<SkShader> fShader;
76 : size_t fBitmapBytes;
77 :
78 0 : const Key& getKey() const override { return fKey; }
79 0 : size_t bytesUsed() const override {
80 : // Just the record overhead -- the actual pixels are accounted by SkImageCacherator.
81 0 : return sizeof(fKey) + sizeof(SkImageShader);
82 : }
83 0 : const char* getCategory() const override { return "bitmap-shader"; }
84 0 : SkDiscardableMemory* diagnostic_only_getDiscardable() const override { return nullptr; }
85 :
86 0 : static bool Visitor(const SkResourceCache::Rec& baseRec, void* contextShader) {
87 0 : const BitmapShaderRec& rec = static_cast<const BitmapShaderRec&>(baseRec);
88 0 : sk_sp<SkShader>* result = reinterpret_cast<sk_sp<SkShader>*>(contextShader);
89 :
90 0 : *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 0 : return true;
95 : }
96 : };
97 :
98 : } // namespace
99 :
100 0 : SkPictureShader::SkPictureShader(sk_sp<SkPicture> picture, TileMode tmx, TileMode tmy,
101 0 : const SkMatrix* localMatrix, const SkRect* tile)
102 : : INHERITED(localMatrix)
103 0 : , fPicture(std::move(picture))
104 0 : , fTile(tile ? *tile : fPicture->cullRect())
105 : , fTmx(tmx)
106 0 : , fTmy(tmy) {
107 0 : }
108 :
109 0 : sk_sp<SkShader> SkPictureShader::Make(sk_sp<SkPicture> picture, TileMode tmx, TileMode tmy,
110 : const SkMatrix* localMatrix, const SkRect* tile) {
111 0 : if (!picture || picture->cullRect().isEmpty() || (tile && tile->isEmpty())) {
112 0 : return SkShader::MakeEmptyShader();
113 : }
114 0 : return sk_sp<SkShader>(new SkPictureShader(std::move(picture), tmx, tmy, localMatrix, tile));
115 : }
116 :
117 0 : sk_sp<SkFlattenable> SkPictureShader::CreateProc(SkReadBuffer& buffer) {
118 : SkMatrix lm;
119 0 : buffer.readMatrix(&lm);
120 0 : TileMode mx = (TileMode)buffer.read32();
121 0 : TileMode my = (TileMode)buffer.read32();
122 : SkRect tile;
123 0 : buffer.readRect(&tile);
124 :
125 0 : sk_sp<SkPicture> picture;
126 :
127 0 : if (buffer.isCrossProcess() && SkPicture::PictureIOSecurityPrecautionsEnabled()) {
128 0 : if (buffer.isVersionLT(SkReadBuffer::kPictureShaderHasPictureBool_Version)) {
129 : // Older code blindly serialized pictures. We don't trust them.
130 0 : buffer.validate(false);
131 0 : 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 0 : buffer.validate(!buffer.readBool());
136 : } else {
137 : // Old code always serialized the picture. New code writes a 'true' first if it did.
138 0 : if (buffer.isVersionLT(SkReadBuffer::kPictureShaderHasPictureBool_Version) ||
139 0 : buffer.readBool()) {
140 0 : picture = SkPicture::MakeFromBuffer(buffer);
141 : }
142 : }
143 0 : return SkPictureShader::Make(picture, mx, my, &lm, &tile);
144 : }
145 :
146 0 : void SkPictureShader::flatten(SkWriteBuffer& buffer) const {
147 0 : buffer.writeMatrix(this->getLocalMatrix());
148 0 : buffer.write32(fTmx);
149 0 : buffer.write32(fTmy);
150 0 : 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 0 : if (buffer.isCrossProcess() && SkPicture::PictureIOSecurityPrecautionsEnabled()) {
155 0 : buffer.writeBool(false);
156 : } else {
157 0 : buffer.writeBool(true);
158 0 : fPicture->flatten(buffer);
159 : }
160 0 : }
161 :
162 0 : sk_sp<SkShader> SkPictureShader::refBitmapShader(const SkMatrix& viewMatrix, const SkMatrix* localM,
163 : SkColorSpace* dstColorSpace,
164 : const int maxTextureSize) const {
165 0 : SkASSERT(fPicture && !fPicture->cullRect().isEmpty());
166 :
167 : SkMatrix m;
168 0 : m.setConcat(viewMatrix, this->getLocalMatrix());
169 0 : if (localM) {
170 0 : 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 0 : if (!SkDecomposeUpper2x2(m, nullptr, &scale, nullptr)) {
179 : // Decomposition failed, use an approximation.
180 0 : scale.set(SkScalarSqrt(m.getScaleX() * m.getScaleX() + m.getSkewX() * m.getSkewX()),
181 0 : SkScalarSqrt(m.getScaleY() * m.getScaleY() + m.getSkewY() * m.getSkewY()));
182 : }
183 0 : SkSize scaledSize = SkSize::Make(SkScalarAbs(scale.x() * fTile.width()),
184 0 : SkScalarAbs(scale.y() * fTile.height()));
185 :
186 : // Clamp the tile size to about 4M pixels
187 : static const SkScalar kMaxTileArea = 2048 * 2048;
188 0 : SkScalar tileArea = scaledSize.width() * scaledSize.height();
189 0 : if (tileArea > kMaxTileArea) {
190 0 : SkScalar clampScale = SkScalarSqrt(kMaxTileArea / tileArea);
191 0 : scaledSize.set(scaledSize.width() * clampScale,
192 0 : 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 0 : if (maxTextureSize) {
197 0 : if (scaledSize.width() > maxTextureSize || scaledSize.height() > maxTextureSize) {
198 0 : SkScalar downScale = maxTextureSize / SkMaxScalar(scaledSize.width(), scaledSize.height());
199 0 : scaledSize.set(SkScalarFloorToScalar(scaledSize.width() * downScale),
200 0 : 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 0 : const SkISize tileSize = scaledSize.toCeil();
209 : #endif
210 0 : if (tileSize.isEmpty()) {
211 0 : return SkShader::MakeEmptyShader();
212 : }
213 :
214 : // The actual scale, compensating for rounding & clamping.
215 0 : const SkSize tileScale = SkSize::Make(SkIntToScalar(tileSize.width()) / fTile.width(),
216 0 : SkIntToScalar(tileSize.height()) / fTile.height());
217 :
218 0 : sk_sp<SkShader> tileShader;
219 : BitmapShaderKey key(fPicture->uniqueID(),
220 : fTile,
221 0 : fTmx,
222 0 : fTmy,
223 : tileScale,
224 0 : this->getLocalMatrix());
225 :
226 0 : if (!SkResourceCache::Find(key, BitmapShaderRec::Visitor, &tileShader)) {
227 : SkMatrix tileMatrix;
228 0 : tileMatrix.setRectToRect(fTile, SkRect::MakeIWH(tileSize.width(), tileSize.height()),
229 0 : SkMatrix::kFill_ScaleToFit);
230 :
231 : sk_sp<SkImage> tileImage = SkImage::MakeFromGenerator(
232 0 : SkPictureImageGenerator::Make(tileSize, fPicture, &tileMatrix, nullptr,
233 0 : SkImage::BitDepth::kU8, sk_ref_sp(dstColorSpace)));
234 0 : if (!tileImage) {
235 0 : return nullptr;
236 : }
237 :
238 0 : SkMatrix shaderMatrix = this->getLocalMatrix();
239 0 : shaderMatrix.preScale(1 / tileScale.width(), 1 / tileScale.height());
240 0 : tileShader = tileImage->makeShader(fTmx, fTmy, &shaderMatrix);
241 :
242 0 : SkResourceCache::Add(new BitmapShaderRec(key, tileShader.get()));
243 : }
244 :
245 0 : return tileShader;
246 : }
247 :
248 0 : 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 0 : auto& bitmapShader = *alloc->make<sk_sp<SkShader>>();
253 0 : bitmapShader = this->refBitmapShader(ctm, localMatrix, cs);
254 0 : return bitmapShader && bitmapShader->appendStages(p, cs, alloc, ctm, paint);
255 : }
256 :
257 : /////////////////////////////////////////////////////////////////////////////////////////
258 0 : SkShader::Context* SkPictureShader::onMakeContext(const ContextRec& rec, SkArenaAlloc* alloc)
259 : const {
260 0 : sk_sp<SkShader> bitmapShader(this->refBitmapShader(*rec.fMatrix, rec.fLocalMatrix,
261 0 : rec.fDstColorSpace));
262 0 : if (!bitmapShader) {
263 0 : return nullptr;
264 : }
265 :
266 : PictureShaderContext* ctx =
267 0 : alloc->make<PictureShaderContext>(*this, rec, std::move(bitmapShader), alloc);
268 0 : if (nullptr == ctx->fBitmapShaderContext) {
269 0 : ctx = nullptr;
270 : }
271 0 : return ctx;
272 : }
273 :
274 : /////////////////////////////////////////////////////////////////////////////////////////
275 :
276 0 : SkPictureShader::PictureShaderContext::PictureShaderContext(
277 : const SkPictureShader& shader, const ContextRec& rec, sk_sp<SkShader> bitmapShader,
278 0 : SkArenaAlloc* alloc)
279 : : INHERITED(shader, rec)
280 0 : , fBitmapShader(std::move(bitmapShader))
281 : {
282 0 : fBitmapShaderContext = fBitmapShader->makeContext(rec, alloc);
283 : //if fBitmapShaderContext is null, we are invalid
284 0 : }
285 :
286 0 : uint32_t SkPictureShader::PictureShaderContext::getFlags() const {
287 0 : SkASSERT(fBitmapShaderContext);
288 0 : return fBitmapShaderContext->getFlags();
289 : }
290 :
291 0 : SkShader::Context::ShadeProc SkPictureShader::PictureShaderContext::asAShadeProc(void** ctx) {
292 0 : SkASSERT(fBitmapShaderContext);
293 0 : return fBitmapShaderContext->asAShadeProc(ctx);
294 : }
295 :
296 0 : void SkPictureShader::PictureShaderContext::shadeSpan(int x, int y, SkPMColor dstC[], int count) {
297 0 : SkASSERT(fBitmapShaderContext);
298 0 : fBitmapShaderContext->shadeSpan(x, y, dstC, count);
299 0 : }
300 :
301 : #ifndef SK_IGNORE_TO_STRING
302 0 : void SkPictureShader::toString(SkString* str) const {
303 : static const char* gTileModeName[SkShader::kTileModeCount] = {
304 : "clamp", "repeat", "mirror"
305 : };
306 :
307 0 : str->appendf("PictureShader: [%f:%f:%f:%f] ",
308 0 : fPicture->cullRect().fLeft,
309 0 : fPicture->cullRect().fTop,
310 0 : fPicture->cullRect().fRight,
311 0 : fPicture->cullRect().fBottom);
312 :
313 0 : str->appendf("(%s, %s)", gTileModeName[fTmx], gTileModeName[fTmy]);
314 :
315 0 : this->INHERITED::toString(str);
316 0 : }
317 : #endif
318 :
319 : #if SK_SUPPORT_GPU
320 0 : sk_sp<GrFragmentProcessor> SkPictureShader::asFragmentProcessor(const AsFPArgs& args) const {
321 0 : int maxTextureSize = 0;
322 0 : if (args.fContext) {
323 0 : maxTextureSize = args.fContext->caps()->maxTextureSize();
324 : }
325 0 : sk_sp<SkShader> bitmapShader(this->refBitmapShader(*args.fViewMatrix, args.fLocalMatrix,
326 0 : args.fDstColorSpace, maxTextureSize));
327 0 : if (!bitmapShader) {
328 0 : return nullptr;
329 : }
330 0 : return bitmapShader->asFragmentProcessor(SkShader::AsFPArgs(
331 0 : args.fContext, args.fViewMatrix, nullptr, args.fFilterQuality, args.fDstColorSpace));
332 : }
333 : #endif
|