| Index: skia/sgl/SkStroke.cpp
|
| ===================================================================
|
| --- skia/sgl/SkStroke.cpp (revision 16859)
|
| +++ skia/sgl/SkStroke.cpp (working copy)
|
| @@ -1,645 +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 "SkStrokerPriv.h"
|
| -#include "SkGeometry.h"
|
| -#include "SkPath.h"
|
| -
|
| -#define kMaxQuadSubdivide 5
|
| -#define kMaxCubicSubdivide 4
|
| -
|
| -static inline bool degenerate_vector(const SkVector& v) {
|
| - return SkScalarNearlyZero(v.fX) && SkScalarNearlyZero(v.fY);
|
| -}
|
| -
|
| -static inline bool degenerate_line(const SkPoint& a, const SkPoint& b,
|
| - SkScalar tolerance = SK_ScalarNearlyZero) {
|
| - return SkScalarNearlyZero(a.fX - b.fX, tolerance) &&
|
| - SkScalarNearlyZero(a.fY - b.fY, tolerance);
|
| -}
|
| -
|
| -static inline bool normals_too_curvy(const SkVector& norm0, SkVector& norm1) {
|
| - /* root2/2 is a 45-degree angle
|
| - make this constant bigger for more subdivisions (but not >= 1)
|
| - */
|
| - static const SkScalar kFlatEnoughNormalDotProd =
|
| - SK_ScalarSqrt2/2 + SK_Scalar1/10;
|
| -
|
| - SkASSERT(kFlatEnoughNormalDotProd > 0 &&
|
| - kFlatEnoughNormalDotProd < SK_Scalar1);
|
| -
|
| - return SkPoint::DotProduct(norm0, norm1) <= kFlatEnoughNormalDotProd;
|
| -}
|
| -
|
| -static inline bool normals_too_pinchy(const SkVector& norm0, SkVector& norm1) {
|
| - static const SkScalar kTooPinchyNormalDotProd = -SK_Scalar1 * 999 / 1000;
|
| -
|
| - return SkPoint::DotProduct(norm0, norm1) <= kTooPinchyNormalDotProd;
|
| -}
|
| -
|
| -static bool set_normal_unitnormal(const SkPoint& before, const SkPoint& after,
|
| - SkScalar radius,
|
| - SkVector* normal, SkVector* unitNormal) {
|
| - if (!unitNormal->setNormalize(after.fX - before.fX, after.fY - before.fY)) {
|
| - return false;
|
| - }
|
| - unitNormal->rotateCCW();
|
| - unitNormal->scale(radius, normal);
|
| - return true;
|
| -}
|
| -
|
| -static bool set_normal_unitnormal(const SkVector& vec,
|
| - SkScalar radius,
|
| - SkVector* normal, SkVector* unitNormal) {
|
| - if (!unitNormal->setNormalize(vec.fX, vec.fY)) {
|
| - return false;
|
| - }
|
| - unitNormal->rotateCCW();
|
| - unitNormal->scale(radius, normal);
|
| - return true;
|
| -}
|
| -
|
| -///////////////////////////////////////////////////////////////////////////////
|
| -
|
| -class SkPathStroker {
|
| -public:
|
| - SkPathStroker(SkScalar radius, SkScalar miterLimit, SkPaint::Cap cap,
|
| - SkPaint::Join join);
|
| -
|
| - void moveTo(const SkPoint&);
|
| - void lineTo(const SkPoint&);
|
| - void quadTo(const SkPoint&, const SkPoint&);
|
| - void cubicTo(const SkPoint&, const SkPoint&, const SkPoint&);
|
| - void close(bool isLine) { this->finishContour(true, isLine); }
|
| -
|
| - void done(SkPath* dst, bool isLine) {
|
| - this->finishContour(false, isLine);
|
| - fOuter.addPath(fExtra);
|
| - dst->swap(fOuter);
|
| - }
|
| -
|
| -private:
|
| - SkScalar fRadius;
|
| - SkScalar fInvMiterLimit;
|
| -
|
| - SkVector fFirstNormal, fPrevNormal, fFirstUnitNormal, fPrevUnitNormal;
|
| - SkPoint fFirstPt, fPrevPt; // on original path
|
| - SkPoint fFirstOuterPt;
|
| - int fSegmentCount;
|
| - bool fPrevIsLine;
|
| -
|
| - SkStrokerPriv::CapProc fCapper;
|
| - SkStrokerPriv::JoinProc fJoiner;
|
| -
|
| - SkPath fInner, fOuter; // outer is our working answer, inner is temp
|
| - SkPath fExtra; // added as extra complete contours
|
| -
|
| - void finishContour(bool close, bool isLine);
|
| - void preJoinTo(const SkPoint&, SkVector* normal, SkVector* unitNormal,
|
| - bool isLine);
|
| - void postJoinTo(const SkPoint&, const SkVector& normal,
|
| - const SkVector& unitNormal);
|
| -
|
| - void line_to(const SkPoint& currPt, const SkVector& normal);
|
| - void quad_to(const SkPoint pts[3],
|
| - const SkVector& normalAB, const SkVector& unitNormalAB,
|
| - SkVector* normalBC, SkVector* unitNormalBC,
|
| - int subDivide);
|
| - void cubic_to(const SkPoint pts[4],
|
| - const SkVector& normalAB, const SkVector& unitNormalAB,
|
| - SkVector* normalCD, SkVector* unitNormalCD,
|
| - int subDivide);
|
| -};
|
| -
|
| -///////////////////////////////////////////////////////////////////////////////
|
| -
|
| -void SkPathStroker::preJoinTo(const SkPoint& currPt, SkVector* normal,
|
| - SkVector* unitNormal, bool currIsLine) {
|
| - SkASSERT(fSegmentCount >= 0);
|
| -
|
| - SkScalar prevX = fPrevPt.fX;
|
| - SkScalar prevY = fPrevPt.fY;
|
| -
|
| - SkAssertResult(set_normal_unitnormal(fPrevPt, currPt, fRadius, normal,
|
| - unitNormal));
|
| -
|
| - if (fSegmentCount == 0) {
|
| - fFirstNormal = *normal;
|
| - fFirstUnitNormal = *unitNormal;
|
| - fFirstOuterPt.set(prevX + normal->fX, prevY + normal->fY);
|
| -
|
| - fOuter.moveTo(fFirstOuterPt.fX, fFirstOuterPt.fY);
|
| - fInner.moveTo(prevX - normal->fX, prevY - normal->fY);
|
| - } else { // we have a previous segment
|
| - fJoiner(&fOuter, &fInner, fPrevUnitNormal, fPrevPt, *unitNormal,
|
| - fRadius, fInvMiterLimit, fPrevIsLine, currIsLine);
|
| - }
|
| - fPrevIsLine = currIsLine;
|
| -}
|
| -
|
| -void SkPathStroker::postJoinTo(const SkPoint& currPt, const SkVector& normal,
|
| - const SkVector& unitNormal) {
|
| - fPrevPt = currPt;
|
| - fPrevUnitNormal = unitNormal;
|
| - fPrevNormal = normal;
|
| - fSegmentCount += 1;
|
| -}
|
| -
|
| -void SkPathStroker::finishContour(bool close, bool currIsLine) {
|
| - if (fSegmentCount > 0) {
|
| - SkPoint pt;
|
| -
|
| - if (close) {
|
| - fJoiner(&fOuter, &fInner, fPrevUnitNormal, fPrevPt,
|
| - fFirstUnitNormal, fRadius, fInvMiterLimit,
|
| - fPrevIsLine, currIsLine);
|
| - fOuter.close();
|
| - // now add fInner as its own contour
|
| - fInner.getLastPt(&pt);
|
| - fOuter.moveTo(pt.fX, pt.fY);
|
| - fOuter.reversePathTo(fInner);
|
| - fOuter.close();
|
| - } else { // add caps to start and end
|
| - // cap the end
|
| - fInner.getLastPt(&pt);
|
| - fCapper(&fOuter, fPrevPt, fPrevNormal, pt,
|
| - currIsLine ? &fInner : NULL);
|
| - fOuter.reversePathTo(fInner);
|
| - // cap the start
|
| - fCapper(&fOuter, fFirstPt, -fFirstNormal, fFirstOuterPt,
|
| - fPrevIsLine ? &fInner : NULL);
|
| - fOuter.close();
|
| - }
|
| - }
|
| - fInner.reset();
|
| - fSegmentCount = -1;
|
| -}
|
| -
|
| -///////////////////////////////////////////////////////////////////////////////
|
| -
|
| -SkPathStroker::SkPathStroker(SkScalar radius, SkScalar miterLimit,
|
| - SkPaint::Cap cap, SkPaint::Join join)
|
| - : fRadius(radius) {
|
| -
|
| - /* This is only used when join is miter_join, but we initialize it here
|
| - so that it is always defined, to fis valgrind warnings.
|
| - */
|
| - fInvMiterLimit = 0;
|
| -
|
| - if (join == SkPaint::kMiter_Join) {
|
| - if (miterLimit <= SK_Scalar1) {
|
| - join = SkPaint::kBevel_Join;
|
| - } else {
|
| - fInvMiterLimit = SkScalarInvert(miterLimit);
|
| - }
|
| - }
|
| - fCapper = SkStrokerPriv::CapFactory(cap);
|
| - fJoiner = SkStrokerPriv::JoinFactory(join);
|
| - fSegmentCount = -1;
|
| - fPrevIsLine = false;
|
| -}
|
| -
|
| -void SkPathStroker::moveTo(const SkPoint& pt) {
|
| - if (fSegmentCount > 0) {
|
| - this->finishContour(false, false);
|
| - }
|
| - fSegmentCount = 0;
|
| - fFirstPt = fPrevPt = pt;
|
| -}
|
| -
|
| -void SkPathStroker::line_to(const SkPoint& currPt, const SkVector& normal) {
|
| - fOuter.lineTo(currPt.fX + normal.fX, currPt.fY + normal.fY);
|
| - fInner.lineTo(currPt.fX - normal.fX, currPt.fY - normal.fY);
|
| -}
|
| -
|
| -void SkPathStroker::lineTo(const SkPoint& currPt) {
|
| - if (degenerate_line(fPrevPt, currPt)) {
|
| - return;
|
| - }
|
| - SkVector normal, unitNormal;
|
| -
|
| - this->preJoinTo(currPt, &normal, &unitNormal, true);
|
| - this->line_to(currPt, normal);
|
| - this->postJoinTo(currPt, normal, unitNormal);
|
| -}
|
| -
|
| -void SkPathStroker::quad_to(const SkPoint pts[3],
|
| - const SkVector& normalAB, const SkVector& unitNormalAB,
|
| - SkVector* normalBC, SkVector* unitNormalBC,
|
| - int subDivide) {
|
| - if (!set_normal_unitnormal(pts[1], pts[2], fRadius,
|
| - normalBC, unitNormalBC)) {
|
| - // pts[1] nearly equals pts[2], so just draw a line to pts[2]
|
| - this->line_to(pts[2], normalAB);
|
| - *normalBC = normalAB;
|
| - *unitNormalBC = unitNormalAB;
|
| - return;
|
| - }
|
| -
|
| - if (--subDivide >= 0 && normals_too_curvy(unitNormalAB, *unitNormalBC)) {
|
| - SkPoint tmp[5];
|
| - SkVector norm, unit;
|
| -
|
| - SkChopQuadAtHalf(pts, tmp);
|
| - this->quad_to(&tmp[0], normalAB, unitNormalAB, &norm, &unit, subDivide);
|
| - this->quad_to(&tmp[2], norm, unit, normalBC, unitNormalBC, subDivide);
|
| - } else {
|
| - SkVector normalB, unitB;
|
| - SkAssertResult(set_normal_unitnormal(pts[0], pts[2], fRadius,
|
| - &normalB, &unitB));
|
| -
|
| - fOuter.quadTo( pts[1].fX + normalB.fX, pts[1].fY + normalB.fY,
|
| - pts[2].fX + normalBC->fX, pts[2].fY + normalBC->fY);
|
| - fInner.quadTo( pts[1].fX - normalB.fX, pts[1].fY - normalB.fY,
|
| - pts[2].fX - normalBC->fX, pts[2].fY - normalBC->fY);
|
| - }
|
| -}
|
| -
|
| -void SkPathStroker::cubic_to(const SkPoint pts[4],
|
| - const SkVector& normalAB, const SkVector& unitNormalAB,
|
| - SkVector* normalCD, SkVector* unitNormalCD,
|
| - int subDivide) {
|
| - SkVector ab = pts[1] - pts[0];
|
| - SkVector cd = pts[3] - pts[2];
|
| - SkVector normalBC, unitNormalBC;
|
| -
|
| - bool degenerateAB = degenerate_vector(ab);
|
| - bool degenerateCD = degenerate_vector(cd);
|
| -
|
| - if (degenerateAB && degenerateCD) {
|
| -DRAW_LINE:
|
| - this->line_to(pts[3], normalAB);
|
| - *normalCD = normalAB;
|
| - *unitNormalCD = unitNormalAB;
|
| - return;
|
| - }
|
| -
|
| - if (degenerateAB) {
|
| - ab = pts[2] - pts[0];
|
| - degenerateAB = degenerate_vector(ab);
|
| - }
|
| - if (degenerateCD) {
|
| - cd = pts[3] - pts[1];
|
| - degenerateCD = degenerate_vector(cd);
|
| - }
|
| - if (degenerateAB || degenerateCD) {
|
| - goto DRAW_LINE;
|
| - }
|
| - SkAssertResult(set_normal_unitnormal(cd, fRadius, normalCD, unitNormalCD));
|
| - bool degenerateBC = !set_normal_unitnormal(pts[1], pts[2], fRadius,
|
| - &normalBC, &unitNormalBC);
|
| -
|
| - if (--subDivide >= 0 &&
|
| - (degenerateBC || normals_too_curvy(unitNormalAB, unitNormalBC) ||
|
| - normals_too_curvy(unitNormalBC, *unitNormalCD))) {
|
| - SkPoint tmp[7];
|
| - SkVector norm, unit, dummy, unitDummy;
|
| -
|
| - SkChopCubicAtHalf(pts, tmp);
|
| - this->cubic_to(&tmp[0], normalAB, unitNormalAB, &norm, &unit,
|
| - subDivide);
|
| - // we use dummys since we already have a valid (and more accurate)
|
| - // normals for CD
|
| - this->cubic_to(&tmp[3], norm, unit, &dummy, &unitDummy, subDivide);
|
| - } else {
|
| - SkVector normalB, normalC;
|
| -
|
| - // need normals to inset/outset the off-curve pts B and C
|
| -
|
| - if (0) { // this is normal to the line between our adjacent pts
|
| - normalB = pts[2] - pts[0];
|
| - normalB.rotateCCW();
|
| - SkAssertResult(normalB.setLength(fRadius));
|
| -
|
| - normalC = pts[3] - pts[1];
|
| - normalC.rotateCCW();
|
| - SkAssertResult(normalC.setLength(fRadius));
|
| - } else { // miter-join
|
| - SkVector unitBC = pts[2] - pts[1];
|
| - unitBC.normalize();
|
| - unitBC.rotateCCW();
|
| -
|
| - normalB = unitNormalAB + unitBC;
|
| - normalC = *unitNormalCD + unitBC;
|
| -
|
| - SkScalar dot = SkPoint::DotProduct(unitNormalAB, unitBC);
|
| - SkAssertResult(normalB.setLength(SkScalarDiv(fRadius,
|
| - SkScalarSqrt((SK_Scalar1 + dot)/2))));
|
| - dot = SkPoint::DotProduct(*unitNormalCD, unitBC);
|
| - SkAssertResult(normalC.setLength(SkScalarDiv(fRadius,
|
| - SkScalarSqrt((SK_Scalar1 + dot)/2))));
|
| - }
|
| -
|
| - fOuter.cubicTo( pts[1].fX + normalB.fX, pts[1].fY + normalB.fY,
|
| - pts[2].fX + normalC.fX, pts[2].fY + normalC.fY,
|
| - pts[3].fX + normalCD->fX, pts[3].fY + normalCD->fY);
|
| -
|
| - fInner.cubicTo( pts[1].fX - normalB.fX, pts[1].fY - normalB.fY,
|
| - pts[2].fX - normalC.fX, pts[2].fY - normalC.fY,
|
| - pts[3].fX - normalCD->fX, pts[3].fY - normalCD->fY);
|
| - }
|
| -}
|
| -
|
| -void SkPathStroker::quadTo(const SkPoint& pt1, const SkPoint& pt2) {
|
| - bool degenerateAB = degenerate_line(fPrevPt, pt1);
|
| - bool degenerateBC = degenerate_line(pt1, pt2);
|
| -
|
| - if (degenerateAB | degenerateBC) {
|
| - if (degenerateAB ^ degenerateBC) {
|
| - this->lineTo(pt2);
|
| - }
|
| - return;
|
| - }
|
| -
|
| - SkVector normalAB, unitAB, normalBC, unitBC;
|
| -
|
| - this->preJoinTo(pt1, &normalAB, &unitAB, false);
|
| -
|
| - {
|
| - SkPoint pts[3], tmp[5];
|
| - pts[0] = fPrevPt;
|
| - pts[1] = pt1;
|
| - pts[2] = pt2;
|
| -
|
| - if (SkChopQuadAtMaxCurvature(pts, tmp) == 2) {
|
| - unitBC.setNormalize(pts[2].fX - pts[1].fX, pts[2].fY - pts[1].fY);
|
| - unitBC.rotateCCW();
|
| - if (normals_too_pinchy(unitAB, unitBC)) {
|
| - normalBC = unitBC;
|
| - normalBC.scale(fRadius);
|
| -
|
| - fOuter.lineTo(tmp[2].fX + normalAB.fX, tmp[2].fY + normalAB.fY);
|
| - fOuter.lineTo(tmp[2].fX + normalBC.fX, tmp[2].fY + normalBC.fY);
|
| - fOuter.lineTo(tmp[4].fX + normalBC.fX, tmp[4].fY + normalBC.fY);
|
| -
|
| - fInner.lineTo(tmp[2].fX - normalAB.fX, tmp[2].fY - normalAB.fY);
|
| - fInner.lineTo(tmp[2].fX - normalBC.fX, tmp[2].fY - normalBC.fY);
|
| - fInner.lineTo(tmp[4].fX - normalBC.fX, tmp[4].fY - normalBC.fY);
|
| -
|
| - fExtra.addCircle(tmp[2].fX, tmp[2].fY, fRadius,
|
| - SkPath::kCW_Direction);
|
| - } else {
|
| - this->quad_to(&tmp[0], normalAB, unitAB, &normalBC, &unitBC,
|
| - kMaxQuadSubdivide);
|
| - SkVector n = normalBC;
|
| - SkVector u = unitBC;
|
| - this->quad_to(&tmp[2], n, u, &normalBC, &unitBC,
|
| - kMaxQuadSubdivide);
|
| - }
|
| - } else {
|
| - this->quad_to(pts, normalAB, unitAB, &normalBC, &unitBC,
|
| - kMaxQuadSubdivide);
|
| - }
|
| - }
|
| -
|
| - this->postJoinTo(pt2, normalBC, unitBC);
|
| -}
|
| -
|
| -void SkPathStroker::cubicTo(const SkPoint& pt1, const SkPoint& pt2,
|
| - const SkPoint& pt3) {
|
| - bool degenerateAB = degenerate_line(fPrevPt, pt1);
|
| - bool degenerateBC = degenerate_line(pt1, pt2);
|
| - bool degenerateCD = degenerate_line(pt2, pt3);
|
| -
|
| - if (degenerateAB + degenerateBC + degenerateCD >= 2) {
|
| - this->lineTo(pt3);
|
| - return;
|
| - }
|
| -
|
| - SkVector normalAB, unitAB, normalCD, unitCD;
|
| -
|
| - // find the first tangent (which might be pt1 or pt2
|
| - {
|
| - const SkPoint* nextPt = &pt1;
|
| - if (degenerateAB)
|
| - nextPt = &pt2;
|
| - this->preJoinTo(*nextPt, &normalAB, &unitAB, false);
|
| - }
|
| -
|
| - {
|
| - SkPoint pts[4], tmp[13];
|
| - int i, count;
|
| - SkVector n, u;
|
| - SkScalar tValues[3];
|
| -
|
| - pts[0] = fPrevPt;
|
| - pts[1] = pt1;
|
| - pts[2] = pt2;
|
| - pts[3] = pt3;
|
| -
|
| -#if 1
|
| - count = SkChopCubicAtMaxCurvature(pts, tmp, tValues);
|
| -#else
|
| - count = 1;
|
| - memcpy(tmp, pts, 4 * sizeof(SkPoint));
|
| -#endif
|
| - n = normalAB;
|
| - u = unitAB;
|
| - for (i = 0; i < count; i++) {
|
| - this->cubic_to(&tmp[i * 3], n, u, &normalCD, &unitCD,
|
| - kMaxCubicSubdivide);
|
| - if (i == count - 1) {
|
| - break;
|
| - }
|
| - n = normalCD;
|
| - u = unitCD;
|
| -
|
| - }
|
| -
|
| - // check for too pinchy
|
| - for (i = 1; i < count; i++) {
|
| - SkPoint p;
|
| - SkVector v, c;
|
| -
|
| - SkEvalCubicAt(pts, tValues[i - 1], &p, &v, &c);
|
| -
|
| - SkScalar dot = SkPoint::DotProduct(c, c);
|
| - v.scale(SkScalarInvert(dot));
|
| -
|
| - if (SkScalarNearlyZero(v.fX) && SkScalarNearlyZero(v.fY)) {
|
| - fExtra.addCircle(p.fX, p.fY, fRadius, SkPath::kCW_Direction);
|
| - }
|
| - }
|
| -
|
| - }
|
| -
|
| - this->postJoinTo(pt3, normalCD, unitCD);
|
| -}
|
| -
|
| -///////////////////////////////////////////////////////////////////////////////
|
| -///////////////////////////////////////////////////////////////////////////////
|
| -
|
| -#include "SkPaint.h"
|
| -
|
| -SkStroke::SkStroke() {
|
| - fWidth = SK_DefaultStrokeWidth;
|
| - fMiterLimit = SK_DefaultMiterLimit;
|
| - fCap = SkPaint::kDefault_Cap;
|
| - fJoin = SkPaint::kDefault_Join;
|
| - fDoFill = false;
|
| -}
|
| -
|
| -SkStroke::SkStroke(const SkPaint& p) {
|
| - fWidth = p.getStrokeWidth();
|
| - fMiterLimit = p.getStrokeMiter();
|
| - fCap = (uint8_t)p.getStrokeCap();
|
| - fJoin = (uint8_t)p.getStrokeJoin();
|
| - fDoFill = SkToU8(p.getStyle() == SkPaint::kStrokeAndFill_Style);
|
| -}
|
| -
|
| -SkStroke::SkStroke(const SkPaint& p, SkScalar width) {
|
| - fWidth = width;
|
| - fMiterLimit = p.getStrokeMiter();
|
| - fCap = (uint8_t)p.getStrokeCap();
|
| - fJoin = (uint8_t)p.getStrokeJoin();
|
| - fDoFill = SkToU8(p.getStyle() == SkPaint::kStrokeAndFill_Style);
|
| -}
|
| -
|
| -void SkStroke::setWidth(SkScalar width) {
|
| - SkASSERT(width >= 0);
|
| - fWidth = width;
|
| -}
|
| -
|
| -void SkStroke::setMiterLimit(SkScalar miterLimit) {
|
| - SkASSERT(miterLimit >= 0);
|
| - fMiterLimit = miterLimit;
|
| -}
|
| -
|
| -void SkStroke::setCap(SkPaint::Cap cap) {
|
| - SkASSERT((unsigned)cap < SkPaint::kCapCount);
|
| - fCap = SkToU8(cap);
|
| -}
|
| -
|
| -void SkStroke::setJoin(SkPaint::Join join) {
|
| - SkASSERT((unsigned)join < SkPaint::kJoinCount);
|
| - fJoin = SkToU8(join);
|
| -}
|
| -
|
| -///////////////////////////////////////////////////////////////////////////////
|
| -
|
| -#ifdef SK_SCALAR_IS_FIXED
|
| - /* return non-zero if the path is too big, and should be shrunk to avoid
|
| - overflows during intermediate calculations. Note that we compute the
|
| - bounds for this. If we had a custom callback/walker for paths, we could
|
| - perhaps go faster by using that, and just perform the abs | in that
|
| - routine
|
| - */
|
| - static int needs_to_shrink(const SkPath& path) {
|
| - SkRect r;
|
| - path.computeBounds(&r, SkPath::kFast_BoundsType);
|
| - SkFixed mask = SkAbs32(r.fLeft);
|
| - mask |= SkAbs32(r.fTop);
|
| - mask |= SkAbs32(r.fRight);
|
| - mask |= SkAbs32(r.fBottom);
|
| - // we need the top 3 bits clear (after abs) to avoid overflow
|
| - return mask >> 29;
|
| - }
|
| -
|
| - static void identity_proc(SkPoint pts[], int count) {}
|
| - static void shift_down_2_proc(SkPoint pts[], int count) {
|
| - for (int i = 0; i < count; i++) {
|
| - pts->fX >>= 2;
|
| - pts->fY >>= 2;
|
| - pts += 1;
|
| - }
|
| - }
|
| - #define APPLY_PROC(proc, pts, count) proc(pts, count)
|
| -#else // float does need any of this
|
| - #define APPLY_PROC(proc, pts, count)
|
| -#endif
|
| -
|
| -void SkStroke::strokePath(const SkPath& src, SkPath* dst) const {
|
| - SkASSERT(&src != NULL && dst != NULL);
|
| -
|
| - SkScalar radius = SkScalarHalf(fWidth);
|
| -
|
| - dst->reset();
|
| - if (radius <= 0) {
|
| - return;
|
| - }
|
| -
|
| -#ifdef SK_SCALAR_IS_FIXED
|
| - void (*proc)(SkPoint pts[], int count) = identity_proc;
|
| - if (needs_to_shrink(src)) {
|
| - proc = shift_down_2_proc;
|
| - radius >>= 2;
|
| - if (radius == 0) {
|
| - return;
|
| - }
|
| - }
|
| -#endif
|
| -
|
| - SkPathStroker stroker(radius, fMiterLimit, this->getCap(),
|
| - this->getJoin());
|
| -
|
| - SkPath::Iter iter(src, false);
|
| - SkPoint pts[4];
|
| - SkPath::Verb verb, lastSegment = SkPath::kMove_Verb;
|
| -
|
| - while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
|
| - switch (verb) {
|
| - case SkPath::kMove_Verb:
|
| - APPLY_PROC(proc, &pts[0], 1);
|
| - stroker.moveTo(pts[0]);
|
| - break;
|
| - case SkPath::kLine_Verb:
|
| - APPLY_PROC(proc, &pts[1], 1);
|
| - stroker.lineTo(pts[1]);
|
| - lastSegment = verb;
|
| - break;
|
| - case SkPath::kQuad_Verb:
|
| - APPLY_PROC(proc, &pts[1], 2);
|
| - stroker.quadTo(pts[1], pts[2]);
|
| - lastSegment = verb;
|
| - break;
|
| - case SkPath::kCubic_Verb:
|
| - APPLY_PROC(proc, &pts[1], 3);
|
| - stroker.cubicTo(pts[1], pts[2], pts[3]);
|
| - lastSegment = verb;
|
| - break;
|
| - case SkPath::kClose_Verb:
|
| - stroker.close(lastSegment == SkPath::kLine_Verb);
|
| - break;
|
| - default:
|
| - break;
|
| - }
|
| - }
|
| - stroker.done(dst, lastSegment == SkPath::kLine_Verb);
|
| -
|
| -#ifdef SK_SCALAR_IS_FIXED
|
| - // undo our previous down_shift
|
| - if (shift_down_2_proc == proc) {
|
| - // need a real shift methid on path. antialias paths could use this too
|
| - SkMatrix matrix;
|
| - matrix.setScale(SkIntToScalar(4), SkIntToScalar(4));
|
| - dst->transform(matrix);
|
| - }
|
| -#endif
|
| -
|
| - if (fDoFill) {
|
| - dst->addPath(src);
|
| - }
|
| -}
|
| -
|
| -void SkStroke::strokeLine(const SkPoint& p0, const SkPoint& p1,
|
| - SkPath* dst) const {
|
| - SkPath tmp;
|
| -
|
| - tmp.moveTo(p0);
|
| - tmp.lineTo(p1);
|
| - this->strokePath(tmp, dst);
|
| -}
|
| -
|
|
|