/* * 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 "GrInvariantOutput.h" #include "effects/GrConstColorProcessor.h" #include "glsl/GrGLSLFragmentProcessor.h" #include "glsl/GrGLSLBlend.h" #include "glsl/GrGLSLFragmentShaderBuilder.h" #include "SkGrPriv.h" class ComposeTwoFragmentProcessor : public GrFragmentProcessor { public: ComposeTwoFragmentProcessor(const GrFragmentProcessor* src, const GrFragmentProcessor* dst, SkXfermode::Mode mode) : fMode(mode) { this->initClassID<ComposeTwoFragmentProcessor>(); SkDEBUGCODE(int shaderAChildIndex = )this->registerChildProcessor(src); SkDEBUGCODE(int shaderBChildIndex = )this->registerChildProcessor(dst); SkASSERT(0 == shaderAChildIndex); SkASSERT(1 == shaderBChildIndex); } const char* name() const override { return "ComposeTwo"; } void onGetGLSLProcessorKey(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 ComposeTwoFragmentProcessor& cs = other.cast<ComposeTwoFragmentProcessor>(); return fMode == cs.fMode; } void onComputeInvariantOutput(GrInvariantOutput* inout) const override { inout->setToUnknown(GrInvariantOutput::kWill_ReadInput); } private: GrGLSLFragmentProcessor* onCreateGLSLInstance() const override; SkXfermode::Mode fMode; GR_DECLARE_FRAGMENT_PROCESSOR_TEST; typedef GrFragmentProcessor INHERITED; }; ///////////////////////////////////////////////////////////////////// class GLComposeTwoFragmentProcessor : public GrGLSLFragmentProcessor { public: void emitCode(EmitArgs&) override; private: typedef GrGLSLFragmentProcessor INHERITED; }; ///////////////////////////////////////////////////////////////////// GR_DEFINE_FRAGMENT_PROCESSOR_TEST(ComposeTwoFragmentProcessor); const GrFragmentProcessor* ComposeTwoFragmentProcessor::TestCreate(GrProcessorTestData* d) { // Create two random frag procs. SkAutoTUnref<const GrFragmentProcessor> fpA(GrProcessorUnitTest::CreateChildFP(d)); SkAutoTUnref<const GrFragmentProcessor> fpB(GrProcessorUnitTest::CreateChildFP(d)); SkXfermode::Mode mode = static_cast<SkXfermode::Mode>( d->fRandom->nextRangeU(0, SkXfermode::kLastMode)); return new ComposeTwoFragmentProcessor(fpA, fpB, mode); } GrGLSLFragmentProcessor* ComposeTwoFragmentProcessor::onCreateGLSLInstance() const{ return new GLComposeTwoFragmentProcessor; } ///////////////////////////////////////////////////////////////////// void GLComposeTwoFragmentProcessor::emitCode(EmitArgs& args) { GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder; const ComposeTwoFragmentProcessor& cs = args.fFp.cast<ComposeTwoFragmentProcessor>(); const char* inputColor = nullptr; if (args.fInputColor) { inputColor = "inputColor"; fragBuilder->codeAppendf("vec4 inputColor = vec4(%s.rgb, 1.0);", args.fInputColor); } // declare outputColor and emit the code for each of the two children SkString srcColor("src"); this->emitChild(0, inputColor, &srcColor, args); SkString dstColor("dst"); this->emitChild(1, inputColor, &dstColor, args); // emit blend code SkXfermode::Mode mode = cs.getMode(); fragBuilder->codeAppendf("// Compose Xfer Mode: %s\n", SkXfermode::ModeName(mode)); GrGLSLBlend::AppendMode(fragBuilder, srcColor.c_str(), dstColor.c_str(), args.fOutputColor, mode); // re-multiply the output color by the input color's alpha if (args.fInputColor) { fragBuilder->codeAppendf("%s *= %s.a;", args.fOutputColor, args.fInputColor); } } const GrFragmentProcessor* GrXfermodeFragmentProcessor::CreateFromTwoProcessors( const GrFragmentProcessor* src, const GrFragmentProcessor* dst, SkXfermode::Mode mode) { switch (mode) { case SkXfermode::kClear_Mode: return GrConstColorProcessor::Create(GrColor_TRANSPARENT_BLACK, GrConstColorProcessor::kIgnore_InputMode); case SkXfermode::kSrc_Mode: return SkRef(src); case SkXfermode::kDst_Mode: return SkRef(dst); default: return new ComposeTwoFragmentProcessor(src, dst, mode); } } ////////////////////////////////////////////////////////////////////////////// class ComposeOneFragmentProcessor : public GrFragmentProcessor { public: enum Child { kDst_Child, kSrc_Child, }; ComposeOneFragmentProcessor(const GrFragmentProcessor* dst, SkXfermode::Mode mode, Child child) : fMode(mode) , fChild(child) { this->initClassID<ComposeOneFragmentProcessor>(); SkDEBUGCODE(int dstIndex = )this->registerChildProcessor(dst); SkASSERT(0 == dstIndex); } const char* name() const override { return "ComposeOne"; } SkString dumpInfo() const override { SkString str; for (int i = 0; i < this->numChildProcessors(); ++i) { str.append(this->childProcessor(i).dumpInfo()); } return str; } void onGetGLSLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const override { GR_STATIC_ASSERT((SkXfermode::kLastMode & SK_MaxU16) == SkXfermode::kLastMode); b->add32(fMode | (fChild << 16)); } SkXfermode::Mode mode() const { return fMode; } Child child() const { return fChild; } protected: bool onIsEqual(const GrFragmentProcessor& that) const override { return fMode == that.cast<ComposeOneFragmentProcessor>().fMode; } void onComputeInvariantOutput(GrInvariantOutput* inout) const override { SkXfermode::Coeff skSrcCoeff, skDstCoeff; if (SkXfermode::ModeAsCoeff(fMode, &skSrcCoeff, &skDstCoeff)) { GrBlendCoeff srcCoeff = SkXfermodeCoeffToGrBlendCoeff(skSrcCoeff); GrBlendCoeff dstCoeff = SkXfermodeCoeffToGrBlendCoeff(skDstCoeff); GrInvariantOutput childOutput(0xFFFFFFFF, kRGBA_GrColorComponentFlags, false); this->childProcessor(0).computeInvariantOutput(&childOutput); GrColor blendColor; GrColorComponentFlags blendFlags; if (kDst_Child == fChild) { GrGetCoeffBlendKnownComponents(srcCoeff, dstCoeff, inout->color(), inout->validFlags(), childOutput.color(), childOutput.validFlags(), &blendColor, &blendFlags); } else { GrGetCoeffBlendKnownComponents(srcCoeff, dstCoeff, childOutput.color(), childOutput.validFlags(), inout->color(), inout->validFlags(), &blendColor, &blendFlags); } // will the shader code reference the input color? GrInvariantOutput::ReadInput readsInput = GrInvariantOutput::kWillNot_ReadInput; if (kDst_Child == fChild) { if (kZero_GrBlendCoeff != srcCoeff || GrBlendCoeffRefsSrc(dstCoeff)) { readsInput = GrInvariantOutput::kWill_ReadInput; } } else { if (kZero_GrBlendCoeff != dstCoeff || GrBlendCoeffRefsDst(srcCoeff)) { readsInput = GrInvariantOutput::kWill_ReadInput; } } inout->setToOther(blendFlags, blendColor, readsInput); } else { inout->setToUnknown(GrInvariantOutput::kWill_ReadInput); } } private: GrGLSLFragmentProcessor* onCreateGLSLInstance() const override; SkXfermode::Mode fMode; Child fChild; GR_DECLARE_FRAGMENT_PROCESSOR_TEST; typedef GrFragmentProcessor INHERITED; }; ////////////////////////////////////////////////////////////////////////////// class GLComposeOneFragmentProcessor : public GrGLSLFragmentProcessor { public: void emitCode(EmitArgs& args) override { GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder; SkXfermode::Mode mode = args.fFp.cast<ComposeOneFragmentProcessor>().mode(); ComposeOneFragmentProcessor::Child child = args.fFp.cast<ComposeOneFragmentProcessor>().child(); SkString childColor("child"); this->emitChild(0, nullptr, &childColor, args); const char* inputColor = args.fInputColor; // We don't try to optimize for this case at all if (!inputColor) { fragBuilder->codeAppendf("const vec4 ones = vec4(1);"); inputColor = "ones"; } // emit blend code fragBuilder->codeAppendf("// Compose Xfer Mode: %s\n", SkXfermode::ModeName(mode)); const char* childStr = childColor.c_str(); if (ComposeOneFragmentProcessor::kDst_Child == child) { GrGLSLBlend::AppendMode(fragBuilder, inputColor, childStr, args.fOutputColor, mode); } else { GrGLSLBlend::AppendMode(fragBuilder, childStr, inputColor, args.fOutputColor, mode); } } private: typedef GrGLSLFragmentProcessor INHERITED; }; ///////////////////////////////////////////////////////////////////// GR_DEFINE_FRAGMENT_PROCESSOR_TEST(ComposeOneFragmentProcessor); const GrFragmentProcessor* ComposeOneFragmentProcessor::TestCreate(GrProcessorTestData* d) { // Create one 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> dst(GrProcessorUnitTest::CreateChildFP(d)); SkXfermode::Mode mode = static_cast<SkXfermode::Mode>( d->fRandom->nextRangeU(0, SkXfermode::kLastMode)); ComposeOneFragmentProcessor::Child child = d->fRandom->nextBool() ? ComposeOneFragmentProcessor::kDst_Child : ComposeOneFragmentProcessor::kSrc_Child; return new ComposeOneFragmentProcessor(dst, mode, child); } GrGLSLFragmentProcessor* ComposeOneFragmentProcessor::onCreateGLSLInstance() const { return new GLComposeOneFragmentProcessor; } ////////////////////////////////////////////////////////////////////////////// const GrFragmentProcessor* GrXfermodeFragmentProcessor::CreateFromDstProcessor( const GrFragmentProcessor* dst, SkXfermode::Mode mode) { switch (mode) { case SkXfermode::kClear_Mode: return GrConstColorProcessor::Create(GrColor_TRANSPARENT_BLACK, GrConstColorProcessor::kIgnore_InputMode); case SkXfermode::kSrc_Mode: return nullptr; default: return new ComposeOneFragmentProcessor(dst, mode, ComposeOneFragmentProcessor::kDst_Child); } } const GrFragmentProcessor* GrXfermodeFragmentProcessor::CreateFromSrcProcessor( const GrFragmentProcessor* src, SkXfermode::Mode mode) { switch (mode) { case SkXfermode::kClear_Mode: return GrConstColorProcessor::Create(GrColor_TRANSPARENT_BLACK, GrConstColorProcessor::kIgnore_InputMode); case SkXfermode::kDst_Mode: return nullptr; default: return new ComposeOneFragmentProcessor(src, mode, ComposeOneFragmentProcessor::kSrc_Child); } }