Index: src/gpu/effects/GrPorterDuffXferProcessor.cpp |
diff --git a/src/gpu/effects/GrPorterDuffXferProcessor.cpp b/src/gpu/effects/GrPorterDuffXferProcessor.cpp |
index 77b7fcaac00c5e52a6d433124d513ea5ca8f208d..9a253cca13361064c319d1407d42b22027914ec6 100644 |
--- a/src/gpu/effects/GrPorterDuffXferProcessor.cpp |
+++ b/src/gpu/effects/GrPorterDuffXferProcessor.cpp |
@@ -1,709 +1,709 @@ |
-/* |
- * Copyright 2014 Google Inc. |
- * |
- * Use of this source code is governed by a BSD-style license that can be |
- * found in the LICENSE file. |
- */ |
- |
-#include "effects/GrPorterDuffXferProcessor.h" |
- |
-#include "GrBlend.h" |
-#include "GrDrawTargetCaps.h" |
-#include "GrProcessor.h" |
-#include "GrProcOptInfo.h" |
-#include "GrTypes.h" |
-#include "GrXferProcessor.h" |
-#include "gl/GrGLXferProcessor.h" |
-#include "gl/builders/GrGLFragmentShaderBuilder.h" |
-#include "gl/builders/GrGLProgramBuilder.h" |
- |
-static bool can_tweak_alpha_for_coverage(GrBlendCoeff dstCoeff) { |
- /* |
- The fractional coverage is f. |
- The src and dst coeffs are Cs and Cd. |
- The dst and src colors are S and D. |
- We want the blend to compute: f*Cs*S + (f*Cd + (1-f))D. By tweaking the source color's alpha |
- we're replacing S with S'=fS. It's obvious that that first term will always be ok. The second |
- term can be rearranged as [1-(1-Cd)f]D. By substituting in the various possibilities for Cd we |
- find that only 1, ISA, and ISC produce the correct destination when applied to S' and D. |
- */ |
- return kOne_GrBlendCoeff == dstCoeff || |
- kISA_GrBlendCoeff == dstCoeff || |
- kISC_GrBlendCoeff == dstCoeff; |
-} |
- |
-class PorterDuffXferProcessor : public GrXferProcessor { |
-public: |
- static GrXferProcessor* Create(GrBlendCoeff srcBlend, GrBlendCoeff dstBlend, |
- GrColor constant, const GrDeviceCoordTexture* dstCopy, |
- bool willReadDstColor) { |
- return SkNEW_ARGS(PorterDuffXferProcessor, (srcBlend, dstBlend, constant, dstCopy, |
- willReadDstColor)); |
- } |
- |
- ~PorterDuffXferProcessor() override; |
- |
- const char* name() const override { return "Porter Duff"; } |
- |
- GrGLXferProcessor* createGLInstance() const override; |
- |
- bool hasSecondaryOutput() const override; |
- |
- /////////////////////////////////////////////////////////////////////////// |
- /// @name Stage Output Types |
- //// |
- |
- enum PrimaryOutputType { |
- kNone_PrimaryOutputType, |
- kColor_PrimaryOutputType, |
- kCoverage_PrimaryOutputType, |
- // Modulate color and coverage, write result as the color output. |
- kModulate_PrimaryOutputType, |
- // Custom Porter-Duff output, used for when we explictly are reading the dst and blending |
- // in the shader. Secondary Output must be none if you use this. The custom blend uses the |
- // equation: cov * (coeffS * S + coeffD * D) + (1 - cov) * D |
- kCustom_PrimaryOutputType |
- }; |
- |
- enum SecondaryOutputType { |
- // There is no secondary output |
- kNone_SecondaryOutputType, |
- // Writes coverage as the secondary output. Only set if dual source blending is supported |
- // and primary output is kModulate. |
- kCoverage_SecondaryOutputType, |
- // Writes coverage * (1 - colorA) as the secondary output. Only set if dual source blending |
- // is supported and primary output is kModulate. |
- kCoverageISA_SecondaryOutputType, |
- // Writes coverage * (1 - colorRGBA) as the secondary output. Only set if dual source |
- // blending is supported and primary output is kModulate. |
- kCoverageISC_SecondaryOutputType, |
- |
- kSecondaryOutputTypeCnt, |
- }; |
- |
- PrimaryOutputType primaryOutputType() const { return fPrimaryOutputType; } |
- SecondaryOutputType secondaryOutputType() const { return fSecondaryOutputType; } |
- |
- GrXferProcessor::OptFlags getOptimizations(const GrProcOptInfo& colorPOI, |
- const GrProcOptInfo& coveragePOI, |
- bool doesStencilWrite, |
- GrColor* overrideColor, |
- const GrDrawTargetCaps& caps) override; |
- |
- GrBlendCoeff getSrcBlend() const { return fSrcBlend; } |
- GrBlendCoeff getDstBlend() const { return fDstBlend; } |
- |
-private: |
- PorterDuffXferProcessor(GrBlendCoeff srcBlend, GrBlendCoeff dstBlend, GrColor constant, |
- const GrDeviceCoordTexture* dstCopy, bool willReadDstColor); |
- |
- void onGetGLProcessorKey(const GrGLCaps& caps, GrProcessorKeyBuilder* b) const override; |
- |
- void onGetBlendInfo(GrXferProcessor::BlendInfo* blendInfo) const override { |
- if (!this->willReadDstColor()) { |
- blendInfo->fSrcBlend = fSrcBlend; |
- blendInfo->fDstBlend = fDstBlend; |
- } else { |
- blendInfo->fSrcBlend = kOne_GrBlendCoeff; |
- blendInfo->fDstBlend = kZero_GrBlendCoeff; |
- } |
- blendInfo->fBlendConstant = fBlendConstant; |
- } |
- |
- bool onIsEqual(const GrXferProcessor& xpBase) const override { |
- const PorterDuffXferProcessor& xp = xpBase.cast<PorterDuffXferProcessor>(); |
- if (fSrcBlend != xp.fSrcBlend || |
- fDstBlend != xp.fDstBlend || |
- fBlendConstant != xp.fBlendConstant || |
- fPrimaryOutputType != xp.fPrimaryOutputType || |
- fSecondaryOutputType != xp.fSecondaryOutputType) { |
- return false; |
- } |
- return true; |
- } |
- |
- GrXferProcessor::OptFlags internalGetOptimizations(const GrProcOptInfo& colorPOI, |
- const GrProcOptInfo& coveragePOI, |
- bool doesStencilWrite); |
- |
- void calcOutputTypes(GrXferProcessor::OptFlags blendOpts, const GrDrawTargetCaps& caps, |
- bool hasSolidCoverage); |
- |
- GrBlendCoeff fSrcBlend; |
- GrBlendCoeff fDstBlend; |
- GrColor fBlendConstant; |
- PrimaryOutputType fPrimaryOutputType; |
- SecondaryOutputType fSecondaryOutputType; |
- |
- typedef GrXferProcessor INHERITED; |
-}; |
- |
-/////////////////////////////////////////////////////////////////////////////// |
- |
-bool append_porterduff_term(GrGLFPFragmentBuilder* fsBuilder, GrBlendCoeff coeff, |
- const char* colorName, const char* srcColorName, |
- const char* dstColorName, bool hasPrevious) { |
- if (kZero_GrBlendCoeff == coeff) { |
- return hasPrevious; |
- } else { |
- if (hasPrevious) { |
- fsBuilder->codeAppend(" + "); |
- } |
- fsBuilder->codeAppendf("%s", colorName); |
- switch (coeff) { |
- case kOne_GrBlendCoeff: |
- break; |
- case kSC_GrBlendCoeff: |
- fsBuilder->codeAppendf(" * %s", srcColorName); |
- break; |
- case kISC_GrBlendCoeff: |
- fsBuilder->codeAppendf(" * (vec4(1.0) - %s)", srcColorName); |
- break; |
- case kDC_GrBlendCoeff: |
- fsBuilder->codeAppendf(" * %s", dstColorName); |
- break; |
- case kIDC_GrBlendCoeff: |
- fsBuilder->codeAppendf(" * (vec4(1.0) - %s)", dstColorName); |
- break; |
- case kSA_GrBlendCoeff: |
- fsBuilder->codeAppendf(" * %s.a", srcColorName); |
- break; |
- case kISA_GrBlendCoeff: |
- fsBuilder->codeAppendf(" * (1.0 - %s.a)", srcColorName); |
- break; |
- case kDA_GrBlendCoeff: |
- fsBuilder->codeAppendf(" * %s.a", dstColorName); |
- break; |
- case kIDA_GrBlendCoeff: |
- fsBuilder->codeAppendf(" * (1.0 - %s.a)", dstColorName); |
- break; |
- default: |
- SkFAIL("Unsupported Blend Coeff"); |
- } |
- return true; |
- } |
-} |
- |
-class GLPorterDuffXferProcessor : public GrGLXferProcessor { |
-public: |
- GLPorterDuffXferProcessor(const GrProcessor&) {} |
- |
- virtual ~GLPorterDuffXferProcessor() {} |
- |
- static void GenKey(const GrProcessor& processor, const GrGLCaps& caps, |
- GrProcessorKeyBuilder* b) { |
- const PorterDuffXferProcessor& xp = processor.cast<PorterDuffXferProcessor>(); |
- b->add32(xp.primaryOutputType()); |
- b->add32(xp.secondaryOutputType()); |
- if (xp.willReadDstColor()) { |
- b->add32(xp.getSrcBlend()); |
- b->add32(xp.getDstBlend()); |
- } |
- }; |
- |
-private: |
- void onEmitCode(const EmitArgs& args) override { |
- const PorterDuffXferProcessor& xp = args.fXP.cast<PorterDuffXferProcessor>(); |
- GrGLFPFragmentBuilder* fsBuilder = args.fPB->getFragmentShaderBuilder(); |
- if (PorterDuffXferProcessor::kCustom_PrimaryOutputType != xp.primaryOutputType()) { |
- SkASSERT(!xp.willReadDstColor()); |
- switch(xp.secondaryOutputType()) { |
- case PorterDuffXferProcessor::kNone_SecondaryOutputType: |
- break; |
- case PorterDuffXferProcessor::kCoverage_SecondaryOutputType: |
- fsBuilder->codeAppendf("%s = %s;", args.fOutputSecondary, |
- args.fInputCoverage); |
- break; |
- case PorterDuffXferProcessor::kCoverageISA_SecondaryOutputType: |
- fsBuilder->codeAppendf("%s = (1.0 - %s.a) * %s;", |
- args.fOutputSecondary, args.fInputColor, |
- args.fInputCoverage); |
- break; |
- case PorterDuffXferProcessor::kCoverageISC_SecondaryOutputType: |
- fsBuilder->codeAppendf("%s = (vec4(1.0) - %s) * %s;", |
- args.fOutputSecondary, args.fInputColor, |
- args.fInputCoverage); |
- break; |
- default: |
- SkFAIL("Unexpected Secondary Output"); |
- } |
- |
- switch (xp.primaryOutputType()) { |
- case PorterDuffXferProcessor::kNone_PrimaryOutputType: |
- fsBuilder->codeAppendf("%s = vec4(0);", args.fOutputPrimary); |
- break; |
- case PorterDuffXferProcessor::kColor_PrimaryOutputType: |
- fsBuilder->codeAppendf("%s = %s;", args.fOutputPrimary, args.fInputColor); |
- break; |
- case PorterDuffXferProcessor::kCoverage_PrimaryOutputType: |
- fsBuilder->codeAppendf("%s = %s;", args.fOutputPrimary, args.fInputCoverage); |
- break; |
- case PorterDuffXferProcessor::kModulate_PrimaryOutputType: |
- fsBuilder->codeAppendf("%s = %s * %s;", args.fOutputPrimary, args.fInputColor, |
- args.fInputCoverage); |
- break; |
- default: |
- SkFAIL("Unexpected Primary Output"); |
- } |
- } else { |
- SkASSERT(xp.willReadDstColor()); |
- |
- const char* dstColor = fsBuilder->dstColor(); |
- |
- fsBuilder->codeAppend("vec4 colorBlend ="); |
- // append src blend |
- bool didAppend = append_porterduff_term(fsBuilder, xp.getSrcBlend(), |
- args.fInputColor, args.fInputColor, |
- dstColor, false); |
- // append dst blend |
- SkAssertResult(append_porterduff_term(fsBuilder, xp.getDstBlend(), |
- dstColor, args.fInputColor, |
- dstColor, didAppend)); |
- fsBuilder->codeAppend(";"); |
- |
- fsBuilder->codeAppendf("%s = %s * colorBlend + (vec4(1.0) - %s) * %s;", |
- args.fOutputPrimary, args.fInputCoverage, args.fInputCoverage, |
- dstColor); |
- } |
- } |
- |
- void onSetData(const GrGLProgramDataManager&, const GrXferProcessor&) override {}; |
- |
- typedef GrGLXferProcessor INHERITED; |
-}; |
- |
-/////////////////////////////////////////////////////////////////////////////// |
- |
-PorterDuffXferProcessor::PorterDuffXferProcessor(GrBlendCoeff srcBlend, |
- GrBlendCoeff dstBlend, |
- GrColor constant, |
- const GrDeviceCoordTexture* dstCopy, |
- bool willReadDstColor) |
- : INHERITED(dstCopy, willReadDstColor) |
- , fSrcBlend(srcBlend) |
- , fDstBlend(dstBlend) |
- , fBlendConstant(constant) |
- , fPrimaryOutputType(kModulate_PrimaryOutputType) |
- , fSecondaryOutputType(kNone_SecondaryOutputType) { |
- this->initClassID<PorterDuffXferProcessor>(); |
-} |
- |
-PorterDuffXferProcessor::~PorterDuffXferProcessor() { |
-} |
- |
-void PorterDuffXferProcessor::onGetGLProcessorKey(const GrGLCaps& caps, |
- GrProcessorKeyBuilder* b) const { |
- GLPorterDuffXferProcessor::GenKey(*this, caps, b); |
-} |
- |
-GrGLXferProcessor* PorterDuffXferProcessor::createGLInstance() const { |
- return SkNEW_ARGS(GLPorterDuffXferProcessor, (*this)); |
-} |
- |
-GrXferProcessor::OptFlags |
-PorterDuffXferProcessor::getOptimizations(const GrProcOptInfo& colorPOI, |
- const GrProcOptInfo& coveragePOI, |
- bool doesStencilWrite, |
- GrColor* overrideColor, |
- const GrDrawTargetCaps& caps) { |
- GrXferProcessor::OptFlags optFlags; |
- // Optimizations when doing RGB Coverage |
- if (coveragePOI.isFourChannelOutput()) { |
- // We want to force our primary output to be alpha * Coverage, where alpha is the alpha |
- // value of the blend the constant. We should already have valid blend coeff's if we are at |
- // a point where we have RGB coverage. We don't need any color stages since the known color |
- // output is already baked into the blendConstant. |
- uint8_t alpha = GrColorUnpackA(fBlendConstant); |
- *overrideColor = GrColorPackRGBA(alpha, alpha, alpha, alpha); |
- optFlags = GrXferProcessor::kOverrideColor_OptFlag; |
- } else { |
- optFlags = this->internalGetOptimizations(colorPOI, |
- coveragePOI, |
- doesStencilWrite); |
- } |
- this->calcOutputTypes(optFlags, caps, coveragePOI.isSolidWhite()); |
- return optFlags; |
-} |
- |
-void PorterDuffXferProcessor::calcOutputTypes(GrXferProcessor::OptFlags optFlags, |
- const GrDrawTargetCaps& caps, |
- bool hasSolidCoverage) { |
- if (this->willReadDstColor()) { |
- fPrimaryOutputType = kCustom_PrimaryOutputType; |
- return; |
- } |
- |
- if (optFlags & kIgnoreColor_OptFlag) { |
- if (optFlags & kIgnoreCoverage_OptFlag) { |
- fPrimaryOutputType = kNone_PrimaryOutputType; |
- return; |
- } else { |
- fPrimaryOutputType = kCoverage_PrimaryOutputType; |
- return; |
- } |
- } else if (optFlags & kIgnoreCoverage_OptFlag) { |
- fPrimaryOutputType = kColor_PrimaryOutputType; |
- return; |
- } |
- |
- // If we do have coverage determine whether it matters. Dual source blending is expensive so |
- // we don't do it if we are doing coverage drawing. If we aren't then We always do dual source |
- // blending if we have any effective coverage stages OR the geometry processor doesn't emits |
- // solid coverage. |
- if (!(optFlags & kSetCoverageDrawing_OptFlag) && !hasSolidCoverage) { |
- if (caps.dualSourceBlendingSupport()) { |
- if (kZero_GrBlendCoeff == fDstBlend) { |
- // write the coverage value to second color |
- fSecondaryOutputType = kCoverage_SecondaryOutputType; |
- fDstBlend = kIS2C_GrBlendCoeff; |
- } else if (kSA_GrBlendCoeff == fDstBlend) { |
- // SA dst coeff becomes 1-(1-SA)*coverage when dst is partially covered. |
- fSecondaryOutputType = kCoverageISA_SecondaryOutputType; |
- fDstBlend = kIS2C_GrBlendCoeff; |
- } else if (kSC_GrBlendCoeff == fDstBlend) { |
- // SA dst coeff becomes 1-(1-SA)*coverage when dst is partially covered. |
- fSecondaryOutputType = kCoverageISC_SecondaryOutputType; |
- fDstBlend = kIS2C_GrBlendCoeff; |
- } |
- } |
- } |
-} |
- |
-GrXferProcessor::OptFlags |
-PorterDuffXferProcessor::internalGetOptimizations(const GrProcOptInfo& colorPOI, |
- const GrProcOptInfo& coveragePOI, |
- bool doesStencilWrite) { |
- if (this->willReadDstColor()) { |
- return GrXferProcessor::kNone_Opt; |
- } |
- |
- bool srcAIsOne = colorPOI.isOpaque(); |
- bool hasCoverage = !coveragePOI.isSolidWhite(); |
- |
- bool dstCoeffIsOne = kOne_GrBlendCoeff == fDstBlend || |
- (kSA_GrBlendCoeff == fDstBlend && srcAIsOne); |
- bool dstCoeffIsZero = kZero_GrBlendCoeff == fDstBlend || |
- (kISA_GrBlendCoeff == fDstBlend && srcAIsOne); |
- |
- // When coeffs are (0,1) there is no reason to draw at all, unless |
- // stenciling is enabled. Having color writes disabled is effectively |
- // (0,1). |
- if ((kZero_GrBlendCoeff == fSrcBlend && dstCoeffIsOne)) { |
- if (doesStencilWrite) { |
- return GrXferProcessor::kIgnoreColor_OptFlag | |
- GrXferProcessor::kSetCoverageDrawing_OptFlag; |
- } else { |
- fDstBlend = kOne_GrBlendCoeff; |
- return GrXferProcessor::kSkipDraw_OptFlag; |
- } |
- } |
- |
- // if we don't have coverage we can check whether the dst |
- // has to read at all. If not, we'll disable blending. |
- if (!hasCoverage) { |
- if (dstCoeffIsZero) { |
- if (kOne_GrBlendCoeff == fSrcBlend) { |
- // if there is no coverage and coeffs are (1,0) then we |
- // won't need to read the dst at all, it gets replaced by src |
- fDstBlend = kZero_GrBlendCoeff; |
- return GrXferProcessor::kNone_Opt; |
- } else if (kZero_GrBlendCoeff == fSrcBlend) { |
- // if the op is "clear" then we don't need to emit a color |
- // or blend, just write transparent black into the dst. |
- fSrcBlend = kOne_GrBlendCoeff; |
- fDstBlend = kZero_GrBlendCoeff; |
- return GrXferProcessor::kIgnoreColor_OptFlag | |
- GrXferProcessor::kIgnoreCoverage_OptFlag; |
- } |
- } |
- } else { |
- // check whether coverage can be safely rolled into alpha |
- // of if we can skip color computation and just emit coverage |
- if (can_tweak_alpha_for_coverage(fDstBlend)) { |
- if (colorPOI.allStagesMultiplyInput()) { |
- return GrXferProcessor::kSetCoverageDrawing_OptFlag | |
- GrXferProcessor::kCanTweakAlphaForCoverage_OptFlag; |
- } else { |
- return GrXferProcessor::kSetCoverageDrawing_OptFlag; |
- |
- } |
- } |
- if (dstCoeffIsZero) { |
- if (kZero_GrBlendCoeff == fSrcBlend) { |
- // the source color is not included in the blend |
- // the dst coeff is effectively zero so blend works out to: |
- // (c)(0)D + (1-c)D = (1-c)D. |
- fDstBlend = kISA_GrBlendCoeff; |
- return GrXferProcessor::kIgnoreColor_OptFlag | |
- GrXferProcessor::kSetCoverageDrawing_OptFlag; |
- } else if (srcAIsOne) { |
- // the dst coeff is effectively zero so blend works out to: |
- // cS + (c)(0)D + (1-c)D = cS + (1-c)D. |
- // If Sa is 1 then we can replace Sa with c |
- // and set dst coeff to 1-Sa. |
- fDstBlend = kISA_GrBlendCoeff; |
- if (colorPOI.allStagesMultiplyInput()) { |
- return GrXferProcessor::kSetCoverageDrawing_OptFlag | |
- GrXferProcessor::kCanTweakAlphaForCoverage_OptFlag; |
- } else { |
- return GrXferProcessor::kSetCoverageDrawing_OptFlag; |
- |
- } |
- } |
- } else if (dstCoeffIsOne) { |
- // the dst coeff is effectively one so blend works out to: |
- // cS + (c)(1)D + (1-c)D = cS + D. |
- fDstBlend = kOne_GrBlendCoeff; |
- if (colorPOI.allStagesMultiplyInput()) { |
- return GrXferProcessor::kSetCoverageDrawing_OptFlag | |
- GrXferProcessor::kCanTweakAlphaForCoverage_OptFlag; |
- } else { |
- return GrXferProcessor::kSetCoverageDrawing_OptFlag; |
- |
- } |
- return GrXferProcessor::kSetCoverageDrawing_OptFlag; |
- } |
- } |
- |
- return GrXferProcessor::kNone_Opt; |
-} |
- |
-bool PorterDuffXferProcessor::hasSecondaryOutput() const { |
- return kNone_SecondaryOutputType != fSecondaryOutputType; |
-} |
- |
-/////////////////////////////////////////////////////////////////////////////// |
- |
-GrPorterDuffXPFactory::GrPorterDuffXPFactory(GrBlendCoeff src, GrBlendCoeff dst) |
- : fSrcCoeff(src), fDstCoeff(dst) { |
- this->initClassID<GrPorterDuffXPFactory>(); |
-} |
- |
-GrXPFactory* GrPorterDuffXPFactory::Create(SkXfermode::Mode mode) { |
- switch (mode) { |
- case SkXfermode::kClear_Mode: { |
- static GrPorterDuffXPFactory gClearPDXPF(kZero_GrBlendCoeff, kZero_GrBlendCoeff); |
- return SkRef(&gClearPDXPF); |
- break; |
- } |
- case SkXfermode::kSrc_Mode: { |
- static GrPorterDuffXPFactory gSrcPDXPF(kOne_GrBlendCoeff, kZero_GrBlendCoeff); |
- return SkRef(&gSrcPDXPF); |
- break; |
- } |
- case SkXfermode::kDst_Mode: { |
- static GrPorterDuffXPFactory gDstPDXPF(kZero_GrBlendCoeff, kOne_GrBlendCoeff); |
- return SkRef(&gDstPDXPF); |
- break; |
- } |
- case SkXfermode::kSrcOver_Mode: { |
- static GrPorterDuffXPFactory gSrcOverPDXPF(kOne_GrBlendCoeff, kISA_GrBlendCoeff); |
- return SkRef(&gSrcOverPDXPF); |
- break; |
- } |
- case SkXfermode::kDstOver_Mode: { |
- static GrPorterDuffXPFactory gDstOverPDXPF(kIDA_GrBlendCoeff, kOne_GrBlendCoeff); |
- return SkRef(&gDstOverPDXPF); |
- break; |
- } |
- case SkXfermode::kSrcIn_Mode: { |
- static GrPorterDuffXPFactory gSrcInPDXPF(kDA_GrBlendCoeff, kZero_GrBlendCoeff); |
- return SkRef(&gSrcInPDXPF); |
- break; |
- } |
- case SkXfermode::kDstIn_Mode: { |
- static GrPorterDuffXPFactory gDstInPDXPF(kZero_GrBlendCoeff, kSA_GrBlendCoeff); |
- return SkRef(&gDstInPDXPF); |
- break; |
- } |
- case SkXfermode::kSrcOut_Mode: { |
- static GrPorterDuffXPFactory gSrcOutPDXPF(kIDA_GrBlendCoeff, kZero_GrBlendCoeff); |
- return SkRef(&gSrcOutPDXPF); |
- break; |
- } |
- case SkXfermode::kDstOut_Mode: { |
- static GrPorterDuffXPFactory gDstOutPDXPF(kZero_GrBlendCoeff, kISA_GrBlendCoeff); |
- return SkRef(&gDstOutPDXPF); |
- break; |
- } |
- case SkXfermode::kSrcATop_Mode: { |
- static GrPorterDuffXPFactory gSrcATopPDXPF(kDA_GrBlendCoeff, kISA_GrBlendCoeff); |
- return SkRef(&gSrcATopPDXPF); |
- break; |
- } |
- case SkXfermode::kDstATop_Mode: { |
- static GrPorterDuffXPFactory gDstATopPDXPF(kIDA_GrBlendCoeff, kSA_GrBlendCoeff); |
- return SkRef(&gDstATopPDXPF); |
- break; |
- } |
- case SkXfermode::kXor_Mode: { |
- static GrPorterDuffXPFactory gXorPDXPF(kIDA_GrBlendCoeff, kISA_GrBlendCoeff); |
- return SkRef(&gXorPDXPF); |
- break; |
- } |
- case SkXfermode::kPlus_Mode: { |
- static GrPorterDuffXPFactory gPlusPDXPF(kOne_GrBlendCoeff, kOne_GrBlendCoeff); |
- return SkRef(&gPlusPDXPF); |
- break; |
- } |
- case SkXfermode::kModulate_Mode: { |
- static GrPorterDuffXPFactory gModulatePDXPF(kZero_GrBlendCoeff, kSC_GrBlendCoeff); |
- return SkRef(&gModulatePDXPF); |
- break; |
- } |
- case SkXfermode::kScreen_Mode: { |
- static GrPorterDuffXPFactory gScreenPDXPF(kOne_GrBlendCoeff, kISC_GrBlendCoeff); |
- return SkRef(&gScreenPDXPF); |
- break; |
- } |
- default: |
- return NULL; |
- } |
-} |
- |
-GrXferProcessor* |
-GrPorterDuffXPFactory::onCreateXferProcessor(const GrDrawTargetCaps& caps, |
- const GrProcOptInfo& colorPOI, |
- const GrProcOptInfo& covPOI, |
- const GrDeviceCoordTexture* dstCopy) const { |
- if (!covPOI.isFourChannelOutput()) { |
- return PorterDuffXferProcessor::Create(fSrcCoeff, fDstCoeff, 0, dstCopy, |
- this->willReadDstColor(caps, colorPOI, covPOI)); |
- } else { |
- if (this->supportsRGBCoverage(colorPOI.color(), colorPOI.validFlags())) { |
- SkASSERT(kRGBA_GrColorComponentFlags == colorPOI.validFlags()); |
- GrColor blendConstant = GrUnPreMulColor(colorPOI.color()); |
- return PorterDuffXferProcessor::Create(kConstC_GrBlendCoeff, kISC_GrBlendCoeff, |
- blendConstant, dstCopy, |
- this->willReadDstColor(caps, colorPOI, covPOI)); |
- } else { |
- return NULL; |
- } |
- } |
-} |
- |
-bool GrPorterDuffXPFactory::supportsRGBCoverage(GrColor /*knownColor*/, |
- uint32_t knownColorFlags) const { |
- if (kOne_GrBlendCoeff == fSrcCoeff && kISA_GrBlendCoeff == fDstCoeff && |
- kRGBA_GrColorComponentFlags == knownColorFlags) { |
- return true; |
- } |
- return false; |
-} |
- |
-bool GrPorterDuffXPFactory::canTweakAlphaForCoverage() const { |
- return can_tweak_alpha_for_coverage(fDstCoeff); |
-} |
- |
-void GrPorterDuffXPFactory::getInvariantOutput(const GrProcOptInfo& colorPOI, |
- const GrProcOptInfo& coveragePOI, |
- GrXPFactory::InvariantOutput* output) const { |
- if (!coveragePOI.isSolidWhite()) { |
- output->fWillBlendWithDst = true; |
- output->fBlendedColorFlags = 0; |
- return; |
- } |
- |
- GrBlendCoeff srcCoeff = fSrcCoeff; |
- GrBlendCoeff dstCoeff = fDstCoeff; |
- |
- // TODO: figure out to merge this simplify with other current optimization code paths and |
- // eventually remove from GrBlend |
- GrSimplifyBlend(&srcCoeff, &dstCoeff, colorPOI.color(), colorPOI.validFlags(), |
- 0, 0, 0); |
- |
- if (GrBlendCoeffRefsDst(srcCoeff)) { |
- output->fWillBlendWithDst = true; |
- output->fBlendedColorFlags = 0; |
- return; |
- } |
- |
- if (kZero_GrBlendCoeff != dstCoeff) { |
- bool srcAIsOne = colorPOI.isOpaque(); |
- if (kISA_GrBlendCoeff != dstCoeff || !srcAIsOne) { |
- output->fWillBlendWithDst = true; |
- } |
- output->fBlendedColorFlags = 0; |
- return; |
- } |
- |
- switch (srcCoeff) { |
- case kZero_GrBlendCoeff: |
- output->fBlendedColor = 0; |
- output->fBlendedColorFlags = kRGBA_GrColorComponentFlags; |
- break; |
- |
- case kOne_GrBlendCoeff: |
- output->fBlendedColor = colorPOI.color(); |
- output->fBlendedColorFlags = colorPOI.validFlags(); |
- break; |
- |
- // The src coeff should never refer to the src and if it refers to dst then opaque |
- // should have been false. |
- case kSC_GrBlendCoeff: |
- case kISC_GrBlendCoeff: |
- case kDC_GrBlendCoeff: |
- case kIDC_GrBlendCoeff: |
- case kSA_GrBlendCoeff: |
- case kISA_GrBlendCoeff: |
- case kDA_GrBlendCoeff: |
- case kIDA_GrBlendCoeff: |
- default: |
- SkFAIL("srcCoeff should not refer to src or dst."); |
- break; |
- |
- // TODO: update this once GrPaint actually has a const color. |
- case kConstC_GrBlendCoeff: |
- case kIConstC_GrBlendCoeff: |
- case kConstA_GrBlendCoeff: |
- case kIConstA_GrBlendCoeff: |
- output->fBlendedColorFlags = 0; |
- break; |
- } |
- |
- output->fWillBlendWithDst = false; |
-} |
- |
-bool GrPorterDuffXPFactory::willReadDstColor(const GrDrawTargetCaps& caps, |
- const GrProcOptInfo& colorPOI, |
- const GrProcOptInfo& coveragePOI) const { |
- // We can always blend correctly if we have dual source blending. |
- if (caps.dualSourceBlendingSupport()) { |
- return false; |
- } |
- |
- if (this->canTweakAlphaForCoverage()) { |
- return false; |
- } |
- |
- bool srcAIsOne = colorPOI.isOpaque(); |
- |
- if (kZero_GrBlendCoeff == fDstCoeff) { |
- if (kZero_GrBlendCoeff == fSrcCoeff || srcAIsOne) { |
- return false; |
- } |
- } |
- |
- // Reduces to: coeffS * (Cov*S) + D |
- if (kSA_GrBlendCoeff == fDstCoeff && srcAIsOne) { |
- return false; |
- } |
- |
- // We can always blend correctly if we have solid coverage. |
- if (coveragePOI.isSolidWhite()) { |
- return false; |
- } |
- |
- return true; |
-} |
- |
-GR_DEFINE_XP_FACTORY_TEST(GrPorterDuffXPFactory); |
- |
-GrXPFactory* GrPorterDuffXPFactory::TestCreate(SkRandom* random, |
- GrContext*, |
- const GrDrawTargetCaps&, |
- GrTexture*[]) { |
- SkXfermode::Mode mode = SkXfermode::Mode(random->nextULessThan(SkXfermode::kLastCoeffMode)); |
- return GrPorterDuffXPFactory::Create(mode); |
-} |
- |
+/* |
+ * Copyright 2014 Google Inc. |
+ * |
+ * Use of this source code is governed by a BSD-style license that can be |
+ * found in the LICENSE file. |
+ */ |
+ |
+#include "effects/GrPorterDuffXferProcessor.h" |
+ |
+#include "GrBlend.h" |
+#include "GrDrawTargetCaps.h" |
+#include "GrProcessor.h" |
+#include "GrProcOptInfo.h" |
+#include "GrTypes.h" |
+#include "GrXferProcessor.h" |
+#include "gl/GrGLXferProcessor.h" |
+#include "gl/builders/GrGLFragmentShaderBuilder.h" |
+#include "gl/builders/GrGLProgramBuilder.h" |
+ |
+static bool can_tweak_alpha_for_coverage(GrBlendCoeff dstCoeff) { |
+ /* |
+ The fractional coverage is f. |
+ The src and dst coeffs are Cs and Cd. |
+ The dst and src colors are S and D. |
+ We want the blend to compute: f*Cs*S + (f*Cd + (1-f))D. By tweaking the source color's alpha |
+ we're replacing S with S'=fS. It's obvious that that first term will always be ok. The second |
+ term can be rearranged as [1-(1-Cd)f]D. By substituting in the various possibilities for Cd we |
+ find that only 1, ISA, and ISC produce the correct destination when applied to S' and D. |
+ */ |
+ return kOne_GrBlendCoeff == dstCoeff || |
+ kISA_GrBlendCoeff == dstCoeff || |
+ kISC_GrBlendCoeff == dstCoeff; |
+} |
+ |
+class PorterDuffXferProcessor : public GrXferProcessor { |
+public: |
+ static GrXferProcessor* Create(GrBlendCoeff srcBlend, GrBlendCoeff dstBlend, |
+ GrColor constant, const GrDeviceCoordTexture* dstCopy, |
+ bool willReadDstColor) { |
+ return SkNEW_ARGS(PorterDuffXferProcessor, (srcBlend, dstBlend, constant, dstCopy, |
+ willReadDstColor)); |
+ } |
+ |
+ ~PorterDuffXferProcessor() override; |
+ |
+ const char* name() const override { return "Porter Duff"; } |
+ |
+ GrGLXferProcessor* createGLInstance() const override; |
+ |
+ bool hasSecondaryOutput() const override; |
+ |
+ /////////////////////////////////////////////////////////////////////////// |
+ /// @name Stage Output Types |
+ //// |
+ |
+ enum PrimaryOutputType { |
+ kNone_PrimaryOutputType, |
+ kColor_PrimaryOutputType, |
+ kCoverage_PrimaryOutputType, |
+ // Modulate color and coverage, write result as the color output. |
+ kModulate_PrimaryOutputType, |
+ // Custom Porter-Duff output, used for when we explictly are reading the dst and blending |
+ // in the shader. Secondary Output must be none if you use this. The custom blend uses the |
+ // equation: cov * (coeffS * S + coeffD * D) + (1 - cov) * D |
+ kCustom_PrimaryOutputType |
+ }; |
+ |
+ enum SecondaryOutputType { |
+ // There is no secondary output |
+ kNone_SecondaryOutputType, |
+ // Writes coverage as the secondary output. Only set if dual source blending is supported |
+ // and primary output is kModulate. |
+ kCoverage_SecondaryOutputType, |
+ // Writes coverage * (1 - colorA) as the secondary output. Only set if dual source blending |
+ // is supported and primary output is kModulate. |
+ kCoverageISA_SecondaryOutputType, |
+ // Writes coverage * (1 - colorRGBA) as the secondary output. Only set if dual source |
+ // blending is supported and primary output is kModulate. |
+ kCoverageISC_SecondaryOutputType, |
+ |
+ kSecondaryOutputTypeCnt, |
+ }; |
+ |
+ PrimaryOutputType primaryOutputType() const { return fPrimaryOutputType; } |
+ SecondaryOutputType secondaryOutputType() const { return fSecondaryOutputType; } |
+ |
+ GrXferProcessor::OptFlags getOptimizations(const GrProcOptInfo& colorPOI, |
+ const GrProcOptInfo& coveragePOI, |
+ bool doesStencilWrite, |
+ GrColor* overrideColor, |
+ const GrDrawTargetCaps& caps) override; |
+ |
+ GrBlendCoeff getSrcBlend() const { return fSrcBlend; } |
+ GrBlendCoeff getDstBlend() const { return fDstBlend; } |
+ |
+private: |
+ PorterDuffXferProcessor(GrBlendCoeff srcBlend, GrBlendCoeff dstBlend, GrColor constant, |
+ const GrDeviceCoordTexture* dstCopy, bool willReadDstColor); |
+ |
+ void onGetGLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const override; |
+ |
+ void onGetBlendInfo(GrXferProcessor::BlendInfo* blendInfo) const override { |
+ if (!this->willReadDstColor()) { |
+ blendInfo->fSrcBlend = fSrcBlend; |
+ blendInfo->fDstBlend = fDstBlend; |
+ } else { |
+ blendInfo->fSrcBlend = kOne_GrBlendCoeff; |
+ blendInfo->fDstBlend = kZero_GrBlendCoeff; |
+ } |
+ blendInfo->fBlendConstant = fBlendConstant; |
+ } |
+ |
+ bool onIsEqual(const GrXferProcessor& xpBase) const override { |
+ const PorterDuffXferProcessor& xp = xpBase.cast<PorterDuffXferProcessor>(); |
+ if (fSrcBlend != xp.fSrcBlend || |
+ fDstBlend != xp.fDstBlend || |
+ fBlendConstant != xp.fBlendConstant || |
+ fPrimaryOutputType != xp.fPrimaryOutputType || |
+ fSecondaryOutputType != xp.fSecondaryOutputType) { |
+ return false; |
+ } |
+ return true; |
+ } |
+ |
+ GrXferProcessor::OptFlags internalGetOptimizations(const GrProcOptInfo& colorPOI, |
+ const GrProcOptInfo& coveragePOI, |
+ bool doesStencilWrite); |
+ |
+ void calcOutputTypes(GrXferProcessor::OptFlags blendOpts, const GrDrawTargetCaps& caps, |
+ bool hasSolidCoverage); |
+ |
+ GrBlendCoeff fSrcBlend; |
+ GrBlendCoeff fDstBlend; |
+ GrColor fBlendConstant; |
+ PrimaryOutputType fPrimaryOutputType; |
+ SecondaryOutputType fSecondaryOutputType; |
+ |
+ typedef GrXferProcessor INHERITED; |
+}; |
+ |
+/////////////////////////////////////////////////////////////////////////////// |
+ |
+bool append_porterduff_term(GrGLFPFragmentBuilder* fsBuilder, GrBlendCoeff coeff, |
+ const char* colorName, const char* srcColorName, |
+ const char* dstColorName, bool hasPrevious) { |
+ if (kZero_GrBlendCoeff == coeff) { |
+ return hasPrevious; |
+ } else { |
+ if (hasPrevious) { |
+ fsBuilder->codeAppend(" + "); |
+ } |
+ fsBuilder->codeAppendf("%s", colorName); |
+ switch (coeff) { |
+ case kOne_GrBlendCoeff: |
+ break; |
+ case kSC_GrBlendCoeff: |
+ fsBuilder->codeAppendf(" * %s", srcColorName); |
+ break; |
+ case kISC_GrBlendCoeff: |
+ fsBuilder->codeAppendf(" * (vec4(1.0) - %s)", srcColorName); |
+ break; |
+ case kDC_GrBlendCoeff: |
+ fsBuilder->codeAppendf(" * %s", dstColorName); |
+ break; |
+ case kIDC_GrBlendCoeff: |
+ fsBuilder->codeAppendf(" * (vec4(1.0) - %s)", dstColorName); |
+ break; |
+ case kSA_GrBlendCoeff: |
+ fsBuilder->codeAppendf(" * %s.a", srcColorName); |
+ break; |
+ case kISA_GrBlendCoeff: |
+ fsBuilder->codeAppendf(" * (1.0 - %s.a)", srcColorName); |
+ break; |
+ case kDA_GrBlendCoeff: |
+ fsBuilder->codeAppendf(" * %s.a", dstColorName); |
+ break; |
+ case kIDA_GrBlendCoeff: |
+ fsBuilder->codeAppendf(" * (1.0 - %s.a)", dstColorName); |
+ break; |
+ default: |
+ SkFAIL("Unsupported Blend Coeff"); |
+ } |
+ return true; |
+ } |
+} |
+ |
+class GLPorterDuffXferProcessor : public GrGLXferProcessor { |
+public: |
+ GLPorterDuffXferProcessor(const GrProcessor&) {} |
+ |
+ virtual ~GLPorterDuffXferProcessor() {} |
+ |
+ static void GenKey(const GrProcessor& processor, const GrGLSLCaps& caps, |
+ GrProcessorKeyBuilder* b) { |
+ const PorterDuffXferProcessor& xp = processor.cast<PorterDuffXferProcessor>(); |
+ b->add32(xp.primaryOutputType()); |
+ b->add32(xp.secondaryOutputType()); |
+ if (xp.willReadDstColor()) { |
+ b->add32(xp.getSrcBlend()); |
+ b->add32(xp.getDstBlend()); |
+ } |
+ }; |
+ |
+private: |
+ void onEmitCode(const EmitArgs& args) override { |
+ const PorterDuffXferProcessor& xp = args.fXP.cast<PorterDuffXferProcessor>(); |
+ GrGLFPFragmentBuilder* fsBuilder = args.fPB->getFragmentShaderBuilder(); |
+ if (PorterDuffXferProcessor::kCustom_PrimaryOutputType != xp.primaryOutputType()) { |
+ SkASSERT(!xp.willReadDstColor()); |
+ switch(xp.secondaryOutputType()) { |
+ case PorterDuffXferProcessor::kNone_SecondaryOutputType: |
+ break; |
+ case PorterDuffXferProcessor::kCoverage_SecondaryOutputType: |
+ fsBuilder->codeAppendf("%s = %s;", args.fOutputSecondary, |
+ args.fInputCoverage); |
+ break; |
+ case PorterDuffXferProcessor::kCoverageISA_SecondaryOutputType: |
+ fsBuilder->codeAppendf("%s = (1.0 - %s.a) * %s;", |
+ args.fOutputSecondary, args.fInputColor, |
+ args.fInputCoverage); |
+ break; |
+ case PorterDuffXferProcessor::kCoverageISC_SecondaryOutputType: |
+ fsBuilder->codeAppendf("%s = (vec4(1.0) - %s) * %s;", |
+ args.fOutputSecondary, args.fInputColor, |
+ args.fInputCoverage); |
+ break; |
+ default: |
+ SkFAIL("Unexpected Secondary Output"); |
+ } |
+ |
+ switch (xp.primaryOutputType()) { |
+ case PorterDuffXferProcessor::kNone_PrimaryOutputType: |
+ fsBuilder->codeAppendf("%s = vec4(0);", args.fOutputPrimary); |
+ break; |
+ case PorterDuffXferProcessor::kColor_PrimaryOutputType: |
+ fsBuilder->codeAppendf("%s = %s;", args.fOutputPrimary, args.fInputColor); |
+ break; |
+ case PorterDuffXferProcessor::kCoverage_PrimaryOutputType: |
+ fsBuilder->codeAppendf("%s = %s;", args.fOutputPrimary, args.fInputCoverage); |
+ break; |
+ case PorterDuffXferProcessor::kModulate_PrimaryOutputType: |
+ fsBuilder->codeAppendf("%s = %s * %s;", args.fOutputPrimary, args.fInputColor, |
+ args.fInputCoverage); |
+ break; |
+ default: |
+ SkFAIL("Unexpected Primary Output"); |
+ } |
+ } else { |
+ SkASSERT(xp.willReadDstColor()); |
+ |
+ const char* dstColor = fsBuilder->dstColor(); |
+ |
+ fsBuilder->codeAppend("vec4 colorBlend ="); |
+ // append src blend |
+ bool didAppend = append_porterduff_term(fsBuilder, xp.getSrcBlend(), |
+ args.fInputColor, args.fInputColor, |
+ dstColor, false); |
+ // append dst blend |
+ SkAssertResult(append_porterduff_term(fsBuilder, xp.getDstBlend(), |
+ dstColor, args.fInputColor, |
+ dstColor, didAppend)); |
+ fsBuilder->codeAppend(";"); |
+ |
+ fsBuilder->codeAppendf("%s = %s * colorBlend + (vec4(1.0) - %s) * %s;", |
+ args.fOutputPrimary, args.fInputCoverage, args.fInputCoverage, |
+ dstColor); |
+ } |
+ } |
+ |
+ void onSetData(const GrGLProgramDataManager&, const GrXferProcessor&) override {}; |
+ |
+ typedef GrGLXferProcessor INHERITED; |
+}; |
+ |
+/////////////////////////////////////////////////////////////////////////////// |
+ |
+PorterDuffXferProcessor::PorterDuffXferProcessor(GrBlendCoeff srcBlend, |
+ GrBlendCoeff dstBlend, |
+ GrColor constant, |
+ const GrDeviceCoordTexture* dstCopy, |
+ bool willReadDstColor) |
+ : INHERITED(dstCopy, willReadDstColor) |
+ , fSrcBlend(srcBlend) |
+ , fDstBlend(dstBlend) |
+ , fBlendConstant(constant) |
+ , fPrimaryOutputType(kModulate_PrimaryOutputType) |
+ , fSecondaryOutputType(kNone_SecondaryOutputType) { |
+ this->initClassID<PorterDuffXferProcessor>(); |
+} |
+ |
+PorterDuffXferProcessor::~PorterDuffXferProcessor() { |
+} |
+ |
+void PorterDuffXferProcessor::onGetGLProcessorKey(const GrGLSLCaps& caps, |
+ GrProcessorKeyBuilder* b) const { |
+ GLPorterDuffXferProcessor::GenKey(*this, caps, b); |
+} |
+ |
+GrGLXferProcessor* PorterDuffXferProcessor::createGLInstance() const { |
+ return SkNEW_ARGS(GLPorterDuffXferProcessor, (*this)); |
+} |
+ |
+GrXferProcessor::OptFlags |
+PorterDuffXferProcessor::getOptimizations(const GrProcOptInfo& colorPOI, |
+ const GrProcOptInfo& coveragePOI, |
+ bool doesStencilWrite, |
+ GrColor* overrideColor, |
+ const GrDrawTargetCaps& caps) { |
+ GrXferProcessor::OptFlags optFlags; |
+ // Optimizations when doing RGB Coverage |
+ if (coveragePOI.isFourChannelOutput()) { |
+ // We want to force our primary output to be alpha * Coverage, where alpha is the alpha |
+ // value of the blend the constant. We should already have valid blend coeff's if we are at |
+ // a point where we have RGB coverage. We don't need any color stages since the known color |
+ // output is already baked into the blendConstant. |
+ uint8_t alpha = GrColorUnpackA(fBlendConstant); |
+ *overrideColor = GrColorPackRGBA(alpha, alpha, alpha, alpha); |
+ optFlags = GrXferProcessor::kOverrideColor_OptFlag; |
+ } else { |
+ optFlags = this->internalGetOptimizations(colorPOI, |
+ coveragePOI, |
+ doesStencilWrite); |
+ } |
+ this->calcOutputTypes(optFlags, caps, coveragePOI.isSolidWhite()); |
+ return optFlags; |
+} |
+ |
+void PorterDuffXferProcessor::calcOutputTypes(GrXferProcessor::OptFlags optFlags, |
+ const GrDrawTargetCaps& caps, |
+ bool hasSolidCoverage) { |
+ if (this->willReadDstColor()) { |
+ fPrimaryOutputType = kCustom_PrimaryOutputType; |
+ return; |
+ } |
+ |
+ if (optFlags & kIgnoreColor_OptFlag) { |
+ if (optFlags & kIgnoreCoverage_OptFlag) { |
+ fPrimaryOutputType = kNone_PrimaryOutputType; |
+ return; |
+ } else { |
+ fPrimaryOutputType = kCoverage_PrimaryOutputType; |
+ return; |
+ } |
+ } else if (optFlags & kIgnoreCoverage_OptFlag) { |
+ fPrimaryOutputType = kColor_PrimaryOutputType; |
+ return; |
+ } |
+ |
+ // If we do have coverage determine whether it matters. Dual source blending is expensive so |
+ // we don't do it if we are doing coverage drawing. If we aren't then We always do dual source |
+ // blending if we have any effective coverage stages OR the geometry processor doesn't emits |
+ // solid coverage. |
+ if (!(optFlags & kSetCoverageDrawing_OptFlag) && !hasSolidCoverage) { |
+ if (caps.dualSourceBlendingSupport()) { |
+ if (kZero_GrBlendCoeff == fDstBlend) { |
+ // write the coverage value to second color |
+ fSecondaryOutputType = kCoverage_SecondaryOutputType; |
+ fDstBlend = kIS2C_GrBlendCoeff; |
+ } else if (kSA_GrBlendCoeff == fDstBlend) { |
+ // SA dst coeff becomes 1-(1-SA)*coverage when dst is partially covered. |
+ fSecondaryOutputType = kCoverageISA_SecondaryOutputType; |
+ fDstBlend = kIS2C_GrBlendCoeff; |
+ } else if (kSC_GrBlendCoeff == fDstBlend) { |
+ // SA dst coeff becomes 1-(1-SA)*coverage when dst is partially covered. |
+ fSecondaryOutputType = kCoverageISC_SecondaryOutputType; |
+ fDstBlend = kIS2C_GrBlendCoeff; |
+ } |
+ } |
+ } |
+} |
+ |
+GrXferProcessor::OptFlags |
+PorterDuffXferProcessor::internalGetOptimizations(const GrProcOptInfo& colorPOI, |
+ const GrProcOptInfo& coveragePOI, |
+ bool doesStencilWrite) { |
+ if (this->willReadDstColor()) { |
+ return GrXferProcessor::kNone_Opt; |
+ } |
+ |
+ bool srcAIsOne = colorPOI.isOpaque(); |
+ bool hasCoverage = !coveragePOI.isSolidWhite(); |
+ |
+ bool dstCoeffIsOne = kOne_GrBlendCoeff == fDstBlend || |
+ (kSA_GrBlendCoeff == fDstBlend && srcAIsOne); |
+ bool dstCoeffIsZero = kZero_GrBlendCoeff == fDstBlend || |
+ (kISA_GrBlendCoeff == fDstBlend && srcAIsOne); |
+ |
+ // When coeffs are (0,1) there is no reason to draw at all, unless |
+ // stenciling is enabled. Having color writes disabled is effectively |
+ // (0,1). |
+ if ((kZero_GrBlendCoeff == fSrcBlend && dstCoeffIsOne)) { |
+ if (doesStencilWrite) { |
+ return GrXferProcessor::kIgnoreColor_OptFlag | |
+ GrXferProcessor::kSetCoverageDrawing_OptFlag; |
+ } else { |
+ fDstBlend = kOne_GrBlendCoeff; |
+ return GrXferProcessor::kSkipDraw_OptFlag; |
+ } |
+ } |
+ |
+ // if we don't have coverage we can check whether the dst |
+ // has to read at all. If not, we'll disable blending. |
+ if (!hasCoverage) { |
+ if (dstCoeffIsZero) { |
+ if (kOne_GrBlendCoeff == fSrcBlend) { |
+ // if there is no coverage and coeffs are (1,0) then we |
+ // won't need to read the dst at all, it gets replaced by src |
+ fDstBlend = kZero_GrBlendCoeff; |
+ return GrXferProcessor::kNone_Opt; |
+ } else if (kZero_GrBlendCoeff == fSrcBlend) { |
+ // if the op is "clear" then we don't need to emit a color |
+ // or blend, just write transparent black into the dst. |
+ fSrcBlend = kOne_GrBlendCoeff; |
+ fDstBlend = kZero_GrBlendCoeff; |
+ return GrXferProcessor::kIgnoreColor_OptFlag | |
+ GrXferProcessor::kIgnoreCoverage_OptFlag; |
+ } |
+ } |
+ } else { |
+ // check whether coverage can be safely rolled into alpha |
+ // of if we can skip color computation and just emit coverage |
+ if (can_tweak_alpha_for_coverage(fDstBlend)) { |
+ if (colorPOI.allStagesMultiplyInput()) { |
+ return GrXferProcessor::kSetCoverageDrawing_OptFlag | |
+ GrXferProcessor::kCanTweakAlphaForCoverage_OptFlag; |
+ } else { |
+ return GrXferProcessor::kSetCoverageDrawing_OptFlag; |
+ |
+ } |
+ } |
+ if (dstCoeffIsZero) { |
+ if (kZero_GrBlendCoeff == fSrcBlend) { |
+ // the source color is not included in the blend |
+ // the dst coeff is effectively zero so blend works out to: |
+ // (c)(0)D + (1-c)D = (1-c)D. |
+ fDstBlend = kISA_GrBlendCoeff; |
+ return GrXferProcessor::kIgnoreColor_OptFlag | |
+ GrXferProcessor::kSetCoverageDrawing_OptFlag; |
+ } else if (srcAIsOne) { |
+ // the dst coeff is effectively zero so blend works out to: |
+ // cS + (c)(0)D + (1-c)D = cS + (1-c)D. |
+ // If Sa is 1 then we can replace Sa with c |
+ // and set dst coeff to 1-Sa. |
+ fDstBlend = kISA_GrBlendCoeff; |
+ if (colorPOI.allStagesMultiplyInput()) { |
+ return GrXferProcessor::kSetCoverageDrawing_OptFlag | |
+ GrXferProcessor::kCanTweakAlphaForCoverage_OptFlag; |
+ } else { |
+ return GrXferProcessor::kSetCoverageDrawing_OptFlag; |
+ |
+ } |
+ } |
+ } else if (dstCoeffIsOne) { |
+ // the dst coeff is effectively one so blend works out to: |
+ // cS + (c)(1)D + (1-c)D = cS + D. |
+ fDstBlend = kOne_GrBlendCoeff; |
+ if (colorPOI.allStagesMultiplyInput()) { |
+ return GrXferProcessor::kSetCoverageDrawing_OptFlag | |
+ GrXferProcessor::kCanTweakAlphaForCoverage_OptFlag; |
+ } else { |
+ return GrXferProcessor::kSetCoverageDrawing_OptFlag; |
+ |
+ } |
+ return GrXferProcessor::kSetCoverageDrawing_OptFlag; |
+ } |
+ } |
+ |
+ return GrXferProcessor::kNone_Opt; |
+} |
+ |
+bool PorterDuffXferProcessor::hasSecondaryOutput() const { |
+ return kNone_SecondaryOutputType != fSecondaryOutputType; |
+} |
+ |
+/////////////////////////////////////////////////////////////////////////////// |
+ |
+GrPorterDuffXPFactory::GrPorterDuffXPFactory(GrBlendCoeff src, GrBlendCoeff dst) |
+ : fSrcCoeff(src), fDstCoeff(dst) { |
+ this->initClassID<GrPorterDuffXPFactory>(); |
+} |
+ |
+GrXPFactory* GrPorterDuffXPFactory::Create(SkXfermode::Mode mode) { |
+ switch (mode) { |
+ case SkXfermode::kClear_Mode: { |
+ static GrPorterDuffXPFactory gClearPDXPF(kZero_GrBlendCoeff, kZero_GrBlendCoeff); |
+ return SkRef(&gClearPDXPF); |
+ break; |
+ } |
+ case SkXfermode::kSrc_Mode: { |
+ static GrPorterDuffXPFactory gSrcPDXPF(kOne_GrBlendCoeff, kZero_GrBlendCoeff); |
+ return SkRef(&gSrcPDXPF); |
+ break; |
+ } |
+ case SkXfermode::kDst_Mode: { |
+ static GrPorterDuffXPFactory gDstPDXPF(kZero_GrBlendCoeff, kOne_GrBlendCoeff); |
+ return SkRef(&gDstPDXPF); |
+ break; |
+ } |
+ case SkXfermode::kSrcOver_Mode: { |
+ static GrPorterDuffXPFactory gSrcOverPDXPF(kOne_GrBlendCoeff, kISA_GrBlendCoeff); |
+ return SkRef(&gSrcOverPDXPF); |
+ break; |
+ } |
+ case SkXfermode::kDstOver_Mode: { |
+ static GrPorterDuffXPFactory gDstOverPDXPF(kIDA_GrBlendCoeff, kOne_GrBlendCoeff); |
+ return SkRef(&gDstOverPDXPF); |
+ break; |
+ } |
+ case SkXfermode::kSrcIn_Mode: { |
+ static GrPorterDuffXPFactory gSrcInPDXPF(kDA_GrBlendCoeff, kZero_GrBlendCoeff); |
+ return SkRef(&gSrcInPDXPF); |
+ break; |
+ } |
+ case SkXfermode::kDstIn_Mode: { |
+ static GrPorterDuffXPFactory gDstInPDXPF(kZero_GrBlendCoeff, kSA_GrBlendCoeff); |
+ return SkRef(&gDstInPDXPF); |
+ break; |
+ } |
+ case SkXfermode::kSrcOut_Mode: { |
+ static GrPorterDuffXPFactory gSrcOutPDXPF(kIDA_GrBlendCoeff, kZero_GrBlendCoeff); |
+ return SkRef(&gSrcOutPDXPF); |
+ break; |
+ } |
+ case SkXfermode::kDstOut_Mode: { |
+ static GrPorterDuffXPFactory gDstOutPDXPF(kZero_GrBlendCoeff, kISA_GrBlendCoeff); |
+ return SkRef(&gDstOutPDXPF); |
+ break; |
+ } |
+ case SkXfermode::kSrcATop_Mode: { |
+ static GrPorterDuffXPFactory gSrcATopPDXPF(kDA_GrBlendCoeff, kISA_GrBlendCoeff); |
+ return SkRef(&gSrcATopPDXPF); |
+ break; |
+ } |
+ case SkXfermode::kDstATop_Mode: { |
+ static GrPorterDuffXPFactory gDstATopPDXPF(kIDA_GrBlendCoeff, kSA_GrBlendCoeff); |
+ return SkRef(&gDstATopPDXPF); |
+ break; |
+ } |
+ case SkXfermode::kXor_Mode: { |
+ static GrPorterDuffXPFactory gXorPDXPF(kIDA_GrBlendCoeff, kISA_GrBlendCoeff); |
+ return SkRef(&gXorPDXPF); |
+ break; |
+ } |
+ case SkXfermode::kPlus_Mode: { |
+ static GrPorterDuffXPFactory gPlusPDXPF(kOne_GrBlendCoeff, kOne_GrBlendCoeff); |
+ return SkRef(&gPlusPDXPF); |
+ break; |
+ } |
+ case SkXfermode::kModulate_Mode: { |
+ static GrPorterDuffXPFactory gModulatePDXPF(kZero_GrBlendCoeff, kSC_GrBlendCoeff); |
+ return SkRef(&gModulatePDXPF); |
+ break; |
+ } |
+ case SkXfermode::kScreen_Mode: { |
+ static GrPorterDuffXPFactory gScreenPDXPF(kOne_GrBlendCoeff, kISC_GrBlendCoeff); |
+ return SkRef(&gScreenPDXPF); |
+ break; |
+ } |
+ default: |
+ return NULL; |
+ } |
+} |
+ |
+GrXferProcessor* |
+GrPorterDuffXPFactory::onCreateXferProcessor(const GrDrawTargetCaps& caps, |
+ const GrProcOptInfo& colorPOI, |
+ const GrProcOptInfo& covPOI, |
+ const GrDeviceCoordTexture* dstCopy) const { |
+ if (!covPOI.isFourChannelOutput()) { |
+ return PorterDuffXferProcessor::Create(fSrcCoeff, fDstCoeff, 0, dstCopy, |
+ this->willReadDstColor(caps, colorPOI, covPOI)); |
+ } else { |
+ if (this->supportsRGBCoverage(colorPOI.color(), colorPOI.validFlags())) { |
+ SkASSERT(kRGBA_GrColorComponentFlags == colorPOI.validFlags()); |
+ GrColor blendConstant = GrUnPreMulColor(colorPOI.color()); |
+ return PorterDuffXferProcessor::Create(kConstC_GrBlendCoeff, kISC_GrBlendCoeff, |
+ blendConstant, dstCopy, |
+ this->willReadDstColor(caps, colorPOI, covPOI)); |
+ } else { |
+ return NULL; |
+ } |
+ } |
+} |
+ |
+bool GrPorterDuffXPFactory::supportsRGBCoverage(GrColor /*knownColor*/, |
+ uint32_t knownColorFlags) const { |
+ if (kOne_GrBlendCoeff == fSrcCoeff && kISA_GrBlendCoeff == fDstCoeff && |
+ kRGBA_GrColorComponentFlags == knownColorFlags) { |
+ return true; |
+ } |
+ return false; |
+} |
+ |
+bool GrPorterDuffXPFactory::canTweakAlphaForCoverage() const { |
+ return can_tweak_alpha_for_coverage(fDstCoeff); |
+} |
+ |
+void GrPorterDuffXPFactory::getInvariantOutput(const GrProcOptInfo& colorPOI, |
+ const GrProcOptInfo& coveragePOI, |
+ GrXPFactory::InvariantOutput* output) const { |
+ if (!coveragePOI.isSolidWhite()) { |
+ output->fWillBlendWithDst = true; |
+ output->fBlendedColorFlags = 0; |
+ return; |
+ } |
+ |
+ GrBlendCoeff srcCoeff = fSrcCoeff; |
+ GrBlendCoeff dstCoeff = fDstCoeff; |
+ |
+ // TODO: figure out to merge this simplify with other current optimization code paths and |
+ // eventually remove from GrBlend |
+ GrSimplifyBlend(&srcCoeff, &dstCoeff, colorPOI.color(), colorPOI.validFlags(), |
+ 0, 0, 0); |
+ |
+ if (GrBlendCoeffRefsDst(srcCoeff)) { |
+ output->fWillBlendWithDst = true; |
+ output->fBlendedColorFlags = 0; |
+ return; |
+ } |
+ |
+ if (kZero_GrBlendCoeff != dstCoeff) { |
+ bool srcAIsOne = colorPOI.isOpaque(); |
+ if (kISA_GrBlendCoeff != dstCoeff || !srcAIsOne) { |
+ output->fWillBlendWithDst = true; |
+ } |
+ output->fBlendedColorFlags = 0; |
+ return; |
+ } |
+ |
+ switch (srcCoeff) { |
+ case kZero_GrBlendCoeff: |
+ output->fBlendedColor = 0; |
+ output->fBlendedColorFlags = kRGBA_GrColorComponentFlags; |
+ break; |
+ |
+ case kOne_GrBlendCoeff: |
+ output->fBlendedColor = colorPOI.color(); |
+ output->fBlendedColorFlags = colorPOI.validFlags(); |
+ break; |
+ |
+ // The src coeff should never refer to the src and if it refers to dst then opaque |
+ // should have been false. |
+ case kSC_GrBlendCoeff: |
+ case kISC_GrBlendCoeff: |
+ case kDC_GrBlendCoeff: |
+ case kIDC_GrBlendCoeff: |
+ case kSA_GrBlendCoeff: |
+ case kISA_GrBlendCoeff: |
+ case kDA_GrBlendCoeff: |
+ case kIDA_GrBlendCoeff: |
+ default: |
+ SkFAIL("srcCoeff should not refer to src or dst."); |
+ break; |
+ |
+ // TODO: update this once GrPaint actually has a const color. |
+ case kConstC_GrBlendCoeff: |
+ case kIConstC_GrBlendCoeff: |
+ case kConstA_GrBlendCoeff: |
+ case kIConstA_GrBlendCoeff: |
+ output->fBlendedColorFlags = 0; |
+ break; |
+ } |
+ |
+ output->fWillBlendWithDst = false; |
+} |
+ |
+bool GrPorterDuffXPFactory::willReadDstColor(const GrDrawTargetCaps& caps, |
+ const GrProcOptInfo& colorPOI, |
+ const GrProcOptInfo& coveragePOI) const { |
+ // We can always blend correctly if we have dual source blending. |
+ if (caps.dualSourceBlendingSupport()) { |
+ return false; |
+ } |
+ |
+ if (this->canTweakAlphaForCoverage()) { |
+ return false; |
+ } |
+ |
+ bool srcAIsOne = colorPOI.isOpaque(); |
+ |
+ if (kZero_GrBlendCoeff == fDstCoeff) { |
+ if (kZero_GrBlendCoeff == fSrcCoeff || srcAIsOne) { |
+ return false; |
+ } |
+ } |
+ |
+ // Reduces to: coeffS * (Cov*S) + D |
+ if (kSA_GrBlendCoeff == fDstCoeff && srcAIsOne) { |
+ return false; |
+ } |
+ |
+ // We can always blend correctly if we have solid coverage. |
+ if (coveragePOI.isSolidWhite()) { |
+ return false; |
+ } |
+ |
+ return true; |
+} |
+ |
+GR_DEFINE_XP_FACTORY_TEST(GrPorterDuffXPFactory); |
+ |
+GrXPFactory* GrPorterDuffXPFactory::TestCreate(SkRandom* random, |
+ GrContext*, |
+ const GrDrawTargetCaps&, |
+ GrTexture*[]) { |
+ SkXfermode::Mode mode = SkXfermode::Mode(random->nextULessThan(SkXfermode::kLastCoeffMode)); |
+ return GrPorterDuffXPFactory::Create(mode); |
+} |
+ |