Home | History | Annotate | Download | only in core
      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 
     21 SkPictureShader::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
     31 SkPictureShader::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 
     39 SkPictureShader::~SkPictureShader() {
     40     fPicture->unref();
     41 }
     42 
     43 SkPictureShader* 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 
     51 SkFlattenable* 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 
     62 void 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 
     70 SkShader* 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 
    134 size_t SkPictureShader::contextSize() const {
    135     return sizeof(PictureShaderContext);
    136 }
    137 
    138 SkShader::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 
    148 SkShader::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 
    159 SkPictureShader::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 
    169 SkPictureShader::PictureShaderContext::~PictureShaderContext() {
    170     if (fBitmapShaderContext) {
    171         fBitmapShaderContext->~Context();
    172     }
    173     sk_free(fBitmapShaderContextStorage);
    174 }
    175 
    176 uint32_t SkPictureShader::PictureShaderContext::getFlags() const {
    177     SkASSERT(fBitmapShaderContext);
    178     return fBitmapShaderContext->getFlags();
    179 }
    180 
    181 SkShader::Context::ShadeProc SkPictureShader::PictureShaderContext::asAShadeProc(void** ctx) {
    182     SkASSERT(fBitmapShaderContext);
    183     return fBitmapShaderContext->asAShadeProc(ctx);
    184 }
    185 
    186 void SkPictureShader::PictureShaderContext::shadeSpan(int x, int y, SkPMColor dstC[], int count) {
    187     SkASSERT(fBitmapShaderContext);
    188     fBitmapShaderContext->shadeSpan(x, y, dstC, count);
    189 }
    190 
    191 void 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
    197 void 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
    215 bool 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
    225 bool 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