SkPictureShader.cpp revision 4739955e98ba86900a5bbac0e3661601427a54d4
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 "SkImageGenerator.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#endif 22 23namespace { 24static unsigned gBitmapSkaderKeyNamespaceLabel; 25 26class PictureImageGenerator : public SkImageGenerator { 27public: 28 PictureImageGenerator(const SkPicture* picture, 29 const SkRect& pictureTile, 30 const SkISize& tileSize) 31 : fPicture(SkRef(picture)) 32 , fPictureTile(pictureTile) 33 , fRasterTileInfo(SkImageInfo::MakeN32Premul(tileSize)) {} 34 35protected: 36 virtual bool onGetInfo(SkImageInfo *info) SK_OVERRIDE { 37 *info = fRasterTileInfo; 38 return true; 39 } 40 41 virtual Result onGetPixels(const SkImageInfo& info, void *pixels, size_t rowBytes, 42 SkPMColor ctable[], int *ctableCount) SK_OVERRIDE { 43 if (info != fRasterTileInfo || SkToBool(ctable) || SkToBool(ctableCount)) { 44 return kInvalidConversion; 45 } 46 47 SkSize tileScale = SkSize::Make(SkIntToScalar(info.width()) / fPictureTile.width(), 48 SkIntToScalar(info.height()) / fPictureTile.height()); 49 SkBitmap tileBitmap; 50 if (!tileBitmap.installPixels(info, pixels, rowBytes)) { 51 return kInvalidParameters; 52 } 53 tileBitmap.eraseColor(SK_ColorTRANSPARENT); 54 55 // Always disable LCD text, since we can't assume our image will be opaque. 56 SkCanvas tileCanvas(tileBitmap, SkSurfaceProps(0, kUnknown_SkPixelGeometry)); 57 tileCanvas.scale(tileScale.width(), tileScale.height()); 58 tileCanvas.translate(-fPictureTile.x(), -fPictureTile.y()); 59 tileCanvas.drawPicture(fPicture); 60 61 return kSuccess; 62 } 63 64private: 65 SkAutoTUnref<const SkPicture> fPicture; 66 const SkRect fPictureTile; 67 const SkImageInfo fRasterTileInfo; 68}; 69 70struct BitmapShaderKey : public SkResourceCache::Key { 71public: 72 BitmapShaderKey(uint32_t pictureID, 73 const SkRect& tile, 74 SkShader::TileMode tmx, 75 SkShader::TileMode tmy, 76 const SkSize& scale, 77 const SkMatrix& localMatrix) 78 : fPictureID(pictureID) 79 , fTile(tile) 80 , fTmx(tmx) 81 , fTmy(tmy) 82 , fScale(scale) { 83 84 for (int i = 0; i < 9; ++i) { 85 fLocalMatrixStorage[i] = localMatrix[i]; 86 } 87 88 static const size_t keySize = sizeof(fPictureID) + 89 sizeof(fTile) + 90 sizeof(fTmx) + sizeof(fTmy) + 91 sizeof(fScale) + 92 sizeof(fLocalMatrixStorage); 93 // This better be packed. 94 SkASSERT(sizeof(uint32_t) * (&fEndOfStruct - &fPictureID) == keySize); 95 this->init(&gBitmapSkaderKeyNamespaceLabel, keySize); 96 } 97 98private: 99 uint32_t fPictureID; 100 SkRect fTile; 101 SkShader::TileMode fTmx, fTmy; 102 SkSize fScale; 103 SkScalar fLocalMatrixStorage[9]; 104 105 SkDEBUGCODE(uint32_t fEndOfStruct;) 106}; 107 108struct BitmapShaderRec : public SkResourceCache::Rec { 109 BitmapShaderRec(const BitmapShaderKey& key, SkShader* tileShader, size_t bitmapBytes) 110 : fKey(key) 111 , fShader(SkRef(tileShader)) 112 , fBitmapBytes(bitmapBytes) {} 113 114 BitmapShaderKey fKey; 115 SkAutoTUnref<SkShader> fShader; 116 size_t fBitmapBytes; 117 118 const Key& getKey() const SK_OVERRIDE { return fKey; } 119 size_t bytesUsed() const SK_OVERRIDE { 120 return sizeof(fKey) + sizeof(SkShader) + fBitmapBytes; 121 } 122 123 static bool Visitor(const SkResourceCache::Rec& baseRec, void* contextShader) { 124 const BitmapShaderRec& rec = static_cast<const BitmapShaderRec&>(baseRec); 125 SkAutoTUnref<SkShader>* result = reinterpret_cast<SkAutoTUnref<SkShader>*>(contextShader); 126 127 result->reset(SkRef(rec.fShader.get())); 128 129 // The bitmap shader is backed by an image generator, thus it can always re-generate its 130 // pixels if discarded. 131 return true; 132 } 133}; 134 135} // namespace 136 137SkPictureShader::SkPictureShader(const SkPicture* picture, TileMode tmx, TileMode tmy, 138 const SkMatrix* localMatrix, const SkRect* tile) 139 : INHERITED(localMatrix) 140 , fPicture(SkRef(picture)) 141 , fTile(tile ? *tile : picture->cullRect()) 142 , fTmx(tmx) 143 , fTmy(tmy) { 144} 145 146SkPictureShader* SkPictureShader::Create(const SkPicture* picture, TileMode tmx, TileMode tmy, 147 const SkMatrix* localMatrix, const SkRect* tile) { 148 if (!picture || picture->cullRect().isEmpty() || (tile && tile->isEmpty())) { 149 return NULL; 150 } 151 return SkNEW_ARGS(SkPictureShader, (picture, tmx, tmy, localMatrix, tile)); 152} 153 154SkFlattenable* SkPictureShader::CreateProc(SkReadBuffer& buffer) { 155 SkMatrix lm; 156 buffer.readMatrix(&lm); 157 TileMode mx = (TileMode)buffer.read32(); 158 TileMode my = (TileMode)buffer.read32(); 159 SkRect tile; 160 buffer.readRect(&tile); 161 SkAutoTUnref<SkPicture> picture(SkPicture::CreateFromBuffer(buffer)); 162 return SkPictureShader::Create(picture, mx, my, &lm, &tile); 163} 164 165void SkPictureShader::flatten(SkWriteBuffer& buffer) const { 166 buffer.writeMatrix(this->getLocalMatrix()); 167 buffer.write32(fTmx); 168 buffer.write32(fTmy); 169 buffer.writeRect(fTile); 170 fPicture->flatten(buffer); 171} 172 173SkShader* SkPictureShader::refBitmapShader(const SkMatrix& matrix, const SkMatrix* localM) const { 174 SkASSERT(fPicture && !fPicture->cullRect().isEmpty()); 175 176 SkMatrix m; 177 m.setConcat(matrix, this->getLocalMatrix()); 178 if (localM) { 179 m.preConcat(*localM); 180 } 181 182 // Use a rotation-invariant scale 183 SkPoint scale; 184 if (!SkDecomposeUpper2x2(m, NULL, &scale, NULL)) { 185 // Decomposition failed, use an approximation. 186 scale.set(SkScalarSqrt(m.getScaleX() * m.getScaleX() + m.getSkewX() * m.getSkewX()), 187 SkScalarSqrt(m.getScaleY() * m.getScaleY() + m.getSkewY() * m.getSkewY())); 188 } 189 SkSize scaledSize = SkSize::Make(SkScalarAbs(scale.x() * fTile.width()), 190 SkScalarAbs(scale.y() * fTile.height())); 191 192 // Clamp the tile size to about 16M pixels 193 static const SkScalar kMaxTileArea = 4096 * 4096; 194 SkScalar tileArea = SkScalarMul(scaledSize.width(), scaledSize.height()); 195 if (tileArea > kMaxTileArea) { 196 SkScalar clampScale = SkScalarSqrt(SkScalarDiv(kMaxTileArea, tileArea)); 197 scaledSize.set(SkScalarMul(scaledSize.width(), clampScale), 198 SkScalarMul(scaledSize.height(), clampScale)); 199 } 200 201 SkISize tileSize = scaledSize.toRound(); 202 if (tileSize.isEmpty()) { 203 return NULL; 204 } 205 206 // The actual scale, compensating for rounding & clamping. 207 SkSize tileScale = SkSize::Make(SkIntToScalar(tileSize.width()) / fTile.width(), 208 SkIntToScalar(tileSize.height()) / fTile.height()); 209 210 SkAutoTUnref<SkShader> tileShader; 211 BitmapShaderKey key(fPicture->uniqueID(), 212 fTile, 213 fTmx, 214 fTmy, 215 tileScale, 216 this->getLocalMatrix()); 217 218 if (!SkResourceCache::Find(key, BitmapShaderRec::Visitor, &tileShader)) { 219 SkBitmap bm; 220 if (!SkInstallDiscardablePixelRef(SkNEW_ARGS(PictureImageGenerator, 221 (fPicture, fTile, tileSize)), &bm)) { 222 return NULL; 223 } 224 225 SkMatrix shaderMatrix = this->getLocalMatrix(); 226 shaderMatrix.preScale(1 / tileScale.width(), 1 / tileScale.height()); 227 tileShader.reset(CreateBitmapShader(bm, fTmx, fTmy, &shaderMatrix)); 228 229 SkResourceCache::Add(SkNEW_ARGS(BitmapShaderRec, (key, tileShader.get(), bm.getSize()))); 230 } 231 232 return tileShader.detach(); 233} 234 235size_t SkPictureShader::contextSize() const { 236 return sizeof(PictureShaderContext); 237} 238 239SkShader::Context* SkPictureShader::onCreateContext(const ContextRec& rec, void* storage) const { 240 SkAutoTUnref<SkShader> bitmapShader(this->refBitmapShader(*rec.fMatrix, rec.fLocalMatrix)); 241 if (NULL == bitmapShader.get()) { 242 return NULL; 243 } 244 return PictureShaderContext::Create(storage, *this, rec, bitmapShader); 245} 246 247///////////////////////////////////////////////////////////////////////////////////////// 248 249SkShader::Context* SkPictureShader::PictureShaderContext::Create(void* storage, 250 const SkPictureShader& shader, const ContextRec& rec, SkShader* bitmapShader) { 251 PictureShaderContext* ctx = SkNEW_PLACEMENT_ARGS(storage, PictureShaderContext, 252 (shader, rec, bitmapShader)); 253 if (NULL == ctx->fBitmapShaderContext) { 254 ctx->~PictureShaderContext(); 255 ctx = NULL; 256 } 257 return ctx; 258} 259 260SkPictureShader::PictureShaderContext::PictureShaderContext( 261 const SkPictureShader& shader, const ContextRec& rec, SkShader* bitmapShader) 262 : INHERITED(shader, rec) 263 , fBitmapShader(SkRef(bitmapShader)) 264{ 265 fBitmapShaderContextStorage = sk_malloc_throw(bitmapShader->contextSize()); 266 fBitmapShaderContext = bitmapShader->createContext(rec, fBitmapShaderContextStorage); 267 //if fBitmapShaderContext is null, we are invalid 268} 269 270SkPictureShader::PictureShaderContext::~PictureShaderContext() { 271 if (fBitmapShaderContext) { 272 fBitmapShaderContext->~Context(); 273 } 274 sk_free(fBitmapShaderContextStorage); 275} 276 277uint32_t SkPictureShader::PictureShaderContext::getFlags() const { 278 SkASSERT(fBitmapShaderContext); 279 return fBitmapShaderContext->getFlags(); 280} 281 282SkShader::Context::ShadeProc SkPictureShader::PictureShaderContext::asAShadeProc(void** ctx) { 283 SkASSERT(fBitmapShaderContext); 284 return fBitmapShaderContext->asAShadeProc(ctx); 285} 286 287void SkPictureShader::PictureShaderContext::shadeSpan(int x, int y, SkPMColor dstC[], int count) { 288 SkASSERT(fBitmapShaderContext); 289 fBitmapShaderContext->shadeSpan(x, y, dstC, count); 290} 291 292void SkPictureShader::PictureShaderContext::shadeSpan16(int x, int y, uint16_t dstC[], int count) { 293 SkASSERT(fBitmapShaderContext); 294 fBitmapShaderContext->shadeSpan16(x, y, dstC, count); 295} 296 297#ifndef SK_IGNORE_TO_STRING 298void SkPictureShader::toString(SkString* str) const { 299 static const char* gTileModeName[SkShader::kTileModeCount] = { 300 "clamp", "repeat", "mirror" 301 }; 302 303 str->appendf("PictureShader: [%f:%f:%f:%f] ", 304 fPicture->cullRect().fLeft, 305 fPicture->cullRect().fTop, 306 fPicture->cullRect().fRight, 307 fPicture->cullRect().fBottom); 308 309 str->appendf("(%s, %s)", gTileModeName[fTmx], gTileModeName[fTmy]); 310 311 this->INHERITED::toString(str); 312} 313#endif 314 315#if SK_SUPPORT_GPU 316bool SkPictureShader::asFragmentProcessor(GrContext* context, const SkPaint& paint, 317 const SkMatrix& viewM, const SkMatrix* localMatrix, 318 GrColor* paintColor, 319 GrFragmentProcessor** fp) const { 320 SkAutoTUnref<SkShader> bitmapShader(this->refBitmapShader(viewM, localMatrix)); 321 if (!bitmapShader) { 322 return false; 323 } 324 return bitmapShader->asFragmentProcessor(context, paint, viewM, NULL, paintColor, fp); 325} 326#else 327bool SkPictureShader::asFragmentProcessor(GrContext*, const SkPaint&, const SkMatrix&, 328 const SkMatrix*, GrColor*, 329 GrFragmentProcessor**) const { 330 SkDEBUGFAIL("Should not call in GPU-less build"); 331 return false; 332} 333#endif 334