Index: src/gpu/effects/GrXfermodeFragmentProcessor.cpp |
diff --git a/src/gpu/effects/GrXfermodeFragmentProcessor.cpp b/src/gpu/effects/GrXfermodeFragmentProcessor.cpp |
new file mode 100644 |
index 0000000000000000000000000000000000000000..aa47541cd0309e4a8c9f6f52d3670dcfa5926297 |
--- /dev/null |
+++ b/src/gpu/effects/GrXfermodeFragmentProcessor.cpp |
@@ -0,0 +1,168 @@ |
+/* |
+* 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" |
+ |
+#include "GrFragmentProcessor.h" |
+#include "effects/GrConstColorProcessor.h" |
+#include "gl/GrGLBlend.h" |
+#include "gl/builders/GrGLProgramBuilder.h" |
+ |
+ |
+class GrComposeTwoFragmentProcessor : public GrFragmentProcessor { |
+public: |
+ GrComposeTwoFragmentProcessor(const GrFragmentProcessor* src, const GrFragmentProcessor* dst, |
+ SkXfermode::Mode mode) |
+ : fMode(mode) { |
+ // Only coefficient xfer modes are supported |
+ SkASSERT(SkXfermode::kLastCoeffMode >= mode); |
+ this->initClassID<GrComposeTwoFragmentProcessor>(); |
+ SkDEBUGCODE(int shaderAChildIndex = )this->registerChildProcessor(src); |
+ SkDEBUGCODE(int shaderBChildIndex = )this->registerChildProcessor(dst); |
+ SkASSERT(0 == shaderAChildIndex); |
+ SkASSERT(1 == shaderBChildIndex); |
+ } |
+ |
+ const char* name() const override { return "ComposeShader"; } |
+ |
+ void onGetGLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const override { |
+ b->add32(fMode); |
+ } |
+ |
+ SkXfermode::Mode getMode() const { return fMode; } |
+ |
+protected: |
+ bool onIsEqual(const GrFragmentProcessor& other) const override { |
+ const GrComposeTwoFragmentProcessor& cs = other.cast<GrComposeTwoFragmentProcessor>(); |
+ return fMode == cs.fMode; |
+ } |
+ |
+ void onComputeInvariantOutput(GrInvariantOutput* inout) const override { |
+ inout->setToUnknown(GrInvariantOutput::kWill_ReadInput); |
+ } |
+ |
+private: |
+ GrGLFragmentProcessor* onCreateGLInstance() const override; |
+ |
+ SkXfermode::Mode fMode; |
+ |
+ GR_DECLARE_FRAGMENT_PROCESSOR_TEST; |
+ |
+ typedef GrFragmentProcessor INHERITED; |
+}; |
+ |
+///////////////////////////////////////////////////////////////////// |
+ |
+class GrGLComposeTwoFragmentProcessor : public GrGLFragmentProcessor { |
+public: |
+ GrGLComposeTwoFragmentProcessor(const GrProcessor& processor) {} |
+ |
+ void emitCode(EmitArgs&) override; |
+ |
+private: |
+ typedef GrGLFragmentProcessor INHERITED; |
+}; |
+ |
+///////////////////////////////////////////////////////////////////// |
+ |
+GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrComposeTwoFragmentProcessor); |
+ |
+const GrFragmentProcessor* GrComposeTwoFragmentProcessor::TestCreate(GrProcessorTestData* d) { |
+#if SK_ALLOW_STATIC_GLOBAL_INITIALIZERS |
+ // Create two random frag procs. |
+ // For now, we'll prevent either children from being a shader with children to prevent the |
+ // possibility of an arbitrarily large tree of procs. |
+ SkAutoTUnref<const GrFragmentProcessor> fpA; |
+ do { |
+ fpA.reset(GrProcessorTestFactory<GrFragmentProcessor>::CreateStage(d)); |
+ SkASSERT(fpA); |
+ } while (fpA->numChildProcessors() != 0); |
+ SkAutoTUnref<const GrFragmentProcessor> fpB; |
+ do { |
+ fpB.reset(GrProcessorTestFactory<GrFragmentProcessor>::CreateStage(d)); |
+ SkASSERT(fpB); |
+ } while (fpB->numChildProcessors() != 0); |
+ |
+ SkXfermode::Mode mode = static_cast<SkXfermode::Mode>( |
+ d->fRandom->nextRangeU(0, SkXfermode::kLastCoeffMode)); |
+ return SkNEW_ARGS(GrComposeTwoFragmentProcessor, (fpA, fpB, mode)); |
+#else |
+ SkFAIL("Should not be called if !SK_ALLOW_STATIC_GLOBAL_INITIALIZERS"); |
+ return nullptr; |
+#endif |
+} |
+ |
+GrGLFragmentProcessor* GrComposeTwoFragmentProcessor::onCreateGLInstance() const{ |
+ return SkNEW_ARGS(GrGLComposeTwoFragmentProcessor, (*this)); |
+} |
+ |
+///////////////////////////////////////////////////////////////////// |
+ |
+void GrGLComposeTwoFragmentProcessor::emitCode(EmitArgs& args) { |
+ |
+ GrGLFragmentBuilder* fsBuilder = args.fBuilder->getFragmentShaderBuilder(); |
+ const GrComposeTwoFragmentProcessor& cs = args.fFp.cast<GrComposeTwoFragmentProcessor>(); |
+ |
+ // Store alpha of input color and un-premultiply the input color by its alpha. We will |
+ // re-multiply by this alpha after blending the output colors of the two child procs. |
+ // This is because we don't want the paint's alpha to affect either child proc's output |
+ // before the blend; we want to apply the paint's alpha AFTER the blend. This mirrors the |
+ // software implementation of SkComposeShader. |
+ SkString inputAlpha("inputAlpha"); |
+ fsBuilder->codeAppendf("float %s = %s.a;", inputAlpha.c_str(), args.fInputColor); |
+ fsBuilder->codeAppendf("%s /= %s.a;", args.fInputColor, args.fInputColor); |
+ |
+ // declare outputColor and emit the code for each of the two children |
+ SkString outputColorSrc(args.fOutputColor); |
+ outputColorSrc.append("_src"); |
+ fsBuilder->codeAppendf("vec4 %s;\n", outputColorSrc.c_str()); |
+ this->emitChild(0, args.fInputColor, outputColorSrc.c_str(), args); |
+ |
+ SkString outputColorDst(args.fOutputColor); |
+ outputColorDst.append("_dst"); |
+ fsBuilder->codeAppendf("vec4 %s;\n", outputColorDst.c_str()); |
+ this->emitChild(1, args.fInputColor, outputColorDst.c_str(), args); |
+ |
+ // emit blend code |
+ SkXfermode::Mode mode = cs.getMode(); |
+ fsBuilder->codeAppend("{"); |
+ fsBuilder->codeAppendf("// Compose Xfer Mode: %s\n", SkXfermode::ModeName(mode)); |
+ GrGLBlend::AppendPorterDuffBlend(fsBuilder, outputColorSrc.c_str(), |
+ outputColorDst.c_str(), args.fOutputColor, mode); |
+ fsBuilder->codeAppend("}"); |
+ |
+ // re-multiply the output color by the input color's alpha |
+ fsBuilder->codeAppendf("%s *= %s;", args.fOutputColor, inputAlpha.c_str()); |
+} |
+ |
+ |
+const GrFragmentProcessor* GrXfermodeFragmentProcessor::CreateFromTwoProcessors( |
+ const GrFragmentProcessor* src, const GrFragmentProcessor* dst, SkXfermode::Mode mode) { |
+ if (SkXfermode::kLastCoeffMode < mode) { |
+ return nullptr; |
+ } |
+ switch (mode) { |
+ case SkXfermode::kClear_Mode: |
+ SkDebugf("CreateFromTwoProcessors() should not be used with kClear_Mode. " |
+ "Use GrConstColorProcessor.\n"); |
+ return GrConstColorProcessor::Create(GrColor_TRANS_BLACK, |
+ GrConstColorProcessor::kIgnore_InputMode); |
+ break; |
+ case SkXfermode::kSrc_Mode: |
+ SkDebugf("CreateFromTwoProcessors() should not be used with kSrc_Mode. " |
+ "Use the src processor directly.\n"); |
+ return SkRef(src); |
+ break; |
+ case SkXfermode::kDst_Mode: |
+ SkDebugf("CreateFromTwoProcessors() should not be used with kDst_Mode. " |
+ "Use the dst processor directly.\n"); |
+ return SkRef(dst); |
+ break; |
+ default: |
+ return new GrComposeTwoFragmentProcessor(src, dst, mode); |
+ } |
+} |