expand SkLayerDrawLooper to allow for an xfermode when transfering the paint's color,
and allow that the offset be applied pre or post



git-svn-id: http://skia.googlecode.com/svn/trunk@1115 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/include/effects/SkLayerDrawLooper.h b/include/effects/SkLayerDrawLooper.h
index 682a9dc..b487a88 100644
--- a/include/effects/SkLayerDrawLooper.h
+++ b/include/effects/SkLayerDrawLooper.h
@@ -2,6 +2,7 @@
 #define SkLayerDrawLooper_DEFINED
 
 #include "SkDrawLooper.h"
+#include "SkXfermode.h"
 
 struct SkPoint;
 
@@ -10,37 +11,77 @@
             SkLayerDrawLooper();
     virtual ~SkLayerDrawLooper();
 
+    /**
+     *  Bits specifies which aspects of the layer's paint should replace the
+     *  corresponding aspects on the draw's paint.
+     *  kEntirePaint_Bits means use the layer's paint completely.
+     *  0 means ignore the layer's paint.
+     */
     enum Bits {
-        kAlpha_Bit      = 1 << 0,   //!< use this layer's alpha
-        kColor_Bit      = 1 << 1,   //!< use this layer's color
-        kStyle_Bit      = 1 << 2,   //!< use this layer's Style/stroke settings
-        kTextSkewX_Bit  = 1 << 3,   //!< use this layer's textskewx
-        kPathEffect_Bit = 1 << 4,   //!< use this layer's patheffect
-        kMaskFilter_Bit = 1 << 5,   //!< use this layer's maskfilter
-        kShader_Bit     = 1 << 6,   //!< use this layer's shader
-        kColorFilter_Bit = 1 << 7,  //!< use this layer's colorfilter
-        kXfermode_Bit   = 1 << 8,   //!< use this layer's xfermode
+        kStyle_Bit      = 1 << 0,   //!< use this layer's Style/stroke settings
+        kTextSkewX_Bit  = 1 << 1,   //!< use this layer's textskewx
+        kPathEffect_Bit = 1 << 2,   //!< use this layer's patheffect
+        kMaskFilter_Bit = 1 << 3,   //!< use this layer's maskfilter
+        kShader_Bit     = 1 << 4,   //!< use this layer's shader
+        kColorFilter_Bit = 1 << 5,  //!< use this layer's colorfilter
+        kXfermode_Bit   = 1 << 6,   //!< use this layer's xfermode
         
         kEntirePaint_Bits = -1,      //!< use this layer's paint entirely
     };
     typedef int32_t BitFlags;
-    
+
+    /**
+     *  Info for how to apply the layer's paint and offset.
+     *
+     *  fColorMode controls how we compute the final color for the layer:
+     *      The layer's paint's color is treated as the SRC
+     *      The draw's paint's color is treated as the DST
+     *      final-color = Mode(layers-color, draws-color);
+     *  Any SkXfermode::Mode will work. Two common choices are:
+     *      kSrc_Mode: to use the layer's color, ignoring the draw's
+     *      kDst_Mode: to just keep the draw's color, ignoring the layer's
+     */
+    struct LayerInfo {
+        BitFlags            fPaintBits;
+        SkXfermode::Mode    fColorMode;
+        SkVector            fOffset;
+        bool                fPostTranslate; //!< applies to fOffset
+
+        /**
+         *  Initial the LayerInfo. Defaults to settings that will draw the
+         *  layer with no changes: e.g.
+         *      fPaintBits == 0
+         *      fColorMode == kDst_Mode
+         *      fOffset == (0, 0)
+         */
+        LayerInfo();
+    };
+
     /**
      *  Call for each layer you want to add (from top to bottom).
      *  This returns a paint you can modify, but that ptr is only valid until
      *  the next call made to this object.
-     *
-     *  The optional bits parameter specifies which aspects of this paint
-     *  should replace the paint that is passed to the draw call. If 0 is
-     *  specified, then this layer's paint will be ignored.
      */
