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.shaderCaps()->dualSourceBlendingSupport()) { return false; } if (can_tweak_alpha_for_coverage(fDstCoeff)) { 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; }
void GrPorterDuffXPFactory::getInvariantBlendedColor(const GrProcOptInfo& colorPOI, InvariantBlendedColor* blendedColor) const { // Find the blended color info based on the formula that does not have coverage. BlendFormula colorFormula = gBlendTable[colorPOI.isOpaque()][0][fXfermode]; if (colorFormula.usesDstColor()) { blendedColor->fWillBlendWithDst = true; blendedColor->fKnownColorFlags = kNone_GrColorComponentFlags; return; } blendedColor->fWillBlendWithDst = false; SkASSERT(kAdd_GrBlendEquation == colorFormula.fBlendEquation); switch (colorFormula.fSrcCoeff) { case kZero_GrBlendCoeff: blendedColor->fKnownColor = 0; blendedColor->fKnownColorFlags = kRGBA_GrColorComponentFlags; return; case kOne_GrBlendCoeff: blendedColor->fKnownColor = colorPOI.color(); blendedColor->fKnownColorFlags = colorPOI.validFlags(); return; default: blendedColor->fKnownColorFlags = kNone_GrColorComponentFlags; return; } }
GrXferProcessor::OptFlags PorterDuffXferProcessor::onGetOptimizations(const GrProcOptInfo& colorPOI, const GrProcOptInfo& coveragePOI, bool doesStencilWrite, GrColor* overrideColor, const GrCaps& caps) { GrXferProcessor::OptFlags optFlags = GrXferProcessor::kNone_OptFlags; if (!fBlendFormula.modifiesDst()) { if (!doesStencilWrite) { optFlags |= GrXferProcessor::kSkipDraw_OptFlag; } optFlags |= (GrXferProcessor::kIgnoreColor_OptFlag | GrXferProcessor::kIgnoreCoverage_OptFlag | GrXferProcessor::kCanTweakAlphaForCoverage_OptFlag); } else { if (!fBlendFormula.usesInputColor()) { optFlags |= GrXferProcessor::kIgnoreColor_OptFlag; } if (coveragePOI.isSolidWhite()) { optFlags |= GrXferProcessor::kIgnoreCoverage_OptFlag; } if (colorPOI.allStagesMultiplyInput() && fBlendFormula.canTweakAlphaForCoverage()) { optFlags |= GrXferProcessor::kCanTweakAlphaForCoverage_OptFlag; } } return optFlags; }
void GrPipeline::adjustProgramFromOptimizations(const GrPipelineBuilder& pipelineBuilder, GrXferProcessor::OptFlags flags, const GrProcOptInfo& colorPOI, const GrProcOptInfo& coveragePOI, int* firstColorStageIdx, int* firstCoverageStageIdx) { fReadsFragPosition = fXferProcessor->willReadFragmentPosition(); if ((flags & GrXferProcessor::kIgnoreColor_OptFlag) || (flags & GrXferProcessor::kOverrideColor_OptFlag)) { *firstColorStageIdx = pipelineBuilder.numColorFragmentStages(); } else { if (coveragePOI.readsFragPosition()) { fReadsFragPosition = true; } } if (flags & GrXferProcessor::kIgnoreCoverage_OptFlag) { *firstCoverageStageIdx = pipelineBuilder.numCoverageFragmentStages(); } else { if (coveragePOI.readsFragPosition()) { fReadsFragPosition = true; } } }
static BlendFormula get_blend_formula(SkXfermode::Mode xfermode, const GrProcOptInfo& colorPOI, const GrProcOptInfo& coveragePOI) { SkASSERT(xfermode >= 0 && xfermode <= SkXfermode::kLastCoeffMode); SkASSERT(!coveragePOI.isFourChannelOutput()); return gBlendTable[colorPOI.isOpaque()][!coveragePOI.isSolidWhite()][xfermode]; }
static BlendFormula get_blend_formula(const GrProcOptInfo& colorPOI, const GrProcOptInfo& coveragePOI, bool hasMixedSamples, SkXfermode::Mode xfermode) { SkASSERT(xfermode >= 0 && xfermode <= SkXfermode::kLastCoeffMode); SkASSERT(!coveragePOI.isFourChannelOutput()); bool conflatesCoverage = !coveragePOI.isSolidWhite() || hasMixedSamples; return gBlendTable[colorPOI.isOpaque()][conflatesCoverage][xfermode]; }
bool GrPaint::isConstantBlendedColor(GrColor* color) const { GrProcOptInfo colorProcInfo; colorProcInfo.calcWithInitialValues(fColorStages.begin(), this->numColorStages(), fColor, kRGBA_GrColorComponentFlags, false); GrXPFactory::InvariantBlendedColor blendedColor; fXPFactory->getInvariantBlendedColor(colorProcInfo, &blendedColor); if (kRGBA_GrColorComponentFlags == blendedColor.fKnownColorFlags) { *color = blendedColor.fKnownColor; return true; } return false; }
static BlendFormula get_lcd_blend_formula(const GrProcOptInfo& coveragePOI, SkXfermode::Mode xfermode) { SkASSERT(xfermode >= 0 && xfermode <= SkXfermode::kLastCoeffMode); SkASSERT(coveragePOI.isFourChannelOutput()); return gLCDBlendTable[xfermode]; }
static BlendFormula get_lcd_blend_formula(const GrProcOptInfo& coveragePOI, SkBlendMode xfermode) { SkASSERT((unsigned)xfermode <= (unsigned)SkBlendMode::kLastCoeffMode); SkASSERT(coveragePOI.isFourChannelOutput()); return gLCDBlendTable[(int)xfermode]; }
bool GrPorterDuffXPFactory::willReadDstColor(const GrCaps& caps, const GrProcOptInfo& colorPOI, const GrProcOptInfo& coveragePOI) const { if (coveragePOI.isFourChannelOutput()) { return false; // The LCD XP never does a dst read. } // We fallback on the shader XP when the blend formula would use dual source blending but we // don't have support for it. return !caps.shaderCaps()->dualSourceBlendingSupport() && get_blend_formula(fXfermode, colorPOI, coveragePOI).hasSecondaryOutput(); }
bool GrPorterDuffXPFactory::willReadDstColor(const GrCaps& caps, const GrProcOptInfo& colorPOI, const GrProcOptInfo& covPOI, bool hasMixedSamples) const { if (caps.shaderCaps()->dualSourceBlendingSupport()) { return false; } // When we have four channel coverage we always need to read the dst in order to correctly // blend. The one exception is when we are using srcover mode and we know the input color into // the XP. if (covPOI.isFourChannelOutput()) { if (SkXfermode::kSrcOver_Mode == fXfermode && kRGBA_GrColorComponentFlags == colorPOI.validFlags()) { return false; } return get_lcd_blend_formula(covPOI, fXfermode).hasSecondaryOutput(); } // We fallback on the shader XP when the blend formula would use dual source blending but we // don't have support for it. return get_blend_formula(colorPOI, covPOI, hasMixedSamples, fXfermode).hasSecondaryOutput(); }
bool GrPaint::isConstantBlendedColor(GrColor* color) const { GrProcOptInfo colorProcInfo; colorProcInfo.calcWithInitialValues(fColorFragmentProcessors.begin(), this->numColorFragmentProcessors(), fColor, kRGBA_GrColorComponentFlags, false); GrXPFactory::InvariantBlendedColor blendedColor; if (fXPFactory) { fXPFactory->getInvariantBlendedColor(colorProcInfo, &blendedColor); } else { GrPorterDuffXPFactory::SrcOverInvariantBlendedColor(colorProcInfo.color(), colorProcInfo.validFlags(), colorProcInfo.isOpaque(), &blendedColor); } if (kRGBA_GrColorComponentFlags == blendedColor.fKnownColorFlags) { *color = blendedColor.fKnownColor; return true; } return false; }
bool GrPorterDuffXPFactory::willReadDstColor(const GrCaps& caps, const GrProcOptInfo& colorPOI, const GrProcOptInfo& covPOI, bool hasMixedSamples) const { if (caps.shaderCaps()->dualSourceBlendingSupport()) { return false; } if (covPOI.isFourChannelOutput()) { return false; // The LCD XP will abort rather than doing a dst read. } // We fallback on the shader XP when the blend formula would use dual source blending but we // don't have support for it. return get_blend_formula(colorPOI, covPOI, hasMixedSamples, fXfermode).hasSecondaryOutput(); }
static bool can_use_hw_blend_equation(GrBlendEquation equation, const GrProcOptInfo& coveragePOI, const GrCaps& caps) { if (!caps.advancedBlendEquationSupport()) { return false; } if (coveragePOI.isFourChannelOutput()) { return false; // LCD coverage must be applied after the blend equation. } if (caps.canUseAdvancedBlendEquation(equation)) { return false; } return true; }
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 GrCoverageSetOpXPFactory::getInvariantOutput(const GrProcOptInfo& colorPOI, const GrProcOptInfo& coveragePOI, GrXPFactory::InvariantOutput* output) const { if (SkRegion::kReplace_Op == fRegionOp) { if (coveragePOI.isSolidWhite()) { output->fBlendedColor = GrColor_WHITE; output->fBlendedColorFlags = kRGBA_GrColorComponentFlags; } else { output->fBlendedColorFlags = 0; } output->fWillBlendWithDst = false; } else { output->fBlendedColorFlags = 0; output->fWillBlendWithDst = true; } }
GrXferProcessor::OptFlags GrXferProcessor::getOptimizations(const GrProcOptInfo& colorPOI, const GrProcOptInfo& coveragePOI, bool doesStencilWrite, GrColor* overrideColor, const GrCaps& caps) { GrXferProcessor::OptFlags flags = this->onGetOptimizations(colorPOI, coveragePOI, doesStencilWrite, overrideColor, caps); if (this->willReadDstColor()) { // When performing a dst read we handle coverage in the base class. SkASSERT(!(flags & GrXferProcessor::kIgnoreCoverage_OptFlag)); if (coveragePOI.isSolidWhite()) { flags |= GrXferProcessor::kIgnoreCoverage_OptFlag; } } if (flags & GrXferProcessor::kIgnoreCoverage_OptFlag) { fReadsCoverage = false; } return flags; }
GrPipeline::GrPipeline(const GrPipelineBuilder& pipelineBuilder, const GrProcOptInfo& colorPOI, const GrProcOptInfo& coveragePOI, const GrDrawTargetCaps& caps, const GrScissorState& scissorState, const GrDeviceCoordTexture* dstCopy) { // Create XferProcessor from DS's XPFactory SkAutoTUnref<GrXferProcessor> xferProcessor( pipelineBuilder.getXPFactory()->createXferProcessor(colorPOI, coveragePOI, dstCopy, caps)); GrColor overrideColor = GrColor_ILLEGAL; if (colorPOI.firstEffectiveStageIndex() != 0) { overrideColor = colorPOI.inputColorToEffectiveStage(); } GrXferProcessor::OptFlags optFlags; if (xferProcessor) { fXferProcessor.reset(xferProcessor.get()); optFlags = xferProcessor->getOptimizations(colorPOI, coveragePOI, pipelineBuilder.getStencil().doesWrite(), &overrideColor, caps); } // When path rendering the stencil settings are not always set on the GrPipelineBuilder // so we must check the draw type. In cases where we will skip drawing we simply return a // null GrPipeline. if (!xferProcessor || (GrXferProcessor::kSkipDraw_OptFlag & optFlags)) { // Set the fields that don't default init and return. The lack of a render target will // indicate that this can be skipped. fFlags = 0; fDrawFace = GrPipelineBuilder::kInvalid_DrawFace; return; } fRenderTarget.reset(pipelineBuilder.fRenderTarget.get()); SkASSERT(fRenderTarget); fScissorState = scissorState; fStencilSettings = pipelineBuilder.getStencil(); fDrawFace = pipelineBuilder.getDrawFace(); fFlags = 0; if (pipelineBuilder.isHWAntialias()) { fFlags |= kHWAA_Flag; } if (pipelineBuilder.isDither()) { fFlags |= kDither_Flag; } if (pipelineBuilder.snapVerticesToPixelCenters()) { fFlags |= kSnapVertices_Flag; } int firstColorStageIdx = colorPOI.firstEffectiveStageIndex(); // TODO: Once we can handle single or four channel input into coverage stages then we can use // GrPipelineBuilder's coverageProcInfo (like color above) to set this initial information. int firstCoverageStageIdx = 0; this->adjustProgramFromOptimizations(pipelineBuilder, optFlags, colorPOI, coveragePOI, &firstColorStageIdx, &firstCoverageStageIdx); bool usesLocalCoords = false; // Copy Stages from PipelineBuilder to Pipeline for (int i = firstColorStageIdx; i < pipelineBuilder.numColorFragmentStages(); ++i) { SkNEW_APPEND_TO_TARRAY(&fFragmentStages, GrPendingFragmentStage, (pipelineBuilder.fColorStages[i])); usesLocalCoords = usesLocalCoords || pipelineBuilder.fColorStages[i].processor()->usesLocalCoords(); } fNumColorStages = fFragmentStages.count(); for (int i = firstCoverageStageIdx; i < pipelineBuilder.numCoverageFragmentStages(); ++i) { SkNEW_APPEND_TO_TARRAY(&fFragmentStages, GrPendingFragmentStage, (pipelineBuilder.fCoverageStages[i])); usesLocalCoords = usesLocalCoords || pipelineBuilder.fCoverageStages[i].processor()->usesLocalCoords(); } // let the GP init the batch tracker fInitBT.fColorIgnored = SkToBool(optFlags & GrXferProcessor::kIgnoreColor_OptFlag); fInitBT.fOverrideColor = fInitBT.fColorIgnored ? GrColor_ILLEGAL : overrideColor; fInitBT.fCoverageIgnored = SkToBool(optFlags & GrXferProcessor::kIgnoreCoverage_OptFlag); fInitBT.fUsesLocalCoords = usesLocalCoords; fInitBT.fCanTweakAlphaForCoverage = SkToBool(optFlags & GrXferProcessor::kCanTweakAlphaForCoverage_OptFlag); }
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; }
GrXferProcessor::OptFlags CustomXP::onGetOptimizations(const GrProcOptInfo& colorPOI, const GrProcOptInfo& coveragePOI, bool doesStencilWrite, GrColor* overrideColor, const GrCaps& caps) { /* Most the optimizations we do here are based on tweaking alpha for coverage. The general SVG blend equation is defined in the spec as follows: Dca' = B(Sc, Dc) * Sa * Da + Y * Sca * (1-Da) + Z * Dca * (1-Sa) Da' = X * Sa * Da + Y * Sa * (1-Da) + Z * Da * (1-Sa) (Note that Sca, Dca indicate RGB vectors that are premultiplied by alpha, and that B(Sc, Dc) is a mode-specific function that accepts non-multiplied RGB colors.) For every blend mode supported by this class, i.e. the "advanced" blend modes, X=Y=Z=1 and this equation reduces to the PDF blend equation. It can be shown that when X=Y=Z=1, these equations can modulate alpha for coverage. == Color == We substitute Y=Z=1 and define a blend() function that calculates Dca' in terms of premultiplied alpha only: blend(Sca, Dca, Sa, Da) = {Dca : if Sa == 0, Sca : if Da == 0, B(Sca/Sa, Dca/Da) * Sa * Da + Sca * (1-Da) + Dca * (1-Sa) : if Sa,Da != 0} And for coverage modulation, we use a post blend src-over model: Dca'' = f * blend(Sca, Dca, Sa, Da) + (1-f) * Dca (Where f is the fractional coverage.) Next we show that canTweakAlphaForCoverage() is true by proving the following relationship: blend(f*Sca, Dca, f*Sa, Da) == f * blend(Sca, Dca, Sa, Da) + (1-f) * Dca General case (f,Sa,Da != 0): f * blend(Sca, Dca, Sa, Da) + (1-f) * Dca = f * (B(Sca/Sa, Dca/Da) * Sa * Da + Sca * (1-Da) + Dca * (1-Sa)) + (1-f) * Dca [Sa,Da != 0, definition of blend()] = B(Sca/Sa, Dca/Da) * f*Sa * Da + f*Sca * (1-Da) + f*Dca * (1-Sa) + Dca - f*Dca = B(Sca/Sa, Dca/Da) * f*Sa * Da + f*Sca - f*Sca * Da + f*Dca - f*Dca * Sa + Dca - f*Dca = B(Sca/Sa, Dca/Da) * f*Sa * Da + f*Sca - f*Sca * Da - f*Dca * Sa + Dca = B(Sca/Sa, Dca/Da) * f*Sa * Da + f*Sca * (1-Da) - f*Dca * Sa + Dca = B(Sca/Sa, Dca/Da) * f*Sa * Da + f*Sca * (1-Da) + Dca * (1 - f*Sa) = B(f*Sca/f*Sa, Dca/Da) * f*Sa * Da + f*Sca * (1-Da) + Dca * (1 - f*Sa) [f!=0] = blend(f*Sca, Dca, f*Sa, Da) [definition of blend()] Corner cases (Sa=0, Da=0, and f=0): Sa=0: f * blend(Sca, Dca, Sa, Da) + (1-f) * Dca = f * Dca + (1-f) * Dca [Sa=0, definition of blend()] = Dca = blend(0, Dca, 0, Da) [definition of blend()] = blend(f*Sca, Dca, f*Sa, Da) [Sa=0] Da=0: f * blend(Sca, Dca, Sa, Da) + (1-f) * Dca = f * Sca + (1-f) * Dca [Da=0, definition of blend()] = f * Sca [Da=0] = blend(f*Sca, 0, f*Sa, 0) [definition of blend()] = blend(f*Sca, Dca, f*Sa, Da) [Da=0] f=0: f * blend(Sca, Dca, Sa, Da) + (1-f) * Dca = Dca [f=0] = blend(0, Dca, 0, Da) [definition of blend()] = blend(f*Sca, Dca, f*Sa, Da) [f=0] == Alpha == We substitute X=Y=Z=1 and define a blend() function that calculates Da': blend(Sa, Da) = Sa * Da + Sa * (1-Da) + Da * (1-Sa) = Sa * Da + Sa - Sa * Da + Da - Da * Sa = Sa + Da - Sa * Da We use the same model for coverage modulation as we did with color: Da'' = f * blend(Sa, Da) + (1-f) * Da And show that canTweakAlphaForCoverage() is true by proving the following relationship: blend(f*Sa, Da) == f * blend(Sa, Da) + (1-f) * Da f * blend(Sa, Da) + (1-f) * Da = f * (Sa + Da - Sa * Da) + (1-f) * Da = f*Sa + f*Da - f*Sa * Da + Da - f*Da = f*Sa - f*Sa * Da + Da = f*Sa + Da - f*Sa * Da = blend(f*Sa, Da) */ OptFlags flags = kNone_OptFlags; if (colorPOI.allStagesMultiplyInput()) { flags |= kCanTweakAlphaForCoverage_OptFlag; } if (this->hasHWBlendEquation() && coveragePOI.isSolidWhite()) { flags |= kIgnoreCoverage_OptFlag; } return flags; }
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; }