Generalize composing imagefilters and shaders to blenders
The preexisting enum versions now are just shallow factories for
the new blender versions, though internally we've kept the
specializations on impl.
Change-Id: I3449682bb443a4ff9f53cc7b0860343c56e377e4
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/424436
Commit-Queue: Mike Reed <reed@google.com>
Reviewed-by: John Stiles <johnstiles@google.com>
Reviewed-by: Brian Osman <brianosman@google.com>
diff --git a/gm/arithmode.cpp b/gm/arithmode.cpp
index 8f68c74..3e8a8a8 100644
--- a/gm/arithmode.cpp
+++ b/gm/arithmode.cpp
@@ -195,7 +195,7 @@
fSrc = make_src(W, H);
fDst = make_dst(W, H);
- fChecker = ToolUtils::create_checkerboard_image(W, H, 0xFF999999, 0xFFCCCCCC, 8);
+ fChecker = ToolUtils::create_checkerboard_image(W, H, 0xFFBBBBBB, 0xFFEEEEEE, 8);
}
bool onAnimate(double nanos) override {
@@ -213,16 +213,30 @@
canvas->drawImage(fSrc, 10, 10);
canvas->drawImage(fDst, 10, 10 + fSrc->height() + 10);
+ auto sampling = SkSamplingOptions();
+ auto blender = SkBlenders::Arithmetic(fK1, fK2, fK3, fK4, true);
+
+ SkPaint paint;
+
canvas->translate(10 + fSrc->width() + 10, 10);
canvas->drawImage(fChecker, 0, 0);
- SkPaint paint;
- paint.setBlender(SkBlenders::Arithmetic(fK1, fK2, fK3, fK4, true));
-
+ // Draw via blend step
canvas->saveLayer(&rect, nullptr);
canvas->drawImage(fDst, 0, 0);
- canvas->drawImage(fSrc, 0, 0, SkSamplingOptions(), &paint);
+ paint.setBlender(blender);
+ canvas->drawImage(fSrc, 0, 0, sampling, &paint);
canvas->restore();
+
+ canvas->translate(0, 10 + fSrc->height());
+ canvas->drawImage(fChecker, 0, 0);
+
+ // Draw via imagefilter (should appear the same as above)
+ paint.setBlender(nullptr);
+ paint.setImageFilter(SkImageFilters::Blend(blender,
+ /* dst imagefilter */nullptr,
+ SkImageFilters::Image(fSrc, sampling)));
+ canvas->drawImage(fDst, 0, 0, sampling, &paint);
}
private:
diff --git a/include/core/SkShader.h b/include/core/SkShader.h
index eccb545..fba17f8 100644
--- a/include/core/SkShader.h
+++ b/include/core/SkShader.h
@@ -17,6 +17,7 @@
class SkArenaAlloc;
class SkBitmap;
+class SkBlender;
class SkColorFilter;
class SkColorSpace;
class SkImage;
@@ -138,6 +139,7 @@
static sk_sp<SkShader> Color(SkColor);
static sk_sp<SkShader> Color(const SkColor4f&, sk_sp<SkColorSpace>);
static sk_sp<SkShader> Blend(SkBlendMode mode, sk_sp<SkShader> dst, sk_sp<SkShader> src);
+ static sk_sp<SkShader> Blend(sk_sp<SkBlender>, sk_sp<SkShader> dst, sk_sp<SkShader> src);
private:
SkShaders() = delete;
diff --git a/include/effects/SkImageFilters.h b/include/effects/SkImageFilters.h
index 9e30b82..bc79cf9 100644
--- a/include/effects/SkImageFilters.h
+++ b/include/effects/SkImageFilters.h
@@ -18,6 +18,7 @@
#include <cstddef>
+class SkBlender;
class SkColorFilter;
class SkPaint;
class SkRegion;
@@ -97,6 +98,17 @@
const CropRect& cropRect = {});
/**
+ * This filter takes an SkBlendMode and uses it to composite the two filters together.
+ * @param blender The blender that defines the compositing operation
+ * @param background The Dst pixels used in blending, if null the source bitmap is used.
+ * @param foreground The Src pixels used in blending, if null the source bitmap is used.
+ * @cropRect Optional rectangle to crop input and output.
+ */
+ static sk_sp<SkImageFilter> Blend(sk_sp<SkBlender> blender, sk_sp<SkImageFilter> background,
+ sk_sp<SkImageFilter> foreground = nullptr,
+ const CropRect& cropRect = {});
+
+ /**
* Create a filter that blurs its input by the separate X and Y sigmas. The provided tile mode
* is used when the blur kernel goes outside the input image.
* @param sigmaX The Gaussian sigma value for blurring along the X axis.
diff --git a/src/core/SkBlendModePriv.h b/src/core/SkBlendModePriv.h
index b19e565..f24c8ca 100644
--- a/src/core/SkBlendModePriv.h
+++ b/src/core/SkBlendModePriv.h
@@ -14,6 +14,13 @@
class SkRasterPipeline;
+/**
+ * Sentinel value for SkBlendMode enum.
+ *
+ * Will never be a valid enum value, but will be storable in a byte.
+ */
+constexpr uint8_t kCustom_SkBlendMode = 0xFF;
+
bool SkBlendMode_SupportsCoverageAsAlpha(SkBlendMode);
static inline bool SkBlendMode_CaresAboutRBOrder(SkBlendMode mode) {
diff --git a/src/core/SkPicturePriv.h b/src/core/SkPicturePriv.h
index 871943f..657f2fe 100644
--- a/src/core/SkPicturePriv.h
+++ b/src/core/SkPicturePriv.h
@@ -97,6 +97,7 @@
// V85: Remove legacy support for inheriting sampling from the paint.
// V86: Remove support for custom data inside SkVertices
// V87: SkPaint now holds a user-defined blend function (SkBlender), no longer has DrawLooper
+ // V88: Add blender to ComposeShader and BlendImageFilter
enum Version {
kPictureShaderFilterParam_Version = 82,
@@ -105,10 +106,11 @@
kNoFilterQualityShaders_Version = 85,
kVerticesRemoveCustomData_Version = 86,
kSkBlenderInSkPaint = 87,
+ kBlenderInEffects = 88,
// Only SKPs within the min/current picture version range (inclusive) can be read.
kMin_Version = kPictureShaderFilterParam_Version,
- kCurrent_Version = kSkBlenderInSkPaint
+ kCurrent_Version = kBlenderInEffects
};
};
diff --git a/src/effects/imagefilters/SkBlendImageFilter.cpp b/src/effects/imagefilters/SkBlendImageFilter.cpp
index a5a40f3..321387f 100644
--- a/src/effects/imagefilters/SkBlendImageFilter.cpp
+++ b/src/effects/imagefilters/SkBlendImageFilter.cpp
@@ -8,7 +8,10 @@
#include "include/core/SkCanvas.h"
#include "include/effects/SkImageFilters.h"
#include "include/private/SkColorData.h"
+#include "src/core/SkBlendModePriv.h"
+#include "src/core/SkBlenderBase.h"
#include "src/core/SkImageFilter_Base.h"
+#include "src/core/SkMatrixProvider.h"
#include "src/core/SkReadBuffer.h"
#include "src/core/SkSpecialImage.h"
#include "src/core/SkSpecialSurface.h"
@@ -30,10 +33,13 @@
class SkBlendImageFilter : public SkImageFilter_Base {
public:
- SkBlendImageFilter(SkBlendMode mode, sk_sp<SkImageFilter> inputs[2],
+ SkBlendImageFilter(sk_sp<SkBlender> blender, sk_sp<SkImageFilter> inputs[2],
const SkRect* cropRect)
: INHERITED(inputs, 2, cropRect)
- , fMode(mode) {}
+ , fBlender(std::move(blender))
+ {
+ SkASSERT(fBlender);
+ }
protected:
sk_sp<SkSpecialImage> onFilterImage(const Context&, SkIPoint* offset) const override;
@@ -58,7 +64,7 @@
friend void ::SkRegisterBlendImageFilterFlattenable();
SK_FLATTENABLE_HOOKS(SkBlendImageFilter)
- SkBlendMode fMode;
+ sk_sp<SkBlender> fBlender;
using INHERITED = SkImageFilter_Base;
};
@@ -70,7 +76,18 @@
sk_sp<SkImageFilter> foreground,
const CropRect& cropRect) {
sk_sp<SkImageFilter> inputs[2] = { std::move(background), std::move(foreground) };
- return sk_sp<SkImageFilter>(new SkBlendImageFilter(mode, inputs, cropRect));
+ return sk_sp<SkImageFilter>(new SkBlendImageFilter(SkBlender::Mode(mode), inputs, cropRect));
+}
+
+sk_sp<SkImageFilter> SkImageFilters::Blend(sk_sp<SkBlender> blender,
+ sk_sp<SkImageFilter> background,
+ sk_sp<SkImageFilter> foreground,
+ const CropRect& cropRect) {
+ if (!blender) {
+ blender = SkBlender::Mode(SkBlendMode::kSrcOver);
+ }
+ sk_sp<SkImageFilter> inputs[2] = { std::move(background), std::move(foreground) };
+ return sk_sp<SkImageFilter>(new SkBlendImageFilter(blender, inputs, cropRect));
}
void SkRegisterBlendImageFilterFlattenable() {
@@ -80,25 +97,32 @@
SkFlattenable::Register("SkXfermodeImageFilterImpl", SkBlendImageFilter::CreateProc);
}
-static unsigned unflatten_blendmode(SkReadBuffer& buffer) {
- unsigned mode = buffer.read32();
- (void)buffer.validate(mode <= (unsigned)SkBlendMode::kLastMode);
- return mode;
-}
-
sk_sp<SkFlattenable> SkBlendImageFilter::CreateProc(SkReadBuffer& buffer) {
SK_IMAGEFILTER_UNFLATTEN_COMMON(common, 2);
- unsigned mode = unflatten_blendmode(buffer);
- if (!buffer.isValid()) {
- return nullptr;
+
+ sk_sp<SkBlender> blender;
+ const uint32_t mode = buffer.read32();
+ if (mode == kCustom_SkBlendMode) {
+ blender = buffer.readBlender();
+ } else {
+ if (mode > (unsigned)SkBlendMode::kLastMode) {
+ buffer.validate(false);
+ return nullptr;
+ }
+ blender = SkBlender::Mode((SkBlendMode)mode);
}
- return SkImageFilters::Blend((SkBlendMode)mode, common.getInput(0), common.getInput(1),
+ return SkImageFilters::Blend(std::move(blender), common.getInput(0), common.getInput(1),
common.cropRect());
}
void SkBlendImageFilter::flatten(SkWriteBuffer& buffer) const {
this->INHERITED::flatten(buffer);
- buffer.write32((unsigned)fMode);
+ if (auto bm = as_BB(fBlender)->asBlendMode()) {
+ buffer.write32((unsigned)bm.value());
+ } else {
+ buffer.write32(kCustom_SkBlendMode);
+ buffer.writeFlattenable(fBlender.get());
+ }
}
///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -183,39 +207,39 @@
auto getForeground = [&]() {
return this->getInput(1) ? this->getInput(1)->filterBounds(src, ctm, dir, inputRect) : src;
};
- switch (fMode) {
- case SkBlendMode::kClear:
- return SkIRect::MakeEmpty();
-
- case SkBlendMode::kSrc:
- case SkBlendMode::kDstATop:
- return getForeground();
-
- case SkBlendMode::kDst:
- case SkBlendMode::kSrcATop:
- return getBackground();
-
- case SkBlendMode::kSrcIn:
- case SkBlendMode::kDstIn: {
- auto result = getBackground();
- if (!result.intersect(getForeground())) {
+ if (auto bm = as_BB(fBlender)->asBlendMode()) {
+ switch (bm.value()) {
+ case SkBlendMode::kClear:
return SkIRect::MakeEmpty();
- }
- return result;
- }
- default: {
- auto result = getBackground();
- result.join(getForeground());
- return result;
+ case SkBlendMode::kSrc:
+ case SkBlendMode::kDstATop:
+ return getForeground();
+
+ case SkBlendMode::kDst:
+ case SkBlendMode::kSrcATop:
+ return getBackground();
+
+ case SkBlendMode::kSrcIn:
+ case SkBlendMode::kDstIn: {
+ auto result = getBackground();
+ if (!result.intersect(getForeground())) {
+ return SkIRect::MakeEmpty();
+ }
+ return result;
+ }
+ default: break;
}
}
+ auto result = getBackground();
+ result.join(getForeground());
+ return result;
}
void SkBlendImageFilter::drawForeground(SkCanvas* canvas, SkSpecialImage* img,
const SkIRect& fgBounds) const {
SkPaint paint;
- paint.setBlendMode(fMode);
+ paint.setBlender(fBlender);
if (img) {
img->draw(canvas, SkIntToScalar(fgBounds.fLeft), SkIntToScalar(fgBounds.fTop),
SkSamplingOptions(), &paint);
@@ -270,6 +294,8 @@
fp = GrFragmentProcessor::MakeColor(SK_PMColor4fTRANSPARENT);
}
+ GrImageInfo info(ctx.grColorType(), kPremul_SkAlphaType, ctx.refColorSpace(), bounds.size());
+
if (foregroundView.asTextureProxy()) {
SkRect fgSubset = SkRect::Make(foreground->subset());
SkMatrix fgMatrix = SkMatrix::Translate(
@@ -280,10 +306,12 @@
fgFP = GrColorSpaceXformEffect::Make(std::move(fgFP), foreground->getColorSpace(),
foreground->alphaType(), ctx.colorSpace(),
kPremul_SkAlphaType);
- fp = GrBlendFragmentProcessor::Make(std::move(fgFP), std::move(fp), fMode);
+
+ GrFPArgs args(rContext, SkSimpleMatrixProvider(SkMatrix::I()), &info.colorInfo());
+
+ fp = as_BB(fBlender)->asFragmentProcessor(std::move(fgFP), std::move(fp), args);
}
- GrImageInfo info(ctx.grColorType(), kPremul_SkAlphaType, ctx.refColorSpace(), bounds.size());
auto sfc = GrSurfaceFillContext::Make(rContext, info, SkBackingFit::kApprox);
if (!sfc) {
return nullptr;
diff --git a/src/shaders/SkComposeShader.cpp b/src/shaders/SkComposeShader.cpp
index f9944d9..e4dce34 100644
--- a/src/shaders/SkComposeShader.cpp
+++ b/src/shaders/SkComposeShader.cpp
@@ -10,6 +10,7 @@
#include "include/private/SkColorData.h"
#include "src/core/SkArenaAlloc.h"
#include "src/core/SkBlendModePriv.h"
+#include "src/core/SkBlenderBase.h"
#include "src/core/SkRasterPipeline.h"
#include "src/core/SkReadBuffer.h"
#include "src/core/SkRuntimeEffectPriv.h"
@@ -54,8 +55,55 @@
return sk_sp<SkShader>(new SkShader_Blend(mode, std::move(dst), std::move(src)));
}
+sk_sp<SkShader> SkShaders::Blend(sk_sp<SkBlender> blender, sk_sp<SkShader> dst, sk_sp<SkShader> src) {
+ if (!src || !dst) {
+ return nullptr;
+ }
+ if (!blender) {
+ return SkShaders::Blend(SkBlendMode::kSrcOver, std::move(dst), std::move(src));
+ }
+ if (auto bm = as_BB(blender)->asBlendMode()) {
+ return SkShaders::Blend(bm.value(), std::move(dst), std::move(src));
+ }
+ return sk_sp<SkShader>(new SkShader_Blend(std::move(blender), std::move(dst), std::move(src)));
+}
+
///////////////////////////////////////////////////////////////////////////////
+sk_sp<SkFlattenable> SkShader_Blend::CreateProc(SkReadBuffer& buffer) {
+ sk_sp<SkShader> dst(buffer.readShader());
+ sk_sp<SkShader> src(buffer.readShader());
+ if (!buffer.validate(dst && src)) {
+ return nullptr;
+ }
+
+ sk_sp<SkBlender> blender(nullptr);
+ unsigned mode = buffer.read32();
+
+ if (mode == kCustom_SkBlendMode) {
+ blender = buffer.readBlender();
+ if (buffer.validate(blender != nullptr)) {
+ return SkShaders::Blend(std::move(blender), std::move(dst), std::move(src));
+ }
+ } else {
+ if (buffer.validate(mode <= (unsigned)SkBlendMode::kLastMode)) {
+ return SkShaders::Blend(static_cast<SkBlendMode>(mode), std::move(dst), std::move(src));
+ }
+ }
+ return nullptr;
+}
+
+void SkShader_Blend::flatten(SkWriteBuffer& buffer) const {
+ buffer.writeFlattenable(fDst.get());
+ buffer.writeFlattenable(fSrc.get());
+ if (fBlender) {
+ buffer.write32(kCustom_SkBlendMode);
+ buffer.writeFlattenable(fBlender.get());
+ } else {
+ buffer.write32((int)fMode);
+ }
+}
+
// Returns the output of e0, and leaves the output of e1 in r,g,b,a
static float* append_two_shaders(const SkStageRec& rec, SkShader* s0, SkShader* s1) {
struct Storage {
@@ -74,27 +122,11 @@
return storage->fRes0;
}
-///////////////////////////////////////////////////////////////////////////////
-
-sk_sp<SkFlattenable> SkShader_Blend::CreateProc(SkReadBuffer& buffer) {
- sk_sp<SkShader> dst(buffer.readShader());
- sk_sp<SkShader> src(buffer.readShader());
- unsigned mode = buffer.read32();
-
- // check for valid mode and children before we cast to the enum type and make the shader.
- if (!buffer.validate(mode <= (unsigned)SkBlendMode::kLastMode && dst && src)) {
- return nullptr;
- }
- return SkShaders::Blend(static_cast<SkBlendMode>(mode), std::move(dst), std::move(src));
-}
-
-void SkShader_Blend::flatten(SkWriteBuffer& buffer) const {
- buffer.writeFlattenable(fDst.get());
- buffer.writeFlattenable(fSrc.get());
- buffer.write32((int)fMode);
-}
-
bool SkShader_Blend::onAppendStages(const SkStageRec& orig_rec) const {
+ if (fBlender) {
+ return false;
+ }
+
const LocalMatrixStageRec rec(orig_rec, this->getLocalMatrix());
float* res0 = append_two_shaders(rec, fDst.get(), fSrc.get());
@@ -110,13 +142,17 @@
skvm::Color SkShader_Blend::onProgram(skvm::Builder* p,
skvm::Coord device, skvm::Coord local, skvm::Color paint,
const SkMatrixProvider& mats, const SkMatrix* localM,
- const SkColorInfo& dst,
+ const SkColorInfo& cinfo,
skvm::Uniforms* uniforms, SkArenaAlloc* alloc) const {
skvm::Color d,s;
- if ((d = as_SB(fDst)->program(p, device,local, paint, mats,localM, dst, uniforms,alloc)) &&
- (s = as_SB(fSrc)->program(p, device,local, paint, mats,localM, dst, uniforms,alloc)))
+ if ((d = as_SB(fDst)->program(p, device,local, paint, mats,localM, cinfo, uniforms,alloc)) &&
+ (s = as_SB(fSrc)->program(p, device,local, paint, mats,localM, cinfo, uniforms,alloc)))
{
- return p->blend(fMode, s,d);
+ if (fBlender) {
+ return as_BB(fBlender)->program(p, s,d, cinfo, uniforms,alloc);
+ } else {
+ return p->blend(fMode, s,d);
+ }
}
return {};
}
@@ -137,7 +173,11 @@
// This is unexpected. Both src and dst shaders should be valid. Just fail.
return nullptr;
}
- auto blend = GrBlendFragmentProcessor::Make(std::move(fpB), std::move(fpA), fMode);
- return GrFragmentProcessor::MakeInputOpaqueAndPostApplyAlpha(std::move(blend));
+ if (fBlender) {
+ return as_BB(fBlender)->asFragmentProcessor(std::move(fpB), std::move(fpA), orig_args);
+ } else {
+ auto blend = GrBlendFragmentProcessor::Make(std::move(fpB), std::move(fpA), fMode);
+ return GrFragmentProcessor::MakeInputOpaqueAndPostApplyAlpha(std::move(blend));
+ }
}
#endif
diff --git a/src/shaders/SkComposeShader.h b/src/shaders/SkComposeShader.h
index ee776ba..8770844 100644
--- a/src/shaders/SkComposeShader.h
+++ b/src/shaders/SkComposeShader.h
@@ -9,6 +9,8 @@
#define SkComposeShader_DEFINED
#include "include/core/SkBlendMode.h"
+#include "src/core/SkBlendModePriv.h"
+#include "src/core/SkBlenderBase.h"
#include "src/shaders/SkShaderBase.h"
class SkShader_Blend final : public SkShaderBase {
@@ -16,9 +18,17 @@
SkShader_Blend(SkBlendMode mode, sk_sp<SkShader> dst, sk_sp<SkShader> src)
: fDst(std::move(dst))
, fSrc(std::move(src))
+ , fBlender(nullptr)
, fMode(mode)
{}
+ SkShader_Blend(sk_sp<SkBlender> blender, sk_sp<SkShader> dst, sk_sp<SkShader> src)
+ : fDst(std::move(dst))
+ , fSrc(std::move(src))
+ , fBlender(std::move(blender))
+ , fMode((SkBlendMode)kCustom_SkBlendMode)
+ {}
+
#if SK_SUPPORT_GPU
std::unique_ptr<GrFragmentProcessor> asFragmentProcessor(const GrFPArgs&) const override;
#endif
@@ -36,7 +46,8 @@
sk_sp<SkShader> fDst;
sk_sp<SkShader> fSrc;
- const SkBlendMode fMode;
+ sk_sp<SkBlender> fBlender; // if null, use fMode
+ const SkBlendMode fMode; // only use if fBlender is null
using INHERITED = SkShaderBase;
};