| Index: skia/sgl/SkPath.cpp
|
| ===================================================================
|
| --- skia/sgl/SkPath.cpp (revision 16859)
|
| +++ skia/sgl/SkPath.cpp (working copy)
|
| @@ -1,1427 +0,0 @@
|
| -/* libs/graphics/sgl/SkPath.cpp
|
| -**
|
| -** 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.
|
| -** 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"
|
| -
|
| -////////////////////////////////////////////////////////////////////////////
|
| -
|
| -/* 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
|
| - - 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;
|
| - compute_fast_bounds(&fFastBounds, fPts);
|
| - }
|
| - *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) {
|
| - SkAutoPathBoundsUpdate apbu(this, left, top, right, bottom);
|
| -
|
| - 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) {
|
| - SkAutoPathBoundsUpdate apbu(this, rect);
|
| -
|
| - 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,
|
| - SkPath::Direction dir, bool forceMoveTo) {
|
| - 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 (SkPath::kCCW_Direction == dir) {
|
| - start += sweep;
|
| - sweep = -sweep;
|
| - }
|
| -
|
| - path->arcTo(r, start, sweep, forceMoveTo);
|
| -}
|
| -
|
| -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, 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, 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 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);
|
| -
|
| - 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(17); // 8 quads + close
|
| - 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 {
|
| - SkDEBUGCODE(this->validate();)
|
| - 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());
|
| - SkDEBUGCODE(dst->validate();)
|
| - }
|
| -}
|
| -
|
| -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);
|
| - 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
|
| -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
|
|
|