diff options
Diffstat (limited to 'skia/sgl/SkStrokerPriv.cpp')
-rw-r--r-- | skia/sgl/SkStrokerPriv.cpp | 275 |
1 files changed, 275 insertions, 0 deletions
diff --git a/skia/sgl/SkStrokerPriv.cpp b/skia/sgl/SkStrokerPriv.cpp new file mode 100644 index 0000000..28276cb --- /dev/null +++ b/skia/sgl/SkStrokerPriv.cpp @@ -0,0 +1,275 @@ +/* libs/graphics/sgl/SkStrokerPriv.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 "SkStrokerPriv.h" +#include "SkGeometry.h" +#include "SkPath.h" + +static void ButtCapper(SkPath* path, const SkPoint& pivot, + const SkVector& normal, const SkPoint& stop, + SkPath*) +{ + path->lineTo(stop.fX, stop.fY); +} + +static void RoundCapper(SkPath* path, const SkPoint& pivot, + const SkVector& normal, const SkPoint& stop, + SkPath*) +{ + SkScalar px = pivot.fX; + SkScalar py = pivot.fY; + SkScalar nx = normal.fX; + SkScalar ny = normal.fY; + SkScalar sx = SkScalarMul(nx, CUBIC_ARC_FACTOR); + SkScalar sy = SkScalarMul(ny, CUBIC_ARC_FACTOR); + + path->cubicTo(px + nx + CWX(sx, sy), py + ny + CWY(sx, sy), + px + CWX(nx, ny) + sx, py + CWY(nx, ny) + sy, + px + CWX(nx, ny), py + CWY(nx, ny)); + path->cubicTo(px + CWX(nx, ny) - sx, py + CWY(nx, ny) - sy, + px - nx + CWX(sx, sy), py - ny + CWY(sx, sy), + stop.fX, stop.fY); +} + +static void SquareCapper(SkPath* path, const SkPoint& pivot, + const SkVector& normal, const SkPoint& stop, + SkPath* otherPath) +{ + SkVector parallel; + normal.rotateCW(¶llel); + + if (otherPath) + { + path->setLastPt(pivot.fX + normal.fX + parallel.fX, pivot.fY + normal.fY + parallel.fY); + path->lineTo(pivot.fX - normal.fX + parallel.fX, pivot.fY - normal.fY + parallel.fY); + } + else + { + path->lineTo(pivot.fX + normal.fX + parallel.fX, pivot.fY + normal.fY + parallel.fY); + path->lineTo(pivot.fX - normal.fX + parallel.fX, pivot.fY - normal.fY + parallel.fY); + path->lineTo(stop.fX, stop.fY); + } +} + +///////////////////////////////////////////////////////////////////////////// + +static bool is_clockwise(const SkVector& before, const SkVector& after) +{ + return SkScalarMul(before.fX, after.fY) - SkScalarMul(before.fY, after.fX) > 0; +} + +enum AngleType { + kNearly180_AngleType, + kSharp_AngleType, + kShallow_AngleType, + kNearlyLine_AngleType +}; + +static AngleType Dot2AngleType(SkScalar dot) +{ +// need more precise fixed normalization +// SkASSERT(SkScalarAbs(dot) <= SK_Scalar1 + SK_ScalarNearlyZero); + + if (dot >= 0) // shallow or line + return SkScalarNearlyZero(SK_Scalar1 - dot) ? kNearlyLine_AngleType : kShallow_AngleType; + else // sharp or 180 + return SkScalarNearlyZero(SK_Scalar1 + dot) ? kNearly180_AngleType : kSharp_AngleType; +} + +static void HandleInnerJoin(SkPath* inner, const SkPoint& pivot, const SkVector& after) +{ +#if 1 + /* In the degenerate case that the stroke radius is larger than our segments + just connecting the two inner segments may "show through" as a funny + diagonal. To pseudo-fix this, we go through the pivot point. This adds + an extra point/edge, but I can't see a cheap way to know when this is + not needed :( + */ + inner->lineTo(pivot.fX, pivot.fY); +#endif + + inner->lineTo(pivot.fX - after.fX, pivot.fY - after.fY); +} + +static void BluntJoiner(SkPath* outer, SkPath* inner, const SkVector& beforeUnitNormal, + const SkPoint& pivot, const SkVector& afterUnitNormal, + SkScalar radius, SkScalar invMiterLimit, bool, bool) +{ + SkVector after; + afterUnitNormal.scale(radius, &after); + + if (!is_clockwise(beforeUnitNormal, afterUnitNormal)) + { + SkTSwap<SkPath*>(outer, inner); + after.negate(); + } + + outer->lineTo(pivot.fX + after.fX, pivot.fY + after.fY); + HandleInnerJoin(inner, pivot, after); +} + +static void RoundJoiner(SkPath* outer, SkPath* inner, const SkVector& beforeUnitNormal, + const SkPoint& pivot, const SkVector& afterUnitNormal, + SkScalar radius, SkScalar invMiterLimit, bool, bool) +{ + SkScalar dotProd = SkPoint::DotProduct(beforeUnitNormal, afterUnitNormal); + AngleType angleType = Dot2AngleType(dotProd); + + if (angleType == kNearlyLine_AngleType) + return; + + SkVector before = beforeUnitNormal; + SkVector after = afterUnitNormal; + SkRotationDirection dir = kCW_SkRotationDirection; + + if (!is_clockwise(before, after)) + { + SkTSwap<SkPath*>(outer, inner); + before.negate(); + after.negate(); + dir = kCCW_SkRotationDirection; + } + + SkPoint pts[kSkBuildQuadArcStorage]; + SkMatrix matrix; + matrix.setScale(radius, radius); + matrix.postTranslate(pivot.fX, pivot.fY); + int count = SkBuildQuadArc(before, after, dir, &matrix, pts); + SkASSERT((count & 1) == 1); + + if (count > 1) + { + for (int i = 1; i < count; i += 2) + outer->quadTo(pts[i].fX, pts[i].fY, pts[i+1].fX, pts[i+1].fY); + + after.scale(radius); + HandleInnerJoin(inner, pivot, after); + } +} + +#ifdef SK_SCALAR_IS_FLOAT + #define kOneOverSqrt2 (0.707106781f) +#else + #define kOneOverSqrt2 (46341) +#endif + +static void MiterJoiner(SkPath* outer, SkPath* inner, const SkVector& beforeUnitNormal, + const SkPoint& pivot, const SkVector& afterUnitNormal, + SkScalar radius, SkScalar invMiterLimit, + bool prevIsLine, bool currIsLine) +{ + // negate the dot since we're using normals instead of tangents + SkScalar dotProd = SkPoint::DotProduct(beforeUnitNormal, afterUnitNormal); + AngleType angleType = Dot2AngleType(dotProd); + SkVector before = beforeUnitNormal; + SkVector after = afterUnitNormal; + SkVector mid; + SkScalar sinHalfAngle; + bool ccw; + + if (angleType == kNearlyLine_AngleType) + return; + if (angleType == kNearly180_AngleType) + { + currIsLine = false; + goto DO_BLUNT; + } + + ccw = !is_clockwise(before, after); + if (ccw) + { + SkTSwap<SkPath*>(outer, inner); + before.negate(); + after.negate(); + } + + /* Before we enter the world of square-roots and divides, + check if we're trying to join an upright right angle + (common case for stroking rectangles). If so, special case + that (for speed an accuracy). + Note: we only need to check one normal if dot==0 + */ + if (0 == dotProd && invMiterLimit <= kOneOverSqrt2) + { + mid.set(SkScalarMul(before.fX + after.fX, radius), + SkScalarMul(before.fY + after.fY, radius)); + goto DO_MITER; + } + + /* midLength = radius / sinHalfAngle + if (midLength > miterLimit * radius) abort + if (radius / sinHalf > miterLimit * radius) abort + if (1 / sinHalf > miterLimit) abort + if (1 / miterLimit > sinHalf) abort + My dotProd is opposite sign, since it is built from normals and not tangents + hence 1 + dot instead of 1 - dot in the formula + */ + sinHalfAngle = SkScalarSqrt(SkScalarHalf(SK_Scalar1 + dotProd)); + if (sinHalfAngle < invMiterLimit) + { + currIsLine = false; + goto DO_BLUNT; + } + + // choose the most accurate way to form the initial mid-vector + if (angleType == kSharp_AngleType) + { + mid.set(after.fY - before.fY, before.fX - after.fX); + if (ccw) + mid.negate(); + } + else + mid.set(before.fX + after.fX, before.fY + after.fY); + + mid.setLength(SkScalarDiv(radius, sinHalfAngle)); +DO_MITER: + if (prevIsLine) + outer->setLastPt(pivot.fX + mid.fX, pivot.fY + mid.fY); + else + outer->lineTo(pivot.fX + mid.fX, pivot.fY + mid.fY); + +DO_BLUNT: + after.scale(radius); + if (!currIsLine) + outer->lineTo(pivot.fX + after.fX, pivot.fY + after.fY); + HandleInnerJoin(inner, pivot, after); +} + +///////////////////////////////////////////////////////////////////////////// + +SkStrokerPriv::CapProc SkStrokerPriv::CapFactory(SkPaint::Cap cap) +{ + static const SkStrokerPriv::CapProc gCappers[] = { + ButtCapper, RoundCapper, SquareCapper + }; + + SkASSERT((unsigned)cap < SkPaint::kCapCount); + return gCappers[cap]; +} + +SkStrokerPriv::JoinProc SkStrokerPriv::JoinFactory(SkPaint::Join join) +{ + static const SkStrokerPriv::JoinProc gJoiners[] = { + MiterJoiner, RoundJoiner, BluntJoiner + }; + + SkASSERT((unsigned)join < SkPaint::kJoinCount); + return gJoiners[join]; +} + + + |