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 "SkArenaAlloc.h"
     11 #include "SkBitmap.h"
     12 #include "SkBitmapProcShader.h"
     13 #include "SkCanvas.h"
     14 #include "SkImage.h"
     15 #include "SkImageShader.h"
     16 #include "SkMatrixUtils.h"
     17 #include "SkPicture.h"
     18 #include "SkPictureImageGenerator.h"
     19 #include "SkReadBuffer.h"
     20 #include "SkResourceCache.h"
     21 
     22 #if SK_SUPPORT_GPU
     23 #include "GrContext.h"
     24 #include "GrCaps.h"
     25 #include "GrFragmentProcessor.h"
     26 #endif
     27 
     28 namespace {
     29 static unsigned gBitmapSkaderKeyNamespaceLabel;
     30 
     31 struct BitmapShaderKey : public SkResourceCache::Key {
     32 public:
     33     BitmapShaderKey(uint32_t pictureID,
     34                     const SkRect& tile,
     35                     SkShader::TileMode tmx,
     36                     SkShader::TileMode tmy,
     37                     const SkSize& scale,
     38                     const SkMatrix& localMatrix)
     39         : fPictureID(pictureID)
     40         , fTile(tile)
     41         , fTmx(tmx)
     42         , fTmy(tmy)
     43         , fScale(scale) {
     44 
     45         for (int i = 0; i < 9; ++i) {
     46             fLocalMatrixStorage[i] = localMatrix[i];
     47         }
     48 
     49         static const size_t keySize = sizeof(fPictureID) +
     50                                       sizeof(fTile) +
     51                                       sizeof(fTmx) + sizeof(fTmy) +
     52                                       sizeof(fScale) +
     53                                       sizeof(fLocalMatrixStorage);
     54         // This better be packed.
     55         SkASSERT(sizeof(uint32_t) * (&fEndOfStruct - &fPictureID) == keySize);
     56         this->init(&gBitmapSkaderKeyNamespaceLabel, 0, keySize);
     57     }
     58 
     59 private:
     60     uint32_t           fPictureID;
     61     SkRect             fTile;
     62     SkShader::TileMode fTmx, fTmy;
     63     SkSize             fScale;
     64     SkScalar           fLocalMatrixStorage[9];
     65 
     66     SkDEBUGCODE(uint32_t fEndOfStruct;)
     67 };
     68 
     69 struct BitmapShaderRec : public SkResourceCache::Rec {
     70     BitmapShaderRec(const BitmapShaderKey& key, SkShader* tileShader)
     71         : fKey(key)
     72         , fShader(SkRef(tileShader)) {}
     73 
     74     BitmapShaderKey fKey;
     75     sk_sp<SkShader> fShader;
     76     size_t          fBitmapBytes;
     77 
     78     const Key& getKey() const override { return fKey; }
     79     size_t bytesUsed() const override {
     80         // Just the record overhead -- the actual pixels are accounted by SkImageCacherator.
     81         return sizeof(fKey) + sizeof(SkImageShader);
     82     }
     83     const char* getCategory() const override { return "bitmap-shader"; }
     84     SkDiscardableMemory* diagnostic_only_getDiscardable() const override { return nullptr; }
     85 
     86     static bool Visitor(const SkResourceCache::Rec& baseRec, void* contextShader) {
     87         const BitmapShaderRec& rec = static_cast<const BitmapShaderRec&>(baseRec);
     88         sk_sp<SkShader>* result = reinterpret_cast<sk_sp<SkShader>*>(contextShader);
     89 
     90         *result = rec.fShader;
     91 
     92         // The bitmap shader is backed by an image generator, thus it can always re-generate its
     93         // pixels if discarded.
     94         return true;
     95     }
     96 };
     97 
     98 } // namespace
     99 
    100 SkPictureShader::SkPictureShader(sk_sp<SkPicture> picture, TileMode tmx, TileMode tmy,
    101                                  const SkMatrix* localMatrix, const SkRect* tile)
    102     : INHERITED(localMatrix)
    103     , fPicture(std::move(picture))
    104     , fTile(tile ? *tile : fPicture->cullRect())
    105     , fTmx(tmx)
    106     , fTmy(tmy) {
    107 }
    108 
    109 sk_sp<SkShader> SkPictureShader::Make(sk_sp<SkPicture> picture, TileMode tmx, TileMode tmy,
    110                                       const SkMatrix* localMatrix, const SkRect* tile) {
    111     if (!picture || picture->cullRect().isEmpty() || (tile && tile->isEmpty())) {
    112         return SkShader::MakeEmptyShader();
    113     }
    114     return sk_sp<SkShader>(new SkPictureShader(std::move(picture), tmx, tmy, localMatrix, tile));
    115 }
    116 
    117 sk_sp<SkFlattenable> SkPictureShader::CreateProc(SkReadBuffer& buffer) {
    118     SkMatrix lm;
    119     buffer.readMatrix(&lm);
    120     TileMode mx = (TileMode)buffer.read32();
    121     TileMode my = (TileMode)buffer.read32();
    122     SkRect tile;
    123     buffer.readRect(&tile);
    124 
    125     sk_sp<SkPicture> picture;
    126 
    127     if (buffer.isCrossProcess() && SkPicture::PictureIOSecurityPrecautionsEnabled()) {
    128         if (buffer.isVersionLT(SkReadBuffer::kPictureShaderHasPictureBool_Version)) {
    129             // Older code blindly serialized pictures.  We don't trust them.
    130             buffer.validate(false);
    131             return nullptr;
    132         }
    133         // Newer code won't serialize pictures in disallow-cross-process-picture mode.
    134         // Assert that they didn't serialize anything except a false here.
    135         buffer.validate(!buffer.readBool());
    136     } else {
    137         // Old code always serialized the picture.  New code writes a 'true' first if it did.
    138         if (buffer.isVersionLT(SkReadBuffer::kPictureShaderHasPictureBool_Version) ||
    139             buffer.readBool()) {
    140             picture = SkPicture::MakeFromBuffer(buffer);
    141         }
    142     }
    143     return SkPictureShader::Make(picture, mx, my, &lm, &tile);
    144 }
    145 
    146 void SkPictureShader::flatten(SkWriteBuffer& buffer) const {
    147     buffer.writeMatrix(this->getLocalMatrix());
    148     buffer.write32(fTmx);
    149     buffer.write32(fTmy);
    150     buffer.writeRect(fTile);
    151 
    152     // The deserialization code won't trust that our serialized picture is safe to deserialize.
    153     // So write a 'false' telling it that we're not serializing a picture.
    154     if (buffer.isCrossProcess() && SkPicture::PictureIOSecurityPrecautionsEnabled()) {
    155         buffer.writeBool(false);
    156     } else {
    157         buffer.writeBool(true);
    158         fPicture->flatten(buffer);
    159     }
    160 }
    161 
    162 sk_sp<SkShader> SkPictureShader::refBitmapShader(const SkMatrix& viewMatrix, const SkMatrix* localM,
    163                                                  SkColorSpace* dstColorSpace,
    164                                                  const int maxTextureSize) const {
    165     SkASSERT(fPicture && !fPicture->cullRect().isEmpty());
    166 
    167     SkMatrix m;
    168     m.setConcat(viewMatrix, this->getLocalMatrix());
    169     if (localM) {
    170         m.preConcat(*localM);
    171     }
    172 
    173     // Use a rotation-invariant scale
    174     SkPoint scale;
    175     //
    176     // TODO: replace this with decomposeScale() -- but beware LayoutTest rebaselines!
    177     //
    178     if (!SkDecomposeUpper2x2(m, nullptr, &scale, nullptr)) {
    179         // Decomposition failed, use an approximation.
    180         scale.set(SkScalarSqrt(m.getScaleX() * m.getScaleX() + m.getSkewX() * m.getSkewX()),
    181                   SkScalarSqrt(m.getScaleY() * m.getScaleY() + m.getSkewY() * m.getSkewY()));
    182     }
    183     SkSize scaledSize = SkSize::Make(SkScalarAbs(scale.x() * fTile.width()),
    184                                      SkScalarAbs(scale.y() * fTile.height()));
    185 
    186     // Clamp the tile size to about 4M pixels
    187     static const SkScalar kMaxTileArea = 2048 * 2048;
    188     SkScalar tileArea = scaledSize.width() * scaledSize.height();
    189     if (tileArea > kMaxTileArea) {
    190         SkScalar clampScale = SkScalarSqrt(kMaxTileArea / tileArea);
    191         scaledSize.set(scaledSize.width() * clampScale,
    192                        scaledSize.height() * clampScale);
    193     }
    194 #if SK_SUPPORT_GPU
    195     // Scale down the tile size if larger than maxTextureSize for GPU Path or it should fail on create texture
    196     if (maxTextureSize) {
    197         if (scaledSize.width() > maxTextureSize || scaledSize.height() > maxTextureSize) {
    198             SkScalar downScale = maxTextureSize / SkMaxScalar(scaledSize.width(), scaledSize.height());
    199             scaledSize.set(SkScalarFloorToScalar(scaledSize.width() * downScale),
    200                            SkScalarFloorToScalar(scaledSize.height() * downScale));
    201         }
    202     }
    203 #endif
    204 
    205 #ifdef SK_SUPPORT_LEGACY_PICTURESHADER_ROUNDING
    206     const SkISize tileSize = scaledSize.toRound();
    207 #else
    208     const SkISize tileSize = scaledSize.toCeil();
    209 #endif
    210     if (tileSize.isEmpty()) {
    211         return SkShader::MakeEmptyShader();
    212     }
    213 
    214     // The actual scale, compensating for rounding & clamping.
    215     const SkSize tileScale = SkSize::Make(SkIntToScalar(tileSize.width()) / fTile.width(),
    216                                           SkIntToScalar(tileSize.height()) / fTile.height());
    217 
    218     sk_sp<SkShader> tileShader;
    219     BitmapShaderKey key(fPicture->uniqueID(),
    220                         fTile,
    221                         fTmx,
    222                         fTmy,
    223                         tileScale,
    224                         this->getLocalMatrix());
    225 
    226     if (!SkResourceCache::Find(key, BitmapShaderRec::Visitor, &tileShader)) {
    227         SkMatrix tileMatrix;
    228         tileMatrix.setRectToRect(fTile, SkRect::MakeIWH(tileSize.width(), tileSize.height()),
    229                                  SkMatrix::kFill_ScaleToFit);
    230 
    231         sk_sp<SkImage> tileImage = SkImage::MakeFromGenerator(
    232                 SkPictureImageGenerator::Make(tileSize, fPicture, &tileMatrix, nullptr,
    233                                               SkImage::BitDepth::kU8, sk_ref_sp(dstColorSpace)));
    234         if (!tileImage) {
    235             return nullptr;
    236         }
    237 
    238         SkMatrix shaderMatrix = this->getLocalMatrix();
    239         shaderMatrix.preScale(1 / tileScale.width(), 1 / tileScale.height());
    240         tileShader = tileImage->makeShader(fTmx, fTmy, &shaderMatrix);
    241 
    242         SkResourceCache::Add(new BitmapShaderRec(key, tileShader.get()));
    243     }
    244 
    245     return tileShader;
    246 }
    247 
    248 bool SkPictureShader::onAppendStages(SkRasterPipeline* p, SkColorSpace* cs, SkArenaAlloc* alloc,
    249                                      const SkMatrix& ctm, const SkPaint& paint,
    250                                      const SkMatrix* localMatrix) const {
    251     // Keep bitmapShader alive by using alloc instead of stack memory
    252     auto& bitmapShader = *alloc->make<sk_sp<SkShader>>();
    253     bitmapShader = this->refBitmapShader(ctm, localMatrix, cs);
    254     return bitmapShader && bitmapShader->appendStages(p, cs, alloc, ctm, paint);
    255 }
    256 
    257 /////////////////////////////////////////////////////////////////////////////////////////
    258 SkShader::Context* SkPictureShader::onMakeContext(const ContextRec& rec, SkArenaAlloc* alloc)
    259 const {
    260     sk_sp<SkShader> bitmapShader(this->refBitmapShader(*rec.fMatrix, rec.fLocalMatrix,
    261                                                        rec.fDstColorSpace));
    262     if (!bitmapShader) {
    263         return nullptr;
    264     }
    265 
    266     PictureShaderContext* ctx =
    267         alloc->make<PictureShaderContext>(*this, rec, std::move(bitmapShader), alloc);
    268     if (nullptr == ctx->fBitmapShaderContext) {
    269         ctx = nullptr;
    270     }
    271     return ctx;
    272 }
    273 
    274 /////////////////////////////////////////////////////////////////////////////////////////
    275 
    276 SkPictureShader::PictureShaderContext::PictureShaderContext(
    277         const SkPictureShader& shader, const ContextRec& rec, sk_sp<SkShader> bitmapShader,
    278         SkArenaAlloc* alloc)
    279     : INHERITED(shader, rec)
    280     , fBitmapShader(std::move(bitmapShader))
    281 {
    282     fBitmapShaderContext = fBitmapShader->makeContext(rec, alloc);
    283     //if fBitmapShaderContext is null, we are invalid
    284 }
    285 
    286 uint32_t SkPictureShader::PictureShaderContext::getFlags() const {
    287     SkASSERT(fBitmapShaderContext);
    288     return fBitmapShaderContext->getFlags();
    289 }
    290 
    291 SkShader::Context::ShadeProc SkPictureShader::PictureShaderContext::asAShadeProc(void** ctx) {
    292     SkASSERT(fBitmapShaderContext);
    293     return fBitmapShaderContext->asAShadeProc(ctx);
    294 }
    295 
    296 void SkPictureShader::PictureShaderContext::shadeSpan(int x, int y, SkPMColor dstC[], int count) {
    297     SkASSERT(fBitmapShaderContext);
    298     fBitmapShaderContext->shadeSpan(x, y, dstC, count);
    299 }
    300 
    301 #ifndef SK_IGNORE_TO_STRING
    302 void SkPictureShader::toString(SkString* str) const {
    303     static const char* gTileModeName[SkShader::kTileModeCount] = {
    304         "clamp", "repeat", "mirror"
    305     };
    306 
    307     str->appendf("PictureShader: [%f:%f:%f:%f] ",
    308                  fPicture->cullRect().fLeft,
    309                  fPicture->cullRect().fTop,
    310                  fPicture->cullRect().fRight,
    311                  fPicture->cullRect().fBottom);
    312 
    313     str->appendf("(%s, %s)", gTileModeName[fTmx], gTileModeName[fTmy]);
    314 
    315     this->INHERITED::toString(str);
    316 }
    317 #endif
    318 
    319 #if SK_SUPPORT_GPU
    320 sk_sp<GrFragmentProcessor> SkPictureShader::asFragmentProcessor(const AsFPArgs& args) const {
    321     int maxTextureSize = 0;
    322     if (args.fContext) {
    323         maxTextureSize = args.fContext->caps()->maxTextureSize();
    324     }
    325     sk_sp<SkShader> bitmapShader(this->refBitmapShader(*args.fViewMatrix, args.fLocalMatrix,
    326                                                        args.fDstColorSpace, maxTextureSize));
    327     if (!bitmapShader) {
    328         return nullptr;
    329     }
    330     return bitmapShader->asFragmentProcessor(SkShader::AsFPArgs(
    331         args.fContext, args.fViewMatrix, nullptr, args.fFilterQuality, args.fDstColorSpace));
    332 }
    333 #endif
    334