| 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);
|
| +}
|
| +
|
|
|