/* | |

* Copyright 2013 Google Inc. | |

* | |

* Use of this source code is governed by a BSD-style license that can be | |

* found in the LICENSE file. | |

*/ | |

#ifndef skgpu_Blend_DEFINED | |

#define skgpu_Blend_DEFINED | |

#include "include/core/SkSpan.h" | |

#include "include/core/SkTypes.h" | |

#include "include/private/SkColorData.h" | |

#include "include/private/base/SkDebug.h" | |

#include <cstdint> | |

enum class SkBlendMode; | |

class SkString; | |

namespace skgpu { | |

/** | |

* Equations for alpha-blending. | |

*/ | |

enum class BlendEquation : uint8_t { | |

// Basic blend equations. | |

kAdd, //<! Cs*S + Cd*D | |

kSubtract, //<! Cs*S - Cd*D | |

kReverseSubtract, //<! Cd*D - Cs*S | |

// Advanced blend equations. These are described in the SVG and PDF specs. | |

kScreen, | |

kOverlay, | |

kDarken, | |

kLighten, | |

kColorDodge, | |

kColorBurn, | |

kHardLight, | |

kSoftLight, | |

kDifference, | |

kExclusion, | |

kMultiply, | |

kHSLHue, | |

kHSLSaturation, | |

kHSLColor, | |

kHSLLuminosity, | |

kIllegal, | |

kFirstAdvanced = kScreen, | |

kLast = kIllegal, | |

}; | |

static const int kBlendEquationCnt = static_cast<int>(BlendEquation::kLast) + 1; | |

/** | |

* Coefficients for alpha-blending. | |

*/ | |

enum class BlendCoeff : uint8_t { | |

kZero, //<! 0 | |

kOne, //<! 1 | |

kSC, //<! src color | |

kISC, //<! one minus src color | |

kDC, //<! dst color | |

kIDC, //<! one minus dst color | |

kSA, //<! src alpha | |

kISA, //<! one minus src alpha | |

kDA, //<! dst alpha | |

kIDA, //<! one minus dst alpha | |

kConstC, //<! constant color | |

kIConstC, //<! one minus constant color | |

kS2C, | |

kIS2C, | |

kS2A, | |

kIS2A, | |

kIllegal, | |

kLast = kIllegal, | |

}; | |

struct BlendInfo { | |

SkDEBUGCODE(SkString dump() const;) | |

bool operator==(const BlendInfo& other) const { | |

return fEquation == other.fEquation && | |

fSrcBlend == other.fSrcBlend && | |

fDstBlend == other.fDstBlend && | |

fBlendConstant == other.fBlendConstant && | |

fWritesColor == other.fWritesColor; | |

} | |

skgpu::BlendEquation fEquation = skgpu::BlendEquation::kAdd; | |

skgpu::BlendCoeff fSrcBlend = skgpu::BlendCoeff::kOne; | |

skgpu::BlendCoeff fDstBlend = skgpu::BlendCoeff::kZero; | |

SkPMColor4f fBlendConstant = SK_PMColor4fTRANSPARENT; | |

bool fWritesColor = true; | |

}; | |

static const int kBlendCoeffCnt = static_cast<int>(BlendCoeff::kLast) + 1; | |

static constexpr bool BlendCoeffRefsSrc(const BlendCoeff coeff) { | |

return BlendCoeff::kSC == coeff || BlendCoeff::kISC == coeff || BlendCoeff::kSA == coeff || | |

BlendCoeff::kISA == coeff; | |

} | |

static constexpr bool BlendCoeffRefsDst(const BlendCoeff coeff) { | |

return BlendCoeff::kDC == coeff || BlendCoeff::kIDC == coeff || BlendCoeff::kDA == coeff || | |

BlendCoeff::kIDA == coeff; | |

} | |

static constexpr bool BlendCoeffRefsSrc2(const BlendCoeff coeff) { | |

return BlendCoeff::kS2C == coeff || BlendCoeff::kIS2C == coeff || | |

BlendCoeff::kS2A == coeff || BlendCoeff::kIS2A == coeff; | |

} | |

static constexpr bool BlendCoeffsUseSrcColor(BlendCoeff srcCoeff, BlendCoeff dstCoeff) { | |

return BlendCoeff::kZero != srcCoeff || BlendCoeffRefsSrc(dstCoeff); | |

} | |

static constexpr bool BlendCoeffsUseDstColor(BlendCoeff srcCoeff, | |

BlendCoeff dstCoeff, | |

bool srcColorIsOpaque) { | |

return BlendCoeffRefsDst(srcCoeff) || | |

(dstCoeff != BlendCoeff::kZero && !(dstCoeff == BlendCoeff::kISA && srcColorIsOpaque)); | |

} | |

static constexpr bool BlendEquationIsAdvanced(BlendEquation equation) { | |

return equation >= BlendEquation::kFirstAdvanced && | |

equation != BlendEquation::kIllegal; | |

} | |

static constexpr bool BlendModifiesDst(BlendEquation equation, | |

BlendCoeff srcCoeff, | |

BlendCoeff dstCoeff) { | |

return (BlendEquation::kAdd != equation && BlendEquation::kReverseSubtract != equation) || | |

BlendCoeff::kZero != srcCoeff || BlendCoeff::kOne != dstCoeff; | |

} | |

static constexpr bool BlendCoeffRefsConstant(const BlendCoeff coeff) { | |

return coeff == BlendCoeff::kConstC || coeff == BlendCoeff::kIConstC; | |

} | |

static constexpr bool BlendShouldDisable(BlendEquation equation, | |

BlendCoeff srcCoeff, | |

BlendCoeff dstCoeff) { | |

return (BlendEquation::kAdd == equation || BlendEquation::kSubtract == equation) && | |

BlendCoeff::kOne == srcCoeff && BlendCoeff::kZero == dstCoeff; | |

} | |

/** | |

* Advanced blend equations can always tweak alpha for coverage. (See GrCustomXfermode.cpp) | |

* | |

* For "add" and "reverse subtract" the blend equation with f=coverage is: | |

* | |

* D' = f * (S * srcCoeff + D * dstCoeff) + (1-f) * D | |

* = f * S * srcCoeff + D * (f * dstCoeff + (1 - f)) | |

* | |

* (Let srcCoeff be negative for reverse subtract.) We can tweak alpha for coverage when the | |

* following relationship holds: | |

* | |

* (f*S) * srcCoeff' + D * dstCoeff' == f * S * srcCoeff + D * (f * dstCoeff + (1 - f)) | |

* | |

* (Where srcCoeff' and dstCoeff' have any reference to S pre-multiplied by f.) | |

* | |

* It's easy to see this works for the src term as long as srcCoeff' == srcCoeff (meaning srcCoeff | |

* does not reference S). For the dst term, this will work as long as the following is true: | |

*| | |

* dstCoeff' == f * dstCoeff + (1 - f) | |

* dstCoeff' == 1 - f * (1 - dstCoeff) | |

* | |

* By inspection we can see this will work as long as dstCoeff has a 1, and any other term in | |

* dstCoeff references S. | |

* | |

* Moreover, if the blend doesn't modify the dst at all then it is ok to arbitrarily modify the src | |

* color so folding in coverage is allowed. | |

*/ | |

static constexpr bool BlendAllowsCoverageAsAlpha(BlendEquation equation, | |

BlendCoeff srcCoeff, | |

BlendCoeff dstCoeff) { | |

return BlendEquationIsAdvanced(equation) || | |

!BlendModifiesDst(equation, srcCoeff, dstCoeff) || | |

((BlendEquation::kAdd == equation || BlendEquation::kReverseSubtract == equation) && | |

!BlendCoeffRefsSrc(srcCoeff) && | |

(BlendCoeff::kOne == dstCoeff || BlendCoeff::kISC == dstCoeff || | |

BlendCoeff::kISA == dstCoeff)); | |

} | |

/** | |

* Returns the name of the SkSL built-in blend function for a SkBlendMode. | |

*/ | |

const char* BlendFuncName(SkBlendMode mode); | |

/** | |

* If a blend can be represented by `blend_porter_duff`, returns the associated blend constants as | |

* an array of four floats. If not, returns an empty span. | |

*/ | |

SkSpan<const float> GetPorterDuffBlendConstants(SkBlendMode mode); | |

/** | |

* Returns a pair of "blend function + uniform data" for a particular SkBlendMode. | |

* This allows us to use fewer unique functions when generating shaders, e.g. every Porter-Duff | |

* blend can use the same function. | |

*/ | |

struct ReducedBlendModeInfo { | |

const char* fFunction; | |

SkSpan<const float> fUniformData; | |

}; | |

ReducedBlendModeInfo GetReducedBlendModeInfo(SkBlendMode mode); | |

} // namespace skgpu | |

#endif // skgpu_Blend_DEFINED |