summaryrefslogtreecommitdiffstats
path: root/skia/sgl/SkPath.cpp
diff options
context:
space:
mode:
authorinitial.commit <initial.commit@0039d316-1c4b-4281-b951-d872f2087c98>2008-07-27 00:09:42 +0000
committerinitial.commit <initial.commit@0039d316-1c4b-4281-b951-d872f2087c98>2008-07-27 00:09:42 +0000
commitae2c20f398933a9e86c387dcc465ec0f71065ffc (patch)
treede668b1411e2ee0b4e49b6d8f8b68183134ac990 /skia/sgl/SkPath.cpp
parent09911bf300f1a419907a9412154760efd0b7abc3 (diff)
downloadchromium_src-ae2c20f398933a9e86c387dcc465ec0f71065ffc.zip
chromium_src-ae2c20f398933a9e86c387dcc465ec0f71065ffc.tar.gz
chromium_src-ae2c20f398933a9e86c387dcc465ec0f71065ffc.tar.bz2
Add skia to the repository.
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@16 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'skia/sgl/SkPath.cpp')
-rw-r--r--skia/sgl/SkPath.cpp1369
1 files changed, 1369 insertions, 0 deletions
diff --git a/skia/sgl/SkPath.cpp b/skia/sgl/SkPath.cpp
new file mode 100644
index 0000000..afe8662
--- /dev/null
+++ b/skia/sgl/SkPath.cpp
@@ -0,0 +1,1369 @@
+/* libs/graphics/sgl/SkPath.cpp
+**
+** Copyright 2006, Google Inc.
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "SkPath.h"
+#include "SkFlattenable.h"
+#include "SkMath.h"
+
+/*
+ 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
+ - we insert a Move(0,0) if Line | Quad | Cubic is our first command
+
+ The iterator does more cleanup, especially if forceClose == true
+ 1. if we encounter Close, return a cons'd up Line() first (if the curr-pt != start-pt)
+ 2. if we encounter Move without a preceeding Close, and forceClose is true, goto #1
+ 3. if we encounter Line | Quad | Cubic after Close, cons up a Move
+*/
+
+////////////////////////////////////////////////////////////////////////////
+
+SkPath::SkPath() : fFastBoundsIsDirty(true), fFillType(kWinding_FillType) {}
+
+SkPath::SkPath(const SkPath& src) {
+ SkDEBUGCODE(src.validate();)
+ *this = src;
+}
+
+SkPath::~SkPath() {
+ SkDEBUGCODE(this->validate();)
+}
+
+SkPath& SkPath::operator=(const SkPath& src) {
+ SkDEBUGCODE(src.validate();)
+
+ if (this != &src) {
+ fFastBounds = src.fFastBounds;
+ fPts = src.fPts;
+ fVerbs = src.fVerbs;
+ fFillType = src.fFillType;
+ fFastBoundsIsDirty = src.fFastBoundsIsDirty;
+ }
+ SkDEBUGCODE(this->validate();)
+ return *this;
+}
+
+void SkPath::swap(SkPath& other) {
+ SkASSERT(&other != NULL);
+
+ if (this != &other) {
+ SkTSwap<SkRect>(fFastBounds, other.fFastBounds);
+ fPts.swap(other.fPts);
+ fVerbs.swap(other.fVerbs);
+ SkTSwap<uint8_t>(fFillType, other.fFillType);
+ SkTSwap<uint8_t>(fFastBoundsIsDirty, other.fFastBoundsIsDirty);
+ }
+}
+
+void SkPath::reset() {
+ SkDEBUGCODE(this->validate();)
+
+ fPts.reset();
+ fVerbs.reset();
+ fFastBoundsIsDirty = true;
+}
+
+void SkPath::rewind() {
+ SkDEBUGCODE(this->validate();)
+
+ fPts.rewind();
+ fVerbs.rewind();
+ fFastBoundsIsDirty = true;
+}
+
+bool SkPath::isEmpty() const {
+ SkDEBUGCODE(this->validate();)
+
+ int count = fVerbs.count();
+ return count == 0 || (count == 1 && fVerbs[0] == kMove_Verb);
+}
+
+bool SkPath::isRect(SkRect*) const {
+ SkDEBUGCODE(this->validate();)
+
+ SkASSERT(!"unimplemented");
+ return false;
+}
+
+int SkPath::getPoints(SkPoint copy[], int max) const {
+ SkDEBUGCODE(this->validate();)
+
+ SkASSERT(max >= 0);
+ int count = fPts.count();
+ if (copy && max > 0 && count > 0) {
+ memcpy(copy, fPts.begin(), sizeof(SkPoint) * SkMin32(max, count));
+ }
+ return count;
+}
+
+void SkPath::getLastPt(SkPoint* lastPt) const {
+ SkDEBUGCODE(this->validate();)
+
+ if (lastPt) {
+ int count = fPts.count();
+ if (count == 0) {
+ lastPt->set(0, 0);
+ } else {
+ *lastPt = fPts[count - 1];
+ }
+ }
+}
+
+void SkPath::setLastPt(SkScalar x, SkScalar y) {
+ SkDEBUGCODE(this->validate();)
+
+ int count = fPts.count();
+ if (count == 0) {
+ this->moveTo(x, y);
+ } else {
+ fPts[count - 1].set(x, y);
+ }
+}
+
+#define ALWAYS_FAST_BOUNDS_FOR_NOW true
+
+void SkPath::computeBounds(SkRect* bounds, BoundsType bt) const {
+ SkDEBUGCODE(this->validate();)
+
+ SkASSERT(bounds);
+
+ // we BoundsType for now
+
+ 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());
+ }
+ }
+#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;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// Construction methods
+
+void SkPath::incReserve(U16CPU inc) {
+ SkDEBUGCODE(this->validate();)
+
+ fVerbs.setReserve(fVerbs.count() + inc);
+ fPts.setReserve(fPts.count() + inc);
+
+ SkDEBUGCODE(this->validate();)
+}
+
+void SkPath::moveTo(SkScalar x, SkScalar y) {
+ SkDEBUGCODE(this->validate();)
+
+ int vc = fVerbs.count();
+ SkPoint* pt;
+
+ if (vc > 0 && fVerbs[vc - 1] == kMove_Verb) {
+ pt = &fPts[fPts.count() - 1];
+ } else {
+ pt = fPts.append();
+ *fVerbs.append() = kMove_Verb;
+ }
+ pt->set(x, y);
+
+ fFastBoundsIsDirty = true;
+}
+
+void SkPath::rMoveTo(SkScalar x, SkScalar y) {
+ SkPoint pt;
+ this->getLastPt(&pt);
+ this->moveTo(pt.fX + x, pt.fY + y);
+}
+
+void SkPath::lineTo(SkScalar x, SkScalar y) {
+ SkDEBUGCODE(this->validate();)
+
+ if (fVerbs.count() == 0) {
+ fPts.append()->set(0, 0);
+ *fVerbs.append() = kMove_Verb;
+ }
+ fPts.append()->set(x, y);
+ *fVerbs.append() = kLine_Verb;
+
+ fFastBoundsIsDirty = true;
+}
+
+void SkPath::rLineTo(SkScalar x, SkScalar y) {
+ SkPoint pt;
+ this->getLastPt(&pt);
+ this->lineTo(pt.fX + x, pt.fY + y);
+}
+
+void SkPath::quadTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2) {
+ SkDEBUGCODE(this->validate();)
+
+ if (fVerbs.count() == 0) {
+ fPts.append()->set(0, 0);
+ *fVerbs.append() = kMove_Verb;
+ }
+
+ SkPoint* pts = fPts.append(2);
+ pts[0].set(x1, y1);
+ pts[1].set(x2, y2);
+ *fVerbs.append() = kQuad_Verb;
+
+ fFastBoundsIsDirty = true;
+}
+
+void SkPath::rQuadTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2) {
+ SkPoint pt;
+ this->getLastPt(&pt);
+ this->quadTo(pt.fX + x1, pt.fY + y1, pt.fX + x2, pt.fY + y2);
+}
+
+void SkPath::cubicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2,
+ SkScalar x3, SkScalar y3) {
+ SkDEBUGCODE(this->validate();)
+
+ if (fVerbs.count() == 0) {
+ fPts.append()->set(0, 0);
+ *fVerbs.append() = kMove_Verb;
+ }
+ SkPoint* pts = fPts.append(3);
+ pts[0].set(x1, y1);
+ pts[1].set(x2, y2);
+ pts[2].set(x3, y3);
+ *fVerbs.append() = kCubic_Verb;
+
+ fFastBoundsIsDirty = true;
+}
+
+void SkPath::rCubicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2,
+ SkScalar x3, SkScalar y3) {
+ SkPoint pt;
+ this->getLastPt(&pt);
+ this->cubicTo(pt.fX + x1, pt.fY + y1, pt.fX + x2, pt.fY + y2,
+ pt.fX + x3, pt.fY + y3);
+}
+
+void SkPath::close() {
+ SkDEBUGCODE(this->validate();)
+
+ int count = fVerbs.count();
+ if (count > 0) {
+ switch (fVerbs[count - 1]) {
+ case kLine_Verb:
+ case kQuad_Verb:
+ case kCubic_Verb:
+ *fVerbs.append() = kClose_Verb;
+ break;
+ default:
+ // don't add a close if the prev wasn't a primitive
+ break;
+ }
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+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) {
+ this->incReserve(5);
+
+ this->moveTo(left, top);
+ if (dir == kCCW_Direction) {
+ this->lineTo(left, bottom);
+ this->lineTo(right, bottom);
+ this->lineTo(right, top);
+ } else {
+ this->lineTo(right, top);
+ this->lineTo(right, bottom);
+ this->lineTo(left, bottom);
+ }
+ this->close();
+}
+
+#define CUBIC_ARC_FACTOR ((SK_ScalarSqrt2 - SK_Scalar1) * 4 / 3)
+
+void SkPath::addRoundRect(const SkRect& rect, SkScalar rx, SkScalar ry,
+ Direction dir) {
+ SkScalar w = rect.width();
+ SkScalar halfW = SkScalarHalf(w);
+ SkScalar h = rect.height();
+ SkScalar halfH = SkScalarHalf(h);
+
+ if (halfW <= 0 || halfH <= 0) {
+ return;
+ }
+
+ bool skip_hori = rx >= halfW;
+ bool skip_vert = ry >= halfH;
+
+ if (skip_hori && skip_vert) {
+ this->addOval(rect, dir);
+ return;
+ }
+ if (skip_hori) {
+ rx = halfW;
+ } else if (skip_vert) {
+ ry = halfH;
+ }
+
+ SkScalar sx = SkScalarMul(rx, CUBIC_ARC_FACTOR);
+ SkScalar sy = SkScalarMul(ry, CUBIC_ARC_FACTOR);
+
+ this->incReserve(17);
+ this->moveTo(rect.fRight - rx, rect.fTop);
+ if (dir == kCCW_Direction) {
+ if (!skip_hori) {
+ this->lineTo(rect.fLeft + rx, rect.fTop); // top
+ }
+ this->cubicTo(rect.fLeft + rx - sx, rect.fTop,
+ rect.fLeft, rect.fTop + ry - sy,
+ rect.fLeft, rect.fTop + ry); // top-left
+ if (!skip_vert) {
+ this->lineTo(rect.fLeft, rect.fBottom - ry); // left
+ }
+ this->cubicTo(rect.fLeft, rect.fBottom - ry + sy,
+ rect.fLeft + rx - sx, rect.fBottom,
+ rect.fLeft + rx, rect.fBottom); // bot-left
+ if (!skip_hori) {
+ this->lineTo(rect.fRight - rx, rect.fBottom); // bottom
+ }
+ this->cubicTo(rect.fRight - rx + sx, rect.fBottom,
+ rect.fRight, rect.fBottom - ry + sy,
+ rect.fRight, rect.fBottom - ry); // bot-right
+ if (!skip_vert) {
+ this->lineTo(rect.fRight, rect.fTop + ry);
+ }
+ this->cubicTo(rect.fRight, rect.fTop + ry - sy,
+ rect.fRight - rx + sx, rect.fTop,
+ rect.fRight - rx, rect.fTop); // top-right
+ } else {
+ this->cubicTo(rect.fRight - rx + sx, rect.fTop,
+ rect.fRight, rect.fTop + ry - sy,
+ rect.fRight, rect.fTop + ry); // top-right
+ if (!skip_vert) {
+ this->lineTo(rect.fRight, rect.fBottom - ry);
+ }
+ this->cubicTo(rect.fRight, rect.fBottom - ry + sy,
+ rect.fRight - rx + sx, rect.fBottom,
+ rect.fRight - rx, rect.fBottom); // bot-right
+ if (!skip_hori) {
+ this->lineTo(rect.fLeft + rx, rect.fBottom); // bottom
+ }
+ this->cubicTo(rect.fLeft + rx - sx, rect.fBottom,
+ rect.fLeft, rect.fBottom - ry + sy,
+ rect.fLeft, rect.fBottom - ry); // bot-left
+ if (!skip_vert) {
+ this->lineTo(rect.fLeft, rect.fTop + ry); // left
+ }
+ this->cubicTo(rect.fLeft, rect.fTop + ry - sy,
+ rect.fLeft + rx - sx, rect.fTop,
+ rect.fLeft + rx, rect.fTop); // top-left
+ if (!skip_hori) {
+ this->lineTo(rect.fRight - rx, rect.fTop); // top
+ }
+ }
+ this->close();
+}
+
+static void add_corner_arc(SkPath* path, const SkRect& rect,
+ SkScalar rx, SkScalar ry, int startAngle,
+ bool ccw, bool forceMoveTo = false) {
+ rx = SkMinScalar(SkScalarHalf(rect.width()), rx);
+ ry = SkMinScalar(SkScalarHalf(rect.height()), ry);
+
+ SkRect r;
+ r.set(-rx, -ry, rx, ry);
+
+ switch (startAngle) {
+ case 0:
+ r.offset(rect.fRight - r.fRight, rect.fBottom - r.fBottom);
+ break;
+ case 90:
+ r.offset(rect.fLeft - r.fLeft, rect.fBottom - r.fBottom);
+ break;
+ case 180: r.offset(rect.fLeft - r.fLeft, rect.fTop - r.fTop); break;
+ case 270: r.offset(rect.fRight - r.fRight, rect.fTop - r.fTop); break;
+ default: SkASSERT(!"unexpected startAngle in add_corner_arc");
+ }
+
+ SkScalar start = SkIntToScalar(startAngle);
+ SkScalar sweep = SkIntToScalar(90);
+ if (ccw) {
+ start += sweep;
+ sweep = -sweep;
+ }
+
+ path->arcTo(r, start, sweep, forceMoveTo);
+}
+
+void SkPath::addRoundRect(const SkRect& rect, const SkScalar rad[],
+ Direction dir) {
+
+ 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);
+ } 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);
+ }
+}
+
+void SkPath::addOval(const SkRect& oval, Direction dir) {
+ 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)
+ SkScalar sx = SkScalarMul(rx, CUBIC_ARC_FACTOR);
+ SkScalar sy = SkScalarMul(ry, CUBIC_ARC_FACTOR);
+
+ this->incReserve(13);
+ this->moveTo(cx + rx, cy);
+ if (dir == kCCW_Direction) {
+ this->cubicTo(cx + rx, cy - sy, cx + sx, cy - ry, cx, cy - ry);
+ this->cubicTo(cx - sx, cy - ry, cx - rx, cy - sy, cx - rx, cy);
+ this->cubicTo(cx - rx, cy + sy, cx - sx, cy + ry, cx, cy + ry);
+ this->cubicTo(cx + sx, cy + ry, cx + rx, cy + sy, cx + rx, cy);
+ } else {
+ this->cubicTo(cx + rx, cy + sy, cx + sx, cy + ry, cx, cy + ry);
+ this->cubicTo(cx - sx, cy + ry, cx - rx, cy + sy, cx - rx, cy);
+ this->cubicTo(cx - rx, cy - sy, cx - sx, cy - ry, cx, cy - ry);
+ this->cubicTo(cx + sx, cy - ry, cx + rx, cy - sy, cx + rx, cy);
+ }
+#else
+ SkScalar sx = SkScalarMul(rx, SK_ScalarTanPIOver8);
+ SkScalar sy = SkScalarMul(ry, SK_ScalarTanPIOver8);
+ SkScalar mx = SkScalarMul(rx, SK_ScalarRoot2Over2);
+ SkScalar my = SkScalarMul(ry, SK_ScalarRoot2Over2);
+
+ this->incReserve(16);
+ this->moveTo(cx + rx, cy);
+ if (dir == kCCW_Direction) {
+ this->quadTo(cx + rx, cy - sy, cx + mx, cy - my);
+ this->quadTo(cx + sx, cy - ry, cx + 0, cy - ry);
+ this->quadTo(cx - sx, cy - ry, cx - mx, cy - my);
+ this->quadTo(cx - rx, cy - sy, cx - rx, cy - 0);
+ this->quadTo(cx - rx, cy + sy, cx - mx, cy + my);
+ this->quadTo(cx - sx, cy + ry, cx - 0, cy + ry);
+ this->quadTo(cx + sx, cy + ry, cx + mx, cy + my);
+ this->quadTo(cx + rx, cy + sy, cx + rx, cy + 0);
+ } else {
+ this->quadTo(cx + rx, cy + sy, cx + mx, cy + my);
+ this->quadTo(cx + sx, cy + ry, cx - 0, cy + ry);
+ this->quadTo(cx - sx, cy + ry, cx - mx, cy + my);
+ this->quadTo(cx - rx, cy + sy, cx - rx, cy - 0);
+ this->quadTo(cx - rx, cy - sy, cx - mx, cy - my);
+ this->quadTo(cx - sx, cy - ry, cx + 0, cy - ry);
+ this->quadTo(cx + sx, cy - ry, cx + mx, cy - my);
+ this->quadTo(cx + rx, cy - sy, cx + rx, cy + 0);
+ }
+#endif
+ this->close();
+}
+
+void SkPath::addCircle(SkScalar x, SkScalar y, SkScalar r, Direction dir) {
+ if (r > 0) {
+ SkRect rect;
+ rect.set(x - r, y - r, x + r, y + r);
+ this->addOval(rect, dir);
+ }
+}
+
+#include "SkGeometry.h"
+
+static int build_arc_points(const SkRect& oval, SkScalar startAngle,
+ SkScalar sweepAngle,
+ SkPoint pts[kSkBuildQuadArcStorage]) {
+ SkVector start, stop;
+
+ start.fY = SkScalarSinCos(SkDegreesToRadians(startAngle), &start.fX);
+ stop.fY = SkScalarSinCos(SkDegreesToRadians(startAngle + sweepAngle),
+ &stop.fX);
+
+ SkMatrix matrix;
+
+ matrix.setScale(SkScalarHalf(oval.width()), SkScalarHalf(oval.height()));
+ matrix.postTranslate(oval.centerX(), oval.centerY());
+
+ return SkBuildQuadArc(start, stop,
+ sweepAngle > 0 ? kCW_SkRotationDirection : kCCW_SkRotationDirection,
+ &matrix, pts);
+}
+
+void SkPath::arcTo(const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle,
+ bool forceMoveTo) {
+ if (oval.width() < 0 || oval.height() < 0) {
+ return;
+ }
+
+ SkPoint pts[kSkBuildQuadArcStorage];
+ int count = build_arc_points(oval, startAngle, sweepAngle, pts);
+ SkASSERT((count & 1) == 1);
+
+ if (fVerbs.count() == 0) {
+ forceMoveTo = true;
+ }
+ this->incReserve(count);
+ forceMoveTo ? this->moveTo(pts[0]) : this->lineTo(pts[0]);
+ for (int i = 1; i < count; i += 2) {
+ this->quadTo(pts[i], pts[i+1]);
+ }
+}
+
+void SkPath::addArc(const SkRect& oval, SkScalar startAngle,
+ SkScalar sweepAngle) {
+ if (oval.isEmpty() || 0 == sweepAngle) {
+ return;
+ }
+
+ const SkScalar kFullCircleAngle = SkIntToScalar(360);
+
+ if (sweepAngle >= kFullCircleAngle || sweepAngle <= -kFullCircleAngle) {
+ this->addOval(oval, sweepAngle > 0 ? kCW_Direction : kCCW_Direction);
+ return;
+ }
+
+ SkPoint pts[kSkBuildQuadArcStorage];
+ int count = build_arc_points(oval, startAngle, sweepAngle, pts);
+
+ this->incReserve(count);
+ this->moveTo(pts[0]);
+ for (int i = 1; i < count; i += 2) {
+ this->quadTo(pts[i], pts[i+1]);
+ }
+}
+
+/*
+ Need to handle the case when the angle is sharp, and our computed end-points
+ for the arc go behind pt1 and/or p2...
+*/
+void SkPath::arcTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2,
+ SkScalar radius) {
+ SkVector before, after;
+
+ // need to know our prev pt so we can construct tangent vectors
+ {
+ SkPoint start;
+ this->getLastPt(&start);
+ before.setNormalize(x1 - start.fX, y1 - start.fY);
+ after.setNormalize(x2 - x1, y2 - y1);
+ }
+
+ SkScalar cosh = SkPoint::DotProduct(before, after);
+ SkScalar sinh = SkPoint::CrossProduct(before, after);
+
+ if (SkScalarNearlyZero(sinh)) { // angle is too tight
+ return;
+ }
+
+ SkScalar dist = SkScalarMulDiv(radius, SK_Scalar1 - cosh, sinh);
+ if (dist < 0) {
+ dist = -dist;
+ }
+
+ SkScalar xx = x1 - SkScalarMul(dist, before.fX);
+ SkScalar yy = y1 - SkScalarMul(dist, before.fY);
+ SkRotationDirection arcDir;
+
+ // now turn before/after into normals
+ if (sinh > 0) {
+ before.rotateCCW();
+ after.rotateCCW();
+ arcDir = kCW_SkRotationDirection;
+ } else {
+ before.rotateCW();
+ after.rotateCW();
+ arcDir = kCCW_SkRotationDirection;
+ }
+
+ SkMatrix matrix;
+ SkPoint pts[kSkBuildQuadArcStorage];
+
+ matrix.setScale(radius, radius);
+ matrix.postTranslate(xx - SkScalarMul(radius, before.fX),
+ yy - SkScalarMul(radius, before.fY));
+
+ int count = SkBuildQuadArc(before, after, arcDir, &matrix, pts);
+
+ this->incReserve(count);
+ // [xx,yy] == pts[0]
+ this->lineTo(xx, yy);
+ for (int i = 1; i < count; i += 2) {
+ this->quadTo(pts[i], pts[i+1]);
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void SkPath::addPath(const SkPath& path, SkScalar dx, SkScalar dy) {
+ SkMatrix matrix;
+
+ matrix.setTranslate(dx, dy);
+ this->addPath(path, matrix);
+}
+
+void SkPath::addPath(const SkPath& path, const SkMatrix& matrix) {
+ this->incReserve(path.fPts.count());
+
+ Iter iter(path, false);
+ SkPoint pts[4];
+ Verb verb;
+
+ SkMatrix::MapPtsProc proc = matrix.getMapPtsProc();
+
+ while ((verb = iter.next(pts)) != kDone_Verb) {
+ switch (verb) {
+ case kMove_Verb:
+ proc(matrix, &pts[0], &pts[0], 1);
+ this->moveTo(pts[0]);
+ break;
+ case kLine_Verb:
+ proc(matrix, &pts[1], &pts[1], 1);
+ this->lineTo(pts[1]);
+ break;
+ case kQuad_Verb:
+ proc(matrix, &pts[1], &pts[1], 2);
+ this->quadTo(pts[1], pts[2]);
+ break;
+ case kCubic_Verb:
+ proc(matrix, &pts[1], &pts[1], 3);
+ this->cubicTo(pts[1], pts[2], pts[3]);
+ break;
+ case kClose_Verb:
+ this->close();
+ break;
+ default:
+ SkASSERT(!"unknown verb");
+ }
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static const uint8_t gPtsInVerb[] = {
+ 1, // kMove
+ 1, // kLine
+ 2, // kQuad
+ 3, // kCubic
+ 0, // kClose
+ 0 // kDone
+};
+
+// ignore the initial moveto, and stop when the 1st contour ends
+void SkPath::pathTo(const SkPath& path) {
+ int i, vcount = path.fVerbs.count();
+ if (vcount == 0) {
+ return;
+ }
+
+ this->incReserve(vcount);
+
+ const uint8_t* verbs = path.fVerbs.begin();
+ const SkPoint* pts = path.fPts.begin() + 1; // 1 for the initial moveTo
+
+ SkASSERT(verbs[0] == kMove_Verb);
+ for (i = 1; i < vcount; i++) {
+ switch (verbs[i]) {
+ case kLine_Verb:
+ this->lineTo(pts[0].fX, pts[0].fY);
+ break;
+ case kQuad_Verb:
+ this->quadTo(pts[0].fX, pts[0].fY, pts[1].fX, pts[1].fY);
+ break;
+ case kCubic_Verb:
+ this->cubicTo(pts[0].fX, pts[0].fY, pts[1].fX, pts[1].fY,
+ pts[2].fX, pts[2].fY);
+ break;
+ case kClose_Verb:
+ return;
+ }
+ pts += gPtsInVerb[verbs[i]];
+ }
+}
+
+// ignore the last point of the 1st contour
+void SkPath::reversePathTo(const SkPath& path) {
+ int i, vcount = path.fVerbs.count();
+ if (vcount == 0) {
+ return;
+ }
+
+ this->incReserve(vcount);
+
+ const uint8_t* verbs = path.fVerbs.begin();
+ const SkPoint* pts = path.fPts.begin();
+
+ SkASSERT(verbs[0] == kMove_Verb);
+ for (i = 1; i < vcount; i++) {
+ int n = gPtsInVerb[verbs[i]];
+ if (n == 0) {
+ break;
+ }
+ pts += n;
+ }
+
+ while (--i > 0) {
+ switch (verbs[i]) {
+ case kLine_Verb:
+ this->lineTo(pts[-1].fX, pts[-1].fY);
+ break;
+ case kQuad_Verb:
+ this->quadTo(pts[-1].fX, pts[-1].fY, pts[-2].fX, pts[-2].fY);
+ break;
+ case kCubic_Verb:
+ this->cubicTo(pts[-1].fX, pts[-1].fY, pts[-2].fX, pts[-2].fY,
+ pts[-3].fX, pts[-3].fY);
+ break;
+ default:
+ SkASSERT(!"bad verb");
+ break;
+ }
+ pts -= gPtsInVerb[verbs[i]];
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void SkPath::offset(SkScalar dx, SkScalar dy, SkPath* dst) const {
+ SkMatrix matrix;
+
+ matrix.setTranslate(dx, dy);
+ this->transform(matrix, dst);
+}
+
+#include "SkGeometry.h"
+
+static void subdivide_quad_to(SkPath* path, const SkPoint pts[3],
+ int level = 2) {
+ if (--level >= 0) {
+ SkPoint tmp[5];
+
+ SkChopQuadAtHalf(pts, tmp);
+ subdivide_quad_to(path, &tmp[0], level);
+ subdivide_quad_to(path, &tmp[2], level);
+ } else {
+ path->quadTo(pts[1], pts[2]);
+ }
+}
+
+static void subdivide_cubic_to(SkPath* path, const SkPoint pts[4],
+ int level = 2) {
+ if (--level >= 0) {
+ SkPoint tmp[7];
+
+ SkChopCubicAtHalf(pts, tmp);
+ subdivide_cubic_to(path, &tmp[0], level);
+ subdivide_cubic_to(path, &tmp[3], level);
+ } else {
+ path->cubicTo(pts[1], pts[2], pts[3]);
+ }
+}
+
+void SkPath::transform(const SkMatrix& matrix, SkPath* dst) const {
+ if (dst == NULL) {
+ dst = (SkPath*)this;
+ }
+
+ if (matrix.getType() & SkMatrix::kPerspective_Mask) {
+ SkPath tmp;
+ tmp.fFillType = fFillType;
+
+ SkPath::Iter iter(*this, false);
+ SkPoint pts[4];
+ SkPath::Verb verb;
+
+ while ((verb = iter.next(pts)) != kDone_Verb) {
+ switch (verb) {
+ case kMove_Verb:
+ tmp.moveTo(pts[0]);
+ break;
+ case kLine_Verb:
+ tmp.lineTo(pts[1]);
+ break;
+ case kQuad_Verb:
+ subdivide_quad_to(&tmp, pts);
+ break;
+ case kCubic_Verb:
+ subdivide_cubic_to(&tmp, pts);
+ break;
+ case kClose_Verb:
+ tmp.close();
+ break;
+ default:
+ SkASSERT(!"unknown verb");
+ break;
+ }
+ }
+
+ dst->swap(tmp);
+ matrix.mapPoints(dst->fPts.begin(), dst->fPts.count());
+ } else {
+ // remember that dst might == this, so be sure to check
+ // fFastBoundsIsDirty before we set it
+ if (!fFastBoundsIsDirty && matrix.rectStaysRect() && fPts.count() > 1) {
+ // if we're empty, fastbounds should not be mapped
+ matrix.mapRect(&dst->fFastBounds, fFastBounds);
+ dst->fFastBoundsIsDirty = false;
+ } else {
+ dst->fFastBoundsIsDirty = true;
+ }
+
+ if (this != dst) {
+ dst->fVerbs = fVerbs;
+ dst->fPts.setCount(fPts.count());
+ dst->fFillType = fFillType;
+ }
+ matrix.mapPoints(dst->fPts.begin(), fPts.begin(), fPts.count());
+ }
+}
+
+void SkPath::updateBoundsCache() const {
+ if (fFastBoundsIsDirty) {
+ SkRect r;
+ this->computeBounds(&r, kFast_BoundsType);
+ SkASSERT(!fFastBoundsIsDirty);
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+enum NeedMoveToState {
+ kAfterClose_NeedMoveToState,
+ kAfterCons_NeedMoveToState,
+ kAfterPrefix_NeedMoveToState
+};
+
+SkPath::Iter::Iter() {
+#ifdef SK_DEBUG
+ fPts = NULL;
+ fMoveTo.fX = fMoveTo.fY = fLastPt.fX = fLastPt.fY = 0;
+ fForceClose = fNeedMoveTo = fCloseLine = false;
+#endif
+ // need to init enough to make next() harmlessly return kDone_Verb
+ fVerbs = NULL;
+ fVerbStop = NULL;
+ fNeedClose = false;
+}
+
+SkPath::Iter::Iter(const SkPath& path, bool forceClose) {
+ this->setPath(path, forceClose);
+}
+
+void SkPath::Iter::setPath(const SkPath& path, bool forceClose) {
+ fPts = path.fPts.begin();
+ fVerbs = path.fVerbs.begin();
+ fVerbStop = path.fVerbs.end();
+ fForceClose = SkToU8(forceClose);
+ fNeedClose = false;
+ fNeedMoveTo = kAfterPrefix_NeedMoveToState;
+}
+
+bool SkPath::Iter::isClosedContour() const {
+ if (fVerbs == NULL || fVerbs == fVerbStop) {
+ return false;
+ }
+ if (fForceClose) {
+ return true;
+ }
+
+ const uint8_t* verbs = fVerbs;
+ const uint8_t* stop = fVerbStop;
+
+ if (kMove_Verb == *verbs) {
+ verbs += 1; // skip the initial moveto
+ }
+
+ while (verbs < stop) {
+ unsigned v = *verbs++;
+ if (kMove_Verb == v) {
+ break;
+ }
+ if (kClose_Verb == v) {
+ return true;
+ }
+ }
+ return false;
+}
+
+SkPath::Verb SkPath::Iter::autoClose(SkPoint pts[2]) {
+ if (fLastPt != fMoveTo) {
+ if (pts) {
+ pts[0] = fLastPt;
+ pts[1] = fMoveTo;
+ }
+ fLastPt = fMoveTo;
+ fCloseLine = true;
+ return kLine_Verb;
+ }
+ return kClose_Verb;
+}
+
+bool SkPath::Iter::cons_moveTo(SkPoint pts[1]) {
+ if (fNeedMoveTo == kAfterClose_NeedMoveToState) {
+ if (pts) {
+ *pts = fMoveTo;
+ }
+ fNeedClose = fForceClose;
+ fNeedMoveTo = kAfterCons_NeedMoveToState;
+ fVerbs -= 1;
+ return true;
+ }
+
+ if (fNeedMoveTo == kAfterCons_NeedMoveToState) {
+ if (pts) {
+ *pts = fMoveTo;
+ }
+ fNeedMoveTo = kAfterPrefix_NeedMoveToState;
+ } else {
+ SkASSERT(fNeedMoveTo == kAfterPrefix_NeedMoveToState);
+ if (pts) {
+ *pts = fPts[-1];
+ }
+ }
+ return false;
+}
+
+SkPath::Verb SkPath::Iter::next(SkPoint pts[4]) {
+ if (fVerbs == fVerbStop) {
+ if (fNeedClose) {
+ if (kLine_Verb == this->autoClose(pts)) {
+ return kLine_Verb;
+ }
+ fNeedClose = false;
+ return kClose_Verb;
+ }
+ return kDone_Verb;
+ }
+
+ unsigned verb = *fVerbs++;
+ const SkPoint* srcPts = fPts;
+
+ switch (verb) {
+ case kMove_Verb:
+ if (fNeedClose) {
+ fVerbs -= 1;
+ verb = this->autoClose(pts);
+ if (verb == kClose_Verb) {
+ fNeedClose = false;
+ }
+ return (Verb)verb;
+ }
+ if (fVerbs == fVerbStop) { // might be a trailing moveto
+ return kDone_Verb;
+ }
+ fMoveTo = *srcPts;
+ if (pts) {
+ pts[0] = *srcPts;
+ }
+ srcPts += 1;
+ fNeedMoveTo = kAfterCons_NeedMoveToState;
+ fNeedClose = fForceClose;
+ break;
+ case kLine_Verb:
+ if (this->cons_moveTo(pts)) {
+ return kMove_Verb;
+ }
+ if (pts) {
+ pts[1] = srcPts[0];
+ }
+ fLastPt = srcPts[0];
+ fCloseLine = false;
+ srcPts += 1;
+ break;
+ case kQuad_Verb:
+ if (this->cons_moveTo(pts)) {
+ return kMove_Verb;
+ }
+ if (pts) {
+ memcpy(&pts[1], srcPts, 2 * sizeof(SkPoint));
+ }
+ fLastPt = srcPts[1];
+ srcPts += 2;
+ break;
+ case kCubic_Verb:
+ if (this->cons_moveTo(pts)) {
+ return kMove_Verb;
+ }
+ if (pts) {
+ memcpy(&pts[1], srcPts, 3 * sizeof(SkPoint));
+ }
+ fLastPt = srcPts[2];
+ srcPts += 3;
+ break;
+ case kClose_Verb:
+ verb = this->autoClose(pts);
+ if (verb == kLine_Verb) {
+ fVerbs -= 1;
+ } else {
+ fNeedClose = false;
+ }
+ fNeedMoveTo = kAfterClose_NeedMoveToState;
+ break;
+ }
+ fPts = srcPts;
+ return (Verb)verb;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static bool exceeds_dist(const SkScalar p[], const SkScalar q[], SkScalar dist,
+ int count) {
+ SkASSERT(dist > 0);
+
+ count *= 2;
+ for (int i = 0; i < count; i++) {
+ if (SkScalarAbs(p[i] - q[i]) > dist) {
+ return true;
+ }
+ }
+ return false;
+}
+
+static void subdivide_quad(SkPath* dst, const SkPoint pts[3], SkScalar dist,
+ int subLevel = 4) {
+ if (--subLevel >= 0 && exceeds_dist(&pts[0].fX, &pts[1].fX, dist, 4)) {
+ SkPoint tmp[5];
+ SkChopQuadAtHalf(pts, tmp);
+
+ subdivide_quad(dst, &tmp[0], dist, subLevel);
+ subdivide_quad(dst, &tmp[2], dist, subLevel);
+ } else {
+ dst->quadTo(pts[1], pts[2]);
+ }
+}
+
+static void subdivide_cubic(SkPath* dst, const SkPoint pts[4], SkScalar dist,
+ int subLevel = 4) {
+ if (--subLevel >= 0 && exceeds_dist(&pts[0].fX, &pts[1].fX, dist, 6)) {
+ SkPoint tmp[7];
+ SkChopCubicAtHalf(pts, tmp);
+
+ subdivide_cubic(dst, &tmp[0], dist, subLevel);
+ subdivide_cubic(dst, &tmp[3], dist, subLevel);
+ } else {
+ dst->cubicTo(pts[1], pts[2], pts[3]);
+ }
+}
+
+void SkPath::subdivide(SkScalar dist, bool bendLines, SkPath* dst) const {
+ SkPath tmpPath;
+ if (NULL == dst || this == dst) {
+ dst = &tmpPath;
+ }
+
+ SkPath::Iter iter(*this, false);
+ SkPoint pts[4];
+
+ for (;;) {
+ switch (iter.next(pts)) {
+ case SkPath::kMove_Verb:
+ dst->moveTo(pts[0]);
+ break;
+ case SkPath::kLine_Verb:
+ if (!bendLines) {
+ dst->lineTo(pts[1]);
+ break;
+ }
+ // construct a quad from the line
+ pts[2] = pts[1];
+ pts[1].set(SkScalarAve(pts[0].fX, pts[2].fX),
+ SkScalarAve(pts[0].fY, pts[2].fY));
+ // fall through to the quad case
+ case SkPath::kQuad_Verb:
+ subdivide_quad(dst, pts, dist);
+ break;
+ case SkPath::kCubic_Verb:
+ subdivide_cubic(dst, pts, dist);
+ break;
+ case SkPath::kClose_Verb:
+ dst->close();
+ break;
+ case SkPath::kDone_Verb:
+ goto DONE;
+ }
+ }
+DONE:
+ if (&tmpPath == dst) { // i.e. the dst should be us
+ dst->swap(*(SkPath*)this);
+ }
+}
+
+///////////////////////////////////////////////////////////////////////
+/*
+ Format in flattened buffer: [ptCount, verbCount, pts[], verbs[]]
+*/
+
+void SkPath::flatten(SkFlattenableWriteBuffer& buffer) const {
+ SkDEBUGCODE(this->validate();)
+
+ buffer.write32(fPts.count());
+ buffer.write32(fVerbs.count());
+ buffer.write32(fFillType);
+ buffer.writeMul4(fPts.begin(), sizeof(SkPoint) * fPts.count());
+ buffer.writePad(fVerbs.begin(), fVerbs.count());
+}
+
+void SkPath::unflatten(SkFlattenableReadBuffer& buffer) {
+ fPts.setCount(buffer.readS32());
+ fVerbs.setCount(buffer.readS32());
+ fFillType = buffer.readS32();
+ buffer.read(fPts.begin(), sizeof(SkPoint) * fPts.count());
+ buffer.read(fVerbs.begin(), fVerbs.count());
+
+ fFastBoundsIsDirty = true;
+
+ SkDEBUGCODE(this->validate();)
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#include "SkString.h"
+#include "SkStream.h"
+
+static void write_scalar(SkWStream* stream, SkScalar value) {
+ char buffer[SkStrAppendScalar_MaxSize];
+ char* stop = SkStrAppendScalar(buffer, value);
+ stream->write(buffer, stop - buffer);
+}
+
+static void append_scalars(SkWStream* stream, char verb, const SkScalar data[],
+ int count) {
+ stream->write(&verb, 1);
+ write_scalar(stream, data[0]);
+ for (int i = 1; i < count; i++) {
+ if (data[i] >= 0) {
+ // can skip the separater if data[i] is negative
+ stream->write(" ", 1);
+ }
+ write_scalar(stream, data[i]);
+ }
+}
+
+void SkPath::toString(SkString* str) const {
+ SkDynamicMemoryWStream stream;
+
+ SkPath::Iter iter(*this, false);
+ SkPoint pts[4];
+
+ for (;;) {
+ switch (iter.next(pts)) {
+ case SkPath::kMove_Verb:
+ append_scalars(&stream, 'M', &pts[0].fX, 2);
+ break;
+ case SkPath::kLine_Verb:
+ append_scalars(&stream, 'L', &pts[1].fX, 2);
+ break;
+ case SkPath::kQuad_Verb:
+ append_scalars(&stream, 'Q', &pts[1].fX, 4);
+ break;
+ case SkPath::kCubic_Verb:
+ append_scalars(&stream, 'C', &pts[1].fX, 6);
+ break;
+ case SkPath::kClose_Verb:
+ stream.write("Z", 1);
+ break;
+ case SkPath::kDone_Verb:
+ str->resize(stream.getOffset());
+ stream.copyTo(str->writable_str());
+ return;
+ }
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+#ifdef SK_DEBUG
+
+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 0 // test to ensure that the iterator returns the same data as the path
+void SkPath::test() const
+{
+ Iter iter(*this, false);
+ SkPoint pts[4];
+ Verb verb;
+
+ const uint8_t* verbs = fVerbs.begin();
+ const SkPoint* points = fPts.begin();
+
+ while ((verb = iter.next(pts)) != kDone_Verb)
+ {
+ SkASSERT(*verbs == verb);
+ verbs += 1;
+
+ int count;
+ switch (verb) {
+ case kMove_Verb:
+ count = 1;
+ break;
+ case kLine_Verb:
+ count = 2;
+ break;
+ case kQuad_Verb:
+ count = 3;
+ break;
+ case kCubic_Verb:
+ count = 4;
+ break;
+ case kClose_Verb:
+ default:
+ count = 0;
+ break;
+ }
+ if (count > 1)
+ points -= 1;
+ SkASSERT(memcmp(pts, points, count * sizeof(SkPoint)) == 0);
+ points += count;
+ }
+
+ int vc = fVerbs.count(), pc = fPts.count();
+ if (vc && fVerbs.begin()[vc-1] == kMove_Verb)
+ {
+ vc -= 1;
+ pc -= 1;
+ }
+ SkASSERT(verbs - fVerbs.begin() == vc);
+ SkASSERT(points - fPts.begin() == pc);
+}
+#endif
+
+void SkPath::dump(bool forceClose, const char title[]) const {
+ Iter iter(*this, forceClose);
+ SkPoint pts[4];
+ Verb verb;
+
+ SkDebugf("path: forceClose=%s %s\n", forceClose ? "true" : "false",
+ title ? title : "");
+
+ while ((verb = iter.next(pts)) != kDone_Verb) {
+ switch (verb) {
+ case kMove_Verb:
+#ifdef SK_CAN_USE_FLOAT
+ SkDebugf(" path: moveTo [%g %g]\n",
+ SkScalarToFloat(pts[0].fX), SkScalarToFloat(pts[0].fY));
+#else
+ SkDebugf(" path: moveTo [%x %x]\n", pts[0].fX, pts[0].fY);
+#endif
+ break;
+ case kLine_Verb:
+#ifdef SK_CAN_USE_FLOAT
+ SkDebugf(" path: lineTo [%g %g]\n",
+ SkScalarToFloat(pts[1].fX), SkScalarToFloat(pts[1].fY));
+#else
+ SkDebugf(" path: lineTo [%x %x]\n", pts[1].fX, pts[1].fY);
+#endif
+ break;
+ case kQuad_Verb:
+#ifdef SK_CAN_USE_FLOAT
+ SkDebugf(" path: quadTo [%g %g] [%g %g]\n",
+ SkScalarToFloat(pts[1].fX), SkScalarToFloat(pts[1].fY),
+ SkScalarToFloat(pts[2].fX), SkScalarToFloat(pts[2].fY));
+#else
+ SkDebugf(" path: quadTo [%x %x] [%x %x]\n",
+ pts[1].fX, pts[1].fY, pts[2].fX, pts[2].fY);
+#endif
+ break;
+ case kCubic_Verb:
+#ifdef SK_CAN_USE_FLOAT
+ SkDebugf(" path: cubeTo [%g %g] [%g %g] [%g %g]\n",
+ SkScalarToFloat(pts[1].fX), SkScalarToFloat(pts[1].fY),
+ SkScalarToFloat(pts[2].fX), SkScalarToFloat(pts[2].fY),
+ SkScalarToFloat(pts[3].fX), SkScalarToFloat(pts[3].fY));
+#else
+ SkDebugf(" path: cubeTo [%x %x] [%x %x] [%x %x]\n",
+ pts[1].fX, pts[1].fY, pts[2].fX, pts[2].fY,
+ pts[3].fX, pts[3].fY);
+#endif
+ break;
+ case kClose_Verb:
+ SkDebugf(" path: close\n");
+ break;
+ default:
+ SkDebugf(" path: UNKNOWN VERB %d, aborting dump...\n", verb);
+ verb = kDone_Verb; // stop the loop
+ break;
+ }
+ }
+ SkDebugf("path: done %s\n", title ? title : "");
+}
+
+#include "SkTSort.h"
+
+void SkPath::UnitTest() {
+#ifdef SK_SUPPORT_UNITTEST
+ SkPath p;
+ SkRect r;
+
+ r.set(0, 0, 10, 20);
+ p.addRect(r);
+ p.dump(false);
+ p.dump(true);
+
+ {
+ int array[] = { 5, 3, 7, 2, 6, 1, 2, 9, 5, 0 };
+ int i;
+
+ for (i = 0; i < (int)SK_ARRAY_COUNT(array); i++) {
+ SkDebugf(" %d", array[i]);
+ }
+ SkDebugf("\n");
+ SkTHeapSort<int>(array, SK_ARRAY_COUNT(array));
+ for (i = 0; i < (int)SK_ARRAY_COUNT(array); i++)
+ SkDebugf(" %d", array[i]);
+ SkDebugf("\n");
+ }
+
+ {
+ SkPath p;
+ SkPoint pt;
+
+ p.moveTo(SK_Scalar1, 0);
+ p.getLastPt(&pt);
+ SkASSERT(pt.fX == SK_Scalar1);
+ }
+#endif
+}
+
+#endif