add SkLerpXfermode
authorreed@google.com <reed@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>
Tue, 21 May 2013 21:33:11 +0000 (21:33 +0000)
committerreed@google.com <reed@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>
Tue, 21 May 2013 21:33:11 +0000 (21:33 +0000)
BUG=
R=bsalomon@google.com

Review URL: https://codereview.chromium.org/15602003

git-svn-id: http://skia.googlecode.com/svn/trunk@9229 2bbb7eff-a529-9590-31e7-b0007b416f81

gm/lerpmode.cpp [new file with mode: 0644]
gyp/effects.gypi
gyp/gmslides.gypi
include/core/SkColorPriv.h
include/effects/SkLerpXfermode.h [new file with mode: 0644]
src/effects/SkLerpXfermode.cpp [new file with mode: 0644]

diff --git a/gm/lerpmode.cpp b/gm/lerpmode.cpp
new file mode 100644 (file)
index 0000000..481af35
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * 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 "gm.h"
+#include "SkCanvas.h"
+#include "SkLerpXfermode.h"
+
+static void show_circlelayers(SkCanvas* canvas, SkXfermode* mode) {
+    SkPaint paint;
+    paint.setAntiAlias(true);
+    SkRect r, bounds = { 10, 10, 110, 110 };
+
+    r = bounds;
+    r.fRight = bounds.centerX();
+    canvas->drawRect(r, paint);
+
+    canvas->saveLayer(&bounds, NULL);
+    
+    paint.setColor(0x80FF0000);
+    r = bounds;
+    r.inset(20, 0);
+    canvas->drawOval(r, paint);
+    
+    paint.setColor(0x800000FF);
+    r = bounds;
+    r.inset(0, 20);
+    paint.setXfermode(mode);
+    canvas->drawOval(r, paint);
+    
+    canvas->restore();
+}
+
+class LerpXfermodeGM : public skiagm::GM {
+public:
+    LerpXfermodeGM() {}
+
+protected:
+    virtual SkString onShortName() SK_OVERRIDE {
+        return SkString("lerpmode");
+    }
+
+    virtual SkISize onISize() SK_OVERRIDE {
+        return SkISize::Make(240, 120);
+    }
+
+    virtual void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+        show_circlelayers(canvas, NULL);
+        canvas->translate(150, 0);
+        SkAutoTUnref<SkXfermode> mode(SkLerpXfermode::Create(0.5f));
+        show_circlelayers(canvas, mode.get());
+    }
+
+private:
+    typedef skiagm::GM INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+DEF_GM( return SkNEW(LerpXfermodeGM); )
+
index 3eb0d95..1730ffc 100644 (file)
@@ -34,6 +34,7 @@
     '<(skia_src_path)/effects/SkKernel33MaskFilter.cpp',
     '<(skia_src_path)/effects/SkLayerDrawLooper.cpp',
     '<(skia_src_path)/effects/SkLayerRasterizer.cpp',
+    '<(skia_src_path)/effects/SkLerpXfermode.cpp',
     '<(skia_src_path)/effects/SkLightingImageFilter.cpp',
     '<(skia_src_path)/effects/SkMatrixConvolutionImageFilter.cpp',
     '<(skia_src_path)/effects/SkMergeImageFilter.cpp',
@@ -91,6 +92,7 @@
     '<(skia_include_path)/effects/SkKernel33MaskFilter.h',
     '<(skia_include_path)/effects/SkLayerDrawLooper.h',
     '<(skia_include_path)/effects/SkLayerRasterizer.h',
+    '<(skia_include_path)/effects/SkLerpXfermode.h',
     '<(skia_include_path)/effects/SkLightingImageFilter.h',
     '<(skia_include_path)/effects/SkOffsetImageFilter.h',
     '<(skia_include_path)/effects/SkMorphologyImageFilter.h',
index 96c252d..d07820d 100644 (file)
@@ -49,6 +49,7 @@
     '../gm/hittestpath.cpp',
     '../gm/imageblur.cpp',
     '../gm/imagemagnifier.cpp',
+    '../gm/lerpmode.cpp',
     '../gm/lighting.cpp',
     '../src/image/SkImage_Codec.cpp',
     '../gm/image.cpp',
