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
17#if SK_SUPPORT_GPU
18#include "GrContext.h"
19#endif
20
21SkPictureShader::SkPictureShader(const SkPicture* picture, TileMode tmx, TileMode tmy,
22                                 const SkMatrix* localMatrix, const SkRect* tile)
23    : INHERITED(localMatrix)
24    , fPicture(SkRef(picture))
25    , fTile(tile ? *tile : picture->cullRect())
26    , fTmx(tmx)
27    , fTmy(tmy) {
28}
29
30#ifdef SK_SUPPORT_LEGACY_DEEPFLATTENING
31SkPictureShader::SkPictureShader(SkReadBuffer& buffer) : INHERITED(buffer) {
32    fTmx = static_cast<SkShader::TileMode>(buffer.read32());
33    fTmy = static_cast<SkShader::TileMode>(buffer.read32());
34    buffer.readRect(&fTile);
35    fPicture = SkPicture::CreateFromBuffer(buffer);
36}
37#endif
38
39SkPictureShader::~SkPictureShader() {
40    fPicture->unref();
41}
42
43SkPictureShader* SkPictureShader::Create(const SkPicture* picture, TileMode tmx, TileMode tmy,
44                                         const SkMatrix* localMatrix, const SkRect* tile) {
45    if (!picture || picture->cullRect().isEmpty() || (tile && tile->isEmpty())) {
46        return NULL;
47    }
48    return SkNEW_ARGS(SkPictureShader, (picture, tmx, tmy, localMatrix, tile));
49}
50
51SkFlattenable* SkPictureShader::CreateProc(SkReadBuffer& buffer) {
52    SkMatrix lm;
53    buffer.readMatrix(&lm);
54    TileMode mx = (TileMode)buffer.read32();
55    TileMode my = (TileMode)buffer.read32();
56    SkRect tile;
57    buffer.readRect(&tile);
58    SkAutoTUnref<SkPicture> picture(SkPicture::CreateFromBuffer(buffer));
59    return SkPictureShader::Create(picture, mx, my, &lm, &tile);
60}
61
62void SkPictureShader::flatten(SkWriteBuffer& buffer) const {
63    buffer.writeMatrix(this->getLocalMatrix());
64    buffer.write32(fTmx);
65    buffer.write32(fTmy);
66    buffer.writeRect(fTile);
67    fPicture->flatten(buffer);
68}
69
70SkShader* SkPictureShader::refBitmapShader(const SkMatrix& matrix, const SkMatrix* localM) const {
71    SkASSERT(fPicture && !fPicture->cullRect().isEmpty());
72
73    SkMatrix m;
74    m.setConcat(matrix, this->getLocalMatrix());
75    if (localM) {
76        m.preConcat(*localM);
77    }
78
79    // Use a rotation-invariant scale
80    SkPoint scale;
81    if (!SkDecomposeUpper2x2(m, NULL, &scale, NULL)) {
82        // Decomposition failed, use an approximation.
83        scale.set(SkScalarSqrt(m.getScaleX() * m.getScaleX() + m.getSkewX() * m.getSkewX()),
84                  SkScalarSqrt(m.getScaleY() * m.getScaleY() + m.getSkewY() * m.getSkewY()));
85    }
86    SkSize scaledSize = SkSize::Make(scale.x() * fTile.width(), scale.y() * fTile.height());
87
88    // Clamp the tile size to about 16M pixels
89    static const SkScalar kMaxTileArea = 4096 * 4096;
90    SkScalar tileArea = SkScalarMul(scaledSize.width(), scaledSize.height());
91    if (tileArea > kMaxTileArea) {
92        SkScalar clampScale = SkScalarSqrt(SkScalarDiv(kMaxTileArea, tileArea));
93        scaledSize.set(SkScalarMul(scaledSize.width(), clampScale),
94                       SkScalarMul(scaledSize.height(), clampScale));
95    }
96
97    SkISize tileSize = scaledSize.toRound();
98    if (tileSize.isEmpty()) {
99        return NULL;
100    }
101
102    // The actual scale, compensating for rounding & clamping.
103    SkSize tileScale = SkSize::Make(SkIntToScalar(tileSize.width()) / fTile.width(),
104                                    SkIntToScalar(tileSize.height()) / fTile.height());
105
106    SkAutoMutexAcquire ama(fCachedBitmapShaderMutex);
107
108    if (!fCachedBitmapShader || tileScale != fCachedTileScale) {
109        SkBitmap bm;
110        if (!bm.tryAllocN32Pixels(tileSize.width(), tileSize.height())) {
111            return NULL;
112        }
113        bm.eraseColor(SK_ColorTRANSPARENT);
114
115        SkCanvas canvas(bm);
116        canvas.scale(tileScale.width(), tileScale.height());
117        canvas.translate(fTile.x(), fTile.y());
118        canvas.drawPicture(fPicture);
119
120        fCachedTileScale = tileScale;
121
122        SkMatrix shaderMatrix = this->getLocalMatrix();
123        shaderMatrix.preScale(1 / tileScale.width(), 1 / tileScale.height());
124        fCachedBitmapShader.reset(CreateBitmapShader(bm, fTmx, fTmy, &shaderMatrix));
125    }
126
127    // Increment the ref counter inside the mutex to ensure the returned pointer is still valid.
128    // Otherwise, the pointer may have been overwritten on a different thread before the object's
129    // ref count was incremented.
130    fCachedBitmapShader.get()->ref();
131    return fCachedBitmapShader;
132}
133
134size_t SkPictureShader::contextSize() const {
135    return sizeof(PictureShaderContext);
136}
137
138SkShader::Context* SkPictureShader::onCreateContext(const ContextRec& rec, void* storage) const {
139    SkAutoTUnref<SkShader> bitmapShader(this->refBitmapShader(*rec.fMatrix, rec.fLocalMatrix));
140    if (NULL == bitmapShader.get()) {
141        return NULL;
142    }
143    return PictureShaderContext::Create(storage, *this, rec, bitmapShader);
144}
145
146/////////////////////////////////////////////////////////////////////////////////////////
147
148SkShader::Context* SkPictureShader::PictureShaderContext::Create(void* storage,
149                   const SkPictureShader& shader, const ContextRec& rec, SkShader* bitmapShader) {
150    PictureShaderContext* ctx = SkNEW_PLACEMENT_ARGS(storage, PictureShaderContext,
151                                                     (shader, rec, bitmapShader));
152    if (NULL == ctx->fBitmapShaderContext) {
153        ctx->~PictureShaderContext();
154        ctx = NULL;
155    }
156    return ctx;
157}
158
159SkPictureShader::PictureShaderContext::PictureShaderContext(
160        const SkPictureShader& shader, const ContextRec& rec, SkShader* bitmapShader)
161    : INHERITED(shader, rec)
162    , fBitmapShader(SkRef(bitmapShader))
163{
164    fBitmapShaderContextStorage = sk_malloc_throw(bitmapShader->contextSize());
165    fBitmapShaderContext = bitmapShader->createContext(rec, fBitmapShaderContextStorage);
166    //if fBitmapShaderContext is null, we are invalid
167}
168
169SkPictureShader::PictureShaderContext::~PictureShaderContext() {
170    if (fBitmapShaderContext) {
171        fBitmapShaderContext->~Context();
172    }
173    sk_free(fBitmapShaderContextStorage);
174}
175
176uint32_t SkPictureShader::PictureShaderContext::getFlags() const {
177    SkASSERT(fBitmapShaderContext);
178    return fBitmapShaderContext->getFlags();
179}
180
181SkShader::Context::ShadeProc SkPictureShader::PictureShaderContext::asAShadeProc(void** ctx) {
182    SkASSERT(fBitmapShaderContext);
183    return fBitmapShaderContext->asAShadeProc(ctx);
184}
185
186void SkPictureShader::PictureShaderContext::shadeSpan(int x, int y, SkPMColor dstC[], int count) {
187    SkASSERT(fBitmapShaderContext);
188    fBitmapShaderContext->shadeSpan(x, y, dstC, count);
189}
190
191void SkPictureShader::PictureShaderContext::shadeSpan16(int x, int y, uint16_t dstC[], int count) {
192    SkASSERT(fBitmapShaderContext);
193    fBitmapShaderContext->shadeSpan16(x, y, dstC, count);
194}
195
196#ifndef SK_IGNORE_TO_STRING
197void SkPictureShader::toString(SkString* str) const {
198    static const char* gTileModeName[SkShader::kTileModeCount] = {
199        "clamp", "repeat", "mirror"
200    };
201
202    str->appendf("PictureShader: [%f:%f:%f:%f] ",
203                 fPicture ? fPicture->cullRect().fLeft : 0,
204                 fPicture ? fPicture->cullRect().fTop : 0,
205                 fPicture ? fPicture->cullRect().fRight : 0,
206                 fPicture ? fPicture->cullRect().fBottom : 0);
207
208    str->appendf("(%s, %s)", gTileModeName[fTmx], gTileModeName[fTmy]);
209
210    this->INHERITED::toString(str);
211}
212#endif
213
214#if SK_SUPPORT_GPU
215bool SkPictureShader::asFragmentProcessor(GrContext* context, const SkPaint& paint,
216                                          const SkMatrix* localMatrix, GrColor* paintColor,
217                                          GrFragmentProcessor** fp) const {
218    SkAutoTUnref<SkShader> bitmapShader(this->refBitmapShader(context->getMatrix(), localMatrix));
219    if (!bitmapShader) {
220        return false;
221    }
222    return bitmapShader->asFragmentProcessor(context, paint, NULL, paintColor, fp);
223}
224#else
225bool SkPictureShader::asFragmentProcessor(GrContext*, const SkPaint&, const SkMatrix*, GrColor*,
226                                          GrFragmentProcessor**) const {
227    SkDEBUGFAIL("Should not call in GPU-less build");
228    return false;
229}
230#endif
231