Update SkModeColorFilter to support an input FP.
This change relies on the Xfermode updates in the prior CL
(http://review.skia.org/299703) to render properly, and requires
slightly different blending behavior in the compose FP which
necessitated a new ComposeBehavior enum.
Eventually we would like to settle on a universal ComposeBehavior which
works well for all call sites, but that will be its own fairly
disruptive change. This work will be tracked at skia:10457.
Change-Id: I3cc0ea5e016fbef82bc63d653d60d0505efaa66f
Bug: skia:10217
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/298821
Commit-Queue: John Stiles <johnstiles@google.com>
Reviewed-by: Brian Salomon <bsalomon@google.com>
Reviewed-by: Brian Osman <brianosman@google.com>
diff --git a/gm/tablecolorfilter.cpp b/gm/tablecolorfilter.cpp
index b4bbd12..5b6fa86 100644
--- a/gm/tablecolorfilter.cpp
+++ b/gm/tablecolorfilter.cpp
@@ -131,7 +131,6 @@
canvas->drawColor(0xFFDDDDDD);
canvas->translate(20, 20);
-
static sk_sp<SkColorFilter> (*gColorFilterMakers[])() = {
make_null_cf, make_cf0, make_cf1, make_cf2, make_cf3
};
@@ -142,7 +141,7 @@
// - A first line with the original bitmap, followed by the image drawn once
// with each of the N color filters
// - N lines of the bitmap drawn N times, this will cover all N*N combinations of
- // pair of color filters in order to test the collpsing of consecutive table
+ // pair of color filters in order to test the collapsing of consecutive table
// color filters.
//
// Here is a graphical representation of the result for 2 bitmaps and 2 filters
diff --git a/gn/core.gni b/gn/core.gni
index fab22d3..56769b4 100644
--- a/gn/core.gni
+++ b/gn/core.gni
@@ -269,6 +269,7 @@
"$_src/core/SkMipMap.cpp",
"$_src/core/SkMipMap.h",
"$_src/core/SkModeColorFilter.cpp",
+ "$_src/core/SkModeColorFilter.h",
"$_src/core/SkNextID.h",
"$_src/core/SkOSFile.h",
"$_src/core/SkOpts.cpp",
diff --git a/src/core/SkModeColorFilter.cpp b/src/core/SkModeColorFilter.cpp
index 6394e65..445d9c3 100644
--- a/src/core/SkModeColorFilter.cpp
+++ b/src/core/SkModeColorFilter.cpp
@@ -88,27 +88,39 @@
#include "src/gpu/effects/GrXfermodeFragmentProcessor.h"
#include "src/gpu/effects/generated/GrConstColorProcessor.h"
-std::unique_ptr<GrFragmentProcessor> SkModeColorFilter::asFragmentProcessor(
- GrRecordingContext*, const GrColorInfo& dstColorInfo) const {
- if (SkBlendMode::kDst == fMode) {
- return nullptr;
+GrFPResult SkModeColorFilter::asFragmentProcessor(std::unique_ptr<GrFragmentProcessor> inputFP,
+ GrRecordingContext*,
+ const GrColorInfo& dstColorInfo) const {
+ if (fMode == SkBlendMode::kDst) {
+ // If the blend mode is "dest," the blend color won't factor into it at all.
+ // We can return the input FP as-is.
+ return GrFPSuccess(std::move(inputFP));
}
- auto constFP = GrConstColorProcessor::Make(/*inputFP=*/nullptr,
- SkColorToPMColor4f(fColor, dstColorInfo),
- GrConstColorProcessor::InputMode::kIgnore);
- auto fp = GrXfermodeFragmentProcessor::Make(std::move(constFP), /*dst=*/nullptr, fMode);
- if (!fp) {
- return nullptr;
+ SkDEBUGCODE(const bool fpHasConstIO = !inputFP || inputFP->hasConstantOutputForConstantInput();)
+
+ auto colorFP = GrConstColorProcessor::Make(
+ /*inputFP=*/nullptr, SkColorToPMColor4f(fColor, dstColorInfo),
+ GrConstColorProcessor::InputMode::kIgnore);
+ auto xferFP = GrXfermodeFragmentProcessor::Make(
+ std::move(colorFP), std::move(inputFP), fMode,
+ GrXfermodeFragmentProcessor::ComposeBehavior::kSkModeBehavior);
+
+ if (xferFP == nullptr) {
+ // This is only expected to happen if the blend mode is "dest" and the input FP is null.
+ // Since we already did an early-out in the "dest" blend mode case, we shouldn't get here.
+ SkDEBUGFAIL("GrXfermodeFragmentProcessor::Make returned null unexpectedly");
+ return GrFPFailure(nullptr);
}
-#ifdef SK_DEBUG
+
// With a solid color input this should always be able to compute the blended color
- // (at least for coeff modes)
- if ((unsigned)fMode <= (unsigned)SkBlendMode::kLastCoeffMode) {
- SkASSERT(fp->hasConstantOutputForConstantInput());
- }
-#endif
- return fp;
+ // (at least for coeff modes).
+ // Occasionally, we even do better than we started; specifically, in "src" blend mode, we end up
+ // ditching the input FP entirely, which turns a non-constant operation into a constant one.
+ SkASSERT(fMode > SkBlendMode::kLastCoeffMode ||
+ xferFP->hasConstantOutputForConstantInput() >= fpHasConstIO);
+
+ return GrFPSuccess(std::move(xferFP));
}
#endif
diff --git a/src/core/SkModeColorFilter.h b/src/core/SkModeColorFilter.h
index e2aa599..dc3ce44 100644
--- a/src/core/SkModeColorFilter.h
+++ b/src/core/SkModeColorFilter.h
@@ -19,8 +19,9 @@
uint32_t onGetFlags() const override;
#if SK_SUPPORT_GPU
- std::unique_ptr<GrFragmentProcessor> asFragmentProcessor(GrRecordingContext*,
- const GrColorInfo&) const override;
+ bool colorFilterAcceptsInputFP() const override { return true; }
+ GrFPResult asFragmentProcessor(std::unique_ptr<GrFragmentProcessor> inputFP,
+ GrRecordingContext*, const GrColorInfo&) const override;
#endif
SK_FLATTENABLE_HOOKS(SkModeColorFilter)
diff --git a/src/gpu/effects/GrXfermodeFragmentProcessor.cpp b/src/gpu/effects/GrXfermodeFragmentProcessor.cpp
index cf42a9f..f994107 100644
--- a/src/gpu/effects/GrXfermodeFragmentProcessor.cpp
+++ b/src/gpu/effects/GrXfermodeFragmentProcessor.cpp
@@ -15,6 +15,8 @@
#include "src/gpu/glsl/GrGLSLFragmentProcessor.h"
#include "src/gpu/glsl/GrGLSLFragmentShaderBuilder.h"
+using GrXfermodeFragmentProcessor::ComposeBehavior;
+
// Some of the cpu implementations of blend modes differ too much from the GPU enough that
// we can't use the cpu implementation to implement constantOutputForConstantInput.
static inline bool does_cpu_blend_impl_match_gpu(SkBlendMode mode) {
@@ -25,15 +27,27 @@
mode != SkBlendMode::kColorBurn;
}
+static const char* ComposeBehavior_Name(ComposeBehavior behavior) {
+ SkASSERT(unsigned(behavior) <= unsigned(ComposeBehavior::kLastComposeBehavior));
+ static constexpr const char* gStrings[] = {
+ "Default",
+ "Compose-One",
+ "Compose-Two",
+ "SkMode",
+ };
+ static_assert(SK_ARRAY_COUNT(gStrings) == size_t(ComposeBehavior::kLastComposeBehavior) + 1);
+ return gStrings[int(behavior)];
+}
+
//////////////////////////////////////////////////////////////////////////////
class ComposeFragmentProcessor : public GrFragmentProcessor {
public:
static std::unique_ptr<GrFragmentProcessor> Make(std::unique_ptr<GrFragmentProcessor> src,
std::unique_ptr<GrFragmentProcessor> dst,
- SkBlendMode mode) {
+ SkBlendMode mode, ComposeBehavior behavior) {
return std::unique_ptr<GrFragmentProcessor>(
- new ComposeFragmentProcessor(std::move(src), std::move(dst), mode));
+ new ComposeFragmentProcessor(std::move(src), std::move(dst), mode, behavior));
}
const char* name() const override { return "Compose"; }
@@ -55,15 +69,21 @@
std::unique_ptr<GrFragmentProcessor> clone() const override;
SkBlendMode getMode() const { return fMode; }
+ ComposeBehavior composeBehavior() const { return fComposeBehavior; }
int srcFPIndex() const { return fSrcFPIndex; }
int dstFPIndex() const { return fDstFPIndex; }
private:
ComposeFragmentProcessor(std::unique_ptr<GrFragmentProcessor> src,
std::unique_ptr<GrFragmentProcessor> dst,
- SkBlendMode mode)
+ SkBlendMode mode, ComposeBehavior behavior)
: INHERITED(kComposeFragmentProcessor_ClassID, OptFlags(src.get(), dst.get(), mode))
- , fMode(mode) {
+ , fMode(mode)
+ , fComposeBehavior(behavior) {
+ if (fComposeBehavior == ComposeBehavior::kDefault) {
+ fComposeBehavior = (src && dst) ? ComposeBehavior::kComposeTwoBehavior
+ : ComposeBehavior::kComposeOneBehavior;
+ }
if (src != nullptr) {
fSrcFPIndex = this->registerChild(std::move(src));
}
@@ -75,6 +95,7 @@
ComposeFragmentProcessor(const ComposeFragmentProcessor& that)
: INHERITED(kComposeFragmentProcessor_ClassID, ProcessorOptimizationFlags(&that))
, fMode(that.fMode)
+ , fComposeBehavior(that.fComposeBehavior)
, fSrcFPIndex(that.fSrcFPIndex)
, fDstFPIndex(that.fDstFPIndex) {
this->cloneAndRegisterAllChildProcessors(that);
@@ -176,26 +197,41 @@
const auto* src = (fSrcFPIndex >= 0) ? &this->childProcessor(fSrcFPIndex) : nullptr;
const auto* dst = (fDstFPIndex >= 0) ? &this->childProcessor(fDstFPIndex) : nullptr;
- if (src && dst) {
- SkPMColor4f opaqueInput = { input.fR, input.fG, input.fB, 1 };
- SkPMColor4f srcColor = ConstantOutputForConstantInput(*src, opaqueInput);
- SkPMColor4f dstColor = ConstantOutputForConstantInput(*dst, opaqueInput);
- SkPMColor4f result = SkBlendMode_Apply(fMode, srcColor, dstColor);
- return result * input.fA;
- } else if (src) {
- SkPMColor4f srcColor = ConstantOutputForConstantInput(*src, SK_PMColor4fWHITE);
- return SkBlendMode_Apply(fMode, srcColor, input);
- } else if (dst) {
- SkPMColor4f dstColor = ConstantOutputForConstantInput(*dst, SK_PMColor4fWHITE);
- return SkBlendMode_Apply(fMode, input, dstColor);
- } else {
- return input;
+ switch (fComposeBehavior) {
+ case ComposeBehavior::kComposeOneBehavior: {
+ SkPMColor4f srcColor = src ? ConstantOutputForConstantInput(*src, SK_PMColor4fWHITE)
+ : input;
+ SkPMColor4f dstColor = dst ? ConstantOutputForConstantInput(*dst, SK_PMColor4fWHITE)
+ : input;
+ return SkBlendMode_Apply(fMode, srcColor, dstColor);
+ }
+
+ case ComposeBehavior::kComposeTwoBehavior: {
+ SkPMColor4f opaqueInput = { input.fR, input.fG, input.fB, 1 };
+ SkPMColor4f srcColor = ConstantOutputForConstantInput(*src, opaqueInput);
+ SkPMColor4f dstColor = ConstantOutputForConstantInput(*dst, opaqueInput);
+ SkPMColor4f result = SkBlendMode_Apply(fMode, srcColor, dstColor);
+ return result * input.fA;
+ }
+
+ case ComposeBehavior::kSkModeBehavior: {
+ SkPMColor4f srcColor = src ? ConstantOutputForConstantInput(*src, SK_PMColor4fWHITE)
+ : input;
+ SkPMColor4f dstColor = dst ? ConstantOutputForConstantInput(*dst, input)
+ : input;
+ return SkBlendMode_Apply(fMode, srcColor, dstColor);
+ }
+
+ default:
+ SK_ABORT("unrecognized compose behavior");
+ return input;
}
}
GrGLSLFragmentProcessor* onCreateGLSLInstance() const override;
SkBlendMode fMode;
+ ComposeBehavior fComposeBehavior;
int fSrcFPIndex = -1;
int fDstFPIndex = -1;
@@ -225,11 +261,14 @@
std::unique_ptr<GrFragmentProcessor> fpB(GrProcessorUnitTest::MakeChildFP(d));
SkBlendMode mode;
+ ComposeBehavior behavior;
do {
mode = static_cast<SkBlendMode>(d->fRandom->nextRangeU(0, (int)SkBlendMode::kLastMode));
+ behavior = static_cast<ComposeBehavior>(
+ d->fRandom->nextRangeU(0, (int)ComposeBehavior::kLastComposeBehavior));
} while (SkBlendMode::kClear == mode || SkBlendMode::kSrc == mode || SkBlendMode::kDst == mode);
return std::unique_ptr<GrFragmentProcessor>(
- new ComposeFragmentProcessor(std::move(fpA), std::move(fpB), mode));
+ new ComposeFragmentProcessor(std::move(fpA), std::move(fpB), mode, behavior));
}
#endif
@@ -248,24 +287,42 @@
GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
const ComposeFragmentProcessor& cs = args.fFp.cast<ComposeFragmentProcessor>();
SkBlendMode mode = cs.getMode();
+ ComposeBehavior behavior = cs.composeBehavior();
int srcFPIndex = cs.srcFPIndex();
int dstFPIndex = cs.dstFPIndex();
// Load the input color and make an opaque copy if needed.
- fragBuilder->codeAppendf("// Compose Xfer Mode: %s\n", SkBlendMode_Name(mode));
+ fragBuilder->codeAppendf("// %s Xfer Mode: %s\n",
+ ComposeBehavior_Name(behavior), SkBlendMode_Name(mode));
SkString srcColor, dstColor;
- if (srcFPIndex >= 0 && dstFPIndex >= 0) {
- // Compose-two operations historically have forced the input color to opaque.
- fragBuilder->codeAppendf("half4 inputOpaque = %s.rgb1;\n", args.fInputColor);
- srcColor = this->invokeChild(srcFPIndex, "inputOpaque", args);
- dstColor = this->invokeChild(dstFPIndex, "inputOpaque", args);
- } else {
- // Compose-one operations historically leave the alpha on the input color.
- srcColor = (srcFPIndex >= 0) ? this->invokeChild(srcFPIndex, args)
- : SkString(args.fInputColor);
- dstColor = (dstFPIndex >= 0) ? this->invokeChild(dstFPIndex, args)
- : SkString(args.fInputColor);
+ switch (behavior) {
+ case ComposeBehavior::kComposeOneBehavior:
+ // Compose-one operations historically leave the alpha on the input color.
+ srcColor = (srcFPIndex >= 0) ? this->invokeChild(srcFPIndex, args)
+ : SkString(args.fInputColor);
+ dstColor = (dstFPIndex >= 0) ? this->invokeChild(dstFPIndex, args)
+ : SkString(args.fInputColor);
+ break;
+
+ case ComposeBehavior::kComposeTwoBehavior:
+ // Compose-two operations historically have forced the input color to opaque.
+ fragBuilder->codeAppendf("half4 inputOpaque = %s.rgb1;\n", args.fInputColor);
+ srcColor = this->invokeChild(srcFPIndex, "inputOpaque", args);
+ dstColor = this->invokeChild(dstFPIndex, "inputOpaque", args);
+ break;
+
+ case ComposeBehavior::kSkModeBehavior:
+ // SkModeColorFilter operations act like ComposeOne, but pass the input color to dst.
+ srcColor = (srcFPIndex >= 0) ? this->invokeChild(srcFPIndex, args)
+ : SkString(args.fInputColor);
+ dstColor = (dstFPIndex >= 0) ? this->invokeChild(dstFPIndex, args.fInputColor, args)
+ : SkString(args.fInputColor);
+ break;
+
+ default:
+ SK_ABORT("unrecognized compose behavior");
+ break;
}
// Blend src and dst colors together.
@@ -273,7 +330,7 @@
args.fOutputColor, mode);
// Reapply alpha from input color if we are doing a compose-two.
- if (srcFPIndex >= 0 && dstFPIndex >= 0) {
+ if (behavior == ComposeBehavior::kComposeTwoBehavior) {
fragBuilder->codeAppendf("%s *= %s.a;\n", args.fOutputColor, args.fInputColor);
}
}
@@ -283,7 +340,7 @@
std::unique_ptr<GrFragmentProcessor> GrXfermodeFragmentProcessor::Make(
std::unique_ptr<GrFragmentProcessor> src,
std::unique_ptr<GrFragmentProcessor> dst,
- SkBlendMode mode) {
+ SkBlendMode mode, ComposeBehavior behavior) {
switch (mode) {
case SkBlendMode::kClear:
return GrConstColorProcessor::Make(/*inputFP=*/nullptr, SK_PMColor4fTRANSPARENT,
@@ -295,6 +352,6 @@
return GrFragmentProcessor::OverrideInput(std::move(dst), SK_PMColor4fWHITE,
/*useUniform=*/false);
default:
- return ComposeFragmentProcessor::Make(std::move(src), std::move(dst), mode);
+ return ComposeFragmentProcessor::Make(std::move(src), std::move(dst), mode, behavior);
}
}
diff --git a/src/gpu/effects/GrXfermodeFragmentProcessor.h b/src/gpu/effects/GrXfermodeFragmentProcessor.h
index 7493fb1..5801163 100644
--- a/src/gpu/effects/GrXfermodeFragmentProcessor.h
+++ b/src/gpu/effects/GrXfermodeFragmentProcessor.h
@@ -15,12 +15,29 @@
namespace GrXfermodeFragmentProcessor {
+enum class ComposeBehavior {
+ // Picks "ComposeOne" or "ComposeTwo" automatically depending on presence of src/dst FPs.
+ kDefault = 0,
+
+ // half(1) is passed as the input color to child FPs. No alpha channel trickery.
+ kComposeOneBehavior,
+
+ // sk_InColor.rgb1 is passed as the input color to child FPs. Alpha is manually blended.
+ kComposeTwoBehavior,
+
+ // half(1) is passed to src; sk_InColor.rgba is passed to dst. No alpha channel trickery.
+ kSkModeBehavior,
+
+ kLastComposeBehavior = kSkModeBehavior,
+};
+
/** Blends src and dst inputs according to the blend mode.
* If either input is null, the input color (sk_InColor) is used instead.
*/
std::unique_ptr<GrFragmentProcessor> Make(std::unique_ptr<GrFragmentProcessor> src,
std::unique_ptr<GrFragmentProcessor> dst,
- SkBlendMode mode);
+ SkBlendMode mode,
+ ComposeBehavior behavior = ComposeBehavior::kDefault);
};
diff --git a/src/gpu/glsl/GrGLSLFragmentProcessor.cpp b/src/gpu/glsl/GrGLSLFragmentProcessor.cpp
index 7a0858c..9b3b7b7 100644
--- a/src/gpu/glsl/GrGLSLFragmentProcessor.cpp
+++ b/src/gpu/glsl/GrGLSLFragmentProcessor.cpp
@@ -17,6 +17,7 @@
}
void GrGLSLFragmentProcessor::emitChildFunction(int childIndex, EmitArgs& args) {
+ SkASSERT(childIndex >= 0);
GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
while (childIndex >= (int) fFunctionNames.size()) {
fFunctionNames.emplace_back();
@@ -43,6 +44,7 @@
SkString GrGLSLFragmentProcessor::invokeChild(int childIndex, const char* inputColor,
EmitArgs& args, SkSL::String skslCoords) {
+ SkASSERT(childIndex >= 0);
this->emitChildFunction(childIndex, args);
if (skslCoords.empty()) {
@@ -72,6 +74,7 @@
SkString GrGLSLFragmentProcessor::invokeChildWithMatrix(int childIndex, const char* inputColor,
EmitArgs& args,
SkSL::String skslMatrix) {
+ SkASSERT(childIndex >= 0);
this->emitChildFunction(childIndex, args);
const GrFragmentProcessor& childProc = args.fFp.childProcessor(childIndex);