index 46f7d27..5d2df62 100644 (file)
@@ -273,16 +273,16 @@ static inline SkPMColor SkFourByteInterp(SkPMColor src, SkPMColor dst,
  * architectures than an equivalent 64b version and 30% faster than
  * SkFourByteInterp(). Third parameter controls blending of the first two:
  *   (src, dst, 0) returns dst
- *   (src, dst, 0xFF) returns src
- * ** Does not match the results of SkFourByteInterp() because we use
+ *   (src, dst, 256) returns src
+ * ** Does not match the results of SkFourByteInterp256() because we use
  * a more accurate scale computation!
  * TODO: migrate Skia function to using an accurate 255->266 alpha
  * conversion.
  */
-static inline SkPMColor SkFastFourByteInterp(SkPMColor src,
-                                             SkPMColor dst,
-                                             U8CPU srcWeight) {
-    SkASSERT(srcWeight < 256);
+static inline SkPMColor SkFastFourByteInterp256(SkPMColor src,
+                                                SkPMColor dst,
+                                                unsigned scale) {
+    SkASSERT(scale <= 256);
 
     // Reorders ARGB to AG-RB in order to reduce the number of operations.
     const uint32_t mask = 0xFF00FF;
@@ -291,16 +291,21 @@ static inline SkPMColor SkFastFourByteInterp(SkPMColor src,
     uint32_t dst_rb = dst & mask;
     uint32_t dst_ag = (dst >> 8) & mask;
 
-    // scale = srcWeight + (srcWeight >> 7) is more accurate than
-    // scale = srcWeight + 1, but 7% slower
-    int scale = srcWeight + (srcWeight >> 7);
-
     uint32_t ret_rb = src_rb * scale + (256 - scale) * dst_rb;
     uint32_t ret_ag = src_ag * scale + (256 - scale) * dst_ag;
 
     return (ret_ag & ~mask) | ((ret_rb & ~mask) >> 8);
 }
 
+static inline SkPMColor SkFastFourByteInterp(SkPMColor src,
+                                             SkPMColor dst,
+                                             U8CPU srcWeight) {
+    SkASSERT(srcWeight <= 255);
+    // scale = srcWeight + (srcWeight >> 7) is more accurate than
+    // scale = srcWeight + 1, but 7% slower
+    return SkFastFourByteInterp256(src, dst, srcWeight + (srcWeight >> 7));
+}
+
 /**
  *  Same as SkPackARGB32, but this version guarantees to not check that the
  *  values are premultiplied in the debug version.
diff --git a/include/effects/SkLerpXfermode.h b/include/effects/SkLerpXfermode.h
new file mode 100644 (file)
index 0000000..6151f3d
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2013 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkLerpXfermode_DEFINED
+#define SkLerpXfermode_DEFINED
+
+#include "SkXfermode.h"
+
+class SK_API SkLerpXfermode : public SkXfermode {
+public:
+    /**
+     *  result = scale * src + (1 - scale) * dst
+     *
+     *  When scale == 1, this is the same as kSrc_Mode
+     *  When scale == 0, this is the same as kDst_Mode
+     */
+    static SkXfermode* Create(SkScalar scale);
+
+    // overrides from SkXfermode
+    virtual void xfer32(SkPMColor dst[], const SkPMColor src[], int count,
+                        const SkAlpha aa[]) const SK_OVERRIDE;
+    virtual void xfer16(uint16_t dst[], const SkPMColor src[], int count,
+                        const SkAlpha aa[]) const SK_OVERRIDE;
+    virtual void xferA8(SkAlpha dst[], const SkPMColor src[], int count,
+                        const SkAlpha aa[]) const SK_OVERRIDE;
+
+    SK_DEVELOPER_TO_STRING()
+    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkLerpXfermode)
+
+protected:
+    SkLerpXfermode(SkFlattenableReadBuffer&);
+    virtual void flatten(SkFlattenableWriteBuffer&) const SK_OVERRIDE;
+
+private:
+    SkLerpXfermode(unsigned scale256);
+
+    unsigned fScale256;  // 0..256
+
+    typedef SkXfermode INHERITED;
+};
+
+#endif
diff --git a/src/effects/SkLerpXfermode.cpp b/src/effects/SkLerpXfermode.cpp
new file mode 100644 (file)
index 0000000..d73ecf4
--- /dev/null
@@ -0,0 +1,110 @@
+/*
+ * 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 "SkLerpXfermode.h"
+#include "SkColorPriv.h"
+#include "SkFlattenableBuffers.h"
+#include "SkString.h"
+
+SkXfermode* SkLerpXfermode::Create(SkScalar scale) {
+    int scale256 = SkScalarRoundToInt(scale * 256);
+    if (scale256 >= 256) {
+        return SkXfermode::Create(SkXfermode::kSrc_Mode);
+    } else if (scale256 <= 0) {
+        return SkXfermode::Create(SkXfermode::kDst_Mode);
+    }
+    return SkNEW_ARGS(SkLerpXfermode, (scale256));
+}
+
+SkLerpXfermode::SkLerpXfermode(unsigned scale256) : fScale256(scale256) {}
+
+SkLerpXfermode::SkLerpXfermode(SkFlattenableReadBuffer& buffer)
+    : INHERITED(buffer) {
+    fScale256 = buffer.readUInt();
+}
+
+void SkLerpXfermode::flatten(SkFlattenableWriteBuffer& buffer) const {
+    this->INHERITED::flatten(buffer);
+    buffer.writeUInt(fScale256);
+}
+
+void SkLerpXfermode::xfer32(SkPMColor dst[], const SkPMColor src[], int count,
+                             const SkAlpha aa[]) const {
+    const int scale = fScale256;
+
+    if (aa) {
+        for (int i = 0; i < count; ++i) {
+            unsigned a = aa[i];
+            if (a) {
+                SkPMColor dstC = dst[i];
+                SkPMColor resC = SkFastFourByteInterp256(src[i], dstC, scale);
+                if (a < 255) {
+                    resC = SkFastFourByteInterp256(resC, dstC, a + (a >> 7));
+                }
+                dst[i] = resC;
+            }
+        }
+    } else {
+        for (int i = 0; i < count; ++i) {
+            dst[i] = SkFastFourByteInterp256(src[i], dst[i], scale);
+        }
+    }
+}
+
+void SkLerpXfermode::xfer16(uint16_t dst[], const SkPMColor src[], int count,
+                             const SkAlpha aa[]) const {
+    const int scale = fScale256;
+
+    if (aa) {
+        for (int i = 0; i < count; ++i) {
+            unsigned a = aa[i];
+            if (a) {
+                SkPMColor dstC = SkPixel16ToPixel32(dst[i]);
+                SkPMColor resC = SkFastFourByteInterp256(src[i], dstC, scale);
+                if (a < 255) {
+                    resC = SkFastFourByteInterp256(resC, dstC, a + (a >> 7));
+                }
+                dst[i] = SkPixel32ToPixel16(resC);
+            }
+        }
+    } else {
+        for (int i = 0; i < count; ++i) {
+            SkPMColor dstC = SkPixel16ToPixel32(dst[i]);
+            SkPMColor resC = SkFastFourByteInterp256(src[i], dstC, scale);
+            dst[i] = SkPixel32ToPixel16(resC);
+        }
+    }
+}
+
+void SkLerpXfermode::xferA8(SkAlpha dst[], const SkPMColor src[], int count,
+                             const SkAlpha aa[]) const {
+    const int scale = fScale256;
+
+    if (aa) {
+        for (int i = 0; i < count; ++i) {
+            unsigned a = aa[i];
+            if (a) {
+                unsigned dstA = dst[i];
+                unsigned resA = SkAlphaBlend(SkGetPackedA32(src[i]), dstA, scale);
+                if (a < 255) {
+                    resA = SkAlphaBlend(resA, dstA, a + (a >> 7));
+                }
+                dst[i] = resA;
+            }
+        }
+    } else {
+        for (int i = 0; i < count; ++i) {
+            dst[i] = SkAlphaBlend(SkGetPackedA32(src[i]), dst[i], scale);
+        }
+    }
+}
+
+#ifdef SK_DEVELOPER
+void SkLerpXfermode::toString(SkString* str) const {
+    str->printf("SkLerpXfermode: scale: %g", fScale256 / 256.0);
+}
+#endif