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