|  | /* | 
|  | * 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/GrGLBlend.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,        //<! 0 | 
|  | kCoverage_OutputType,    //<! inputCoverage | 
|  | kModulate_OutputType,    //<! inputColor * inputCoverage | 
|  | kISAModulate_OutputType, //<! (1 - inputColor.a) * inputCoverage | 
|  | kISCModulate_OutputType, //<! (1 - inputColor) * inputCoverage | 
|  |  | 
|  | kLast_OutputType = kISCModulate_OutputType | 
|  | }; | 
|  |  | 
|  | enum Properties { | 
|  | kModifiesDst_Property              = 1, | 
|  | kUsesDstColor_Property             = 1 << 1, | 
|  | kUsesInputColor_Property           = 1 << 2, | 
|  | kCanTweakAlphaForCoverage_Property = 1 << 3, | 
|  |  | 
|  | kLast_Property = kCanTweakAlphaForCoverage_Property | 
|  | }; | 
|  |  | 
|  | BlendFormula& operator =(const BlendFormula& other) { | 
|  | fData = other.fData; | 
|  | return *this; | 
|  | } | 
|  |  | 
|  | bool operator ==(const BlendFormula& other) const { | 
|  | return fData == other.fData; | 
|  | } | 
|  |  | 
|  | bool hasSecondaryOutput() const { return kNone_OutputType != fSecondaryOutputType; } | 
|  | bool modifiesDst() const { return SkToBool(fProps & kModifiesDst_Property); } | 
|  | bool usesDstColor() const { return SkToBool(fProps & kUsesDstColor_Property); } | 
|  | bool usesInputColor() const { return SkToBool(fProps & kUsesInputColor_Property); } | 
|  | bool canTweakAlphaForCoverage() const { | 
|  | return SkToBool(fProps & kCanTweakAlphaForCoverage_Property); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Deduce the properties of a compile-time constant BlendFormula. | 
|  | */ | 
|  | template<OutputType PrimaryOut, OutputType SecondaryOut, | 
|  | GrBlendEquation BlendEquation, GrBlendCoeff SrcCoeff, GrBlendCoeff DstCoeff> | 
|  | struct get_properties : SkTIntegralConstant<Properties, static_cast<Properties>( | 
|  |  | 
|  | (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<PRIMARY_OUT, SECONDARY_OUT, \ | 
|  | BLEND_EQUATION, SRC_COEFF, DST_COEFF>::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] = { | 
|  |  | 
|  | /*>> 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 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 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), | 
|  |  | 
|  | }, /*>> 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), | 
|  | }}}; | 
|  |  | 
|  | 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]; | 
|  | } | 
|  |  | 
|  | /////////////////////////////////////////////////////////////////////////////// | 
|  |  | 
|  | class PorterDuffXferProcessor : public GrXferProcessor { | 
|  | public: | 
|  | PorterDuffXferProcessor(BlendFormula blendFormula) : fBlendFormula(blendFormula) { | 
|  | this->initClassID<PorterDuffXferProcessor>(); | 
|  | } | 
|  |  | 
|  | const char* name() const override { return "Porter Duff"; } | 
|  |  | 
|  | GrGLXferProcessor* createGLInstance() const override; | 
|  |  | 
|  | BlendFormula getBlendFormula() const { return fBlendFormula; } | 
|  |  | 
|  | private: | 
|  | 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; | 
|  |  | 
|  | bool onHasSecondaryOutput() const override { return fBlendFormula.hasSecondaryOutput(); } | 
|  |  | 
|  | void onGetBlendInfo(GrXferProcessor::BlendInfo* blendInfo) const override { | 
|  | 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<PorterDuffXferProcessor>(); | 
|  | return fBlendFormula == xp.fBlendFormula; | 
|  | } | 
|  |  | 
|  | const BlendFormula fBlendFormula; | 
|  |  | 
|  | typedef GrXferProcessor INHERITED; | 
|  | }; | 
|  |  | 
|  | /////////////////////////////////////////////////////////////////////////////// | 
|  |  | 
|  | static 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: | 
|  | // We can have a coverage formula while not reading coverage if there are mixed samples. | 
|  | 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; | 
|  | } | 
|  | } | 
|  |  | 
|  | class GLPorterDuffXferProcessor : public GrGLXferProcessor { | 
|  | public: | 
|  | static void GenKey(const GrProcessor& processor, GrProcessorKeyBuilder* b) { | 
|  | const PorterDuffXferProcessor& xp = processor.cast<PorterDuffXferProcessor>(); | 
|  | b->add32(SkToInt(xp.readsCoverage()) | | 
|  | (xp.getBlendFormula().fPrimaryOutputType << 1) | | 
|  | (xp.getBlendFormula().fSecondaryOutputType << 4)); | 
|  | GR_STATIC_ASSERT(BlendFormula::kLast_OutputType < 8); | 
|  | }; | 
|  |  | 
|  | private: | 
|  | void emitOutputsForBlendState(const EmitArgs& args) override { | 
|  | const PorterDuffXferProcessor& xp = args.fXP.cast<PorterDuffXferProcessor>(); | 
|  | GrGLXPFragmentBuilder* fsBuilder = args.fPB->getFragmentShaderBuilder(); | 
|  |  | 
|  | 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); | 
|  | } | 
|  |  | 
|  | void onSetData(const GrGLProgramDataManager&, const GrXferProcessor&) override {} | 
|  |  | 
|  | typedef GrGLXferProcessor INHERITED; | 
|  | }; | 
|  |  | 
|  | /////////////////////////////////////////////////////////////////////////////// | 
|  |  | 
|  | void PorterDuffXferProcessor::onGetGLProcessorKey(const GrGLSLCaps&, | 
|  | GrProcessorKeyBuilder* b) const { | 
|  | GLPorterDuffXferProcessor::GenKey(*this, b); | 
|  | } | 
|  |  | 
|  | GrGLXferProcessor* PorterDuffXferProcessor::createGLInstance() const { | 
|  | return SkNEW(GLPorterDuffXferProcessor); | 
|  | } | 
|  |  | 
|  | 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; | 
|  | } | 
|  |  | 
|  | /////////////////////////////////////////////////////////////////////////////// | 
|  |  | 
|  | class ShaderPDXferProcessor : public GrXferProcessor { | 
|  | public: | 
|  | ShaderPDXferProcessor(const DstTexture* dstTexture, | 
|  | bool hasMixedSamples, | 
|  | SkXfermode::Mode xfermode) | 
|  | : INHERITED(dstTexture, true, hasMixedSamples) | 
|  | , fXfermode(xfermode) { | 
|  | this->initClassID<ShaderPDXferProcessor>(); | 
|  | } | 
|  |  | 
|  | const char* name() const override { return "Porter Duff Shader"; } | 
|  |  | 
|  | GrGLXferProcessor* createGLInstance() const override; | 
|  |  | 
|  | SkXfermode::Mode getXfermode() const { return fXfermode; } | 
|  |  | 
|  | private: | 
|  | GrXferProcessor::OptFlags onGetOptimizations(const GrProcOptInfo&, const GrProcOptInfo&, | 
|  | bool, GrColor*, const GrCaps&) override { | 
|  | return kNone_OptFlags; | 
|  | } | 
|  |  | 
|  | void onGetGLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const override; | 
|  |  | 
|  | bool onIsEqual(const GrXferProcessor& xpBase) const override { | 
|  | const ShaderPDXferProcessor& xp = xpBase.cast<ShaderPDXferProcessor>(); | 
|  | return fXfermode == xp.fXfermode; | 
|  | } | 
|  |  | 
|  | const SkXfermode::Mode fXfermode; | 
|  |  | 
|  | typedef GrXferProcessor INHERITED; | 
|  | }; | 
|  |  | 
|  | /////////////////////////////////////////////////////////////////////////////// | 
|  |  | 
|  | class GLShaderPDXferProcessor : public GrGLXferProcessor { | 
|  | public: | 
|  | static void GenKey(const GrProcessor& processor, GrProcessorKeyBuilder* b) { | 
|  | const ShaderPDXferProcessor& xp = processor.cast<ShaderPDXferProcessor>(); | 
|  | b->add32(xp.getXfermode()); | 
|  | } | 
|  |  | 
|  | private: | 
|  | void emitBlendCodeForDstRead(GrGLXPBuilder* pb, const char* srcColor, const char* dstColor, | 
|  | const char* outColor, const GrXferProcessor& proc) override { | 
|  | const ShaderPDXferProcessor& xp = proc.cast<ShaderPDXferProcessor>(); | 
|  | GrGLXPFragmentBuilder* fsBuilder = pb->getFragmentShaderBuilder(); | 
|  |  | 
|  | GrGLBlend::AppendPorterDuffBlend(fsBuilder, srcColor, dstColor, outColor, xp.getXfermode()); | 
|  | } | 
|  |  | 
|  | void onSetData(const GrGLProgramDataManager&, const GrXferProcessor&) override {} | 
|  |  | 
|  | typedef GrGLXferProcessor INHERITED; | 
|  | }; | 
|  |  | 
|  | /////////////////////////////////////////////////////////////////////////////// | 
|  |  | 
|  | void ShaderPDXferProcessor::onGetGLProcessorKey(const GrGLSLCaps&, | 
|  | GrProcessorKeyBuilder* b) const { | 
|  | GLShaderPDXferProcessor::GenKey(*this, b); | 
|  | } | 
|  |  | 
|  | GrGLXferProcessor* ShaderPDXferProcessor::createGLInstance() const { | 
|  | return SkNEW(GLShaderPDXferProcessor); | 
|  | } | 
|  |  | 
|  | /////////////////////////////////////////////////////////////////////////////// | 
|  |  | 
|  | 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; | 
|  |  | 
|  | 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<PDLCDXferProcessor>(); | 
|  | 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 emitOutputsForBlendState(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<PDLCDXferProcessor>(); | 
|  | } | 
|  |  | 
|  | 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) { | 
|  | SkASSERT(fXfermode <= SkXfermode::kLastCoeffMode); | 
|  | this->initClassID<GrPorterDuffXPFactory>(); | 
|  | } | 
|  |  | 
|  | 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, | 
|  | bool hasMixedSamples, | 
|  | const DstTexture* dstTexture) const { | 
|  | if (covPOI.isFourChannelOutput()) { | 
|  | SkASSERT(!dstTexture || !dstTexture->texture()); | 
|  | return PDLCDXferProcessor::Create(fXfermode, colorPOI); | 
|  | } | 
|  |  | 
|  | BlendFormula blendFormula = get_blend_formula(colorPOI, covPOI, hasMixedSamples, fXfermode); | 
|  | if (blendFormula.hasSecondaryOutput() && !caps.shaderCaps()->dualSourceBlendingSupport()) { | 
|  | return SkNEW_ARGS(ShaderPDXferProcessor, (dstTexture, hasMixedSamples, fXfermode)); | 
|  | } | 
|  |  | 
|  | SkASSERT(!dstTexture || !dstTexture->texture()); | 
|  | return SkNEW_ARGS(PorterDuffXferProcessor, (blendFormula)); | 
|  | } | 
|  |  | 
|  | bool GrPorterDuffXPFactory::supportsRGBCoverage(GrColor /*knownColor*/, | 
|  | uint32_t knownColorFlags) const { | 
|  | if (SkXfermode::kSrcOver_Mode == fXfermode && | 
|  | kRGBA_GrColorComponentFlags == knownColorFlags) { | 
|  | return true; | 
|  | } | 
|  | return false; | 
|  | } | 
|  |  | 
|  | 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; | 
|  | } | 
|  | } | 
|  |  | 
|  | 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(); | 
|  | } | 
|  |  | 
|  | GR_DEFINE_XP_FACTORY_TEST(GrPorterDuffXPFactory); | 
|  |  | 
|  | GrXPFactory* GrPorterDuffXPFactory::TestCreate(GrProcessorTestData* d) { | 
|  | SkXfermode::Mode mode = SkXfermode::Mode(d->fRandom->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<const PorterDuffXferProcessor*>(xp)->getBlendFormula(); | 
|  | *outPrimary = blendFormula.fPrimaryOutputType; | 
|  | *outSecondary = blendFormula.fSecondaryOutputType; | 
|  | } |