check-point for aaclip



git-svn-id: http://skia.googlecode.com/svn/trunk@2413 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/samplecode/SampleAAClip.cpp b/samplecode/SampleAAClip.cpp
new file mode 100644
index 0000000..dd6af09
--- /dev/null
+++ b/samplecode/SampleAAClip.cpp
@@ -0,0 +1,94 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SampleCode.h"
+#include "SkView.h"
+#include "SkCanvas.h"
+#include "SkAAClip.h"
+
+static void drawClip(SkCanvas* canvas, const SkAAClip& clip) {
+    SkMask mask;
+    SkBitmap bm;
+    
+    clip.copyToMask(&mask);
+    bm.setConfig(SkBitmap::kA8_Config, mask.fBounds.width(),
+                 mask.fBounds.height(), mask.fRowBytes);
+    bm.setPixels(mask.fImage);
+
+    SkPaint paint;
+    canvas->drawBitmap(bm, mask.fBounds.fLeft, mask.fBounds.fTop, &paint);
+}
+
+class AAClipView : public SampleView {
+public:
+    AAClipView() {
+    }
+
+protected:
+    // overrides from SkEventSink
+    virtual bool onQuery(SkEvent* evt) {
+        if (SampleCode::TitleQ(*evt)) {
+            SampleCode::TitleR(evt, "AAClip");
+            return true;
+        }
+        return this->INHERITED::onQuery(evt);
+    }
+
+    virtual void onDrawContent(SkCanvas* canvas) {
+#if 1
+        SkAAClip aaclip;
+        SkPath path;
+        SkRect bounds;
+        
+        bounds.set(0, 0, 20, 20);
+        bounds.inset(SK_ScalarHalf, SK_ScalarHalf);
+
+//        path.addRect(bounds);
+//        path.addOval(bounds);
+        path.addRoundRect(bounds, 4, 4);
+        aaclip.setPath(path);
+        canvas->translate(30, 30);
+        drawClip(canvas, aaclip);
+
+        SkAAClip aaclip2;
+        path.offset(10, 10);
+        aaclip2.setPath(path);
+        canvas->translate(30, 0);
+        drawClip(canvas, aaclip2);
+
+        SkAAClip aaclip3;
+        aaclip3.op(aaclip, aaclip2, SkRegion::kIntersect_Op);
+        canvas->translate(30, 0);
+        drawClip(canvas, aaclip3);
+        
+#endif
+        
+#if 0
+        SkRect r;
+        r.set(0, 0, this->width(), this->height());
+        r.inset(20, 20);
+        canvas->clipRect(r);
+        
+        SkPath path;
+        path.addRect(r);
+        SkPaint paint;
+        paint.setAntiAlias(true);
+        paint.setColor(SK_ColorRED);
+        canvas->drawPath(path, paint);
+#endif
+    }
+
+private:
+    typedef SkView INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new AAClipView; }
+static SkViewRegister reg(MyFactory);
+
diff --git a/src/core/SkAAClip.cpp b/src/core/SkAAClip.cpp
index c7cfca5..a79cca1 100644
--- a/src/core/SkAAClip.cpp
+++ b/src/core/SkAAClip.cpp
@@ -57,6 +57,58 @@
     }
 };
 
