/* * 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 "GrCaps.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" /** * Wraps the shader outputs and HW blend state that comprise a Porter Duff blend mode with coverage. */ struct BlendFormula { public: /** * Values the shader can write to primary and secondary outputs. These must all be modulated by * coverage to support mixed samples. The XP will ignore the multiplies when not using coverage. */ enum OutputType { kNone_OutputType, // struct get_properties : SkTIntegralConstant( (GR_BLEND_MODIFIES_DST(BlendEquation, SrcCoeff, DstCoeff) ? kModifiesDst_Property : 0) | (GR_BLEND_COEFFS_USE_DST_COLOR(SrcCoeff, DstCoeff) ? kUsesDstColor_Property : 0) | ((PrimaryOut >= kModulate_OutputType && GR_BLEND_COEFFS_USE_SRC_COLOR(SrcCoeff,DstCoeff)) || (SecondaryOut >= kModulate_OutputType && GR_BLEND_COEFF_REFS_SRC2(DstCoeff)) ? kUsesInputColor_Property : 0) | // We assert later that SrcCoeff doesn't ref src2. (kModulate_OutputType == PrimaryOut && kNone_OutputType == SecondaryOut && GR_BLEND_CAN_TWEAK_ALPHA_FOR_COVERAGE(BlendEquation, SrcCoeff, DstCoeff) ? kCanTweakAlphaForCoverage_Property : 0))> { // The provided formula should already be optimized. GR_STATIC_ASSERT((kNone_OutputType == PrimaryOut) == !GR_BLEND_COEFFS_USE_SRC_COLOR(SrcCoeff, DstCoeff)); GR_STATIC_ASSERT(!GR_BLEND_COEFF_REFS_SRC2(SrcCoeff)); GR_STATIC_ASSERT((kNone_OutputType == SecondaryOut) == !GR_BLEND_COEFF_REFS_SRC2(DstCoeff)); GR_STATIC_ASSERT(PrimaryOut != SecondaryOut || kNone_OutputType == PrimaryOut); GR_STATIC_ASSERT(kNone_OutputType != PrimaryOut || kNone_OutputType == SecondaryOut); }; union { struct { // We allot the enums one more bit than they require because MSVC seems to sign-extend // them when the top bit is set. (This is in violation of the C++03 standard 9.6/4) OutputType fPrimaryOutputType : 4; OutputType fSecondaryOutputType : 4; GrBlendEquation fBlendEquation : 6; GrBlendCoeff fSrcCoeff : 6; GrBlendCoeff fDstCoeff : 6; Properties fProps : 32 - (4 + 4 + 6 + 6 + 6); }; uint32_t fData; }; GR_STATIC_ASSERT(kLast_OutputType < (1 << 3)); GR_STATIC_ASSERT(kLast_GrBlendEquation < (1 << 5)); GR_STATIC_ASSERT(kLast_GrBlendCoeff < (1 << 5)); GR_STATIC_ASSERT(kLast_Property < (1 << 6)); }; GR_STATIC_ASSERT(4 == sizeof(BlendFormula)); GR_MAKE_BITFIELD_OPS(BlendFormula::Properties); /** * Initialize a compile-time constant BlendFormula and automatically deduce fProps. */ #define INIT_BLEND_FORMULA(PRIMARY_OUT, SECONDARY_OUT, BLEND_EQUATION, SRC_COEFF, DST_COEFF) \ {{{PRIMARY_OUT, \ SECONDARY_OUT, \ BLEND_EQUATION, SRC_COEFF, DST_COEFF, \ BlendFormula::get_properties::value}}} /** * When there is no coverage, or the blend mode can tweak alpha for coverage, we use the standard * Porter Duff formula. */ #define COEFF_FORMULA(SRC_COEFF, DST_COEFF) \ INIT_BLEND_FORMULA(BlendFormula::kModulate_OutputType, \ BlendFormula::kNone_OutputType, \ kAdd_GrBlendEquation, SRC_COEFF, DST_COEFF) /** * When the coeffs are (Zero, Zero), we clear the dst. This formula has its own macro so we can set * the primary output type to none. */ #define DST_CLEAR_FORMULA \ INIT_BLEND_FORMULA(BlendFormula::kNone_OutputType, \ BlendFormula::kNone_OutputType, \ kAdd_GrBlendEquation, kZero_GrBlendCoeff, kZero_GrBlendCoeff) /** * When the coeffs are (Zero, One), we don't write to the dst at all. This formula has its own macro * so we can set the primary output type to none. */ #define NO_DST_WRITE_FORMULA \ INIT_BLEND_FORMULA(BlendFormula::kNone_OutputType, \ BlendFormula::kNone_OutputType, \ kAdd_GrBlendEquation, kZero_GrBlendCoeff, kOne_GrBlendCoeff) /** * When there is coverage, the equation with f=coverage is: * * D' = f * (S * srcCoeff + D * dstCoeff) + (1-f) * D * * This can be rewritten as: * * D' = f * S * srcCoeff + D * (1 - [f * (1 - dstCoeff)]) * * To implement this formula, we output [f * (1 - dstCoeff)] for the secondary color and replace the * HW dst coeff with IS2C. * * Xfer modes: dst-atop (Sa!=1) */ #define COVERAGE_FORMULA(ONE_MINUS_DST_COEFF_MODULATE_OUTPUT, SRC_COEFF) \ INIT_BLEND_FORMULA(BlendFormula::kModulate_OutputType, \ ONE_MINUS_DST_COEFF_MODULATE_OUTPUT, \ kAdd_GrBlendEquation, SRC_COEFF, kIS2C_GrBlendCoeff) /** * When there is coverage and the src coeff is Zero, the equation with f=coverage becomes: * * D' = f * D * dstCoeff + (1-f) * D * * This can be rewritten as: * * D' = D - D * [f * (1 - dstCoeff)] * * To implement this formula, we output [f * (1 - dstCoeff)] for the primary color and use a reverse * subtract HW blend equation with coeffs of (DC, One). * * Xfer modes: clear, dst-out (Sa=1), dst-in (Sa!=1), modulate (Sc!=1) */ #define COVERAGE_SRC_COEFF_ZERO_FORMULA(ONE_MINUS_DST_COEFF_MODULATE_OUTPUT) \ INIT_BLEND_FORMULA(ONE_MINUS_DST_COEFF_MODULATE_OUTPUT, \ BlendFormula::kNone_OutputType, \ kReverseSubtract_GrBlendEquation, kDC_GrBlendCoeff, kOne_GrBlendCoeff) /** * When there is coverage and the dst coeff is Zero, the equation with f=coverage becomes: * * D' = f * S * srcCoeff + (1-f) * D * * To implement this formula, we output [f] for the secondary color and replace the HW dst coeff * with IS2A. (Note that we can avoid dual source blending when Sa=1 by using ISA.) * * Xfer modes (Sa!=1): src, src-in, src-out */ #define COVERAGE_DST_COEFF_ZERO_FORMULA(SRC_COEFF) \ INIT_BLEND_FORMULA(BlendFormula::kModulate_OutputType, \ BlendFormula::kCoverage_OutputType, \ kAdd_GrBlendEquation, SRC_COEFF, kIS2A_GrBlendCoeff) /** * This table outlines the blend formulas we will use with each xfermode, with and without coverage, * with and without an opaque input color. Optimization properties are deduced at compile time so we * can make runtime decisions quickly. RGB coverage is not supported. */ static const BlendFormula gBlendTable[2][2][SkXfermode::kLastCoeffMode + 1] = { /*>> Has coverage, input color unknown <<*/ {{ /* clear */ COVERAGE_SRC_COEFF_ZERO_FORMULA(BlendFormula::kCoverage_OutputType), /* src */ COVERAGE_DST_COEFF_ZERO_FORMULA(kOne_GrBlendCoeff), /* dst */ NO_DST_WRITE_FORMULA, /* src-over */ COEFF_FORMULA( kOne_GrBlendCoeff, kISA_GrBlendCoeff), /* dst-over */ COEFF_FORMULA( kIDA_GrBlendCoeff, kOne_GrBlendCoeff), /* src-in */ COVERAGE_DST_COEFF_ZERO_FORMULA(kDA_GrBlendCoeff), /* dst-in */ COVERAGE_SRC_COEFF_ZERO_FORMULA(BlendFormula::kISAModulate_OutputType), /* src-out */ COVERAGE_DST_COEFF_ZERO_FORMULA(kIDA_GrBlendCoeff), /* dst-out */ COEFF_FORMULA( kZero_GrBlendCoeff, kISA_GrBlendCoeff), /* src-atop */ COEFF_FORMULA( kDA_GrBlendCoeff, kISA_GrBlendCoeff), /* dst-atop */ COVERAGE_FORMULA(BlendFormula::kISAModulate_OutputType, kIDA_GrBlendCoeff), /* xor */ COEFF_FORMULA( kIDA_GrBlendCoeff, kISA_GrBlendCoeff), /* plus */ COEFF_FORMULA( kOne_GrBlendCoeff, kOne_GrBlendCoeff), /* modulate */ COVERAGE_SRC_COEFF_ZERO_FORMULA(BlendFormula::kISCModulate_OutputType), /* screen */ COEFF_FORMULA( kOne_GrBlendCoeff, kISC_GrBlendCoeff), }, /*>> No coverage, input color unknown <<*/ { /* clear */ DST_CLEAR_FORMULA, /* src */ COEFF_FORMULA( kOne_GrBlendCoeff, kZero_GrBlendCoeff), /* dst */ NO_DST_WRITE_FORMULA, /* src-over */ COEFF_FORMULA( kOne_GrBlendCoeff, kISA_GrBlendCoeff), /* dst-over */ COEFF_FORMULA( kIDA_GrBlendCoeff, kOne_GrBlendCoeff), /* src-in */ COEFF_FORMULA( kDA_GrBlendCoeff, kZero_GrBlendCoeff), /* dst-in */ COEFF_FORMULA( kZero_GrBlendCoeff, kSA_GrBlendCoeff), /* src-out */ COEFF_FORMULA( kIDA_GrBlendCoeff, kZero_GrBlendCoeff), /* dst-out */ COEFF_FORMULA( kZero_GrBlendCoeff, kISA_GrBlendCoeff), /* src-atop */ COEFF_FORMULA( kDA_GrBlendCoeff, kISA_GrBlendCoeff), /* dst-atop */ COEFF_FORMULA( kIDA_GrBlendCoeff, kSA_GrBlendCoeff), /* xor */ COEFF_FORMULA( kIDA_GrBlendCoeff, kISA_GrBlendCoeff), /* plus */ COEFF_FORMULA( kOne_GrBlendCoeff, kOne_GrBlendCoeff), /* modulate */ COEFF_FORMULA( kZero_GrBlendCoeff, kSC_GrBlendCoeff), /* screen */ COEFF_FORMULA( kOne_GrBlendCoeff, kISC_GrBlendCoeff), }}, /*>> Has coverage, input color opaque <<*/ {{ /* clear */ COVERAGE_SRC_COEFF_ZERO_FORMULA(BlendFormula::kCoverage_OutputType), /* src */ COEFF_FORMULA( kOne_GrBlendCoeff, kISA_GrBlendCoeff), /* dst */ NO_DST_WRITE_FORMULA, /* src-over */ COEFF_FORMULA( kOne_GrBlendCoeff, kISA_GrBlendCoeff), /* dst-over */ COEFF_FORMULA( kIDA_GrBlendCoeff, kOne_GrBlendCoeff), /* src-in */ COEFF_FORMULA( kDA_GrBlendCoeff, kISA_GrBlendCoeff), /* dst-in */ NO_DST_WRITE_FORMULA, /* src-out */ COEFF_FORMULA( kIDA_GrBlendCoeff, kISA_GrBlendCoeff), /* dst-out */ COVERAGE_SRC_COEFF_ZERO_FORMULA(BlendFormula::kCoverage_OutputType), /* src-atop */ COEFF_FORMULA( kDA_GrBlendCoeff, kISA_GrBlendCoeff), /* dst-atop */ COEFF_FORMULA( kIDA_GrBlendCoeff, kOne_GrBlendCoeff), /* xor */ COEFF_FORMULA( kIDA_GrBlendCoeff, kISA_GrBlendCoeff), /* plus */ COEFF_FORMULA( kOne_GrBlendCoeff, kOne_GrBlendCoeff), /* modulate */ COVERAGE_SRC_COEFF_ZERO_FORMULA(BlendFormula::kISCModulate_OutputType), /* screen */ COEFF_FORMULA( kOne_GrBlendCoeff, kISC_GrBlendCoeff), }, /*>> No coverage, input color opaque <<*/ { /* clear */ DST_CLEAR_FORMULA, /* src */ COEFF_FORMULA( kOne_GrBlendCoeff, kZero_GrBlendCoeff), /* dst */ NO_DST_WRITE_FORMULA, /* src-over */ COEFF_FORMULA( kOne_GrBlendCoeff, kZero_GrBlendCoeff), /* dst-over */ COEFF_FORMULA( kIDA_GrBlendCoeff, kOne_GrBlendCoeff), /* src-in */ COEFF_FORMULA( kDA_GrBlendCoeff, kZero_GrBlendCoeff), /* dst-in */ NO_DST_WRITE_FORMULA, /* src-out */ COEFF_FORMULA( kIDA_GrBlendCoeff, kZero_GrBlendCoeff), /* dst-out */ DST_CLEAR_FORMULA, /* src-atop */ COEFF_FORMULA( kDA_GrBlendCoeff, kZero_GrBlendCoeff), /* dst-atop */ COEFF_FORMULA( kIDA_GrBlendCoeff, kOne_GrBlendCoeff), /* xor */ COEFF_FORMULA( kIDA_GrBlendCoeff, kZero_GrBlendCoeff), /* plus */ COEFF_FORMULA( kOne_GrBlendCoeff, kOne_GrBlendCoeff), /* modulate */ COEFF_FORMULA( kZero_GrBlendCoeff, kSC_GrBlendCoeff), /* screen */ COEFF_FORMULA( kOne_GrBlendCoeff, kISC_GrBlendCoeff), }}}; 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_unoptimized_blend_formula(SkXfermode::Mode xfermode) { SkASSERT(xfermode >= 0 && xfermode <= SkXfermode::kLastCoeffMode); return gBlendTable[0][0][xfermode]; } /////////////////////////////////////////////////////////////////////////////// class PorterDuffXferProcessor : public GrXferProcessor { public: static GrXferProcessor* Create(SkXfermode::Mode xfermode, const GrDeviceCoordTexture* dstCopy, bool willReadDstColor) { return SkNEW_ARGS(PorterDuffXferProcessor, (xfermode, dstCopy, willReadDstColor)); } ~PorterDuffXferProcessor() override; const char* name() const override { return "Porter Duff"; } GrGLXferProcessor* createGLInstance() const override; bool hasSecondaryOutput() const override { return fBlendFormula.hasSecondaryOutput(); } SkXfermode::Mode getXfermode() const { return fXfermode; } BlendFormula getBlendFormula() const { return fBlendFormula; } private: PorterDuffXferProcessor(SkXfermode::Mode, const GrDeviceCoordTexture* dstCopy, bool willReadDstColor); GrXferProcessor::OptFlags onGetOptimizations(const GrProcOptInfo& colorPOI, const GrProcOptInfo& coveragePOI, bool doesStencilWrite, GrColor* overrideColor, const GrCaps& caps) override; void onGetGLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const override; void onGetBlendInfo(GrXferProcessor::BlendInfo* blendInfo) const override { if (!this->willReadDstColor()) { blendInfo->fEquation = fBlendFormula.fBlendEquation; blendInfo->fSrcBlend = fBlendFormula.fSrcCoeff; blendInfo->fDstBlend = fBlendFormula.fDstCoeff; blendInfo->fWriteColor = fBlendFormula.modifiesDst(); } } bool onIsEqual(const GrXferProcessor& xpBase) const override { const PorterDuffXferProcessor& xp = xpBase.cast(); return fXfermode == xp.fXfermode && fBlendFormula == xp.fBlendFormula; } SkXfermode::Mode fXfermode; BlendFormula fBlendFormula; typedef GrXferProcessor INHERITED; }; /////////////////////////////////////////////////////////////////////////////// void append_color_output(const PorterDuffXferProcessor& xp, GrGLXPFragmentBuilder* fsBuilder, BlendFormula::OutputType outputType, const char* output, const char* inColor, const char* inCoverage) { switch (outputType) { case BlendFormula::kNone_OutputType: fsBuilder->codeAppendf("%s = vec4(0.0);", output); break; case BlendFormula::kCoverage_OutputType: fsBuilder->codeAppendf("%s = %s;", output, xp.readsCoverage() ? inCoverage : "vec4(1.0)"); break; case BlendFormula::kModulate_OutputType: if (xp.readsCoverage()) { fsBuilder->codeAppendf("%s = %s * %s;", output, inColor, inCoverage); } else { fsBuilder->codeAppendf("%s = %s;", output, inColor); } break; case BlendFormula::kISAModulate_OutputType: if (xp.readsCoverage()) { fsBuilder->codeAppendf("%s = (1.0 - %s.a) * %s;", output, inColor, inCoverage); } else { fsBuilder->codeAppendf("%s = vec4(1.0 - %s.a);", output, inColor); } break; case BlendFormula::kISCModulate_OutputType: if (xp.readsCoverage()) { fsBuilder->codeAppendf("%s = (vec4(1.0) - %s) * %s;", output, inColor, inCoverage); } else { fsBuilder->codeAppendf("%s = vec4(1.0) - %s;", output, inColor); } break; default: SkFAIL("Unsupported output type."); break; } } bool append_porterduff_term(GrGLXPFragmentBuilder* fsBuilder, SkXfermode::Coeff coeff, const char* colorName, const char* srcColorName, const char* dstColorName, bool hasPrevious) { if (SkXfermode::kZero_Coeff == coeff) { return hasPrevious; } else { if (hasPrevious) { fsBuilder->codeAppend(" + "); } fsBuilder->codeAppendf("%s", colorName); switch (coeff) { case SkXfermode::kOne_Coeff: break; case SkXfermode::kSC_Coeff: fsBuilder->codeAppendf(" * %s", srcColorName); break; case SkXfermode::kISC_Coeff: fsBuilder->codeAppendf(" * (vec4(1.0) - %s)", srcColorName); break; case SkXfermode::kDC_Coeff: fsBuilder->codeAppendf(" * %s", dstColorName); break; case SkXfermode::kIDC_Coeff: fsBuilder->codeAppendf(" * (vec4(1.0) - %s)", dstColorName); break; case SkXfermode::kSA_Coeff: fsBuilder->codeAppendf(" * %s.a", srcColorName); break; case SkXfermode::kISA_Coeff: fsBuilder->codeAppendf(" * (1.0 - %s.a)", srcColorName); break; case SkXfermode::kDA_Coeff: fsBuilder->codeAppendf(" * %s.a", dstColorName); break; case SkXfermode::kIDA_Coeff: 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(); if (xp.willReadDstColor()) { b->add32(xp.getXfermode()); // Parent class includes willReadDstColor() in key. } else { b->add32(SkToInt(xp.readsCoverage()) | (xp.getBlendFormula().fPrimaryOutputType << 1) | (xp.getBlendFormula().fSecondaryOutputType << 4)); GR_STATIC_ASSERT(BlendFormula::kLast_OutputType < 8); } }; private: void onEmitCode(const EmitArgs& args) override { const PorterDuffXferProcessor& xp = args.fXP.cast(); GrGLXPFragmentBuilder* fsBuilder = args.fPB->getFragmentShaderBuilder(); if (!xp.willReadDstColor()) { BlendFormula blendFormula = xp.getBlendFormula(); if (blendFormula.hasSecondaryOutput()) { append_color_output(xp, fsBuilder, blendFormula.fSecondaryOutputType, args.fOutputSecondary, args.fInputColor, args.fInputCoverage); } append_color_output(xp, fsBuilder, blendFormula.fPrimaryOutputType, args.fOutputPrimary, args.fInputColor, args.fInputCoverage); } else { SkASSERT(xp.willReadDstColor()); SkXfermode::Coeff srcCoeff, dstCoeff; SkXfermode::ModeAsCoeff(xp.getXfermode(), &srcCoeff, &dstCoeff); const char* dstColor = fsBuilder->dstColor(); fsBuilder->codeAppend("vec4 colorBlend ="); // append src blend bool didAppend = append_porterduff_term(fsBuilder, srcCoeff, args.fInputColor, args.fInputColor, dstColor, false); // append dst blend SkAssertResult(append_porterduff_term(fsBuilder, dstCoeff, 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(SkXfermode::Mode xfermode, const GrDeviceCoordTexture* dstCopy, bool willReadDstColor) : INHERITED(dstCopy, willReadDstColor) , fXfermode(xfermode) , fBlendFormula(get_unoptimized_blend_formula(xfermode)) { this->initClassID(); } 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::onGetOptimizations(const GrProcOptInfo& colorPOI, const GrProcOptInfo& coveragePOI, bool doesStencilWrite, GrColor* overrideColor, const GrCaps& caps) { if (this->willReadDstColor()) { return GrXferProcessor::kNone_Opt; } fBlendFormula = get_blend_formula(fXfermode, colorPOI, coveragePOI); GrXferProcessor::OptFlags optFlags = GrXferProcessor::kNone_Opt; 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; } /////////////////////////////////////////////////////////////////////////////// class PDLCDXferProcessor : public GrXferProcessor { public: static GrXferProcessor* Create(SkXfermode::Mode xfermode, const GrProcOptInfo& colorPOI); ~PDLCDXferProcessor() override; const char* name() const override { return "Porter Duff LCD"; } GrGLXferProcessor* createGLInstance() const override; bool hasSecondaryOutput() const override { return false; } private: PDLCDXferProcessor(GrColor blendConstant, uint8_t alpha); GrXferProcessor::OptFlags onGetOptimizations(const GrProcOptInfo& colorPOI, const GrProcOptInfo& coveragePOI, bool doesStencilWrite, GrColor* overrideColor, const GrCaps& caps) override; void onGetGLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const override; void onGetBlendInfo(GrXferProcessor::BlendInfo* blendInfo) const override { blendInfo->fSrcBlend = kConstC_GrBlendCoeff; blendInfo->fDstBlend = kISC_GrBlendCoeff; blendInfo->fBlendConstant = fBlendConstant; } bool onIsEqual(const GrXferProcessor& xpBase) const override { const PDLCDXferProcessor& xp = xpBase.cast(); if (fBlendConstant != xp.fBlendConstant || fAlpha != xp.fAlpha) { return false; } return true; } GrColor fBlendConstant; uint8_t fAlpha; typedef GrXferProcessor INHERITED; }; /////////////////////////////////////////////////////////////////////////////// class GLPDLCDXferProcessor : public GrGLXferProcessor { public: GLPDLCDXferProcessor(const GrProcessor&) {} virtual ~GLPDLCDXferProcessor() {} static void GenKey(const GrProcessor& processor, const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) {} private: void onEmitCode(const EmitArgs& args) override { GrGLXPFragmentBuilder* fsBuilder = args.fPB->getFragmentShaderBuilder(); fsBuilder->codeAppendf("%s = %s * %s;", args.fOutputPrimary, args.fInputColor, args.fInputCoverage); } void onSetData(const GrGLProgramDataManager&, const GrXferProcessor&) override {}; typedef GrGLXferProcessor INHERITED; }; /////////////////////////////////////////////////////////////////////////////// PDLCDXferProcessor::PDLCDXferProcessor(GrColor blendConstant, uint8_t alpha) : fBlendConstant(blendConstant) , fAlpha(alpha) { this->initClassID(); } GrXferProcessor* PDLCDXferProcessor::Create(SkXfermode::Mode xfermode, const GrProcOptInfo& colorPOI) { if (SkXfermode::kSrcOver_Mode != xfermode) { return NULL; } if (kRGBA_GrColorComponentFlags != colorPOI.validFlags()) { return NULL; } GrColor blendConstant = GrUnPreMulColor(colorPOI.color()); uint8_t alpha = GrColorUnpackA(blendConstant); blendConstant |= (0xff << GrColor_SHIFT_A); return SkNEW_ARGS(PDLCDXferProcessor, (blendConstant, alpha)); } PDLCDXferProcessor::~PDLCDXferProcessor() { } void PDLCDXferProcessor::onGetGLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const { GLPDLCDXferProcessor::GenKey(*this, caps, b); } GrGLXferProcessor* PDLCDXferProcessor::createGLInstance() const { return SkNEW_ARGS(GLPDLCDXferProcessor, (*this)); } GrXferProcessor::OptFlags PDLCDXferProcessor::onGetOptimizations(const GrProcOptInfo& colorPOI, const GrProcOptInfo& coveragePOI, bool doesStencilWrite, GrColor* overrideColor, const GrCaps& caps) { // 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. *overrideColor = GrColorPackRGBA(fAlpha, fAlpha, fAlpha, fAlpha); return GrXferProcessor::kOverrideColor_OptFlag; } /////////////////////////////////////////////////////////////////////////////// GrPorterDuffXPFactory::GrPorterDuffXPFactory(SkXfermode::Mode xfermode) : fXfermode(xfermode) { this->initClassID(); } GrXPFactory* GrPorterDuffXPFactory::Create(SkXfermode::Mode xfermode) { static GrPorterDuffXPFactory gClearPDXPF(SkXfermode::kClear_Mode); static GrPorterDuffXPFactory gSrcPDXPF(SkXfermode::kSrc_Mode); static GrPorterDuffXPFactory gDstPDXPF(SkXfermode::kDst_Mode); static GrPorterDuffXPFactory gSrcOverPDXPF(SkXfermode::kSrcOver_Mode); static GrPorterDuffXPFactory gDstOverPDXPF(SkXfermode::kDstOver_Mode); static GrPorterDuffXPFactory gSrcInPDXPF(SkXfermode::kSrcIn_Mode); static GrPorterDuffXPFactory gDstInPDXPF(SkXfermode::kDstIn_Mode); static GrPorterDuffXPFactory gSrcOutPDXPF(SkXfermode::kSrcOut_Mode); static GrPorterDuffXPFactory gDstOutPDXPF(SkXfermode::kDstOut_Mode); static GrPorterDuffXPFactory gSrcATopPDXPF(SkXfermode::kSrcATop_Mode); static GrPorterDuffXPFactory gDstATopPDXPF(SkXfermode::kDstATop_Mode); static GrPorterDuffXPFactory gXorPDXPF(SkXfermode::kXor_Mode); static GrPorterDuffXPFactory gPlusPDXPF(SkXfermode::kPlus_Mode); static GrPorterDuffXPFactory gModulatePDXPF(SkXfermode::kModulate_Mode); static GrPorterDuffXPFactory gScreenPDXPF(SkXfermode::kScreen_Mode); static GrPorterDuffXPFactory* gFactories[] = { &gClearPDXPF, &gSrcPDXPF, &gDstPDXPF, &gSrcOverPDXPF, &gDstOverPDXPF, &gSrcInPDXPF, &gDstInPDXPF, &gSrcOutPDXPF, &gDstOutPDXPF, &gSrcATopPDXPF, &gDstATopPDXPF, &gXorPDXPF, &gPlusPDXPF, &gModulatePDXPF, &gScreenPDXPF }; GR_STATIC_ASSERT(SK_ARRAY_COUNT(gFactories) == SkXfermode::kLastCoeffMode + 1); if (xfermode < 0 || xfermode > SkXfermode::kLastCoeffMode) { return NULL; } return SkRef(gFactories[xfermode]); } GrXferProcessor* GrPorterDuffXPFactory::onCreateXferProcessor(const GrCaps& caps, const GrProcOptInfo& colorPOI, const GrProcOptInfo& covPOI, const GrDeviceCoordTexture* dstCopy) const { if (covPOI.isFourChannelOutput()) { return PDLCDXferProcessor::Create(fXfermode, colorPOI); } else { return PorterDuffXferProcessor::Create(fXfermode, dstCopy, this->willReadDstColor(caps, colorPOI, covPOI)); } } bool GrPorterDuffXPFactory::supportsRGBCoverage(GrColor /*knownColor*/, uint32_t knownColorFlags) const { if (SkXfermode::kSrcOver_Mode == fXfermode && kRGBA_GrColorComponentFlags == knownColorFlags) { return true; } return false; } void GrPorterDuffXPFactory::getInvariantOutput(const GrProcOptInfo& colorPOI, const GrProcOptInfo& coveragePOI, GrXPFactory::InvariantOutput* output) const { const BlendFormula& blendFormula = get_blend_formula(fXfermode, colorPOI, coveragePOI); if (blendFormula.usesDstColor()) { output->fWillBlendWithDst = true; output->fBlendedColorFlags = kNone_GrColorComponentFlags; return; } SkASSERT(coveragePOI.isSolidWhite()); SkASSERT(kAdd_GrBlendEquation == blendFormula.fBlendEquation); output->fWillBlendWithDst = false; switch (blendFormula.fSrcCoeff) { case kZero_GrBlendCoeff: output->fBlendedColor = 0; output->fBlendedColorFlags = kRGBA_GrColorComponentFlags; return; case kOne_GrBlendCoeff: output->fBlendedColor = colorPOI.color(); output->fBlendedColorFlags = colorPOI.validFlags(); return; // TODO: update if we ever use const color. default: output->fBlendedColorFlags = kNone_GrColorComponentFlags; return; } } bool GrPorterDuffXPFactory::willReadDstColor(const GrCaps& caps, const GrProcOptInfo& colorPOI, const GrProcOptInfo& coveragePOI) const { // Some formulas use dual source blending, so we fall back if it is required but not supported. return !caps.shaderCaps()->dualSourceBlendingSupport() && get_blend_formula(fXfermode, colorPOI, coveragePOI).hasSecondaryOutput(); } GR_DEFINE_XP_FACTORY_TEST(GrPorterDuffXPFactory); GrXPFactory* GrPorterDuffXPFactory::TestCreate(SkRandom* random, GrContext*, const GrCaps&, GrTexture*[]) { SkXfermode::Mode mode = SkXfermode::Mode(random->nextULessThan(SkXfermode::kLastCoeffMode)); return GrPorterDuffXPFactory::Create(mode); } void GrPorterDuffXPFactory::TestGetXPOutputTypes(const GrXferProcessor* xp, int* outPrimary, int* outSecondary) { if (!!strcmp(xp->name(), "Porter Duff")) { *outPrimary = *outSecondary = -1; return; } BlendFormula blendFormula = static_cast(xp)->getBlendFormula(); *outPrimary = blendFormula.fPrimaryOutputType; *outSecondary = blendFormula.fSecondaryOutputType; }