/* * 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; void getBlendInfo(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; } 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; bool onIsEqual(const GrXferProcessor& xpBase) const override { const PorterDuffXferProcessor& xp = xpBase.cast(); 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(); 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(); 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() { } 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(); } 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); }