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