| /* |
| * 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 "SkArithmeticMode_gpu.h" |
| |
| #if SK_SUPPORT_GPU |
| #include "GrContext.h" |
| #include "GrFragmentProcessor.h" |
| #include "GrInvariantOutput.h" |
| #include "GrProcessor.h" |
| #include "GrTexture.h" |
| #include "gl/GrGLCaps.h" |
| #include "gl/GrGLFragmentProcessor.h" |
| #include "gl/builders/GrGLProgramBuilder.h" |
| #include "glsl/GrGLSLProgramDataManager.h" |
| |
| static const bool gUseUnpremul = false; |
| |
| static void add_arithmetic_code(GrGLFragmentBuilder* fsBuilder, |
| const char* srcColor, |
| const char* dstColor, |
| const char* outputColor, |
| const char* kUni, |
| bool enforcePMColor) { |
| // We don't try to optimize for this case at all |
| if (nullptr == srcColor) { |
| fsBuilder->codeAppend("const vec4 src = vec4(1);"); |
| } else { |
| fsBuilder->codeAppendf("vec4 src = %s;", srcColor); |
| if (gUseUnpremul) { |
| fsBuilder->codeAppend("src.rgb = clamp(src.rgb / src.a, 0.0, 1.0);"); |
| } |
| } |
| |
| fsBuilder->codeAppendf("vec4 dst = %s;", dstColor); |
| if (gUseUnpremul) { |
| fsBuilder->codeAppend("dst.rgb = clamp(dst.rgb / dst.a, 0.0, 1.0);"); |
| } |
| |
| fsBuilder->codeAppendf("%s = %s.x * src * dst + %s.y * src + %s.z * dst + %s.w;", |
| outputColor, kUni, kUni, kUni, kUni); |
| fsBuilder->codeAppendf("%s = clamp(%s, 0.0, 1.0);\n", outputColor, outputColor); |
| if (gUseUnpremul) { |
| fsBuilder->codeAppendf("%s.rgb *= %s.a;", outputColor, outputColor); |
| } else if (enforcePMColor) { |
| fsBuilder->codeAppendf("%s.rgb = min(%s.rgb, %s.a);", |
| outputColor, outputColor, outputColor); |
| } |
| } |
| |
| class GLArithmeticFP : public GrGLFragmentProcessor { |
| public: |
| GLArithmeticFP(const GrArithmeticFP& arithmeticFP) |
| : fEnforcePMColor(arithmeticFP.enforcePMColor()) {} |
| |
| ~GLArithmeticFP() override {} |
| |
| void emitCode(EmitArgs& args) override { |
| GrGLFragmentBuilder* fsBuilder = args.fBuilder->getFragmentShaderBuilder(); |
| SkString dstColor("dstColor"); |
| this->emitChild(0, nullptr, &dstColor, args); |
| |
| fKUni = args.fBuilder->addUniform(GrGLProgramBuilder::kFragment_Visibility, |
| kVec4f_GrSLType, kDefault_GrSLPrecision, |
| "k"); |
| const char* kUni = args.fBuilder->getUniformCStr(fKUni); |
| |
| add_arithmetic_code(fsBuilder, args.fInputColor, dstColor.c_str(), args.fOutputColor, kUni, |
| fEnforcePMColor); |
| } |
| |
| static void GenKey(const GrProcessor& proc, const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) { |
| const GrArithmeticFP& arith = proc.cast<GrArithmeticFP>(); |
| uint32_t key = arith.enforcePMColor() ? 1 : 0; |
| b->add32(key); |
| } |
| |
| protected: |
| void onSetData(const GrGLSLProgramDataManager& pdman, const GrProcessor& proc) override { |
| const GrArithmeticFP& arith = proc.cast<GrArithmeticFP>(); |
| pdman.set4f(fKUni, arith.k1(), arith.k2(), arith.k3(), arith.k4()); |
| fEnforcePMColor = arith.enforcePMColor(); |
| } |
| |
| private: |
| GrGLSLProgramDataManager::UniformHandle fKUni; |
| bool fEnforcePMColor; |
| |
| typedef GrGLFragmentProcessor INHERITED; |
| }; |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| |
| GrArithmeticFP::GrArithmeticFP(float k1, float k2, float k3, float k4, bool enforcePMColor, |
| const GrFragmentProcessor* dst) |
| : fK1(k1), fK2(k2), fK3(k3), fK4(k4), fEnforcePMColor(enforcePMColor) { |
| this->initClassID<GrArithmeticFP>(); |
| |
| SkASSERT(dst); |
| SkDEBUGCODE(int dstIndex = )this->registerChildProcessor(dst); |
| SkASSERT(0 == dstIndex); |
| } |
| |
| void GrArithmeticFP::onGetGLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const { |
| GLArithmeticFP::GenKey(*this, caps, b); |
| } |
| |
| GrGLFragmentProcessor* GrArithmeticFP::onCreateGLInstance() const { |
| return new GLArithmeticFP(*this); |
| } |
| |
| bool GrArithmeticFP::onIsEqual(const GrFragmentProcessor& fpBase) const { |
| const GrArithmeticFP& fp = fpBase.cast<GrArithmeticFP>(); |
| return fK1 == fp.fK1 && |
| fK2 == fp.fK2 && |
| fK3 == fp.fK3 && |
| fK4 == fp.fK4 && |
| fEnforcePMColor == fp.fEnforcePMColor; |
| } |
| |
| void GrArithmeticFP::onComputeInvariantOutput(GrInvariantOutput* inout) const { |
| // TODO: optimize this |
| inout->setToUnknown(GrInvariantOutput::kWill_ReadInput); |
| } |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| |
| const GrFragmentProcessor* GrArithmeticFP::TestCreate(GrProcessorTestData* d) { |
| float k1 = d->fRandom->nextF(); |
| float k2 = d->fRandom->nextF(); |
| float k3 = d->fRandom->nextF(); |
| float k4 = d->fRandom->nextF(); |
| bool enforcePMColor = d->fRandom->nextBool(); |
| |
| SkAutoTUnref<const GrFragmentProcessor> dst(GrProcessorUnitTest::CreateChildFP(d)); |
| return new GrArithmeticFP(k1, k2, k3, k4, enforcePMColor, dst); |
| } |
| |
| GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrArithmeticFP); |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| // Xfer Processor |
| /////////////////////////////////////////////////////////////////////////////// |
| |
| class ArithmeticXP : public GrXferProcessor { |
| public: |
| ArithmeticXP(const DstTexture*, bool hasMixedSamples, |
| float k1, float k2, float k3, float k4, bool enforcePMColor); |
| |
| const char* name() const override { return "Arithmetic"; } |
| |
| GrGLXferProcessor* createGLInstance() const override; |
| |
| float k1() const { return fK1; } |
| float k2() const { return fK2; } |
| float k3() const { return fK3; } |
| float k4() const { return fK4; } |
| bool enforcePMColor() const { return fEnforcePMColor; } |
| |
| private: |
| GrXferProcessor::OptFlags onGetOptimizations(const GrProcOptInfo& colorPOI, |
| const GrProcOptInfo& coveragePOI, |
| bool doesStencilWrite, |
| GrColor* overrideColor, |
| const GrCaps& caps) override; |
| |
| void onGetGLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const override; |
| |
| bool onIsEqual(const GrXferProcessor& xpBase) const override { |
| const ArithmeticXP& xp = xpBase.cast<ArithmeticXP>(); |
| if (fK1 != xp.fK1 || |
| fK2 != xp.fK2 || |
| fK3 != xp.fK3 || |
| fK4 != xp.fK4 || |
| fEnforcePMColor != xp.fEnforcePMColor) { |
| return false; |
| } |
| return true; |
| } |
| |
| float fK1, fK2, fK3, fK4; |
| bool fEnforcePMColor; |
| |
| typedef GrXferProcessor INHERITED; |
| }; |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| |
| class GLArithmeticXP : public GrGLXferProcessor { |
| public: |
| GLArithmeticXP(const ArithmeticXP& arithmeticXP) |
| : fEnforcePMColor(arithmeticXP.enforcePMColor()) { |
| } |
| |
| ~GLArithmeticXP() override {} |
| |
| static void GenKey(const GrProcessor& processor, const GrGLSLCaps& caps, |
| GrProcessorKeyBuilder* b) { |
| const ArithmeticXP& arith = processor.cast<ArithmeticXP>(); |
| uint32_t key = arith.enforcePMColor() ? 1 : 0; |
| b->add32(key); |
| } |
| |
| private: |
| void emitBlendCodeForDstRead(GrGLXPBuilder* pb, const char* srcColor, const char* dstColor, |
| const char* outColor, const GrXferProcessor& proc) override { |
| GrGLXPFragmentBuilder* fsBuilder = pb->getFragmentShaderBuilder(); |
| |
| fKUni = pb->addUniform(GrGLProgramBuilder::kFragment_Visibility, |
| kVec4f_GrSLType, kDefault_GrSLPrecision, |
| "k"); |
| const char* kUni = pb->getUniformCStr(fKUni); |
| |
| add_arithmetic_code(fsBuilder, srcColor, dstColor, outColor, kUni, fEnforcePMColor); |
| } |
| |
| void onSetData(const GrGLSLProgramDataManager& pdman, |
| const GrXferProcessor& processor) override { |
| const ArithmeticXP& arith = processor.cast<ArithmeticXP>(); |
| pdman.set4f(fKUni, arith.k1(), arith.k2(), arith.k3(), arith.k4()); |
| fEnforcePMColor = arith.enforcePMColor(); |
| }; |
| |
| GrGLSLProgramDataManager::UniformHandle fKUni; |
| bool fEnforcePMColor; |
| |
| typedef GrGLXferProcessor INHERITED; |
| }; |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| |
| ArithmeticXP::ArithmeticXP(const DstTexture* dstTexture, bool hasMixedSamples, |
| float k1, float k2, float k3, float k4, bool enforcePMColor) |
| : INHERITED(dstTexture, true, hasMixedSamples) |
| , fK1(k1) |
| , fK2(k2) |
| , fK3(k3) |
| , fK4(k4) |
| , fEnforcePMColor(enforcePMColor) { |
| this->initClassID<ArithmeticXP>(); |
| } |
| |
| void ArithmeticXP::onGetGLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const { |
| GLArithmeticXP::GenKey(*this, caps, b); |
| } |
| |
| GrGLXferProcessor* ArithmeticXP::createGLInstance() const { return new GLArithmeticXP(*this); } |
| |
| GrXferProcessor::OptFlags ArithmeticXP::onGetOptimizations(const GrProcOptInfo& colorPOI, |
| const GrProcOptInfo& coveragePOI, |
| bool doesStencilWrite, |
| GrColor* overrideColor, |
| const GrCaps& caps) { |
| return GrXferProcessor::kNone_OptFlags; |
| } |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| |
| GrArithmeticXPFactory::GrArithmeticXPFactory(float k1, float k2, float k3, float k4, |
| bool enforcePMColor) |
| : fK1(k1), fK2(k2), fK3(k3), fK4(k4), fEnforcePMColor(enforcePMColor) { |
| this->initClassID<GrArithmeticXPFactory>(); |
| } |
| |
| GrXferProcessor* |
| GrArithmeticXPFactory::onCreateXferProcessor(const GrCaps& caps, |
| const GrProcOptInfo& colorPOI, |
| const GrProcOptInfo& coveragePOI, |
| bool hasMixedSamples, |
| const DstTexture* dstTexture) const { |
| return new ArithmeticXP(dstTexture, hasMixedSamples, fK1, fK2, fK3, fK4, fEnforcePMColor); |
| } |
| |
| |
| void GrArithmeticXPFactory::getInvariantBlendedColor(const GrProcOptInfo& colorPOI, |
| InvariantBlendedColor* blendedColor) const { |
| blendedColor->fWillBlendWithDst = true; |
| |
| // TODO: We could try to optimize this more. For example if fK1 and fK3 are zero, then we won't |
| // be blending the color with dst at all so we can know what the output color is (up to the |
| // valid color components passed in). |
| blendedColor->fKnownColorFlags = kNone_GrColorComponentFlags; |
| } |
| |
| GR_DEFINE_XP_FACTORY_TEST(GrArithmeticXPFactory); |
| |
| const GrXPFactory* GrArithmeticXPFactory::TestCreate(GrProcessorTestData* d) { |
| float k1 = d->fRandom->nextF(); |
| float k2 = d->fRandom->nextF(); |
| float k3 = d->fRandom->nextF(); |
| float k4 = d->fRandom->nextF(); |
| bool enforcePMColor = d->fRandom->nextBool(); |
| |
| return GrArithmeticXPFactory::Create(k1, k2, k3, k4, enforcePMColor); |
| } |
| |
| #endif |