diff options
author | Florin Malita <fmalita@chromium.org> | 2017-05-25 15:29:13 -0400 |
---|---|---|
committer | Skia Commit-Bot <skia-commit-bot@chromium.org> | 2017-05-25 20:05:08 +0000 |
commit | fabe0b26d05624ce7374f6ca89bd66df6142534e (patch) | |
tree | f32b5873b31185d7e86e6c48fbdbd654efb8af7a /src/shaders/SkPictureShader.cpp | |
parent | 1c214313248a4b5a69af14539608c54fb67c2bf8 (diff) |
Relocate shaders to own dir
Consolidate all shader impls under src/shaders/.
Change-Id: I450e37541214704c1ad9e379d9d753b7cc62fac3
Reviewed-on: https://skia-review.googlesource.com/17927
Commit-Queue: Florin Malita <fmalita@chromium.org>
Reviewed-by: Herb Derby <herb@google.com>
Diffstat (limited to 'src/shaders/SkPictureShader.cpp')
-rw-r--r-- | src/shaders/SkPictureShader.cpp | 364 |
1 files changed, 364 insertions, 0 deletions
diff --git a/src/shaders/SkPictureShader.cpp b/src/shaders/SkPictureShader.cpp new file mode 100644 index 0000000000..d6ee941251 --- /dev/null +++ b/src/shaders/SkPictureShader.cpp @@ -0,0 +1,364 @@ +/* + * Copyright 2014 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "SkPictureShader.h" + +#include "SkArenaAlloc.h" +#include "SkBitmap.h" +#include "SkBitmapProcShader.h" +#include "SkCanvas.h" +#include "SkColorSpaceXformCanvas.h" +#include "SkImage.h" +#include "SkImageShader.h" +#include "SkMatrixUtils.h" +#include "SkPicture.h" +#include "SkPictureImageGenerator.h" +#include "SkReadBuffer.h" +#include "SkResourceCache.h" + +#if SK_SUPPORT_GPU +#include "GrContext.h" +#include "GrCaps.h" +#include "GrFragmentProcessor.h" +#endif + +namespace { +static unsigned gBitmapSkaderKeyNamespaceLabel; + +struct BitmapShaderKey : public SkResourceCache::Key { +public: + BitmapShaderKey(sk_sp<SkColorSpace> colorSpace, + uint32_t pictureID, + const SkRect& tile, + SkShader::TileMode tmx, + SkShader::TileMode tmy, + const SkSize& scale, + const SkMatrix& localMatrix, + SkTransferFunctionBehavior blendBehavior) + : fColorSpace(std::move(colorSpace)) + , fPictureID(pictureID) + , fTile(tile) + , fTmx(tmx) + , fTmy(tmy) + , fScale(scale) + , fBlendBehavior(blendBehavior) { + + for (int i = 0; i < 9; ++i) { + fLocalMatrixStorage[i] = localMatrix[i]; + } + + static const size_t keySize = sizeof(fColorSpace) + + sizeof(fPictureID) + + sizeof(fTile) + + sizeof(fTmx) + sizeof(fTmy) + + sizeof(fScale) + + sizeof(fLocalMatrixStorage) + + sizeof(fBlendBehavior); + // This better be packed. + SkASSERT(sizeof(uint32_t) * (&fEndOfStruct - (uint32_t*)&fColorSpace) == keySize); + this->init(&gBitmapSkaderKeyNamespaceLabel, 0, keySize); + } + +private: + sk_sp<SkColorSpace> fColorSpace; + uint32_t fPictureID; + SkRect fTile; + SkShader::TileMode fTmx, fTmy; + SkSize fScale; + SkScalar fLocalMatrixStorage[9]; + SkTransferFunctionBehavior fBlendBehavior; + + SkDEBUGCODE(uint32_t fEndOfStruct;) +}; + +struct BitmapShaderRec : public SkResourceCache::Rec { + BitmapShaderRec(const BitmapShaderKey& key, SkShader* tileShader) + : fKey(key) + , fShader(SkRef(tileShader)) {} + + BitmapShaderKey fKey; + sk_sp<SkShader> fShader; + size_t fBitmapBytes; + + const Key& getKey() const override { return fKey; } + size_t bytesUsed() const override { + // Just the record overhead -- the actual pixels are accounted by SkImageCacherator. + return sizeof(fKey) + sizeof(SkImageShader); + } + const char* getCategory() const override { return "bitmap-shader"; } + SkDiscardableMemory* diagnostic_only_getDiscardable() const override { return nullptr; } + + static bool Visitor(const SkResourceCache::Rec& baseRec, void* contextShader) { + const BitmapShaderRec& rec = static_cast<const BitmapShaderRec&>(baseRec); + sk_sp<SkShader>* result = reinterpret_cast<sk_sp<SkShader>*>(contextShader); + + *result = rec.fShader; + + // The bitmap shader is backed by an image generator, thus it can always re-generate its + // pixels if discarded. + return true; + } +}; + +} // namespace + +SkPictureShader::SkPictureShader(sk_sp<SkPicture> picture, TileMode tmx, TileMode tmy, + const SkMatrix* localMatrix, const SkRect* tile, + sk_sp<SkColorSpace> colorSpace) + : INHERITED(localMatrix) + , fPicture(std::move(picture)) + , fTile(tile ? *tile : fPicture->cullRect()) + , fTmx(tmx) + , fTmy(tmy) + , fColorSpace(std::move(colorSpace)) +{} + +sk_sp<SkShader> SkPictureShader::Make(sk_sp<SkPicture> picture, TileMode tmx, TileMode tmy, + const SkMatrix* localMatrix, const SkRect* tile) { + if (!picture || picture->cullRect().isEmpty() || (tile && tile->isEmpty())) { + return SkShader::MakeEmptyShader(); + } + return sk_sp<SkShader>(new SkPictureShader(std::move(picture), tmx, tmy, localMatrix, tile, + nullptr)); +} + +sk_sp<SkFlattenable> SkPictureShader::CreateProc(SkReadBuffer& buffer) { + SkMatrix lm; + buffer.readMatrix(&lm); + TileMode mx = (TileMode)buffer.read32(); + TileMode my = (TileMode)buffer.read32(); + SkRect tile; + buffer.readRect(&tile); + + sk_sp<SkPicture> picture; + + if (buffer.isCrossProcess() && SkPicture::PictureIOSecurityPrecautionsEnabled()) { + if (buffer.isVersionLT(SkReadBuffer::kPictureShaderHasPictureBool_Version)) { + // Older code blindly serialized pictures. We don't trust them. + buffer.validate(false); + return nullptr; + } + // Newer code won't serialize pictures in disallow-cross-process-picture mode. + // Assert that they didn't serialize anything except a false here. + buffer.validate(!buffer.readBool()); + } else { + // Old code always serialized the picture. New code writes a 'true' first if it did. + if (buffer.isVersionLT(SkReadBuffer::kPictureShaderHasPictureBool_Version) || + buffer.readBool()) { + picture = SkPicture::MakeFromBuffer(buffer); + } + } + return SkPictureShader::Make(picture, mx, my, &lm, &tile); +} + +void SkPictureShader::flatten(SkWriteBuffer& buffer) const { + buffer.writeMatrix(this->getLocalMatrix()); + buffer.write32(fTmx); + buffer.write32(fTmy); + buffer.writeRect(fTile); + + // The deserialization code won't trust that our serialized picture is safe to deserialize. + // So write a 'false' telling it that we're not serializing a picture. + if (buffer.isCrossProcess() && SkPicture::PictureIOSecurityPrecautionsEnabled()) { + buffer.writeBool(false); + } else { + buffer.writeBool(true); + fPicture->flatten(buffer); + } +} + +sk_sp<SkShader> SkPictureShader::refBitmapShader(const SkMatrix& viewMatrix, const SkMatrix* localM, + SkColorSpace* dstColorSpace, + const int maxTextureSize) const { + SkASSERT(fPicture && !fPicture->cullRect().isEmpty()); + + SkMatrix m; + m.setConcat(viewMatrix, this->getLocalMatrix()); + if (localM) { + m.preConcat(*localM); + } + + // Use a rotation-invariant scale + SkPoint scale; + // + // TODO: replace this with decomposeScale() -- but beware LayoutTest rebaselines! + // + if (!SkDecomposeUpper2x2(m, nullptr, &scale, nullptr)) { + // Decomposition failed, use an approximation. + scale.set(SkScalarSqrt(m.getScaleX() * m.getScaleX() + m.getSkewX() * m.getSkewX()), + SkScalarSqrt(m.getScaleY() * m.getScaleY() + m.getSkewY() * m.getSkewY())); + } + SkSize scaledSize = SkSize::Make(SkScalarAbs(scale.x() * fTile.width()), + SkScalarAbs(scale.y() * fTile.height())); + + // Clamp the tile size to about 4M pixels + static const SkScalar kMaxTileArea = 2048 * 2048; + SkScalar tileArea = scaledSize.width() * scaledSize.height(); + if (tileArea > kMaxTileArea) { + SkScalar clampScale = SkScalarSqrt(kMaxTileArea / tileArea); + scaledSize.set(scaledSize.width() * clampScale, + scaledSize.height() * clampScale); + } +#if SK_SUPPORT_GPU + // Scale down the tile size if larger than maxTextureSize for GPU Path or it should fail on create texture + if (maxTextureSize) { + if (scaledSize.width() > maxTextureSize || scaledSize.height() > maxTextureSize) { + SkScalar downScale = maxTextureSize / SkMaxScalar(scaledSize.width(), scaledSize.height()); + scaledSize.set(SkScalarFloorToScalar(scaledSize.width() * downScale), + SkScalarFloorToScalar(scaledSize.height() * downScale)); + } + } +#endif + +#ifdef SK_SUPPORT_LEGACY_PICTURESHADER_ROUNDING + const SkISize tileSize = scaledSize.toRound(); +#else + const SkISize tileSize = scaledSize.toCeil(); +#endif + if (tileSize.isEmpty()) { + return SkShader::MakeEmptyShader(); + } + + // The actual scale, compensating for rounding & clamping. + const SkSize tileScale = SkSize::Make(SkIntToScalar(tileSize.width()) / fTile.width(), + SkIntToScalar(tileSize.height()) / fTile.height()); + + // |fColorSpace| will only be set when using an SkColorSpaceXformCanvas to do pre-draw xforms. + // This canvas is strictly for legacy mode. A non-null |dstColorSpace| indicates that we + // should perform color correct rendering and xform at draw time. + SkASSERT(!fColorSpace || !dstColorSpace); + sk_sp<SkColorSpace> keyCS = dstColorSpace ? sk_ref_sp(dstColorSpace) : fColorSpace; + SkTransferFunctionBehavior blendBehavior = dstColorSpace ? SkTransferFunctionBehavior::kRespect + : SkTransferFunctionBehavior::kIgnore; + + sk_sp<SkShader> tileShader; + BitmapShaderKey key(std::move(keyCS), + fPicture->uniqueID(), + fTile, + fTmx, + fTmy, + tileScale, + this->getLocalMatrix(), + blendBehavior); + + if (!SkResourceCache::Find(key, BitmapShaderRec::Visitor, &tileShader)) { + SkMatrix tileMatrix; + tileMatrix.setRectToRect(fTile, SkRect::MakeIWH(tileSize.width(), tileSize.height()), + SkMatrix::kFill_ScaleToFit); + + sk_sp<SkImage> tileImage = SkImage::MakeFromGenerator( + SkPictureImageGenerator::Make(tileSize, fPicture, &tileMatrix, nullptr, + SkImage::BitDepth::kU8, sk_ref_sp(dstColorSpace))); + if (!tileImage) { + return nullptr; + } + + if (fColorSpace) { + tileImage = tileImage->makeColorSpace(fColorSpace, SkTransferFunctionBehavior::kIgnore); + } + + SkMatrix shaderMatrix = this->getLocalMatrix(); + shaderMatrix.preScale(1 / tileScale.width(), 1 / tileScale.height()); + tileShader = tileImage->makeShader(fTmx, fTmy, &shaderMatrix); + + SkResourceCache::Add(new BitmapShaderRec(key, tileShader.get())); + } + + return tileShader; +} + +bool SkPictureShader::onAppendStages(SkRasterPipeline* p, SkColorSpace* cs, SkArenaAlloc* alloc, + const SkMatrix& ctm, const SkPaint& paint, + const SkMatrix* localMatrix) const { + // Keep bitmapShader alive by using alloc instead of stack memory + auto& bitmapShader = *alloc->make<sk_sp<SkShader>>(); + bitmapShader = this->refBitmapShader(ctm, localMatrix, cs); + return bitmapShader && as_SB(bitmapShader)->appendStages(p, cs, alloc, ctm, paint); +} + +///////////////////////////////////////////////////////////////////////////////////////// +SkShaderBase::Context* SkPictureShader::onMakeContext(const ContextRec& rec, SkArenaAlloc* alloc) +const { + sk_sp<SkShader> bitmapShader(this->refBitmapShader(*rec.fMatrix, rec.fLocalMatrix, + rec.fDstColorSpace)); + if (!bitmapShader) { + return nullptr; + } + + PictureShaderContext* ctx = + alloc->make<PictureShaderContext>(*this, rec, std::move(bitmapShader), alloc); + if (nullptr == ctx->fBitmapShaderContext) { + ctx = nullptr; + } + return ctx; +} + +sk_sp<SkShader> SkPictureShader::onMakeColorSpace(SkColorSpaceXformer* xformer) const { + return sk_sp<SkPictureShader>(new SkPictureShader(fPicture, fTmx, fTmy, &this->getLocalMatrix(), + &fTile, xformer->dst())); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +SkPictureShader::PictureShaderContext::PictureShaderContext( + const SkPictureShader& shader, const ContextRec& rec, sk_sp<SkShader> bitmapShader, + SkArenaAlloc* alloc) + : INHERITED(shader, rec) + , fBitmapShader(std::move(bitmapShader)) +{ + fBitmapShaderContext = as_SB(fBitmapShader)->makeContext(rec, alloc); + //if fBitmapShaderContext is null, we are invalid +} + +uint32_t SkPictureShader::PictureShaderContext::getFlags() const { + SkASSERT(fBitmapShaderContext); + return fBitmapShaderContext->getFlags(); +} + +SkShaderBase::Context::ShadeProc SkPictureShader::PictureShaderContext::asAShadeProc(void** ctx) { + SkASSERT(fBitmapShaderContext); + return fBitmapShaderContext->asAShadeProc(ctx); +} + +void SkPictureShader::PictureShaderContext::shadeSpan(int x, int y, SkPMColor dstC[], int count) { + SkASSERT(fBitmapShaderContext); + fBitmapShaderContext->shadeSpan(x, y, dstC, count); +} + +#ifndef SK_IGNORE_TO_STRING +void SkPictureShader::toString(SkString* str) const { + static const char* gTileModeName[SkShader::kTileModeCount] = { + "clamp", "repeat", "mirror" + }; + + str->appendf("PictureShader: [%f:%f:%f:%f] ", + fPicture->cullRect().fLeft, + fPicture->cullRect().fTop, + fPicture->cullRect().fRight, + fPicture->cullRect().fBottom); + + str->appendf("(%s, %s)", gTileModeName[fTmx], gTileModeName[fTmy]); + + this->INHERITED::toString(str); +} +#endif + +#if SK_SUPPORT_GPU +sk_sp<GrFragmentProcessor> SkPictureShader::asFragmentProcessor(const AsFPArgs& args) const { + int maxTextureSize = 0; + if (args.fContext) { + maxTextureSize = args.fContext->caps()->maxTextureSize(); + } + sk_sp<SkShader> bitmapShader(this->refBitmapShader(*args.fViewMatrix, args.fLocalMatrix, + args.fDstColorSpace, maxTextureSize)); + if (!bitmapShader) { + return nullptr; + } + return as_SB(bitmapShader)->asFragmentProcessor(SkShaderBase::AsFPArgs( + args.fContext, args.fViewMatrix, nullptr, args.fFilterQuality, args.fDstColorSpace)); +} +#endif |