Index: skia/sgl/SkPathMeasure.cpp |
=================================================================== |
--- skia/sgl/SkPathMeasure.cpp (revision 16859) |
+++ skia/sgl/SkPathMeasure.cpp (working copy) |
@@ -1,598 +0,0 @@ |
-/* |
- * Copyright (C) 2006-2008 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 "SkPathMeasure.h" |
-#include "SkGeometry.h" |
-#include "SkPath.h" |
-#include "SkTSearch.h" |
- |
-// these must be 0,1,2 since they are in our 2-bit field |
-enum { |
- kLine_SegType, |
- kCloseLine_SegType, |
- kQuad_SegType, |
- kCubic_SegType |
-}; |
- |
-#define kMaxTValue 32767 |
- |
-static inline SkScalar tValue2Scalar(int t) { |
- SkASSERT((unsigned)t <= kMaxTValue); |
- |
-#ifdef SK_SCALAR_IS_FLOAT |
- return t * 3.05185e-5f; // t / 32767 |
-#else |
- return (t + (t >> 14)) << 1; |
-#endif |
-} |
- |
-SkScalar SkPathMeasure::Segment::getScalarT() const { |
- return tValue2Scalar(fTValue); |
-} |
- |
-const SkPathMeasure::Segment* SkPathMeasure::NextSegment(const Segment* seg) { |
- unsigned ptIndex = seg->fPtIndex; |
- |
- do { |
- ++seg; |
- } while (seg->fPtIndex == ptIndex); |
- return seg; |
-} |
- |
-/////////////////////////////////////////////////////////////////////////////// |
- |
-static inline int tspan_big_enough(int tspan) { |
- SkASSERT((unsigned)tspan <= kMaxTValue); |
- return tspan >> 10; |
-} |
- |
-#if 0 |
-static inline bool tangents_too_curvy(const SkVector& tan0, SkVector& tan1) { |
- static const SkScalar kFlatEnoughTangentDotProd = SK_Scalar1 * 99 / 100; |
- |
- SkASSERT(kFlatEnoughTangentDotProd > 0 && |
- kFlatEnoughTangentDotProd < SK_Scalar1); |
- |
- return SkPoint::DotProduct(tan0, tan1) < kFlatEnoughTangentDotProd; |
-} |
-#endif |
- |
-// can't use tangents, since we need [0..1..................2] to be seen |
-// as definitely not a line (it is when drawn, but not parametrically) |
-// so we compare midpoints |
-#define CHEAP_DIST_LIMIT (SK_Scalar1/2) // just made this value up |
- |
-static bool quad_too_curvy(const SkPoint pts[3]) { |
- // diff = (a/4 + b/2 + c/4) - (a/2 + c/2) |
- // diff = -a/4 + b/2 - c/4 |
- SkScalar dx = SkScalarHalf(pts[1].fX) - |
- SkScalarHalf(SkScalarHalf(pts[0].fX + pts[2].fX)); |
- SkScalar dy = SkScalarHalf(pts[1].fY) - |
- SkScalarHalf(SkScalarHalf(pts[0].fY + pts[2].fY)); |
- |
- SkScalar dist = SkMaxScalar(SkScalarAbs(dx), SkScalarAbs(dy)); |
- return dist > CHEAP_DIST_LIMIT; |
-} |
- |
-static bool cheap_dist_exceeds_limit(const SkPoint& pt, |
- SkScalar x, SkScalar y) { |
- SkScalar dist = SkMaxScalar(SkScalarAbs(x - pt.fX), SkScalarAbs(y - pt.fY)); |
- // just made up the 1/2 |
- return dist > CHEAP_DIST_LIMIT; |
-} |
- |
-static bool cubic_too_curvy(const SkPoint pts[4]) { |
- return cheap_dist_exceeds_limit(pts[1], |
- SkScalarInterp(pts[0].fX, pts[3].fX, SK_Scalar1/3), |
- SkScalarInterp(pts[0].fY, pts[3].fY, SK_Scalar1/3)) |
- || |
- cheap_dist_exceeds_limit(pts[2], |
- SkScalarInterp(pts[0].fX, pts[3].fX, SK_Scalar1*2/3), |
- SkScalarInterp(pts[0].fY, pts[3].fY, SK_Scalar1*2/3)); |
-} |
- |
-SkScalar SkPathMeasure::compute_quad_segs(const SkPoint pts[3], |
- SkScalar distance, int mint, int maxt, int ptIndex) { |
- if (tspan_big_enough(maxt - mint) && quad_too_curvy(pts)) { |
- SkPoint tmp[5]; |
- int halft = (mint + maxt) >> 1; |
- |
- SkChopQuadAtHalf(pts, tmp); |
- distance = this->compute_quad_segs(tmp, distance, mint, halft, ptIndex); |
- distance = this->compute_quad_segs(&tmp[2], distance, halft, maxt, ptIndex); |
- } else { |
- SkScalar d = SkPoint::Distance(pts[0], pts[2]); |
- SkASSERT(d >= 0); |
- if (!SkScalarNearlyZero(d)) { |
- distance += d; |
- Segment* seg = fSegments.append(); |
- seg->fDistance = distance; |
- seg->fPtIndex = ptIndex; |
- seg->fType = kQuad_SegType; |
- seg->fTValue = maxt; |
- } |
- } |
- return distance; |
-} |
- |
-SkScalar SkPathMeasure::compute_cubic_segs(const SkPoint pts[4], |
- SkScalar distance, int mint, int maxt, int ptIndex) { |
- if (tspan_big_enough(maxt - mint) && cubic_too_curvy(pts)) { |
- SkPoint tmp[7]; |
- int halft = (mint + maxt) >> 1; |
- |
- SkChopCubicAtHalf(pts, tmp); |
- distance = this->compute_cubic_segs(tmp, distance, mint, halft, ptIndex); |
- distance = this->compute_cubic_segs(&tmp[3], distance, halft, maxt, ptIndex); |
- } else { |
- SkScalar d = SkPoint::Distance(pts[0], pts[3]); |
- SkASSERT(d >= 0); |
- if (!SkScalarNearlyZero(d)) { |
- distance += d; |
- Segment* seg = fSegments.append(); |
- seg->fDistance = distance; |
- seg->fPtIndex = ptIndex; |
- seg->fType = kCubic_SegType; |
- seg->fTValue = maxt; |
- } |
- } |
- return distance; |
-} |
- |
-void SkPathMeasure::buildSegments() { |
- SkPoint pts[4]; |
- int ptIndex = fFirstPtIndex; |
- SkScalar d, distance = 0; |
- bool isClosed = fForceClosed; |
- bool firstMoveTo = ptIndex < 0; |
- Segment* seg; |
- |
- fSegments.reset(); |
- for (;;) { |
- switch (fIter.next(pts)) { |
- case SkPath::kMove_Verb: |
- if (!firstMoveTo) { |
- goto DONE; |
- } |
- ptIndex += 1; |
- firstMoveTo = false; |
- break; |
- |
- case SkPath::kLine_Verb: |
- d = SkPoint::Distance(pts[0], pts[1]); |
- SkASSERT(d >= 0); |
- if (!SkScalarNearlyZero(d)) { |
- distance += d; |
- seg = fSegments.append(); |
- seg->fDistance = distance; |
- seg->fPtIndex = ptIndex; |
- seg->fType = fIter.isCloseLine() ? |
- kCloseLine_SegType : kLine_SegType; |
- seg->fTValue = kMaxTValue; |
- } |
- ptIndex += !fIter.isCloseLine(); |
- break; |
- |
- case SkPath::kQuad_Verb: |
- distance = this->compute_quad_segs(pts, distance, 0, |
- kMaxTValue, ptIndex); |
- ptIndex += 2; |
- break; |
- |
- case SkPath::kCubic_Verb: |
- distance = this->compute_cubic_segs(pts, distance, 0, |
- kMaxTValue, ptIndex); |
- ptIndex += 3; |
- break; |
- |
- case SkPath::kClose_Verb: |
- isClosed = true; |
- break; |
- |
- case SkPath::kDone_Verb: |
- goto DONE; |
- } |
- } |
-DONE: |
- fLength = distance; |
- fIsClosed = isClosed; |
- fFirstPtIndex = ptIndex + 1; |
- |
-#ifdef SK_DEBUG |
- { |
- const Segment* seg = fSegments.begin(); |
- const Segment* stop = fSegments.end(); |
- unsigned ptIndex = 0; |
- SkScalar distance = 0; |
- |
- while (seg < stop) { |
- SkASSERT(seg->fDistance > distance); |
- SkASSERT(seg->fPtIndex >= ptIndex); |
- SkASSERT(seg->fTValue > 0); |
- |
- const Segment* s = seg; |
- while (s < stop - 1 && s[0].fPtIndex == s[1].fPtIndex) { |
- SkASSERT(s[0].fType == s[1].fType); |
- SkASSERT(s[0].fTValue < s[1].fTValue); |
- s += 1; |
- } |
- |
- distance = seg->fDistance; |
- ptIndex = seg->fPtIndex; |
- seg += 1; |
- } |
- // SkDebugf("\n"); |
- } |
-#endif |
-} |
- |
-// marked as a friend in SkPath.h |
-const SkPoint* sk_get_path_points(const SkPath& path, int index) { |
- return &path.fPts[index]; |
-} |
- |
-static void compute_pos_tan(const SkPath& path, int firstPtIndex, int ptIndex, |
- int segType, SkScalar t, SkPoint* pos, SkVector* tangent) { |
- const SkPoint* pts = sk_get_path_points(path, ptIndex); |
- |
- switch (segType) { |
- case kLine_SegType: |
- case kCloseLine_SegType: { |
- const SkPoint* endp = (segType == kLine_SegType) ? |
- &pts[1] : |
- sk_get_path_points(path, firstPtIndex); |
- |
- if (pos) { |
- pos->set(SkScalarInterp(pts[0].fX, endp->fX, t), |
- SkScalarInterp(pts[0].fY, endp->fY, t)); |
- } |
- if (tangent) { |
- tangent->setNormalize(endp->fX - pts[0].fX, endp->fY - pts[0].fY); |
- } |
- break; |
- } |
- case kQuad_SegType: |
- SkEvalQuadAt(pts, t, pos, tangent); |
- if (tangent) { |
- tangent->normalize(); |
- } |
- break; |
- case kCubic_SegType: |
- SkEvalCubicAt(pts, t, pos, tangent, NULL); |
- if (tangent) { |
- tangent->normalize(); |
- } |
- break; |
- default: |
- SkASSERT(!"unknown segType"); |
- } |
-} |
- |
-static void seg_to(const SkPath& src, int firstPtIndex, int ptIndex, |
- int segType, SkScalar startT, SkScalar stopT, SkPath* dst) { |
- SkASSERT(startT >= 0 && startT <= SK_Scalar1); |
- SkASSERT(stopT >= 0 && stopT <= SK_Scalar1); |
- SkASSERT(startT <= stopT); |
- |
- if (SkScalarNearlyZero(stopT - startT)) { |
- return; |
- } |
- |
- const SkPoint* pts = sk_get_path_points(src, ptIndex); |
- SkPoint tmp0[7], tmp1[7]; |
- |
- switch (segType) { |
- case kLine_SegType: |
- case kCloseLine_SegType: { |
- const SkPoint* endp = (segType == kLine_SegType) ? |
- &pts[1] : |
- sk_get_path_points(src, firstPtIndex); |
- |
- if (stopT == kMaxTValue) { |
- dst->lineTo(*endp); |
- } else { |
- dst->lineTo(SkScalarInterp(pts[0].fX, endp->fX, stopT), |
- SkScalarInterp(pts[0].fY, endp->fY, stopT)); |
- } |
- break; |
- } |
- case kQuad_SegType: |
- if (startT == 0) { |
- if (stopT == SK_Scalar1) { |
- dst->quadTo(pts[1], pts[2]); |
- } else { |
- SkChopQuadAt(pts, tmp0, stopT); |
- dst->quadTo(tmp0[1], tmp0[2]); |
- } |
- } else { |
- SkChopQuadAt(pts, tmp0, startT); |
- if (stopT == SK_Scalar1) { |
- dst->quadTo(tmp0[3], tmp0[4]); |
- } else { |
- SkChopQuadAt(&tmp0[2], tmp1, SkScalarDiv(stopT - startT, |
- SK_Scalar1 - startT)); |
- dst->quadTo(tmp1[1], tmp1[2]); |
- } |
- } |
- break; |
- case kCubic_SegType: |
- if (startT == 0) { |
- if (stopT == SK_Scalar1) { |
- dst->cubicTo(pts[1], pts[2], pts[3]); |
- } else { |
- SkChopCubicAt(pts, tmp0, stopT); |
- dst->cubicTo(tmp0[1], tmp0[2], tmp0[3]); |
- } |
- } else { |
- SkChopCubicAt(pts, tmp0, startT); |
- if (stopT == SK_Scalar1) { |
- dst->cubicTo(tmp0[4], tmp0[5], tmp0[6]); |
- } else { |
- SkChopCubicAt(&tmp0[3], tmp1, SkScalarDiv(stopT - startT, |
- SK_Scalar1 - startT)); |
- dst->cubicTo(tmp1[1], tmp1[2], tmp1[3]); |
- } |
- } |
- break; |
- default: |
- SkASSERT(!"unknown segType"); |
- sk_throw(); |
- } |
-} |
- |
-//////////////////////////////////////////////////////////////////////////////// |
-//////////////////////////////////////////////////////////////////////////////// |
- |
-SkPathMeasure::SkPathMeasure() { |
- fPath = NULL; |
- fLength = -1; // signal we need to compute it |
- fForceClosed = false; |
- fFirstPtIndex = -1; |
-} |
- |
-SkPathMeasure::SkPathMeasure(const SkPath& path, bool forceClosed) { |
- fPath = &path; |
- fLength = -1; // signal we need to compute it |
- fForceClosed = forceClosed; |
- fFirstPtIndex = -1; |
- |
- fIter.setPath(path, forceClosed); |
-} |
- |
-SkPathMeasure::~SkPathMeasure() {} |
- |
-/** Assign a new path, or null to have none. |
-*/ |
-void SkPathMeasure::setPath(const SkPath* path, bool forceClosed) { |
- fPath = path; |
- fLength = -1; // signal we need to compute it |
- fForceClosed = forceClosed; |
- fFirstPtIndex = -1; |
- |
- if (path) { |
- fIter.setPath(*path, forceClosed); |
- } |
- fSegments.reset(); |
-} |
- |
-SkScalar SkPathMeasure::getLength() { |
- if (fPath == NULL) { |
- return 0; |
- } |
- if (fLength < 0) { |
- this->buildSegments(); |
- } |
- SkASSERT(fLength >= 0); |
- return fLength; |
-} |
- |
-const SkPathMeasure::Segment* SkPathMeasure::distanceToSegment( |
- SkScalar distance, SkScalar* t) { |
- SkDEBUGCODE(SkScalar length = ) this->getLength(); |
- SkASSERT(distance >= 0 && distance <= length); |
- |
- const Segment* seg = fSegments.begin(); |
- int count = fSegments.count(); |
- |
- int index = SkTSearch<SkScalar>(&seg->fDistance, count, distance, |
- sizeof(Segment)); |
- // don't care if we hit an exact match or not, so we xor index if it is negative |
- index ^= (index >> 31); |
- seg = &seg[index]; |
- |
- // now interpolate t-values with the prev segment (if possible) |
- SkScalar startT = 0, startD = 0; |
- // check if the prev segment is legal, and references the same set of points |
- if (index > 0) { |
- startD = seg[-1].fDistance; |
- if (seg[-1].fPtIndex == seg->fPtIndex) { |
- SkASSERT(seg[-1].fType == seg->fType); |
- startT = seg[-1].getScalarT(); |
- } |
- } |
- |
- SkASSERT(seg->getScalarT() > startT); |
- SkASSERT(distance >= startD); |
- SkASSERT(seg->fDistance > startD); |
- |
- *t = startT + SkScalarMulDiv(seg->getScalarT() - startT, |
- distance - startD, |
- seg->fDistance - startD); |
- return seg; |
-} |
- |
-bool SkPathMeasure::getPosTan(SkScalar distance, SkPoint* pos, |
- SkVector* tangent) { |
- SkASSERT(fPath); |
- if (fPath == NULL) { |
- EMPTY: |
- return false; |
- } |
- |
- SkScalar length = this->getLength(); // call this to force computing it |
- int count = fSegments.count(); |
- |
- if (count == 0 || length == 0) { |
- goto EMPTY; |
- } |
- |
- // pin the distance to a legal range |
- if (distance < 0) { |
- distance = 0; |
- } else if (distance > length) { |
- distance = length; |
- } |
- |
- SkScalar t; |
- const Segment* seg = this->distanceToSegment(distance, &t); |
- |
- compute_pos_tan(*fPath, fSegments[0].fPtIndex, seg->fPtIndex, seg->fType, |
- t, pos, tangent); |
- return true; |
-} |
- |
-bool SkPathMeasure::getMatrix(SkScalar distance, SkMatrix* matrix, |
- MatrixFlags flags) { |
- SkPoint position; |
- SkVector tangent; |
- |
- if (this->getPosTan(distance, &position, &tangent)) { |
- if (matrix) { |
- if (flags & kGetTangent_MatrixFlag) { |
- matrix->setSinCos(tangent.fY, tangent.fX, 0, 0); |
- } else { |
- matrix->reset(); |
- } |
- if (flags & kGetPosition_MatrixFlag) { |
- matrix->postTranslate(position.fX, position.fY); |
- } |
- } |
- return true; |
- } |
- return false; |
-} |
- |
-bool SkPathMeasure::getSegment(SkScalar startD, SkScalar stopD, SkPath* dst, |
- bool startWithMoveTo) { |
- SkASSERT(dst); |
- |
- SkScalar length = this->getLength(); // ensure we have built our segments |
- |
- if (startD < 0) { |
- startD = 0; |
- } |
- if (stopD > length) { |
- stopD = length; |
- } |
- if (startD >= stopD) { |
- return false; |
- } |
- |
- SkPoint p; |
- SkScalar startT, stopT; |
- const Segment* seg = this->distanceToSegment(startD, &startT); |
- const Segment* stopSeg = this->distanceToSegment(stopD, &stopT); |
- SkASSERT(seg <= stopSeg); |
- |
- if (startWithMoveTo) { |
- compute_pos_tan(*fPath, fSegments[0].fPtIndex, seg->fPtIndex, |
- seg->fType, startT, &p, NULL); |
- dst->moveTo(p); |
- } |
- |
- if (seg->fPtIndex == stopSeg->fPtIndex) { |
- seg_to(*fPath, fSegments[0].fPtIndex, seg->fPtIndex, seg->fType, |
- startT, stopT, dst); |
- } else { |
- do { |
- seg_to(*fPath, fSegments[0].fPtIndex, seg->fPtIndex, seg->fType, |
- startT, SK_Scalar1, dst); |
- seg = SkPathMeasure::NextSegment(seg); |
- startT = 0; |
- } while (seg->fPtIndex < stopSeg->fPtIndex); |
- seg_to(*fPath, fSegments[0].fPtIndex, seg->fPtIndex, seg->fType, |
- 0, stopT, dst); |
- } |
- return true; |
-} |
- |
-bool SkPathMeasure::isClosed() { |
- (void)this->getLength(); |
- return fIsClosed; |
-} |
- |
-/** Move to the next contour in the path. Return true if one exists, or false if |
- we're done with the path. |
-*/ |
-bool SkPathMeasure::nextContour() { |
- fLength = -1; |
- return this->getLength() > 0; |
-} |
- |
-/////////////////////////////////////////////////////////////////////////////// |
-/////////////////////////////////////////////////////////////////////////////// |
- |
-#ifdef SK_DEBUG |
- |
-void SkPathMeasure::dump() { |
- SkDebugf("pathmeas: length=%g, segs=%d\n", fLength, fSegments.count()); |
- |
- for (int i = 0; i < fSegments.count(); i++) { |
- const Segment* seg = &fSegments[i]; |
- SkDebugf("pathmeas: seg[%d] distance=%g, point=%d, t=%g, type=%d\n", |
- i, seg->fDistance, seg->fPtIndex, seg->getScalarT(), |
- seg->fType); |
- } |
-} |
- |
-void SkPathMeasure::UnitTest() { |
-#ifdef SK_SUPPORT_UNITTEST |
- SkPath path; |
- |
- path.moveTo(0, 0); |
- path.lineTo(SK_Scalar1, 0); |
- path.lineTo(SK_Scalar1, SK_Scalar1); |
- path.lineTo(0, SK_Scalar1); |
- |
- SkPathMeasure meas(path, true); |
- SkScalar length = meas.getLength(); |
- SkASSERT(length == SK_Scalar1*4); |
- |
- path.reset(); |
- path.moveTo(0, 0); |
- path.lineTo(SK_Scalar1*3, SK_Scalar1*4); |
- meas.setPath(&path, false); |
- length = meas.getLength(); |
- SkASSERT(length == SK_Scalar1*5); |
- |
- path.reset(); |
- path.addCircle(0, 0, SK_Scalar1); |
- meas.setPath(&path, true); |
- length = meas.getLength(); |
- SkDebugf("circle arc-length = %g\n", length); |
- |
- for (int i = 0; i < 8; i++) { |
- SkScalar d = length * i / 8; |
- SkPoint p; |
- SkVector v; |
- meas.getPosTan(d, &p, &v); |
- SkDebugf("circle arc-length=%g, pos[%g %g] tan[%g %g]\n", |
- d, p.fX, p.fY, v.fX, v.fY); |
- } |
-#endif |
-} |
- |
-#endif |