diff --git a/gm/arithmode.cpp b/gm/arithmode.cpp index 8f68c7474b..3e8a8a8b85 100644 --- a/gm/arithmode.cpp +++ b/gm/arithmode.cpp @@ -195,7 +195,7 @@ class Arithmode2GM : public skiagm::GM { 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 @@ class Arithmode2GM : public skiagm::GM { 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 eccb545a7c..fba17f8ad4 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 @@ public: static sk_sp Color(SkColor); static sk_sp Color(const SkColor4f&, sk_sp); static sk_sp Blend(SkBlendMode mode, sk_sp dst, sk_sp src); + static sk_sp Blend(sk_sp, sk_sp dst, sk_sp src); private: SkShaders() = delete; diff --git a/include/effects/SkImageFilters.h b/include/effects/SkImageFilters.h index 9e30b82336..bc79cf9341 100644 --- a/include/effects/SkImageFilters.h +++ b/include/effects/SkImageFilters.h @@ -18,6 +18,7 @@ #include +class SkBlender; class SkColorFilter; class SkPaint; class SkRegion; @@ -96,6 +97,17 @@ public: sk_sp foreground = nullptr, 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 Blend(sk_sp blender, sk_sp background, + sk_sp 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. diff --git a/src/core/SkBlendModePriv.h b/src/core/SkBlendModePriv.h index b19e5659e8..f24c8ca982 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 871943f48b..657f2feccb 100644 --- a/src/core/SkPicturePriv.h +++ b/src/core/SkPicturePriv.h @@ -97,6 +97,7 @@ public: // 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 @@ public: 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 a5a40f38c0..321387f489 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 @@ namespace { class SkBlendImageFilter : public SkImageFilter_Base { public: - SkBlendImageFilter(SkBlendMode mode, sk_sp inputs[2], + SkBlendImageFilter(sk_sp blender, sk_sp inputs[2], const SkRect* cropRect) : INHERITED(inputs, 2, cropRect) - , fMode(mode) {} + , fBlender(std::move(blender)) + { + SkASSERT(fBlender); + } protected: sk_sp onFilterImage(const Context&, SkIPoint* offset) const override; @@ -58,7 +64,7 @@ private: friend void ::SkRegisterBlendImageFilterFlattenable(); SK_FLATTENABLE_HOOKS(SkBlendImageFilter) - SkBlendMode fMode; + sk_sp fBlender; using INHERITED = SkImageFilter_Base; }; @@ -70,7 +76,18 @@ sk_sp SkImageFilters::Blend(SkBlendMode mode, sk_sp foreground, const CropRect& cropRect) { sk_sp inputs[2] = { std::move(background), std::move(foreground) }; - return sk_sp(new SkBlendImageFilter(mode, inputs, cropRect)); + return sk_sp(new SkBlendImageFilter(SkBlender::Mode(mode), inputs, cropRect)); +} + +sk_sp SkImageFilters::Blend(sk_sp blender, + sk_sp background, + sk_sp foreground, + const CropRect& cropRect) { + if (!blender) { + blender = SkBlender::Mode(SkBlendMode::kSrcOver); + } + sk_sp inputs[2] = { std::move(background), std::move(foreground) }; + return sk_sp(new SkBlendImageFilter(blender, inputs, cropRect)); } void SkRegisterBlendImageFilterFlattenable() { @@ -80,25 +97,32 @@ void SkRegisterBlendImageFilterFlattenable() { 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 SkBlendImageFilter::CreateProc(SkReadBuffer& buffer) { SK_IMAGEFILTER_UNFLATTEN_COMMON(common, 2); - unsigned mode = unflatten_blendmode(buffer); - if (!buffer.isValid()) { - return nullptr; + + sk_sp 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 @@ SkIRect SkBlendImageFilter::onFilterBounds(const SkIRect& src, 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 @@ sk_sp SkBlendImageFilter::filterImageGPU(const Context& ctx, 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 @@ sk_sp SkBlendImageFilter::filterImageGPU(const Context& ctx, 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 f9944d967d..e4dce34c2e 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 @@ sk_sp SkShaders::Blend(SkBlendMode mode, sk_sp dst, sk_sp(new SkShader_Blend(mode, std::move(dst), std::move(src))); } +sk_sp SkShaders::Blend(sk_sp blender, sk_sp dst, sk_sp 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(new SkShader_Blend(std::move(blender), std::move(dst), std::move(src))); +} + /////////////////////////////////////////////////////////////////////////////// +sk_sp SkShader_Blend::CreateProc(SkReadBuffer& buffer) { + sk_sp dst(buffer.readShader()); + sk_sp src(buffer.readShader()); + if (!buffer.validate(dst && src)) { + return nullptr; + } + + sk_sp 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(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 @@ static float* append_two_shaders(const SkStageRec& rec, SkShader* s0, SkShader* return storage->fRes0; } -/////////////////////////////////////////////////////////////////////////////// - -sk_sp SkShader_Blend::CreateProc(SkReadBuffer& buffer) { - sk_sp dst(buffer.readShader()); - sk_sp 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(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 @@ bool SkShader_Blend::onAppendStages(const SkStageRec& orig_rec) const { 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 @@ std::unique_ptr SkShader_Blend::asFragmentProcessor( // 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 ee776bab05..87708444f0 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 @@ public: SkShader_Blend(SkBlendMode mode, sk_sp dst, sk_sp src) : fDst(std::move(dst)) , fSrc(std::move(src)) + , fBlender(nullptr) , fMode(mode) {} + SkShader_Blend(sk_sp blender, sk_sp dst, sk_sp src) + : fDst(std::move(dst)) + , fSrc(std::move(src)) + , fBlender(std::move(blender)) + , fMode((SkBlendMode)kCustom_SkBlendMode) + {} + #if SK_SUPPORT_GPU std::unique_ptr asFragmentProcessor(const GrFPArgs&) const override; #endif @@ -36,7 +46,8 @@ private: sk_sp fDst; sk_sp fSrc; - const SkBlendMode fMode; + sk_sp fBlender; // if null, use fMode + const SkBlendMode fMode; // only use if fBlender is null using INHERITED = SkShaderBase; };