| 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);
|
| + }
|
| +}
|
|
|