aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar cdalton <cdalton@nvidia.com>2015-05-27 15:08:33 -0700
committerGravatar Commit bot <commit-bot@chromium.org>2015-05-27 15:08:33 -0700
commit6fd158ea47472c4d038e48980a95e36623f840c9 (patch)
tree7f56ce81edc8a63bdc7289caa2db54d57815102a
parent3098a752efba40b0d240061a9cf8a5b6ecd7e547 (diff)
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
-rw-r--r--gm/aaxfermodes.cpp236
-rw-r--r--gyp/gpu.gypi1
-rw-r--r--include/gpu/GrColor.h4
-rw-r--r--include/gpu/GrContextOptions.h6
-rw-r--r--include/gpu/GrXferProcessor.h8
-rw-r--r--include/gpu/effects/GrPorterDuffXferProcessor.h9
-rw-r--r--src/gpu/GrBlend.cpp154
-rw-r--r--src/gpu/GrBlend.h125
-rw-r--r--src/gpu/GrCaps.cpp4
-rw-r--r--src/gpu/effects/GrPorterDuffXferProcessor.cpp1065
-rw-r--r--tests/GrPorterDuffTest.cpp1011
11 files changed, 1929 insertions, 694 deletions
diff --git a/gm/aaxfermodes.cpp b/gm/aaxfermodes.cpp
new file mode 100644
index 0000000000..9970c4fd6d
--- /dev/null
+++ b/gm/aaxfermodes.cpp
@@ -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);
+
+}
diff --git a/gyp/gpu.gypi b/gyp/gpu.gypi
index 8a93bf8264..6a93a7c7d9 100644
--- a/gyp/gpu.gypi
+++ b/gyp/gpu.gypi
@@ -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',
diff --git a/include/gpu/GrColor.h b/include/gpu/GrColor.h
index f5757b93f5..9ccc638fc9 100644
--- a/include/gpu/GrColor.h
+++ b/include/gpu/GrColor.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) {
diff --git a/include/gpu/GrContextOptions.h b/include/gpu/GrContextOptions.h
index 686d6a9ac0..dfb352b507 100644
--- a/include/gpu/GrContextOptions.h
+++ b/include/gpu/GrContextOptions.h
@@ -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
diff --git a/include/gpu/GrXferProcessor.h b/include/gpu/GrXferProcessor.h
index a9faad016c..812a9b32ab 100644
--- a/include/gpu/GrXferProcessor.h
+++ b/include/gpu/GrXferProcessor.h
@@ -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.
*/
@@ -184,10 +180,6 @@ public:
*/
kOverrideColor_OptFlag = 0x8,
/**
- * Set CoverageDrawing_StateBit
- */
- kSetCoverageDrawing_OptFlag = 0x10,
- /**
* Can tweak alpha for coverage. Currently this flag should only be used by a batch
*/
kCanTweakAlphaForCoverage_OptFlag = 0x20,
diff --git a/include/gpu/effects/GrPorterDuffXferProcessor.h b/include/gpu/effects/GrPorterDuffXferProcessor.h
index f8fc1a38cc..e4f72203f6 100644
--- a/include/gpu/effects/GrPorterDuffXferProcessor.h
+++ b/include/gpu/effects/GrPorterDuffXferProcessor.h
@@ -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;
};
diff --git a/src/gpu/GrBlend.cpp b/src/gpu/GrBlend.cpp
deleted file mode 100644
index 52e335e9b0..0000000000
--- a/src/gpu/GrBlend.cpp
+++ /dev/null
@@ -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;
- }
-}
diff --git a/src/gpu/GrBlend.h b/src/gpu/GrBlend.h
index e5b8993377..7021902765 100644
--- a/src/gpu/GrBlend.h
+++ b/src/gpu/GrBlend.h
@@ -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
diff --git a/src/gpu/GrCaps.cpp b/src/gpu/GrCaps.cpp
index 625bd90e8f..f078fad9fb 100644
--- a/src/gpu/GrCaps.cpp
+++ b/src/gpu/GrCaps.cpp
@@ -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;
}
///////////////////////////////////////////////////////////////////////////////
diff --git a/src/gpu/effects/GrPorterDuffXferProcessor.cpp b/src/gpu/effects/GrPorterDuffXferProcessor.cpp
index d1e02d2c2b..812cfeaaa4 100644
--- a/src/gpu/effects/GrPorterDuffXferProcessor.cpp
+++ b/src/gpu/effects/GrPorterDuffXferProcessor.cpp
@@ -17,79 +17,305 @@
#include "gl/builders/GrGLFragmentShaderBuilder.h"
#include "gl/builders/GrGLProgramBuilder.h"
-static bool can_tweak_alpha_for_coverage(GrBlendCoeff dstCoeff) {
- /*
- The fractional coverage is f.
- The src and dst coeffs are Cs and Cd.
- The dst and src colors are S and D.
- We want the blend to compute: f*Cs*S + (f*Cd + (1-f))D. By tweaking the source color's alpha
- we're replacing S with S'=fS. It's obvious that that first term will always be ok. The second
- term can be rearranged as [1-(1-Cd)f]D. By substituting in the various possibilities for Cd we
- find that only 1, ISA, and ISC produce the correct destination when applied to S' and D.
+/**
+ * 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.
*/
- return kOne_GrBlendCoeff == dstCoeff ||
- kISA_GrBlendCoeff == dstCoeff ||
- kISC_GrBlendCoeff == dstCoeff;
-}
+ 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
+ };
-class PorterDuffXferProcessor : public GrXferProcessor {
-public:
- static GrXferProcessor* Create(GrBlendCoeff srcBlend, GrBlendCoeff dstBlend,
- GrColor constant, const DstTexture* dstTexture,
- bool willReadDstColor) {
- return SkNEW_ARGS(PorterDuffXferProcessor, (srcBlend, dstBlend, constant, dstTexture,
- willReadDstColor));
- }
+ enum Properties {
+ kModifiesDst_Property = 1,
+ kUsesDstColor_Property = 1 << 1,
+ kUsesInputColor_Property = 1 << 2,
+ kCanTweakAlphaForCoverage_Property = 1 << 3,
+
+ kLast_Property = kCanTweakAlphaForCoverage_Property
+ };
- ~PorterDuffXferProcessor() override;
+ BlendFormula& operator =(const BlendFormula& other) {
+ fData = other.fData;
+ return *this;
+ }
- const char* name() const override { return "Porter Duff"; }
+ bool operator ==(const BlendFormula& other) const {
+ return fData == other.fData;
+ }
- GrGLXferProcessor* createGLInstance() const override;
+ 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);
+ }
- bool hasSecondaryOutput() const override;
-
- ///////////////////////////////////////////////////////////////////////////
- /// @name Stage Output Types
- ////
-
- enum PrimaryOutputType {
- kNone_PrimaryOutputType,
- kColor_PrimaryOutputType,
- kCoverage_PrimaryOutputType,
- // Modulate color and coverage, write result as the color output.
- kModulate_PrimaryOutputType,
- // Custom Porter-Duff output, used for when we explictly are reading the dst and blending
- // in the shader. Secondary Output must be none if you use this. The custom blend uses the
- // equation: cov * (coeffS * S + coeffD * D) + (1 - cov) * D
- kCustom_PrimaryOutputType
+ /**
+ * 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);
};
- enum SecondaryOutputType {
- // There is no secondary output
- kNone_SecondaryOutputType,
- // Writes coverage as the secondary output. Only set if dual source blending is supported
- // and primary output is kModulate.
- kCoverage_SecondaryOutputType,
- // Writes coverage * (1 - colorA) as the secondary output. Only set if dual source blending
- // is supported and primary output is kModulate.
- kCoverageISA_SecondaryOutputType,
- // Writes coverage * (1 - colorRGBA) as the secondary output. Only set if dual source
- // blending is supported and primary output is kModulate.
- kCoverageISC_SecondaryOutputType,
-
- kSecondaryOutputTypeCnt,
+ 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;
};
- PrimaryOutputType primaryOutputType() const { return fPrimaryOutputType; }
- SecondaryOutputType secondaryOutputType() const { return fSecondaryOutputType; }
+ 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)
- GrBlendCoeff getSrcBlend() const { return fSrcBlend; }
- GrBlendCoeff getDstBlend() const { return fDstBlend; }
+/**
+ * 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(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];
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+class PorterDuffXferProcessor : public GrXferProcessor {
+public:
+ static GrXferProcessor* Create(BlendFormula blendFormula) {
+ return SkNEW_ARGS(PorterDuffXferProcessor, (blendFormula));
+ }
+
+ const char* name() const override { return "Porter Duff"; }
+ bool hasSecondaryOutput() const override { return fBlendFormula.hasSecondaryOutput(); }
+
+ GrGLXferProcessor* createGLInstance() const override;
+
+ BlendFormula getBlendFormula() const { return fBlendFormula; }
private:
- PorterDuffXferProcessor(GrBlendCoeff srcBlend, GrBlendCoeff dstBlend, GrColor constant,
- const DstTexture*, bool willReadDstColor);
+ PorterDuffXferProcessor(BlendFormula blendFormula) : fBlendFormula(blendFormula) {
+ this->initClassID<PorterDuffXferProcessor>();
+ }
GrXferProcessor::OptFlags onGetOptimizations(const GrProcOptInfo& colorPOI,
const GrProcOptInfo& coveragePOI,
@@ -100,204 +326,100 @@ private:
void onGetGLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const override;
void onGetBlendInfo(GrXferProcessor::BlendInfo* blendInfo) const override {
- if (!this->willReadDstColor()) {
- blendInfo->fSrcBlend = fSrcBlend;
- blendInfo->fDstBlend = fDstBlend;
- } else {
- blendInfo->fSrcBlend = kOne_GrBlendCoeff;
- blendInfo->fDstBlend = kZero_GrBlendCoeff;
- }
- blendInfo->fBlendConstant = fBlendConstant;
+ 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>();
- if (fSrcBlend != xp.fSrcBlend ||
- fDstBlend != xp.fDstBlend ||
- fBlendConstant != xp.fBlendConstant ||
- fPrimaryOutputType != xp.fPrimaryOutputType ||
- fSecondaryOutputType != xp.fSecondaryOutputType) {
- return false;
- }
- return true;
+ return fBlendFormula == xp.fBlendFormula;
}
- GrXferProcessor::OptFlags internalGetOptimizations(const GrProcOptInfo& colorPOI,
- const GrProcOptInfo& coveragePOI,
- bool doesStencilWrite);
-
- void calcOutputTypes(GrXferProcessor::OptFlags blendOpts, const GrCaps& caps,
- bool hasSolidCoverage);
-
- GrBlendCoeff fSrcBlend;
- GrBlendCoeff fDstBlend;
- GrColor fBlendConstant;
- PrimaryOutputType fPrimaryOutputType;
- SecondaryOutputType fSecondaryOutputType;
+ const BlendFormula fBlendFormula;
typedef GrXferProcessor INHERITED;
};
///////////////////////////////////////////////////////////////////////////////
-bool append_porterduff_term(GrGLXPFragmentBuilder* fsBuilder, GrBlendCoeff coeff,
- const char* colorName, const char* srcColorName,
- const char* dstColorName, bool hasPrevious) {
- if (kZero_GrBlendCoeff == coeff) {
- return hasPrevious;
- } else {
- if (hasPrevious) {
- fsBuilder->codeAppend(" + ");
- }
- fsBuilder->codeAppendf("%s", colorName);
- switch (coeff) {
- case kOne_GrBlendCoeff:
- break;
- case kSC_GrBlendCoeff:
- fsBuilder->codeAppendf(" * %s", srcColorName);
- break;
- case kISC_GrBlendCoeff:
- fsBuilder->codeAppendf(" * (vec4(1.0) - %s)", srcColorName);
- break;
- case kDC_GrBlendCoeff:
- fsBuilder->codeAppendf(" * %s", dstColorName);
- break;
- case kIDC_GrBlendCoeff:
- fsBuilder->codeAppendf(" * (vec4(1.0) - %s)", dstColorName);
- break;
- case kSA_GrBlendCoeff:
- fsBuilder->codeAppendf(" * %s.a", srcColorName);
- break;
- case kISA_GrBlendCoeff:
- fsBuilder->codeAppendf(" * (1.0 - %s.a)", srcColorName);
- break;
- case kDA_GrBlendCoeff:
- fsBuilder->codeAppendf(" * %s.a", dstColorName);
- break;
- case kIDA_GrBlendCoeff:
- fsBuilder->codeAppendf(" * (1.0 - %s.a)", dstColorName);
- break;
- default:
- SkFAIL("Unsupported Blend Coeff");
- }
- return true;
+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:
+ 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:
- GLPorterDuffXferProcessor(const GrProcessor&) {}
-
- virtual ~GLPorterDuffXferProcessor() {}
-
- static void GenKey(const GrProcessor& processor, const GrGLSLCaps& caps,
- GrProcessorKeyBuilder* b) {
+ static void GenKey(const GrProcessor& processor, GrProcessorKeyBuilder* b) {
const PorterDuffXferProcessor& xp = processor.cast<PorterDuffXferProcessor>();
- b->add32(xp.primaryOutputType());
- b->add32(xp.secondaryOutputType());
- if (xp.willReadDstColor()) {
- b->add32(xp.getSrcBlend());
- b->add32(xp.getDstBlend());
- }
+ 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<PorterDuffXferProcessor>();
GrGLXPFragmentBuilder* fsBuilder = args.fPB->getFragmentShaderBuilder();
- if (PorterDuffXferProcessor::kCustom_PrimaryOutputType != xp.primaryOutputType()) {
- SkASSERT(!xp.willReadDstColor());
- switch(xp.secondaryOutputType()) {
- case PorterDuffXferProcessor::kNone_SecondaryOutputType:
- break;
- case PorterDuffXferProcessor::kCoverage_SecondaryOutputType:
- fsBuilder->codeAppendf("%s = %s;", args.fOutputSecondary,
- args.fInputCoverage);
- break;
- case PorterDuffXferProcessor::kCoverageISA_SecondaryOutputType:
- fsBuilder->codeAppendf("%s = (1.0 - %s.a) * %s;",
- args.fOutputSecondary, args.fInputColor,
- args.fInputCoverage);
- break;
- case PorterDuffXferProcessor::kCoverageISC_SecondaryOutputType:
- fsBuilder->codeAppendf("%s = (vec4(1.0) - %s) * %s;",
- args.fOutputSecondary, args.fInputColor,
- args.fInputCoverage);
- break;
- default:
- SkFAIL("Unexpected Secondary Output");
- }
- switch (xp.primaryOutputType()) {
- case PorterDuffXferProcessor::kNone_PrimaryOutputType:
- fsBuilder->codeAppendf("%s = vec4(0);", args.fOutputPrimary);
- break;
- case PorterDuffXferProcessor::kColor_PrimaryOutputType:
- fsBuilder->codeAppendf("%s = %s;", args.fOutputPrimary, args.fInputColor);
- break;
- case PorterDuffXferProcessor::kCoverage_PrimaryOutputType:
- fsBuilder->codeAppendf("%s = %s;", args.fOutputPrimary, args.fInputCoverage);
- break;
- case PorterDuffXferProcessor::kModulate_PrimaryOutputType:
- fsBuilder->codeAppendf("%s = %s * %s;", args.fOutputPrimary, args.fInputColor,
- args.fInputCoverage);
- break;
- default:
- SkFAIL("Unexpected Primary Output");
- }
- } else {
- SkASSERT(xp.willReadDstColor());
-
- const char* dstColor = fsBuilder->dstColor();
-
- fsBuilder->codeAppend("vec4 colorBlend =");
- // append src blend
- bool didAppend = append_porterduff_term(fsBuilder, xp.getSrcBlend(),
- args.fInputColor, args.fInputColor,
- dstColor, false);
- // append dst blend
- SkAssertResult(append_porterduff_term(fsBuilder, xp.getDstBlend(),
- dstColor, args.fInputColor,
- dstColor, didAppend));
- fsBuilder->codeAppend(";");
-
- fsBuilder->codeAppendf("%s = %s * colorBlend + (vec4(1.0) - %s) * %s;",
- args.fOutputPrimary, args.fInputCoverage, args.fInputCoverage,
- dstColor);
+ 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 {};
+ void onSetData(const GrGLProgramDataManager&, const GrXferProcessor&) override {}
typedef GrGLXferProcessor INHERITED;
};
///////////////////////////////////////////////////////////////////////////////
-PorterDuffXferProcessor::PorterDuffXferProcessor(GrBlendCoeff srcBlend,
- GrBlendCoeff dstBlend,
- GrColor constant,
- const DstTexture* dstTexture,
- bool willReadDstColor)
- : INHERITED(dstTexture, willReadDstColor)
- , fSrcBlend(srcBlend)
- , fDstBlend(dstBlend)
- , fBlendConstant(constant)
- , fPrimaryOutputType(kModulate_PrimaryOutputType)
- , fSecondaryOutputType(kNone_SecondaryOutputType) {
- this->initClassID<PorterDuffXferProcessor>();
-}
-
-PorterDuffXferProcessor::~PorterDuffXferProcessor() {
-}
-
-void PorterDuffXferProcessor::onGetGLProcessorKey(const GrGLSLCaps& caps,
+void PorterDuffXferProcessor::onGetGLProcessorKey(const GrGLSLCaps&,
GrProcessorKeyBuilder* b) const {
- GLPorterDuffXferProcessor::GenKey(*this, caps, b);
+ GLPorterDuffXferProcessor::GenKey(*this, b);
}
GrGLXferProcessor* PorterDuffXferProcessor::createGLInstance() const {
- return SkNEW_ARGS(GLPorterDuffXferProcessor, (*this));
+ return SkNEW(GLPorterDuffXferProcessor);
}
GrXferProcessor::OptFlags
@@ -306,168 +428,167 @@ PorterDuffXferProcessor::onGetOptimizations(const GrProcOptInfo& colorPOI,
bool doesStencilWrite,
GrColor* overrideColor,
const GrCaps& caps) {
- GrXferProcessor::OptFlags optFlags = this->internalGetOptimizations(colorPOI,
- coveragePOI,
- doesStencilWrite);
- this->calcOutputTypes(optFlags, caps, coveragePOI.isSolidWhite());
+ 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;
}
-void PorterDuffXferProcessor::calcOutputTypes(GrXferProcessor::OptFlags optFlags,
- const GrCaps& caps,
- bool hasSolidCoverage) {
- if (this->willReadDstColor()) {
- fPrimaryOutputType = kCustom_PrimaryOutputType;
- return;
+///////////////////////////////////////////////////////////////////////////////
+
+class ShaderPDXferProcessor : public GrXferProcessor {
+public:
+ static GrXferProcessor* Create(SkXfermode::Mode xfermode, const DstTexture* dstTexture) {
+ return SkNEW_ARGS(ShaderPDXferProcessor, (xfermode, dstTexture));
}
- if (optFlags & kIgnoreColor_OptFlag) {
- if (optFlags & kIgnoreCoverage_OptFlag) {
- fPrimaryOutputType = kNone_PrimaryOutputType;
- return;
- } else {
- fPrimaryOutputType = kCoverage_PrimaryOutputType;
- return;
- }
- } else if (optFlags & kIgnoreCoverage_OptFlag) {
- fPrimaryOutputType = kColor_PrimaryOutputType;
- return;
+ const char* name() const override { return "Porter Duff Shader"; }
+ bool hasSecondaryOutput() const override { return false; }
+
+ GrGLXferProcessor* createGLInstance() const override;
+
+ SkXfermode::Mode getXfermode() const { return fXfermode; }
+
+private:
+ ShaderPDXferProcessor(SkXfermode::Mode xfermode, const DstTexture* dstTexture)
+ : INHERITED(dstTexture, true)
+ , fXfermode(xfermode) {
+ this->initClassID<ShaderPDXferProcessor>();
}
- // If we do have coverage determine whether it matters. Dual source blending is expensive so
- // we don't do it if we are doing coverage drawing. If we aren't then We always do dual source
- // blending if we have any effective coverage stages OR the geometry processor doesn't emits
- // solid coverage.
- if (!(optFlags & kSetCoverageDrawing_OptFlag) && !hasSolidCoverage) {
- if (caps.shaderCaps()->dualSourceBlendingSupport()) {
- if (kZero_GrBlendCoeff == fDstBlend) {
- // write the coverage value to second color
- fSecondaryOutputType = kCoverage_SecondaryOutputType;
- fDstBlend = kIS2C_GrBlendCoeff;
- } else if (kSA_GrBlendCoeff == fDstBlend) {
- // SA dst coeff becomes 1-(1-SA)*coverage when dst is partially covered.
- fSecondaryOutputType = kCoverageISA_SecondaryOutputType;
- fDstBlend = kIS2C_GrBlendCoeff;
- } else if (kSC_GrBlendCoeff == fDstBlend) {
- // SA dst coeff becomes 1-(1-SA)*coverage when dst is partially covered.
- fSecondaryOutputType = kCoverageISC_SecondaryOutputType;
- fDstBlend = kIS2C_GrBlendCoeff;
- }
- }
+ GrXferProcessor::OptFlags onGetOptimizations(const GrProcOptInfo&, const GrProcOptInfo&,
+ bool, GrColor*, const GrCaps&) override {
+ return kNone_Opt;
}
-}
-GrXferProcessor::OptFlags
-PorterDuffXferProcessor::internalGetOptimizations(const GrProcOptInfo& colorPOI,
- const GrProcOptInfo& coveragePOI,
- bool doesStencilWrite) {
- if (this->willReadDstColor()) {
- return GrXferProcessor::kNone_Opt;
+ 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;
}
- bool srcAIsOne = colorPOI.isOpaque();
- bool hasCoverage = !coveragePOI.isSolidWhite();
-
- bool dstCoeffIsOne = kOne_GrBlendCoeff == fDstBlend ||
- (kSA_GrBlendCoeff == fDstBlend && srcAIsOne);
- bool dstCoeffIsZero = kZero_GrBlendCoeff == fDstBlend ||
- (kISA_GrBlendCoeff == fDstBlend && srcAIsOne);
-
- // When coeffs are (0,1) there is no reason to draw at all, unless
- // stenciling is enabled. Having color writes disabled is effectively
- // (0,1).
- if ((kZero_GrBlendCoeff == fSrcBlend && dstCoeffIsOne)) {
- if (doesStencilWrite) {
- return GrXferProcessor::kIgnoreColor_OptFlag |
- GrXferProcessor::kSetCoverageDrawing_OptFlag;
- } else {
- fDstBlend = kOne_GrBlendCoeff;
- return GrXferProcessor::kSkipDraw_OptFlag;
+ const SkXfermode::Mode fXfermode;
+
+ typedef GrXferProcessor INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+static 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;
}
+}
- // if we don't have coverage we can check whether the dst
- // has to read at all. If not, we'll disable blending.
- if (!hasCoverage) {
- if (dstCoeffIsZero) {
- if (kOne_GrBlendCoeff == fSrcBlend) {
- // if there is no coverage and coeffs are (1,0) then we
- // won't need to read the dst at all, it gets replaced by src
- fDstBlend = kZero_GrBlendCoeff;
- return GrXferProcessor::kNone_Opt |
- GrXferProcessor::kIgnoreCoverage_OptFlag;
- } else if (kZero_GrBlendCoeff == fSrcBlend) {
- // if the op is "clear" then we don't need to emit a color
- // or blend, just write transparent black into the dst.
- fSrcBlend = kOne_GrBlendCoeff;
- fDstBlend = kZero_GrBlendCoeff;
- return GrXferProcessor::kIgnoreColor_OptFlag |
- GrXferProcessor::kIgnoreCoverage_OptFlag;
- }
- }
- return GrXferProcessor::kIgnoreCoverage_OptFlag;
+class GLShaderPDXferProcessor : public GrGLXferProcessor {
+public:
+ static void GenKey(const GrProcessor& processor, GrProcessorKeyBuilder* b) {
+ const ShaderPDXferProcessor& xp = processor.cast<ShaderPDXferProcessor>();
+ b->add32(xp.getXfermode());
}
- // check whether coverage can be safely rolled into alpha
- // of if we can skip color computation and just emit coverage
- if (can_tweak_alpha_for_coverage(fDstBlend)) {
- if (colorPOI.allStagesMultiplyInput()) {
- return GrXferProcessor::kSetCoverageDrawing_OptFlag |
- GrXferProcessor::kCanTweakAlphaForCoverage_OptFlag;
- } else {
- return GrXferProcessor::kSetCoverageDrawing_OptFlag;
+private:
+ void onEmitCode(const EmitArgs& args) override {
+ const ShaderPDXferProcessor& xp = args.fXP.cast<ShaderPDXferProcessor>();
+ GrGLXPFragmentBuilder* fsBuilder = args.fPB->getFragmentShaderBuilder();
- }
+ 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);
}
- if (dstCoeffIsZero) {
- if (kZero_GrBlendCoeff == fSrcBlend) {
- // the source color is not included in the blend
- // the dst coeff is effectively zero so blend works out to:
- // (c)(0)D + (1-c)D = (1-c)D.
- fDstBlend = kISA_GrBlendCoeff;
- return GrXferProcessor::kIgnoreColor_OptFlag |
- GrXferProcessor::kSetCoverageDrawing_OptFlag;
- } else if (srcAIsOne) {
- // the dst coeff is effectively zero so blend works out to:
- // cS + (c)(0)D + (1-c)D = cS + (1-c)D.
- // If Sa is 1 then we can replace Sa with c
- // and set dst coeff to 1-Sa.
- fDstBlend = kISA_GrBlendCoeff;
- if (colorPOI.allStagesMultiplyInput()) {
- return GrXferProcessor::kSetCoverageDrawing_OptFlag |
- GrXferProcessor::kCanTweakAlphaForCoverage_OptFlag;
- } else {
- return GrXferProcessor::kSetCoverageDrawing_OptFlag;
- }
- }
- } else if (dstCoeffIsOne) {
- // the dst coeff is effectively one so blend works out to:
- // cS + (c)(1)D + (1-c)D = cS + D.
- fDstBlend = kOne_GrBlendCoeff;
- if (colorPOI.allStagesMultiplyInput()) {
- return GrXferProcessor::kSetCoverageDrawing_OptFlag |
- GrXferProcessor::kCanTweakAlphaForCoverage_OptFlag;
- } else {
- return GrXferProcessor::kSetCoverageDrawing_OptFlag;
+ void onSetData(const GrGLProgramDataManager&, const GrXferProcessor&) override {}
- }
- return GrXferProcessor::kSetCoverageDrawing_OptFlag;
- }
+ typedef GrGLXferProcessor INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////////
- return GrXferProcessor::kNone_Opt;
+void ShaderPDXferProcessor::onGetGLProcessorKey(const GrGLSLCaps&,
+ GrProcessorKeyBuilder* b) const {
+ GLShaderPDXferProcessor::GenKey(*this, b);
}
-bool PorterDuffXferProcessor::hasSecondaryOutput() const {
- return kNone_SecondaryOutputType != fSecondaryOutputType;
+GrGLXferProcessor* ShaderPDXferProcessor::createGLInstance() const {
+ return SkNEW(GLShaderPDXferProcessor);
}
///////////////////////////////////////////////////////////////////////////////
class PDLCDXferProcessor : public GrXferProcessor {
public:
- static GrXferProcessor* Create(GrBlendCoeff srcBlend, GrBlendCoeff dstBlend,
- const GrProcOptInfo& colorPOI);
+ static GrXferProcessor* Create(SkXfermode::Mode xfermode, const GrProcOptInfo& colorPOI);
~PDLCDXferProcessor() override;
@@ -541,9 +662,9 @@ PDLCDXferProcessor::PDLCDXferProcessor(GrColor blendConstant, uint8_t alpha)
this->initClassID<PDLCDXferProcessor>();
}
-GrXferProcessor* PDLCDXferProcessor::Create(GrBlendCoeff srcBlend, GrBlendCoeff dstBlend,
+GrXferProcessor* PDLCDXferProcessor::Create(SkXfermode::Mode xfermode,
const GrProcOptInfo& colorPOI) {
- if (kOne_GrBlendCoeff != srcBlend || kISA_GrBlendCoeff != dstBlend) {
+ if (SkXfermode::kSrcOver_Mode != xfermode) {
return NULL;
}
@@ -585,91 +706,40 @@ PDLCDXferProcessor::onGetOptimizations(const GrProcOptInfo& colorPOI,
}
///////////////////////////////////////////////////////////////////////////////
-GrPorterDuffXPFactory::GrPorterDuffXPFactory(GrBlendCoeff src, GrBlendCoeff dst)
- : fSrcCoeff(src), fDstCoeff(dst) {
+
+GrPorterDuffXPFactory::GrPorterDuffXPFactory(SkXfermode::Mode xfermode)
+ : fXfermode(xfermode) {
this->initClassID<GrPorterDuffXPFactory>();
}
-GrXPFactory* GrPorterDuffXPFactory::Create(SkXfermode::Mode mode) {
- switch (mode) {
- case SkXfermode::kClear_Mode: {
- static GrPorterDuffXPFactory gClearPDXPF(kZero_GrBlendCoeff, kZero_GrBlendCoeff);
- return SkRef(&gClearPDXPF);
- break;
- }
- case SkXfermode::kSrc_Mode: {
- static GrPorterDuffXPFactory gSrcPDXPF(kOne_GrBlendCoeff, kZero_GrBlendCoeff);
- return SkRef(&gSrcPDXPF);
- break;
- }
- case SkXfermode::kDst_Mode: {
- static GrPorterDuffXPFactory gDstPDXPF(kZero_GrBlendCoeff, kOne_GrBlendCoeff);
- return SkRef(&gDstPDXPF);
- break;
- }
- case SkXfermode::kSrcOver_Mode: {
- static GrPorterDuffXPFactory gSrcOverPDXPF(kOne_GrBlendCoeff, kISA_GrBlendCoeff);
- return SkRef(&gSrcOverPDXPF);
- break;
- }
- case SkXfermode::kDstOver_Mode: {
- static GrPorterDuffXPFactory gDstOverPDXPF(kIDA_GrBlendCoeff, kOne_GrBlendCoeff);
- return SkRef(&gDstOverPDXPF);
- break;
- }
- case SkXfermode::kSrcIn_Mode: {
- static GrPorterDuffXPFactory gSrcInPDXPF(kDA_GrBlendCoeff, kZero_GrBlendCoeff);
- return SkRef(&gSrcInPDXPF);
- break;
- }
- case SkXfermode::kDstIn_Mode: {
- static GrPorterDuffXPFactory gDstInPDXPF(kZero_GrBlendCoeff, kSA_GrBlendCoeff);
- return SkRef(&gDstInPDXPF);
- break;
- }
- case SkXfermode::kSrcOut_Mode: {
- static GrPorterDuffXPFactory gSrcOutPDXPF(kIDA_GrBlendCoeff, kZero_GrBlendCoeff);
- return SkRef(&gSrcOutPDXPF);
- break;
- }
- case SkXfermode::kDstOut_Mode: {
- static GrPorterDuffXPFactory gDstOutPDXPF(kZero_GrBlendCoeff, kISA_GrBlendCoeff);
- return SkRef(&gDstOutPDXPF);
- break;
- }
- case SkXfermode::kSrcATop_Mode: {
- static GrPorterDuffXPFactory gSrcATopPDXPF(kDA_GrBlendCoeff, kISA_GrBlendCoeff);
- return SkRef(&gSrcATopPDXPF);
- break;
- }
- case SkXfermode::kDstATop_Mode: {
- static GrPorterDuffXPFactory gDstATopPDXPF(kIDA_GrBlendCoeff, kSA_GrBlendCoeff);
- return SkRef(&gDstATopPDXPF);
- break;
- }
- case SkXfermode::kXor_Mode: {
- static GrPorterDuffXPFactory gXorPDXPF(kIDA_GrBlendCoeff, kISA_GrBlendCoeff);
- return SkRef(&gXorPDXPF);
- break;
- }
- case SkXfermode::kPlus_Mode: {
- static GrPorterDuffXPFactory gPlusPDXPF(kOne_GrBlendCoeff, kOne_GrBlendCoeff);
- return SkRef(&gPlusPDXPF);
- break;
- }
- case SkXfermode::kModulate_Mode: {
- static GrPorterDuffXPFactory gModulatePDXPF(kZero_GrBlendCoeff, kSC_GrBlendCoeff);
- return SkRef(&gModulatePDXPF);
- break;
- }
- case SkXfermode::kScreen_Mode: {
- static GrPorterDuffXPFactory gScreenPDXPF(kOne_GrBlendCoeff, kISC_GrBlendCoeff);
- return SkRef(&gScreenPDXPF);
- break;
- }
- default:
- return NULL;
+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*
@@ -678,16 +748,22 @@ GrPorterDuffXPFactory::onCreateXferProcessor(const GrCaps& caps,
const GrProcOptInfo& covPOI,
const DstTexture* dstTexture) const {
if (covPOI.isFourChannelOutput()) {
- return PDLCDXferProcessor::Create(fSrcCoeff, fDstCoeff, colorPOI);
- } else {
- return PorterDuffXferProcessor::Create(fSrcCoeff, fDstCoeff, 0, dstTexture,
- this->willReadDstColor(caps, colorPOI, covPOI));
+ SkASSERT(!dstTexture || !dstTexture->texture());
+ return PDLCDXferProcessor::Create(fXfermode, colorPOI);
+ }
+
+ BlendFormula blendFormula = get_blend_formula(fXfermode, colorPOI, covPOI);
+ if (blendFormula.hasSecondaryOutput() && !caps.shaderCaps()->dualSourceBlendingSupport()) {
+ return ShaderPDXferProcessor::Create(fXfermode, dstTexture);
}
+
+ SkASSERT(!dstTexture || !dstTexture->texture());
+ return PorterDuffXferProcessor::Create(blendFormula);
}
bool GrPorterDuffXPFactory::supportsRGBCoverage(GrColor /*knownColor*/,
uint32_t knownColorFlags) const {
- if (kOne_GrBlendCoeff == fSrcCoeff && kISA_GrBlendCoeff == fDstCoeff &&
+ if (SkXfermode::kSrcOver_Mode == fXfermode &&
kRGBA_GrColorComponentFlags == knownColorFlags) {
return true;
}
@@ -697,103 +773,50 @@ bool GrPorterDuffXPFactory::supportsRGBCoverage(GrColor /*knownColor*/,
void GrPorterDuffXPFactory::getInvariantOutput(const GrProcOptInfo& colorPOI,
const GrProcOptInfo& coveragePOI,
GrXPFactory::InvariantOutput* output) const {
- if (!coveragePOI.isSolidWhite()) {
- output->fWillBlendWithDst = true;
- output->fBlendedColorFlags = 0;
- return;
- }
+ output->fWillBlendWithDst = true;
+ output->fBlendedColorFlags = kNone_GrColorComponentFlags;
- GrBlendCoeff srcCoeff = fSrcCoeff;
- GrBlendCoeff dstCoeff = fDstCoeff;
-
- // TODO: figure out to merge this simplify with other current optimization code paths and
- // eventually remove from GrBlend
- GrSimplifyBlend(&srcCoeff, &dstCoeff, colorPOI.color(), colorPOI.validFlags(),
- 0, 0, 0);
-
- if (GrBlendCoeffRefsDst(srcCoeff)) {
- output->fWillBlendWithDst = true;
- output->fBlendedColorFlags = 0;
+ // The blend table doesn't support LCD coverage, but these formulas never have invariant output.
+ if (coveragePOI.isFourChannelOutput()) {
return;
}
- if (kZero_GrBlendCoeff != dstCoeff) {
- bool srcAIsOne = colorPOI.isOpaque();
- if (kISA_GrBlendCoeff != dstCoeff || !srcAIsOne) {
- output->fWillBlendWithDst = true;
- }
- output->fBlendedColorFlags = 0;
+ const BlendFormula& blendFormula = get_blend_formula(fXfermode, colorPOI, coveragePOI);
+ if (blendFormula.usesDstColor()) {
return;
}
- switch (srcCoeff) {
+ SkASSERT(coveragePOI.isSolidWhite());
+ SkASSERT(kAdd_GrBlendEquation == blendFormula.fBlendEquation);
+
+ output->fWillBlendWithDst = false;
+
+ switch (blendFormula.fSrcCoeff) {
case kZero_GrBlendCoeff:
output->fBlendedColor = 0;
output->fBlendedColorFlags = kRGBA_GrColorComponentFlags;
- break;
+ return;
case kOne_GrBlendCoeff:
output->fBlendedColor = colorPOI.color();
output->fBlendedColorFlags = colorPOI.validFlags();
- break;
-
- // The src coeff should never refer to the src and if it refers to dst then opaque
- // should have been false.
- case kSC_GrBlendCoeff:
- case kISC_GrBlendCoeff:
- case kDC_GrBlendCoeff:
- case kIDC_GrBlendCoeff:
- case kSA_GrBlendCoeff:
- case kISA_GrBlendCoeff:
- case kDA_GrBlendCoeff:
- case kIDA_GrBlendCoeff:
- default:
- SkFAIL("srcCoeff should not refer to src or dst.");
- break;
+ return;
- // TODO: update this once GrPaint actually has a const color.
- case kConstC_GrBlendCoeff:
- case kIConstC_GrBlendCoeff:
- case kConstA_GrBlendCoeff:
- case kIConstA_GrBlendCoeff:
- output->fBlendedColorFlags = 0;
- break;
+ default: return;
}
-
- output->fWillBlendWithDst = false;
}
bool GrPorterDuffXPFactory::willReadDstColor(const GrCaps& caps,
const GrProcOptInfo& colorPOI,
const GrProcOptInfo& coveragePOI) const {
- // We can always blend correctly if we have dual source blending.
- if (caps.shaderCaps()->dualSourceBlendingSupport()) {
- return false;
+ if (coveragePOI.isFourChannelOutput()) {
+ return false; // The LCD XP never does a dst read.
}
- if (can_tweak_alpha_for_coverage(fDstCoeff)) {
- return false;
- }
-
- bool srcAIsOne = colorPOI.isOpaque();
-
- if (kZero_GrBlendCoeff == fDstCoeff) {
- if (kZero_GrBlendCoeff == fSrcCoeff || srcAIsOne) {
- return false;
- }
- }
-
- // Reduces to: coeffS * (Cov*S) + D
- if (kSA_GrBlendCoeff == fDstCoeff && srcAIsOne) {
- return false;
- }
-
- // We can always blend correctly if we have solid coverage.
- if (coveragePOI.isSolidWhite()) {
- return false;
- }
-
- return true;
+ // We fallback on the shader XP when the blend formula would use dual source blending but we
+ // don't have support for it.
+ return !caps.shaderCaps()->dualSourceBlendingSupport() &&
+ get_blend_formula(fXfermode, colorPOI, coveragePOI).hasSecondaryOutput();
}
GR_DEFINE_XP_FACTORY_TEST(GrPorterDuffXPFactory);
@@ -806,3 +829,15 @@ GrXPFactory* GrPorterDuffXPFactory::TestCreate(SkRandom* random,
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;
+}
+
diff --git a/tests/GrPorterDuffTest.cpp b/tests/GrPorterDuffTest.cpp
new file mode 100644
index 0000000000..314671200f
--- /dev/null
+++ b/tests/GrPorterDuffTest.cpp
@@ -0,0 +1,1011 @@
+/*
+ * 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 "SkXfermode.h"
+#include "Test.h"
+
+#if SK_SUPPORT_GPU
+
+#include "GrBatch.h"
+#include "GrContextFactory.h"
+#include "GrContextOptions.h"
+#include "GrGpu.h"
+#include "GrResourceProvider.h"
+#include "GrXferProcessor.h"
+#include "effects/GrPorterDuffXferProcessor.h"
+#include "gl/GrGLCaps.h"
+
+////////////////////////////////////////////////////////////////////////////////
+
+static void test_color_unknown_with_coverage(skiatest::Reporter* reporter, const GrCaps& caps);
+static void test_color_unknown_no_coverage(skiatest::Reporter* reporter, const GrCaps& caps);
+static void test_color_opaque_with_coverage(skiatest::Reporter* reporter, const GrCaps& caps);
+static void test_color_opaque_no_coverage(skiatest::Reporter* reporter, const GrCaps& caps);
+static void test_lcd_coverage(skiatest::Reporter* reporter, const GrCaps& caps);
+static void test_no_dual_source_blending(skiatest::Reporter* reporter);
+
+DEF_GPUTEST(GrPorterDuff, reporter, factory) {
+ GrContext* ctx = factory->get(GrContextFactory::kNull_GLContextType);
+ if (!ctx) {
+ SkFAIL("Failed to create null context.");
+ return;
+ }
+
+ const GrCaps& caps = *ctx->getGpu()->caps();
+ if (!caps.shaderCaps()->dualSourceBlendingSupport()) {
+ SkFAIL("Null context does not support dual source blending.");
+ return;
+ }
+
+ test_color_unknown_with_coverage(reporter, caps);
+ test_color_unknown_no_coverage(reporter, caps);
+ test_color_opaque_with_coverage(reporter, caps);
+ test_color_opaque_no_coverage(reporter, caps);
+ test_lcd_coverage(reporter, caps);
+ test_no_dual_source_blending(reporter);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+#define TEST_ASSERT(...) REPORTER_ASSERT(reporter, __VA_ARGS__)
+
+enum {
+ kNone_OutputType,
+ kCoverage_OutputType,
+ kModulate_OutputType,
+ kISAModulate_OutputType,
+ kISCModulate_OutputType
+};
+
+enum {
+ kNone_Opt = GrXferProcessor::kNone_Opt,
+ kSkipDraw_OptFlag = GrXferProcessor::kSkipDraw_OptFlag,
+ kIgnoreColor_OptFlag = GrXferProcessor::kIgnoreColor_OptFlag,
+ kIgnoreCoverage_OptFlag = GrXferProcessor::kIgnoreCoverage_OptFlag,
+ kCanTweakAlphaForCoverage_OptFlag = GrXferProcessor::kCanTweakAlphaForCoverage_OptFlag
+};
+
+class GrPorterDuffTest {
+public:
+ struct XPInfo {
+ XPInfo(skiatest::Reporter* reporter, SkXfermode::Mode xfermode, const GrCaps& caps,
+ const GrProcOptInfo& colorPOI, const GrProcOptInfo& covPOI) {
+ SkAutoTUnref<GrXPFactory> xpf(GrPorterDuffXPFactory::Create(xfermode));
+ SkAutoTUnref<GrXferProcessor> xp(xpf->createXferProcessor(colorPOI, covPOI, 0, caps));
+ TEST_ASSERT(!xpf->willNeedDstTexture(caps, colorPOI, covPOI));
+ xpf->getInvariantOutput(colorPOI, covPOI, &fInvariantOutput);
+ fOptFlags = xp->getOptimizations(colorPOI, covPOI, false, 0, caps);
+ GetXPOutputTypes(xp, &fPrimaryOutputType, &fSecondaryOutputType);
+ xp->getBlendInfo(&fBlendInfo);
+ TEST_ASSERT(!xp->willReadDstColor());
+ TEST_ASSERT(xp->hasSecondaryOutput() == GrBlendCoeffRefsSrc2(fBlendInfo.fDstBlend));
+ }
+
+ GrXPFactory::InvariantOutput fInvariantOutput;
+ int fOptFlags;
+ int fPrimaryOutputType;
+ int fSecondaryOutputType;
+ GrXferProcessor::BlendInfo fBlendInfo;
+ };
+
+ static void GetXPOutputTypes(const GrXferProcessor* xp, int* outPrimary, int* outSecondary) {
+ GrPorterDuffXPFactory::TestGetXPOutputTypes(xp, outPrimary, outSecondary);
+ }
+};
+
+static void test_color_unknown_with_coverage(skiatest::Reporter* reporter, const GrCaps& caps) {
+ GrProcOptInfo colorPOI, covPOI;
+ colorPOI.calcWithInitialValues(NULL, 0, 0, kNone_GrColorComponentFlags, false);
+ covPOI.calcWithInitialValues(NULL, 0, 0, kNone_GrColorComponentFlags, true);
+
+ SkASSERT(!colorPOI.isOpaque());
+ SkASSERT(!colorPOI.isSolidWhite());
+ SkASSERT(!covPOI.isSolidWhite());
+ SkASSERT(!covPOI.isFourChannelOutput());
+
+ for (int m = 0; m <= SkXfermode::kLastCoeffMode; m++) {
+ SkXfermode::Mode xfermode = static_cast<SkXfermode::Mode>(m);
+ const GrPorterDuffTest::XPInfo xpi(reporter, xfermode, caps, colorPOI, covPOI);
+
+ switch (xfermode) {
+ case SkXfermode::kClear_Mode:
+ TEST_ASSERT(xpi.fInvariantOutput.fWillBlendWithDst);
+ TEST_ASSERT(kNone_GrColorComponentFlags == xpi.fInvariantOutput.fBlendedColorFlags);
+ TEST_ASSERT((kIgnoreColor_OptFlag) == xpi.fOptFlags);
+ TEST_ASSERT(kCoverage_OutputType == xpi.fPrimaryOutputType);
+ TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
+ TEST_ASSERT(kReverseSubtract_GrBlendEquation == xpi.fBlendInfo.fEquation);
+ TEST_ASSERT(kDC_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
+ TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
+ TEST_ASSERT(xpi.fBlendInfo.fWriteColor);
+ break;
+ case SkXfermode::kSrc_Mode:
+ TEST_ASSERT(xpi.fInvariantOutput.fWillBlendWithDst);
+ TEST_ASSERT(kNone_GrColorComponentFlags == xpi.fInvariantOutput.fBlendedColorFlags);
+ TEST_ASSERT((kNone_Opt) == xpi.fOptFlags);
+ TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType);
+ TEST_ASSERT(kCoverage_OutputType == xpi.fSecondaryOutputType);
+ TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
+ TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
+ TEST_ASSERT(kIS2A_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
+ TEST_ASSERT(xpi.fBlendInfo.fWriteColor);
+ break;
+ case SkXfermode::kDst_Mode:
+ TEST_ASSERT(xpi.fInvariantOutput.fWillBlendWithDst);
+ TEST_ASSERT(kNone_GrColorComponentFlags == xpi.fInvariantOutput.fBlendedColorFlags);
+ TEST_ASSERT((kSkipDraw_OptFlag |
+ kIgnoreColor_OptFlag |
+ kIgnoreCoverage_OptFlag |
+ kCanTweakAlphaForCoverage_OptFlag) == xpi.fOptFlags);
+ TEST_ASSERT(kNone_OutputType == xpi.fPrimaryOutputType);
+ TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
+ TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
+ TEST_ASSERT(kZero_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
+ TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
+ TEST_ASSERT(!xpi.fBlendInfo.fWriteColor);
+ break;
+ case SkXfermode::kSrcOver_Mode:
+ TEST_ASSERT(xpi.fInvariantOutput.fWillBlendWithDst);
+ TEST_ASSERT(kNone_GrColorComponentFlags == xpi.fInvariantOutput.fBlendedColorFlags);
+ TEST_ASSERT((kCanTweakAlphaForCoverage_OptFlag) == xpi.fOptFlags);
+ TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType);
+ TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
+ TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
+ TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
+ TEST_ASSERT(kISA_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
+ TEST_ASSERT(xpi.fBlendInfo.fWriteColor);
+ break;
+ case SkXfermode::kDstOver_Mode:
+ TEST_ASSERT(xpi.fInvariantOutput.fWillBlendWithDst);
+ TEST_ASSERT(kNone_GrColorComponentFlags == xpi.fInvariantOutput.fBlendedColorFlags);
+ TEST_ASSERT((kCanTweakAlphaForCoverage_OptFlag) == xpi.fOptFlags);
+ TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType);
+ TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
+ TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
+ TEST_ASSERT(kIDA_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
+ TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
+ TEST_ASSERT(xpi.fBlendInfo.fWriteColor);
+ break;
+ case SkXfermode::kSrcIn_Mode:
+ TEST_ASSERT(xpi.fInvariantOutput.fWillBlendWithDst);
+ TEST_ASSERT(kNone_GrColorComponentFlags == xpi.fInvariantOutput.fBlendedColorFlags);
+ TEST_ASSERT((kNone_Opt) == xpi.fOptFlags);
+ TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType);
+ TEST_ASSERT(kCoverage_OutputType == xpi.fSecondaryOutputType);
+ TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
+ TEST_ASSERT(kDA_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
+ TEST_ASSERT(kIS2A_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
+ TEST_ASSERT(xpi.fBlendInfo.fWriteColor);
+ break;
+ case SkXfermode::kDstIn_Mode:
+ TEST_ASSERT(xpi.fInvariantOutput.fWillBlendWithDst);
+ TEST_ASSERT(kNone_GrColorComponentFlags == xpi.fInvariantOutput.fBlendedColorFlags);
+ TEST_ASSERT((kNone_Opt) == xpi.fOptFlags);
+ TEST_ASSERT(kISAModulate_OutputType == xpi.fPrimaryOutputType);
+ TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
+ TEST_ASSERT(kReverseSubtract_GrBlendEquation == xpi.fBlendInfo.fEquation);
+ TEST_ASSERT(kDC_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
+ TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
+ TEST_ASSERT(xpi.fBlendInfo.fWriteColor);
+ break;
+ case SkXfermode::kSrcOut_Mode:
+ TEST_ASSERT(xpi.fInvariantOutput.fWillBlendWithDst);
+ TEST_ASSERT(kNone_GrColorComponentFlags == xpi.fInvariantOutput.fBlendedColorFlags);
+ TEST_ASSERT((kNone_Opt) == xpi.fOptFlags);
+ TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType);
+ TEST_ASSERT(kCoverage_OutputType == xpi.fSecondaryOutputType);
+ TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
+ TEST_ASSERT(kIDA_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
+ TEST_ASSERT(kIS2A_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
+ TEST_ASSERT(xpi.fBlendInfo.fWriteColor);
+ break;
+ case SkXfermode::kDstOut_Mode:
+ TEST_ASSERT(xpi.fInvariantOutput.fWillBlendWithDst);
+ TEST_ASSERT(kNone_GrColorComponentFlags == xpi.fInvariantOutput.fBlendedColorFlags);
+ TEST_ASSERT((kCanTweakAlphaForCoverage_OptFlag) == xpi.fOptFlags);
+ TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType);
+ TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
+ TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
+ TEST_ASSERT(kZero_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
+ TEST_ASSERT(kISA_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
+ TEST_ASSERT(xpi.fBlendInfo.fWriteColor);
+ break;
+ case SkXfermode::kSrcATop_Mode:
+ TEST_ASSERT(xpi.fInvariantOutput.fWillBlendWithDst);
+ TEST_ASSERT(kNone_GrColorComponentFlags == xpi.fInvariantOutput.fBlendedColorFlags);
+ TEST_ASSERT((kCanTweakAlphaForCoverage_OptFlag) == xpi.fOptFlags);
+ TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType);
+ TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
+ TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
+ TEST_ASSERT(kDA_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
+ TEST_ASSERT(kISA_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
+ TEST_ASSERT(xpi.fBlendInfo.fWriteColor);
+ break;
+ case SkXfermode::kDstATop_Mode:
+ TEST_ASSERT(xpi.fInvariantOutput.fWillBlendWithDst);
+ TEST_ASSERT(kNone_GrColorComponentFlags == xpi.fInvariantOutput.fBlendedColorFlags);
+ TEST_ASSERT((kNone_Opt) == xpi.fOptFlags);
+ TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType);
+ TEST_ASSERT(kISAModulate_OutputType == xpi.fSecondaryOutputType);
+ TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
+ TEST_ASSERT(kIDA_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
+ TEST_ASSERT(kIS2C_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
+ TEST_ASSERT(xpi.fBlendInfo.fWriteColor);
+ break;
+ case SkXfermode::kXor_Mode:
+ TEST_ASSERT(xpi.fInvariantOutput.fWillBlendWithDst);
+ TEST_ASSERT(kNone_GrColorComponentFlags == xpi.fInvariantOutput.fBlendedColorFlags);
+ TEST_ASSERT((kCanTweakAlphaForCoverage_OptFlag) == xpi.fOptFlags);
+ TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType);
+ TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
+ TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
+ TEST_ASSERT(kIDA_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
+ TEST_ASSERT(kISA_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
+ TEST_ASSERT(xpi.fBlendInfo.fWriteColor);
+ break;
+ case SkXfermode::kPlus_Mode:
+ TEST_ASSERT(xpi.fInvariantOutput.fWillBlendWithDst);
+ TEST_ASSERT(kNone_GrColorComponentFlags == xpi.fInvariantOutput.fBlendedColorFlags);
+ TEST_ASSERT((kCanTweakAlphaForCoverage_OptFlag) == xpi.fOptFlags);
+ TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType);
+ TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
+ TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
+ TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
+ TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
+ TEST_ASSERT(xpi.fBlendInfo.fWriteColor);
+ break;
+ case SkXfermode::kModulate_Mode:
+ TEST_ASSERT(xpi.fInvariantOutput.fWillBlendWithDst);
+ TEST_ASSERT(kNone_GrColorComponentFlags == xpi.fInvariantOutput.fBlendedColorFlags);
+ TEST_ASSERT((kNone_Opt) == xpi.fOptFlags);
+ TEST_ASSERT(kISCModulate_OutputType == xpi.fPrimaryOutputType);
+ TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
+ TEST_ASSERT(kReverseSubtract_GrBlendEquation == xpi.fBlendInfo.fEquation);
+ TEST_ASSERT(kDC_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
+ TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
+ TEST_ASSERT(xpi.fBlendInfo.fWriteColor);
+ break;
+ case SkXfermode::kScreen_Mode:
+ TEST_ASSERT(xpi.fInvariantOutput.fWillBlendWithDst);
+ TEST_ASSERT(kNone_GrColorComponentFlags == xpi.fInvariantOutput.fBlendedColorFlags);
+ TEST_ASSERT((kCanTweakAlphaForCoverage_OptFlag) == xpi.fOptFlags);
+ TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType);
+ TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
+ TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
+ TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
+ TEST_ASSERT(kISC_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
+ TEST_ASSERT(xpi.fBlendInfo.fWriteColor);
+ break;
+ default:
+ ERRORF(reporter, "Invalid xfermode.");
+ break;
+ }
+ }
+}
+
+static void test_color_unknown_no_coverage(skiatest::Reporter* reporter, const GrCaps& caps) {
+ GrProcOptInfo colorPOI, covPOI;
+ colorPOI.calcWithInitialValues(NULL, 0, GrColorPackRGBA(229, 0, 154, 0),
+ kR_GrColorComponentFlag | kB_GrColorComponentFlag, false);
+ covPOI.calcWithInitialValues(NULL, 0, GrColorPackA4(255), kRGBA_GrColorComponentFlags, true);
+
+ SkASSERT(!colorPOI.isOpaque());
+ SkASSERT(!colorPOI.isSolidWhite());
+ SkASSERT(covPOI.isSolidWhite());
+ SkASSERT(!covPOI.isFourChannelOutput());
+
+ for (int m = 0; m <= SkXfermode::kLastCoeffMode; m++) {
+ SkXfermode::Mode xfermode = static_cast<SkXfermode::Mode>(m);
+ const GrPorterDuffTest::XPInfo xpi(reporter, xfermode, caps, colorPOI, covPOI);
+
+ switch (xfermode) {
+ case SkXfermode::kClear_Mode:
+ TEST_ASSERT(!xpi.fInvariantOutput.fWillBlendWithDst);
+ TEST_ASSERT(0 == xpi.fInvariantOutput.fBlendedColor);
+ TEST_ASSERT(kRGBA_GrColorComponentFlags == xpi.fInvariantOutput.fBlendedColorFlags);
+ TEST_ASSERT((kIgnoreColor_OptFlag |
+ kIgnoreCoverage_OptFlag) == xpi.fOptFlags);
+ TEST_ASSERT(kNone_OutputType == xpi.fPrimaryOutputType);
+ TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
+ TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
+ TEST_ASSERT(kZero_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
+ TEST_ASSERT(kZero_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
+ TEST_ASSERT(xpi.fBlendInfo.fWriteColor);
+ break;
+ case SkXfermode::kSrc_Mode:
+ TEST_ASSERT(!xpi.fInvariantOutput.fWillBlendWithDst);
+ TEST_ASSERT(229 == GrColorUnpackR(xpi.fInvariantOutput.fBlendedColor));
+ TEST_ASSERT(154 == GrColorUnpackB(xpi.fInvariantOutput.fBlendedColor));
+ TEST_ASSERT((kR_GrColorComponentFlag |
+ kB_GrColorComponentFlag) == xpi.fInvariantOutput.fBlendedColorFlags);
+ TEST_ASSERT((kIgnoreCoverage_OptFlag) == xpi.fOptFlags);
+ TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType);
+ TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
+ TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
+ TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
+ TEST_ASSERT(kZero_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
+ TEST_ASSERT(xpi.fBlendInfo.fWriteColor);
+ break;
+ case SkXfermode::kDst_Mode:
+ TEST_ASSERT(xpi.fInvariantOutput.fWillBlendWithDst);
+ TEST_ASSERT(kNone_GrColorComponentFlags == xpi.fInvariantOutput.fBlendedColorFlags);
+ TEST_ASSERT((kSkipDraw_OptFlag |
+ kIgnoreColor_OptFlag |
+ kIgnoreCoverage_OptFlag |
+ kCanTweakAlphaForCoverage_OptFlag) == xpi.fOptFlags);
+ TEST_ASSERT(kNone_OutputType == xpi.fPrimaryOutputType);
+ TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
+ TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
+ TEST_ASSERT(kZero_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
+ TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
+ TEST_ASSERT(!xpi.fBlendInfo.fWriteColor);
+ break;
+ case SkXfermode::kSrcOver_Mode:
+ TEST_ASSERT(xpi.fInvariantOutput.fWillBlendWithDst);
+ TEST_ASSERT(kNone_GrColorComponentFlags == xpi.fInvariantOutput.fBlendedColorFlags);
+ TEST_ASSERT((kIgnoreCoverage_OptFlag |
+ kCanTweakAlphaForCoverage_OptFlag) == xpi.fOptFlags);
+ TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType);
+ TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
+ TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
+ TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
+ TEST_ASSERT(kISA_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
+ TEST_ASSERT(xpi.fBlendInfo.fWriteColor);
+ break;
+ case SkXfermode::kDstOver_Mode:
+ TEST_ASSERT(xpi.fInvariantOutput.fWillBlendWithDst);
+ TEST_ASSERT(kNone_GrColorComponentFlags == xpi.fInvariantOutput.fBlendedColorFlags);
+ TEST_ASSERT((kIgnoreCoverage_OptFlag |
+ kCanTweakAlphaForCoverage_OptFlag) == xpi.fOptFlags);
+ TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType);
+ TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
+ TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
+ TEST_ASSERT(kIDA_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
+ TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
+ TEST_ASSERT(xpi.fBlendInfo.fWriteColor);
+ break;
+ case SkXfermode::kSrcIn_Mode:
+ TEST_ASSERT(xpi.fInvariantOutput.fWillBlendWithDst);
+ TEST_ASSERT(kNone_GrColorComponentFlags == xpi.fInvariantOutput.fBlendedColorFlags);
+ TEST_ASSERT((kIgnoreCoverage_OptFlag) == xpi.fOptFlags);
+ TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType);
+ TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
+ TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
+ TEST_ASSERT(kDA_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
+ TEST_ASSERT(kZero_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
+ TEST_ASSERT(xpi.fBlendInfo.fWriteColor);
+ break;
+ case SkXfermode::kDstIn_Mode:
+ TEST_ASSERT(xpi.fInvariantOutput.fWillBlendWithDst);
+ TEST_ASSERT(kNone_GrColorComponentFlags == xpi.fInvariantOutput.fBlendedColorFlags);
+ TEST_ASSERT((kIgnoreCoverage_OptFlag) == xpi.fOptFlags);
+ TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType);
+ TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
+ TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
+ TEST_ASSERT(kZero_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
+ TEST_ASSERT(kSA_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
+ TEST_ASSERT(xpi.fBlendInfo.fWriteColor);
+ break;
+ case SkXfermode::kSrcOut_Mode:
+ TEST_ASSERT(xpi.fInvariantOutput.fWillBlendWithDst);
+ TEST_ASSERT(kNone_GrColorComponentFlags == xpi.fInvariantOutput.fBlendedColorFlags);
+ TEST_ASSERT((kIgnoreCoverage_OptFlag) == xpi.fOptFlags);
+ TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType);
+ TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
+ TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
+ TEST_ASSERT(kIDA_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
+ TEST_ASSERT(kZero_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
+ TEST_ASSERT(xpi.fBlendInfo.fWriteColor);
+ break;
+ case SkXfermode::kDstOut_Mode:
+ TEST_ASSERT(xpi.fInvariantOutput.fWillBlendWithDst);
+ TEST_ASSERT(kNone_GrColorComponentFlags == xpi.fInvariantOutput.fBlendedColorFlags);
+ TEST_ASSERT((kIgnoreCoverage_OptFlag |
+ kCanTweakAlphaForCoverage_OptFlag) == xpi.fOptFlags);
+ TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType);
+ TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
+ TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
+ TEST_ASSERT(kZero_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
+ TEST_ASSERT(kISA_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
+ TEST_ASSERT(xpi.fBlendInfo.fWriteColor);
+ break;
+ case SkXfermode::kSrcATop_Mode:
+ TEST_ASSERT(xpi.fInvariantOutput.fWillBlendWithDst);
+ TEST_ASSERT(kNone_GrColorComponentFlags == xpi.fInvariantOutput.fBlendedColorFlags);
+ TEST_ASSERT((kIgnoreCoverage_OptFlag |
+ kCanTweakAlphaForCoverage_OptFlag) == xpi.fOptFlags);
+ TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType);
+ TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
+ TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
+ TEST_ASSERT(kDA_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
+ TEST_ASSERT(kISA_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
+ TEST_ASSERT(xpi.fBlendInfo.fWriteColor);
+ break;
+ case SkXfermode::kDstATop_Mode:
+ TEST_ASSERT(xpi.fInvariantOutput.fWillBlendWithDst);
+ TEST_ASSERT(kNone_GrColorComponentFlags == xpi.fInvariantOutput.fBlendedColorFlags);
+ TEST_ASSERT((kIgnoreCoverage_OptFlag) == xpi.fOptFlags);
+ TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType);
+ TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
+ TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
+ TEST_ASSERT(kIDA_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
+ TEST_ASSERT(kSA_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
+ TEST_ASSERT(xpi.fBlendInfo.fWriteColor);
+ break;
+ case SkXfermode::kXor_Mode:
+ TEST_ASSERT(xpi.fInvariantOutput.fWillBlendWithDst);
+ TEST_ASSERT(kNone_GrColorComponentFlags == xpi.fInvariantOutput.fBlendedColorFlags);
+ TEST_ASSERT((kIgnoreCoverage_OptFlag |
+ kCanTweakAlphaForCoverage_OptFlag) == xpi.fOptFlags);
+ TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType);
+ TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
+ TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
+ TEST_ASSERT(kIDA_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
+ TEST_ASSERT(kISA_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
+ TEST_ASSERT(xpi.fBlendInfo.fWriteColor);
+ break;
+ case SkXfermode::kPlus_Mode:
+ TEST_ASSERT(xpi.fInvariantOutput.fWillBlendWithDst);
+ TEST_ASSERT(kNone_GrColorComponentFlags == xpi.fInvariantOutput.fBlendedColorFlags);
+ TEST_ASSERT((kIgnoreCoverage_OptFlag |
+ kCanTweakAlphaForCoverage_OptFlag) == xpi.fOptFlags);
+ TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType);
+ TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
+ TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
+ TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
+ TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
+ TEST_ASSERT(xpi.fBlendInfo.fWriteColor);
+ break;
+ case SkXfermode::kModulate_Mode:
+ TEST_ASSERT(xpi.fInvariantOutput.fWillBlendWithDst);
+ TEST_ASSERT(kNone_GrColorComponentFlags == xpi.fInvariantOutput.fBlendedColorFlags);
+ TEST_ASSERT((kIgnoreCoverage_OptFlag) == xpi.fOptFlags);
+ TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType);
+ TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
+ TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
+ TEST_ASSERT(kZero_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
+ TEST_ASSERT(kSC_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
+ TEST_ASSERT(xpi.fBlendInfo.fWriteColor);
+ break;
+ case SkXfermode::kScreen_Mode:
+ TEST_ASSERT(xpi.fInvariantOutput.fWillBlendWithDst);
+ TEST_ASSERT(kNone_GrColorComponentFlags == xpi.fInvariantOutput.fBlendedColorFlags);
+ TEST_ASSERT((kIgnoreCoverage_OptFlag |
+ kCanTweakAlphaForCoverage_OptFlag) == xpi.fOptFlags);
+ TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType);
+ TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
+ TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
+ TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
+ TEST_ASSERT(kISC_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
+ TEST_ASSERT(xpi.fBlendInfo.fWriteColor);
+ break;
+ default:
+ ERRORF(reporter, "Invalid xfermode.");
+ break;
+ }
+ }
+}
+
+static void test_color_opaque_with_coverage(skiatest::Reporter* reporter, const GrCaps& caps) {
+ GrProcOptInfo colorPOI, covPOI;
+ colorPOI.calcWithInitialValues(NULL, 0, GrColorPackA4(255), kA_GrColorComponentFlag, false);
+ covPOI.calcWithInitialValues(NULL, 0, 0, kNone_GrColorComponentFlags, true);
+
+ SkASSERT(colorPOI.isOpaque());
+ SkASSERT(!colorPOI.isSolidWhite());
+ SkASSERT(!covPOI.isSolidWhite());
+ SkASSERT(!covPOI.isFourChannelOutput());
+
+ for (int m = 0; m <= SkXfermode::kLastCoeffMode; m++) {
+ SkXfermode::Mode xfermode = static_cast<SkXfermode::Mode>(m);
+ const GrPorterDuffTest::XPInfo xpi(reporter, xfermode, caps, colorPOI, covPOI);
+
+ switch (xfermode) {
+ case SkXfermode::kClear_Mode:
+ TEST_ASSERT(xpi.fInvariantOutput.fWillBlendWithDst);
+ TEST_ASSERT(kNone_GrColorComponentFlags == xpi.fInvariantOutput.fBlendedColorFlags);
+ TEST_ASSERT((kIgnoreColor_OptFlag) == xpi.fOptFlags);
+ TEST_ASSERT(kCoverage_OutputType == xpi.fPrimaryOutputType);
+ TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
+ TEST_ASSERT(kReverseSubtract_GrBlendEquation == xpi.fBlendInfo.fEquation);
+ TEST_ASSERT(kDC_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
+ TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
+ TEST_ASSERT(xpi.fBlendInfo.fWriteColor);
+ break;
+ case SkXfermode::kSrc_Mode:
+ TEST_ASSERT(xpi.fInvariantOutput.fWillBlendWithDst);
+ TEST_ASSERT(kNone_GrColorComponentFlags == xpi.fInvariantOutput.fBlendedColorFlags);
+ TEST_ASSERT((kCanTweakAlphaForCoverage_OptFlag) == xpi.fOptFlags);
+ TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType);
+ TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
+ TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
+ TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
+ TEST_ASSERT(kISA_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
+ TEST_ASSERT(xpi.fBlendInfo.fWriteColor);
+ break;
+ case SkXfermode::kDst_Mode:
+ TEST_ASSERT(xpi.fInvariantOutput.fWillBlendWithDst);
+ TEST_ASSERT(kNone_GrColorComponentFlags == xpi.fInvariantOutput.fBlendedColorFlags);
+ TEST_ASSERT((kSkipDraw_OptFlag |
+ kIgnoreColor_OptFlag |
+ kIgnoreCoverage_OptFlag |
+ kCanTweakAlphaForCoverage_OptFlag) == xpi.fOptFlags);
+ TEST_ASSERT(kNone_OutputType == xpi.fPrimaryOutputType);
+ TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
+ TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
+ TEST_ASSERT(kZero_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
+ TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
+ TEST_ASSERT(!xpi.fBlendInfo.fWriteColor);
+ break;
+ case SkXfermode::kSrcOver_Mode:
+ TEST_ASSERT(xpi.fInvariantOutput.fWillBlendWithDst);
+ TEST_ASSERT(kNone_GrColorComponentFlags == xpi.fInvariantOutput.fBlendedColorFlags);
+ TEST_ASSERT((kCanTweakAlphaForCoverage_OptFlag) == xpi.fOptFlags);
+ TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType);
+ TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
+ TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
+ TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
+ TEST_ASSERT(kISA_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
+ TEST_ASSERT(xpi.fBlendInfo.fWriteColor);
+ break;
+ case SkXfermode::kDstOver_Mode:
+ TEST_ASSERT(xpi.fInvariantOutput.fWillBlendWithDst);
+ TEST_ASSERT(kNone_GrColorComponentFlags == xpi.fInvariantOutput.fBlendedColorFlags);
+ TEST_ASSERT((kCanTweakAlphaForCoverage_OptFlag) == xpi.fOptFlags);
+ TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType);
+ TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
+ TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
+ TEST_ASSERT(kIDA_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
+ TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
+ TEST_ASSERT(xpi.fBlendInfo.fWriteColor);
+ break;
+ case SkXfermode::kSrcIn_Mode:
+ TEST_ASSERT(xpi.fInvariantOutput.fWillBlendWithDst);
+ TEST_ASSERT(kNone_GrColorComponentFlags == xpi.fInvariantOutput.fBlendedColorFlags);
+ TEST_ASSERT((kCanTweakAlphaForCoverage_OptFlag) == xpi.fOptFlags);
+ TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType);
+ TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
+ TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
+ TEST_ASSERT(kDA_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
+ TEST_ASSERT(kISA_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
+ TEST_ASSERT(xpi.fBlendInfo.fWriteColor);
+ break;
+ case SkXfermode::kDstIn_Mode:
+ TEST_ASSERT(xpi.fInvariantOutput.fWillBlendWithDst);
+ TEST_ASSERT(kNone_GrColorComponentFlags == xpi.fInvariantOutput.fBlendedColorFlags);
+ TEST_ASSERT((kSkipDraw_OptFlag |
+ kIgnoreColor_OptFlag |
+ kIgnoreCoverage_OptFlag |
+ kCanTweakAlphaForCoverage_OptFlag) == xpi.fOptFlags);
+ TEST_ASSERT(kNone_OutputType == xpi.fPrimaryOutputType);
+ TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
+ TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
+ TEST_ASSERT(kZero_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
+ TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
+ TEST_ASSERT(!xpi.fBlendInfo.fWriteColor);
+ break;
+ case SkXfermode::kSrcOut_Mode:
+ TEST_ASSERT(xpi.fInvariantOutput.fWillBlendWithDst);
+ TEST_ASSERT(kNone_GrColorComponentFlags == xpi.fInvariantOutput.fBlendedColorFlags);
+ TEST_ASSERT((kCanTweakAlphaForCoverage_OptFlag) == xpi.fOptFlags);
+ TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType);
+ TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
+ TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
+ TEST_ASSERT(kIDA_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
+ TEST_ASSERT(kISA_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
+ TEST_ASSERT(xpi.fBlendInfo.fWriteColor);
+ break;
+ case SkXfermode::kDstOut_Mode:
+ TEST_ASSERT(xpi.fInvariantOutput.fWillBlendWithDst);
+ TEST_ASSERT(kNone_GrColorComponentFlags == xpi.fInvariantOutput.fBlendedColorFlags);
+ TEST_ASSERT((kIgnoreColor_OptFlag) == xpi.fOptFlags);
+ TEST_ASSERT(kCoverage_OutputType == xpi.fPrimaryOutputType);
+ TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
+ TEST_ASSERT(kReverseSubtract_GrBlendEquation == xpi.fBlendInfo.fEquation);
+ TEST_ASSERT(kDC_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
+ TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
+ TEST_ASSERT(xpi.fBlendInfo.fWriteColor);
+ break;
+ case SkXfermode::kSrcATop_Mode:
+ TEST_ASSERT(xpi.fInvariantOutput.fWillBlendWithDst);
+ TEST_ASSERT(kNone_GrColorComponentFlags == xpi.fInvariantOutput.fBlendedColorFlags);
+ TEST_ASSERT((kCanTweakAlphaForCoverage_OptFlag) == xpi.fOptFlags);
+ TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType);
+ TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
+ TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
+ TEST_ASSERT(kDA_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
+ TEST_ASSERT(kISA_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
+ TEST_ASSERT(xpi.fBlendInfo.fWriteColor);
+ break;
+ case SkXfermode::kDstATop_Mode:
+ TEST_ASSERT(xpi.fInvariantOutput.fWillBlendWithDst);
+ TEST_ASSERT(kNone_GrColorComponentFlags == xpi.fInvariantOutput.fBlendedColorFlags);
+ TEST_ASSERT((kCanTweakAlphaForCoverage_OptFlag) == xpi.fOptFlags);
+ TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType);
+ TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
+ TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
+ TEST_ASSERT(kIDA_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
+ TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
+ TEST_ASSERT(xpi.fBlendInfo.fWriteColor);
+ break;
+ case SkXfermode::kXor_Mode:
+ TEST_ASSERT(xpi.fInvariantOutput.fWillBlendWithDst);
+ TEST_ASSERT(kNone_GrColorComponentFlags == xpi.fInvariantOutput.fBlendedColorFlags);
+ TEST_ASSERT((kCanTweakAlphaForCoverage_OptFlag) == xpi.fOptFlags);
+ TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType);
+ TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
+ TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
+ TEST_ASSERT(kIDA_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
+ TEST_ASSERT(kISA_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
+ TEST_ASSERT(xpi.fBlendInfo.fWriteColor);
+ break;
+ case SkXfermode::kPlus_Mode:
+ TEST_ASSERT(xpi.fInvariantOutput.fWillBlendWithDst);
+ TEST_ASSERT(kNone_GrColorComponentFlags == xpi.fInvariantOutput.fBlendedColorFlags);
+ TEST_ASSERT((kCanTweakAlphaForCoverage_OptFlag) == xpi.fOptFlags);
+ TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType);
+ TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
+ TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
+ TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
+ TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
+ TEST_ASSERT(xpi.fBlendInfo.fWriteColor);
+ break;
+ case SkXfermode::kModulate_Mode:
+ TEST_ASSERT(xpi.fInvariantOutput.fWillBlendWithDst);
+ TEST_ASSERT(kNone_GrColorComponentFlags == xpi.fInvariantOutput.fBlendedColorFlags);
+ TEST_ASSERT((kNone_Opt) == xpi.fOptFlags);
+ TEST_ASSERT(kISCModulate_OutputType == xpi.fPrimaryOutputType);
+ TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
+ TEST_ASSERT(kReverseSubtract_GrBlendEquation == xpi.fBlendInfo.fEquation);
+ TEST_ASSERT(kDC_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
+ TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
+ TEST_ASSERT(xpi.fBlendInfo.fWriteColor);
+ break;
+ case SkXfermode::kScreen_Mode:
+ TEST_ASSERT(xpi.fInvariantOutput.fWillBlendWithDst);
+ TEST_ASSERT(kNone_GrColorComponentFlags == xpi.fInvariantOutput.fBlendedColorFlags);
+ TEST_ASSERT((kCanTweakAlphaForCoverage_OptFlag) == xpi.fOptFlags);
+ TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType);
+ TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
+ TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
+ TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
+ TEST_ASSERT(kISC_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
+ TEST_ASSERT(xpi.fBlendInfo.fWriteColor);
+ break;
+ default:
+ ERRORF(reporter, "Invalid xfermode.");
+ break;
+ }
+ }
+}
+
+static void test_color_opaque_no_coverage(skiatest::Reporter* reporter, const GrCaps& caps) {
+ GrProcOptInfo colorPOI, covPOI;
+ colorPOI.calcWithInitialValues(NULL, 0, GrColorPackRGBA(0, 82, 0, 255),
+ kG_GrColorComponentFlag | kA_GrColorComponentFlag, false);
+ covPOI.calcWithInitialValues(NULL, 0, GrColorPackA4(255), kRGBA_GrColorComponentFlags, true);
+
+ SkASSERT(colorPOI.isOpaque());
+ SkASSERT(!colorPOI.isSolidWhite());
+ SkASSERT(covPOI.isSolidWhite());
+ SkASSERT(!covPOI.isFourChannelOutput());
+
+ for (int m = 0; m <= SkXfermode::kLastCoeffMode; m++) {
+ SkXfermode::Mode xfermode = static_cast<SkXfermode::Mode>(m);
+ const GrPorterDuffTest::XPInfo xpi(reporter, xfermode, caps, colorPOI, covPOI);
+
+ switch (xfermode) {
+ case SkXfermode::kClear_Mode:
+ TEST_ASSERT(!xpi.fInvariantOutput.fWillBlendWithDst);
+ TEST_ASSERT(0 == xpi.fInvariantOutput.fBlendedColor);
+ TEST_ASSERT(kRGBA_GrColorComponentFlags == xpi.fInvariantOutput.fBlendedColorFlags);
+ TEST_ASSERT((kIgnoreColor_OptFlag |
+ kIgnoreCoverage_OptFlag) == xpi.fOptFlags);
+ TEST_ASSERT(kNone_OutputType == xpi.fPrimaryOutputType);
+ TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
+ TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
+ TEST_ASSERT(kZero_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
+ TEST_ASSERT(kZero_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
+ TEST_ASSERT(xpi.fBlendInfo.fWriteColor);
+ break;
+ case SkXfermode::kSrc_Mode:
+ TEST_ASSERT(!xpi.fInvariantOutput.fWillBlendWithDst);
+ TEST_ASSERT(82 == GrColorUnpackG(xpi.fInvariantOutput.fBlendedColor));
+ TEST_ASSERT(255 == GrColorUnpackA(xpi.fInvariantOutput.fBlendedColor));
+ TEST_ASSERT((kG_GrColorComponentFlag |
+ kA_GrColorComponentFlag) == xpi.fInvariantOutput.fBlendedColorFlags);
+ TEST_ASSERT((kIgnoreCoverage_OptFlag) == xpi.fOptFlags);
+ TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType);
+ TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
+ TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
+ TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
+ TEST_ASSERT(kZero_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
+ TEST_ASSERT(xpi.fBlendInfo.fWriteColor);
+ break;
+ case SkXfermode::kDst_Mode:
+ TEST_ASSERT(xpi.fInvariantOutput.fWillBlendWithDst);
+ TEST_ASSERT(kNone_GrColorComponentFlags == xpi.fInvariantOutput.fBlendedColorFlags);
+ TEST_ASSERT((kSkipDraw_OptFlag |
+ kIgnoreColor_OptFlag |
+ kIgnoreCoverage_OptFlag |
+ kCanTweakAlphaForCoverage_OptFlag) == xpi.fOptFlags);
+ TEST_ASSERT(kNone_OutputType == xpi.fPrimaryOutputType);
+ TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
+ TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
+ TEST_ASSERT(kZero_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
+ TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
+ TEST_ASSERT(!xpi.fBlendInfo.fWriteColor);
+ break;
+ case SkXfermode::kSrcOver_Mode:
+ TEST_ASSERT(!xpi.fInvariantOutput.fWillBlendWithDst);
+ TEST_ASSERT(82 == GrColorUnpackG(xpi.fInvariantOutput.fBlendedColor));
+ TEST_ASSERT(255 == GrColorUnpackA(xpi.fInvariantOutput.fBlendedColor));
+ TEST_ASSERT((kG_GrColorComponentFlag |
+ kA_GrColorComponentFlag) == xpi.fInvariantOutput.fBlendedColorFlags);
+ TEST_ASSERT((kIgnoreCoverage_OptFlag) == xpi.fOptFlags);
+ TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType);
+ TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
+ TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
+ TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
+ TEST_ASSERT(kZero_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
+ TEST_ASSERT(xpi.fBlendInfo.fWriteColor);
+ break;
+ case SkXfermode::kDstOver_Mode:
+ TEST_ASSERT(xpi.fInvariantOutput.fWillBlendWithDst);
+ TEST_ASSERT(kNone_GrColorComponentFlags == xpi.fInvariantOutput.fBlendedColorFlags);
+ TEST_ASSERT((kIgnoreCoverage_OptFlag |
+ kCanTweakAlphaForCoverage_OptFlag) == xpi.fOptFlags);
+ TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType);
+ TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
+ TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
+ TEST_ASSERT(kIDA_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
+ TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
+ TEST_ASSERT(xpi.fBlendInfo.fWriteColor);
+ break;
+ case SkXfermode::kSrcIn_Mode:
+ TEST_ASSERT(xpi.fInvariantOutput.fWillBlendWithDst);
+ TEST_ASSERT(kNone_GrColorComponentFlags == xpi.fInvariantOutput.fBlendedColorFlags);
+ TEST_ASSERT((kIgnoreCoverage_OptFlag) == xpi.fOptFlags);
+ TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType);
+ TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
+ TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
+ TEST_ASSERT(kDA_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
+ TEST_ASSERT(kZero_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
+ TEST_ASSERT(xpi.fBlendInfo.fWriteColor);
+ break;
+ case SkXfermode::kDstIn_Mode:
+ TEST_ASSERT(xpi.fInvariantOutput.fWillBlendWithDst);
+ TEST_ASSERT(kNone_GrColorComponentFlags == xpi.fInvariantOutput.fBlendedColorFlags);
+ TEST_ASSERT((kSkipDraw_OptFlag |
+ kIgnoreColor_OptFlag |
+ kIgnoreCoverage_OptFlag |
+ kCanTweakAlphaForCoverage_OptFlag) == xpi.fOptFlags);
+ TEST_ASSERT(kNone_OutputType == xpi.fPrimaryOutputType);
+ TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
+ TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
+ TEST_ASSERT(kZero_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
+ TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
+ TEST_ASSERT(!xpi.fBlendInfo.fWriteColor);
+ break;
+ case SkXfermode::kSrcOut_Mode:
+ TEST_ASSERT(xpi.fInvariantOutput.fWillBlendWithDst);
+ TEST_ASSERT(kNone_GrColorComponentFlags == xpi.fInvariantOutput.fBlendedColorFlags);
+ TEST_ASSERT((kIgnoreCoverage_OptFlag) == xpi.fOptFlags);
+ TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType);
+ TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
+ TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
+ TEST_ASSERT(kIDA_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
+ TEST_ASSERT(kZero_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
+ TEST_ASSERT(xpi.fBlendInfo.fWriteColor);
+ break;
+ case SkXfermode::kDstOut_Mode:
+ TEST_ASSERT(!xpi.fInvariantOutput.fWillBlendWithDst);
+ TEST_ASSERT(0 == xpi.fInvariantOutput.fBlendedColor);
+ TEST_ASSERT(kRGBA_GrColorComponentFlags == xpi.fInvariantOutput.fBlendedColorFlags);
+ TEST_ASSERT((kIgnoreColor_OptFlag |
+ kIgnoreCoverage_OptFlag) == xpi.fOptFlags);
+ TEST_ASSERT(kNone_OutputType == xpi.fPrimaryOutputType);
+ TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
+ TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
+ TEST_ASSERT(kZero_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
+ TEST_ASSERT(kZero_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
+ TEST_ASSERT(xpi.fBlendInfo.fWriteColor);
+ break;
+ case SkXfermode::kSrcATop_Mode:
+ TEST_ASSERT(xpi.fInvariantOutput.fWillBlendWithDst);
+ TEST_ASSERT(kNone_GrColorComponentFlags == xpi.fInvariantOutput.fBlendedColorFlags);
+ TEST_ASSERT((kIgnoreCoverage_OptFlag) == xpi.fOptFlags);
+ TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType);
+ TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
+ TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
+ TEST_ASSERT(kDA_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
+ TEST_ASSERT(kZero_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
+ TEST_ASSERT(xpi.fBlendInfo.fWriteColor);
+ break;
+ case SkXfermode::kDstATop_Mode:
+ TEST_ASSERT(xpi.fInvariantOutput.fWillBlendWithDst);
+ TEST_ASSERT(kNone_GrColorComponentFlags == xpi.fInvariantOutput.fBlendedColorFlags);
+ TEST_ASSERT((kIgnoreCoverage_OptFlag |
+ kCanTweakAlphaForCoverage_OptFlag) == xpi.fOptFlags);
+ TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType);
+ TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
+ TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
+ TEST_ASSERT(kIDA_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
+ TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
+ TEST_ASSERT(xpi.fBlendInfo.fWriteColor);
+ break;
+ case SkXfermode::kXor_Mode:
+ TEST_ASSERT(xpi.fInvariantOutput.fWillBlendWithDst);
+ TEST_ASSERT(kNone_GrColorComponentFlags == xpi.fInvariantOutput.fBlendedColorFlags);
+ TEST_ASSERT((kIgnoreCoverage_OptFlag) == xpi.fOptFlags);
+ TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType);
+ TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
+ TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
+ TEST_ASSERT(kIDA_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
+ TEST_ASSERT(kZero_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
+ TEST_ASSERT(xpi.fBlendInfo.fWriteColor);
+ break;
+ case SkXfermode::kPlus_Mode:
+ TEST_ASSERT(xpi.fInvariantOutput.fWillBlendWithDst);
+ TEST_ASSERT(kNone_GrColorComponentFlags == xpi.fInvariantOutput.fBlendedColorFlags);
+ TEST_ASSERT((kIgnoreCoverage_OptFlag |
+ kCanTweakAlphaForCoverage_OptFlag) == xpi.fOptFlags);
+ TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType);
+ TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
+ TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
+ TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
+ TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
+ TEST_ASSERT(xpi.fBlendInfo.fWriteColor);
+ break;
+ case SkXfermode::kModulate_Mode:
+ TEST_ASSERT(xpi.fInvariantOutput.fWillBlendWithDst);
+ TEST_ASSERT(kNone_GrColorComponentFlags == xpi.fInvariantOutput.fBlendedColorFlags);
+ TEST_ASSERT((kIgnoreCoverage_OptFlag) == xpi.fOptFlags);
+ TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType);
+ TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
+ TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
+ TEST_ASSERT(kZero_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
+ TEST_ASSERT(kSC_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
+ TEST_ASSERT(xpi.fBlendInfo.fWriteColor);
+ break;
+ case SkXfermode::kScreen_Mode:
+ TEST_ASSERT(xpi.fInvariantOutput.fWillBlendWithDst);
+ TEST_ASSERT(kNone_GrColorComponentFlags == xpi.fInvariantOutput.fBlendedColorFlags);
+ TEST_ASSERT((kIgnoreCoverage_OptFlag |
+ kCanTweakAlphaForCoverage_OptFlag) == xpi.fOptFlags);
+ TEST_ASSERT(kModulate_OutputType == xpi.fPrimaryOutputType);
+ TEST_ASSERT(kNone_OutputType == xpi.fSecondaryOutputType);
+ TEST_ASSERT(kAdd_GrBlendEquation == xpi.fBlendInfo.fEquation);
+ TEST_ASSERT(kOne_GrBlendCoeff == xpi.fBlendInfo.fSrcBlend);
+ TEST_ASSERT(kISC_GrBlendCoeff == xpi.fBlendInfo.fDstBlend);
+ TEST_ASSERT(xpi.fBlendInfo.fWriteColor);
+ break;
+ default:
+ ERRORF(reporter, "Invalid xfermode.");
+ break;
+ }
+ }
+}
+
+static void test_lcd_coverage(skiatest::Reporter* reporter, const GrCaps& caps) {
+ class : public GrBatch {
+ void getInvariantOutputColor(GrInitInvariantOutput* out) const override {
+ out->setKnownFourComponents(GrColorPackRGBA(123, 45, 67, 221));
+ }
+
+ void getInvariantOutputCoverage(GrInitInvariantOutput* out) const override {
+ out->setUnknownFourComponents();
+ out->setUsingLCDCoverage();
+ }
+
+ const char* name() const override { return "Test LCD Text Batch"; }
+ void initBatchTracker(const GrPipelineInfo&) override {}
+ bool onCombineIfPossible(GrBatch*) override { return false; }
+ void generateGeometry(GrBatchTarget*, const GrPipeline*) override {}
+
+ } testLCDCoverageBatch;
+
+ GrProcOptInfo colorPOI, covPOI;
+ colorPOI.calcColorWithBatch(&testLCDCoverageBatch, NULL, 0);
+ covPOI.calcCoverageWithBatch(&testLCDCoverageBatch, NULL, 0);
+
+ SkASSERT(kRGBA_GrColorComponentFlags == colorPOI.validFlags());
+ SkASSERT(covPOI.isFourChannelOutput());
+
+ SkAutoTUnref<GrXPFactory> xpf(GrPorterDuffXPFactory::Create(SkXfermode::kSrcOver_Mode));
+ TEST_ASSERT(!xpf->willNeedDstTexture(caps, colorPOI, covPOI));
+
+ SkAutoTUnref<GrXferProcessor> xp(xpf->createXferProcessor(colorPOI, covPOI, 0, caps));
+ if (!xp) {
+ ERRORF(reporter, "Failed to create an XP with LCD coverage.");
+ return;
+ }
+
+ GrXPFactory::InvariantOutput invariantOutput;
+ xpf->getInvariantOutput(colorPOI, covPOI, &invariantOutput);
+ TEST_ASSERT(invariantOutput.fWillBlendWithDst);
+ TEST_ASSERT(kNone_GrColorComponentFlags == invariantOutput.fBlendedColorFlags);
+
+ GrColor overrideColor;
+ xp->getOptimizations(colorPOI, covPOI, false, &overrideColor, caps);
+
+ GrXferProcessor::BlendInfo blendInfo;
+ xp->getBlendInfo(&blendInfo);
+ TEST_ASSERT(blendInfo.fWriteColor);
+}
+
+static void test_no_dual_source_blending(skiatest::Reporter* reporter) {
+ GrContextOptions opts;
+ opts.fSuppressDualSourceBlending = true;
+ GrContextFactory factory(opts);
+ factory.get(GrContextFactory::kNull_GLContextType);
+ GrContext* ctx = factory.get(GrContextFactory::kNull_GLContextType);
+ if (!ctx) {
+ SkFAIL("Failed to create null context without ARB_blend_func_extended.");
+ return;
+ }
+
+ const GrCaps& caps = *ctx->getGpu()->caps();
+ if (caps.shaderCaps()->dualSourceBlendingSupport()) {
+ SkFAIL("Null context failed to honor request for no ARB_blend_func_extended.");
+ return;
+ }
+
+ GrBackendTextureDesc fakeDesc;
+ fakeDesc.fConfig = kRGBA_8888_GrPixelConfig;
+ fakeDesc.fWidth = fakeDesc.fHeight = 100;
+ fakeDesc.fTextureHandle = 1;
+ SkAutoTUnref<GrTexture> fakeTexture(ctx->resourceProvider()->wrapBackendTexture(fakeDesc));
+ GrXferProcessor::DstTexture fakeDstTexture;
+ fakeDstTexture.setTexture(fakeTexture);
+
+ static const GrColor testColors[] = {
+ 0,
+ GrColorPackRGBA(0, 82, 0, 255),
+ GrColorPackA4(255)
+ };
+ static const GrColorComponentFlags testColorFlags[] = {
+ kNone_GrColorComponentFlags,
+ kG_GrColorComponentFlag | kA_GrColorComponentFlag,
+ kRGBA_GrColorComponentFlags
+ };
+ GR_STATIC_ASSERT(SK_ARRAY_COUNT(testColors) == SK_ARRAY_COUNT(testColorFlags));
+
+ for (size_t c = 0; c < SK_ARRAY_COUNT(testColors); c++) {
+ GrProcOptInfo colorPOI;
+ colorPOI.calcWithInitialValues(NULL, 0, testColors[c], testColorFlags[c], false);
+ for (int f = 0; f <= 1; f++) {
+ GrProcOptInfo covPOI;
+ if (!f) {
+ covPOI.calcWithInitialValues(NULL, 0, 0, kNone_GrColorComponentFlags, true);
+ } else {
+ covPOI.calcWithInitialValues(NULL, 0, GrColorPackA4(255),
+ kRGBA_GrColorComponentFlags, true);
+ }
+ for (int m = 0; m <= SkXfermode::kLastCoeffMode; m++) {
+ SkXfermode::Mode xfermode = static_cast<SkXfermode::Mode>(m);
+ SkAutoTUnref<GrXPFactory> xpf(GrPorterDuffXPFactory::Create(xfermode));
+ SkAutoTUnref<GrXferProcessor> xp;
+ if (xpf->willNeedDstTexture(caps, colorPOI, covPOI)) {
+ xp.reset(xpf->createXferProcessor(colorPOI, covPOI, &fakeDstTexture, caps));
+ } else {
+ xp.reset(xpf->createXferProcessor(colorPOI, covPOI, NULL, caps));
+ }
+ if (!xp) {
+ ERRORF(reporter, "Failed to create an XP without dual source blending.");
+ return;
+ }
+ TEST_ASSERT(!xp->hasSecondaryOutput());
+ xp->getOptimizations(colorPOI, covPOI, false, 0, caps);
+ TEST_ASSERT(!xp->hasSecondaryOutput());
+ }
+ }
+ }
+}
+
+#endif
+