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>
This commit is contained in:
Mike Reed 2021-07-26 15:29:43 -04:00 committed by SkCQ
parent 72ce9be7fd
commit a2a85e473c
8 changed files with 190 additions and 74 deletions

View File

@ -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:

View File

@ -17,6 +17,7 @@
class SkArenaAlloc;
class SkBitmap;
class SkBlender;
class SkColorFilter;
class SkColorSpace;
class SkImage;
@ -138,6 +139,7 @@ public:
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;

View File

@ -18,6 +18,7 @@
#include <cstddef>
class SkBlender;
class SkColorFilter;
class SkPaint;
class SkRegion;
@ -96,6 +97,17 @@ public:
sk_sp<SkImageFilter> 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<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.

View File

@ -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) {

View File

@ -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
};
};

View File

@ -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<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 @@ private:
friend void ::SkRegisterBlendImageFilterFlattenable();
SK_FLATTENABLE_HOOKS(SkBlendImageFilter)
SkBlendMode fMode;
sk_sp<SkBlender> fBlender;
using INHERITED = SkImageFilter_Base;
};
@ -70,7 +76,18 @@ sk_sp<SkImageFilter> SkImageFilters::Blend(SkBlendMode mode,
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 @@ 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<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 @@ 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<SkSpecialImage> 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<SkSpecialImage> 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;

View File

@ -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<SkShader> SkShaders::Blend(SkBlendMode mode, sk_sp<SkShader> dst, sk_sp<Sk
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 @@ static float* append_two_shaders(const SkStageRec& rec, SkShader* s0, SkShader*
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 @@ 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<GrFragmentProcessor> 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

View File

@ -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<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 @@ private:
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;
};