summaryrefslogtreecommitdiffstats
path: root/skia/sgl/SkPath.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'skia/sgl/SkPath.cpp')
-rw-r--r--skia/sgl/SkPath.cpp128
1 files changed, 93 insertions, 35 deletions
diff --git a/skia/sgl/SkPath.cpp b/skia/sgl/SkPath.cpp
index afe8662..5d6882e 100644
--- a/skia/sgl/SkPath.cpp
+++ b/skia/sgl/SkPath.cpp
@@ -1,6 +1,6 @@
/* libs/graphics/sgl/SkPath.cpp
**
-** Copyright 2006, Google Inc.
+** Copyright 2006, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
@@ -19,6 +19,63 @@
#include "SkFlattenable.h"
#include "SkMath.h"
+////////////////////////////////////////////////////////////////////////////
+
+/* This guy's constructor/destructor bracket a path editing operation. It is
+ used when we know the bounds of the amount we are going to add to the path
+ (usually a new contour, but not required).
+
+ It captures some state about the path up front (i.e. if it already has a
+ cached bounds), and the if it can, it updates the cache bounds explicitly,
+ avoiding the need to revisit all of the points in computeBounds().
+ */
+class SkAutoPathBoundsUpdate {
+public:
+ SkAutoPathBoundsUpdate(SkPath* path, const SkRect& r) : fRect(r) {
+ this->init(path);
+ }
+
+ SkAutoPathBoundsUpdate(SkPath* path, SkScalar left, SkScalar top,
+ SkScalar right, SkScalar bottom) {
+ fRect.set(left, top, right, bottom);
+ this->init(path);
+ }
+
+ ~SkAutoPathBoundsUpdate() {
+ if (fEmpty) {
+ fPath->fFastBounds = fRect;
+ fPath->fFastBoundsIsDirty = false;
+ } else if (!fDirty) {
+ fPath->fFastBounds.join(fRect);
+ fPath->fFastBoundsIsDirty = false;
+ }
+ }
+
+private:
+ const SkPath* fPath;
+ SkRect fRect;
+ bool fDirty;
+ bool fEmpty;
+
+ // returns true if we should proceed
+ void init(const SkPath* path) {
+ fPath = path;
+ fDirty = path->fFastBoundsIsDirty;
+ fEmpty = path->isEmpty();
+ }
+};
+
+static void compute_fast_bounds(SkRect* bounds, const SkTDArray<SkPoint>& pts) {
+ if (pts.count() <= 1) { // we ignore just 1 point (moveto)
+ bounds->set(0, 0, 0, 0);
+ } else {
+ bounds->set(pts.begin(), pts.count());
+// SkDebugf("------- compute bounds %p %d", &pts, pts.count());
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////
+
/*
Stores the verbs and points as they are given to us, with exceptions:
- we only record "Close" if it was immediately preceeded by Line | Quad | Cubic
@@ -145,24 +202,8 @@ void SkPath::computeBounds(SkRect* bounds, BoundsType bt) const {
if (fFastBoundsIsDirty) {
fFastBoundsIsDirty = false;
- if (fPts.count() <= 1) { // we ignore just 1 point (moveto)
- fFastBounds.set(0, 0, 0, 0);
- } else {
- fFastBounds.set(fPts.begin(), fPts.count());
- }
+ compute_fast_bounds(&fFastBounds, fPts);
}
-#ifdef SK_DEBUG
- else { // check that our cache is correct
- SkRect r;
- if (fPts.count() <= 1) { // we ignore just 1 point (moveto)
- r.set(0, 0, 0, 0);
- } else {
- r.set(fPts.begin(), fPts.count());
- }
- SkASSERT(r == fFastBounds);
- }
-#endif
-
*bounds = fFastBounds;
}
@@ -286,13 +327,15 @@ void SkPath::close() {
}
///////////////////////////////////////////////////////////////////////////////
-
+
void SkPath::addRect(const SkRect& rect, Direction dir) {
this->addRect(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, dir);
}
void SkPath::addRect(SkScalar left, SkScalar top, SkScalar right,
SkScalar bottom, Direction dir) {
+ SkAutoPathBoundsUpdate apbu(this, left, top, right, bottom);
+
this->incReserve(5);
this->moveTo(left, top);
@@ -312,6 +355,8 @@ void SkPath::addRect(SkScalar left, SkScalar top, SkScalar right,
void SkPath::addRoundRect(const SkRect& rect, SkScalar rx, SkScalar ry,
Direction dir) {
+ SkAutoPathBoundsUpdate apbu(this, rect);
+
SkScalar w = rect.width();
SkScalar halfW = SkScalarHalf(w);
SkScalar h = rect.height();
@@ -395,7 +440,7 @@ void SkPath::addRoundRect(const SkRect& rect, SkScalar rx, SkScalar ry,
static void add_corner_arc(SkPath* path, const SkRect& rect,
SkScalar rx, SkScalar ry, int startAngle,
- bool ccw, bool forceMoveTo = false) {
+ SkPath::Direction dir, bool forceMoveTo) {
rx = SkMinScalar(SkScalarHalf(rect.width()), rx);
ry = SkMinScalar(SkScalarHalf(rect.height()), ry);
@@ -416,7 +461,7 @@ static void add_corner_arc(SkPath* path, const SkRect& rect,
SkScalar start = SkIntToScalar(startAngle);
SkScalar sweep = SkIntToScalar(90);
- if (ccw) {
+ if (SkPath::kCCW_Direction == dir) {
start += sweep;
sweep = -sweep;
}
@@ -426,26 +471,30 @@ static void add_corner_arc(SkPath* path, const SkRect& rect,
void SkPath::addRoundRect(const SkRect& rect, const SkScalar rad[],
Direction dir) {
+ SkAutoPathBoundsUpdate apbu(this, rect);
if (kCW_Direction == dir) {
- add_corner_arc(this, rect, rad[0], rad[1], 180, false, true);
- add_corner_arc(this, rect, rad[2], rad[3], 270, false);
- add_corner_arc(this, rect, rad[4], rad[5], 0, false);
- add_corner_arc(this, rect, rad[6], rad[7], 90, false);
+ add_corner_arc(this, rect, rad[0], rad[1], 180, dir, true);
+ add_corner_arc(this, rect, rad[2], rad[3], 270, dir, false);
+ add_corner_arc(this, rect, rad[4], rad[5], 0, dir, false);
+ add_corner_arc(this, rect, rad[6], rad[7], 90, dir, false);
} else {
- add_corner_arc(this, rect, rad[0], rad[1], 180, true, true);
- add_corner_arc(this, rect, rad[6], rad[7], 90, true);
- add_corner_arc(this, rect, rad[4], rad[5], 0, true);
- add_corner_arc(this, rect, rad[2], rad[3], 270, true);
+ add_corner_arc(this, rect, rad[0], rad[1], 180, dir, true);
+ add_corner_arc(this, rect, rad[6], rad[7], 90, dir, false);
+ add_corner_arc(this, rect, rad[4], rad[5], 0, dir, false);
+ add_corner_arc(this, rect, rad[2], rad[3], 270, dir, false);
}
+ this->close();
}
void SkPath::addOval(const SkRect& oval, Direction dir) {
+ SkAutoPathBoundsUpdate apbu(this, oval);
+
SkScalar cx = oval.centerX();
SkScalar cy = oval.centerY();
SkScalar rx = SkScalarHalf(oval.width());
SkScalar ry = SkScalarHalf(oval.height());
-#if 1 // these seem faster than using quads (1/2 the number of edges)
+#if 0 // these seem faster than using quads (1/2 the number of edges)
SkScalar sx = SkScalarMul(rx, CUBIC_ARC_FACTOR);
SkScalar sy = SkScalarMul(ry, CUBIC_ARC_FACTOR);
@@ -468,7 +517,7 @@ void SkPath::addOval(const SkRect& oval, Direction dir) {
SkScalar mx = SkScalarMul(rx, SK_ScalarRoot2Over2);
SkScalar my = SkScalarMul(ry, SK_ScalarRoot2Over2);
- this->incReserve(16);
+ this->incReserve(17); // 8 quads + close
this->moveTo(cx + rx, cy);
if (dir == kCCW_Direction) {
this->quadTo(cx + rx, cy - sy, cx + mx, cy - my);
@@ -547,7 +596,7 @@ void SkPath::addArc(const SkRect& oval, SkScalar startAngle,
if (oval.isEmpty() || 0 == sweepAngle) {
return;
}
-
+
const SkScalar kFullCircleAngle = SkIntToScalar(360);
if (sweepAngle >= kFullCircleAngle || sweepAngle <= -kFullCircleAngle) {
@@ -792,6 +841,7 @@ static void subdivide_cubic_to(SkPath* path, const SkPoint pts[4],
}
void SkPath::transform(const SkMatrix& matrix, SkPath* dst) const {
+ SkDEBUGCODE(this->validate();)
if (dst == NULL) {
dst = (SkPath*)this;
}
@@ -846,6 +896,7 @@ void SkPath::transform(const SkMatrix& matrix, SkPath* dst) const {
dst->fFillType = fFillType;
}
matrix.mapPoints(dst->fPts.begin(), fPts.begin(), fPts.count());
+ SkDEBUGCODE(dst->validate();)
}
}
@@ -1211,11 +1262,18 @@ void SkPath::toString(SkString* str) const {
void SkPath::validate() const {
SkASSERT(this != NULL);
SkASSERT((fFillType & ~3) == 0);
- if (!fFastBoundsIsDirty) {
- SkASSERT(fFastBounds.width() >= 0 && fFastBounds.height() >= 0);
- }
fPts.validate();
fVerbs.validate();
+
+ if (!fFastBoundsIsDirty) {
+ SkRect bounds;
+ compute_fast_bounds(&bounds, fPts);
+ // can't call contains(), since it returns false if the rect is empty
+ SkASSERT(fFastBounds.fLeft <= bounds.fLeft);
+ SkASSERT(fFastBounds.fTop <= bounds.fTop);
+ SkASSERT(fFastBounds.fRight >= bounds.fRight);
+ SkASSERT(fFastBounds.fBottom >= bounds.fBottom);
+ }
}
#if 0 // test to ensure that the iterator returns the same data as the path