Allow sampling from SkBlenders.
Runtime shaders, color filters, and blenders are all able to sample from
a blender. These use the blend-function signature; both a src-color and
dst-color must be passed to sample. i.e.: sample(blender, s, d)
Change-Id: I3738e6b0b4af6d1d79e62ca1815c80d6a1ae9d6f
Bug: skia:12257
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/432056
Commit-Queue: John Stiles <johnstiles@google.com>
Reviewed-by: Brian Osman <brianosman@google.com>
diff --git a/include/effects/SkRuntimeEffect.h b/include/effects/SkRuntimeEffect.h
index b879e67..a5c3e3b 100644
--- a/include/effects/SkRuntimeEffect.h
+++ b/include/effects/SkRuntimeEffect.h
@@ -82,6 +82,7 @@
enum class Type {
kShader,
kColorFilter,
+ kBlender,
};
SkString name;
@@ -161,13 +162,15 @@
static Result MakeForBlender(std::unique_ptr<SkSL::Program> program, const Options&);
static Result MakeForBlender(std::unique_ptr<SkSL::Program> program);
- // Object that allows passing either an SkShader or SkColorFilter as a child
+ // Object that allows passing a SkShader, SkColorFilter or SkBlender as a child
struct ChildPtr {
ChildPtr() = default;
ChildPtr(sk_sp<SkShader> s) : shader(std::move(s)) {}
ChildPtr(sk_sp<SkColorFilter> cf) : colorFilter(std::move(cf)) {}
+ ChildPtr(sk_sp<SkBlender> b) : blender(std::move(b)) {}
sk_sp<SkShader> shader;
sk_sp<SkColorFilter> colorFilter;
+ sk_sp<SkBlender> blender;
};
sk_sp<SkShader> makeShader(sk_sp<SkData> uniforms,
diff --git a/src/core/SkRuntimeEffect.cpp b/src/core/SkRuntimeEffect.cpp
index 7655595..d137599 100644
--- a/src/core/SkRuntimeEffect.cpp
+++ b/src/core/SkRuntimeEffect.cpp
@@ -130,7 +130,7 @@
static SkRuntimeEffect::Child::Type child_type(const SkSL::Type& type) {
switch (type.typeKind()) {
- // TODO(skia:12257): add support for kBlender
+ case SkSL::Type::TypeKind::kBlender: return SkRuntimeEffect::Child::Type::kBlender;
case SkSL::Type::TypeKind::kColorFilter: return SkRuntimeEffect::Child::Type::kColorFilter;
case SkSL::Type::TypeKind::kShader: return SkRuntimeEffect::Child::Type::kShader;
default: SkUNREACHABLE;
@@ -147,12 +147,17 @@
for (size_t i = 0; i < effectPtrs.size(); ++i) {
switch (reflected[i].type) {
case SkRuntimeEffect::Child::Type::kShader:
- if (effectPtrs[i].colorFilter) {
+ if (effectPtrs[i].colorFilter || effectPtrs[i].blender) {
return false;
}
continue;
case SkRuntimeEffect::Child::Type::kColorFilter:
- if (effectPtrs[i].shader) {
+ if (effectPtrs[i].shader || effectPtrs[i].blender) {
+ return false;
+ }
+ continue;
+ case SkRuntimeEffect::Child::Type::kBlender:
+ if (effectPtrs[i].shader || effectPtrs[i].colorFilter) {
return false;
}
continue;
@@ -178,6 +183,8 @@
children->emplace_back(buffer.readShader());
} else if (child.type == SkRuntimeEffect::Child::Type::kColorFilter) {
children->emplace_back(buffer.readColorFilter());
+ } else if (child.type == SkRuntimeEffect::Child::Type::kBlender) {
+ children->emplace_back(buffer.readBlender());
} else {
return false;
}
@@ -190,8 +197,10 @@
const std::vector<SkRuntimeEffect::ChildPtr>& children) {
buffer.write32(children.size());
for (const auto& child : children) {
- buffer.writeFlattenable(child.shader ? (const SkFlattenable*)child.shader.get()
- : child.colorFilter.get());
+ buffer.writeFlattenable(
+ child.shader ? (const SkFlattenable*)child.shader.get() :
+ child.colorFilter ? (const SkFlattenable*)child.colorFilter.get() :
+ (const SkFlattenable*)child.blender.get());
}
}
@@ -569,9 +578,10 @@
// of those, we are unable to use this per-effect program, and callers will need to fall back
// to another (slower) implementation.
- // We also require that any children are *also* color filters (not shaders). In theory we could
- // detect the coords being passed to shader children, and replicate those calls, but that's
- // very complicated, and has diminishing returns. (eg, for table lookup color filters).
+ // We also require that any children are *also* color filters (not shaders or blenders). In
+ // theory we could detect the coords being passed to shader children, and replicate those calls,
+ // but that's very complicated, and has diminishing returns. (eg, for table lookup color
+ // filters).
if (!std::all_of(effect->fChildren.begin(),
effect->fChildren.end(),
[](const SkRuntimeEffect::Child& c) {
@@ -777,12 +787,14 @@
SkSTArray<8, std::unique_ptr<GrFragmentProcessor>> childFPs;
for (const auto& child : children) {
if (child.shader) {
+ // Convert a SkShader into a child FP.
auto childFP = as_SB(child.shader)->asFragmentProcessor(childArgs);
if (!childFP) {
return GrFPFailure(std::move(inputFP));
}
childFPs.push_back(std::move(childFP));
} else if (child.colorFilter) {
+ // Convert a SkColorFilter into a child FP.
auto [success, childFP] = as_CFB(child.colorFilter)
->asFragmentProcessor(/*inputFP=*/nullptr,
childArgs.fContext,
@@ -791,7 +803,17 @@
return GrFPFailure(std::move(inputFP));
}
childFPs.push_back(std::move(childFP));
+ } else if (child.blender) {
+ // Convert a SkBlender into a child FP.
+ auto childFP = as_BB(child.blender)->asFragmentProcessor(/*srcFP=*/nullptr,
+ /*dstFP=*/nullptr,
+ childArgs);
+ if (!childFP) {
+ return GrFPFailure(std::move(inputFP));
+ }
+ childFPs.push_back(std::move(childFP));
} else {
+ // We have a null child effect.
childFPs.push_back(nullptr);
}
}
@@ -860,8 +882,10 @@
return color;
};
auto sampleBlender = [&](int ix, skvm::Color src, skvm::Color dst) {
- // TODO(skia:12257): implement sample(blender, src, dst)
- return src;
+ if (SkBlender* blender = fChildren[ix].blender.get()) {
+ return as_BB(blender)->program(p, src, dst, colorInfo, uniforms, alloc);
+ }
+ return blend(SkBlendMode::kSrcOver, src, dst);
};
std::vector<skvm::Val> uniform = make_skvm_uniforms(p, uniforms, fEffect->uniformSize(),
@@ -891,8 +915,9 @@
auto evalChild = [&](int index, SkPMColor4f inColor) {
const auto& child = fChildren[index];
- // Guaranteed by initFilterColorInfo
+ // SkFilterColorProgram::Make has guaranteed that any children will be color filters.
SkASSERT(!child.shader);
+ SkASSERT(!child.blender);
return child.colorFilter ? as_CFB(child.colorFilter)->onFilterColor4f(inColor, dstCS)
: inColor;
};
@@ -1036,8 +1061,10 @@
return color;
};
auto sampleBlender = [&](int ix, skvm::Color src, skvm::Color dst) {
- // TODO(skia:12257): implement sample(blender, src, dst)
- return src;
+ if (SkBlender* blender = fChildren[ix].blender.get()) {
+ return as_BB(blender)->program(p, src, dst, colorInfo, uniforms, alloc);
+ }
+ return blend(SkBlendMode::kSrcOver, src, dst);
};
std::vector<skvm::Val> uniform = make_skvm_uniforms(p, uniforms, fEffect->uniformSize(),
@@ -1144,8 +1171,10 @@
return color;
};
auto sampleBlender = [&](int ix, skvm::Color src, skvm::Color dst) {
- // TODO(skia:12257): implement sample(blender, src, dst)
- return src;
+ if (SkBlender* blender = fChildren[ix].blender.get()) {
+ return as_BB(blender)->program(p, src, dst, colorInfo, uniforms, alloc);
+ }
+ return blend(SkBlendMode::kSrcOver, src, dst);
};
std::vector<skvm::Val> uniform = make_skvm_uniforms(p, uniforms, fEffect->uniformSize(),
diff --git a/src/gpu/effects/GrSkSLFP.cpp b/src/gpu/effects/GrSkSLFP.cpp
index 290a2be..d631bb8 100644
--- a/src/gpu/effects/GrSkSLFP.cpp
+++ b/src/gpu/effects/GrSkSLFP.cpp
@@ -146,8 +146,10 @@
}
String sampleBlender(int index, String src, String dst) override {
- // TODO(skia:12257): invokeChild does not yet allow sampling from a blender
- return "half4(1)";
+ if (!fSelf->childProcessor(index)) {
+ return String::printf("blend_src_over(%s, %s)", src.c_str(), dst.c_str());
+ }
+ return String(fSelf->invokeChild(index, src.c_str(), dst.c_str(), fArgs).c_str());
}
GrGLSLSkSLFP* fSelf;
diff --git a/src/sksl/SkSLBuiltinTypes.cpp b/src/sksl/SkSLBuiltinTypes.cpp
index 11cb7d2..28ad637 100644
--- a/src/sksl/SkSLBuiltinTypes.cpp
+++ b/src/sksl/SkSLBuiltinTypes.cpp
@@ -180,6 +180,6 @@
, fSkCaps(Type::MakeSpecialType("$sk_Caps", "O", Type::TypeKind::kOther))
, fColorFilter(Type::MakeSpecialType("colorFilter", "CF", Type::TypeKind::kColorFilter))
, fShader(Type::MakeSpecialType("shader", "SH", Type::TypeKind::kShader))
- , fBlender(Type::MakeSpecialType("$blender", "B", Type::TypeKind::kBlender)) {}
+ , fBlender(Type::MakeSpecialType("blender", "B", Type::TypeKind::kBlender)) {}
} // namespace SkSL
diff --git a/src/sksl/codegen/SkSLPipelineStageCodeGenerator.cpp b/src/sksl/codegen/SkSLPipelineStageCodeGenerator.cpp
index c6e2c90..3c1b2c2 100644
--- a/src/sksl/codegen/SkSLPipelineStageCodeGenerator.cpp
+++ b/src/sksl/codegen/SkSLPipelineStageCodeGenerator.cpp
@@ -141,7 +141,7 @@
const FunctionDeclaration& function = c.function();
const ExpressionArray& arguments = c.arguments();
if (function.isBuiltin() && function.name() == "sample") {
- SkASSERT(arguments.size() == 2);
+ SkASSERT(arguments.size() >= 2);
const Expression* child = arguments[0].get();
SkASSERT(child->type().isEffectChild());
SkASSERT(child->is<VariableReference>());
@@ -164,20 +164,44 @@
SkASSERT(found);
// Shaders require a coordinate argument. Color filters require a color argument.
- // When we call sampleChild, the other value remains empty.
+ // Blenders require two color arguments.
String sampleOutput;
{
- AutoOutputBuffer outputToBuffer(this);
- this->writeExpression(*arguments.back(), Precedence::kSequence);
- if (child->type().typeKind() == Type::TypeKind::kShader) {
- SkASSERT(arguments[1]->type() == *fProgram.fContext->fTypes.fFloat2);
- sampleOutput = fCallbacks->sampleShader(index, outputToBuffer.fBuffer.str());
+ AutoOutputBuffer exprBuffer(this);
+ this->writeExpression(*arguments[1], Precedence::kSequence);
- } else {
- SkASSERT(child->type().typeKind() == Type::TypeKind::kColorFilter);
- SkASSERT(arguments[1]->type() == *fProgram.fContext->fTypes.fHalf4 ||
- arguments[1]->type() == *fProgram.fContext->fTypes.fFloat4);
- sampleOutput = fCallbacks->sampleColorFilter(index, outputToBuffer.fBuffer.str());
+ switch (child->type().typeKind()) {
+ case Type::TypeKind::kShader: {
+ SkASSERT(arguments.size() == 2);
+ SkASSERT(arguments[1]->type() == *fProgram.fContext->fTypes.fFloat2);
+ sampleOutput = fCallbacks->sampleShader(index, exprBuffer.fBuffer.str());
+ break;
+ }
+ case Type::TypeKind::kColorFilter: {
+ SkASSERT(arguments.size() == 2);
+ SkASSERT(arguments[1]->type() == *fProgram.fContext->fTypes.fHalf4 ||
+ arguments[1]->type() == *fProgram.fContext->fTypes.fFloat4);
+ sampleOutput = fCallbacks->sampleColorFilter(index, exprBuffer.fBuffer.str());
+ break;
+ }
+ case Type::TypeKind::kBlender: {
+ SkASSERT(arguments.size() == 3);
+ SkASSERT(arguments[1]->type() == *fProgram.fContext->fTypes.fHalf4 ||
+ arguments[1]->type() == *fProgram.fContext->fTypes.fFloat4);
+ SkASSERT(arguments[2]->type() == *fProgram.fContext->fTypes.fHalf4 ||
+ arguments[2]->type() == *fProgram.fContext->fTypes.fFloat4);
+
+ AutoOutputBuffer exprBuffer2(this);
+ this->writeExpression(*arguments[2], Precedence::kSequence);
+
+ sampleOutput = fCallbacks->sampleBlender(index, exprBuffer.fBuffer.str(),
+ exprBuffer2.fBuffer.str());
+ break;
+ }
+ default: {
+ SkDEBUGFAILF("cannot sample from type '%s'",
+ child->type().description().c_str());
+ }
}
}
this->write(sampleOutput);
diff --git a/src/sksl/codegen/SkSLVMCodeGenerator.cpp b/src/sksl/codegen/SkSLVMCodeGenerator.cpp
index f36dfea..0e8f16c 100644
--- a/src/sksl/codegen/SkSLVMCodeGenerator.cpp
+++ b/src/sksl/codegen/SkSLVMCodeGenerator.cpp
@@ -873,7 +873,7 @@
if (intrinsicKind == k_sample_IntrinsicKind) {
// Sample is very special. The first argument is a child (shader/colorFilter/blender),
// which is opaque and can't be evaluated.
- SkASSERT(nargs == 2);
+ SkASSERT(nargs >= 2);
const Expression* child = c.arguments()[0].get();
SkASSERT(child->type().isEffectChild());
SkASSERT(child->is<VariableReference>());
@@ -887,16 +887,43 @@
Value argVal = this->writeExpression(*arg);
skvm::Color color;
- if (child->type().typeKind() == Type::TypeKind::kShader) {
- SkASSERT(arg->type() == *fProgram.fContext->fTypes.fFloat2);
- skvm::Coord coord = {f32(argVal[0]), f32(argVal[1])};
- color = fSampleShader(fp_it->second, coord);
- } else {
- SkASSERT(child->type().typeKind() == Type::TypeKind::kColorFilter);
- SkASSERT(arg->type() == *fProgram.fContext->fTypes.fHalf4 ||
- arg->type() == *fProgram.fContext->fTypes.fFloat4);
- skvm::Color inColor = {f32(argVal[0]), f32(argVal[1]), f32(argVal[2]), f32(argVal[3])};
- color = fSampleColorFilter(fp_it->second, inColor);
+ switch (child->type().typeKind()) {
+ case Type::TypeKind::kShader: {
+ SkASSERT(nargs == 2);
+ SkASSERT(arg->type() == *fProgram.fContext->fTypes.fFloat2);
+ skvm::Coord coord = {f32(argVal[0]), f32(argVal[1])};
+ color = fSampleShader(fp_it->second, coord);
+ break;
+ }
+ case Type::TypeKind::kColorFilter: {
+ SkASSERT(nargs == 2);
+ SkASSERT(arg->type() == *fProgram.fContext->fTypes.fHalf4 ||
+ arg->type() == *fProgram.fContext->fTypes.fFloat4);
+ skvm::Color inColor = {f32(argVal[0]), f32(argVal[1]),
+ f32(argVal[2]), f32(argVal[3])};
+ color = fSampleColorFilter(fp_it->second, inColor);
+ break;
+ }
+ case Type::TypeKind::kBlender: {
+ SkASSERT(nargs == 3);
+ SkASSERT(arg->type() == *fProgram.fContext->fTypes.fHalf4 ||
+ arg->type() == *fProgram.fContext->fTypes.fFloat4);
+ skvm::Color srcColor = {f32(argVal[0]), f32(argVal[1]),
+ f32(argVal[2]), f32(argVal[3])};
+
+ arg = c.arguments()[2].get();
+ argVal = this->writeExpression(*arg);
+ SkASSERT(arg->type() == *fProgram.fContext->fTypes.fHalf4 ||
+ arg->type() == *fProgram.fContext->fTypes.fFloat4);
+ skvm::Color dstColor = {f32(argVal[0]), f32(argVal[1]),
+ f32(argVal[2]), f32(argVal[3])};
+
+ color = fSampleBlender(fp_it->second, srcColor, dstColor);
+ break;
+ }
+ default: {
+ SkDEBUGFAILF("cannot sample from type '%s'", child->type().description().c_str());
+ }
}
Value result(4);
diff --git a/src/sksl/generated/sksl_rt_blend.dehydrated.sksl b/src/sksl/generated/sksl_rt_blend.dehydrated.sksl
index 16fbb2c..2b3400f 100644
--- a/src/sksl/generated/sksl_rt_blend.dehydrated.sksl
+++ b/src/sksl/generated/sksl_rt_blend.dehydrated.sksl
@@ -1,4 +1,4 @@
-static uint8_t SKSL_INCLUDE_sksl_rt_blend[] = {56,0,
+static uint8_t SKSL_INCLUDE_sksl_rt_blend[] = {74,0,
1,115,
6,115,104,97,100,101,114,
6,99,111,111,114,100,115,
@@ -8,7 +8,11 @@
1,102,
11,99,111,108,111,114,70,105,108,116,101,114,
5,99,111,108,111,114,
-47,7,0,
+1,98,
+7,98,108,101,110,100,101,114,
+3,115,114,99,
+3,100,115,116,
+47,12,0,
51,1,0,
16,2,0,
48,2,0,4,0,3,
@@ -29,8 +33,24 @@
28,11,0,
16,25,0,2,7,0,9,0,
45,6,0,
-45,11,0,1,0,
-5,0,
+45,11,0,
+51,12,0,
+16,58,0,
+48,13,0,60,0,3,
+51,14,0,
+16,68,0,
+45,6,0,3,
+51,15,0,
+16,72,0,
+45,6,0,3,
+50,16,0,3,
+45,5,0,
+45,11,0,
+28,17,0,
+16,25,0,3,12,0,14,0,15,0,
+45,6,0,
+45,17,0,1,0,
+10,0,
19,
20,};
static constexpr size_t SKSL_INCLUDE_sksl_rt_blend_LENGTH = sizeof(SKSL_INCLUDE_sksl_rt_blend);
diff --git a/src/sksl/generated/sksl_rt_colorfilter.dehydrated.sksl b/src/sksl/generated/sksl_rt_colorfilter.dehydrated.sksl
index 32a4139..3550ce5 100644
--- a/src/sksl/generated/sksl_rt_colorfilter.dehydrated.sksl
+++ b/src/sksl/generated/sksl_rt_colorfilter.dehydrated.sksl
@@ -1,4 +1,4 @@
-static uint8_t SKSL_INCLUDE_sksl_rt_colorfilter[] = {56,0,
+static uint8_t SKSL_INCLUDE_sksl_rt_colorfilter[] = {74,0,
1,115,
6,115,104,97,100,101,114,
6,99,111,111,114,100,115,
@@ -8,7 +8,11 @@
1,102,
11,99,111,108,111,114,70,105,108,116,101,114,
5,99,111,108,111,114,
-47,7,0,
+1,98,
+7,98,108,101,110,100,101,114,
+3,115,114,99,
+3,100,115,116,
+47,12,0,
51,1,0,
16,2,0,
48,2,0,4,0,3,
@@ -29,8 +33,24 @@
28,11,0,
16,25,0,2,7,0,9,0,
45,6,0,
-45,11,0,1,0,
-5,0,
+45,11,0,
+51,12,0,
+16,58,0,
+48,13,0,60,0,3,
+51,14,0,
+16,68,0,
+45,6,0,3,
+51,15,0,
+16,72,0,
+45,6,0,3,
+50,16,0,3,
+45,5,0,
+45,11,0,
+28,17,0,
+16,25,0,3,12,0,14,0,15,0,
+45,6,0,
+45,17,0,1,0,
+10,0,
19,
20,};
static constexpr size_t SKSL_INCLUDE_sksl_rt_colorfilter_LENGTH = sizeof(SKSL_INCLUDE_sksl_rt_colorfilter);
diff --git a/src/sksl/generated/sksl_rt_shader.dehydrated.sksl b/src/sksl/generated/sksl_rt_shader.dehydrated.sksl
index 3d36e1f..795c531 100644
--- a/src/sksl/generated/sksl_rt_shader.dehydrated.sksl
+++ b/src/sksl/generated/sksl_rt_shader.dehydrated.sksl
@@ -1,4 +1,4 @@
-static uint8_t SKSL_INCLUDE_sksl_rt_shader[] = {76,0,
+static uint8_t SKSL_INCLUDE_sksl_rt_shader[] = {94,0,
12,115,107,95,70,114,97,103,67,111,111,114,100,
6,102,108,111,97,116,52,
1,115,
@@ -10,7 +10,11 @@
1,102,
11,99,111,108,111,114,70,105,108,116,101,114,
5,99,111,108,111,114,
-47,8,0,
+1,98,
+7,98,108,101,110,100,101,114,
+3,115,114,99,
+3,100,115,116,
+47,13,0,
51,1,0,
35,
34,0,2,0,0,255,255,255,255,255,15,0,255,255,255,255,0,2,0,
@@ -35,8 +39,24 @@
28,13,0,
16,45,0,2,9,0,11,0,
45,8,0,
-45,13,0,2,0,
-6,0,
+45,13,0,
+51,14,0,
+16,78,0,
+48,15,0,80,0,3,
+51,16,0,
+16,88,0,
+45,8,0,3,
+51,17,0,
+16,92,0,
+45,8,0,3,
+50,18,0,3,
+45,7,0,
+45,13,0,
+28,19,0,
+16,45,0,3,14,0,16,0,17,0,
+45,8,0,
+45,19,0,2,0,
+11,0,
0,0,
19,
53,
diff --git a/src/sksl/sksl_rt_blend.sksl b/src/sksl/sksl_rt_blend.sksl
index 06b4a42..02d931d 100644
--- a/src/sksl/sksl_rt_blend.sksl
+++ b/src/sksl/sksl_rt_blend.sksl
@@ -1,2 +1,3 @@
half4 sample(shader s, float2 coords);
half4 sample(colorFilter f, half4 color);
+half4 sample(blender b, half4 src, half4 dst);
diff --git a/src/sksl/sksl_rt_colorfilter.sksl b/src/sksl/sksl_rt_colorfilter.sksl
index 06b4a42..02d931d 100644
--- a/src/sksl/sksl_rt_colorfilter.sksl
+++ b/src/sksl/sksl_rt_colorfilter.sksl
@@ -1,2 +1,3 @@
half4 sample(shader s, float2 coords);
half4 sample(colorFilter f, half4 color);
+half4 sample(blender b, half4 src, half4 dst);
diff --git a/src/sksl/sksl_rt_shader.sksl b/src/sksl/sksl_rt_shader.sksl
index e259cc3..5e24a04 100644
--- a/src/sksl/sksl_rt_shader.sksl
+++ b/src/sksl/sksl_rt_shader.sksl
@@ -2,3 +2,4 @@
half4 sample(shader s, float2 coords);
half4 sample(colorFilter f, half4 color);
+half4 sample(blender b, half4 src, half4 dst);
diff --git a/tests/SkRuntimeEffectTest.cpp b/tests/SkRuntimeEffectTest.cpp
index 9e2ebda..c139588 100644
--- a/tests/SkRuntimeEffectTest.cpp
+++ b/tests/SkRuntimeEffectTest.cpp
@@ -12,6 +12,7 @@
#include "include/core/SkData.h"
#include "include/core/SkPaint.h"
#include "include/core/SkSurface.h"
+#include "include/effects/SkBlenders.h"
#include "include/effects/SkRuntimeEffect.h"
#include "include/gpu/GrDirectContext.h"
#include "src/core/SkColorSpacePriv.h"
@@ -618,10 +619,10 @@
effect.test(0xFF888888);
// Fill the destination with a variety of colors (using the RGBW shader)
- SkPaint paint;
- paint.setShader(make_RGBW_shader());
- paint.setBlendMode(SkBlendMode::kSrc);
- surface->getCanvas()->drawPaint(paint);
+ SkPaint rgbwPaint;
+ rgbwPaint.setShader(make_RGBW_shader());
+ rgbwPaint.setBlendMode(SkBlendMode::kSrc);
+ surface->getCanvas()->drawPaint(rgbwPaint);
// Verify that we can read back the dest color exactly as-is (ignoring the source color)
// This is equivalent to the kDst blend mode.
@@ -643,13 +644,27 @@
// Sampling children
//
- // Sampling a null child should return the paint color
+ // Sampling a null shader/color filter should return the paint color.
effect.build("uniform shader child;"
"half4 main(half4 s, half4 d) { return sample(child, s.rg); }");
effect.child("child") = nullptr;
effect.test(0xFF00FFFF,
[](SkCanvas*, SkPaint* paint) { paint->setColor4f({1.0f, 1.0f, 0.0f, 1.0f}); });
+ effect.build("uniform colorFilter child;"
+ "half4 main(half4 s, half4 d) { return sample(child, s); }");
+ effect.child("child") = nullptr;
+ effect.test(0xFF00FFFF,
+ [](SkCanvas*, SkPaint* paint) { paint->setColor4f({1.0f, 1.0f, 0.0f, 1.0f}); });
+
+ // Sampling a null blender should do a src-over blend. Draw 50% black over RGBW to verify this.
+ surface->getCanvas()->drawPaint(rgbwPaint);
+ effect.build("uniform blender child;"
+ "half4 main(half4 s, half4 d) { return sample(child, s, d); }");
+ effect.child("child") = nullptr;
+ effect.test({0xFF000080, 0xFF008000, 0xFF800000, 0xFF808080},
+ [](SkCanvas*, SkPaint* paint) { paint->setColor4f({0.0f, 0.0f, 0.0f, 0.497f}); });
+
// Sampling a shader at various coordinates
effect.build("uniform shader child;"
"uniform half2 pos;"
@@ -672,6 +687,22 @@
"half4 main(half4 s, half4 d) { return sample(child, half4(1)); }");
effect.child("child") = SkColorFilters::Blend(0xFF012345, SkBlendMode::kSrc);
effect.test(0xFF452301);
+
+ // Sampling a built-in blender
+ surface->getCanvas()->drawPaint(rgbwPaint);
+ effect.build("uniform blender child;"
+ "half4 main(half4 s, half4 d) { return sample(child, s, d); }");
+ effect.child("child") = SkBlender::Mode(SkBlendMode::kPlus);
+ effect.test({0xFF4523FF, 0xFF45FF01, 0xFFFF2301, 0xFFFFFFFF},
+ [](SkCanvas*, SkPaint* paint) { paint->setColor(0xFF012345); });
+
+ // Sampling a runtime-effect blender
+ surface->getCanvas()->drawPaint(rgbwPaint);
+ effect.build("uniform blender child;"
+ "half4 main(half4 s, half4 d) { return sample(child, s, d); }");
+ effect.child("child") = SkBlenders::Arithmetic(0, 1, 1, 0, /*enforcePremul=*/false);
+ effect.test({0xFF4523FF, 0xFF45FF01, 0xFFFF2301, 0xFFFFFFFF},
+ [](SkCanvas*, SkPaint* paint) { paint->setColor(0xFF012345); });
}
DEF_TEST(SkRuntimeEffect_Blender_CPU, r) {