aboutsummaryrefslogtreecommitdiffhomepage
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/core/SkPixmap.cpp67
-rw-r--r--src/shaders/SkImageShader.cpp46
-rw-r--r--src/shaders/SkImageShader.h30
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;
};