aboutsummaryrefslogtreecommitdiffhomepage
path: root/gm/aaxfermodes.cpp
diff options
context:
space:
mode:
authorGravatar cdalton <cdalton@nvidia.com>2015-05-22 11:36:57 -0700
committerGravatar Commit bot <commit-bot@chromium.org>2015-05-22 11:36:57 -0700
commit9a70920db22b6309c671f8e5d519bb95570e4414 (patch)
tree7c0d6884d581bd2860c2f4af5627de8dce3159d3 /gm/aaxfermodes.cpp
parent2a97c55ae3b2b62fef2045e839600dc13b481c4c (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. 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: Review URL: https://codereview.chromium.org/1124373002
Diffstat (limited to 'gm/aaxfermodes.cpp')
-rw-r--r--gm/aaxfermodes.cpp191
1 files changed, 191 insertions, 0 deletions
diff --git a/gm/aaxfermodes.cpp b/gm/aaxfermodes.cpp
new file mode 100644
index 0000000000..30b34a4d53
--- /dev/null
+++ b/gm/aaxfermodes.cpp
@@ -0,0 +1,191 @@
+
+/*
+ * 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 = 2 * kShapeSpacing,
+ kPadding = (kPaintSpacing - kShapeSpacing) / 2,
+
+ kPaintWidth = 3*kShapeSpacing + 2*kShapeTypeSpacing + kShapeSize,
+ kPaintPadding = kPaintSpacing - kShapeSize,
+};
+
+static const SkColor kBGColor = SkColorSetARGB(200, 210, 184, 135);
+
+static const SkColor kShapeColors[3] = {
+ SkColorSetARGB(130, 255, 0, 128), // input color unknown
+ SkColorSetARGB(255, 0, 255, 255), // input color opaque
+ SkColorSetARGB(255, 255, 255, 255) // input solid white
+};
+
+enum Shape {
+ kSquare_Shape,
+ kDiamond_Shape,
+ kOval_Shape,
+
+ kLast_Shape = kOval_Shape
+};
+
+namespace skiagm {
+
+static void draw_shape(SkCanvas* canvas, Shape shape, const SkColor color, size_t xfermodeIdx);
+
+/**
+ * Verifies AA works properly on all Xfermodes, including arithmetic, with various color invariants.
+ */
+class AAXfermodesGM : public GM {
+public:
+ AAXfermodesGM() {}
+
+protected:
+ SkString onShortName() override {
+ return SkString("aaxfermodes");
+ }
+
+ SkISize onISize() override {
+ return SkISize::Make(3*kPaintWidth + 2*kPaintPadding + 2*kPadding,
+ (2 + SkXfermode::kLastCoeffMode) * kShapeSpacing + 2*kPadding);
+ }
+
+ 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(kPadding + kShapeSize/2, kPadding + kShapeSpacing + kShapeSize/2);
+
+ for (size_t colorIdx = 0; colorIdx < SK_ARRAY_COUNT(kShapeColors); colorIdx++) {
+ SkColor color = kShapeColors[colorIdx];
+
+ for (size_t shapeIdx = 0; shapeIdx <= kLast_Shape; shapeIdx++) {
+ Shape shape = static_cast<Shape>(shapeIdx);
+ canvas->save();
+
+ for (size_t xfermodeIdx = 0; xfermodeIdx < kXfermodeCount; xfermodeIdx++) {
+ draw_shape(canvas, shape, color, xfermodeIdx);
+
+ if (xfermodeIdx == SkXfermode::kLastCoeffMode) {
+ // New column.
+ canvas->restore();
+ canvas->translate(kShapeSpacing, 0);
+ canvas->save();
+ } else {
+ canvas->translate(0, kShapeSpacing);
+ }
+ }
+
+ canvas->restore();
+
+ if (shape != kLast_Shape) {
+ canvas->translate(kShapeTypeSpacing, 0);
+ } else {
+ canvas->translate(kPaintSpacing, 0);
+ }
+ }
+ }
+
+ canvas->restore();
+
+ SkPaint textPaint;
+ textPaint.setAntiAlias(true);
+ sk_tool_utils::set_portable_typeface(&textPaint);
+ textPaint.setTextAlign(SkPaint::kCenter_Align);
+ textPaint.setFakeBoldText(true);
+ textPaint.setTextSize(21 * kShapeSize/32);
+
+ canvas->translate(kPadding + kPaintWidth/2,
+ kPadding + kShapeSize/2 + textPaint.getTextSize()/4);
+ canvas->drawText("input color unknown", sizeof("input color unknown") - 1, 0, 0, textPaint);
+
+ canvas->translate(kPaintWidth + kPaintPadding, 0);
+ canvas->drawText("input color opaque", sizeof("input color opaque") - 1, 0, 0, textPaint);
+
+ canvas->translate(kPaintWidth + kPaintPadding, 0);
+ canvas->drawText("input solid white", sizeof("input solid white") - 1, 0, 0, textPaint);
+ }
+
+private:
+ typedef GM INHERITED;
+};
+
+static void draw_shape(SkCanvas* canvas, Shape shape, const SkColor color, size_t xfermodeIdx) {
+ SkPaint shapePaint;
+ shapePaint.setAntiAlias(kSquare_Shape != shape);
+ shapePaint.setColor(color);
+
+ SkAutoTUnref<SkXfermode> xfermode;
+ if (xfermodeIdx <= SkXfermode::kLastMode) {
+ SkXfermode::Mode mode = static_cast<SkXfermode::Mode>(xfermodeIdx);
+ xfermode.reset(SkXfermode::Create(mode));
+ } else {
+ xfermode.reset(SkArithmeticMode::Create(+1.0f, +0.25f, -0.5f, +0.1f));
+ }
+ shapePaint.setXfermode(xfermode);
+
+ if (xfermodeIdx == SkXfermode::kPlus_Mode) {
+ // Check for overflow and dim the src and dst colors if we need to, 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.setARGB(255 * 255 / maxSum, 0, 0, 0);
+ dimPaint.setAntiAlias(false);
+ dimPaint.setXfermode(SkXfermode::Create(SkXfermode::kDstIn_Mode));
+ canvas->drawRectCoords(-kShapeSpacing/2, -kShapeSpacing/2,
+ kShapeSpacing/2, kShapeSpacing/2, dimPaint);
+
+ shapePaint.setAlpha(255 * shapePaint.getAlpha() / maxSum);
+ }
+ }
+
+ 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 * xfermodeIdx + 257) % 360));
+ canvas->drawArc(SkRect::MakeLTRB(-kShapeSize/2, -1.4f * kShapeSize/2,
+ kShapeSize/2, 1.4f * kShapeSize/2),
+ 0, 360, true, shapePaint);
+ canvas->restore();
+ break;
+
+ default:
+ SkFAIL("Invalid shape.");
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+static GM* MyFactory(void*) { return new AAXfermodesGM; }
+static GMRegistry reg(MyFactory);
+
+}