-    SkPaint* addLayer(SkScalar dx, SkScalar dy, BitFlags = kEntirePaint_Bits);
+    SkPaint* addLayer(const LayerInfo&);
+
+    /**
+     *  Call for each layer you want to add (from top to bottom).
+     *  This returns a paint you can modify, but that ptr is only valid until
+     *  the next call made to this object.
+     *  The returned paint will be ignored, and only the offset will be applied
+     *
+     *  DEPRECATED: call addLayer(const LayerInfo&)
+     */
+    SkPaint* addLayer(SkScalar dx, SkScalar dy);
     
     /**
-     *  Helper for addLayer() which passes (0, 0) for the offset parameters
+     *  Helper for addLayer() which passes (0, 0) for the offset parameters.
+     *  This layer will not affect the drawing in any way.
+     *
+     *  DEPRECATED: call addLayer(const LayerInfo&)
      */
-    SkPaint* addLayer(BitFlags bits = kEntirePaint_Bits) {
-        return this->addLayer(0, 0, bits);
+    SkPaint* addLayer() {
+        return this->addLayer(0, 0);
     }
     
     // overrides from SkDrawLooper
@@ -63,9 +104,8 @@
     struct Rec {
         Rec*    fNext;
         SkPaint fPaint;
-        SkPoint fOffset;
-        uint32_t fBits;
-        
+        LayerInfo fInfo;
+
         static Rec* Reverse(Rec*);
     };
     Rec*    fRecs;
@@ -74,7 +114,8 @@
     // state-machine during the init/next cycle
     Rec* fCurrRec;
 
-    static void ApplyBits(SkPaint* dst, const SkPaint& src, BitFlags bits);
+    static void ApplyBits(SkPaint* dst, const SkPaint& src, BitFlags,
+                          SkXfermode::Mode);
 
     class MyRegistrar : public SkFlattenable::Registrar {
     public:
diff --git a/src/effects/SkLayerDrawLooper.cpp b/src/effects/SkLayerDrawLooper.cpp
index db64133..b3f3886 100644
--- a/src/effects/SkLayerDrawLooper.cpp
+++ b/src/effects/SkLayerDrawLooper.cpp
@@ -1,6 +1,15 @@
 #include "SkCanvas.h"
+#include "SkColor.h"
 #include "SkLayerDrawLooper.h"
 #include "SkPaint.h"
+#include "SkUnPreMultiply.h"
+
+SkLayerDrawLooper::LayerInfo::LayerInfo() {
+    fPaintBits = 0;                     // ignore out paint
+    fColorMode = SkXfermode::kDst_Mode; // ignore our color
+    fOffset.set(0, 0);
+    fPostTranslate = false;
+}
 
 SkLayerDrawLooper::SkLayerDrawLooper() {
     fRecs = NULL;
@@ -15,45 +24,60 @@
         rec = next;
     }
 }
-    
-SkPaint* SkLayerDrawLooper::addLayer(SkScalar dx, SkScalar dy, BitFlags bits) {
+
+SkPaint* SkLayerDrawLooper::addLayer(const LayerInfo& info) {
     fCount += 1;
 
     Rec* rec = SkNEW(Rec);
     rec->fNext = fRecs;
-    rec->fOffset.set(dx, dy);
-    rec->fBits = bits;
+    rec->fInfo = info;
     fRecs = rec;
 
     return &rec->fPaint;
 }
 
+SkPaint* SkLayerDrawLooper::addLayer(SkScalar dx, SkScalar dy) {
+    LayerInfo info;
+
+    info.fOffset.set(dx, dy);
+    return this->addLayer(info);
+}
+
 void SkLayerDrawLooper::init(SkCanvas* canvas) {
     fCurrRec = fRecs;
     canvas->save(SkCanvas::kMatrix_SaveFlag);
 }
 
+static SkColor xferColor(SkColor src, SkColor dst, SkXfermode::Mode mode) {
+    switch (mode) {
+        case SkXfermode::kSrc_Mode:
+            return src;
+        case SkXfermode::kDst_Mode:
+            return dst;
+        default: {
+            SkPMColor pmS = SkPreMultiplyColor(src);
+            SkPMColor pmD = SkPreMultiplyColor(dst);
+            SkPMColor result = SkXfermode::GetProc(mode)(pmS, pmD);
+            return SkUnPreMultiply::PMColorToColor(result);
+        }
+    }
+}
+
 void SkLayerDrawLooper::ApplyBits(SkPaint* dst, const SkPaint& src,
-                                  BitFlags bits) {
+                                  BitFlags bits, SkXfermode::Mode colorMode) {
+    dst->setColor(xferColor(src.getColor(), dst->getColor(), colorMode));
+
     if (0 == bits) {
         return;
     }
     if (kEntirePaint_Bits == bits) {
+        // we've already compute the color, so save it from the assignment
+        SkColor c = dst->getColor();
         *dst = src;
+        dst->setColor(c);
         return;
     }
 
-    SkColor c = dst->getColor();
-    if (bits & kAlpha_Bit) {
-        c &= 0x00FFFFFF;
-        c |= src.getColor() & 0xFF000000;
-    }
-    if (bits & kColor_Bit) {
-        c &= 0xFF000000;
-        c |= src.getColor() & 0x00FFFFFF;
-    }
-    dst->setColor(c);
-
     if (bits & kStyle_Bit) {
         dst->setStyle(src.getStyle());
         dst->setStrokeWidth(src.getStrokeWidth());
@@ -96,15 +120,29 @@
 #endif
 }
 
+// Should we add this to canvas?
+static void postTranslate(SkCanvas* canvas, SkScalar dx, SkScalar dy) {
+    SkMatrix m = canvas->getTotalMatrix();
+    m.postTranslate(dx, dy);
+    canvas->setMatrix(m);
+}
+
 bool SkLayerDrawLooper::next(SkCanvas* canvas, SkPaint* paint) {
     canvas->restore();
     if (NULL == fCurrRec) {
         return false;
     }
 
-    ApplyBits(paint, fCurrRec->fPaint, fCurrRec->fBits);
+    ApplyBits(paint, fCurrRec->fPaint, fCurrRec->fInfo.fPaintBits,
+              fCurrRec->fInfo.fColorMode);
+
     canvas->save(SkCanvas::kMatrix_SaveFlag);
-    canvas->translate(fCurrRec->fOffset.fX, fCurrRec->fOffset.fY);
+    if (fCurrRec->fInfo.fPostTranslate) {
+        postTranslate(canvas, fCurrRec->fInfo.fOffset.fX,
+                      fCurrRec->fInfo.fOffset.fY);
+    } else {
+        canvas->translate(fCurrRec->fInfo.fOffset.fX, fCurrRec->fInfo.fOffset.fY);
+    }
     fCurrRec = fCurrRec->fNext;
 
     return true;
@@ -143,8 +181,11 @@
     
     Rec* rec = fRecs;
     for (int i = 0; i < fCount; i++) {
-        buffer.writeScalar(rec->fOffset.fX);
-        buffer.writeScalar(rec->fOffset.fY);
+        buffer.writeInt(rec->fInfo.fPaintBits);
+        buffer.writeInt(rec->fInfo.fColorMode);
+        buffer.writeScalar(rec->fInfo.fOffset.fX);
+        buffer.writeScalar(rec->fInfo.fOffset.fY);
+        buffer.writeBool(rec->fInfo.fPostTranslate);
         rec->fPaint.flatten(buffer);
         rec = rec->fNext;
     }
@@ -158,9 +199,13 @@
     int count = buffer.readInt();
 
     for (int i = 0; i < count; i++) {
-        SkScalar dx = buffer.readScalar();
-        SkScalar dy = buffer.readScalar();
-        this->addLayer(dx, dy)->unflatten(buffer);
+        LayerInfo info;
+        info.fPaintBits = buffer.readInt();
+        info.fColorMode = (SkXfermode::Mode)buffer.readInt();
+        info.fOffset.fX = buffer.readScalar();
+        info.fOffset.fY = buffer.readScalar();
+        info.fPostTranslate = buffer.readBool();
+        this->addLayer(info)->unflatten(buffer);
     }
     SkASSERT(count == fCount);