| /* | |
| * Copyright 2023 Google LLC | |
| * | |
| * Use of this source code is governed by a BSD-style license that can be | |
| * found in the LICENSE file. | |
| */ | |
| #include "src/gpu/BlendFormula.h" | |
| #include "include/core/SkBlendMode.h" | |
| namespace { | |
| using skgpu::BlendFormula; | |
| /** | |
| * When there is no coverage, or the blend mode can tweak alpha for coverage, we use the standard | |
| * Porter Duff formula. | |
| */ | |
| constexpr BlendFormula MakeCoeffFormula(skgpu::BlendCoeff srcCoeff, skgpu::BlendCoeff dstCoeff) { | |
| // When the coeffs are (Zero, Zero) or (Zero, One) we set the primary output to none. | |
| return (skgpu::BlendCoeff::kZero == srcCoeff && | |
| (skgpu::BlendCoeff::kZero == dstCoeff || skgpu::BlendCoeff::kOne == dstCoeff)) | |
| ? BlendFormula(BlendFormula::kNone_OutputType, BlendFormula::kNone_OutputType, | |
| skgpu::BlendEquation::kAdd, skgpu::BlendCoeff::kZero, dstCoeff) | |
| : BlendFormula(BlendFormula::kModulate_OutputType, BlendFormula::kNone_OutputType, | |
| skgpu::BlendEquation::kAdd, srcCoeff, dstCoeff); | |
| } | |
| /** | |
| * Basic coeff formula similar to MakeCoeffFormula but we will make the src f*Sa. This is used in | |
| * LCD dst-out. | |
| */ | |
| constexpr BlendFormula MakeSAModulateFormula(skgpu::BlendCoeff srcCoeff, | |
| skgpu::BlendCoeff dstCoeff) { | |
| return BlendFormula(BlendFormula::kSAModulate_OutputType, BlendFormula::kNone_OutputType, | |
| skgpu::BlendEquation::kAdd, srcCoeff, dstCoeff); | |
| } | |
| /** | |
| * 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) | |
| */ | |
| constexpr BlendFormula MakeCoverageFormula(BlendFormula::OutputType oneMinusDstCoeffModulateOutput, | |
| skgpu::BlendCoeff srcCoeff) { | |
| return BlendFormula(BlendFormula::kModulate_OutputType, oneMinusDstCoeffModulateOutput, | |
| skgpu::BlendEquation::kAdd, srcCoeff, skgpu::BlendCoeff::kIS2C); | |
| } | |
| /** | |
| * 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) | |
| */ | |
| constexpr BlendFormula MakeCoverageSrcCoeffZeroFormula( | |
| BlendFormula::OutputType oneMinusDstCoeffModulateOutput) { | |
| return BlendFormula(oneMinusDstCoeffModulateOutput, BlendFormula::kNone_OutputType, | |
| skgpu::BlendEquation::kReverseSubtract, skgpu::BlendCoeff::kDC, | |
| skgpu::BlendCoeff::kOne); | |
| } | |
| /** | |
| * 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 | |
| */ | |
| constexpr BlendFormula MakeCoverageDstCoeffZeroFormula(skgpu::BlendCoeff srcCoeff) { | |
| return BlendFormula(BlendFormula::kModulate_OutputType, BlendFormula::kCoverage_OutputType, | |
| skgpu::BlendEquation::kAdd, srcCoeff, skgpu::BlendCoeff::kIS2A); | |
| } | |
| /** | |
| * 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. | |
| */ | |
| constexpr BlendFormula gBlendTable[2][2][(int)SkBlendMode::kLastCoeffMode + 1] = { | |
| /*>> No coverage, input color unknown <<*/ {{ | |
| /* clear */ MakeCoeffFormula(skgpu::BlendCoeff::kZero, skgpu::BlendCoeff::kZero), | |
| /* src */ MakeCoeffFormula(skgpu::BlendCoeff::kOne, skgpu::BlendCoeff::kZero), | |
| /* dst */ MakeCoeffFormula(skgpu::BlendCoeff::kZero, skgpu::BlendCoeff::kOne), | |
| /* src-over */ MakeCoeffFormula(skgpu::BlendCoeff::kOne, skgpu::BlendCoeff::kISA), | |
| /* dst-over */ MakeCoeffFormula(skgpu::BlendCoeff::kIDA, skgpu::BlendCoeff::kOne), | |
| /* src-in */ MakeCoeffFormula(skgpu::BlendCoeff::kDA, skgpu::BlendCoeff::kZero), | |
| /* dst-in */ MakeCoeffFormula(skgpu::BlendCoeff::kZero, skgpu::BlendCoeff::kSA), | |
| /* src-out */ MakeCoeffFormula(skgpu::BlendCoeff::kIDA, skgpu::BlendCoeff::kZero), | |
| /* dst-out */ MakeCoeffFormula(skgpu::BlendCoeff::kZero, skgpu::BlendCoeff::kISA), | |
| /* src-atop */ MakeCoeffFormula(skgpu::BlendCoeff::kDA, skgpu::BlendCoeff::kISA), | |
| /* dst-atop */ MakeCoeffFormula(skgpu::BlendCoeff::kIDA, skgpu::BlendCoeff::kSA), | |
| /* xor */ MakeCoeffFormula(skgpu::BlendCoeff::kIDA, skgpu::BlendCoeff::kISA), | |
| /* plus */ MakeCoeffFormula(skgpu::BlendCoeff::kOne, skgpu::BlendCoeff::kOne), | |
| /* modulate */ MakeCoeffFormula(skgpu::BlendCoeff::kZero, skgpu::BlendCoeff::kSC), | |
| /* screen */ MakeCoeffFormula(skgpu::BlendCoeff::kOne, skgpu::BlendCoeff::kISC), | |
| }, /*>> Has coverage, input color unknown <<*/ { | |
| /* clear */ MakeCoverageSrcCoeffZeroFormula(BlendFormula::kCoverage_OutputType), | |
| /* src */ MakeCoverageDstCoeffZeroFormula(skgpu::BlendCoeff::kOne), | |
| /* dst */ MakeCoeffFormula(skgpu::BlendCoeff::kZero, skgpu::BlendCoeff::kOne), | |
| /* src-over */ MakeCoeffFormula(skgpu::BlendCoeff::kOne, skgpu::BlendCoeff::kISA), | |
| /* dst-over */ MakeCoeffFormula(skgpu::BlendCoeff::kIDA, skgpu::BlendCoeff::kOne), | |
| /* src-in */ MakeCoverageDstCoeffZeroFormula(skgpu::BlendCoeff::kDA), | |
| /* dst-in */ MakeCoverageSrcCoeffZeroFormula(BlendFormula::kISAModulate_OutputType), | |
| /* src-out */ MakeCoverageDstCoeffZeroFormula(skgpu::BlendCoeff::kIDA), | |
| /* dst-out */ MakeCoeffFormula(skgpu::BlendCoeff::kZero, skgpu::BlendCoeff::kISA), | |
| /* src-atop */ MakeCoeffFormula(skgpu::BlendCoeff::kDA, skgpu::BlendCoeff::kISA), | |
| /* dst-atop */ MakeCoverageFormula(BlendFormula::kISAModulate_OutputType, | |
| skgpu::BlendCoeff::kIDA), | |
| /* xor */ MakeCoeffFormula(skgpu::BlendCoeff::kIDA, skgpu::BlendCoeff::kISA), | |
| /* plus */ MakeCoeffFormula(skgpu::BlendCoeff::kOne, skgpu::BlendCoeff::kOne), | |
| /* modulate */ MakeCoverageSrcCoeffZeroFormula(BlendFormula::kISCModulate_OutputType), | |
| /* screen */ MakeCoeffFormula(skgpu::BlendCoeff::kOne, skgpu::BlendCoeff::kISC), | |
| }}, /*>> No coverage, input color opaque <<*/ {{ | |
| /* clear */ MakeCoeffFormula(skgpu::BlendCoeff::kZero, skgpu::BlendCoeff::kZero), | |
| /* src */ MakeCoeffFormula(skgpu::BlendCoeff::kOne, skgpu::BlendCoeff::kZero), | |
| /* dst */ MakeCoeffFormula(skgpu::BlendCoeff::kZero, skgpu::BlendCoeff::kOne), | |
| /* src-over */ MakeCoeffFormula(skgpu::BlendCoeff::kOne, | |
| skgpu::BlendCoeff::kISA), // see comment below | |
| /* dst-over */ MakeCoeffFormula(skgpu::BlendCoeff::kIDA, skgpu::BlendCoeff::kOne), | |
| /* src-in */ MakeCoeffFormula(skgpu::BlendCoeff::kDA, skgpu::BlendCoeff::kZero), | |
| /* dst-in */ MakeCoeffFormula(skgpu::BlendCoeff::kZero, skgpu::BlendCoeff::kOne), | |
| /* src-out */ MakeCoeffFormula(skgpu::BlendCoeff::kIDA, skgpu::BlendCoeff::kZero), | |
| /* dst-out */ MakeCoeffFormula(skgpu::BlendCoeff::kZero, skgpu::BlendCoeff::kZero), | |
| /* src-atop */ MakeCoeffFormula(skgpu::BlendCoeff::kDA, skgpu::BlendCoeff::kZero), | |
| /* dst-atop */ MakeCoeffFormula(skgpu::BlendCoeff::kIDA, skgpu::BlendCoeff::kOne), | |
| /* xor */ MakeCoeffFormula(skgpu::BlendCoeff::kIDA, skgpu::BlendCoeff::kZero), | |
| /* plus */ MakeCoeffFormula(skgpu::BlendCoeff::kOne, skgpu::BlendCoeff::kOne), | |
| /* modulate */ MakeCoeffFormula(skgpu::BlendCoeff::kZero, skgpu::BlendCoeff::kSC), | |
| /* screen */ MakeCoeffFormula(skgpu::BlendCoeff::kOne, skgpu::BlendCoeff::kISC), | |
| }, /*>> Has coverage, input color opaque <<*/ { | |
| /* clear */ MakeCoverageSrcCoeffZeroFormula(BlendFormula::kCoverage_OutputType), | |
| /* src */ MakeCoeffFormula(skgpu::BlendCoeff::kOne, skgpu::BlendCoeff::kISA), | |
| /* dst */ MakeCoeffFormula(skgpu::BlendCoeff::kZero, skgpu::BlendCoeff::kOne), | |
| /* src-over */ MakeCoeffFormula(skgpu::BlendCoeff::kOne, skgpu::BlendCoeff::kISA), | |
| /* dst-over */ MakeCoeffFormula(skgpu::BlendCoeff::kIDA, skgpu::BlendCoeff::kOne), | |
| /* src-in */ MakeCoeffFormula(skgpu::BlendCoeff::kDA, skgpu::BlendCoeff::kISA), | |
| /* dst-in */ MakeCoeffFormula(skgpu::BlendCoeff::kZero, skgpu::BlendCoeff::kOne), | |
| /* src-out */ MakeCoeffFormula(skgpu::BlendCoeff::kIDA, skgpu::BlendCoeff::kISA), | |
| /* dst-out */ MakeCoverageSrcCoeffZeroFormula(BlendFormula::kCoverage_OutputType), | |
| /* src-atop */ MakeCoeffFormula(skgpu::BlendCoeff::kDA, skgpu::BlendCoeff::kISA), | |
| /* dst-atop */ MakeCoeffFormula(skgpu::BlendCoeff::kIDA, skgpu::BlendCoeff::kOne), | |
| /* xor */ MakeCoeffFormula(skgpu::BlendCoeff::kIDA, skgpu::BlendCoeff::kISA), | |
| /* plus */ MakeCoeffFormula(skgpu::BlendCoeff::kOne, skgpu::BlendCoeff::kOne), | |
| /* modulate */ MakeCoverageSrcCoeffZeroFormula(BlendFormula::kISCModulate_OutputType), | |
| /* screen */ MakeCoeffFormula(skgpu::BlendCoeff::kOne, skgpu::BlendCoeff::kISC), | |
| }}}; | |
| // In the above table src-over is not optimized to src mode when the color is opaque because we | |
| // found no advantage to doing so. Also, we are using a global src-over blend in most cases which is | |
| // not specialized for opaque input. For GPUs where dropping to src (and thus able to disable | |
| // blending) is an advantage we change the blend mode to src before getting the blend formula from | |
| // this table. | |
| constexpr BlendFormula gLCDBlendTable[(int)SkBlendMode::kLastCoeffMode + 1] = { | |
| /* clear */ MakeCoverageSrcCoeffZeroFormula(BlendFormula::kCoverage_OutputType), | |
| /* src */ MakeCoverageFormula(BlendFormula::kCoverage_OutputType, | |
| skgpu::BlendCoeff::kOne), | |
| /* dst */ MakeCoeffFormula(skgpu::BlendCoeff::kZero, | |
| skgpu::BlendCoeff::kOne), | |
| /* src-over */ MakeCoverageFormula(BlendFormula::kSAModulate_OutputType, | |
| skgpu::BlendCoeff::kOne), | |
| /* dst-over */ MakeCoeffFormula(skgpu::BlendCoeff::kIDA, | |
| skgpu::BlendCoeff::kOne), | |
| /* src-in */ MakeCoverageFormula(BlendFormula::kCoverage_OutputType, | |
| skgpu::BlendCoeff::kDA), | |
| /* dst-in */ MakeCoverageSrcCoeffZeroFormula(BlendFormula::kISAModulate_OutputType), | |
| /* src-out */ MakeCoverageFormula(BlendFormula::kCoverage_OutputType, | |
| skgpu::BlendCoeff::kIDA), | |
| /* dst-out */ MakeSAModulateFormula(skgpu::BlendCoeff::kZero, | |
| skgpu::BlendCoeff::kISC), | |
| /* src-atop */ MakeCoverageFormula(BlendFormula::kSAModulate_OutputType, | |
| skgpu::BlendCoeff::kDA), | |
| /* dst-atop */ MakeCoverageFormula(BlendFormula::kISAModulate_OutputType, | |
| skgpu::BlendCoeff::kIDA), | |
| /* xor */ MakeCoverageFormula(BlendFormula::kSAModulate_OutputType, | |
| skgpu::BlendCoeff::kIDA), | |
| /* plus */ MakeCoeffFormula(skgpu::BlendCoeff::kOne, | |
| skgpu::BlendCoeff::kOne), | |
| /* modulate */ MakeCoverageSrcCoeffZeroFormula(BlendFormula::kISCModulate_OutputType), | |
| /* screen */ MakeCoeffFormula(skgpu::BlendCoeff::kOne, | |
| skgpu::BlendCoeff::kISC), | |
| }; | |
| } // anonymous namespace | |
| namespace skgpu { | |
| BlendFormula GetBlendFormula(bool isOpaque, bool hasCoverage, SkBlendMode xfermode) { | |
| SkASSERT((unsigned)xfermode <= (unsigned)SkBlendMode::kLastCoeffMode); | |
| return gBlendTable[isOpaque][hasCoverage][(int)xfermode]; | |
| } | |
| BlendFormula GetLCDBlendFormula(SkBlendMode xfermode) { | |
| SkASSERT((unsigned)xfermode <= (unsigned)SkBlendMode::kLastCoeffMode); | |
| return gLCDBlendTable[(int)xfermode]; | |
| } | |
| } // namespace skgpu |