Index: tests/BlendA8Test.cpp
diff --git a/tests/BlendA8Test.cpp b/tests/BlendA8Test.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..9940b4d46226471c401080d67cc9b91cb2ee0540
--- /dev/null
+++ b/tests/BlendA8Test.cpp
@@ -0,0 +1,372 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "Test.h"
+#include "SkBitmap.h"
+#include "SkBlitter.h"
+#include "SkBlitRow.h"
+#include "SkColor.h"
+#include "SkColorPriv.h"
+#include "SkColorShader.h"
+#include "SkGradientShader.h"
+#include "SkPaint.h"
+
+#define COLOR_STEP 11
+#define ALPHA_STEP 5
+#define AA_STEP 7
+
+typedef uint8_t PMColorOut;
+
+static bool next_color(SkColor& color) {
+ unsigned a = SkColorGetA(color);
+ unsigned r = SkColorGetR(color) + COLOR_STEP;
+ unsigned g = SkColorGetG(color) + COLOR_STEP;
+ unsigned b = SkColorGetB(color) + COLOR_STEP;
+
+ // Spread the 3 color channels out
+ if (r == COLOR_STEP)
+ r -= COLOR_STEP/3;
+ if (g == COLOR_STEP)
+ g -= COLOR_STEP*2/3;
+
+ if (r > 255) {
+ if (a == 255)
+ return false;
+ r = g = b = 0;
+ a += ALPHA_STEP;
+ a = a <= 255 ? a : 255;
+ }
+ color = SkColorSetARGBInline(a, r, g, b);
+ return true;
+}
+
+static bool next_aa(U8CPU& a) {
+ if (a == 255)
+ return false;
+ a += AA_STEP;
+ a = a <= 255 ? a : 255;
+ return true;
+}
+
+static void skip_test(SkColor& dstIn, SkColor& srcIn) {
+ dstIn = srcIn = 0xffffffff;
+}
+
+static void skip_test(SkColor& dstIn, SkColor& srcIn, U8CPU& aa) {
+ skip_test(dstIn, srcIn);
+ aa = 0xff;
+}
+
+struct BlendOp { virtual PMColorOut operator()(PMColorOut dstIn, SkPMColor srcIn, SkColor srcInOrig, U8CPU aa) = 0; };
+
+enum TestCaseFlags {
+ kOnlyOpaque = 1 << 0, // Only opaque input colors are valid for this function
+ kOnlyOpaqueAA = 1 << 2, // Only opaque AA is valid for this function
+ kOnlyNonOpaqueAA = 1 << 3, // Only non-opaque AA is valid for this function
+};
+
+static bool testCaseValid(unsigned flags, SkPMColor srcIn, U8CPU aa) {
+ if (flags & kOnlyOpaque && SkGetPackedA32(srcIn) != 0xff)
+ return false;
+ if (flags & kOnlyOpaqueAA && aa != 0xff)
+ return false;
+ if (flags & kOnlyNonOpaqueAA && aa == 0xff)
+ return false;
+ return true;
+}
+
+struct TestCase {
+ unsigned flags;
+ BlendOp* func;
+};
+
+struct CompareTestCase {
+ unsigned flags;
+ BlendOp* funcA;
+ BlendOp* funcB;
+};
+
+// If srcAlpha == 0 then dstOut == dstIn
+static void test_src_alpha_0(skiatest::Reporter* reporter, TestCase tests[]) {
+ SkColor srcIn = 0;
+ SkColor dstIn = 0;
+ U8CPU aa = 0;
+ for (int i = 0; tests[i].func; ++i) {
+ do {
+ if (!SkColorGetA(srcIn)) {
+ do {
+ do {
+ SkPMColor srcIn8888 = SkPreMultiplyColor(srcIn);
+ PMColorOut dstIn8 = SkColorGetA(dstIn);
+ PMColorOut dstOut;
+
+ if (!testCaseValid(tests[i].flags, srcIn8888, aa))
+ continue;
+
+ dstOut = (*tests[i].func)(dstIn8, srcIn8888, srcIn, aa);
+ if (dstOut != dstIn8) {
+ reporter->reportFailed("FAILED: dstOut == dstIn8 : srcAlpha == 0 modified target pixels");
+ skip_test(dstIn, srcIn, aa);
+ }
+ } while (next_aa(aa));
+ } while (next_color(dstIn));
+ }
+ } while (next_color(srcIn));
+ }
+}
+
+// If srcAlpha == 0xff then dstOut == srcIn
+static void test_src_alpha_ff(skiatest::Reporter* reporter, TestCase tests[]) {
+ SkColor srcIn = 0;
+ SkColor dstIn = 0;
+ do {
+ if (SkColorGetA(srcIn) == 0xff) {
+ do {
+ SkPMColor srcIn8888 = SkPreMultiplyColor(srcIn);
+ PMColorOut srcIn8 = SkColorGetA(srcIn);
+ PMColorOut dstIn8 = SkColorGetA(dstIn);
+ PMColorOut dstOut;
+
+ for (int i = 0; tests[i].func; ++i) {
+ if (!testCaseValid(tests[i].flags, srcIn8888, 0xff))
+ continue;
+
+ dstOut = (*tests[i].func)(dstIn8, srcIn8888, srcIn, 0xff);
+ if (dstOut != srcIn8) {
+ reporter->reportFailed("dstOut == srcIn8 : srcAlpha == 0xff didn't fully overwrite target pixels");
+ skip_test(dstIn, srcIn);
+ }
+ }
+ } while (next_color(dstIn));
+ }
+ } while (next_color(srcIn));
+}
+
+// If dstAlpha == 0xff then dstOutAlpha == 0xff
+static void test_dst_alpha_ff(skiatest::Reporter* reporter, TestCase tests[]) {
+ SkColor srcIn = 0;
+ SkColor dstIn = 0;
+ U8CPU aa = 0;
+ do {
+ if (SkColorGetA(dstIn) == 0xff) {
+ do {
+ do {
+ SkPMColor srcIn8888 = SkPreMultiplyColor(srcIn);
+ PMColorOut dstIn8 = SkColorGetA(dstIn);
+ PMColorOut dstOut;
+
+ for (int i = 0; tests[i].func; ++i) {
+ if (!testCaseValid(tests[i].flags, srcIn8888, aa))
+ continue;
+
+ dstOut = (*tests[i].func)(dstIn8, srcIn8888, srcIn, aa);
+ if (dstOut != 0xff) {
+ reporter->reportFailed("dstOut == 0xff : dstAlpha == 0xff but became non-opaque from blend");
+ skip_test(dstIn, srcIn, aa);
+ }
+ }
+ } while (next_aa(aa));
+ } while (next_color(srcIn));
+ }
+ } while (next_color(dstIn));
+}
+
+// Test two methods give the same results
+static void test_equal(skiatest::Reporter* reporter, CompareTestCase tests[]) {
+ SkColor srcIn = 0;
+ SkColor dstIn = 0;
+ U8CPU aa = 0;
+ do {
+ do {
+ do {
+ SkPMColor srcIn8888 = SkPreMultiplyColor(srcIn);
+ PMColorOut dstIn8 = SkColorGetA(dstIn);
+ PMColorOut dstOutA, dstOutB;
+
+ for (int i = 0; tests[i].funcA; ++i) {
+ if (!testCaseValid(tests[i].flags, srcIn8888, aa))
+ continue;
+
+ dstOutA = (*tests[i].funcA)(dstIn8, srcIn8888, srcIn, aa);
+ dstOutB = (*tests[i].funcB)(dstIn8, srcIn8888, srcIn, aa);
+ if (dstOutA != dstOutB) {
+ reporter->reportFailed("dstOutA == dstOutB : expected equal outputs but they differ");
+ skip_test(dstIn, srcIn, aa);
+ }
+ }
+ } while (next_aa(aa));
+ } while (next_color(dstIn));
+ } while (next_color(srcIn));
+}
+
+//////////////////////////////////////////////////////////////////////////
+
+// Operation types
+
+struct BitmapOp { virtual void operator()(SkBitmap&, SkPMColor srcIn, SkColor srcInOrig, U8CPU aa) = 0; };
+struct BlitterOp { virtual void operator()(SkBlitter&, U8CPU aa) = 0; };
+
+struct BlitterMaskOp : public BlitterOp {
+ uint8_t image[1];
+ SkMask mask(U8CPU aa) {
+ SkMask mask;
+ image[0] = aa;
+ mask.fImage = image;
+ mask.fBounds = SkIRect::MakeWH(1, 1);
+ mask.fRowBytes = 0;
+ mask.fFormat = SkMask::kA8_Format;
+ return mask;
+ }
+};
+
+// Operation adapters
+
+struct BlendToBitmapA8Op : public BlendOp {
+ explicit BlendToBitmapA8Op(BitmapOp* bitmapOp) : fBitmapOp(bitmapOp) { }
+ virtual ~BlendToBitmapA8Op() { delete fBitmapOp; }
+ BitmapOp* fBitmapOp;
+
+ virtual PMColorOut operator()(PMColorOut dstIn, SkPMColor srcIn, SkColor srcInOrig, U8CPU aa) {
+ SkBitmap bitmap;
+ bitmap.setConfig(SkBitmap::kA8_Config, 4, 4);
+ bitmap.allocPixels();
+ bitmap.getAddr8(0, 0)[0] = dstIn;
+ (*fBitmapOp)(bitmap, srcIn, srcInOrig, aa);
+ SkPMColor out = *bitmap.getAddr8(0, 0);
+ return out;
+ }
+};
+
+struct BitmapToBlitterOpColor : public BitmapOp {
+ explicit BitmapToBlitterOpColor(BlitterOp* blitterOp) : fBlitterOp(blitterOp) { }
+ virtual ~BitmapToBlitterOpColor() { delete fBlitterOp; }
+ BlitterOp* fBlitterOp;
+
+ virtual void operator()(SkBitmap& bitmap, SkPMColor srcIn, SkColor srcInOrig, U8CPU aa) {
+ SkPaint paint;
+ paint.setColor(srcInOrig);
+ SkBlitter* blitter = SkBlitter::Choose(bitmap, SkMatrix::I(), paint);
+ (*fBlitterOp)(*blitter, aa);
+ delete blitter;
+ }
+};
+
+struct BitmapToBlitterOpColorShader : public BitmapOp {
+ explicit BitmapToBlitterOpColorShader(BlitterOp* blitterOp) : fBlitterOp(blitterOp) { }
+ virtual ~BitmapToBlitterOpColorShader() { delete fBlitterOp; }
+ BlitterOp* fBlitterOp;
+
+ virtual void operator()(SkBitmap& bitmap, SkPMColor srcIn, SkColor srcInOrig, U8CPU aa) {
+ SkPaint paint;
+ paint.setShader(new SkColorShader(srcInOrig));
+ SkBlitter* blitter = SkBlitter::Choose(bitmap, SkMatrix::I(), paint);
+ (*fBlitterOp)(*blitter, aa);
+ delete blitter;
+ }
+};
+
+struct BitmapToBlitterOpGradientShader : public BitmapOp {
+ explicit BitmapToBlitterOpGradientShader(BlitterOp* blitterOp) : fBlitterOp(blitterOp) { }
+ virtual ~BitmapToBlitterOpGradientShader() { delete fBlitterOp; }
+ BlitterOp* fBlitterOp;
+
+ virtual void operator()(SkBitmap& bitmap, SkPMColor srcIn, SkColor srcInOrig, U8CPU aa) {
+ SkPaint paint;
+ SkPoint pts[2] = { SkPoint::Make(0, 0), SkPoint::Make(1, 1) };
+ SkColor colors[2] = { srcInOrig, srcInOrig };
+ paint.setShader(SkGradientShader::CreateLinear(pts, colors, NULL, 2, SkShader::kRepeat_TileMode, NULL));
+ SkBlitter* blitter = SkBlitter::Choose(bitmap, SkMatrix::I(), paint);
+ (*fBlitterOp)(*blitter, aa);
+ delete blitter;
+ }
+};
+
+// Test operations
+
+struct A8_Blitter_BlitH : public BlitterOp { virtual void operator()(SkBlitter& blitter, U8CPU aa) { blitter.blitH(0, 0, 1); } };
+struct A8_Blitter_BlitV : public BlitterOp { virtual void operator()(SkBlitter& blitter, U8CPU aa) { blitter.blitV(0, 0, 1, aa); } };
+struct A8_Blitter_BlitAntiH : public BlitterOp { virtual void operator()(SkBlitter& blitter, U8CPU aa) { int16_t runs[] = { 1, 0 }; SkAlpha aaArray[] = { aa }; blitter.blitAntiH(0, 0, aaArray, runs); } };
+struct A8_Blitter_BlitRect : public BlitterOp { virtual void operator()(SkBlitter& blitter, U8CPU aa) { blitter.blitRect(0, 0, 1, 1); } };
+struct A8_Blitter_BlitAntiRect : public BlitterOp { virtual void operator()(SkBlitter& blitter, U8CPU aa) { blitter.blitAntiRect(0, 0, 2, 2, aa, aa); } };
+struct A8_Blitter_BlitMask : public BlitterMaskOp { virtual void operator()(SkBlitter& blitter, U8CPU aa) { blitter.blitMask(mask(aa), SkIRect::MakeWH(1, 1)); } };
+
+static TestCase tests[] = {
+ { kOnlyOpaqueAA, new BlendToBitmapA8Op(new BitmapToBlitterOpColor(new A8_Blitter_BlitH)) },
+ { kOnlyOpaqueAA, new BlendToBitmapA8Op(new BitmapToBlitterOpColor(new A8_Blitter_BlitV())) },
+ { 0, new BlendToBitmapA8Op(new BitmapToBlitterOpColor(new A8_Blitter_BlitAntiH())) },
+ { kOnlyOpaqueAA, new BlendToBitmapA8Op(new BitmapToBlitterOpColor(new A8_Blitter_BlitRect())) },
+ { 0, new BlendToBitmapA8Op(new BitmapToBlitterOpColor(new A8_Blitter_BlitAntiRect())) },
+ { 0, new BlendToBitmapA8Op(new BitmapToBlitterOpColor(new A8_Blitter_BlitMask())) },
+
+ { kOnlyOpaqueAA, new BlendToBitmapA8Op(new BitmapToBlitterOpColorShader(new A8_Blitter_BlitH)) },
+ { kOnlyOpaqueAA, new BlendToBitmapA8Op(new BitmapToBlitterOpColorShader(new A8_Blitter_BlitV())) },
+ { 0, new BlendToBitmapA8Op(new BitmapToBlitterOpColorShader(new A8_Blitter_BlitAntiH())) },
+ { kOnlyOpaqueAA, new BlendToBitmapA8Op(new BitmapToBlitterOpColorShader(new A8_Blitter_BlitRect())) },
+ { 0, new BlendToBitmapA8Op(new BitmapToBlitterOpColorShader(new A8_Blitter_BlitAntiRect())) },
+ { 0, new BlendToBitmapA8Op(new BitmapToBlitterOpColorShader(new A8_Blitter_BlitMask())) },
+
+ { kOnlyOpaqueAA, new BlendToBitmapA8Op(new BitmapToBlitterOpGradientShader(new A8_Blitter_BlitH)) },
+ { kOnlyOpaqueAA, new BlendToBitmapA8Op(new BitmapToBlitterOpGradientShader(new A8_Blitter_BlitV())) },
+ { 0, new BlendToBitmapA8Op(new BitmapToBlitterOpGradientShader(new A8_Blitter_BlitAntiH())) },
+ { kOnlyOpaqueAA, new BlendToBitmapA8Op(new BitmapToBlitterOpGradientShader(new A8_Blitter_BlitRect())) },
+ { 0, new BlendToBitmapA8Op(new BitmapToBlitterOpGradientShader(new A8_Blitter_BlitAntiRect())) },
+ { 0, new BlendToBitmapA8Op(new BitmapToBlitterOpGradientShader(new A8_Blitter_BlitMask())) },
+ { 0, 0 }
+};
+
+static CompareTestCase equal_tests[] = {
+ { 0, new BlendToBitmapA8Op(new BitmapToBlitterOpColor(new A8_Blitter_BlitH())), new BlendToBitmapA8Op(new BitmapToBlitterOpColor(new A8_Blitter_BlitV())) },
+ { 0, new BlendToBitmapA8Op(new BitmapToBlitterOpColor(new A8_Blitter_BlitH())), new BlendToBitmapA8Op(new BitmapToBlitterOpColor(new A8_Blitter_BlitRect())) },
+
+ { kOnlyOpaqueAA, new BlendToBitmapA8Op(new BitmapToBlitterOpColor(new A8_Blitter_BlitH())), new BlendToBitmapA8Op(new BitmapToBlitterOpColor(new A8_Blitter_BlitAntiH())) },
+ { kOnlyOpaqueAA, new BlendToBitmapA8Op(new BitmapToBlitterOpColor(new A8_Blitter_BlitH())), new BlendToBitmapA8Op(new BitmapToBlitterOpColor(new A8_Blitter_BlitAntiRect())) },
+ { kOnlyOpaqueAA, new BlendToBitmapA8Op(new BitmapToBlitterOpColor(new A8_Blitter_BlitH())), new BlendToBitmapA8Op(new BitmapToBlitterOpColor(new A8_Blitter_BlitMask())) },
+
+ { kOnlyOpaqueAA, new BlendToBitmapA8Op(new BitmapToBlitterOpColor(new A8_Blitter_BlitH)), new BlendToBitmapA8Op(new BitmapToBlitterOpColorShader(new A8_Blitter_BlitH)) },
+ { kOnlyOpaqueAA, new BlendToBitmapA8Op(new BitmapToBlitterOpColor(new A8_Blitter_BlitV())), new BlendToBitmapA8Op(new BitmapToBlitterOpColorShader(new A8_Blitter_BlitV())) },
+ { 0, new BlendToBitmapA8Op(new BitmapToBlitterOpColor(new A8_Blitter_BlitAntiH())), new BlendToBitmapA8Op(new BitmapToBlitterOpColorShader(new A8_Blitter_BlitAntiH())) },
+ { kOnlyOpaqueAA, new BlendToBitmapA8Op(new BitmapToBlitterOpColor(new A8_Blitter_BlitRect())), new BlendToBitmapA8Op(new BitmapToBlitterOpColorShader(new A8_Blitter_BlitRect())) },
+ { 0, new BlendToBitmapA8Op(new BitmapToBlitterOpColor(new A8_Blitter_BlitAntiRect())), new BlendToBitmapA8Op(new BitmapToBlitterOpColorShader(new A8_Blitter_BlitAntiRect())) },
+ { 0, new BlendToBitmapA8Op(new BitmapToBlitterOpColor(new A8_Blitter_BlitMask())), new BlendToBitmapA8Op(new BitmapToBlitterOpColorShader(new A8_Blitter_BlitMask())) },
+
+ { kOnlyOpaqueAA, new BlendToBitmapA8Op(new BitmapToBlitterOpColor(new A8_Blitter_BlitH)), new BlendToBitmapA8Op(new BitmapToBlitterOpGradientShader(new A8_Blitter_BlitH)) },
+ { kOnlyOpaqueAA, new BlendToBitmapA8Op(new BitmapToBlitterOpColor(new A8_Blitter_BlitV())), new BlendToBitmapA8Op(new BitmapToBlitterOpGradientShader(new A8_Blitter_BlitV())) },
+ { 0, new BlendToBitmapA8Op(new BitmapToBlitterOpColor(new A8_Blitter_BlitAntiH())), new BlendToBitmapA8Op(new BitmapToBlitterOpGradientShader(new A8_Blitter_BlitAntiH())) },
+ { kOnlyOpaqueAA, new BlendToBitmapA8Op(new BitmapToBlitterOpColor(new A8_Blitter_BlitRect())), new BlendToBitmapA8Op(new BitmapToBlitterOpGradientShader(new A8_Blitter_BlitRect())) },
+ { 0, new BlendToBitmapA8Op(new BitmapToBlitterOpColor(new A8_Blitter_BlitAntiRect())), new BlendToBitmapA8Op(new BitmapToBlitterOpGradientShader(new A8_Blitter_BlitAntiRect())) },
+ { 0, new BlendToBitmapA8Op(new BitmapToBlitterOpColor(new A8_Blitter_BlitMask())), new BlendToBitmapA8Op(new BitmapToBlitterOpGradientShader(new A8_Blitter_BlitMask())) },
+ { 0, 0, 0 }
+};
+
+static void cleanup_tests(TestCase tests[], CompareTestCase compareTests[])
+{
+ for (int i = 0; tests[i].func; ++i)
+ delete tests[i].func;
+ for (int i = 0; compareTests[i].funcA; ++i) {
+ delete compareTests[i].funcA;
+ delete compareTests[i].funcB;
+ }
+}
+
+static void TestA8Blend(skiatest::Reporter* reporter) {
+ SkBlitRow::UsePlatformOptsForFactory(true);
+ test_src_alpha_0(reporter, tests);
+ test_src_alpha_ff(reporter, tests);
+ test_dst_alpha_ff(reporter, tests);
+ test_equal(reporter, equal_tests);
+
+ SkBlitRow::UsePlatformOptsForFactory(false);
+ test_src_alpha_0(reporter, tests);
+ test_src_alpha_ff(reporter, tests);
+ test_dst_alpha_ff(reporter, tests);
+ test_equal(reporter, equal_tests);
+
+ cleanup_tests(tests, equal_tests);
+}
+
+#include "TestClassDef.h"
+DEFINE_TESTCLASS("BlendA8Test", TestBlendA8Class, TestA8Blend)