+class SkAAClip::Iter {
+public:
+    Iter(const SkAAClip&);
+
+    bool done() const { return fDone; }
+    int top() const { SkASSERT(!fDone); return fTop; }
+    int bottom() const { SkASSERT(!fDone); return fBottom; }
+    const uint8_t* data() const { SkASSERT(!fDone); return fData; }
+    void next();
+
+private:
+    const YOffset* fCurrYOff;
+    const YOffset* fStopYOff;
+    const uint8_t* fData;
+
+    int fTop, fBottom;
+    bool fDone;
+};
+
+SkAAClip::Iter::Iter(const SkAAClip& clip) {
+    if (clip.isEmpty()) {
+        fDone = true;
+        return;
+    }
+    
+    const RunHead* head = clip.fRunHead;
+    fCurrYOff = head->yoffsets();
+    fStopYOff = fCurrYOff + head->fRowCount;
+    fData     = head->data() + fCurrYOff->fOffset;
+
+    // setup first value
+    fTop = clip.fBounds.fTop;
+    fBottom = clip.fBounds.fTop + fCurrYOff->fY + 1;
+    fDone = false;
+}
+
+void SkAAClip::Iter::next() {
+    SkASSERT(!fDone);
+
+    const YOffset* prev = fCurrYOff;
+    const YOffset* curr = prev + 1;
+
+    if (curr >= fStopYOff) {
+        fDone = true;
+    } else {
+        fTop = fBottom;
+        fBottom += curr->fY - prev->fY;
+        fData += curr->fOffset - prev->fOffset;
+        fCurrYOff = curr;
+    }
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 
 void SkAAClip::freeRuns() {
@@ -125,6 +177,11 @@
     SkTSwap(fRunHead, other.fRunHead);
 }
 
+bool SkAAClip::set(const SkAAClip& src) {
+    *this = src;
+    return !this->isEmpty();
+}
+
 bool SkAAClip::setEmpty() {
     this->freeRuns();
     fBounds.setEmpty();
@@ -147,15 +204,9 @@
         return this->setEmpty();
     }
 
-    SkIRect ibounds;
-    r.roundOut(&ibounds);
-
-    SkRegion clip;
-    clip.setRect(ibounds);
-
     SkPath path;
     path.addRect(r);
-    return this->setPath(path, clip);
+    return this->setPath(path);
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -204,9 +255,11 @@
     if (!fBounds.contains(left, top, right, bottom)) {
         return false;
     }
+#if 0
     if (this->isRect()) {
         return true;
     }
+#endif
 
     int lastY;
     const uint8_t* row = this->findRow(top, &lastY);
@@ -249,6 +302,8 @@
         }
     }
 
+    const SkIRect& getBounds() const { return fBounds; }
+
     void addRun(int x, int y, U8CPU alpha, int count) {
         SkASSERT(count > 0);
         SkASSERT(fBounds.contains(x, y));
@@ -336,7 +391,7 @@
             SkDebugf("\n");
         }
 
-#if 0
+#if 1
         int prevY = -1;
         for (y = 0; y < fRows.count(); ++y) {
             const Row& row = fRows[y];
@@ -478,26 +533,30 @@
     }
 };
 
