Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(287)

Unified Diff: src/gpu/effects/GrPorterDuffXferProcessor.cpp

Issue 1109863004: Use GLSLCaps for creating processor keys and GLSL-specific programs (Closed) Base URL: https://chromium.googlesource.com/skia@master
Patch Set: Created 5 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « src/gpu/effects/GrOvalEffect.cpp ('k') | src/gpu/effects/GrRRectEffect.cpp » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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);
+}
+
« no previous file with comments | « src/gpu/effects/GrOvalEffect.cpp ('k') | src/gpu/effects/GrRRectEffect.cpp » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698