Do not lose existing paint shaders when drawing alpha images
This fixes the case where SkBitmapDevice or SkDraw falls back to use an SkShader
to draw the image. Previously it just assigned a new shader to a paint clone,
which worked fine with color images that are defined to ignore any set shader.
However, with alpha images it would overwrite the effect. This changes it to
compose the image shader with the original shader.
It fixes the behavior of GMs bleed_alpha_image_shader, bleed_alpha_bmp_shader,
and alpha_image.
Bug: skia:
Change-Id: Iaf0552ddb9991dd8d414b76f625a45cd167b7070
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/190677
Commit-Queue: Michael Ludwig <michaelludwig@google.com>
Reviewed-by: Mike Reed <reed@google.com>
diff --git a/src/core/SkBitmapDevice.cpp b/src/core/SkBitmapDevice.cpp
index d2c45ae..7bb14d2 100644
--- a/src/core/SkBitmapDevice.cpp
+++ b/src/core/SkBitmapDevice.cpp
@@ -518,15 +518,15 @@
// if its mutable, since that precaution is not needed (give the short lifetime of the shader).
// construct a shader, so we can call drawRect with the dst
- auto s = SkMakeBitmapShader(*bitmapPtr, SkShader::kClamp_TileMode, SkShader::kClamp_TileMode,
- &matrix, kNever_SkCopyPixelsMode);
+ auto s = SkMakeBitmapShaderForPaint(paint, *bitmapPtr, SkShader::kClamp_TileMode,
+ SkShader::kClamp_TileMode, &matrix, kNever_SkCopyPixelsMode);
if (!s) {
return;
}
SkPaint paintWithShader(paint);
paintWithShader.setStyle(SkPaint::kFill_Style);
- paintWithShader.setShader(s);
+ paintWithShader.setShader(std::move(s));
// Call ourself, in case the subclass wanted to share this setup code
// but handle the drawRect code themselves.
diff --git a/src/core/SkDraw.cpp b/src/core/SkDraw.cpp
index 6578d80..67d0622 100644
--- a/src/core/SkDraw.cpp
+++ b/src/core/SkDraw.cpp
@@ -40,9 +40,8 @@
static SkPaint make_paint_with_image(
const SkPaint& origPaint, const SkBitmap& bitmap, SkMatrix* matrix = nullptr) {
SkPaint paint(origPaint);
- paint.setShader(SkMakeBitmapShader(bitmap, SkShader::kClamp_TileMode,
- SkShader::kClamp_TileMode, matrix,
- kNever_SkCopyPixelsMode));
+ paint.setShader(SkMakeBitmapShaderForPaint(origPaint, bitmap, SkShader::kClamp_TileMode,
+ SkShader::kClamp_TileMode, matrix, kNever_SkCopyPixelsMode));
return paint;
}
diff --git a/src/core/SkImagePriv.h b/src/core/SkImagePriv.h
index 1fd7675..46a9370 100644
--- a/src/core/SkImagePriv.h
+++ b/src/core/SkImagePriv.h
@@ -25,6 +25,14 @@
sk_sp<SkShader> SkMakeBitmapShader(const SkBitmap& src, SkShader::TileMode, SkShader::TileMode,
const SkMatrix* localMatrix, SkCopyPixelsMode);
+// Convenience function to return a shader that implements the shader+image behavior defined for
+// drawImage/Bitmap where the paint's shader is ignored when the bitmap is a color image, but
+// properly compose them together when it is an alpha image. This allows the returned paint to
+// be assigned to a paint clone without discarding the original behavior.
+sk_sp<SkShader> SkMakeBitmapShaderForPaint(const SkPaint& paint, const SkBitmap& src,
+ SkShader::TileMode, SkShader::TileMode,
+ const SkMatrix* localMatrix, SkCopyPixelsMode);
+
/**
* Examines the bitmap to decide if it can share the existing pixelRef, or
* if it needs to make a deep-copy of the pixels.
diff --git a/src/shaders/SkImageShader.cpp b/src/shaders/SkImageShader.cpp
index b35da85..fa4d9b7 100644
--- a/src/shaders/SkImageShader.cpp
+++ b/src/shaders/SkImageShader.cpp
@@ -272,6 +272,22 @@
tmx, tmy, localMatrix);
}
+sk_sp<SkShader> SkMakeBitmapShaderForPaint(const SkPaint& paint, const SkBitmap& src,
+ SkShader::TileMode tmx, SkShader::TileMode tmy,
+ const SkMatrix* localMatrix, SkCopyPixelsMode mode) {
+ auto s = SkMakeBitmapShader(src, tmx, tmy, localMatrix, mode);
+ if (!s) {
+ return nullptr;
+ }
+ if (src.colorType() == kAlpha_8_SkColorType && paint.getShader()) {
+ // Compose the image shader with the paint's shader. Alpha images+shaders should output the
+ // texture's alpha multiplied by the shader's color. DstIn (d*sa) will achieve this with
+ // the source image and dst shader (MakeBlend takes dst first, src second).
+ s = SkShader::MakeBlend(SkBlendMode::kDstIn, paint.refShader(), std::move(s));
+ }
+ return s;
+}
+
void SkShaderBase::RegisterFlattenables() { SK_REGISTER_FLATTENABLE(SkImageShader); }
bool SkImageShader::onAppendStages(const SkStageRec& rec) const {
diff --git a/src/xps/SkXPSDevice.cpp b/src/xps/SkXPSDevice.cpp
index ca1c663..98c40ac 100644
--- a/src/xps/SkXPSDevice.cpp
+++ b/src/xps/SkXPSDevice.cpp
@@ -2084,9 +2084,9 @@
}
matrix.mapRect(&actualDst, srcBounds);
}
- auto bitmapShader = SkMakeBitmapShader(bitmap, SkShader::kClamp_TileMode,
- SkShader::kClamp_TileMode, &matrix,
- kNever_SkCopyPixelsMode);
+ auto bitmapShader = SkMakeBitmapShaderForPaint(paint, bitmap, SkShader::kClamp_TileMode,
+ SkShader::kClamp_TileMode, &matrix,
+ kNever_SkCopyPixelsMode);
SkASSERT(bitmapShader);
if (!bitmapShader) { return; }
SkPaint paintWithShader(paint);