-bool SkAAClip::setPath(const SkPath& path, const SkRegion& clip) {
-    if (clip.isEmpty()) {
+bool SkAAClip::setPath(const SkPath& path, const SkRegion* clip) {
+    if (clip && clip->isEmpty()) {
         return this->setEmpty();
     }
 
     SkIRect ibounds;
+    path.getBounds().roundOut(&ibounds);
 
+    SkRegion tmpClip;
+    if (NULL == clip) {
+        tmpClip.setRect(ibounds);
+        clip = &tmpClip;
+    }
+    
     if (!path.isInverseFillType()) {
-        path.getBounds().roundOut(&ibounds);
-        if (ibounds.isEmpty() || !ibounds.intersect(clip.getBounds())) {
+        if (ibounds.isEmpty() || !ibounds.intersect(clip->getBounds())) {
             return this->setEmpty();
         }
-    } else {
-        ibounds = clip.getBounds();
     }
 
     Builder        builder(ibounds);
     BuilderBlitter blitter(&builder);
 
-    SkScan::AntiFillPath(path, clip, &blitter, true);
+    SkScan::AntiFillPath(path, *clip, &blitter, true);
 
     this->freeRuns();
     fBounds = ibounds;
@@ -508,8 +567,331 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
-bool SkAAClip::op(const SkAAClip&, const SkAAClip&, SkRegion::Op op) {
-    return true;
+typedef void (*RowProc)(SkAAClip::Builder&, int bottom,
+                        const uint8_t* rowA, const SkIRect& rectA,
+                        const uint8_t* rowB, const SkIRect& rectB);
+
+static void sectRowProc(SkAAClip::Builder& builder, int bottom,
+                        const uint8_t* rowA, const SkIRect& rectA,
+                        const uint8_t* rowB, const SkIRect& rectB) {
+    
+}
+
+typedef U8CPU (*AlphaProc)(U8CPU alphaA, U8CPU alphaB);
+
+static U8CPU sectAlphaProc(U8CPU alphaA, U8CPU alphaB) {
+    // Multiply
+    return SkMulDiv255Round(alphaA, alphaB);
+}
+
+static U8CPU unionAlphaProc(U8CPU alphaA, U8CPU alphaB) {
+    // SrcOver
+    return alphaA + alphaB - SkMulDiv255Round(alphaA, alphaB);
+}
+
+static U8CPU diffAlphaProc(U8CPU alphaA, U8CPU alphaB) {
+    // SrcOut
+    return SkMulDiv255Round(alphaA, 0xFF - alphaB);
+}
+
+static U8CPU xorAlphaProc(U8CPU alphaA, U8CPU alphaB) {
+    // XOR
+    return alphaA + alphaB - 2 * SkMulDiv255Round(alphaA, alphaB);
+}
+
+static AlphaProc find_alpha_proc(SkRegion::Op op) {
+    switch (op) {
+        case SkRegion::kIntersect_Op:
+            return sectAlphaProc;
+        case SkRegion::kDifference_Op:
+            return diffAlphaProc;
+        case SkRegion::kUnion_Op:
+            return unionAlphaProc;
+        case SkRegion::kXOR_Op:
+            return xorAlphaProc;
+        default:
+            SkASSERT(!"unexpected region op");
+            return sectAlphaProc;
+    }
+}
+
+static const uint8_t gEmptyRow[] = {
+    0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0,
+    0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0,
+    0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0,
+    0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0,
+    0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0,
+    0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0,
+    0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0,
+    0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0, 0xFF, 0,
+};
+
+class RowIter {
+public:
+    RowIter(const uint8_t* row, const SkIRect& bounds) {
+        fRow = row;
+        fLeft = bounds.fLeft;
+        fRight = bounds.fLeft + row[0];
+        fBoundsRight = bounds.fRight;
+        SkASSERT(fRight <= fBoundsRight);
+        fDone = false;
+    }
+
+    bool done() const { return fDone; }
+    int left() const { SkASSERT(!done()); return fLeft; }
+    int right() const { SkASSERT(!done()); return fRight; }
+    U8CPU alpha() const { SkASSERT(!done()); return fRow[1]; }
+    void next() {
+        SkASSERT(!done());
+        if (fRight == fBoundsRight) {
+            fDone = true;
+        } else {
+            fRow += 2;
+            fLeft = fRight;
+            fRight += fRow[0];
+            SkASSERT(fRight <= fBoundsRight);
+        }
+    }
+
+private:
+    const uint8_t*  fRow;
+    int             fLeft;
+    int             fRight;
+    int             fBoundsRight;
+    bool            fDone;
+};
+
+static void adjust_row(RowIter& iter, int& leftA, int& riteA,
+                       int left, int rite) {
+    if (leftA == left) {
+        leftA = rite;
+        if (leftA == riteA) {
+            if (iter.done()) {
+                leftA = 0x7FFFFFFF;
+                riteA = 0x7FFFFFFF;
+            } else {
+                iter.next();
+                leftA = iter.left();
+                riteA = iter.right();
+            }
+        }
+    }
+}
+
+static void operatorX(SkAAClip::Builder& builder, int lastY,
+                      RowIter& iterA, RowIter& iterB,
+                      AlphaProc proc, const SkIRect& bounds) {
+    SkASSERT(!iterA.done());
+    int leftA = iterA.left();
+    int riteA = iterA.right();
+    SkASSERT(!iterB.done());
+    int leftB = iterB.left();
+    int riteB = iterB.right();
+
+    for (;;) {
+        U8CPU alphaA = 0;
+        U8CPU alphaB = 0;
+        
+        int left, rite;
+        if (riteA <= leftB) {         // all A
+            left = leftA;
+            rite = riteA;
+            alphaA = iterA.alpha();
+        } else if (riteB <= leftA) {  // all B
+            left = leftB;
+            rite = riteB;
+            alphaB = iterB.alpha();
+        } else {
+            // some overlap
+            alphaA = iterA.alpha();
+            alphaB = iterB.alpha();
+            if (leftA <= leftB) {
+                left = leftA;
+                if (leftA == leftB) {
+                    rite = SkMin32(riteA, riteB);
+                } else{
+                    rite = leftB;
+                }
+            } else {    // leftB < leftA
+                left = leftB;
+                rite = leftA;
+            }
+        }
+
+        if (left >= bounds.fRight) {
+            break;
+        }
+        
+        SkASSERT(rite <= bounds.fRight);
+        if (left >= bounds.fLeft) {
+            builder.addRun(left, lastY, proc(alphaA, alphaB), rite - left);
+        } else {
+            SkASSERT(rite <= bounds.fLeft);
+        }
+        
+        if (rite == bounds.fRight) {
+            break;
+        }
+        
+        adjust_row(iterA, leftA, riteA, left, rite);
+        adjust_row(iterB, leftB, riteB, left, rite);
+    }
+}
+
+static void adjust_iter(SkAAClip::Iter& iter, int& topA, int& botA,
+                        int top, int bot) {
+    if (topA == top) {
+        topA = bot;
+        if (topA == botA) {
+            if (iter.done()) {
+                topA = 0x7FFFFFFF;
+                botA = 0x7FFFFFFF;
+            } else {
+                iter.next();
+                topA = iter.top();
+                botA = iter.bottom();
+            }
+        }
+    }
+}
+
+static void operateY(SkAAClip::Builder& builder, const SkAAClip& A,
+                     const SkAAClip& B, SkRegion::Op op) {
+    AlphaProc proc = find_alpha_proc(op);
+    const SkIRect& bounds = builder.getBounds();
+
+    SkAAClip::Iter iterA(A);
+    SkAAClip::Iter iterB(B);
+
+    SkASSERT(!iterA.done());
+    int topA = iterA.top();
+    int botA = iterA.bottom();
+    SkASSERT(!iterB.done());
+    int topB = iterB.top();
+    int botB = iterB.bottom();
+
+    for (;;) {
+        SkASSERT(topA < botA);
+        SkASSERT(topB < botB);
+
+        const uint8_t* rowA = gEmptyRow;
+        const uint8_t* rowB = gEmptyRow;
+
+        // find the vertical
+        int top, bot;
+        if (botA <= topB) {         // all A
+            top = topA;
+            bot = botA;
+            rowA = iterA.data();
+        } else if (botB <= topA) {  // all B
+            top = topB;
+            bot = botB;
+            rowB = iterB.data();
+        } else {
+            // some overlap
+            rowA = iterA.data();
+            rowB = iterB.data();
+            if (topA <= topB) {
+                top = topA;
+                if (topA == topB) {
+                    bot = SkMin32(botA, botB);
+                } else{
+                    bot = topB;
+                }
+            } else {    // topB < topA
+                top = topB;
+                bot = topA;
+            }
+        }
+
+        if (top >= bounds.fBottom) {
+            break;
+        }
+
+        SkASSERT(bot <= bounds.fBottom);
+        if (top >= bounds.fTop) {
+            RowIter rowIterA(rowA, A.getBounds());
+            RowIter rowIterB(rowB, B.getBounds());
+            operatorX(builder, bot - 1, rowIterA, rowIterB, proc, bounds);
+        } else {
+            SkASSERT(bot <= bounds.fTop);
+        }
+
+        if (bot == bounds.fBottom) {
+            break;
+        }
+        
+        adjust_iter(iterA, topA, botA, top, bot);
+        adjust_iter(iterB, topB, botB, top, bot);
+    }
+    
+}
+
+bool SkAAClip::op(const SkAAClip& clipAOrig, const SkAAClip& clipBOrig,
+                  SkRegion::Op op) {
+    if (SkRegion::kReplace_Op == op) {
+        return this->set(clipBOrig);
+    }
+    
+    const SkAAClip* clipA = &clipAOrig;
+    const SkAAClip* clipB = &clipBOrig;
+    
+    if (SkRegion::kReverseDifference_Op == op) {
+        SkTSwap(clipA, clipB);
+        op = SkRegion::kDifference_Op;
+    }
+
+    bool a_empty = clipA->isEmpty();
+    bool b_empty = clipB->isEmpty();
+
+    SkIRect bounds;
+    switch (op) {
+        case SkRegion::kDifference_Op:
+            if (a_empty) {
+                return this->setEmpty();
+            }
+            if (b_empty || !SkIRect::Intersects(clipA->fBounds, clipB->fBounds)) {
+                return this->set(*clipA);
+            }
+            bounds = clipA->fBounds;
+            break;
+            
+        case SkRegion::kIntersect_Op:
+            if ((a_empty | b_empty) || !bounds.intersect(clipA->fBounds,
+                                                         clipB->fBounds)) {
+                return this->setEmpty();
+            }
+            break;
+            
+        case SkRegion::kUnion_Op:
+        case SkRegion::kXOR_Op:
+            if (a_empty) {
+                return this->set(*clipB);
+            }
+            if (b_empty) {
+                return this->set(*clipA);
+            }
+            bounds = clipA->fBounds;
+            bounds.join(clipB->fBounds);
+            break;
+
+        default:
+            SkASSERT(!"unknown region op");
+            return !this->isEmpty();
+    }
+
+    SkASSERT(SkIRect::Intersects(clipA->fBounds, clipB->fBounds));
+    SkASSERT(SkIRect::Intersects(bounds, clipB->fBounds));
+    SkASSERT(SkIRect::Intersects(bounds, clipB->fBounds));
+
+    Builder builder(bounds);
+    operateY(builder, *clipA, *clipB, op);
+    // don't free us until now, since we might be clipA or clipB
+    this->freeRuns();
+    fBounds = bounds;
+    fRunHead = builder.finish();
+    
+    return !this->isEmpty();
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -674,3 +1056,46 @@
     return NULL;
 }
 
+///////////////////////////////////////////////////////////////////////////////
+
+static void expand_row_to_mask(uint8_t* SK_RESTRICT mask,
+                               const uint8_t* SK_RESTRICT row,
+                               int width) {
+    while (width > 0) {
+        int n = row[0];
+        SkASSERT(width >= n);
+        memset(mask, row[1], n);
+        mask += n;
+        row += 2;
+        width -= n;
+    }
+}
+
+void SkAAClip::copyToMask(SkMask* mask) const {
+    mask->fFormat = SkMask::kA8_Format;
+    if (this->isEmpty()) {
+        mask->fBounds.setEmpty();
+        mask->fImage = NULL;
+        mask->fRowBytes = 0;
+        return;
+    }
+
+    mask->fBounds = fBounds;
+    mask->fRowBytes = fBounds.width();
+    size_t size = mask->computeImageSize();
+    mask->fImage = SkMask::AllocImage(size);
+
+    Iter iter(*this);
+    uint8_t* dst = mask->fImage;
+    const int width = fBounds.width();
+
+    int y = fBounds.fTop;
+    while (!iter.done()) {
+        do {
+            expand_row_to_mask(dst, iter.data(), width);
+            dst += mask->fRowBytes;
+        } while (++y < iter.bottom());
+        iter.next();
+    }
+}
+
diff --git a/src/core/SkAAClip.h b/src/core/SkAAClip.h
index c62292f..98df3a6 100644
--- a/src/core/SkAAClip.h
+++ b/src/core/SkAAClip.h
@@ -30,33 +30,41 @@
     void swap(SkAAClip&);
 
     bool isEmpty() const { return SkAAClip_gEmptyPtr == fRunHead; }
-    bool isRect() const { return SkAAClip_gRectPtr == fRunHead; }
-    bool isComplex() const { return !this->isEmpty() && !this->isRect(); }
+//    bool isRect() const { return SkAAClip_gRectPtr == fRunHead; }
+    bool isComplex() const { return !this->isEmpty() /*&& !this->isRect()*/; }
     const SkIRect& getBounds() const { return fBounds; }
 
     bool setEmpty();
     bool setRect(const SkIRect&);
     bool setRect(const SkRect&);
-    bool setPath(const SkPath&, const SkRegion& clip);
+    bool setPath(const SkPath&, const SkRegion* clip = NULL);
+    bool set(const SkAAClip&);
 
     bool op(const SkAAClip&, const SkAAClip&, SkRegion::Op);
 
+    /**
+     *  Allocates a mask the size of the aaclip, and expands its data into
+     *  the mask, using kA8_Format
+     */
+    void copyToMask(SkMask*) const;
+
     // called internally
     
     bool quickContains(int left, int top, int right, int bottom) const;
     const uint8_t* findRow(int y, int* lastYForRow) const;
     const uint8_t* findX(const uint8_t data[], int x, int* initialCount) const;
 
-private:
+    class Iter;
     struct RunHead;
     struct YOffset;
+    class Builder;
 
+private:
     SkIRect  fBounds;
     RunHead* fRunHead;
 
     void freeRuns();
 
-    class Builder;
     friend class Builder;
     class BuilderBlitter;
     friend class BuilderBlitter;