2 * Copyright 2014 Google Inc.
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
8 #include "SkPictureShader.h"
11 #include "SkBitmapProcShader.h"
14 #include "SkImageShader.h"
15 #include "SkMatrixUtils.h"
16 #include "SkPicture.h"
17 #include "SkReadBuffer.h"
18 #include "SkResourceCache.h"
21 #include "GrContext.h"
26 static unsigned gBitmapSkaderKeyNamespaceLabel;
28 struct BitmapShaderKey : public SkResourceCache::Key {
30 BitmapShaderKey(uint32_t pictureID,
32 SkShader::TileMode tmx,
33 SkShader::TileMode tmy,
35 const SkMatrix& localMatrix)
36 : fPictureID(pictureID)
42 for (int i = 0; i < 9; ++i) {
43 fLocalMatrixStorage[i] = localMatrix[i];
46 static const size_t keySize = sizeof(fPictureID) +
48 sizeof(fTmx) + sizeof(fTmy) +
50 sizeof(fLocalMatrixStorage);
51 // This better be packed.
52 SkASSERT(sizeof(uint32_t) * (&fEndOfStruct - &fPictureID) == keySize);
53 this->init(&gBitmapSkaderKeyNamespaceLabel, 0, keySize);
59 SkShader::TileMode fTmx, fTmy;
61 SkScalar fLocalMatrixStorage[9];
63 SkDEBUGCODE(uint32_t fEndOfStruct;)
66 struct BitmapShaderRec : public SkResourceCache::Rec {
67 BitmapShaderRec(const BitmapShaderKey& key, SkShader* tileShader)
69 , fShader(SkRef(tileShader)) {}
72 sk_sp<SkShader> fShader;
75 const Key& getKey() const override { return fKey; }
76 size_t bytesUsed() const override {
77 // Just the record overhead -- the actual pixels are accounted by SkImageCacherator.
78 return sizeof(fKey) + sizeof(SkImageShader);
80 const char* getCategory() const override { return "bitmap-shader"; }
81 SkDiscardableMemory* diagnostic_only_getDiscardable() const override { return nullptr; }
83 static bool Visitor(const SkResourceCache::Rec& baseRec, void* contextShader) {
84 const BitmapShaderRec& rec = static_cast<const BitmapShaderRec&>(baseRec);
85 sk_sp<SkShader>* result = reinterpret_cast<sk_sp<SkShader>*>(contextShader);
87 *result = rec.fShader;
89 // The bitmap shader is backed by an image generator, thus it can always re-generate its
90 // pixels if discarded.
97 SkPictureShader::SkPictureShader(sk_sp<SkPicture> picture, TileMode tmx, TileMode tmy,
98 const SkMatrix* localMatrix, const SkRect* tile)
99 : INHERITED(localMatrix)
100 , fPicture(std::move(picture))
101 , fTile(tile ? *tile : fPicture->cullRect())
106 sk_sp<SkShader> SkPictureShader::Make(sk_sp<SkPicture> picture, TileMode tmx, TileMode tmy,
107 const SkMatrix* localMatrix, const SkRect* tile) {
108 if (!picture || picture->cullRect().isEmpty() || (tile && tile->isEmpty())) {
109 return SkShader::MakeEmptyShader();
111 return sk_sp<SkShader>(new SkPictureShader(std::move(picture), tmx, tmy, localMatrix, tile));
114 sk_sp<SkFlattenable> SkPictureShader::CreateProc(SkReadBuffer& buffer) {
116 buffer.readMatrix(&lm);
117 TileMode mx = (TileMode)buffer.read32();
118 TileMode my = (TileMode)buffer.read32();
120 buffer.readRect(&tile);
122 sk_sp<SkPicture> picture;
124 if (buffer.isCrossProcess() && SkPicture::PictureIOSecurityPrecautionsEnabled()) {
125 if (buffer.isVersionLT(SkReadBuffer::kPictureShaderHasPictureBool_Version)) {
126 // Older code blindly serialized pictures. We don't trust them.
127 buffer.validate(false);
130 // Newer code won't serialize pictures in disallow-cross-process-picture mode.
131 // Assert that they didn't serialize anything except a false here.
132 buffer.validate(!buffer.readBool());
134 // Old code always serialized the picture. New code writes a 'true' first if it did.
135 if (buffer.isVersionLT(SkReadBuffer::kPictureShaderHasPictureBool_Version) ||
137 picture = SkPicture::MakeFromBuffer(buffer);
140 return SkPictureShader::Make(picture, mx, my, &lm, &tile);
143 void SkPictureShader::flatten(SkWriteBuffer& buffer) const {
144 buffer.writeMatrix(this->getLocalMatrix());
145 buffer.write32(fTmx);
146 buffer.write32(fTmy);
147 buffer.writeRect(fTile);
149 // The deserialization code won't trust that our serialized picture is safe to deserialize.
150 // So write a 'false' telling it that we're not serializing a picture.
151 if (buffer.isCrossProcess() && SkPicture::PictureIOSecurityPrecautionsEnabled()) {
152 buffer.writeBool(false);
154 buffer.writeBool(true);
155 fPicture->flatten(buffer);
159 sk_sp<SkShader> SkPictureShader::refBitmapShader(const SkMatrix& viewMatrix, const SkMatrix* localM,
160 const int maxTextureSize) const {
161 SkASSERT(fPicture && !fPicture->cullRect().isEmpty());
164 m.setConcat(viewMatrix, this->getLocalMatrix());
166 m.preConcat(*localM);
169 // Use a rotation-invariant scale
172 // TODO: replace this with decomposeScale() -- but beware LayoutTest rebaselines!
174 if (!SkDecomposeUpper2x2(m, nullptr, &scale, nullptr)) {
175 // Decomposition failed, use an approximation.
176 scale.set(SkScalarSqrt(m.getScaleX() * m.getScaleX() + m.getSkewX() * m.getSkewX()),
177 SkScalarSqrt(m.getScaleY() * m.getScaleY() + m.getSkewY() * m.getSkewY()));
179 SkSize scaledSize = SkSize::Make(SkScalarAbs(scale.x() * fTile.width()),
180 SkScalarAbs(scale.y() * fTile.height()));
182 // Clamp the tile size to about 4M pixels
183 static const SkScalar kMaxTileArea = 2048 * 2048;
184 SkScalar tileArea = SkScalarMul(scaledSize.width(), scaledSize.height());
185 if (tileArea > kMaxTileArea) {
186 SkScalar clampScale = SkScalarSqrt(kMaxTileArea / tileArea);
187 scaledSize.set(SkScalarMul(scaledSize.width(), clampScale),
188 SkScalarMul(scaledSize.height(), clampScale));
191 // Scale down the tile size if larger than maxTextureSize for GPU Path or it should fail on create texture
192 if (maxTextureSize) {
193 if (scaledSize.width() > maxTextureSize || scaledSize.height() > maxTextureSize) {
194 SkScalar downScale = maxTextureSize / SkMaxScalar(scaledSize.width(), scaledSize.height());
195 scaledSize.set(SkScalarFloorToScalar(SkScalarMul(scaledSize.width(), downScale)),
196 SkScalarFloorToScalar(SkScalarMul(scaledSize.height(), downScale)));
201 #ifdef SK_SUPPORT_LEGACY_PICTURESHADER_ROUNDING
202 const SkISize tileSize = scaledSize.toRound();
204 const SkISize tileSize = scaledSize.toCeil();
206 if (tileSize.isEmpty()) {
207 return SkShader::MakeEmptyShader();
210 // The actual scale, compensating for rounding & clamping.
211 const SkSize tileScale = SkSize::Make(SkIntToScalar(tileSize.width()) / fTile.width(),
212 SkIntToScalar(tileSize.height()) / fTile.height());
214 sk_sp<SkShader> tileShader;
215 BitmapShaderKey key(fPicture->uniqueID(),
220 this->getLocalMatrix());
222 if (!SkResourceCache::Find(key, BitmapShaderRec::Visitor, &tileShader)) {
224 tileMatrix.setRectToRect(fTile, SkRect::MakeIWH(tileSize.width(), tileSize.height()),
225 SkMatrix::kFill_ScaleToFit);
227 sk_sp<SkImage> tileImage(
228 SkImage::MakeFromPicture(fPicture, tileSize, &tileMatrix, nullptr));
233 SkMatrix shaderMatrix = this->getLocalMatrix();
234 shaderMatrix.preScale(1 / tileScale.width(), 1 / tileScale.height());
235 tileShader = tileImage->makeShader(fTmx, fTmy, &shaderMatrix);
237 SkResourceCache::Add(new BitmapShaderRec(key, tileShader.get()));
243 size_t SkPictureShader::onContextSize(const ContextRec&) const {
244 return sizeof(PictureShaderContext);
247 SkShader::Context* SkPictureShader::onCreateContext(const ContextRec& rec, void* storage) const {
248 sk_sp<SkShader> bitmapShader(this->refBitmapShader(*rec.fMatrix, rec.fLocalMatrix));
252 return PictureShaderContext::Create(storage, *this, rec, bitmapShader);
255 /////////////////////////////////////////////////////////////////////////////////////////
257 SkShader::Context* SkPictureShader::PictureShaderContext::Create(void* storage,
258 const SkPictureShader& shader, const ContextRec& rec,
259 sk_sp<SkShader> bitmapShader) {
260 PictureShaderContext* ctx = new (storage) PictureShaderContext(shader, rec,
261 std::move(bitmapShader));
262 if (nullptr == ctx->fBitmapShaderContext) {
263 ctx->~PictureShaderContext();
269 SkPictureShader::PictureShaderContext::PictureShaderContext(
270 const SkPictureShader& shader, const ContextRec& rec, sk_sp<SkShader> bitmapShader)
271 : INHERITED(shader, rec)
272 , fBitmapShader(std::move(bitmapShader))
274 fBitmapShaderContextStorage = sk_malloc_throw(fBitmapShader->contextSize(rec));
275 fBitmapShaderContext = fBitmapShader->createContext(rec, fBitmapShaderContextStorage);
276 //if fBitmapShaderContext is null, we are invalid
279 SkPictureShader::PictureShaderContext::~PictureShaderContext() {
280 if (fBitmapShaderContext) {
281 fBitmapShaderContext->~Context();
283 sk_free(fBitmapShaderContextStorage);
286 uint32_t SkPictureShader::PictureShaderContext::getFlags() const {
287 SkASSERT(fBitmapShaderContext);
288 return fBitmapShaderContext->getFlags();
291 SkShader::Context::ShadeProc SkPictureShader::PictureShaderContext::asAShadeProc(void** ctx) {
292 SkASSERT(fBitmapShaderContext);
293 return fBitmapShaderContext->asAShadeProc(ctx);
296 void SkPictureShader::PictureShaderContext::shadeSpan(int x, int y, SkPMColor dstC[], int count) {
297 SkASSERT(fBitmapShaderContext);
298 fBitmapShaderContext->shadeSpan(x, y, dstC, count);
301 #ifndef SK_IGNORE_TO_STRING
302 void SkPictureShader::toString(SkString* str) const {
303 static const char* gTileModeName[SkShader::kTileModeCount] = {
304 "clamp", "repeat", "mirror"
307 str->appendf("PictureShader: [%f:%f:%f:%f] ",
308 fPicture->cullRect().fLeft,
309 fPicture->cullRect().fTop,
310 fPicture->cullRect().fRight,
311 fPicture->cullRect().fBottom);
313 str->appendf("(%s, %s)", gTileModeName[fTmx], gTileModeName[fTmy]);
315 this->INHERITED::toString(str);
320 sk_sp<GrFragmentProcessor> SkPictureShader::asFragmentProcessor(const AsFPArgs& args) const {
321 int maxTextureSize = 0;
323 maxTextureSize = args.fContext->caps()->maxTextureSize();
325 sk_sp<SkShader> bitmapShader(this->refBitmapShader(*args.fViewMatrix, args.fLocalMatrix,
330 return bitmapShader->asFragmentProcessor(SkShader::AsFPArgs(
331 args.fContext, args.fViewMatrix, nullptr, args.fFilterQuality, args.fDstColorSpace));