aboutsummaryrefslogtreecommitdiffhomepage
path: root/src
diff options
context:
space:
mode:
authorGravatar Brian Salomon <bsalomon@google.com>2017-02-13 12:41:44 -0500
committerGravatar Skia Commit-Bot <skia-commit-bot@chromium.org>2017-02-13 18:14:12 +0000
commita12c15376ce3f94c81f461120067f8bbcf0a0c69 (patch)
tree3d77a9d562b9b764aa15c40bed9a2ded7584a6af /src
parent1fd1823b4775040b6a9723db90029d037c34ea54 (diff)
Add preserves premul and modulate optimization to compose fragment processors.
Fixes out of range colors produced by matrix convolution and dither effects. Adds modulate optimization to matrix convolution. Change-Id: I8424250a52e864f4b5feaf4474293695c26039d8 Reviewed-on: https://skia-review.googlesource.com/8351 Commit-Queue: Brian Salomon <bsalomon@google.com> Reviewed-by: Greg Daniel <egdaniel@google.com>
Diffstat (limited to 'src')
-rw-r--r--src/gpu/effects/GrDitherEffect.cpp2
-rw-r--r--src/gpu/effects/GrMatrixConvolutionEffect.cpp8
-rw-r--r--src/gpu/effects/GrXfermodeFragmentProcessor.cpp199
3 files changed, 180 insertions, 29 deletions
diff --git a/src/gpu/effects/GrDitherEffect.cpp b/src/gpu/effects/GrDitherEffect.cpp
index 036d42c4cf..adae12c979 100644
--- a/src/gpu/effects/GrDitherEffect.cpp
+++ b/src/gpu/effects/GrDitherEffect.cpp
@@ -73,7 +73,7 @@ void GLDitherEffect::emitCode(EmitArgs& args) {
fragBuilder->codeAppendf("\t\tfloat r = "
"fract(sin(dot(sk_FragCoord.xy, vec2(12.9898,78.233))) * "
"43758.5453);\n");
- fragBuilder->codeAppendf("\t\t%s = (1.0/255.0) * vec4(r, r, r, r) + %s;\n",
+ fragBuilder->codeAppendf("\t\t%s = clamp((1.0/255.0) * vec4(r, r, r, r) + %s, 0, 1);\n",
args.fOutputColor, GrGLSLExpr4(args.fInputColor).c_str());
}
diff --git a/src/gpu/effects/GrMatrixConvolutionEffect.cpp b/src/gpu/effects/GrMatrixConvolutionEffect.cpp
index 1a40514c83..fc8add85fd 100644
--- a/src/gpu/effects/GrMatrixConvolutionEffect.cpp
+++ b/src/gpu/effects/GrMatrixConvolutionEffect.cpp
@@ -98,6 +98,7 @@ void GrGLMatrixConvolutionEffect::emitCode(EmitArgs& args) {
}
if (mce.convolveAlpha()) {
fragBuilder->codeAppendf("%s = sum * %s + %s;", args.fOutputColor, gain, bias);
+ fragBuilder->codeAppendf("%s.a = clamp(%s.a, 0, 1);", args.fOutputColor, args.fOutputColor);
fragBuilder->codeAppendf("%s.rgb = clamp(%s.rgb, 0.0, %s.a);",
args.fOutputColor, args.fOutputColor, args.fOutputColor);
} else {
@@ -109,7 +110,7 @@ void GrGLMatrixConvolutionEffect::emitCode(EmitArgs& args) {
coords2D,
args.fTexSamplers[0]);
fragBuilder->codeAppendf("%s.a = c.a;", args.fOutputColor);
- fragBuilder->codeAppendf("%s.rgb = sum.rgb * %s + %s;", args.fOutputColor, gain, bias);
+ fragBuilder->codeAppendf("%s.rgb = clamp(sum.rgb * %s + %s, 0, 1);", args.fOutputColor, gain, bias);
fragBuilder->codeAppendf("%s.rgb *= %s.a;", args.fOutputColor, args.fOutputColor);
}
@@ -157,9 +158,8 @@ GrMatrixConvolutionEffect::GrMatrixConvolutionEffect(GrTexture* texture,
const SkIPoint& kernelOffset,
GrTextureDomain::Mode tileMode,
bool convolveAlpha)
- // To advertise either the modulation or opaqueness optimizations we'd have to examine the
- // parameters.
- : INHERITED(texture, nullptr, SkMatrix::I(), kNone_OptimizationFlags)
+ // To advertise the preserves opaqueness optimization we'd have to examine the parameters.
+ : INHERITED(texture, nullptr, SkMatrix::I(), kModulatesInput_OptimizationFlag)
, fKernelSize(kernelSize)
, fGain(SkScalarToFloat(gain))
, fBias(SkScalarToFloat(bias) / 255.0f)
diff --git a/src/gpu/effects/GrXfermodeFragmentProcessor.cpp b/src/gpu/effects/GrXfermodeFragmentProcessor.cpp
index 612ebeab69..5433b67352 100644
--- a/src/gpu/effects/GrXfermodeFragmentProcessor.cpp
+++ b/src/gpu/effects/GrXfermodeFragmentProcessor.cpp
@@ -1,9 +1,9 @@
/*
-* Copyright 2015 Google Inc.
-*
-* Use of this source code is governed by a BSD-style license that can be
-* found in the LICENSE file.
-*/
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
#include "effects/GrXfermodeFragmentProcessor.h"
@@ -49,11 +49,73 @@ public:
private:
static OptimizationFlags OptFlags(const GrFragmentProcessor* src,
const GrFragmentProcessor* dst, SkBlendMode mode) {
+ OptimizationFlags flags;
+ switch (mode) {
+ case SkBlendMode::kClear:
+ case SkBlendMode::kSrc:
+ case SkBlendMode::kDst:
+ SkFAIL("Should never create clear, src, or dst compose two FP.");
+ flags = kNone_OptimizationFlags;
+ break;
+
+ // Produces opaque if both src and dst are opaque.
+ case SkBlendMode::kSrcIn:
+ case SkBlendMode::kDstIn:
+ case SkBlendMode::kModulate:
+ flags = src->preservesOpaqueInput() && dst->preservesOpaqueInput()
+ ? kPreservesOpaqueInput_OptimizationFlag
+ : kNone_OptimizationFlags;
+ break;
+
+ // Produces zero when both are opaque, indeterminate if one is opaque.
+ case SkBlendMode::kSrcOut:
+ case SkBlendMode::kDstOut:
+ case SkBlendMode::kXor:
+ flags = kNone_OptimizationFlags;
+ break;
+
+ // Is opaque if the dst is opaque.
+ case SkBlendMode::kSrcATop:
+ flags = dst->preservesOpaqueInput() ? kPreservesOpaqueInput_OptimizationFlag
+ : kNone_OptimizationFlags;
+ break;
+
+ // DstATop is the converse of kSrcATop. Screen is also opaque if the src is a opaque.
+ case SkBlendMode::kDstATop:
+ case SkBlendMode::kScreen:
+ flags = src->preservesOpaqueInput() ? kPreservesOpaqueInput_OptimizationFlag
+ : kNone_OptimizationFlags;
+ break;
+
+ // These modes are all opaque if either src or dst is opaque. All the advanced modes
+ // compute alpha as src-over.
+ case SkBlendMode::kSrcOver:
+ case SkBlendMode::kDstOver:
+ case SkBlendMode::kPlus:
+ case SkBlendMode::kOverlay:
+ case SkBlendMode::kDarken:
+ case SkBlendMode::kLighten:
+ case SkBlendMode::kColorDodge:
+ case SkBlendMode::kColorBurn:
+ case SkBlendMode::kHardLight:
+ case SkBlendMode::kSoftLight:
+ case SkBlendMode::kDifference:
+ case SkBlendMode::kExclusion:
+ case SkBlendMode::kMultiply:
+ case SkBlendMode::kHue:
+ case SkBlendMode::kSaturation:
+ case SkBlendMode::kColor:
+ case SkBlendMode::kLuminosity:
+ flags = src->preservesOpaqueInput() || dst->preservesOpaqueInput()
+ ? kPreservesOpaqueInput_OptimizationFlag
+ : kNone_OptimizationFlags;
+ break;
+ }
if (does_cpu_blend_impl_match_gpu(mode) && src->hasConstantOutputForConstantInput() &&
dst->hasConstantOutputForConstantInput()) {
- return kConstantOutputForConstantInput_OptimizationFlag;
+ flags |= kConstantOutputForConstantInput_OptimizationFlag;
}
- return kNone_OptimizationFlags;
+ return flags;
}
bool onIsEqual(const GrFragmentProcessor& other) const override {
@@ -101,8 +163,10 @@ sk_sp<GrFragmentProcessor> ComposeTwoFragmentProcessor::TestCreate(GrProcessorTe
sk_sp<GrFragmentProcessor> fpA(GrProcessorUnitTest::MakeChildFP(d));
sk_sp<GrFragmentProcessor> fpB(GrProcessorUnitTest::MakeChildFP(d));
- SkBlendMode mode = static_cast<SkBlendMode>(
- d->fRandom->nextRangeU(0, (int)SkBlendMode::kLastMode));
+ SkBlendMode mode;
+ do {
+ mode = static_cast<SkBlendMode>(d->fRandom->nextRangeU(0, (int)SkBlendMode::kLastMode));
+ } while (SkBlendMode::kClear == mode || SkBlendMode::kSrc == mode || SkBlendMode::kDst == mode);
return sk_sp<GrFragmentProcessor>(
new ComposeTwoFragmentProcessor(std::move(fpA), std::move(fpB), mode));
}
@@ -172,10 +236,10 @@ public:
kSrc_Child,
};
- ComposeOneFragmentProcessor(sk_sp<GrFragmentProcessor> dst, SkBlendMode mode, Child child)
- : INHERITED(OptFlags(dst.get(), mode)), fMode(mode), fChild(child) {
+ ComposeOneFragmentProcessor(sk_sp<GrFragmentProcessor> fp, SkBlendMode mode, Child child)
+ : INHERITED(OptFlags(fp.get(), mode, child)), fMode(mode), fChild(child) {
this->initClassID<ComposeOneFragmentProcessor>();
- SkDEBUGCODE(int dstIndex = )this->registerChildProcessor(std::move(dst));
+ SkDEBUGCODE(int dstIndex =) this->registerChildProcessor(std::move(fp));
SkASSERT(0 == dstIndex);
}
@@ -200,11 +264,90 @@ public:
Child child() const { return fChild; }
private:
- OptimizationFlags OptFlags(const GrFragmentProcessor* child, SkBlendMode mode) {
- if (does_cpu_blend_impl_match_gpu(mode) && child->hasConstantOutputForConstantInput()) {
- return kConstantOutputForConstantInput_OptimizationFlag;
+ OptimizationFlags OptFlags(const GrFragmentProcessor* fp, SkBlendMode mode, Child child) {
+ OptimizationFlags flags;
+ switch (mode) {
+ case SkBlendMode::kClear:
+ SkFAIL("Should never create clear compose one FP.");
+ flags = kNone_OptimizationFlags;
+ break;
+
+ case SkBlendMode::kSrc:
+ SkASSERT(child == kSrc_Child);
+ flags = fp->preservesOpaqueInput() ? kPreservesOpaqueInput_OptimizationFlag
+ : kNone_OptimizationFlags;
+ break;
+
+ case SkBlendMode::kDst:
+ SkASSERT(child == kDst_Child);
+ flags = fp->preservesOpaqueInput() ? kPreservesOpaqueInput_OptimizationFlag
+ : kNone_OptimizationFlags;
+ break;
+
+ // Produces opaque if both src and dst are opaque. These also will modulate the child's
+ // output by either the input color or alpha.
+ case SkBlendMode::kSrcIn:
+ case SkBlendMode::kDstIn:
+ case SkBlendMode::kModulate:
+ flags = fp->preservesOpaqueInput()
+ ? kPreservesOpaqueInput_OptimizationFlag | kModulatesInput_OptimizationFlag
+ : kModulatesInput_OptimizationFlag;
+ break;
+
+ // Produces zero when both are opaque, indeterminate if one is opaque.
+ case SkBlendMode::kSrcOut:
+ case SkBlendMode::kDstOut:
+ case SkBlendMode::kXor:
+ flags = kNone_OptimizationFlags;
+ break;
+
+ // Is opaque if the dst is opaque.
+ case SkBlendMode::kSrcATop:
+ if (child == kDst_Child) {
+ flags = fp->preservesOpaqueInput() ? kPreservesOpaqueInput_OptimizationFlag
+ : kNone_OptimizationFlags;
+ } else {
+ flags = kPreservesOpaqueInput_OptimizationFlag;
+ }
+ break;
+
+ // DstATop is the converse of kSrcATop. Screen is also opaque if the src is a opaque.
+ case SkBlendMode::kDstATop:
+ case SkBlendMode::kScreen:
+ if (child == kSrc_Child) {
+ flags = fp->preservesOpaqueInput() ? kPreservesOpaqueInput_OptimizationFlag
+ : kNone_OptimizationFlags;
+ } else {
+ flags = kPreservesOpaqueInput_OptimizationFlag;
+ }
+ break;
+
+ // These modes are all opaque if either src or dst is opaque. All the advanced modes
+ // compute alpha as src-over.
+ case SkBlendMode::kSrcOver:
+ case SkBlendMode::kDstOver:
+ case SkBlendMode::kPlus:
+ case SkBlendMode::kOverlay:
+ case SkBlendMode::kDarken:
+ case SkBlendMode::kLighten:
+ case SkBlendMode::kColorDodge:
+ case SkBlendMode::kColorBurn:
+ case SkBlendMode::kHardLight:
+ case SkBlendMode::kSoftLight:
+ case SkBlendMode::kDifference:
+ case SkBlendMode::kExclusion:
+ case SkBlendMode::kMultiply:
+ case SkBlendMode::kHue:
+ case SkBlendMode::kSaturation:
+ case SkBlendMode::kColor:
+ case SkBlendMode::kLuminosity:
+ flags = kPreservesOpaqueInput_OptimizationFlag;
+ break;
+ }
+ if (does_cpu_blend_impl_match_gpu(mode) && fp->hasConstantOutputForConstantInput()) {
+ flags |= kConstantOutputForConstantInput_OptimizationFlag;
}
- return kNone_OptimizationFlags;
+ return flags;
}
bool onIsEqual(const GrFragmentProcessor& that) const override {
@@ -280,11 +423,13 @@ sk_sp<GrFragmentProcessor> ComposeOneFragmentProcessor::TestCreate(GrProcessorTe
// For now, we'll prevent either children from being a shader with children to prevent the
// possibility of an arbitrarily large tree of procs.
sk_sp<GrFragmentProcessor> dst(GrProcessorUnitTest::MakeChildFP(d));
- SkBlendMode mode = static_cast<SkBlendMode>(
- d->fRandom->nextRangeU(0, (int)SkBlendMode::kLastMode));
- ComposeOneFragmentProcessor::Child child = d->fRandom->nextBool() ?
- ComposeOneFragmentProcessor::kDst_Child :
- ComposeOneFragmentProcessor::kSrc_Child;
+ SkBlendMode mode;
+ ComposeOneFragmentProcessor::Child child;
+ do {
+ mode = static_cast<SkBlendMode>(d->fRandom->nextRangeU(0, (int)SkBlendMode::kLastMode));
+ child = d->fRandom->nextBool() ? kDst_Child : kSrc_Child;
+ } while (SkBlendMode::kClear == mode || (SkBlendMode::kDst == mode && child == kSrc_Child) ||
+ (SkBlendMode::kSrc == mode && child == kDst_Child));
return sk_sp<GrFragmentProcessor>(new ComposeOneFragmentProcessor(std::move(dst), mode, child));
}
#endif
@@ -295,12 +440,18 @@ GrGLSLFragmentProcessor* ComposeOneFragmentProcessor::onCreateGLSLInstance() con
//////////////////////////////////////////////////////////////////////////////
+// It may seems as though when the input FP is the dst and the mode is kDst (or same for src/kSrc)
+// that these factories could simply return the input FP. However, that doesn't have quite
+// the same effect as the returned compose FP will replace the FP's input with solid white and
+// ignore the original input. This could be implemented as:
+// RunInSeries(ConstColor(GrColor_WHITE, kIgnoreInput), inputFP).
+
sk_sp<GrFragmentProcessor> GrXfermodeFragmentProcessor::MakeFromDstProcessor(
sk_sp<GrFragmentProcessor> dst, SkBlendMode mode) {
switch (mode) {
case SkBlendMode::kClear:
return GrConstColorProcessor::Make(GrColor4f::TransparentBlack(),
- GrConstColorProcessor::kIgnore_InputMode);
+ GrConstColorProcessor::kIgnore_InputMode);
case SkBlendMode::kSrc:
return nullptr;
default:
@@ -315,12 +466,12 @@ sk_sp<GrFragmentProcessor> GrXfermodeFragmentProcessor::MakeFromSrcProcessor(
switch (mode) {
case SkBlendMode::kClear:
return GrConstColorProcessor::Make(GrColor4f::TransparentBlack(),
- GrConstColorProcessor::kIgnore_InputMode);
+ GrConstColorProcessor::kIgnore_InputMode);
case SkBlendMode::kDst:
return nullptr;
default:
return sk_sp<GrFragmentProcessor>(
- new ComposeOneFragmentProcessor(src, mode,
+ new ComposeOneFragmentProcessor(std::move(src), mode,
ComposeOneFragmentProcessor::kSrc_Child));
}
}