blob: a4928d4fa99fea07729ee8b1779801eed6c1b5d4 [file] [log] [blame]
commit-bot@chromium.orgc5d9bb02014-04-08 15:19:34 +00001/*
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"
fmalita23df2d62014-10-22 07:39:08 -070013#include "SkDiscardableMemory.h"
commit-bot@chromium.orgc5d9bb02014-04-08 15:19:34 +000014#include "SkMatrixUtils.h"
15#include "SkPicture.h"
16#include "SkReadBuffer.h"
fmalita23df2d62014-10-22 07:39:08 -070017#include "SkResourceCache.h"
18#include "SkThread.h"
commit-bot@chromium.orgc5d9bb02014-04-08 15:19:34 +000019
20#if SK_SUPPORT_GPU
21#include "GrContext.h"
22#endif
23
fmalita23df2d62014-10-22 07:39:08 -070024struct BitmapShaderKey : public SkResourceCache::Key {
25public:
26 BitmapShaderKey(uint32_t pictureID,
27 const SkRect& tile,
28 SkShader::TileMode tmx,
29 SkShader::TileMode tmy,
30 const SkSize& scale,
31 const SkMatrix& localMatrix)
32 : fPictureID(pictureID)
33 , fTile(tile)
34 , fTmx(tmx)
35 , fTmy(tmy)
36 , fScale(scale)
37 , fLocalMatrix(localMatrix) {
38
39 static const size_t keySize = sizeof(fPictureID) +
40 sizeof(fTile) +
41 sizeof(fTmx) + sizeof(fTmy) +
42 sizeof(fScale) +
43 sizeof(fLocalMatrix);
44 // This better be packed.
45 SkASSERT(sizeof(uint32_t) * (&fEndOfStruct - &fPictureID) == keySize);
46 this->init(keySize);
47 }
48
49private:
50 uint32_t fPictureID;
51 SkRect fTile;
52 SkShader::TileMode fTmx, fTmy;
53 SkSize fScale;
54 SkMatrix fLocalMatrix;
55
56 SkDEBUGCODE(uint32_t fEndOfStruct;)
57};
58
59struct BitmapShaderRec : public SkResourceCache::Rec {
60 BitmapShaderRec(const BitmapShaderKey& key, SkShader* tileShader, size_t bitmapBytes)
61 : fKey(key)
62 , fShader(SkRef(tileShader))
63 , fBitmapBytes(bitmapBytes) {}
64
65 BitmapShaderKey fKey;
66 SkAutoTUnref<SkShader> fShader;
67 size_t fBitmapBytes;
68
69 virtual const Key& getKey() const SK_OVERRIDE { return fKey; }
70 virtual size_t bytesUsed() const SK_OVERRIDE {
71 return sizeof(fKey) + sizeof(SkShader) + fBitmapBytes;
72 }
73
74 static bool Visitor(const SkResourceCache::Rec& baseRec, void* contextShader) {
75 const BitmapShaderRec& rec = static_cast<const BitmapShaderRec&>(baseRec);
76 SkAutoTUnref<SkShader>* result = reinterpret_cast<SkAutoTUnref<SkShader>*>(contextShader);
77
78 result->reset(SkRef(rec.fShader.get()));
79 return true;
80 }
81};
82
83// FIXME: there's considerable boilerplate/duplication here vs. the global resource cache.
84SK_DECLARE_STATIC_MUTEX(gBitmapShaderCacheMutex);
85static SkResourceCache* gBitmapShaderCache = NULL;
86
87#ifndef SK_DEFAULT_TILE_CACHE_LIMIT
88 #define SK_DEFAULT_TILE_CACHE_LIMIT (2 * 1024 * 1024)
89#endif
90
91static void cleanup_cache() {
92 // We'll clean this up in our own tests, but disable for clients.
93 // Chrome seems to have funky multi-process things going on in unit tests that
94 // makes this unsafe to delete when the main process atexit()s.
95 // SkLazyPtr does the same sort of thing.
96#if SK_DEVELOPER
97 SkDELETE(gBitmapShaderCache);
98#endif
99}
100
101/** Must hold gBitmapShaderCacheMutex when calling. */
102static SkResourceCache* cache() {
103 // gTileCacheMutex is always held when this is called, so we don't need to be fancy in here.
104 gBitmapShaderCacheMutex.assertHeld();
105 if (NULL == gBitmapShaderCache) {
106#ifdef SK_USE_DISCARDABLE_SCALEDIMAGECACHE
107 gBitmapShaderCache = SkNEW_ARGS(SkResourceCache, (SkDiscardableMemory::Create));
108#else
109 gBitmapShaderCache = SkNEW_ARGS(SkResourceCache, (SK_DEFAULT_TILE_CACHE_LIMIT));
110#endif
111 atexit(cleanup_cache);
112 }
113 return gBitmapShaderCache;
114}
115
116static bool cache_find(const BitmapShaderKey& key, SkAutoTUnref<SkShader>* result) {
117 SkAutoMutexAcquire am(gBitmapShaderCacheMutex);
118 return cache()->find(key, BitmapShaderRec::Visitor, result);
119}
120
121static void cache_add(BitmapShaderRec* rec) {
122 SkAutoMutexAcquire am(gBitmapShaderCacheMutex);
123 cache()->add(rec);
124}
125
126static bool cache_try_alloc_pixels(SkBitmap* bitmap) {
127 SkAutoMutexAcquire am(gBitmapShaderCacheMutex);
128 SkBitmap::Allocator* allocator = cache()->allocator();
129
130 if (NULL != allocator) {
131 return allocator->allocPixelRef(bitmap, NULL);
132 } else {
133 return bitmap->tryAllocPixels();
134 }
135}
136
robertphillipse8464992014-07-14 07:53:26 -0700137SkPictureShader::SkPictureShader(const SkPicture* picture, TileMode tmx, TileMode tmy,
fmalitab5f78262014-08-06 13:07:15 -0700138 const SkMatrix* localMatrix, const SkRect* tile)
commit-bot@chromium.org5aacfe92014-05-02 21:23:52 +0000139 : INHERITED(localMatrix)
140 , fPicture(SkRef(picture))
bsalomon49f085d2014-09-05 13:34:00 -0700141 , fTile(tile ? *tile : picture->cullRect())
commit-bot@chromium.orgc5d9bb02014-04-08 15:19:34 +0000142 , fTmx(tmx)
fmalitab5f78262014-08-06 13:07:15 -0700143 , fTmy(tmy) {
fmalitab5f78262014-08-06 13:07:15 -0700144}
commit-bot@chromium.orgc5d9bb02014-04-08 15:19:34 +0000145
reed9fa60da2014-08-21 07:59:51 -0700146#ifdef SK_SUPPORT_LEGACY_DEEPFLATTENING
147SkPictureShader::SkPictureShader(SkReadBuffer& buffer) : INHERITED(buffer) {
commit-bot@chromium.orgc5d9bb02014-04-08 15:19:34 +0000148 fTmx = static_cast<SkShader::TileMode>(buffer.read32());
149 fTmy = static_cast<SkShader::TileMode>(buffer.read32());
fmalitab5f78262014-08-06 13:07:15 -0700150 buffer.readRect(&fTile);
commit-bot@chromium.org855e88e2014-04-21 19:33:12 +0000151 fPicture = SkPicture::CreateFromBuffer(buffer);
commit-bot@chromium.orgc5d9bb02014-04-08 15:19:34 +0000152}
reed9fa60da2014-08-21 07:59:51 -0700153#endif
commit-bot@chromium.orgc5d9bb02014-04-08 15:19:34 +0000154
155SkPictureShader::~SkPictureShader() {
commit-bot@chromium.org855e88e2014-04-21 19:33:12 +0000156 fPicture->unref();
commit-bot@chromium.orgc5d9bb02014-04-08 15:19:34 +0000157}
158
robertphillipse8464992014-07-14 07:53:26 -0700159SkPictureShader* SkPictureShader::Create(const SkPicture* picture, TileMode tmx, TileMode tmy,
fmalitab5f78262014-08-06 13:07:15 -0700160 const SkMatrix* localMatrix, const SkRect* tile) {
bsalomon49f085d2014-09-05 13:34:00 -0700161 if (!picture || picture->cullRect().isEmpty() || (tile && tile->isEmpty())) {
commit-bot@chromium.org855e88e2014-04-21 19:33:12 +0000162 return NULL;
163 }
fmalitab5f78262014-08-06 13:07:15 -0700164 return SkNEW_ARGS(SkPictureShader, (picture, tmx, tmy, localMatrix, tile));
commit-bot@chromium.orgc5d9bb02014-04-08 15:19:34 +0000165}
166
reed9fa60da2014-08-21 07:59:51 -0700167SkFlattenable* SkPictureShader::CreateProc(SkReadBuffer& buffer) {
168 SkMatrix lm;
169 buffer.readMatrix(&lm);
170 TileMode mx = (TileMode)buffer.read32();
171 TileMode my = (TileMode)buffer.read32();
172 SkRect tile;
173 buffer.readRect(&tile);
174 SkAutoTUnref<SkPicture> picture(SkPicture::CreateFromBuffer(buffer));
175 return SkPictureShader::Create(picture, mx, my, &lm, &tile);
176}
commit-bot@chromium.orgc5d9bb02014-04-08 15:19:34 +0000177
reed9fa60da2014-08-21 07:59:51 -0700178void SkPictureShader::flatten(SkWriteBuffer& buffer) const {
179 buffer.writeMatrix(this->getLocalMatrix());
commit-bot@chromium.orgc5d9bb02014-04-08 15:19:34 +0000180 buffer.write32(fTmx);
181 buffer.write32(fTmy);
fmalitab5f78262014-08-06 13:07:15 -0700182 buffer.writeRect(fTile);
commit-bot@chromium.org855e88e2014-04-21 19:33:12 +0000183 fPicture->flatten(buffer);
commit-bot@chromium.orgc5d9bb02014-04-08 15:19:34 +0000184}
185
commit-bot@chromium.org80116dc2014-05-06 17:16:03 +0000186SkShader* SkPictureShader::refBitmapShader(const SkMatrix& matrix, const SkMatrix* localM) const {
robertphillipsa8d7f0b2014-08-29 08:03:56 -0700187 SkASSERT(fPicture && !fPicture->cullRect().isEmpty());
commit-bot@chromium.orgc5d9bb02014-04-08 15:19:34 +0000188
189 SkMatrix m;
commit-bot@chromium.org5970f622014-05-12 20:42:21 +0000190 m.setConcat(matrix, this->getLocalMatrix());
commit-bot@chromium.org80116dc2014-05-06 17:16:03 +0000191 if (localM) {
192 m.preConcat(*localM);
193 }
commit-bot@chromium.orgc5d9bb02014-04-08 15:19:34 +0000194
195 // Use a rotation-invariant scale
196 SkPoint scale;
197 if (!SkDecomposeUpper2x2(m, NULL, &scale, NULL)) {
198 // Decomposition failed, use an approximation.
199 scale.set(SkScalarSqrt(m.getScaleX() * m.getScaleX() + m.getSkewX() * m.getSkewX()),
200 SkScalarSqrt(m.getScaleY() * m.getScaleY() + m.getSkewY() * m.getSkewY()));
201 }
fmalitab5f78262014-08-06 13:07:15 -0700202 SkSize scaledSize = SkSize::Make(scale.x() * fTile.width(), scale.y() * fTile.height());
commit-bot@chromium.orgc5d9bb02014-04-08 15:19:34 +0000203
fmalitabb204f42014-08-07 08:39:24 -0700204 // Clamp the tile size to about 16M pixels
205 static const SkScalar kMaxTileArea = 4096 * 4096;
206 SkScalar tileArea = SkScalarMul(scaledSize.width(), scaledSize.height());
207 if (tileArea > kMaxTileArea) {
208 SkScalar clampScale = SkScalarSqrt(SkScalarDiv(kMaxTileArea, tileArea));
209 scaledSize.set(SkScalarMul(scaledSize.width(), clampScale),
210 SkScalarMul(scaledSize.height(), clampScale));
211 }
212
commit-bot@chromium.orgc5d9bb02014-04-08 15:19:34 +0000213 SkISize tileSize = scaledSize.toRound();
214 if (tileSize.isEmpty()) {
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000215 return NULL;
commit-bot@chromium.orgc5d9bb02014-04-08 15:19:34 +0000216 }
217
fmalitabb204f42014-08-07 08:39:24 -0700218 // The actual scale, compensating for rounding & clamping.
fmalitab5f78262014-08-06 13:07:15 -0700219 SkSize tileScale = SkSize::Make(SkIntToScalar(tileSize.width()) / fTile.width(),
220 SkIntToScalar(tileSize.height()) / fTile.height());
commit-bot@chromium.orgc5d9bb02014-04-08 15:19:34 +0000221
fmalita23df2d62014-10-22 07:39:08 -0700222 SkAutoTUnref<SkShader> tileShader;
223 BitmapShaderKey key(fPicture->uniqueID(),
224 fTile,
225 fTmx,
226 fTmy,
227 tileScale,
228 this->getLocalMatrix());
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000229
fmalita23df2d62014-10-22 07:39:08 -0700230 if (!cache_find(key, &tileShader)) {
commit-bot@chromium.orgc5d9bb02014-04-08 15:19:34 +0000231 SkBitmap bm;
fmalita23df2d62014-10-22 07:39:08 -0700232 bm.setInfo(SkImageInfo::MakeN32Premul(tileSize));
233 if (!cache_try_alloc_pixels(&bm)) {
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000234 return NULL;
commit-bot@chromium.orgc5d9bb02014-04-08 15:19:34 +0000235 }
236 bm.eraseColor(SK_ColorTRANSPARENT);
237
238 SkCanvas canvas(bm);
239 canvas.scale(tileScale.width(), tileScale.height());
fmalitab5f78262014-08-06 13:07:15 -0700240 canvas.translate(fTile.x(), fTile.y());
robertphillips9b14f262014-06-04 05:40:44 -0700241 canvas.drawPicture(fPicture);
commit-bot@chromium.orgc5d9bb02014-04-08 15:19:34 +0000242
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000243 SkMatrix shaderMatrix = this->getLocalMatrix();
244 shaderMatrix.preScale(1 / tileScale.width(), 1 / tileScale.height());
fmalita23df2d62014-10-22 07:39:08 -0700245 tileShader.reset(CreateBitmapShader(bm, fTmx, fTmy, &shaderMatrix));
246
247 cache_add(SkNEW_ARGS(BitmapShaderRec, (key, tileShader.get(), bm.getSize())));
commit-bot@chromium.orgc5d9bb02014-04-08 15:19:34 +0000248 }
249
fmalita23df2d62014-10-22 07:39:08 -0700250 return tileShader.detach();
commit-bot@chromium.orgc5d9bb02014-04-08 15:19:34 +0000251}
252
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000253size_t SkPictureShader::contextSize() const {
254 return sizeof(PictureShaderContext);
commit-bot@chromium.orgc5d9bb02014-04-08 15:19:34 +0000255}
256
commit-bot@chromium.orgce56d962014-05-05 18:39:18 +0000257SkShader::Context* SkPictureShader::onCreateContext(const ContextRec& rec, void* storage) const {
commit-bot@chromium.org80116dc2014-05-06 17:16:03 +0000258 SkAutoTUnref<SkShader> bitmapShader(this->refBitmapShader(*rec.fMatrix, rec.fLocalMatrix));
commit-bot@chromium.orgce56d962014-05-05 18:39:18 +0000259 if (NULL == bitmapShader.get()) {
260 return NULL;
261 }
commit-bot@chromium.orgf03f9ff2014-05-06 13:43:17 +0000262 return PictureShaderContext::Create(storage, *this, rec, bitmapShader);
commit-bot@chromium.orgce56d962014-05-05 18:39:18 +0000263}
264
265/////////////////////////////////////////////////////////////////////////////////////////
266
267SkShader::Context* SkPictureShader::PictureShaderContext::Create(void* storage,
268 const SkPictureShader& shader, const ContextRec& rec, SkShader* bitmapShader) {
269 PictureShaderContext* ctx = SkNEW_PLACEMENT_ARGS(storage, PictureShaderContext,
270 (shader, rec, bitmapShader));
271 if (NULL == ctx->fBitmapShaderContext) {
272 ctx->~PictureShaderContext();
273 ctx = NULL;
274 }
275 return ctx;
276}
277
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000278SkPictureShader::PictureShaderContext::PictureShaderContext(
commit-bot@chromium.orge901b6d2014-05-01 19:31:31 +0000279 const SkPictureShader& shader, const ContextRec& rec, SkShader* bitmapShader)
280 : INHERITED(shader, rec)
commit-bot@chromium.orgce56d962014-05-05 18:39:18 +0000281 , fBitmapShader(SkRef(bitmapShader))
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000282{
commit-bot@chromium.orgce56d962014-05-05 18:39:18 +0000283 fBitmapShaderContextStorage = sk_malloc_throw(bitmapShader->contextSize());
284 fBitmapShaderContext = bitmapShader->createContext(rec, fBitmapShaderContextStorage);
285 //if fBitmapShaderContext is null, we are invalid
commit-bot@chromium.orgc5d9bb02014-04-08 15:19:34 +0000286}
287
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000288SkPictureShader::PictureShaderContext::~PictureShaderContext() {
commit-bot@chromium.orgce56d962014-05-05 18:39:18 +0000289 if (fBitmapShaderContext) {
290 fBitmapShaderContext->~Context();
291 }
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000292 sk_free(fBitmapShaderContextStorage);
commit-bot@chromium.orgc5d9bb02014-04-08 15:19:34 +0000293}
294
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000295uint32_t SkPictureShader::PictureShaderContext::getFlags() const {
commit-bot@chromium.orgce56d962014-05-05 18:39:18 +0000296 SkASSERT(fBitmapShaderContext);
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000297 return fBitmapShaderContext->getFlags();
commit-bot@chromium.orgc5d9bb02014-04-08 15:19:34 +0000298}
299
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000300SkShader::Context::ShadeProc SkPictureShader::PictureShaderContext::asAShadeProc(void** ctx) {
commit-bot@chromium.orgce56d962014-05-05 18:39:18 +0000301 SkASSERT(fBitmapShaderContext);
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000302 return fBitmapShaderContext->asAShadeProc(ctx);
303}
304
305void SkPictureShader::PictureShaderContext::shadeSpan(int x, int y, SkPMColor dstC[], int count) {
306 SkASSERT(fBitmapShaderContext);
307 fBitmapShaderContext->shadeSpan(x, y, dstC, count);
308}
309
310void SkPictureShader::PictureShaderContext::shadeSpan16(int x, int y, uint16_t dstC[], int count) {
311 SkASSERT(fBitmapShaderContext);
312 fBitmapShaderContext->shadeSpan16(x, y, dstC, count);
commit-bot@chromium.orgc5d9bb02014-04-08 15:19:34 +0000313}
314
315#ifndef SK_IGNORE_TO_STRING
316void SkPictureShader::toString(SkString* str) const {
317 static const char* gTileModeName[SkShader::kTileModeCount] = {
318 "clamp", "repeat", "mirror"
319 };
320
robertphillipsa8d7f0b2014-08-29 08:03:56 -0700321 str->appendf("PictureShader: [%f:%f:%f:%f] ",
322 fPicture ? fPicture->cullRect().fLeft : 0,
323 fPicture ? fPicture->cullRect().fTop : 0,
324 fPicture ? fPicture->cullRect().fRight : 0,
325 fPicture ? fPicture->cullRect().fBottom : 0);
commit-bot@chromium.orgc5d9bb02014-04-08 15:19:34 +0000326
327 str->appendf("(%s, %s)", gTileModeName[fTmx], gTileModeName[fTmy]);
328
329 this->INHERITED::toString(str);
330}
331#endif
332
333#if SK_SUPPORT_GPU
joshualittb0a8a372014-09-23 09:50:21 -0700334bool SkPictureShader::asFragmentProcessor(GrContext* context, const SkPaint& paint,
335 const SkMatrix* localMatrix, GrColor* paintColor,
336 GrFragmentProcessor** fp) const {
commit-bot@chromium.org96fb7482014-05-09 20:28:11 +0000337 SkAutoTUnref<SkShader> bitmapShader(this->refBitmapShader(context->getMatrix(), localMatrix));
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +0000338 if (!bitmapShader) {
dandov9de5b512014-06-10 14:38:28 -0700339 return false;
commit-bot@chromium.orgc5d9bb02014-04-08 15:19:34 +0000340 }
joshualittb0a8a372014-09-23 09:50:21 -0700341 return bitmapShader->asFragmentProcessor(context, paint, NULL, paintColor, fp);
dandov9de5b512014-06-10 14:38:28 -0700342}
343#else
joshualittb0a8a372014-09-23 09:50:21 -0700344bool SkPictureShader::asFragmentProcessor(GrContext*, const SkPaint&, const SkMatrix*, GrColor*,
345 GrFragmentProcessor**) const {
dandov9de5b512014-06-10 14:38:28 -0700346 SkDEBUGFAIL("Should not call in GPU-less build");
347 return false;
commit-bot@chromium.orgc5d9bb02014-04-08 15:19:34 +0000348}
349#endif