aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--samplecode/SampleAAClip.cpp94
-rw-r--r--src/core/SkAAClip.cpp459
-rw-r--r--src/core/SkAAClip.h18
3 files changed, 549 insertions, 22 deletions
diff --git a/samplecode/SampleAAClip.cpp b/samplecode/SampleAAClip.cpp
new file mode 100644
index 0000000000..dd6af09253
--- /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 c7cfca5c34..a79cca1a3a 100644
--- a/src/core/SkAAClip.cpp
+++ b/src/core/SkAAClip.cpp
@@ -57,6 +57,58 @@ struct SkAAClip::RunHead {
}
};
+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 @@ void SkAAClip::swap(SkAAClip& other) {
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 @@ bool SkAAClip::setRect(const SkRect& r) {
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 @@ bool SkAAClip::quickContains(int left, int top, int right, int bottom) const {
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 @@ public:
}
}
+ 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 @@ public:
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 @@ private:
}
};
-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::setPath(const SkPath& path, const SkRegion& clip) {
///////////////////////////////////////////////////////////////////////////////
-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 @@ const SkBitmap* SkAAClipBlitter::justAnOpaqueColor(uint32_t* value) {
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 c62292f79e..98df3a6c01 100644
--- a/src/core/SkAAClip.h
+++ b/src/core/SkAAClip.h
@@ -30,33 +30,41 @@ public:
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;