Implement Porter Duff XP with a blend table
Removes the runtime logic used by PorterDuffXferProcessor to decide blend coeffs and shader outputs, and instead uses a compile-time constant table of pre-selected blend formulas. Separates out the dst read fallback into its own XP. Introduces a new blend strategy for srcCoeff=0 that can apply coverage with a reverse subtract blend equation instead of dual source blending. Adds new macros in GrBlend.h to analyze blend formulas both runtime. Removes kSetCoverageDrawing_OptFlag and GrSimplifyBlend as they are no longer used. Adds a GM that verifies all xfermodes, including arithmetic, with the color/coverage invariants used by Porter Duff. Adds a unit test that verifies each Porter Duff formula with every color/coverage invariant. Major changes: * Uses a reverse subtract blend equation for coverage when srcCoeff=0 (clear, dst-out [Sa=1], dst-in, modulate). Platforms that don't support dual source blending no longer require a dst copy for dst-in and modulate. * Sets BlendInfo::fWriteColor to false when the blend does not modify the dst. GrGLGpu will now use glColorMask instead of blending for these modes (dst, dst-in [Sa=1], modulate ignored for [Sc=1]). * Converts all SA blend coeffs to One for opaque inputs, and ISA to Zero if there is also no coverage. (We keep ISA around when there is coverage because we use it to tweak alpha for coverage.) * Abandons solid white optimizations for the sake of simplicity (screen was the only mode that previous had solid white opts). Minor differences: * Inconsequential differences in opt flags (e.g. we now return kCanTweakAlphaForCoverage_OptFlag even when there is no coverage). * Src coeffs when the shader outputs 0. * IS2C vs IS2A when the secondary output is scalar. BUG=skia: Committed: https://skia.googlesource.com/skia/+/9a70920db22b6309c671f8e5d519bb95570e4414 Review URL: https://codereview.chromium.org/1124373002
This commit is contained in:
parent
3098a752ef
commit
6fd158ea47
236
gm/aaxfermodes.cpp
Normal file
236
gm/aaxfermodes.cpp
Normal file
@ -0,0 +1,236 @@
|
||||
|
||||
/*
|
||||
* Copyright 2015 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
#include "gm.h"
|
||||
#include "SkArithmeticMode.h"
|
||||
#include "SkShader.h"
|
||||
#include "SkXfermode.h"
|
||||
|
||||
enum {
|
||||
kXfermodeCount = SkXfermode::kLastMode + 2, // All xfermodes plus arithmetic mode.
|
||||
kShapeSize = 22,
|
||||
kShapeSpacing = 36,
|
||||
kShapeTypeSpacing = 4 * kShapeSpacing / 3,
|
||||
kPaintSpacing = 3 * kShapeTypeSpacing,
|
||||
kLabelSpacing = 3 * kShapeSize,
|
||||
kMargin = kShapeSpacing / 2,
|
||||
kXfermodeTypeSpacing = kLabelSpacing + 2 * kPaintSpacing + kShapeTypeSpacing,
|
||||
kTitleSpacing = 3 * kShapeSpacing / 4,
|
||||
kSubtitleSpacing = 5 * kShapeSpacing / 8
|
||||
};
|
||||
|
||||
static const SkColor kBGColor = SkColorSetARGB(200, 210, 184, 135);
|
||||
|
||||
static const SkColor kShapeColors[2] = {
|
||||
SkColorSetARGB(130, 255, 0, 128), // input color unknown
|
||||
SkColorSetARGB(255, 0, 255, 255) // input color opaque
|
||||
};
|
||||
|
||||
enum Shape {
|
||||
kSquare_Shape,
|
||||
kDiamond_Shape,
|
||||
kOval_Shape,
|
||||
|
||||
kLast_Shape = kOval_Shape
|
||||
};
|
||||
|
||||
namespace skiagm {
|
||||
|
||||
/**
|
||||
* Verifies AA works properly on all Xfermodes, including arithmetic, with both opaque and unknown
|
||||
* src colors.
|
||||
*/
|
||||
class AAXfermodesGM : public GM {
|
||||
public:
|
||||
AAXfermodesGM() {}
|
||||
|
||||
protected:
|
||||
SkString onShortName() override {
|
||||
return SkString("aaxfermodes");
|
||||
}
|
||||
|
||||
SkISize onISize() override {
|
||||
return SkISize::Make(2 * kMargin + 2 * kXfermodeTypeSpacing -
|
||||
(kXfermodeTypeSpacing - (kLabelSpacing + 2 * kPaintSpacing)),
|
||||
2 * kMargin + kTitleSpacing + kSubtitleSpacing +
|
||||
(1 + SkXfermode::kLastCoeffMode) * kShapeSpacing);
|
||||
}
|
||||
|
||||
void onOnceBeforeDraw() override {
|
||||
fLabelPaint.setAntiAlias(true);
|
||||
sk_tool_utils::set_portable_typeface(&fLabelPaint);
|
||||
fLabelPaint.setTextSize(5 * kShapeSize/8);
|
||||
fLabelPaint.setSubpixelText(true);
|
||||
|
||||
static const SkScalar radius = -1.4f * kShapeSize/2;
|
||||
SkPoint pts[4] = {
|
||||
{-radius, 0},
|
||||
{0, -1.33f * radius},
|
||||
{radius, 0},
|
||||
{0, 1.33f * radius}
|
||||
};
|
||||
fPath.moveTo(pts[0]);
|
||||
fPath.quadTo(pts[1], pts[2]);
|
||||
fPath.quadTo(pts[3], pts[0]);
|
||||
}
|
||||
|
||||
void onDraw(SkCanvas* canvas) override {
|
||||
sk_tool_utils::draw_checkerboard(canvas, 0xffffffff, 0xffc0c0c0, 10);
|
||||
|
||||
canvas->saveLayer(NULL, NULL);
|
||||
canvas->drawColor(kBGColor, SkXfermode::kSrc_Mode);
|
||||
|
||||
canvas->translate(kMargin, kMargin);
|
||||
|
||||
SkPaint titlePaint(fLabelPaint);
|
||||
titlePaint.setTextSize(9 * titlePaint.getTextSize() / 8);
|
||||
titlePaint.setFakeBoldText(true);
|
||||
titlePaint.setTextAlign(SkPaint::kCenter_Align);
|
||||
canvas->drawText("Porter Duff", sizeof("Porter Duff") - 1,
|
||||
kLabelSpacing + 3 * kShapeTypeSpacing,
|
||||
kTitleSpacing / 2 + titlePaint.getTextSize() / 3, titlePaint);
|
||||
canvas->drawText("Advanced", sizeof("Advanced") - 1,
|
||||
kXfermodeTypeSpacing + kLabelSpacing + 3 * kShapeTypeSpacing,
|
||||
kTitleSpacing / 2 + titlePaint.getTextSize() / 3, titlePaint);
|
||||
|
||||
canvas->translate(0, kTitleSpacing);
|
||||
|
||||
for (size_t xfermodeSet = 0; xfermodeSet < 2; xfermodeSet++) {
|
||||
size_t firstMode = (SkXfermode::kLastCoeffMode + 1) * xfermodeSet;
|
||||
canvas->save();
|
||||
|
||||
fLabelPaint.setTextAlign(SkPaint::kCenter_Align);
|
||||
canvas->drawText("Src Unknown", sizeof("Src Unknown") - 1,
|
||||
kLabelSpacing + kShapeSpacing / 2 + kShapeTypeSpacing,
|
||||
kSubtitleSpacing / 2 + fLabelPaint.getTextSize() / 3, fLabelPaint);
|
||||
canvas->drawText("Src Opaque", sizeof("Src Opaque") - 1,
|
||||
kLabelSpacing + kShapeSpacing / 2 + kShapeTypeSpacing + kPaintSpacing,
|
||||
kSubtitleSpacing / 2 + fLabelPaint.getTextSize() / 3, fLabelPaint);
|
||||
|
||||
canvas->translate(0, kSubtitleSpacing + kShapeSpacing/2);
|
||||
|
||||
for (size_t m = 0; m <= SkXfermode::kLastCoeffMode; m++) {
|
||||
SkXfermode::Mode mode = static_cast<SkXfermode::Mode>(firstMode + m);
|
||||
canvas->save();
|
||||
|
||||
this->drawModeName(canvas, mode);
|
||||
canvas->translate(kLabelSpacing + kShapeSpacing/2, 0);
|
||||
|
||||
for (size_t colorIdx = 0; colorIdx < SK_ARRAY_COUNT(kShapeColors); colorIdx++) {
|
||||
SkPaint paint;
|
||||
this->setupShapePaint(canvas, kShapeColors[colorIdx], mode, &paint);
|
||||
SkASSERT(colorIdx == 0 || 255 == paint.getAlpha());
|
||||
canvas->save();
|
||||
|
||||
for (size_t shapeIdx = 0; shapeIdx <= kLast_Shape; shapeIdx++) {
|
||||
this->drawShape(canvas, static_cast<Shape>(shapeIdx), paint, mode);
|
||||
canvas->translate(kShapeTypeSpacing, 0);
|
||||
}
|
||||
|
||||
canvas->restore();
|
||||
canvas->translate(kPaintSpacing, 0);
|
||||
}
|
||||
|
||||
canvas->restore();
|
||||
canvas->translate(0, kShapeSpacing);
|
||||
}
|
||||
|
||||
canvas->restore();
|
||||
canvas->translate(kXfermodeTypeSpacing, 0);
|
||||
}
|
||||
|
||||
canvas->restore();
|
||||
}
|
||||
|
||||
void drawModeName(SkCanvas* canvas, SkXfermode::Mode mode) {
|
||||
const char* modeName = mode <= SkXfermode::kLastMode ? SkXfermode::ModeName(mode)
|
||||
: "Arithmetic";
|
||||
fLabelPaint.setTextAlign(SkPaint::kRight_Align);
|
||||
canvas->drawText(modeName, strlen(modeName), kLabelSpacing - kShapeSize / 4,
|
||||
fLabelPaint.getTextSize() / 3, fLabelPaint);
|
||||
}
|
||||
|
||||
void setupShapePaint(SkCanvas* canvas, GrColor color, SkXfermode::Mode mode, SkPaint* paint) {
|
||||
paint->setColor(color);
|
||||
|
||||
if (mode == SkXfermode::kPlus_Mode) {
|
||||
// Check for overflow, otherwise we might get confusing AA artifacts.
|
||||
int maxSum = SkTMax(SkTMax(SkColorGetA(kBGColor) + SkColorGetA(color),
|
||||
SkColorGetR(kBGColor) + SkColorGetR(color)),
|
||||
SkTMax(SkColorGetG(kBGColor) + SkColorGetG(color),
|
||||
SkColorGetB(kBGColor) + SkColorGetB(color)));
|
||||
|
||||
if (maxSum > 255) {
|
||||
SkPaint dimPaint;
|
||||
dimPaint.setAntiAlias(false);
|
||||
dimPaint.setXfermode(SkXfermode::Create(SkXfermode::kDstIn_Mode));
|
||||
if (255 != paint->getAlpha()) {
|
||||
// Dim the src and dst colors.
|
||||
dimPaint.setARGB(255 * 255 / maxSum, 0, 0, 0);
|
||||
paint->setAlpha(255 * paint->getAlpha() / maxSum);
|
||||
} else {
|
||||
// Just clear the dst, we need to preserve the paint's opacity.
|
||||
dimPaint.setARGB(0, 0, 0, 0);
|
||||
}
|
||||
canvas->drawRectCoords(-kShapeSpacing/2, -kShapeSpacing/2,
|
||||
kShapeSpacing/2 + 2 * kShapeTypeSpacing,
|
||||
kShapeSpacing/2, dimPaint);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void drawShape(SkCanvas* canvas, Shape shape, const SkPaint& paint, SkXfermode::Mode mode) {
|
||||
SkPaint shapePaint(paint);
|
||||
shapePaint.setAntiAlias(kSquare_Shape != shape);
|
||||
|
||||
SkAutoTUnref<SkXfermode> xfermode;
|
||||
if (mode <= SkXfermode::kLastMode) {
|
||||
xfermode.reset(SkXfermode::Create(mode));
|
||||
} else {
|
||||
xfermode.reset(SkArithmeticMode::Create(+1.0f, +0.25f, -0.5f, +0.1f));
|
||||
}
|
||||
shapePaint.setXfermode(xfermode);
|
||||
|
||||
switch (shape) {
|
||||
case kSquare_Shape:
|
||||
canvas->drawRectCoords(-kShapeSize/2, -kShapeSize/2, kShapeSize/2, kShapeSize/2,
|
||||
shapePaint);
|
||||
break;
|
||||
|
||||
case kDiamond_Shape:
|
||||
canvas->save();
|
||||
canvas->rotate(45);
|
||||
canvas->drawRectCoords(-kShapeSize/2, -kShapeSize/2, kShapeSize/2, kShapeSize/2,
|
||||
shapePaint);
|
||||
canvas->restore();
|
||||
break;
|
||||
|
||||
case kOval_Shape:
|
||||
canvas->save();
|
||||
canvas->rotate(static_cast<SkScalar>((511 * mode + 257) % 360));
|
||||
canvas->drawPath(fPath, shapePaint);
|
||||
canvas->restore();
|
||||
break;
|
||||
|
||||
default:
|
||||
SkFAIL("Invalid shape.");
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
SkPaint fLabelPaint;
|
||||
SkPath fPath;
|
||||
|
||||
typedef GM INHERITED;
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static GM* MyFactory(void*) { return new AAXfermodesGM; }
|
||||
static GMRegistry reg(MyFactory);
|
||||
|
||||
}
|
@ -81,7 +81,6 @@
|
||||
'<(skia_src_path)/gpu/GrBatchTarget.h',
|
||||
'<(skia_src_path)/gpu/GrBatchTest.cpp',
|
||||
'<(skia_src_path)/gpu/GrBatchTest.h',
|
||||
'<(skia_src_path)/gpu/GrBlend.cpp',
|
||||
'<(skia_src_path)/gpu/GrBlend.h',
|
||||
'<(skia_src_path)/gpu/GrBlurUtils.cpp',
|
||||
'<(skia_src_path)/gpu/GrBlurUtils.h',
|
||||
|
@ -142,6 +142,8 @@ enum GrColorComponentFlags {
|
||||
kB_GrColorComponentFlag = 1 << (GrColor_SHIFT_B / 8),
|
||||
kA_GrColorComponentFlag = 1 << (GrColor_SHIFT_A / 8),
|
||||
|
||||
kNone_GrColorComponentFlags = 0,
|
||||
|
||||
kRGB_GrColorComponentFlags = (kR_GrColorComponentFlag | kG_GrColorComponentFlag |
|
||||
kB_GrColorComponentFlag),
|
||||
|
||||
@ -149,6 +151,8 @@ enum GrColorComponentFlags {
|
||||
kB_GrColorComponentFlag | kA_GrColorComponentFlag)
|
||||
};
|
||||
|
||||
GR_MAKE_BITFIELD_OPS(GrColorComponentFlags)
|
||||
|
||||
static inline char GrColorComponentFlagToChar(GrColorComponentFlags component) {
|
||||
SkASSERT(SkIsPow2(component));
|
||||
switch (component) {
|
||||
|
@ -14,7 +14,8 @@ struct GrContextOptions {
|
||||
GrContextOptions()
|
||||
: fDrawPathToCompressedTexture(false)
|
||||
, fSuppressPrints(false)
|
||||
, fMaxTextureSizeOverride(SK_MaxS32) {}
|
||||
, fMaxTextureSizeOverride(SK_MaxS32)
|
||||
, fSuppressDualSourceBlending(false) {}
|
||||
|
||||
// EXPERIMENTAL
|
||||
// May be removed in the future, or may become standard depending
|
||||
@ -28,7 +29,8 @@ struct GrContextOptions {
|
||||
overrides can only reduce the feature set or limits, never increase them beyond the
|
||||
detected values. */
|
||||
|
||||
int fMaxTextureSizeOverride;
|
||||
int fMaxTextureSizeOverride;
|
||||
bool fSuppressDualSourceBlending;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -51,10 +51,6 @@ enum GrBlendEquation {
|
||||
|
||||
static const int kGrBlendEquationCnt = kLast_GrBlendEquation + 1;
|
||||
|
||||
inline bool GrBlendEquationIsAdvanced(GrBlendEquation equation) {
|
||||
return equation >= kFirstAdvancedGrBlendEquation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Coeffecients for alpha-blending.
|
||||
*/
|
||||
@ -183,10 +179,6 @@ public:
|
||||
* Clear color stages and override input color to that returned by getOptimizations
|
||||
*/
|
||||
kOverrideColor_OptFlag = 0x8,
|
||||
/**
|
||||
* Set CoverageDrawing_StateBit
|
||||
*/
|
||||
kSetCoverageDrawing_OptFlag = 0x10,
|
||||
/**
|
||||
* Can tweak alpha for coverage. Currently this flag should only be used by a batch
|
||||
*/
|
||||
|
@ -24,7 +24,7 @@ public:
|
||||
GrXPFactory::InvariantOutput*) const override;
|
||||
|
||||
private:
|
||||
GrPorterDuffXPFactory(GrBlendCoeff src, GrBlendCoeff dst);
|
||||
GrPorterDuffXPFactory(SkXfermode::Mode);
|
||||
|
||||
GrXferProcessor* onCreateXferProcessor(const GrCaps& caps,
|
||||
const GrProcOptInfo& colorPOI,
|
||||
@ -37,14 +37,15 @@ private:
|
||||
|
||||
bool onIsEqual(const GrXPFactory& xpfBase) const override {
|
||||
const GrPorterDuffXPFactory& xpf = xpfBase.cast<GrPorterDuffXPFactory>();
|
||||
return (fSrcCoeff == xpf.fSrcCoeff && fDstCoeff == xpf.fDstCoeff);
|
||||
return fXfermode == xpf.fXfermode;
|
||||
}
|
||||
|
||||
GR_DECLARE_XP_FACTORY_TEST;
|
||||
static void TestGetXPOutputTypes(const GrXferProcessor*, int* outPrimary, int* outSecondary);
|
||||
|
||||
GrBlendCoeff fSrcCoeff;
|
||||
GrBlendCoeff fDstCoeff;
|
||||
SkXfermode::Mode fXfermode;
|
||||
|
||||
friend class GrPorterDuffTest; // for TestGetXPOutputTypes()
|
||||
typedef GrXPFactory INHERITED;
|
||||
};
|
||||
|
||||
|
@ -1,154 +0,0 @@
|
||||
|
||||
/*
|
||||
* Copyright 2013 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include "GrBlend.h"
|
||||
|
||||
static inline GrBlendCoeff swap_coeff_src_dst(GrBlendCoeff coeff) {
|
||||
switch (coeff) {
|
||||
case kDC_GrBlendCoeff:
|
||||
return kSC_GrBlendCoeff;
|
||||
case kIDC_GrBlendCoeff:
|
||||
return kISC_GrBlendCoeff;
|
||||
case kDA_GrBlendCoeff:
|
||||
return kSA_GrBlendCoeff;
|
||||
case kIDA_GrBlendCoeff:
|
||||
return kISA_GrBlendCoeff;
|
||||
case kSC_GrBlendCoeff:
|
||||
return kDC_GrBlendCoeff;
|
||||
case kISC_GrBlendCoeff:
|
||||
return kIDC_GrBlendCoeff;
|
||||
case kSA_GrBlendCoeff:
|
||||
return kDA_GrBlendCoeff;
|
||||
case kISA_GrBlendCoeff:
|
||||
return kIDA_GrBlendCoeff;
|
||||
default:
|
||||
return coeff;
|
||||
}
|
||||
}
|
||||
|
||||
static inline unsigned saturated_add(unsigned a, unsigned b) {
|
||||
SkASSERT(a <= 255);
|
||||
SkASSERT(b <= 255);
|
||||
unsigned sum = a + b;
|
||||
if (sum > 255) {
|
||||
sum = 255;
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
static GrColor add_colors(GrColor src, GrColor dst) {
|
||||
unsigned r = saturated_add(GrColorUnpackR(src), GrColorUnpackR(dst));
|
||||
unsigned g = saturated_add(GrColorUnpackG(src), GrColorUnpackG(dst));
|
||||
unsigned b = saturated_add(GrColorUnpackB(src), GrColorUnpackB(dst));
|
||||
unsigned a = saturated_add(GrColorUnpackA(src), GrColorUnpackA(dst));
|
||||
return GrColorPackRGBA(r, g, b, a);
|
||||
}
|
||||
|
||||
static inline bool valid_color(uint32_t compFlags) {
|
||||
return (kRGBA_GrColorComponentFlags & compFlags) == kRGBA_GrColorComponentFlags;
|
||||
}
|
||||
|
||||
static GrColor simplify_blend_term(GrBlendCoeff* srcCoeff,
|
||||
GrColor srcColor, uint32_t srcCompFlags,
|
||||
GrColor dstColor, uint32_t dstCompFlags,
|
||||
GrColor constantColor) {
|
||||
|
||||
SkASSERT(!GrBlendCoeffRefsSrc(*srcCoeff));
|
||||
SkASSERT(srcCoeff);
|
||||
|
||||
// Check whether srcCoeff can be reduced to kOne or kZero based on known color inputs.
|
||||
// We could pick out the coeff r,g,b,a values here and use them to compute the blend term color,
|
||||
// if possible, below but that is not implemented now.
|
||||
switch (*srcCoeff) {
|
||||
case kIDC_GrBlendCoeff:
|
||||
dstColor = ~dstColor; // fallthrough
|
||||
case kDC_GrBlendCoeff:
|
||||
if (valid_color(dstCompFlags)) {
|
||||
if (0xffffffff == dstColor) {
|
||||
*srcCoeff = kOne_GrBlendCoeff;
|
||||
} else if (0 == dstColor) {
|
||||
*srcCoeff = kZero_GrBlendCoeff;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case kIDA_GrBlendCoeff:
|
||||
dstColor = ~dstColor; // fallthrough
|
||||
case kDA_GrBlendCoeff:
|
||||
if (kA_GrColorComponentFlag & dstCompFlags) {
|
||||
if (0xff == GrColorUnpackA(dstColor)) {
|
||||
*srcCoeff = kOne_GrBlendCoeff;
|
||||
} else if (0 == GrColorUnpackA(dstColor)) {
|
||||
*srcCoeff = kZero_GrBlendCoeff;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case kIConstC_GrBlendCoeff:
|
||||
constantColor = ~constantColor; // fallthrough
|
||||
case kConstC_GrBlendCoeff:
|
||||
if (0xffffffff == constantColor) {
|
||||
*srcCoeff = kOne_GrBlendCoeff;
|
||||
} else if (0 == constantColor) {
|
||||
*srcCoeff = kZero_GrBlendCoeff;
|
||||
}
|
||||
break;
|
||||
|
||||
case kIConstA_GrBlendCoeff:
|
||||
constantColor = ~constantColor; // fallthrough
|
||||
case kConstA_GrBlendCoeff:
|
||||
if (0xff == GrColorUnpackA(constantColor)) {
|
||||
*srcCoeff = kOne_GrBlendCoeff;
|
||||
} else if (0 == GrColorUnpackA(constantColor)) {
|
||||
*srcCoeff = kZero_GrBlendCoeff;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
// We may have invalidated these above and shouldn't read them again.
|
||||
SkDEBUGCODE(dstColor = constantColor = GrColor_ILLEGAL;)
|
||||
|
||||
if (kZero_GrBlendCoeff == *srcCoeff || (valid_color(srcCompFlags) && 0 == srcColor)) {
|
||||
*srcCoeff = kZero_GrBlendCoeff;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (kOne_GrBlendCoeff == *srcCoeff && valid_color(srcCompFlags)) {
|
||||
return srcColor;
|
||||
} else {
|
||||
return GrColor_ILLEGAL;
|
||||
}
|
||||
}
|
||||
|
||||
GrColor GrSimplifyBlend(GrBlendCoeff* srcCoeff,
|
||||
GrBlendCoeff* dstCoeff,
|
||||
GrColor srcColor, uint32_t srcCompFlags,
|
||||
GrColor dstColor, uint32_t dstCompFlags,
|
||||
GrColor constantColor) {
|
||||
GrColor srcTermColor = simplify_blend_term(srcCoeff,
|
||||
srcColor, srcCompFlags,
|
||||
dstColor, dstCompFlags,
|
||||
constantColor);
|
||||
|
||||
// We call the same function to simplify the dst blend coeff. We trick it out by swapping the
|
||||
// src and dst.
|
||||
GrBlendCoeff spoofedCoeff = swap_coeff_src_dst(*dstCoeff);
|
||||
GrColor dstTermColor = simplify_blend_term(&spoofedCoeff,
|
||||
dstColor, dstCompFlags,
|
||||
srcColor, srcCompFlags,
|
||||
constantColor);
|
||||
*dstCoeff = swap_coeff_src_dst(spoofedCoeff);
|
||||
|
||||
if (GrColor_ILLEGAL != srcTermColor && GrColor_ILLEGAL != dstTermColor) {
|
||||
return add_colors(srcTermColor, dstTermColor);
|
||||
} else {
|
||||
return GrColor_ILLEGAL;
|
||||
}
|
||||
}
|
@ -7,13 +7,22 @@
|
||||
*/
|
||||
|
||||
#include "GrTypes.h"
|
||||
#include "GrColor.h"
|
||||
#include "SkTLogic.h"
|
||||
#include "GrXferProcessor.h"
|
||||
|
||||
#ifndef GrBlend_DEFINED
|
||||
#define GrBlend_DEFINED
|
||||
|
||||
static inline bool GrBlendCoeffRefsSrc(GrBlendCoeff coeff) {
|
||||
template<GrBlendCoeff Coeff>
|
||||
struct GrTBlendCoeffRefsSrc : SkTBool<kSC_GrBlendCoeff == Coeff ||
|
||||
kISC_GrBlendCoeff == Coeff ||
|
||||
kSA_GrBlendCoeff == Coeff ||
|
||||
kISA_GrBlendCoeff == Coeff> {};
|
||||
|
||||
#define GR_BLEND_COEFF_REFS_SRC(COEFF) \
|
||||
GrTBlendCoeffRefsSrc<COEFF>::value
|
||||
|
||||
inline bool GrBlendCoeffRefsSrc(GrBlendCoeff coeff) {
|
||||
switch (coeff) {
|
||||
case kSC_GrBlendCoeff:
|
||||
case kISC_GrBlendCoeff:
|
||||
@ -25,7 +34,17 @@ static inline bool GrBlendCoeffRefsSrc(GrBlendCoeff coeff) {
|
||||
}
|
||||
}
|
||||
|
||||
static inline bool GrBlendCoeffRefsDst(GrBlendCoeff coeff) {
|
||||
|
||||
template<GrBlendCoeff Coeff>
|
||||
struct GrTBlendCoeffRefsDst : SkTBool<kDC_GrBlendCoeff == Coeff ||
|
||||
kIDC_GrBlendCoeff == Coeff ||
|
||||
kDA_GrBlendCoeff == Coeff ||
|
||||
kIDA_GrBlendCoeff == Coeff> {};
|
||||
|
||||
#define GR_BLEND_COEFF_REFS_DST(COEFF) \
|
||||
GrTBlendCoeffRefsDst<COEFF>::value
|
||||
|
||||
inline bool GrBlendCoeffRefsDst(GrBlendCoeff coeff) {
|
||||
switch (coeff) {
|
||||
case kDC_GrBlendCoeff:
|
||||
case kIDC_GrBlendCoeff:
|
||||
@ -37,10 +56,100 @@ static inline bool GrBlendCoeffRefsDst(GrBlendCoeff coeff) {
|
||||
}
|
||||
}
|
||||
|
||||
GrColor GrSimplifyBlend(GrBlendCoeff* srcCoeff,
|
||||
GrBlendCoeff* dstCoeff,
|
||||
GrColor srcColor, uint32_t srcCompFlags,
|
||||
GrColor dstColor, uint32_t dstCompFlags,
|
||||
GrColor constantColor);
|
||||
|
||||
template<GrBlendCoeff Coeff>
|
||||
struct GrTBlendCoeffRefsSrc2 : SkTBool<kS2C_GrBlendCoeff == Coeff ||
|
||||
kIS2C_GrBlendCoeff == Coeff ||
|
||||
kS2A_GrBlendCoeff == Coeff ||
|
||||
kIS2A_GrBlendCoeff == Coeff> {};
|
||||
|
||||
#define GR_BLEND_COEFF_REFS_SRC2(COEFF) \
|
||||
GrTBlendCoeffRefsSrc2<COEFF>::value
|
||||
|
||||
inline bool GrBlendCoeffRefsSrc2(GrBlendCoeff coeff) {
|
||||
switch (coeff) {
|
||||
case kS2C_GrBlendCoeff:
|
||||
case kIS2C_GrBlendCoeff:
|
||||
case kS2A_GrBlendCoeff:
|
||||
case kIS2A_GrBlendCoeff:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template<GrBlendCoeff SrcCoeff, GrBlendCoeff DstCoeff>
|
||||
struct GrTBlendCoeffsUseSrcColor : SkTBool<kZero_GrBlendCoeff != SrcCoeff ||
|
||||
GR_BLEND_COEFF_REFS_SRC(DstCoeff)> {};
|
||||
|
||||
#define GR_BLEND_COEFFS_USE_SRC_COLOR(SRC_COEFF, DST_COEFF) \
|
||||
GrTBlendCoeffsUseSrcColor<SRC_COEFF, DST_COEFF>::value
|
||||
|
||||
|
||||
template<GrBlendCoeff SrcCoeff, GrBlendCoeff DstCoeff>
|
||||
struct GrTBlendCoeffsUseDstColor : SkTBool<GR_BLEND_COEFF_REFS_DST(SrcCoeff) ||
|
||||
kZero_GrBlendCoeff != DstCoeff> {};
|
||||
|
||||
#define GR_BLEND_COEFFS_USE_DST_COLOR(SRC_COEFF, DST_COEFF) \
|
||||
GrTBlendCoeffsUseDstColor<SRC_COEFF, DST_COEFF>::value
|
||||
|
||||
|
||||
template<GrBlendEquation Equation>
|
||||
struct GrTBlendEquationIsAdvanced : SkTBool<Equation >= kFirstAdvancedGrBlendEquation> {};
|
||||
|
||||
#define GR_BLEND_EQUATION_IS_ADVANCED(EQUATION) \
|
||||
GrTBlendEquationIsAdvanced<EQUATION>::value
|
||||
|
||||
inline bool GrBlendEquationIsAdvanced(GrBlendEquation equation) {
|
||||
return equation >= kFirstAdvancedGrBlendEquation;
|
||||
}
|
||||
|
||||
|
||||
template<GrBlendEquation BlendEquation, GrBlendCoeff SrcCoeff, GrBlendCoeff DstCoeff>
|
||||
struct GrTBlendModifiesDst : SkTBool<(kAdd_GrBlendEquation != BlendEquation &&
|
||||
kReverseSubtract_GrBlendEquation != BlendEquation) ||
|
||||
kZero_GrBlendCoeff != SrcCoeff ||
|
||||
kOne_GrBlendCoeff != DstCoeff> {};
|
||||
|
||||
#define GR_BLEND_MODIFIES_DST(EQUATION, SRC_COEFF, DST_COEFF) \
|
||||
GrTBlendModifiesDst<EQUATION, SRC_COEFF, DST_COEFF>::value
|
||||
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
template<GrBlendEquation Equation, GrBlendCoeff SrcCoeff, GrBlendCoeff DstCoeff>
|
||||
struct GrTBlendCanTweakAlphaForCoverage : SkTBool<GR_BLEND_EQUATION_IS_ADVANCED(Equation) ||
|
||||
((kAdd_GrBlendEquation == Equation ||
|
||||
kReverseSubtract_GrBlendEquation == Equation) &&
|
||||
!GR_BLEND_COEFF_REFS_SRC(SrcCoeff) &&
|
||||
(kOne_GrBlendCoeff == DstCoeff ||
|
||||
kISC_GrBlendCoeff == DstCoeff ||
|
||||
kISA_GrBlendCoeff == DstCoeff))> {};
|
||||
|
||||
#define GR_BLEND_CAN_TWEAK_ALPHA_FOR_COVERAGE(EQUATION, SRC_COEFF, DST_COEFF) \
|
||||
GrTBlendCanTweakAlphaForCoverage<EQUATION, SRC_COEFF, DST_COEFF>::value
|
||||
|
||||
#endif
|
||||
|
@ -73,8 +73,8 @@ SkString GrShaderCaps::dump() const {
|
||||
return r;
|
||||
}
|
||||
|
||||
void GrShaderCaps::applyOptionsOverrides(const GrContextOptions&) {
|
||||
// Currently no overrides apply to shader caps.
|
||||
void GrShaderCaps::applyOptionsOverrides(const GrContextOptions& options) {
|
||||
fDualSourceBlendingSupport = fDualSourceBlendingSupport && !options.fSuppressDualSourceBlending;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
File diff suppressed because it is too large
Load Diff
1011
tests/GrPorterDuffTest.cpp
Normal file
1011
tests/GrPorterDuffTest.cpp
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user