add first cut at arithmetic mode
git-svn-id: http://skia.googlecode.com/svn/trunk@2849 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/src/effects/SkArithmeticMode.cpp b/src/effects/SkArithmeticMode.cpp
new file mode 100644
index 0000000..8190b39
--- /dev/null
+++ b/src/effects/SkArithmeticMode.cpp
@@ -0,0 +1,177 @@
+#include "SkArithmeticMode.h"
+#include "SkColorPriv.h"
+#include "SkUnPreMultiply.h"
+
+class SkArithmeticMode_scalar : public SkXfermode {
+public:
+ SkArithmeticMode_scalar(SkScalar k1, SkScalar k2, SkScalar k3, SkScalar k4) {
+ fK[0] = k1;
+ fK[1] = k2;
+ fK[2] = k3;
+ fK[3] = k4;
+ }
+
+ virtual void xfer32(SkPMColor dst[], const SkPMColor src[], int count,
+ const SkAlpha aa[]) SK_OVERRIDE;
+ virtual Factory getFactory() SK_OVERRIDE;
+
+ static SkFlattenable* Create(SkFlattenableReadBuffer& buffer) {
+ return NULL;
+ }
+
+private:
+ SkScalar fK[4];
+};
+
+SkFlattenable::Factory SkArithmeticMode_scalar::getFactory() {
+ return Create;
+}
+
+static int pinToByte(int value) {
+ if (value < 0) {
+ value = 0;
+ } else if (value > 255) {
+ value = 255;
+ }
+ return value;
+}
+
+static int arith(SkScalar k1, SkScalar k2, SkScalar k3, SkScalar k4,
+ int src, int dst) {
+ SkScalar result = SkScalarMul(k1, src * dst) +
+ SkScalarMul(k2, src) +
+ SkScalarMul(k3, dst) +
+ k4;
+ int res = SkScalarRoundToInt(result);
+ return pinToByte(res);
+}
+
+static int blend(int src, int dst, int scale) {
+ return dst + ((src - dst) * scale >> 8);
+}
+
+static bool needsUnpremul(int alpha) {
+ return 0 != alpha && 0xFF != alpha;
+}
+
+void SkArithmeticMode_scalar::xfer32(SkPMColor dst[], const SkPMColor src[],
+ int count, const SkAlpha aaCoverage[]) {
+ SkScalar k1 = fK[0] / 255;
+ SkScalar k2 = fK[1];
+ SkScalar k3 = fK[2];
+ SkScalar k4 = fK[3] * 255;
+
+ for (int i = 0; i < count; ++i) {
+ if ((NULL == aaCoverage) || aaCoverage[i]) {
+ SkPMColor sc = src[i];
+ SkPMColor dc = dst[i];
+ int sa = SkGetPackedA32(sc);
+ int da = SkGetPackedA32(dc);
+
+ int srcNeedsUnpremul = needsUnpremul(sa);
+ int dstNeedsUnpremul = needsUnpremul(sa);
+
+ int a, r, g, b;
+
+ if (!srcNeedsUnpremul && !srcNeedsUnpremul) {
+ a = arith(k1, k2, k3, k4, sa, sa);
+ r = arith(k1, k2, k3, k4, SkGetPackedR32(sc), SkGetPackedR32(dc));
+ g = arith(k1, k2, k3, k4, SkGetPackedG32(sc), SkGetPackedG32(dc));
+ b = arith(k1, k2, k3, k4, SkGetPackedB32(sc), SkGetPackedB32(dc));
+ } else {
+ int sr = SkGetPackedR32(sc);
+ int sg = SkGetPackedG32(sc);
+ int sb = SkGetPackedB32(sc);
+ if (srcNeedsUnpremul) {
+ SkUnPreMultiply::Scale scale = SkUnPreMultiply::GetScale(sa);
+ sr = SkUnPreMultiply::ApplyScale(scale, sr);
+ sg = SkUnPreMultiply::ApplyScale(scale, sg);
+ sb = SkUnPreMultiply::ApplyScale(scale, sb);
+ }
+
+ int dr = SkGetPackedR32(dc);
+ int dg = SkGetPackedG32(dc);
+ int db = SkGetPackedB32(dc);
+ if (dstNeedsUnpremul) {
+ SkUnPreMultiply::Scale scale = SkUnPreMultiply::GetScale(da);
+ dr = SkUnPreMultiply::ApplyScale(scale, dr);
+ dg = SkUnPreMultiply::ApplyScale(scale, dg);
+ db = SkUnPreMultiply::ApplyScale(scale, db);
+ }
+
+ a = arith(k1, k2, k3, k4, sa, sa);
+ r = arith(k1, k2, k3, k4, sr, dr);
+ g = arith(k1, k2, k3, k4, sg, dg);
+ b = arith(k1, k2, k3, k4, sb, db);
+ }
+
+ // apply antialias coverage if necessary
+ if (aaCoverage && 0xFF != aaCoverage[i]) {
+ int scale = aaCoverage[i] + (aaCoverage[i] >> 7);
+ a = blend(a, SkGetPackedA32(sc), scale);
+ r = blend(r, SkGetPackedR32(sc), scale);
+ g = blend(g, SkGetPackedG32(sc), scale);
+ b = blend(b, SkGetPackedB32(sc), scale);
+ }
+
+ // turn the result back into premul
+ if (0xFF != a) {
+ int scale = a + (a >> 7);
+ r = SkAlphaMul(r, scale);
+ g = SkAlphaMul(g, scale);
+ b = SkAlphaMul(b, scale);
+ }
+ dst[i] = SkPackARGB32(a, r, g, b);
+ }
+ }
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+
+static bool fitsInBits(SkScalar x, int bits) {
+#ifdef SK_SCALAR_IS_FIXED
+ x = SkAbs32(x);
+ x += 1 << 7;
+ x >>= 8;
+ return x < (1 << (bits - 1));
+#else
+ return SkScalarAbs(x) < (1 << (bits - 1));
+#endif
+}
+
+static int32_t toDot8(SkScalar x) {
+#ifdef SK_SCALAR_IS_FIXED
+ x += 1 << 7;
+ x >>= 8;
+ return x;
+#else
+ return (int32_t)(x * 256);
+#endif
+}
+
+SkXfermode* SkArithmeticMode::Create(SkScalar k1, SkScalar k2,
+ SkScalar k3, SkScalar k4) {
+ if (fitsInBits(k1, 8) && fitsInBits(k2, 16) &&
+ fitsInBits(k2, 16) && fitsInBits(k2, 24)) {
+
+ int32_t i1 = toDot8(k1);
+ int32_t i2 = toDot8(k2);
+ int32_t i3 = toDot8(k3);
+ int32_t i4 = toDot8(k4);
+#if 0
+ if (i1) {
+ return SkNEW_ARGS(SkArithmeticMode_quad, (i1, i2, i3, i4));
+ }
+ if (0 == i2) {
+ return SkNEW_ARGS(SkArithmeticMode_dst, (i3, i4));
+ }
+ if (0 == i3) {
+ return SkNEW_ARGS(SkArithmeticMode_src, (i2, i4));
+ }
+ return SkNEW_ARGS(SkArithmeticMode_linear, (i2, i3, i4));
+#endif
+ }
+ return SkNEW_ARGS(SkArithmeticMode_scalar, (k1, k2, k3, k4));
+}
+