diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/core/SkPixmap.cpp | 67 | ||||
-rw-r--r-- | src/shaders/SkImageShader.cpp | 46 | ||||
-rw-r--r-- | src/shaders/SkImageShader.h | 30 |
3 files changed, 86 insertions, 57 deletions
diff --git a/src/core/SkPixmap.cpp b/src/core/SkPixmap.cpp index 42e2c66029..050c6e2ab4 100644 --- a/src/core/SkPixmap.cpp +++ b/src/core/SkPixmap.cpp @@ -11,6 +11,7 @@ #include "SkConvertPixels.h" #include "SkData.h" #include "SkImageInfoPriv.h" +#include "SkImageShader.h" #include "SkHalf.h" #include "SkMask.h" #include "SkNx.h" @@ -229,10 +230,6 @@ bool SkPixmap::erase(const SkColor4f& origColor, const SkIRect* subset) const { return true; } -static void set_alphatype(SkPixmap* dst, const SkPixmap& src, SkAlphaType at) { - dst->reset(src.info().makeAlphaType(at), src.addr(), src.rowBytes()); -} - bool SkPixmap::scalePixels(const SkPixmap& dst, SkFilterQuality quality) const { // Can't do anthing with empty src or dst if (this->width() <= 0 || this->height() <= 0 || dst.width() <= 0 || dst.height() <= 0) { @@ -244,39 +241,51 @@ bool SkPixmap::scalePixels(const SkPixmap& dst, SkFilterQuality quality) const { return this->readPixels(dst); } - // Temp storage in case we need to edit the requested alphatypes - SkPixmap storage_src, storage_dst; - const SkPixmap* srcPtr = this; - const SkPixmap* dstPtr = &dst; - - // Trick: if src and dst are both unpremul, we can give the correct result if we change both - // to premul (or opaque), since the draw will not try to blend or otherwise interpret - // the pixels' alpha. - if (srcPtr->alphaType() == kUnpremul_SkAlphaType && - dstPtr->alphaType() == kUnpremul_SkAlphaType) - { - set_alphatype(&storage_src, *this, kPremul_SkAlphaType); - set_alphatype(&storage_dst, dst, kPremul_SkAlphaType); - srcPtr = &storage_src; - dstPtr = &storage_dst; - } - SkBitmap bitmap; - if (!bitmap.installPixels(*srcPtr)) { + if (!bitmap.installPixels(*this)) { return false; } - bitmap.setIsVolatile(true); // so we don't try to cache it - - auto surface(SkSurface::MakeRasterDirect(dstPtr->info(), dstPtr->writable_addr(), dstPtr->rowBytes())); - if (!surface) { + bitmap.setImmutable(); // Don't copy when we create an image. + bitmap.setIsVolatile(true); // Disable any caching. + + // We're going to set this up a little oddly so that we can scale unpremul SkPixmaps + // to unpremul dsts without ever premultiplying (and potentially throwing away information). + + // 1) Here's the scale matrix between our input pixels and the scaled destination. + SkMatrix matrix = SkMatrix::MakeRectToRect(SkRect::Make(this->bounds()), + SkRect::Make(dst.bounds()), + SkMatrix::kFill_ScaleToFit); + + + // 2) Instead of just calling drawBitmap(), we'll create a shader that carefully + // keeps its output in the alpha type we request, dst.alphaType(). + sk_sp<SkShader> shader = SkImageShader::Make(SkImage::MakeFromBitmap(bitmap), + SkShader::kClamp_TileMode, + SkShader::kClamp_TileMode, + &matrix, + dst.alphaType()); + + // 3) No matter what dst's actual alpha type is, we'll construct this surface + // as if it were not unpremul. This keeps the rest of the drawing pipeline oblivious + // to our trickery here, and prevents it from doing anything like a manual unpremul. + SkImageInfo info = dst.info(); + if (info.alphaType() == kUnpremul_SkAlphaType) { + info = info.makeAlphaType(kPremul_SkAlphaType); + } + sk_sp<SkSurface> surface = + SkSurface::MakeRasterDirect(info, dst.writable_addr(), dst.rowBytes()); + if (!shader || !surface) { return false; } + // 4) Using SkBlendMode::kSrc means we won't be tempted to try any premul-only blending math. SkPaint paint; - paint.setFilterQuality(quality); paint.setBlendMode(SkBlendMode::kSrc); - surface->getCanvas()->drawBitmapRect(bitmap, SkRect::MakeIWH(dst.width(), dst.height()), - &paint); + paint.setFilterQuality(quality); + paint.setShader(std::move(shader)); + + // 5) Draw it! + surface->getCanvas()->drawPaint(paint); return true; } diff --git a/src/shaders/SkImageShader.cpp b/src/shaders/SkImageShader.cpp index 1f77158206..3e58782aee 100644 --- a/src/shaders/SkImageShader.cpp +++ b/src/shaders/SkImageShader.cpp @@ -31,23 +31,30 @@ static SkShader::TileMode optimize(SkShader::TileMode tm, int dimension) { #endif } -SkImageShader::SkImageShader(sk_sp<SkImage> img, TileMode tmx, TileMode tmy, const SkMatrix* matrix) - : INHERITED(matrix) +SkImageShader::SkImageShader(sk_sp<SkImage> img, + TileMode tmx, TileMode tmy, + const SkMatrix* localMatrix, + SkAlphaType outputAlphaType) + : INHERITED(localMatrix) , fImage(std::move(img)) , fTileModeX(optimize(tmx, fImage->width())) , fTileModeY(optimize(tmy, fImage->height())) + , fOutputAlphaType(outputAlphaType) {} +// fOutputAlphaType is always kPremul_SkAlphaType when an SkImageShader is constructed +// through public APIs that might lead to serialization, so we don't read or write it. + sk_sp<SkFlattenable> SkImageShader::CreateProc(SkReadBuffer& buffer) { const TileMode tx = (TileMode)buffer.readUInt(); const TileMode ty = (TileMode)buffer.readUInt(); - SkMatrix matrix; - buffer.readMatrix(&matrix); + SkMatrix localMatrix; + buffer.readMatrix(&localMatrix); sk_sp<SkImage> img = buffer.readImage(); if (!img) { return nullptr; } - return SkImageShader::Make(std::move(img), tx, ty, &matrix); + return SkImageShader::Make(std::move(img), tx, ty, &localMatrix); } void SkImageShader::flatten(SkWriteBuffer& buffer) const { @@ -55,6 +62,7 @@ void SkImageShader::flatten(SkWriteBuffer& buffer) const { buffer.writeUInt(fTileModeY); buffer.writeMatrix(this->getLocalMatrix()); buffer.writeImage(fImage.get()); + SkASSERT(fOutputAlphaType == kPremul_SkAlphaType); } bool SkImageShader::isOpaque() const { @@ -164,13 +172,14 @@ static bool bitmap_is_too_big(int w, int h) { return w > kMaxSize || h > kMaxSize; } -sk_sp<SkShader> SkImageShader::Make(sk_sp<SkImage> image, TileMode tx, TileMode ty, - const SkMatrix* localMatrix) { +sk_sp<SkShader> SkImageShader::Make(sk_sp<SkImage> image, + TileMode tx, TileMode ty, + const SkMatrix* localMatrix, + SkAlphaType outputAlphaType) { if (!image || bitmap_is_too_big(image->width(), image->height())) { return sk_make_sp<SkEmptyShader>(); - } else { - return sk_make_sp<SkImageShader>(image, tx, ty, localMatrix); } + return sk_sp<SkShader>{ new SkImageShader(image, tx,ty, localMatrix, outputAlphaType) }; } #ifndef SK_IGNORE_TO_STRING @@ -385,19 +394,24 @@ bool SkImageShader::onAppendStages(const StageRec& rec) const { auto append_misc = [&] { if (info.colorType() == kAlpha_8_SkColorType) { p->append(SkRasterPipeline::set_rgb, &misc->paint_color); - } - if (info.colorType() == kAlpha_8_SkColorType || - info.alphaType() == kUnpremul_SkAlphaType) { p->append(SkRasterPipeline::premul); + } else if (info.alphaType() == kUnpremul_SkAlphaType && + fOutputAlphaType == kPremul_SkAlphaType) { + p->append(SkRasterPipeline::premul); + } else if (info.alphaType() == kPremul_SkAlphaType && + fOutputAlphaType == kUnpremul_SkAlphaType) { + p->append(SkRasterPipeline::unpremul); } -#if defined(SK_LEGACY_HIGH_QUALITY_SCALING_CLAMP) + + #if defined(SK_LEGACY_HIGH_QUALITY_SCALING_CLAMP) if (quality > kLow_SkFilterQuality) { // Bicubic filtering naturally produces out of range values on both sides. p->append(SkRasterPipeline::clamp_0); - p->append(SkRasterPipeline::clamp_a); + p->append(fOutputAlphaType == kPremul_SkAlphaType ? SkRasterPipeline::clamp_a + : SkRasterPipeline::clamp_1); } -#endif - append_gamut_transform(p, alloc, info.colorSpace(), rec.fDstCS, kPremul_SkAlphaType); + #endif + append_gamut_transform(p, alloc, info.colorSpace(), rec.fDstCS, fOutputAlphaType); return true; }; diff --git a/src/shaders/SkImageShader.h b/src/shaders/SkImageShader.h index a3cf3dbe7f..9ce615508d 100644 --- a/src/shaders/SkImageShader.h +++ b/src/shaders/SkImageShader.h @@ -15,8 +15,11 @@ class SkImageShader : public SkShaderBase { public: - static sk_sp<SkShader> Make(sk_sp<SkImage>, TileMode tx, TileMode ty, - const SkMatrix* localMatrix); + static sk_sp<SkShader> Make(sk_sp<SkImage>, + SkShader::TileMode tx, + SkShader::TileMode ty, + const SkMatrix* localMatrix, + SkAlphaType outputAlphaType = kPremul_SkAlphaType); bool isOpaque() const override; @@ -27,19 +30,23 @@ public: std::unique_ptr<GrFragmentProcessor> asFragmentProcessor(const GrFPArgs&) const override; #endif - SkImageShader(sk_sp<SkImage>, TileMode tx, TileMode ty, const SkMatrix* localMatrix); - static bool IsRasterPipelineOnly(const SkMatrix& ctm, SkColorType, SkAlphaType, SkShader::TileMode tx, SkShader::TileMode ty, const SkMatrix& localM); -protected: +private: + SkImageShader(sk_sp<SkImage>, + SkShader::TileMode tx, + SkShader::TileMode ty, + const SkMatrix* localMatrix, + SkAlphaType outputAlphaType); + void flatten(SkWriteBuffer&) const override; Context* onMakeContext(const ContextRec&, SkArenaAlloc* storage) const override; #ifdef SK_SUPPORT_LEGACY_SHADER_ISABITMAP - bool onIsABitmap(SkBitmap*, SkMatrix*, TileMode*) const override; + bool onIsABitmap(SkBitmap*, SkMatrix*, SkShader::TileMode*) const override; #endif - SkImage* onIsAImage(SkMatrix*, TileMode*) const override; + SkImage* onIsAImage(SkMatrix*, SkShader::TileMode*) const override; bool onIsRasterPipelineOnly(const SkMatrix& ctm) const override; @@ -50,13 +57,12 @@ protected: &this->getLocalMatrix()); } - sk_sp<SkImage> fImage; - const TileMode fTileModeX; - const TileMode fTileModeY; + sk_sp<SkImage> fImage; + const SkShader::TileMode fTileModeX; + const SkShader::TileMode fTileModeY; + const SkAlphaType fOutputAlphaType; -private: friend class SkShaderBase; - typedef SkShaderBase INHERITED; }; |