1cb93a386Sopenharmony_ci/* 2cb93a386Sopenharmony_ci * Copyright 2006 The Android Open Source Project 3cb93a386Sopenharmony_ci * 4cb93a386Sopenharmony_ci * Use of this source code is governed by a BSD-style license that can be 5cb93a386Sopenharmony_ci * found in the LICENSE file. 6cb93a386Sopenharmony_ci */ 7cb93a386Sopenharmony_ci 8cb93a386Sopenharmony_ci#include "include/core/SkPath.h" 9cb93a386Sopenharmony_ci#include "src/core/SkGeometry.h" 10cb93a386Sopenharmony_ci#include "src/core/SkPointPriv.h" 11cb93a386Sopenharmony_ci#include "src/core/SkStrokerPriv.h" 12cb93a386Sopenharmony_ci 13cb93a386Sopenharmony_ci#include <utility> 14cb93a386Sopenharmony_ci 15cb93a386Sopenharmony_cistatic void ButtCapper(SkPath* path, const SkPoint& pivot, const SkVector& normal, 16cb93a386Sopenharmony_ci const SkPoint& stop, SkPath*) { 17cb93a386Sopenharmony_ci path->lineTo(stop.fX, stop.fY); 18cb93a386Sopenharmony_ci} 19cb93a386Sopenharmony_ci 20cb93a386Sopenharmony_cistatic void RoundCapper(SkPath* path, const SkPoint& pivot, const SkVector& normal, 21cb93a386Sopenharmony_ci const SkPoint& stop, SkPath*) { 22cb93a386Sopenharmony_ci SkVector parallel; 23cb93a386Sopenharmony_ci SkPointPriv::RotateCW(normal, ¶llel); 24cb93a386Sopenharmony_ci 25cb93a386Sopenharmony_ci SkPoint projectedCenter = pivot + parallel; 26cb93a386Sopenharmony_ci 27cb93a386Sopenharmony_ci path->conicTo(projectedCenter + normal, projectedCenter, SK_ScalarRoot2Over2); 28cb93a386Sopenharmony_ci path->conicTo(projectedCenter - normal, stop, SK_ScalarRoot2Over2); 29cb93a386Sopenharmony_ci} 30cb93a386Sopenharmony_ci 31cb93a386Sopenharmony_cistatic void SquareCapper(SkPath* path, const SkPoint& pivot, const SkVector& normal, 32cb93a386Sopenharmony_ci const SkPoint& stop, SkPath* otherPath) { 33cb93a386Sopenharmony_ci SkVector parallel; 34cb93a386Sopenharmony_ci SkPointPriv::RotateCW(normal, ¶llel); 35cb93a386Sopenharmony_ci 36cb93a386Sopenharmony_ci if (otherPath) { 37cb93a386Sopenharmony_ci path->setLastPt(pivot.fX + normal.fX + parallel.fX, pivot.fY + normal.fY + parallel.fY); 38cb93a386Sopenharmony_ci path->lineTo(pivot.fX - normal.fX + parallel.fX, pivot.fY - normal.fY + parallel.fY); 39cb93a386Sopenharmony_ci } else { 40cb93a386Sopenharmony_ci path->lineTo(pivot.fX + normal.fX + parallel.fX, pivot.fY + normal.fY + parallel.fY); 41cb93a386Sopenharmony_ci path->lineTo(pivot.fX - normal.fX + parallel.fX, pivot.fY - normal.fY + parallel.fY); 42cb93a386Sopenharmony_ci path->lineTo(stop.fX, stop.fY); 43cb93a386Sopenharmony_ci } 44cb93a386Sopenharmony_ci} 45cb93a386Sopenharmony_ci 46cb93a386Sopenharmony_ci///////////////////////////////////////////////////////////////////////////// 47cb93a386Sopenharmony_ci 48cb93a386Sopenharmony_cistatic bool is_clockwise(const SkVector& before, const SkVector& after) { 49cb93a386Sopenharmony_ci return before.fX * after.fY > before.fY * after.fX; 50cb93a386Sopenharmony_ci} 51cb93a386Sopenharmony_ci 52cb93a386Sopenharmony_cienum AngleType { 53cb93a386Sopenharmony_ci kNearly180_AngleType, 54cb93a386Sopenharmony_ci kSharp_AngleType, 55cb93a386Sopenharmony_ci kShallow_AngleType, 56cb93a386Sopenharmony_ci kNearlyLine_AngleType 57cb93a386Sopenharmony_ci}; 58cb93a386Sopenharmony_ci 59cb93a386Sopenharmony_cistatic AngleType Dot2AngleType(SkScalar dot) { 60cb93a386Sopenharmony_ci// need more precise fixed normalization 61cb93a386Sopenharmony_ci// SkASSERT(SkScalarAbs(dot) <= SK_Scalar1 + SK_ScalarNearlyZero); 62cb93a386Sopenharmony_ci 63cb93a386Sopenharmony_ci if (dot >= 0) { // shallow or line 64cb93a386Sopenharmony_ci return SkScalarNearlyZero(SK_Scalar1 - dot) ? kNearlyLine_AngleType : kShallow_AngleType; 65cb93a386Sopenharmony_ci } else { // sharp or 180 66cb93a386Sopenharmony_ci return SkScalarNearlyZero(SK_Scalar1 + dot) ? kNearly180_AngleType : kSharp_AngleType; 67cb93a386Sopenharmony_ci } 68cb93a386Sopenharmony_ci} 69cb93a386Sopenharmony_ci 70cb93a386Sopenharmony_cistatic void HandleInnerJoin(SkPath* inner, const SkPoint& pivot, const SkVector& after) { 71cb93a386Sopenharmony_ci#if 1 72cb93a386Sopenharmony_ci /* In the degenerate case that the stroke radius is larger than our segments 73cb93a386Sopenharmony_ci just connecting the two inner segments may "show through" as a funny 74cb93a386Sopenharmony_ci diagonal. To pseudo-fix this, we go through the pivot point. This adds 75cb93a386Sopenharmony_ci an extra point/edge, but I can't see a cheap way to know when this is 76cb93a386Sopenharmony_ci not needed :( 77cb93a386Sopenharmony_ci */ 78cb93a386Sopenharmony_ci inner->lineTo(pivot.fX, pivot.fY); 79cb93a386Sopenharmony_ci#endif 80cb93a386Sopenharmony_ci 81cb93a386Sopenharmony_ci inner->lineTo(pivot.fX - after.fX, pivot.fY - after.fY); 82cb93a386Sopenharmony_ci} 83cb93a386Sopenharmony_ci 84cb93a386Sopenharmony_cistatic void BluntJoiner(SkPath* outer, SkPath* inner, const SkVector& beforeUnitNormal, 85cb93a386Sopenharmony_ci const SkPoint& pivot, const SkVector& afterUnitNormal, 86cb93a386Sopenharmony_ci SkScalar radius, SkScalar invMiterLimit, bool, bool) { 87cb93a386Sopenharmony_ci SkVector after; 88cb93a386Sopenharmony_ci afterUnitNormal.scale(radius, &after); 89cb93a386Sopenharmony_ci 90cb93a386Sopenharmony_ci if (!is_clockwise(beforeUnitNormal, afterUnitNormal)) { 91cb93a386Sopenharmony_ci using std::swap; 92cb93a386Sopenharmony_ci swap(outer, inner); 93cb93a386Sopenharmony_ci after.negate(); 94cb93a386Sopenharmony_ci } 95cb93a386Sopenharmony_ci 96cb93a386Sopenharmony_ci outer->lineTo(pivot.fX + after.fX, pivot.fY + after.fY); 97cb93a386Sopenharmony_ci HandleInnerJoin(inner, pivot, after); 98cb93a386Sopenharmony_ci} 99cb93a386Sopenharmony_ci 100cb93a386Sopenharmony_cistatic void RoundJoiner(SkPath* outer, SkPath* inner, const SkVector& beforeUnitNormal, 101cb93a386Sopenharmony_ci const SkPoint& pivot, const SkVector& afterUnitNormal, 102cb93a386Sopenharmony_ci SkScalar radius, SkScalar invMiterLimit, bool, bool) { 103cb93a386Sopenharmony_ci SkScalar dotProd = SkPoint::DotProduct(beforeUnitNormal, afterUnitNormal); 104cb93a386Sopenharmony_ci AngleType angleType = Dot2AngleType(dotProd); 105cb93a386Sopenharmony_ci 106cb93a386Sopenharmony_ci if (angleType == kNearlyLine_AngleType) 107cb93a386Sopenharmony_ci return; 108cb93a386Sopenharmony_ci 109cb93a386Sopenharmony_ci SkVector before = beforeUnitNormal; 110cb93a386Sopenharmony_ci SkVector after = afterUnitNormal; 111cb93a386Sopenharmony_ci SkRotationDirection dir = kCW_SkRotationDirection; 112cb93a386Sopenharmony_ci 113cb93a386Sopenharmony_ci if (!is_clockwise(before, after)) { 114cb93a386Sopenharmony_ci using std::swap; 115cb93a386Sopenharmony_ci swap(outer, inner); 116cb93a386Sopenharmony_ci before.negate(); 117cb93a386Sopenharmony_ci after.negate(); 118cb93a386Sopenharmony_ci dir = kCCW_SkRotationDirection; 119cb93a386Sopenharmony_ci } 120cb93a386Sopenharmony_ci 121cb93a386Sopenharmony_ci SkMatrix matrix; 122cb93a386Sopenharmony_ci matrix.setScale(radius, radius); 123cb93a386Sopenharmony_ci matrix.postTranslate(pivot.fX, pivot.fY); 124cb93a386Sopenharmony_ci SkConic conics[SkConic::kMaxConicsForArc]; 125cb93a386Sopenharmony_ci int count = SkConic::BuildUnitArc(before, after, dir, &matrix, conics); 126cb93a386Sopenharmony_ci if (count > 0) { 127cb93a386Sopenharmony_ci for (int i = 0; i < count; ++i) { 128cb93a386Sopenharmony_ci outer->conicTo(conics[i].fPts[1], conics[i].fPts[2], conics[i].fW); 129cb93a386Sopenharmony_ci } 130cb93a386Sopenharmony_ci after.scale(radius); 131cb93a386Sopenharmony_ci HandleInnerJoin(inner, pivot, after); 132cb93a386Sopenharmony_ci } 133cb93a386Sopenharmony_ci} 134cb93a386Sopenharmony_ci 135cb93a386Sopenharmony_ci#define kOneOverSqrt2 (0.707106781f) 136cb93a386Sopenharmony_ci 137cb93a386Sopenharmony_cistatic void MiterJoiner(SkPath* outer, SkPath* inner, const SkVector& beforeUnitNormal, 138cb93a386Sopenharmony_ci const SkPoint& pivot, const SkVector& afterUnitNormal, 139cb93a386Sopenharmony_ci SkScalar radius, SkScalar invMiterLimit, 140cb93a386Sopenharmony_ci bool prevIsLine, bool currIsLine) { 141cb93a386Sopenharmony_ci // negate the dot since we're using normals instead of tangents 142cb93a386Sopenharmony_ci SkScalar dotProd = SkPoint::DotProduct(beforeUnitNormal, afterUnitNormal); 143cb93a386Sopenharmony_ci AngleType angleType = Dot2AngleType(dotProd); 144cb93a386Sopenharmony_ci SkVector before = beforeUnitNormal; 145cb93a386Sopenharmony_ci SkVector after = afterUnitNormal; 146cb93a386Sopenharmony_ci SkVector mid; 147cb93a386Sopenharmony_ci SkScalar sinHalfAngle; 148cb93a386Sopenharmony_ci bool ccw; 149cb93a386Sopenharmony_ci 150cb93a386Sopenharmony_ci if (angleType == kNearlyLine_AngleType) { 151cb93a386Sopenharmony_ci return; 152cb93a386Sopenharmony_ci } 153cb93a386Sopenharmony_ci if (angleType == kNearly180_AngleType) { 154cb93a386Sopenharmony_ci currIsLine = false; 155cb93a386Sopenharmony_ci goto DO_BLUNT; 156cb93a386Sopenharmony_ci } 157cb93a386Sopenharmony_ci 158cb93a386Sopenharmony_ci ccw = !is_clockwise(before, after); 159cb93a386Sopenharmony_ci if (ccw) { 160cb93a386Sopenharmony_ci using std::swap; 161cb93a386Sopenharmony_ci swap(outer, inner); 162cb93a386Sopenharmony_ci before.negate(); 163cb93a386Sopenharmony_ci after.negate(); 164cb93a386Sopenharmony_ci } 165cb93a386Sopenharmony_ci 166cb93a386Sopenharmony_ci /* Before we enter the world of square-roots and divides, 167cb93a386Sopenharmony_ci check if we're trying to join an upright right angle 168cb93a386Sopenharmony_ci (common case for stroking rectangles). If so, special case 169cb93a386Sopenharmony_ci that (for speed an accuracy). 170cb93a386Sopenharmony_ci Note: we only need to check one normal if dot==0 171cb93a386Sopenharmony_ci */ 172cb93a386Sopenharmony_ci if (0 == dotProd && invMiterLimit <= kOneOverSqrt2) { 173cb93a386Sopenharmony_ci mid = (before + after) * radius; 174cb93a386Sopenharmony_ci goto DO_MITER; 175cb93a386Sopenharmony_ci } 176cb93a386Sopenharmony_ci 177cb93a386Sopenharmony_ci /* midLength = radius / sinHalfAngle 178cb93a386Sopenharmony_ci if (midLength > miterLimit * radius) abort 179cb93a386Sopenharmony_ci if (radius / sinHalf > miterLimit * radius) abort 180cb93a386Sopenharmony_ci if (1 / sinHalf > miterLimit) abort 181cb93a386Sopenharmony_ci if (1 / miterLimit > sinHalf) abort 182cb93a386Sopenharmony_ci My dotProd is opposite sign, since it is built from normals and not tangents 183cb93a386Sopenharmony_ci hence 1 + dot instead of 1 - dot in the formula 184cb93a386Sopenharmony_ci */ 185cb93a386Sopenharmony_ci sinHalfAngle = SkScalarSqrt(SkScalarHalf(SK_Scalar1 + dotProd)); 186cb93a386Sopenharmony_ci if (sinHalfAngle < invMiterLimit) { 187cb93a386Sopenharmony_ci currIsLine = false; 188cb93a386Sopenharmony_ci goto DO_BLUNT; 189cb93a386Sopenharmony_ci } 190cb93a386Sopenharmony_ci 191cb93a386Sopenharmony_ci // choose the most accurate way to form the initial mid-vector 192cb93a386Sopenharmony_ci if (angleType == kSharp_AngleType) { 193cb93a386Sopenharmony_ci mid.set(after.fY - before.fY, before.fX - after.fX); 194cb93a386Sopenharmony_ci if (ccw) { 195cb93a386Sopenharmony_ci mid.negate(); 196cb93a386Sopenharmony_ci } 197cb93a386Sopenharmony_ci } else { 198cb93a386Sopenharmony_ci mid.set(before.fX + after.fX, before.fY + after.fY); 199cb93a386Sopenharmony_ci } 200cb93a386Sopenharmony_ci 201cb93a386Sopenharmony_ci mid.setLength(radius / sinHalfAngle); 202cb93a386Sopenharmony_ciDO_MITER: 203cb93a386Sopenharmony_ci if (prevIsLine) { 204cb93a386Sopenharmony_ci outer->setLastPt(pivot.fX + mid.fX, pivot.fY + mid.fY); 205cb93a386Sopenharmony_ci } else { 206cb93a386Sopenharmony_ci outer->lineTo(pivot.fX + mid.fX, pivot.fY + mid.fY); 207cb93a386Sopenharmony_ci } 208cb93a386Sopenharmony_ci 209cb93a386Sopenharmony_ciDO_BLUNT: 210cb93a386Sopenharmony_ci after.scale(radius); 211cb93a386Sopenharmony_ci if (!currIsLine) { 212cb93a386Sopenharmony_ci outer->lineTo(pivot.fX + after.fX, pivot.fY + after.fY); 213cb93a386Sopenharmony_ci } 214cb93a386Sopenharmony_ci HandleInnerJoin(inner, pivot, after); 215cb93a386Sopenharmony_ci} 216cb93a386Sopenharmony_ci 217cb93a386Sopenharmony_ci///////////////////////////////////////////////////////////////////////////// 218cb93a386Sopenharmony_ci 219cb93a386Sopenharmony_ciSkStrokerPriv::CapProc SkStrokerPriv::CapFactory(SkPaint::Cap cap) { 220cb93a386Sopenharmony_ci const SkStrokerPriv::CapProc gCappers[] = { 221cb93a386Sopenharmony_ci ButtCapper, RoundCapper, SquareCapper 222cb93a386Sopenharmony_ci }; 223cb93a386Sopenharmony_ci 224cb93a386Sopenharmony_ci SkASSERT((unsigned)cap < SkPaint::kCapCount); 225cb93a386Sopenharmony_ci return gCappers[cap]; 226cb93a386Sopenharmony_ci} 227cb93a386Sopenharmony_ci 228cb93a386Sopenharmony_ciSkStrokerPriv::JoinProc SkStrokerPriv::JoinFactory(SkPaint::Join join) { 229cb93a386Sopenharmony_ci const SkStrokerPriv::JoinProc gJoiners[] = { 230cb93a386Sopenharmony_ci MiterJoiner, RoundJoiner, BluntJoiner 231cb93a386Sopenharmony_ci }; 232cb93a386Sopenharmony_ci 233cb93a386Sopenharmony_ci SkASSERT((unsigned)join < SkPaint::kJoinCount); 234cb93a386Sopenharmony_ci return gJoiners[join]; 235cb93a386Sopenharmony_ci} 236