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