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 10cb93a386Sopenharmony_ci#include "include/core/SkData.h" 11cb93a386Sopenharmony_ci#include "include/core/SkMath.h" 12cb93a386Sopenharmony_ci#include "include/core/SkPathBuilder.h" 13cb93a386Sopenharmony_ci#include "include/core/SkRRect.h" 14cb93a386Sopenharmony_ci#include "include/private/SkMacros.h" 15cb93a386Sopenharmony_ci#include "include/private/SkPathRef.h" 16cb93a386Sopenharmony_ci#include "include/private/SkTo.h" 17cb93a386Sopenharmony_ci#include "src/core/SkBuffer.h" 18cb93a386Sopenharmony_ci#include "src/core/SkCubicClipper.h" 19cb93a386Sopenharmony_ci#include "src/core/SkGeometry.h" 20cb93a386Sopenharmony_ci#include "src/core/SkMatrixPriv.h" 21cb93a386Sopenharmony_ci#include "src/core/SkPathMakers.h" 22cb93a386Sopenharmony_ci#include "src/core/SkPathPriv.h" 23cb93a386Sopenharmony_ci#include "src/core/SkPointPriv.h" 24cb93a386Sopenharmony_ci#include "src/core/SkSafeMath.h" 25cb93a386Sopenharmony_ci#include "src/core/SkTLazy.h" 26cb93a386Sopenharmony_ci// need SkDVector 27cb93a386Sopenharmony_ci#include "src/pathops/SkPathOpsPoint.h" 28cb93a386Sopenharmony_ci 29cb93a386Sopenharmony_ci#include <cmath> 30cb93a386Sopenharmony_ci#include <utility> 31cb93a386Sopenharmony_ci 32cb93a386Sopenharmony_cistruct SkPath_Storage_Equivalent { 33cb93a386Sopenharmony_ci void* fPtr; 34cb93a386Sopenharmony_ci int32_t fIndex; 35cb93a386Sopenharmony_ci uint32_t fFlags; 36cb93a386Sopenharmony_ci}; 37cb93a386Sopenharmony_ci 38cb93a386Sopenharmony_cistatic_assert(sizeof(SkPath) == sizeof(SkPath_Storage_Equivalent), 39cb93a386Sopenharmony_ci "Please keep an eye on SkPath packing."); 40cb93a386Sopenharmony_ci 41cb93a386Sopenharmony_cistatic float poly_eval(float A, float B, float C, float t) { 42cb93a386Sopenharmony_ci return (A * t + B) * t + C; 43cb93a386Sopenharmony_ci} 44cb93a386Sopenharmony_ci 45cb93a386Sopenharmony_cistatic float poly_eval(float A, float B, float C, float D, float t) { 46cb93a386Sopenharmony_ci return ((A * t + B) * t + C) * t + D; 47cb93a386Sopenharmony_ci} 48cb93a386Sopenharmony_ci 49cb93a386Sopenharmony_ci//////////////////////////////////////////////////////////////////////////// 50cb93a386Sopenharmony_ci 51cb93a386Sopenharmony_ci/** 52cb93a386Sopenharmony_ci * Path.bounds is defined to be the bounds of all the control points. 53cb93a386Sopenharmony_ci * If we called bounds.join(r) we would skip r if r was empty, which breaks 54cb93a386Sopenharmony_ci * our promise. Hence we have a custom joiner that doesn't look at emptiness 55cb93a386Sopenharmony_ci */ 56cb93a386Sopenharmony_cistatic void joinNoEmptyChecks(SkRect* dst, const SkRect& src) { 57cb93a386Sopenharmony_ci dst->fLeft = std::min(dst->fLeft, src.fLeft); 58cb93a386Sopenharmony_ci dst->fTop = std::min(dst->fTop, src.fTop); 59cb93a386Sopenharmony_ci dst->fRight = std::max(dst->fRight, src.fRight); 60cb93a386Sopenharmony_ci dst->fBottom = std::max(dst->fBottom, src.fBottom); 61cb93a386Sopenharmony_ci} 62cb93a386Sopenharmony_ci 63cb93a386Sopenharmony_cistatic bool is_degenerate(const SkPath& path) { 64cb93a386Sopenharmony_ci return (path.countVerbs() - SkPathPriv::LeadingMoveToCount(path)) == 0; 65cb93a386Sopenharmony_ci} 66cb93a386Sopenharmony_ci 67cb93a386Sopenharmony_ciclass SkAutoDisableDirectionCheck { 68cb93a386Sopenharmony_cipublic: 69cb93a386Sopenharmony_ci SkAutoDisableDirectionCheck(SkPath* path) : fPath(path) { 70cb93a386Sopenharmony_ci fSaved = static_cast<SkPathFirstDirection>(fPath->getFirstDirection()); 71cb93a386Sopenharmony_ci } 72cb93a386Sopenharmony_ci 73cb93a386Sopenharmony_ci ~SkAutoDisableDirectionCheck() { 74cb93a386Sopenharmony_ci fPath->setFirstDirection(fSaved); 75cb93a386Sopenharmony_ci } 76cb93a386Sopenharmony_ci 77cb93a386Sopenharmony_ciprivate: 78cb93a386Sopenharmony_ci SkPath* fPath; 79cb93a386Sopenharmony_ci SkPathFirstDirection fSaved; 80cb93a386Sopenharmony_ci}; 81cb93a386Sopenharmony_ci 82cb93a386Sopenharmony_ci/* This class's constructor/destructor bracket a path editing operation. It is 83cb93a386Sopenharmony_ci used when we know the bounds of the amount we are going to add to the path 84cb93a386Sopenharmony_ci (usually a new contour, but not required). 85cb93a386Sopenharmony_ci 86cb93a386Sopenharmony_ci It captures some state about the path up front (i.e. if it already has a 87cb93a386Sopenharmony_ci cached bounds), and then if it can, it updates the cache bounds explicitly, 88cb93a386Sopenharmony_ci avoiding the need to revisit all of the points in getBounds(). 89cb93a386Sopenharmony_ci 90cb93a386Sopenharmony_ci It also notes if the path was originally degenerate, and if so, sets 91cb93a386Sopenharmony_ci isConvex to true. Thus it can only be used if the contour being added is 92cb93a386Sopenharmony_ci convex. 93cb93a386Sopenharmony_ci */ 94cb93a386Sopenharmony_ciclass SkAutoPathBoundsUpdate { 95cb93a386Sopenharmony_cipublic: 96cb93a386Sopenharmony_ci SkAutoPathBoundsUpdate(SkPath* path, const SkRect& r) : fPath(path), fRect(r) { 97cb93a386Sopenharmony_ci // Cannot use fRect for our bounds unless we know it is sorted 98cb93a386Sopenharmony_ci fRect.sort(); 99cb93a386Sopenharmony_ci // Mark the path's bounds as dirty if (1) they are, or (2) the path 100cb93a386Sopenharmony_ci // is non-finite, and therefore its bounds are not meaningful 101cb93a386Sopenharmony_ci fHasValidBounds = path->hasComputedBounds() && path->isFinite(); 102cb93a386Sopenharmony_ci fEmpty = path->isEmpty(); 103cb93a386Sopenharmony_ci if (fHasValidBounds && !fEmpty) { 104cb93a386Sopenharmony_ci joinNoEmptyChecks(&fRect, fPath->getBounds()); 105cb93a386Sopenharmony_ci } 106cb93a386Sopenharmony_ci fDegenerate = is_degenerate(*path); 107cb93a386Sopenharmony_ci } 108cb93a386Sopenharmony_ci 109cb93a386Sopenharmony_ci ~SkAutoPathBoundsUpdate() { 110cb93a386Sopenharmony_ci fPath->setConvexity(fDegenerate ? SkPathConvexity::kConvex 111cb93a386Sopenharmony_ci : SkPathConvexity::kUnknown); 112cb93a386Sopenharmony_ci if ((fEmpty || fHasValidBounds) && fRect.isFinite()) { 113cb93a386Sopenharmony_ci fPath->setBounds(fRect); 114cb93a386Sopenharmony_ci } 115cb93a386Sopenharmony_ci } 116cb93a386Sopenharmony_ci 117cb93a386Sopenharmony_ciprivate: 118cb93a386Sopenharmony_ci SkPath* fPath; 119cb93a386Sopenharmony_ci SkRect fRect; 120cb93a386Sopenharmony_ci bool fHasValidBounds; 121cb93a386Sopenharmony_ci bool fDegenerate; 122cb93a386Sopenharmony_ci bool fEmpty; 123cb93a386Sopenharmony_ci}; 124cb93a386Sopenharmony_ci 125cb93a386Sopenharmony_ci//////////////////////////////////////////////////////////////////////////// 126cb93a386Sopenharmony_ci 127cb93a386Sopenharmony_ci/* 128cb93a386Sopenharmony_ci Stores the verbs and points as they are given to us, with exceptions: 129cb93a386Sopenharmony_ci - we only record "Close" if it was immediately preceeded by Move | Line | Quad | Cubic 130cb93a386Sopenharmony_ci - we insert a Move(0,0) if Line | Quad | Cubic is our first command 131cb93a386Sopenharmony_ci 132cb93a386Sopenharmony_ci The iterator does more cleanup, especially if forceClose == true 133cb93a386Sopenharmony_ci 1. If we encounter degenerate segments, remove them 134cb93a386Sopenharmony_ci 2. if we encounter Close, return a cons'd up Line() first (if the curr-pt != start-pt) 135cb93a386Sopenharmony_ci 3. if we encounter Move without a preceeding Close, and forceClose is true, goto #2 136cb93a386Sopenharmony_ci 4. if we encounter Line | Quad | Cubic after Close, cons up a Move 137cb93a386Sopenharmony_ci*/ 138cb93a386Sopenharmony_ci 139cb93a386Sopenharmony_ci//////////////////////////////////////////////////////////////////////////// 140cb93a386Sopenharmony_ci 141cb93a386Sopenharmony_ci// flag to require a moveTo if we begin with something else, like lineTo etc. 142cb93a386Sopenharmony_ci// This will also be the value of lastMoveToIndex for a single contour 143cb93a386Sopenharmony_ci// ending with close, so countVerbs needs to be checked against 0. 144cb93a386Sopenharmony_ci#define INITIAL_LASTMOVETOINDEX_VALUE ~0 145cb93a386Sopenharmony_ci 146cb93a386Sopenharmony_ciSkPath::SkPath() 147cb93a386Sopenharmony_ci : fPathRef(SkPathRef::CreateEmpty()) { 148cb93a386Sopenharmony_ci this->resetFields(); 149cb93a386Sopenharmony_ci fIsVolatile = false; 150cb93a386Sopenharmony_ci} 151cb93a386Sopenharmony_ci 152cb93a386Sopenharmony_ciSkPath::SkPath(sk_sp<SkPathRef> pr, SkPathFillType ft, bool isVolatile, SkPathConvexity ct, 153cb93a386Sopenharmony_ci SkPathFirstDirection firstDirection) 154cb93a386Sopenharmony_ci : fPathRef(std::move(pr)) 155cb93a386Sopenharmony_ci , fLastMoveToIndex(INITIAL_LASTMOVETOINDEX_VALUE) 156cb93a386Sopenharmony_ci , fConvexity((uint8_t)ct) 157cb93a386Sopenharmony_ci , fFirstDirection((uint8_t)firstDirection) 158cb93a386Sopenharmony_ci , fFillType((unsigned)ft) 159cb93a386Sopenharmony_ci , fIsVolatile(isVolatile) 160cb93a386Sopenharmony_ci{} 161cb93a386Sopenharmony_ci 162cb93a386Sopenharmony_civoid SkPath::resetFields() { 163cb93a386Sopenharmony_ci //fPathRef is assumed to have been emptied by the caller. 164cb93a386Sopenharmony_ci fLastMoveToIndex = INITIAL_LASTMOVETOINDEX_VALUE; 165cb93a386Sopenharmony_ci fFillType = SkToU8(SkPathFillType::kWinding); 166cb93a386Sopenharmony_ci this->setConvexity(SkPathConvexity::kUnknown); 167cb93a386Sopenharmony_ci this->setFirstDirection(SkPathFirstDirection::kUnknown); 168cb93a386Sopenharmony_ci 169cb93a386Sopenharmony_ci // We don't touch Android's fSourcePath. It's used to track texture garbage collection, so we 170cb93a386Sopenharmony_ci // don't want to muck with it if it's been set to something non-nullptr. 171cb93a386Sopenharmony_ci} 172cb93a386Sopenharmony_ci 173cb93a386Sopenharmony_ciSkPath::SkPath(const SkPath& that) 174cb93a386Sopenharmony_ci : fPathRef(SkRef(that.fPathRef.get())) { 175cb93a386Sopenharmony_ci this->copyFields(that); 176cb93a386Sopenharmony_ci SkDEBUGCODE(that.validate();) 177cb93a386Sopenharmony_ci} 178cb93a386Sopenharmony_ci 179cb93a386Sopenharmony_ciSkPath::~SkPath() { 180cb93a386Sopenharmony_ci SkDEBUGCODE(this->validate();) 181cb93a386Sopenharmony_ci} 182cb93a386Sopenharmony_ci 183cb93a386Sopenharmony_ciSkPath& SkPath::operator=(const SkPath& that) { 184cb93a386Sopenharmony_ci SkDEBUGCODE(that.validate();) 185cb93a386Sopenharmony_ci 186cb93a386Sopenharmony_ci if (this != &that) { 187cb93a386Sopenharmony_ci fPathRef.reset(SkRef(that.fPathRef.get())); 188cb93a386Sopenharmony_ci this->copyFields(that); 189cb93a386Sopenharmony_ci } 190cb93a386Sopenharmony_ci SkDEBUGCODE(this->validate();) 191cb93a386Sopenharmony_ci return *this; 192cb93a386Sopenharmony_ci} 193cb93a386Sopenharmony_ci 194cb93a386Sopenharmony_civoid SkPath::copyFields(const SkPath& that) { 195cb93a386Sopenharmony_ci //fPathRef is assumed to have been set by the caller. 196cb93a386Sopenharmony_ci fLastMoveToIndex = that.fLastMoveToIndex; 197cb93a386Sopenharmony_ci fFillType = that.fFillType; 198cb93a386Sopenharmony_ci fIsVolatile = that.fIsVolatile; 199cb93a386Sopenharmony_ci 200cb93a386Sopenharmony_ci // Non-atomic assignment of atomic values. 201cb93a386Sopenharmony_ci this->setConvexity(that.getConvexityOrUnknown()); 202cb93a386Sopenharmony_ci this->setFirstDirection(that.getFirstDirection()); 203cb93a386Sopenharmony_ci} 204cb93a386Sopenharmony_ci 205cb93a386Sopenharmony_cibool operator==(const SkPath& a, const SkPath& b) { 206cb93a386Sopenharmony_ci // note: don't need to look at isConvex or bounds, since just comparing the 207cb93a386Sopenharmony_ci // raw data is sufficient. 208cb93a386Sopenharmony_ci return &a == &b || 209cb93a386Sopenharmony_ci (a.fFillType == b.fFillType && *a.fPathRef == *b.fPathRef); 210cb93a386Sopenharmony_ci} 211cb93a386Sopenharmony_ci 212cb93a386Sopenharmony_civoid SkPath::swap(SkPath& that) { 213cb93a386Sopenharmony_ci if (this != &that) { 214cb93a386Sopenharmony_ci fPathRef.swap(that.fPathRef); 215cb93a386Sopenharmony_ci std::swap(fLastMoveToIndex, that.fLastMoveToIndex); 216cb93a386Sopenharmony_ci 217cb93a386Sopenharmony_ci const auto ft = fFillType; 218cb93a386Sopenharmony_ci fFillType = that.fFillType; 219cb93a386Sopenharmony_ci that.fFillType = ft; 220cb93a386Sopenharmony_ci 221cb93a386Sopenharmony_ci const auto iv = fIsVolatile; 222cb93a386Sopenharmony_ci fIsVolatile = that.fIsVolatile; 223cb93a386Sopenharmony_ci that.fIsVolatile = iv; 224cb93a386Sopenharmony_ci 225cb93a386Sopenharmony_ci // Non-atomic swaps of atomic values. 226cb93a386Sopenharmony_ci SkPathConvexity c = this->getConvexityOrUnknown(); 227cb93a386Sopenharmony_ci this->setConvexity(that.getConvexityOrUnknown()); 228cb93a386Sopenharmony_ci that.setConvexity(c); 229cb93a386Sopenharmony_ci 230cb93a386Sopenharmony_ci SkPathFirstDirection fd = this->getFirstDirection(); 231cb93a386Sopenharmony_ci this->setFirstDirection(that.getFirstDirection()); 232cb93a386Sopenharmony_ci that.setFirstDirection(fd); 233cb93a386Sopenharmony_ci } 234cb93a386Sopenharmony_ci} 235cb93a386Sopenharmony_ci 236cb93a386Sopenharmony_cibool SkPath::isInterpolatable(const SkPath& compare) const { 237cb93a386Sopenharmony_ci // need the same structure (verbs, conicweights) and same point-count 238cb93a386Sopenharmony_ci return fPathRef->fPoints.count() == compare.fPathRef->fPoints.count() && 239cb93a386Sopenharmony_ci fPathRef->fVerbs == compare.fPathRef->fVerbs && 240cb93a386Sopenharmony_ci fPathRef->fConicWeights == compare.fPathRef->fConicWeights; 241cb93a386Sopenharmony_ci} 242cb93a386Sopenharmony_ci 243cb93a386Sopenharmony_cibool SkPath::interpolate(const SkPath& ending, SkScalar weight, SkPath* out) const { 244cb93a386Sopenharmony_ci int pointCount = fPathRef->countPoints(); 245cb93a386Sopenharmony_ci if (pointCount != ending.fPathRef->countPoints()) { 246cb93a386Sopenharmony_ci return false; 247cb93a386Sopenharmony_ci } 248cb93a386Sopenharmony_ci if (!pointCount) { 249cb93a386Sopenharmony_ci return true; 250cb93a386Sopenharmony_ci } 251cb93a386Sopenharmony_ci out->reset(); 252cb93a386Sopenharmony_ci out->addPath(*this); 253cb93a386Sopenharmony_ci fPathRef->interpolate(*ending.fPathRef, weight, out->fPathRef.get()); 254cb93a386Sopenharmony_ci return true; 255cb93a386Sopenharmony_ci} 256cb93a386Sopenharmony_ci 257cb93a386Sopenharmony_cistatic inline bool check_edge_against_rect(const SkPoint& p0, 258cb93a386Sopenharmony_ci const SkPoint& p1, 259cb93a386Sopenharmony_ci const SkRect& rect, 260cb93a386Sopenharmony_ci SkPathFirstDirection dir) { 261cb93a386Sopenharmony_ci const SkPoint* edgeBegin; 262cb93a386Sopenharmony_ci SkVector v; 263cb93a386Sopenharmony_ci if (SkPathFirstDirection::kCW == dir) { 264cb93a386Sopenharmony_ci v = p1 - p0; 265cb93a386Sopenharmony_ci edgeBegin = &p0; 266cb93a386Sopenharmony_ci } else { 267cb93a386Sopenharmony_ci v = p0 - p1; 268cb93a386Sopenharmony_ci edgeBegin = &p1; 269cb93a386Sopenharmony_ci } 270cb93a386Sopenharmony_ci if (v.fX || v.fY) { 271cb93a386Sopenharmony_ci // check the cross product of v with the vec from edgeBegin to each rect corner 272cb93a386Sopenharmony_ci SkScalar yL = v.fY * (rect.fLeft - edgeBegin->fX); 273cb93a386Sopenharmony_ci SkScalar xT = v.fX * (rect.fTop - edgeBegin->fY); 274cb93a386Sopenharmony_ci SkScalar yR = v.fY * (rect.fRight - edgeBegin->fX); 275cb93a386Sopenharmony_ci SkScalar xB = v.fX * (rect.fBottom - edgeBegin->fY); 276cb93a386Sopenharmony_ci if ((xT < yL) || (xT < yR) || (xB < yL) || (xB < yR)) { 277cb93a386Sopenharmony_ci return false; 278cb93a386Sopenharmony_ci } 279cb93a386Sopenharmony_ci } 280cb93a386Sopenharmony_ci return true; 281cb93a386Sopenharmony_ci} 282cb93a386Sopenharmony_ci 283cb93a386Sopenharmony_cibool SkPath::conservativelyContainsRect(const SkRect& rect) const { 284cb93a386Sopenharmony_ci // This only handles non-degenerate convex paths currently. 285cb93a386Sopenharmony_ci if (!this->isConvex()) { 286cb93a386Sopenharmony_ci return false; 287cb93a386Sopenharmony_ci } 288cb93a386Sopenharmony_ci 289cb93a386Sopenharmony_ci SkPathFirstDirection direction = SkPathPriv::ComputeFirstDirection(*this); 290cb93a386Sopenharmony_ci if (direction == SkPathFirstDirection::kUnknown) { 291cb93a386Sopenharmony_ci return false; 292cb93a386Sopenharmony_ci } 293cb93a386Sopenharmony_ci 294cb93a386Sopenharmony_ci SkPoint firstPt; 295cb93a386Sopenharmony_ci SkPoint prevPt; 296cb93a386Sopenharmony_ci int segmentCount = 0; 297cb93a386Sopenharmony_ci SkDEBUGCODE(int moveCnt = 0;) 298cb93a386Sopenharmony_ci 299cb93a386Sopenharmony_ci for (auto [verb, pts, weight] : SkPathPriv::Iterate(*this)) { 300cb93a386Sopenharmony_ci if (verb == SkPathVerb::kClose || (segmentCount > 0 && verb == SkPathVerb::kMove)) { 301cb93a386Sopenharmony_ci // Closing the current contour; but since convexity is a precondition, it's the only 302cb93a386Sopenharmony_ci // contour that matters. 303cb93a386Sopenharmony_ci SkASSERT(moveCnt); 304cb93a386Sopenharmony_ci segmentCount++; 305cb93a386Sopenharmony_ci break; 306cb93a386Sopenharmony_ci } else if (verb == SkPathVerb::kMove) { 307cb93a386Sopenharmony_ci // A move at the start of the contour (or multiple leading moves, in which case we 308cb93a386Sopenharmony_ci // keep the last one before a non-move verb). 309cb93a386Sopenharmony_ci SkASSERT(!segmentCount); 310cb93a386Sopenharmony_ci SkDEBUGCODE(++moveCnt); 311cb93a386Sopenharmony_ci firstPt = prevPt = pts[0]; 312cb93a386Sopenharmony_ci } else { 313cb93a386Sopenharmony_ci int pointCount = SkPathPriv::PtsInVerb((unsigned) verb); 314cb93a386Sopenharmony_ci SkASSERT(pointCount > 0); 315cb93a386Sopenharmony_ci 316cb93a386Sopenharmony_ci if (!SkPathPriv::AllPointsEq(pts, pointCount + 1)) { 317cb93a386Sopenharmony_ci SkASSERT(moveCnt); 318cb93a386Sopenharmony_ci int nextPt = pointCount; 319cb93a386Sopenharmony_ci segmentCount++; 320cb93a386Sopenharmony_ci 321cb93a386Sopenharmony_ci if (SkPathVerb::kConic == verb) { 322cb93a386Sopenharmony_ci SkConic orig; 323cb93a386Sopenharmony_ci orig.set(pts, *weight); 324cb93a386Sopenharmony_ci SkPoint quadPts[5]; 325cb93a386Sopenharmony_ci int count = orig.chopIntoQuadsPOW2(quadPts, 1); 326cb93a386Sopenharmony_ci SkASSERT_RELEASE(2 == count); 327cb93a386Sopenharmony_ci 328cb93a386Sopenharmony_ci if (!check_edge_against_rect(quadPts[0], quadPts[2], rect, direction)) { 329cb93a386Sopenharmony_ci return false; 330cb93a386Sopenharmony_ci } 331cb93a386Sopenharmony_ci if (!check_edge_against_rect(quadPts[2], quadPts[4], rect, direction)) { 332cb93a386Sopenharmony_ci return false; 333cb93a386Sopenharmony_ci } 334cb93a386Sopenharmony_ci } else { 335cb93a386Sopenharmony_ci if (!check_edge_against_rect(prevPt, pts[nextPt], rect, direction)) { 336cb93a386Sopenharmony_ci return false; 337cb93a386Sopenharmony_ci } 338cb93a386Sopenharmony_ci } 339cb93a386Sopenharmony_ci prevPt = pts[nextPt]; 340cb93a386Sopenharmony_ci } 341cb93a386Sopenharmony_ci } 342cb93a386Sopenharmony_ci } 343cb93a386Sopenharmony_ci 344cb93a386Sopenharmony_ci if (segmentCount) { 345cb93a386Sopenharmony_ci return check_edge_against_rect(prevPt, firstPt, rect, direction); 346cb93a386Sopenharmony_ci } 347cb93a386Sopenharmony_ci return false; 348cb93a386Sopenharmony_ci} 349cb93a386Sopenharmony_ci 350cb93a386Sopenharmony_ciuint32_t SkPath::getGenerationID() const { 351cb93a386Sopenharmony_ci uint32_t genID = fPathRef->genID(); 352cb93a386Sopenharmony_ci#ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK 353cb93a386Sopenharmony_ci SkASSERT((unsigned)fFillType < (1 << (32 - SkPathPriv::kPathRefGenIDBitCnt))); 354cb93a386Sopenharmony_ci genID |= static_cast<uint32_t>(fFillType) << SkPathPriv::kPathRefGenIDBitCnt; 355cb93a386Sopenharmony_ci#endif 356cb93a386Sopenharmony_ci return genID; 357cb93a386Sopenharmony_ci} 358cb93a386Sopenharmony_ci 359cb93a386Sopenharmony_ciSkPath& SkPath::reset() { 360cb93a386Sopenharmony_ci SkDEBUGCODE(this->validate();) 361cb93a386Sopenharmony_ci 362cb93a386Sopenharmony_ci fPathRef.reset(SkPathRef::CreateEmpty()); 363cb93a386Sopenharmony_ci this->resetFields(); 364cb93a386Sopenharmony_ci return *this; 365cb93a386Sopenharmony_ci} 366cb93a386Sopenharmony_ci 367cb93a386Sopenharmony_ciSkPath& SkPath::rewind() { 368cb93a386Sopenharmony_ci SkDEBUGCODE(this->validate();) 369cb93a386Sopenharmony_ci 370cb93a386Sopenharmony_ci SkPathRef::Rewind(&fPathRef); 371cb93a386Sopenharmony_ci this->resetFields(); 372cb93a386Sopenharmony_ci return *this; 373cb93a386Sopenharmony_ci} 374cb93a386Sopenharmony_ci 375cb93a386Sopenharmony_cibool SkPath::isLastContourClosed() const { 376cb93a386Sopenharmony_ci int verbCount = fPathRef->countVerbs(); 377cb93a386Sopenharmony_ci if (0 == verbCount) { 378cb93a386Sopenharmony_ci return false; 379cb93a386Sopenharmony_ci } 380cb93a386Sopenharmony_ci return kClose_Verb == fPathRef->atVerb(verbCount - 1); 381cb93a386Sopenharmony_ci} 382cb93a386Sopenharmony_ci 383cb93a386Sopenharmony_cibool SkPath::isLine(SkPoint line[2]) const { 384cb93a386Sopenharmony_ci int verbCount = fPathRef->countVerbs(); 385cb93a386Sopenharmony_ci 386cb93a386Sopenharmony_ci if (2 == verbCount) { 387cb93a386Sopenharmony_ci SkASSERT(kMove_Verb == fPathRef->atVerb(0)); 388cb93a386Sopenharmony_ci if (kLine_Verb == fPathRef->atVerb(1)) { 389cb93a386Sopenharmony_ci SkASSERT(2 == fPathRef->countPoints()); 390cb93a386Sopenharmony_ci if (line) { 391cb93a386Sopenharmony_ci const SkPoint* pts = fPathRef->points(); 392cb93a386Sopenharmony_ci line[0] = pts[0]; 393cb93a386Sopenharmony_ci line[1] = pts[1]; 394cb93a386Sopenharmony_ci } 395cb93a386Sopenharmony_ci return true; 396cb93a386Sopenharmony_ci } 397cb93a386Sopenharmony_ci } 398cb93a386Sopenharmony_ci return false; 399cb93a386Sopenharmony_ci} 400cb93a386Sopenharmony_ci 401cb93a386Sopenharmony_ci/* 402cb93a386Sopenharmony_ci Determines if path is a rect by keeping track of changes in direction 403cb93a386Sopenharmony_ci and looking for a loop either clockwise or counterclockwise. 404cb93a386Sopenharmony_ci 405cb93a386Sopenharmony_ci The direction is computed such that: 406cb93a386Sopenharmony_ci 0: vertical up 407cb93a386Sopenharmony_ci 1: horizontal left 408cb93a386Sopenharmony_ci 2: vertical down 409cb93a386Sopenharmony_ci 3: horizontal right 410cb93a386Sopenharmony_ci 411cb93a386Sopenharmony_ciA rectangle cycles up/right/down/left or up/left/down/right. 412cb93a386Sopenharmony_ci 413cb93a386Sopenharmony_ciThe test fails if: 414cb93a386Sopenharmony_ci The path is closed, and followed by a line. 415cb93a386Sopenharmony_ci A second move creates a new endpoint. 416cb93a386Sopenharmony_ci A diagonal line is parsed. 417cb93a386Sopenharmony_ci There's more than four changes of direction. 418cb93a386Sopenharmony_ci There's a discontinuity on the line (e.g., a move in the middle) 419cb93a386Sopenharmony_ci The line reverses direction. 420cb93a386Sopenharmony_ci The path contains a quadratic or cubic. 421cb93a386Sopenharmony_ci The path contains fewer than four points. 422cb93a386Sopenharmony_ci *The rectangle doesn't complete a cycle. 423cb93a386Sopenharmony_ci *The final point isn't equal to the first point. 424cb93a386Sopenharmony_ci 425cb93a386Sopenharmony_ci *These last two conditions we relax if we have a 3-edge path that would 426cb93a386Sopenharmony_ci form a rectangle if it were closed (as we do when we fill a path) 427cb93a386Sopenharmony_ci 428cb93a386Sopenharmony_ciIt's OK if the path has: 429cb93a386Sopenharmony_ci Several colinear line segments composing a rectangle side. 430cb93a386Sopenharmony_ci Single points on the rectangle side. 431cb93a386Sopenharmony_ci 432cb93a386Sopenharmony_ciThe direction takes advantage of the corners found since opposite sides 433cb93a386Sopenharmony_cimust travel in opposite directions. 434cb93a386Sopenharmony_ci 435cb93a386Sopenharmony_ciFIXME: Allow colinear quads and cubics to be treated like lines. 436cb93a386Sopenharmony_ciFIXME: If the API passes fill-only, return true if the filled stroke 437cb93a386Sopenharmony_ci is a rectangle, though the caller failed to close the path. 438cb93a386Sopenharmony_ci 439cb93a386Sopenharmony_ci directions values: 440cb93a386Sopenharmony_ci 0x1 is set if the segment is horizontal 441cb93a386Sopenharmony_ci 0x2 is set if the segment is moving to the right or down 442cb93a386Sopenharmony_ci thus: 443cb93a386Sopenharmony_ci two directions are opposites iff (dirA ^ dirB) == 0x2 444cb93a386Sopenharmony_ci two directions are perpendicular iff (dirA ^ dirB) == 0x1 445cb93a386Sopenharmony_ci 446cb93a386Sopenharmony_ci */ 447cb93a386Sopenharmony_cistatic int rect_make_dir(SkScalar dx, SkScalar dy) { 448cb93a386Sopenharmony_ci return ((0 != dx) << 0) | ((dx > 0 || dy > 0) << 1); 449cb93a386Sopenharmony_ci} 450cb93a386Sopenharmony_ci 451cb93a386Sopenharmony_cibool SkPath::isRect(SkRect* rect, bool* isClosed, SkPathDirection* direction) const { 452cb93a386Sopenharmony_ci SkDEBUGCODE(this->validate();) 453cb93a386Sopenharmony_ci int currVerb = 0; 454cb93a386Sopenharmony_ci const SkPoint* pts = fPathRef->points(); 455cb93a386Sopenharmony_ci return SkPathPriv::IsRectContour(*this, false, &currVerb, &pts, isClosed, direction, rect); 456cb93a386Sopenharmony_ci} 457cb93a386Sopenharmony_ci 458cb93a386Sopenharmony_cibool SkPath::isOval(SkRect* bounds) const { 459cb93a386Sopenharmony_ci return SkPathPriv::IsOval(*this, bounds, nullptr, nullptr); 460cb93a386Sopenharmony_ci} 461cb93a386Sopenharmony_ci 462cb93a386Sopenharmony_cibool SkPath::isRRect(SkRRect* rrect) const { 463cb93a386Sopenharmony_ci return SkPathPriv::IsRRect(*this, rrect, nullptr, nullptr); 464cb93a386Sopenharmony_ci} 465cb93a386Sopenharmony_ci 466cb93a386Sopenharmony_ciint SkPath::countPoints() const { 467cb93a386Sopenharmony_ci return fPathRef->countPoints(); 468cb93a386Sopenharmony_ci} 469cb93a386Sopenharmony_ci 470cb93a386Sopenharmony_ciint SkPath::getPoints(SkPoint dst[], int max) const { 471cb93a386Sopenharmony_ci SkDEBUGCODE(this->validate();) 472cb93a386Sopenharmony_ci 473cb93a386Sopenharmony_ci SkASSERT(max >= 0); 474cb93a386Sopenharmony_ci SkASSERT(!max || dst); 475cb93a386Sopenharmony_ci int count = std::min(max, fPathRef->countPoints()); 476cb93a386Sopenharmony_ci sk_careful_memcpy(dst, fPathRef->points(), count * sizeof(SkPoint)); 477cb93a386Sopenharmony_ci return fPathRef->countPoints(); 478cb93a386Sopenharmony_ci} 479cb93a386Sopenharmony_ci 480cb93a386Sopenharmony_ciSkPoint SkPath::getPoint(int index) const { 481cb93a386Sopenharmony_ci if ((unsigned)index < (unsigned)fPathRef->countPoints()) { 482cb93a386Sopenharmony_ci return fPathRef->atPoint(index); 483cb93a386Sopenharmony_ci } 484cb93a386Sopenharmony_ci return SkPoint::Make(0, 0); 485cb93a386Sopenharmony_ci} 486cb93a386Sopenharmony_ci 487cb93a386Sopenharmony_ciint SkPath::countVerbs() const { 488cb93a386Sopenharmony_ci return fPathRef->countVerbs(); 489cb93a386Sopenharmony_ci} 490cb93a386Sopenharmony_ci 491cb93a386Sopenharmony_ciint SkPath::getVerbs(uint8_t dst[], int max) const { 492cb93a386Sopenharmony_ci SkDEBUGCODE(this->validate();) 493cb93a386Sopenharmony_ci 494cb93a386Sopenharmony_ci SkASSERT(max >= 0); 495cb93a386Sopenharmony_ci SkASSERT(!max || dst); 496cb93a386Sopenharmony_ci int count = std::min(max, fPathRef->countVerbs()); 497cb93a386Sopenharmony_ci if (count) { 498cb93a386Sopenharmony_ci memcpy(dst, fPathRef->verbsBegin(), count); 499cb93a386Sopenharmony_ci } 500cb93a386Sopenharmony_ci return fPathRef->countVerbs(); 501cb93a386Sopenharmony_ci} 502cb93a386Sopenharmony_ci 503cb93a386Sopenharmony_cisize_t SkPath::approximateBytesUsed() const { 504cb93a386Sopenharmony_ci size_t size = sizeof (SkPath); 505cb93a386Sopenharmony_ci if (fPathRef != nullptr) { 506cb93a386Sopenharmony_ci size += fPathRef->approximateBytesUsed(); 507cb93a386Sopenharmony_ci } 508cb93a386Sopenharmony_ci return size; 509cb93a386Sopenharmony_ci} 510cb93a386Sopenharmony_ci 511cb93a386Sopenharmony_cibool SkPath::getLastPt(SkPoint* lastPt) const { 512cb93a386Sopenharmony_ci SkDEBUGCODE(this->validate();) 513cb93a386Sopenharmony_ci 514cb93a386Sopenharmony_ci int count = fPathRef->countPoints(); 515cb93a386Sopenharmony_ci if (count > 0) { 516cb93a386Sopenharmony_ci if (lastPt) { 517cb93a386Sopenharmony_ci *lastPt = fPathRef->atPoint(count - 1); 518cb93a386Sopenharmony_ci } 519cb93a386Sopenharmony_ci return true; 520cb93a386Sopenharmony_ci } 521cb93a386Sopenharmony_ci if (lastPt) { 522cb93a386Sopenharmony_ci lastPt->set(0, 0); 523cb93a386Sopenharmony_ci } 524cb93a386Sopenharmony_ci return false; 525cb93a386Sopenharmony_ci} 526cb93a386Sopenharmony_ci 527cb93a386Sopenharmony_civoid SkPath::setPt(int index, SkScalar x, SkScalar y) { 528cb93a386Sopenharmony_ci SkDEBUGCODE(this->validate();) 529cb93a386Sopenharmony_ci 530cb93a386Sopenharmony_ci int count = fPathRef->countPoints(); 531cb93a386Sopenharmony_ci if (count <= index) { 532cb93a386Sopenharmony_ci return; 533cb93a386Sopenharmony_ci } else { 534cb93a386Sopenharmony_ci SkPathRef::Editor ed(&fPathRef); 535cb93a386Sopenharmony_ci ed.atPoint(index)->set(x, y); 536cb93a386Sopenharmony_ci } 537cb93a386Sopenharmony_ci} 538cb93a386Sopenharmony_ci 539cb93a386Sopenharmony_civoid SkPath::setLastPt(SkScalar x, SkScalar y) { 540cb93a386Sopenharmony_ci SkDEBUGCODE(this->validate();) 541cb93a386Sopenharmony_ci 542cb93a386Sopenharmony_ci int count = fPathRef->countPoints(); 543cb93a386Sopenharmony_ci if (count == 0) { 544cb93a386Sopenharmony_ci this->moveTo(x, y); 545cb93a386Sopenharmony_ci } else { 546cb93a386Sopenharmony_ci SkPathRef::Editor ed(&fPathRef); 547cb93a386Sopenharmony_ci ed.atPoint(count-1)->set(x, y); 548cb93a386Sopenharmony_ci } 549cb93a386Sopenharmony_ci} 550cb93a386Sopenharmony_ci 551cb93a386Sopenharmony_ci// This is the public-facing non-const setConvexity(). 552cb93a386Sopenharmony_civoid SkPath::setConvexity(SkPathConvexity c) { 553cb93a386Sopenharmony_ci fConvexity.store((uint8_t)c, std::memory_order_relaxed); 554cb93a386Sopenharmony_ci} 555cb93a386Sopenharmony_ci 556cb93a386Sopenharmony_ci// Const hooks for working with fConvexity and fFirstDirection from const methods. 557cb93a386Sopenharmony_civoid SkPath::setConvexity(SkPathConvexity c) const { 558cb93a386Sopenharmony_ci fConvexity.store((uint8_t)c, std::memory_order_relaxed); 559cb93a386Sopenharmony_ci} 560cb93a386Sopenharmony_civoid SkPath::setFirstDirection(SkPathFirstDirection d) const { 561cb93a386Sopenharmony_ci fFirstDirection.store((uint8_t)d, std::memory_order_relaxed); 562cb93a386Sopenharmony_ci} 563cb93a386Sopenharmony_ciSkPathFirstDirection SkPath::getFirstDirection() const { 564cb93a386Sopenharmony_ci return (SkPathFirstDirection)fFirstDirection.load(std::memory_order_relaxed); 565cb93a386Sopenharmony_ci} 566cb93a386Sopenharmony_ci 567cb93a386Sopenharmony_cibool SkPath::isConvexityAccurate() const { 568cb93a386Sopenharmony_ci SkPathConvexity convexity = this->getConvexityOrUnknown(); 569cb93a386Sopenharmony_ci if (convexity != SkPathConvexity::kUnknown) { 570cb93a386Sopenharmony_ci auto conv = this->computeConvexity(); 571cb93a386Sopenharmony_ci if (conv != convexity) { 572cb93a386Sopenharmony_ci SkASSERT(false); 573cb93a386Sopenharmony_ci return false; 574cb93a386Sopenharmony_ci } 575cb93a386Sopenharmony_ci } 576cb93a386Sopenharmony_ci return true; 577cb93a386Sopenharmony_ci} 578cb93a386Sopenharmony_ci 579cb93a386Sopenharmony_ciSkPathConvexity SkPath::getConvexity() const { 580cb93a386Sopenharmony_ci// Enable once we fix all the bugs 581cb93a386Sopenharmony_ci// SkDEBUGCODE(this->isConvexityAccurate()); 582cb93a386Sopenharmony_ci SkPathConvexity convexity = this->getConvexityOrUnknown(); 583cb93a386Sopenharmony_ci if (convexity == SkPathConvexity::kUnknown) { 584cb93a386Sopenharmony_ci convexity = this->computeConvexity(); 585cb93a386Sopenharmony_ci } 586cb93a386Sopenharmony_ci SkASSERT(convexity != SkPathConvexity::kUnknown); 587cb93a386Sopenharmony_ci return convexity; 588cb93a386Sopenharmony_ci} 589cb93a386Sopenharmony_ci 590cb93a386Sopenharmony_ci////////////////////////////////////////////////////////////////////////////// 591cb93a386Sopenharmony_ci// Construction methods 592cb93a386Sopenharmony_ci 593cb93a386Sopenharmony_ciSkPath& SkPath::dirtyAfterEdit() { 594cb93a386Sopenharmony_ci this->setConvexity(SkPathConvexity::kUnknown); 595cb93a386Sopenharmony_ci this->setFirstDirection(SkPathFirstDirection::kUnknown); 596cb93a386Sopenharmony_ci 597cb93a386Sopenharmony_ci#ifdef SK_DEBUG 598cb93a386Sopenharmony_ci // enable this as needed for testing, but it slows down some chrome tests so much 599cb93a386Sopenharmony_ci // that they don't complete, so we don't enable it by default 600cb93a386Sopenharmony_ci // e.g. TEST(IdentifiabilityPaintOpDigestTest, MassiveOpSkipped) 601cb93a386Sopenharmony_ci if (this->countVerbs() < 16) { 602cb93a386Sopenharmony_ci SkASSERT(fPathRef->dataMatchesVerbs()); 603cb93a386Sopenharmony_ci } 604cb93a386Sopenharmony_ci#endif 605cb93a386Sopenharmony_ci 606cb93a386Sopenharmony_ci return *this; 607cb93a386Sopenharmony_ci} 608cb93a386Sopenharmony_ci 609cb93a386Sopenharmony_civoid SkPath::incReserve(int inc) { 610cb93a386Sopenharmony_ci SkDEBUGCODE(this->validate();) 611cb93a386Sopenharmony_ci if (inc > 0) { 612cb93a386Sopenharmony_ci SkPathRef::Editor(&fPathRef, inc, inc); 613cb93a386Sopenharmony_ci } 614cb93a386Sopenharmony_ci SkDEBUGCODE(this->validate();) 615cb93a386Sopenharmony_ci} 616cb93a386Sopenharmony_ci 617cb93a386Sopenharmony_ciSkPath& SkPath::moveTo(SkScalar x, SkScalar y) { 618cb93a386Sopenharmony_ci SkDEBUGCODE(this->validate();) 619cb93a386Sopenharmony_ci 620cb93a386Sopenharmony_ci SkPathRef::Editor ed(&fPathRef); 621cb93a386Sopenharmony_ci 622cb93a386Sopenharmony_ci // remember our index 623cb93a386Sopenharmony_ci fLastMoveToIndex = fPathRef->countPoints(); 624cb93a386Sopenharmony_ci 625cb93a386Sopenharmony_ci ed.growForVerb(kMove_Verb)->set(x, y); 626cb93a386Sopenharmony_ci 627cb93a386Sopenharmony_ci return this->dirtyAfterEdit(); 628cb93a386Sopenharmony_ci} 629cb93a386Sopenharmony_ci 630cb93a386Sopenharmony_ciSkPath& SkPath::rMoveTo(SkScalar x, SkScalar y) { 631cb93a386Sopenharmony_ci SkPoint pt = {0,0}; 632cb93a386Sopenharmony_ci int count = fPathRef->countPoints(); 633cb93a386Sopenharmony_ci if (count > 0) { 634cb93a386Sopenharmony_ci if (fLastMoveToIndex >= 0) { 635cb93a386Sopenharmony_ci pt = fPathRef->atPoint(count - 1); 636cb93a386Sopenharmony_ci } else { 637cb93a386Sopenharmony_ci pt = fPathRef->atPoint(~fLastMoveToIndex); 638cb93a386Sopenharmony_ci } 639cb93a386Sopenharmony_ci } 640cb93a386Sopenharmony_ci return this->moveTo(pt.fX + x, pt.fY + y); 641cb93a386Sopenharmony_ci} 642cb93a386Sopenharmony_ci 643cb93a386Sopenharmony_civoid SkPath::injectMoveToIfNeeded() { 644cb93a386Sopenharmony_ci if (fLastMoveToIndex < 0) { 645cb93a386Sopenharmony_ci SkScalar x, y; 646cb93a386Sopenharmony_ci if (fPathRef->countVerbs() == 0) { 647cb93a386Sopenharmony_ci x = y = 0; 648cb93a386Sopenharmony_ci } else { 649cb93a386Sopenharmony_ci const SkPoint& pt = fPathRef->atPoint(~fLastMoveToIndex); 650cb93a386Sopenharmony_ci x = pt.fX; 651cb93a386Sopenharmony_ci y = pt.fY; 652cb93a386Sopenharmony_ci } 653cb93a386Sopenharmony_ci this->moveTo(x, y); 654cb93a386Sopenharmony_ci } 655cb93a386Sopenharmony_ci} 656cb93a386Sopenharmony_ci 657cb93a386Sopenharmony_ciSkPath& SkPath::lineTo(SkScalar x, SkScalar y) { 658cb93a386Sopenharmony_ci SkDEBUGCODE(this->validate();) 659cb93a386Sopenharmony_ci 660cb93a386Sopenharmony_ci this->injectMoveToIfNeeded(); 661cb93a386Sopenharmony_ci 662cb93a386Sopenharmony_ci SkPathRef::Editor ed(&fPathRef); 663cb93a386Sopenharmony_ci ed.growForVerb(kLine_Verb)->set(x, y); 664cb93a386Sopenharmony_ci 665cb93a386Sopenharmony_ci return this->dirtyAfterEdit(); 666cb93a386Sopenharmony_ci} 667cb93a386Sopenharmony_ci 668cb93a386Sopenharmony_ciSkPath& SkPath::rLineTo(SkScalar x, SkScalar y) { 669cb93a386Sopenharmony_ci this->injectMoveToIfNeeded(); // This can change the result of this->getLastPt(). 670cb93a386Sopenharmony_ci SkPoint pt; 671cb93a386Sopenharmony_ci this->getLastPt(&pt); 672cb93a386Sopenharmony_ci return this->lineTo(pt.fX + x, pt.fY + y); 673cb93a386Sopenharmony_ci} 674cb93a386Sopenharmony_ci 675cb93a386Sopenharmony_ciSkPath& SkPath::quadTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2) { 676cb93a386Sopenharmony_ci SkDEBUGCODE(this->validate();) 677cb93a386Sopenharmony_ci 678cb93a386Sopenharmony_ci this->injectMoveToIfNeeded(); 679cb93a386Sopenharmony_ci 680cb93a386Sopenharmony_ci SkPathRef::Editor ed(&fPathRef); 681cb93a386Sopenharmony_ci SkPoint* pts = ed.growForVerb(kQuad_Verb); 682cb93a386Sopenharmony_ci pts[0].set(x1, y1); 683cb93a386Sopenharmony_ci pts[1].set(x2, y2); 684cb93a386Sopenharmony_ci 685cb93a386Sopenharmony_ci return this->dirtyAfterEdit(); 686cb93a386Sopenharmony_ci} 687cb93a386Sopenharmony_ci 688cb93a386Sopenharmony_ciSkPath& SkPath::rQuadTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2) { 689cb93a386Sopenharmony_ci this->injectMoveToIfNeeded(); // This can change the result of this->getLastPt(). 690cb93a386Sopenharmony_ci SkPoint pt; 691cb93a386Sopenharmony_ci this->getLastPt(&pt); 692cb93a386Sopenharmony_ci return this->quadTo(pt.fX + x1, pt.fY + y1, pt.fX + x2, pt.fY + y2); 693cb93a386Sopenharmony_ci} 694cb93a386Sopenharmony_ci 695cb93a386Sopenharmony_ciSkPath& SkPath::conicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2, 696cb93a386Sopenharmony_ci SkScalar w) { 697cb93a386Sopenharmony_ci // check for <= 0 or NaN with this test 698cb93a386Sopenharmony_ci if (!(w > 0)) { 699cb93a386Sopenharmony_ci this->lineTo(x2, y2); 700cb93a386Sopenharmony_ci } else if (!SkScalarIsFinite(w)) { 701cb93a386Sopenharmony_ci this->lineTo(x1, y1); 702cb93a386Sopenharmony_ci this->lineTo(x2, y2); 703cb93a386Sopenharmony_ci } else if (SK_Scalar1 == w) { 704cb93a386Sopenharmony_ci this->quadTo(x1, y1, x2, y2); 705cb93a386Sopenharmony_ci } else { 706cb93a386Sopenharmony_ci SkDEBUGCODE(this->validate();) 707cb93a386Sopenharmony_ci 708cb93a386Sopenharmony_ci this->injectMoveToIfNeeded(); 709cb93a386Sopenharmony_ci 710cb93a386Sopenharmony_ci SkPathRef::Editor ed(&fPathRef); 711cb93a386Sopenharmony_ci SkPoint* pts = ed.growForVerb(kConic_Verb, w); 712cb93a386Sopenharmony_ci pts[0].set(x1, y1); 713cb93a386Sopenharmony_ci pts[1].set(x2, y2); 714cb93a386Sopenharmony_ci 715cb93a386Sopenharmony_ci (void)this->dirtyAfterEdit(); 716cb93a386Sopenharmony_ci } 717cb93a386Sopenharmony_ci return *this; 718cb93a386Sopenharmony_ci} 719cb93a386Sopenharmony_ci 720cb93a386Sopenharmony_ciSkPath& SkPath::rConicTo(SkScalar dx1, SkScalar dy1, SkScalar dx2, SkScalar dy2, 721cb93a386Sopenharmony_ci SkScalar w) { 722cb93a386Sopenharmony_ci this->injectMoveToIfNeeded(); // This can change the result of this->getLastPt(). 723cb93a386Sopenharmony_ci SkPoint pt; 724cb93a386Sopenharmony_ci this->getLastPt(&pt); 725cb93a386Sopenharmony_ci return this->conicTo(pt.fX + dx1, pt.fY + dy1, pt.fX + dx2, pt.fY + dy2, w); 726cb93a386Sopenharmony_ci} 727cb93a386Sopenharmony_ci 728cb93a386Sopenharmony_ciSkPath& SkPath::cubicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2, 729cb93a386Sopenharmony_ci SkScalar x3, SkScalar y3) { 730cb93a386Sopenharmony_ci SkDEBUGCODE(this->validate();) 731cb93a386Sopenharmony_ci 732cb93a386Sopenharmony_ci this->injectMoveToIfNeeded(); 733cb93a386Sopenharmony_ci 734cb93a386Sopenharmony_ci SkPathRef::Editor ed(&fPathRef); 735cb93a386Sopenharmony_ci SkPoint* pts = ed.growForVerb(kCubic_Verb); 736cb93a386Sopenharmony_ci pts[0].set(x1, y1); 737cb93a386Sopenharmony_ci pts[1].set(x2, y2); 738cb93a386Sopenharmony_ci pts[2].set(x3, y3); 739cb93a386Sopenharmony_ci 740cb93a386Sopenharmony_ci return this->dirtyAfterEdit(); 741cb93a386Sopenharmony_ci} 742cb93a386Sopenharmony_ci 743cb93a386Sopenharmony_ciSkPath& SkPath::rCubicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2, 744cb93a386Sopenharmony_ci SkScalar x3, SkScalar y3) { 745cb93a386Sopenharmony_ci this->injectMoveToIfNeeded(); // This can change the result of this->getLastPt(). 746cb93a386Sopenharmony_ci SkPoint pt; 747cb93a386Sopenharmony_ci this->getLastPt(&pt); 748cb93a386Sopenharmony_ci return this->cubicTo(pt.fX + x1, pt.fY + y1, pt.fX + x2, pt.fY + y2, 749cb93a386Sopenharmony_ci pt.fX + x3, pt.fY + y3); 750cb93a386Sopenharmony_ci} 751cb93a386Sopenharmony_ci 752cb93a386Sopenharmony_ciSkPath& SkPath::close() { 753cb93a386Sopenharmony_ci SkDEBUGCODE(this->validate();) 754cb93a386Sopenharmony_ci 755cb93a386Sopenharmony_ci int count = fPathRef->countVerbs(); 756cb93a386Sopenharmony_ci if (count > 0) { 757cb93a386Sopenharmony_ci switch (fPathRef->atVerb(count - 1)) { 758cb93a386Sopenharmony_ci case kLine_Verb: 759cb93a386Sopenharmony_ci case kQuad_Verb: 760cb93a386Sopenharmony_ci case kConic_Verb: 761cb93a386Sopenharmony_ci case kCubic_Verb: 762cb93a386Sopenharmony_ci case kMove_Verb: { 763cb93a386Sopenharmony_ci SkPathRef::Editor ed(&fPathRef); 764cb93a386Sopenharmony_ci ed.growForVerb(kClose_Verb); 765cb93a386Sopenharmony_ci break; 766cb93a386Sopenharmony_ci } 767cb93a386Sopenharmony_ci case kClose_Verb: 768cb93a386Sopenharmony_ci // don't add a close if it's the first verb or a repeat 769cb93a386Sopenharmony_ci break; 770cb93a386Sopenharmony_ci default: 771cb93a386Sopenharmony_ci SkDEBUGFAIL("unexpected verb"); 772cb93a386Sopenharmony_ci break; 773cb93a386Sopenharmony_ci } 774cb93a386Sopenharmony_ci } 775cb93a386Sopenharmony_ci 776cb93a386Sopenharmony_ci // signal that we need a moveTo to follow us (unless we're done) 777cb93a386Sopenharmony_ci#if 0 778cb93a386Sopenharmony_ci if (fLastMoveToIndex >= 0) { 779cb93a386Sopenharmony_ci fLastMoveToIndex = ~fLastMoveToIndex; 780cb93a386Sopenharmony_ci } 781cb93a386Sopenharmony_ci#else 782cb93a386Sopenharmony_ci fLastMoveToIndex ^= ~fLastMoveToIndex >> (8 * sizeof(fLastMoveToIndex) - 1); 783cb93a386Sopenharmony_ci#endif 784cb93a386Sopenharmony_ci return *this; 785cb93a386Sopenharmony_ci} 786cb93a386Sopenharmony_ci 787cb93a386Sopenharmony_ci/////////////////////////////////////////////////////////////////////////////// 788cb93a386Sopenharmony_ci 789cb93a386Sopenharmony_cistatic void assert_known_direction(SkPathDirection dir) { 790cb93a386Sopenharmony_ci SkASSERT(SkPathDirection::kCW == dir || SkPathDirection::kCCW == dir); 791cb93a386Sopenharmony_ci} 792cb93a386Sopenharmony_ci 793cb93a386Sopenharmony_ciSkPath& SkPath::addRect(const SkRect &rect, SkPathDirection dir, unsigned startIndex) { 794cb93a386Sopenharmony_ci assert_known_direction(dir); 795cb93a386Sopenharmony_ci this->setFirstDirection(this->hasOnlyMoveTos() ? (SkPathFirstDirection)dir 796cb93a386Sopenharmony_ci : SkPathFirstDirection::kUnknown); 797cb93a386Sopenharmony_ci SkAutoDisableDirectionCheck addc(this); 798cb93a386Sopenharmony_ci SkAutoPathBoundsUpdate apbu(this, rect); 799cb93a386Sopenharmony_ci 800cb93a386Sopenharmony_ci SkDEBUGCODE(int initialVerbCount = this->countVerbs()); 801cb93a386Sopenharmony_ci 802cb93a386Sopenharmony_ci const int kVerbs = 5; // moveTo + 3x lineTo + close 803cb93a386Sopenharmony_ci this->incReserve(kVerbs); 804cb93a386Sopenharmony_ci 805cb93a386Sopenharmony_ci SkPath_RectPointIterator iter(rect, dir, startIndex); 806cb93a386Sopenharmony_ci 807cb93a386Sopenharmony_ci this->moveTo(iter.current()); 808cb93a386Sopenharmony_ci this->lineTo(iter.next()); 809cb93a386Sopenharmony_ci this->lineTo(iter.next()); 810cb93a386Sopenharmony_ci this->lineTo(iter.next()); 811cb93a386Sopenharmony_ci this->close(); 812cb93a386Sopenharmony_ci 813cb93a386Sopenharmony_ci SkASSERT(this->countVerbs() == initialVerbCount + kVerbs); 814cb93a386Sopenharmony_ci return *this; 815cb93a386Sopenharmony_ci} 816cb93a386Sopenharmony_ci 817cb93a386Sopenharmony_ciSkPath& SkPath::addPoly(const SkPoint pts[], int count, bool close) { 818cb93a386Sopenharmony_ci SkDEBUGCODE(this->validate();) 819cb93a386Sopenharmony_ci if (count <= 0) { 820cb93a386Sopenharmony_ci return *this; 821cb93a386Sopenharmony_ci } 822cb93a386Sopenharmony_ci 823cb93a386Sopenharmony_ci fLastMoveToIndex = fPathRef->countPoints(); 824cb93a386Sopenharmony_ci 825cb93a386Sopenharmony_ci // +close makes room for the extra kClose_Verb 826cb93a386Sopenharmony_ci SkPathRef::Editor ed(&fPathRef, count+close, count); 827cb93a386Sopenharmony_ci 828cb93a386Sopenharmony_ci ed.growForVerb(kMove_Verb)->set(pts[0].fX, pts[0].fY); 829cb93a386Sopenharmony_ci if (count > 1) { 830cb93a386Sopenharmony_ci SkPoint* p = ed.growForRepeatedVerb(kLine_Verb, count - 1); 831cb93a386Sopenharmony_ci memcpy(p, &pts[1], (count-1) * sizeof(SkPoint)); 832cb93a386Sopenharmony_ci } 833cb93a386Sopenharmony_ci 834cb93a386Sopenharmony_ci if (close) { 835cb93a386Sopenharmony_ci ed.growForVerb(kClose_Verb); 836cb93a386Sopenharmony_ci fLastMoveToIndex ^= ~fLastMoveToIndex >> (8 * sizeof(fLastMoveToIndex) - 1); 837cb93a386Sopenharmony_ci } 838cb93a386Sopenharmony_ci 839cb93a386Sopenharmony_ci (void)this->dirtyAfterEdit(); 840cb93a386Sopenharmony_ci SkDEBUGCODE(this->validate();) 841cb93a386Sopenharmony_ci return *this; 842cb93a386Sopenharmony_ci} 843cb93a386Sopenharmony_ci 844cb93a386Sopenharmony_ci#include "src/core/SkGeometry.h" 845cb93a386Sopenharmony_ci 846cb93a386Sopenharmony_cistatic bool arc_is_lone_point(const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle, 847cb93a386Sopenharmony_ci SkPoint* pt) { 848cb93a386Sopenharmony_ci if (0 == sweepAngle && (0 == startAngle || SkIntToScalar(360) == startAngle)) { 849cb93a386Sopenharmony_ci // Chrome uses this path to move into and out of ovals. If not 850cb93a386Sopenharmony_ci // treated as a special case the moves can distort the oval's 851cb93a386Sopenharmony_ci // bounding box (and break the circle special case). 852cb93a386Sopenharmony_ci pt->set(oval.fRight, oval.centerY()); 853cb93a386Sopenharmony_ci return true; 854cb93a386Sopenharmony_ci } else if (0 == oval.width() && 0 == oval.height()) { 855cb93a386Sopenharmony_ci // Chrome will sometimes create 0 radius round rects. Having degenerate 856cb93a386Sopenharmony_ci // quad segments in the path prevents the path from being recognized as 857cb93a386Sopenharmony_ci // a rect. 858cb93a386Sopenharmony_ci // TODO: optimizing the case where only one of width or height is zero 859cb93a386Sopenharmony_ci // should also be considered. This case, however, doesn't seem to be 860cb93a386Sopenharmony_ci // as common as the single point case. 861cb93a386Sopenharmony_ci pt->set(oval.fRight, oval.fTop); 862cb93a386Sopenharmony_ci return true; 863cb93a386Sopenharmony_ci } 864cb93a386Sopenharmony_ci return false; 865cb93a386Sopenharmony_ci} 866cb93a386Sopenharmony_ci 867cb93a386Sopenharmony_ci// Return the unit vectors pointing at the start/stop points for the given start/sweep angles 868cb93a386Sopenharmony_ci// 869cb93a386Sopenharmony_cistatic void angles_to_unit_vectors(SkScalar startAngle, SkScalar sweepAngle, 870cb93a386Sopenharmony_ci SkVector* startV, SkVector* stopV, SkRotationDirection* dir) { 871cb93a386Sopenharmony_ci SkScalar startRad = SkDegreesToRadians(startAngle), 872cb93a386Sopenharmony_ci stopRad = SkDegreesToRadians(startAngle + sweepAngle); 873cb93a386Sopenharmony_ci 874cb93a386Sopenharmony_ci startV->fY = SkScalarSinSnapToZero(startRad); 875cb93a386Sopenharmony_ci startV->fX = SkScalarCosSnapToZero(startRad); 876cb93a386Sopenharmony_ci stopV->fY = SkScalarSinSnapToZero(stopRad); 877cb93a386Sopenharmony_ci stopV->fX = SkScalarCosSnapToZero(stopRad); 878cb93a386Sopenharmony_ci 879cb93a386Sopenharmony_ci /* If the sweep angle is nearly (but less than) 360, then due to precision 880cb93a386Sopenharmony_ci loss in radians-conversion and/or sin/cos, we may end up with coincident 881cb93a386Sopenharmony_ci vectors, which will fool SkBuildQuadArc into doing nothing (bad) instead 882cb93a386Sopenharmony_ci of drawing a nearly complete circle (good). 883cb93a386Sopenharmony_ci e.g. canvas.drawArc(0, 359.99, ...) 884cb93a386Sopenharmony_ci -vs- canvas.drawArc(0, 359.9, ...) 885cb93a386Sopenharmony_ci We try to detect this edge case, and tweak the stop vector 886cb93a386Sopenharmony_ci */ 887cb93a386Sopenharmony_ci if (*startV == *stopV) { 888cb93a386Sopenharmony_ci SkScalar sw = SkScalarAbs(sweepAngle); 889cb93a386Sopenharmony_ci if (sw < SkIntToScalar(360) && sw > SkIntToScalar(359)) { 890cb93a386Sopenharmony_ci // make a guess at a tiny angle (in radians) to tweak by 891cb93a386Sopenharmony_ci SkScalar deltaRad = SkScalarCopySign(SK_Scalar1/512, sweepAngle); 892cb93a386Sopenharmony_ci // not sure how much will be enough, so we use a loop 893cb93a386Sopenharmony_ci do { 894cb93a386Sopenharmony_ci stopRad -= deltaRad; 895cb93a386Sopenharmony_ci stopV->fY = SkScalarSinSnapToZero(stopRad); 896cb93a386Sopenharmony_ci stopV->fX = SkScalarCosSnapToZero(stopRad); 897cb93a386Sopenharmony_ci } while (*startV == *stopV); 898cb93a386Sopenharmony_ci } 899cb93a386Sopenharmony_ci } 900cb93a386Sopenharmony_ci *dir = sweepAngle > 0 ? kCW_SkRotationDirection : kCCW_SkRotationDirection; 901cb93a386Sopenharmony_ci} 902cb93a386Sopenharmony_ci 903cb93a386Sopenharmony_ci/** 904cb93a386Sopenharmony_ci * If this returns 0, then the caller should just line-to the singlePt, else it should 905cb93a386Sopenharmony_ci * ignore singlePt and append the specified number of conics. 906cb93a386Sopenharmony_ci */ 907cb93a386Sopenharmony_cistatic int build_arc_conics(const SkRect& oval, const SkVector& start, const SkVector& stop, 908cb93a386Sopenharmony_ci SkRotationDirection dir, SkConic conics[SkConic::kMaxConicsForArc], 909cb93a386Sopenharmony_ci SkPoint* singlePt) { 910cb93a386Sopenharmony_ci SkMatrix matrix; 911cb93a386Sopenharmony_ci 912cb93a386Sopenharmony_ci matrix.setScale(SkScalarHalf(oval.width()), SkScalarHalf(oval.height())); 913cb93a386Sopenharmony_ci matrix.postTranslate(oval.centerX(), oval.centerY()); 914cb93a386Sopenharmony_ci 915cb93a386Sopenharmony_ci int count = SkConic::BuildUnitArc(start, stop, dir, &matrix, conics); 916cb93a386Sopenharmony_ci if (0 == count) { 917cb93a386Sopenharmony_ci matrix.mapXY(stop.x(), stop.y(), singlePt); 918cb93a386Sopenharmony_ci } 919cb93a386Sopenharmony_ci return count; 920cb93a386Sopenharmony_ci} 921cb93a386Sopenharmony_ci 922cb93a386Sopenharmony_ciSkPath& SkPath::addRoundRect(const SkRect& rect, const SkScalar radii[], 923cb93a386Sopenharmony_ci SkPathDirection dir) { 924cb93a386Sopenharmony_ci SkRRect rrect; 925cb93a386Sopenharmony_ci rrect.setRectRadii(rect, (const SkVector*) radii); 926cb93a386Sopenharmony_ci return this->addRRect(rrect, dir); 927cb93a386Sopenharmony_ci} 928cb93a386Sopenharmony_ci 929cb93a386Sopenharmony_ciSkPath& SkPath::addRRect(const SkRRect& rrect, SkPathDirection dir) { 930cb93a386Sopenharmony_ci // legacy start indices: 6 (CW) and 7(CCW) 931cb93a386Sopenharmony_ci return this->addRRect(rrect, dir, dir == SkPathDirection::kCW ? 6 : 7); 932cb93a386Sopenharmony_ci} 933cb93a386Sopenharmony_ci 934cb93a386Sopenharmony_ciSkPath& SkPath::addRRect(const SkRRect &rrect, SkPathDirection dir, unsigned startIndex) { 935cb93a386Sopenharmony_ci assert_known_direction(dir); 936cb93a386Sopenharmony_ci 937cb93a386Sopenharmony_ci bool isRRect = hasOnlyMoveTos(); 938cb93a386Sopenharmony_ci const SkRect& bounds = rrect.getBounds(); 939cb93a386Sopenharmony_ci 940cb93a386Sopenharmony_ci if (rrect.isRect() || rrect.isEmpty()) { 941cb93a386Sopenharmony_ci // degenerate(rect) => radii points are collapsing 942cb93a386Sopenharmony_ci this->addRect(bounds, dir, (startIndex + 1) / 2); 943cb93a386Sopenharmony_ci } else if (rrect.isOval()) { 944cb93a386Sopenharmony_ci // degenerate(oval) => line points are collapsing 945cb93a386Sopenharmony_ci this->addOval(bounds, dir, startIndex / 2); 946cb93a386Sopenharmony_ci } else { 947cb93a386Sopenharmony_ci this->setFirstDirection(this->hasOnlyMoveTos() ? (SkPathFirstDirection)dir 948cb93a386Sopenharmony_ci : SkPathFirstDirection::kUnknown); 949cb93a386Sopenharmony_ci 950cb93a386Sopenharmony_ci SkAutoPathBoundsUpdate apbu(this, bounds); 951cb93a386Sopenharmony_ci SkAutoDisableDirectionCheck addc(this); 952cb93a386Sopenharmony_ci 953cb93a386Sopenharmony_ci // we start with a conic on odd indices when moving CW vs. even indices when moving CCW 954cb93a386Sopenharmony_ci const bool startsWithConic = ((startIndex & 1) == (dir == SkPathDirection::kCW)); 955cb93a386Sopenharmony_ci const SkScalar weight = SK_ScalarRoot2Over2; 956cb93a386Sopenharmony_ci 957cb93a386Sopenharmony_ci SkDEBUGCODE(int initialVerbCount = this->countVerbs()); 958cb93a386Sopenharmony_ci const int kVerbs = startsWithConic 959cb93a386Sopenharmony_ci ? 9 // moveTo + 4x conicTo + 3x lineTo + close 960cb93a386Sopenharmony_ci : 10; // moveTo + 4x lineTo + 4x conicTo + close 961cb93a386Sopenharmony_ci this->incReserve(kVerbs); 962cb93a386Sopenharmony_ci 963cb93a386Sopenharmony_ci SkPath_RRectPointIterator rrectIter(rrect, dir, startIndex); 964cb93a386Sopenharmony_ci // Corner iterator indices follow the collapsed radii model, 965cb93a386Sopenharmony_ci // adjusted such that the start pt is "behind" the radii start pt. 966cb93a386Sopenharmony_ci const unsigned rectStartIndex = startIndex / 2 + (dir == SkPathDirection::kCW ? 0 : 1); 967cb93a386Sopenharmony_ci SkPath_RectPointIterator rectIter(bounds, dir, rectStartIndex); 968cb93a386Sopenharmony_ci 969cb93a386Sopenharmony_ci this->moveTo(rrectIter.current()); 970cb93a386Sopenharmony_ci if (startsWithConic) { 971cb93a386Sopenharmony_ci for (unsigned i = 0; i < 3; ++i) { 972cb93a386Sopenharmony_ci this->conicTo(rectIter.next(), rrectIter.next(), weight); 973cb93a386Sopenharmony_ci this->lineTo(rrectIter.next()); 974cb93a386Sopenharmony_ci } 975cb93a386Sopenharmony_ci this->conicTo(rectIter.next(), rrectIter.next(), weight); 976cb93a386Sopenharmony_ci // final lineTo handled by close(). 977cb93a386Sopenharmony_ci } else { 978cb93a386Sopenharmony_ci for (unsigned i = 0; i < 4; ++i) { 979cb93a386Sopenharmony_ci this->lineTo(rrectIter.next()); 980cb93a386Sopenharmony_ci this->conicTo(rectIter.next(), rrectIter.next(), weight); 981cb93a386Sopenharmony_ci } 982cb93a386Sopenharmony_ci } 983cb93a386Sopenharmony_ci this->close(); 984cb93a386Sopenharmony_ci 985cb93a386Sopenharmony_ci SkPathRef::Editor ed(&fPathRef); 986cb93a386Sopenharmony_ci ed.setIsRRect(isRRect, dir == SkPathDirection::kCCW, startIndex % 8); 987cb93a386Sopenharmony_ci 988cb93a386Sopenharmony_ci SkASSERT(this->countVerbs() == initialVerbCount + kVerbs); 989cb93a386Sopenharmony_ci } 990cb93a386Sopenharmony_ci 991cb93a386Sopenharmony_ci SkDEBUGCODE(fPathRef->validate();) 992cb93a386Sopenharmony_ci return *this; 993cb93a386Sopenharmony_ci} 994cb93a386Sopenharmony_ci 995cb93a386Sopenharmony_cibool SkPath::hasOnlyMoveTos() const { 996cb93a386Sopenharmony_ci int count = fPathRef->countVerbs(); 997cb93a386Sopenharmony_ci const uint8_t* verbs = fPathRef->verbsBegin(); 998cb93a386Sopenharmony_ci for (int i = 0; i < count; ++i) { 999cb93a386Sopenharmony_ci if (*verbs == kLine_Verb || 1000cb93a386Sopenharmony_ci *verbs == kQuad_Verb || 1001cb93a386Sopenharmony_ci *verbs == kConic_Verb || 1002cb93a386Sopenharmony_ci *verbs == kCubic_Verb) { 1003cb93a386Sopenharmony_ci return false; 1004cb93a386Sopenharmony_ci } 1005cb93a386Sopenharmony_ci ++verbs; 1006cb93a386Sopenharmony_ci } 1007cb93a386Sopenharmony_ci return true; 1008cb93a386Sopenharmony_ci} 1009cb93a386Sopenharmony_ci 1010cb93a386Sopenharmony_cibool SkPath::isZeroLengthSincePoint(int startPtIndex) const { 1011cb93a386Sopenharmony_ci int count = fPathRef->countPoints() - startPtIndex; 1012cb93a386Sopenharmony_ci if (count < 2) { 1013cb93a386Sopenharmony_ci return true; 1014cb93a386Sopenharmony_ci } 1015cb93a386Sopenharmony_ci const SkPoint* pts = fPathRef->points() + startPtIndex; 1016cb93a386Sopenharmony_ci const SkPoint& first = *pts; 1017cb93a386Sopenharmony_ci for (int index = 1; index < count; ++index) { 1018cb93a386Sopenharmony_ci if (first != pts[index]) { 1019cb93a386Sopenharmony_ci return false; 1020cb93a386Sopenharmony_ci } 1021cb93a386Sopenharmony_ci } 1022cb93a386Sopenharmony_ci return true; 1023cb93a386Sopenharmony_ci} 1024cb93a386Sopenharmony_ci 1025cb93a386Sopenharmony_ciSkPath& SkPath::addRoundRect(const SkRect& rect, SkScalar rx, SkScalar ry, 1026cb93a386Sopenharmony_ci SkPathDirection dir) { 1027cb93a386Sopenharmony_ci assert_known_direction(dir); 1028cb93a386Sopenharmony_ci 1029cb93a386Sopenharmony_ci if (rx < 0 || ry < 0) { 1030cb93a386Sopenharmony_ci return *this; 1031cb93a386Sopenharmony_ci } 1032cb93a386Sopenharmony_ci 1033cb93a386Sopenharmony_ci SkRRect rrect; 1034cb93a386Sopenharmony_ci rrect.setRectXY(rect, rx, ry); 1035cb93a386Sopenharmony_ci return this->addRRect(rrect, dir); 1036cb93a386Sopenharmony_ci} 1037cb93a386Sopenharmony_ci 1038cb93a386Sopenharmony_ciSkPath& SkPath::addOval(const SkRect& oval, SkPathDirection dir) { 1039cb93a386Sopenharmony_ci // legacy start index: 1 1040cb93a386Sopenharmony_ci return this->addOval(oval, dir, 1); 1041cb93a386Sopenharmony_ci} 1042cb93a386Sopenharmony_ci 1043cb93a386Sopenharmony_ciSkPath& SkPath::addOval(const SkRect &oval, SkPathDirection dir, unsigned startPointIndex) { 1044cb93a386Sopenharmony_ci assert_known_direction(dir); 1045cb93a386Sopenharmony_ci 1046cb93a386Sopenharmony_ci /* If addOval() is called after previous moveTo(), 1047cb93a386Sopenharmony_ci this path is still marked as an oval. This is used to 1048cb93a386Sopenharmony_ci fit into WebKit's calling sequences. 1049cb93a386Sopenharmony_ci We can't simply check isEmpty() in this case, as additional 1050cb93a386Sopenharmony_ci moveTo() would mark the path non empty. 1051cb93a386Sopenharmony_ci */ 1052cb93a386Sopenharmony_ci bool isOval = hasOnlyMoveTos(); 1053cb93a386Sopenharmony_ci if (isOval) { 1054cb93a386Sopenharmony_ci this->setFirstDirection((SkPathFirstDirection)dir); 1055cb93a386Sopenharmony_ci } else { 1056cb93a386Sopenharmony_ci this->setFirstDirection(SkPathFirstDirection::kUnknown); 1057cb93a386Sopenharmony_ci } 1058cb93a386Sopenharmony_ci 1059cb93a386Sopenharmony_ci SkAutoDisableDirectionCheck addc(this); 1060cb93a386Sopenharmony_ci SkAutoPathBoundsUpdate apbu(this, oval); 1061cb93a386Sopenharmony_ci 1062cb93a386Sopenharmony_ci SkDEBUGCODE(int initialVerbCount = this->countVerbs()); 1063cb93a386Sopenharmony_ci const int kVerbs = 6; // moveTo + 4x conicTo + close 1064cb93a386Sopenharmony_ci this->incReserve(kVerbs); 1065cb93a386Sopenharmony_ci 1066cb93a386Sopenharmony_ci SkPath_OvalPointIterator ovalIter(oval, dir, startPointIndex); 1067cb93a386Sopenharmony_ci // The corner iterator pts are tracking "behind" the oval/radii pts. 1068cb93a386Sopenharmony_ci SkPath_RectPointIterator rectIter(oval, dir, startPointIndex + (dir == SkPathDirection::kCW ? 0 : 1)); 1069cb93a386Sopenharmony_ci const SkScalar weight = SK_ScalarRoot2Over2; 1070cb93a386Sopenharmony_ci 1071cb93a386Sopenharmony_ci this->moveTo(ovalIter.current()); 1072cb93a386Sopenharmony_ci for (unsigned i = 0; i < 4; ++i) { 1073cb93a386Sopenharmony_ci this->conicTo(rectIter.next(), ovalIter.next(), weight); 1074cb93a386Sopenharmony_ci } 1075cb93a386Sopenharmony_ci this->close(); 1076cb93a386Sopenharmony_ci 1077cb93a386Sopenharmony_ci SkASSERT(this->countVerbs() == initialVerbCount + kVerbs); 1078cb93a386Sopenharmony_ci 1079cb93a386Sopenharmony_ci SkPathRef::Editor ed(&fPathRef); 1080cb93a386Sopenharmony_ci 1081cb93a386Sopenharmony_ci ed.setIsOval(isOval, SkPathDirection::kCCW == dir, startPointIndex % 4); 1082cb93a386Sopenharmony_ci return *this; 1083cb93a386Sopenharmony_ci} 1084cb93a386Sopenharmony_ci 1085cb93a386Sopenharmony_ciSkPath& SkPath::addCircle(SkScalar x, SkScalar y, SkScalar r, SkPathDirection dir) { 1086cb93a386Sopenharmony_ci if (r > 0) { 1087cb93a386Sopenharmony_ci this->addOval(SkRect::MakeLTRB(x - r, y - r, x + r, y + r), dir); 1088cb93a386Sopenharmony_ci } 1089cb93a386Sopenharmony_ci return *this; 1090cb93a386Sopenharmony_ci} 1091cb93a386Sopenharmony_ci 1092cb93a386Sopenharmony_ciSkPath& SkPath::arcTo(const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle, 1093cb93a386Sopenharmony_ci bool forceMoveTo) { 1094cb93a386Sopenharmony_ci if (oval.width() < 0 || oval.height() < 0) { 1095cb93a386Sopenharmony_ci return *this; 1096cb93a386Sopenharmony_ci } 1097cb93a386Sopenharmony_ci 1098cb93a386Sopenharmony_ci if (fPathRef->countVerbs() == 0) { 1099cb93a386Sopenharmony_ci forceMoveTo = true; 1100cb93a386Sopenharmony_ci } 1101cb93a386Sopenharmony_ci 1102cb93a386Sopenharmony_ci SkPoint lonePt; 1103cb93a386Sopenharmony_ci if (arc_is_lone_point(oval, startAngle, sweepAngle, &lonePt)) { 1104cb93a386Sopenharmony_ci return forceMoveTo ? this->moveTo(lonePt) : this->lineTo(lonePt); 1105cb93a386Sopenharmony_ci } 1106cb93a386Sopenharmony_ci 1107cb93a386Sopenharmony_ci SkVector startV, stopV; 1108cb93a386Sopenharmony_ci SkRotationDirection dir; 1109cb93a386Sopenharmony_ci angles_to_unit_vectors(startAngle, sweepAngle, &startV, &stopV, &dir); 1110cb93a386Sopenharmony_ci 1111cb93a386Sopenharmony_ci SkPoint singlePt; 1112cb93a386Sopenharmony_ci 1113cb93a386Sopenharmony_ci // Adds a move-to to 'pt' if forceMoveTo is true. Otherwise a lineTo unless we're sufficiently 1114cb93a386Sopenharmony_ci // close to 'pt' currently. This prevents spurious lineTos when adding a series of contiguous 1115cb93a386Sopenharmony_ci // arcs from the same oval. 1116cb93a386Sopenharmony_ci auto addPt = [&forceMoveTo, this](const SkPoint& pt) { 1117cb93a386Sopenharmony_ci SkPoint lastPt; 1118cb93a386Sopenharmony_ci if (forceMoveTo) { 1119cb93a386Sopenharmony_ci this->moveTo(pt); 1120cb93a386Sopenharmony_ci } else if (!this->getLastPt(&lastPt) || 1121cb93a386Sopenharmony_ci !SkScalarNearlyEqual(lastPt.fX, pt.fX) || 1122cb93a386Sopenharmony_ci !SkScalarNearlyEqual(lastPt.fY, pt.fY)) { 1123cb93a386Sopenharmony_ci this->lineTo(pt); 1124cb93a386Sopenharmony_ci } 1125cb93a386Sopenharmony_ci }; 1126cb93a386Sopenharmony_ci 1127cb93a386Sopenharmony_ci // At this point, we know that the arc is not a lone point, but startV == stopV 1128cb93a386Sopenharmony_ci // indicates that the sweepAngle is too small such that angles_to_unit_vectors 1129cb93a386Sopenharmony_ci // cannot handle it. 1130cb93a386Sopenharmony_ci if (startV == stopV) { 1131cb93a386Sopenharmony_ci SkScalar endAngle = SkDegreesToRadians(startAngle + sweepAngle); 1132cb93a386Sopenharmony_ci SkScalar radiusX = oval.width() / 2; 1133cb93a386Sopenharmony_ci SkScalar radiusY = oval.height() / 2; 1134cb93a386Sopenharmony_ci // We do not use SkScalar[Sin|Cos]SnapToZero here. When sin(startAngle) is 0 and sweepAngle 1135cb93a386Sopenharmony_ci // is very small and radius is huge, the expected behavior here is to draw a line. But 1136cb93a386Sopenharmony_ci // calling SkScalarSinSnapToZero will make sin(endAngle) be 0 which will then draw a dot. 1137cb93a386Sopenharmony_ci singlePt.set(oval.centerX() + radiusX * SkScalarCos(endAngle), 1138cb93a386Sopenharmony_ci oval.centerY() + radiusY * SkScalarSin(endAngle)); 1139cb93a386Sopenharmony_ci addPt(singlePt); 1140cb93a386Sopenharmony_ci return *this; 1141cb93a386Sopenharmony_ci } 1142cb93a386Sopenharmony_ci 1143cb93a386Sopenharmony_ci SkConic conics[SkConic::kMaxConicsForArc]; 1144cb93a386Sopenharmony_ci int count = build_arc_conics(oval, startV, stopV, dir, conics, &singlePt); 1145cb93a386Sopenharmony_ci if (count) { 1146cb93a386Sopenharmony_ci this->incReserve(count * 2 + 1); 1147cb93a386Sopenharmony_ci const SkPoint& pt = conics[0].fPts[0]; 1148cb93a386Sopenharmony_ci addPt(pt); 1149cb93a386Sopenharmony_ci for (int i = 0; i < count; ++i) { 1150cb93a386Sopenharmony_ci this->conicTo(conics[i].fPts[1], conics[i].fPts[2], conics[i].fW); 1151cb93a386Sopenharmony_ci } 1152cb93a386Sopenharmony_ci } else { 1153cb93a386Sopenharmony_ci addPt(singlePt); 1154cb93a386Sopenharmony_ci } 1155cb93a386Sopenharmony_ci return *this; 1156cb93a386Sopenharmony_ci} 1157cb93a386Sopenharmony_ci 1158cb93a386Sopenharmony_ci// This converts the SVG arc to conics. 1159cb93a386Sopenharmony_ci// Partly adapted from Niko's code in kdelibs/kdecore/svgicons. 1160cb93a386Sopenharmony_ci// Then transcribed from webkit/chrome's SVGPathNormalizer::decomposeArcToCubic() 1161cb93a386Sopenharmony_ci// See also SVG implementation notes: 1162cb93a386Sopenharmony_ci// http://www.w3.org/TR/SVG/implnote.html#ArcConversionEndpointToCenter 1163cb93a386Sopenharmony_ci// Note that arcSweep bool value is flipped from the original implementation. 1164cb93a386Sopenharmony_ciSkPath& SkPath::arcTo(SkScalar rx, SkScalar ry, SkScalar angle, SkPath::ArcSize arcLarge, 1165cb93a386Sopenharmony_ci SkPathDirection arcSweep, SkScalar x, SkScalar y) { 1166cb93a386Sopenharmony_ci this->injectMoveToIfNeeded(); 1167cb93a386Sopenharmony_ci SkPoint srcPts[2]; 1168cb93a386Sopenharmony_ci this->getLastPt(&srcPts[0]); 1169cb93a386Sopenharmony_ci // If rx = 0 or ry = 0 then this arc is treated as a straight line segment (a "lineto") 1170cb93a386Sopenharmony_ci // joining the endpoints. 1171cb93a386Sopenharmony_ci // http://www.w3.org/TR/SVG/implnote.html#ArcOutOfRangeParameters 1172cb93a386Sopenharmony_ci if (!rx || !ry) { 1173cb93a386Sopenharmony_ci return this->lineTo(x, y); 1174cb93a386Sopenharmony_ci } 1175cb93a386Sopenharmony_ci // If the current point and target point for the arc are identical, it should be treated as a 1176cb93a386Sopenharmony_ci // zero length path. This ensures continuity in animations. 1177cb93a386Sopenharmony_ci srcPts[1].set(x, y); 1178cb93a386Sopenharmony_ci if (srcPts[0] == srcPts[1]) { 1179cb93a386Sopenharmony_ci return this->lineTo(x, y); 1180cb93a386Sopenharmony_ci } 1181cb93a386Sopenharmony_ci rx = SkScalarAbs(rx); 1182cb93a386Sopenharmony_ci ry = SkScalarAbs(ry); 1183cb93a386Sopenharmony_ci SkVector midPointDistance = srcPts[0] - srcPts[1]; 1184cb93a386Sopenharmony_ci midPointDistance *= 0.5f; 1185cb93a386Sopenharmony_ci 1186cb93a386Sopenharmony_ci SkMatrix pointTransform; 1187cb93a386Sopenharmony_ci pointTransform.setRotate(-angle); 1188cb93a386Sopenharmony_ci 1189cb93a386Sopenharmony_ci SkPoint transformedMidPoint; 1190cb93a386Sopenharmony_ci pointTransform.mapPoints(&transformedMidPoint, &midPointDistance, 1); 1191cb93a386Sopenharmony_ci SkScalar squareRx = rx * rx; 1192cb93a386Sopenharmony_ci SkScalar squareRy = ry * ry; 1193cb93a386Sopenharmony_ci SkScalar squareX = transformedMidPoint.fX * transformedMidPoint.fX; 1194cb93a386Sopenharmony_ci SkScalar squareY = transformedMidPoint.fY * transformedMidPoint.fY; 1195cb93a386Sopenharmony_ci 1196cb93a386Sopenharmony_ci // Check if the radii are big enough to draw the arc, scale radii if not. 1197cb93a386Sopenharmony_ci // http://www.w3.org/TR/SVG/implnote.html#ArcCorrectionOutOfRangeRadii 1198cb93a386Sopenharmony_ci SkScalar radiiScale = squareX / squareRx + squareY / squareRy; 1199cb93a386Sopenharmony_ci if (radiiScale > 1) { 1200cb93a386Sopenharmony_ci radiiScale = SkScalarSqrt(radiiScale); 1201cb93a386Sopenharmony_ci rx *= radiiScale; 1202cb93a386Sopenharmony_ci ry *= radiiScale; 1203cb93a386Sopenharmony_ci } 1204cb93a386Sopenharmony_ci 1205cb93a386Sopenharmony_ci pointTransform.setScale(1 / rx, 1 / ry); 1206cb93a386Sopenharmony_ci pointTransform.preRotate(-angle); 1207cb93a386Sopenharmony_ci 1208cb93a386Sopenharmony_ci SkPoint unitPts[2]; 1209cb93a386Sopenharmony_ci pointTransform.mapPoints(unitPts, srcPts, (int) SK_ARRAY_COUNT(unitPts)); 1210cb93a386Sopenharmony_ci SkVector delta = unitPts[1] - unitPts[0]; 1211cb93a386Sopenharmony_ci 1212cb93a386Sopenharmony_ci SkScalar d = delta.fX * delta.fX + delta.fY * delta.fY; 1213cb93a386Sopenharmony_ci SkScalar scaleFactorSquared = std::max(1 / d - 0.25f, 0.f); 1214cb93a386Sopenharmony_ci 1215cb93a386Sopenharmony_ci SkScalar scaleFactor = SkScalarSqrt(scaleFactorSquared); 1216cb93a386Sopenharmony_ci if ((arcSweep == SkPathDirection::kCCW) != SkToBool(arcLarge)) { // flipped from the original implementation 1217cb93a386Sopenharmony_ci scaleFactor = -scaleFactor; 1218cb93a386Sopenharmony_ci } 1219cb93a386Sopenharmony_ci delta.scale(scaleFactor); 1220cb93a386Sopenharmony_ci SkPoint centerPoint = unitPts[0] + unitPts[1]; 1221cb93a386Sopenharmony_ci centerPoint *= 0.5f; 1222cb93a386Sopenharmony_ci centerPoint.offset(-delta.fY, delta.fX); 1223cb93a386Sopenharmony_ci unitPts[0] -= centerPoint; 1224cb93a386Sopenharmony_ci unitPts[1] -= centerPoint; 1225cb93a386Sopenharmony_ci SkScalar theta1 = SkScalarATan2(unitPts[0].fY, unitPts[0].fX); 1226cb93a386Sopenharmony_ci SkScalar theta2 = SkScalarATan2(unitPts[1].fY, unitPts[1].fX); 1227cb93a386Sopenharmony_ci SkScalar thetaArc = theta2 - theta1; 1228cb93a386Sopenharmony_ci if (thetaArc < 0 && (arcSweep == SkPathDirection::kCW)) { // arcSweep flipped from the original implementation 1229cb93a386Sopenharmony_ci thetaArc += SK_ScalarPI * 2; 1230cb93a386Sopenharmony_ci } else if (thetaArc > 0 && (arcSweep != SkPathDirection::kCW)) { // arcSweep flipped from the original implementation 1231cb93a386Sopenharmony_ci thetaArc -= SK_ScalarPI * 2; 1232cb93a386Sopenharmony_ci } 1233cb93a386Sopenharmony_ci 1234cb93a386Sopenharmony_ci // Very tiny angles cause our subsequent math to go wonky (skbug.com/9272) 1235cb93a386Sopenharmony_ci // so we do a quick check here. The precise tolerance amount is just made up. 1236cb93a386Sopenharmony_ci // PI/million happens to fix the bug in 9272, but a larger value is probably 1237cb93a386Sopenharmony_ci // ok too. 1238cb93a386Sopenharmony_ci if (SkScalarAbs(thetaArc) < (SK_ScalarPI / (1000 * 1000))) { 1239cb93a386Sopenharmony_ci return this->lineTo(x, y); 1240cb93a386Sopenharmony_ci } 1241cb93a386Sopenharmony_ci 1242cb93a386Sopenharmony_ci pointTransform.setRotate(angle); 1243cb93a386Sopenharmony_ci pointTransform.preScale(rx, ry); 1244cb93a386Sopenharmony_ci 1245cb93a386Sopenharmony_ci // the arc may be slightly bigger than 1/4 circle, so allow up to 1/3rd 1246cb93a386Sopenharmony_ci int segments = SkScalarCeilToInt(SkScalarAbs(thetaArc / (2 * SK_ScalarPI / 3))); 1247cb93a386Sopenharmony_ci SkScalar thetaWidth = thetaArc / segments; 1248cb93a386Sopenharmony_ci SkScalar t = SkScalarTan(0.5f * thetaWidth); 1249cb93a386Sopenharmony_ci if (!SkScalarIsFinite(t)) { 1250cb93a386Sopenharmony_ci return *this; 1251cb93a386Sopenharmony_ci } 1252cb93a386Sopenharmony_ci SkScalar startTheta = theta1; 1253cb93a386Sopenharmony_ci SkScalar w = SkScalarSqrt(SK_ScalarHalf + SkScalarCos(thetaWidth) * SK_ScalarHalf); 1254cb93a386Sopenharmony_ci auto scalar_is_integer = [](SkScalar scalar) -> bool { 1255cb93a386Sopenharmony_ci return scalar == SkScalarFloorToScalar(scalar); 1256cb93a386Sopenharmony_ci }; 1257cb93a386Sopenharmony_ci bool expectIntegers = SkScalarNearlyZero(SK_ScalarPI/2 - SkScalarAbs(thetaWidth)) && 1258cb93a386Sopenharmony_ci scalar_is_integer(rx) && scalar_is_integer(ry) && 1259cb93a386Sopenharmony_ci scalar_is_integer(x) && scalar_is_integer(y); 1260cb93a386Sopenharmony_ci 1261cb93a386Sopenharmony_ci for (int i = 0; i < segments; ++i) { 1262cb93a386Sopenharmony_ci SkScalar endTheta = startTheta + thetaWidth, 1263cb93a386Sopenharmony_ci sinEndTheta = SkScalarSinSnapToZero(endTheta), 1264cb93a386Sopenharmony_ci cosEndTheta = SkScalarCosSnapToZero(endTheta); 1265cb93a386Sopenharmony_ci 1266cb93a386Sopenharmony_ci unitPts[1].set(cosEndTheta, sinEndTheta); 1267cb93a386Sopenharmony_ci unitPts[1] += centerPoint; 1268cb93a386Sopenharmony_ci unitPts[0] = unitPts[1]; 1269cb93a386Sopenharmony_ci unitPts[0].offset(t * sinEndTheta, -t * cosEndTheta); 1270cb93a386Sopenharmony_ci SkPoint mapped[2]; 1271cb93a386Sopenharmony_ci pointTransform.mapPoints(mapped, unitPts, (int) SK_ARRAY_COUNT(unitPts)); 1272cb93a386Sopenharmony_ci /* 1273cb93a386Sopenharmony_ci Computing the arc width introduces rounding errors that cause arcs to start 1274cb93a386Sopenharmony_ci outside their marks. A round rect may lose convexity as a result. If the input 1275cb93a386Sopenharmony_ci values are on integers, place the conic on integers as well. 1276cb93a386Sopenharmony_ci */ 1277cb93a386Sopenharmony_ci if (expectIntegers) { 1278cb93a386Sopenharmony_ci for (SkPoint& point : mapped) { 1279cb93a386Sopenharmony_ci point.fX = SkScalarRoundToScalar(point.fX); 1280cb93a386Sopenharmony_ci point.fY = SkScalarRoundToScalar(point.fY); 1281cb93a386Sopenharmony_ci } 1282cb93a386Sopenharmony_ci } 1283cb93a386Sopenharmony_ci this->conicTo(mapped[0], mapped[1], w); 1284cb93a386Sopenharmony_ci startTheta = endTheta; 1285cb93a386Sopenharmony_ci } 1286cb93a386Sopenharmony_ci 1287cb93a386Sopenharmony_ci#ifndef SK_LEGACY_PATH_ARCTO_ENDPOINT 1288cb93a386Sopenharmony_ci // The final point should match the input point (by definition); replace it to 1289cb93a386Sopenharmony_ci // ensure that rounding errors in the above math don't cause any problems. 1290cb93a386Sopenharmony_ci this->setLastPt(x, y); 1291cb93a386Sopenharmony_ci#endif 1292cb93a386Sopenharmony_ci return *this; 1293cb93a386Sopenharmony_ci} 1294cb93a386Sopenharmony_ci 1295cb93a386Sopenharmony_ciSkPath& SkPath::rArcTo(SkScalar rx, SkScalar ry, SkScalar xAxisRotate, SkPath::ArcSize largeArc, 1296cb93a386Sopenharmony_ci SkPathDirection sweep, SkScalar dx, SkScalar dy) { 1297cb93a386Sopenharmony_ci SkPoint currentPoint; 1298cb93a386Sopenharmony_ci this->getLastPt(¤tPoint); 1299cb93a386Sopenharmony_ci return this->arcTo(rx, ry, xAxisRotate, largeArc, sweep, 1300cb93a386Sopenharmony_ci currentPoint.fX + dx, currentPoint.fY + dy); 1301cb93a386Sopenharmony_ci} 1302cb93a386Sopenharmony_ci 1303cb93a386Sopenharmony_ciSkPath& SkPath::addArc(const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle) { 1304cb93a386Sopenharmony_ci if (oval.isEmpty() || 0 == sweepAngle) { 1305cb93a386Sopenharmony_ci return *this; 1306cb93a386Sopenharmony_ci } 1307cb93a386Sopenharmony_ci 1308cb93a386Sopenharmony_ci const SkScalar kFullCircleAngle = SkIntToScalar(360); 1309cb93a386Sopenharmony_ci 1310cb93a386Sopenharmony_ci if (sweepAngle >= kFullCircleAngle || sweepAngle <= -kFullCircleAngle) { 1311cb93a386Sopenharmony_ci // We can treat the arc as an oval if it begins at one of our legal starting positions. 1312cb93a386Sopenharmony_ci // See SkPath::addOval() docs. 1313cb93a386Sopenharmony_ci SkScalar startOver90 = startAngle / 90.f; 1314cb93a386Sopenharmony_ci SkScalar startOver90I = SkScalarRoundToScalar(startOver90); 1315cb93a386Sopenharmony_ci SkScalar error = startOver90 - startOver90I; 1316cb93a386Sopenharmony_ci if (SkScalarNearlyEqual(error, 0)) { 1317cb93a386Sopenharmony_ci // Index 1 is at startAngle == 0. 1318cb93a386Sopenharmony_ci SkScalar startIndex = std::fmod(startOver90I + 1.f, 4.f); 1319cb93a386Sopenharmony_ci startIndex = startIndex < 0 ? startIndex + 4.f : startIndex; 1320cb93a386Sopenharmony_ci return this->addOval(oval, sweepAngle > 0 ? SkPathDirection::kCW : SkPathDirection::kCCW, 1321cb93a386Sopenharmony_ci (unsigned) startIndex); 1322cb93a386Sopenharmony_ci } 1323cb93a386Sopenharmony_ci } 1324cb93a386Sopenharmony_ci return this->arcTo(oval, startAngle, sweepAngle, true); 1325cb93a386Sopenharmony_ci} 1326cb93a386Sopenharmony_ci 1327cb93a386Sopenharmony_ci/* 1328cb93a386Sopenharmony_ci Need to handle the case when the angle is sharp, and our computed end-points 1329cb93a386Sopenharmony_ci for the arc go behind pt1 and/or p2... 1330cb93a386Sopenharmony_ci*/ 1331cb93a386Sopenharmony_ciSkPath& SkPath::arcTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2, SkScalar radius) { 1332cb93a386Sopenharmony_ci this->injectMoveToIfNeeded(); 1333cb93a386Sopenharmony_ci 1334cb93a386Sopenharmony_ci if (radius == 0) { 1335cb93a386Sopenharmony_ci return this->lineTo(x1, y1); 1336cb93a386Sopenharmony_ci } 1337cb93a386Sopenharmony_ci 1338cb93a386Sopenharmony_ci // need to know our prev pt so we can construct tangent vectors 1339cb93a386Sopenharmony_ci SkPoint start; 1340cb93a386Sopenharmony_ci this->getLastPt(&start); 1341cb93a386Sopenharmony_ci 1342cb93a386Sopenharmony_ci // need double precision for these calcs. 1343cb93a386Sopenharmony_ci SkDVector befored, afterd; 1344cb93a386Sopenharmony_ci befored.set({x1 - start.fX, y1 - start.fY}).normalize(); 1345cb93a386Sopenharmony_ci afterd.set({x2 - x1, y2 - y1}).normalize(); 1346cb93a386Sopenharmony_ci double cosh = befored.dot(afterd); 1347cb93a386Sopenharmony_ci double sinh = befored.cross(afterd); 1348cb93a386Sopenharmony_ci 1349cb93a386Sopenharmony_ci if (!befored.isFinite() || !afterd.isFinite() || SkScalarNearlyZero(SkDoubleToScalar(sinh))) { 1350cb93a386Sopenharmony_ci return this->lineTo(x1, y1); 1351cb93a386Sopenharmony_ci } 1352cb93a386Sopenharmony_ci 1353cb93a386Sopenharmony_ci // safe to convert back to floats now 1354cb93a386Sopenharmony_ci SkVector before = befored.asSkVector(); 1355cb93a386Sopenharmony_ci SkVector after = afterd.asSkVector(); 1356cb93a386Sopenharmony_ci SkScalar dist = SkScalarAbs(SkDoubleToScalar(radius * (1 - cosh) / sinh)); 1357cb93a386Sopenharmony_ci SkScalar xx = x1 - dist * before.fX; 1358cb93a386Sopenharmony_ci SkScalar yy = y1 - dist * before.fY; 1359cb93a386Sopenharmony_ci after.setLength(dist); 1360cb93a386Sopenharmony_ci this->lineTo(xx, yy); 1361cb93a386Sopenharmony_ci SkScalar weight = SkScalarSqrt(SkDoubleToScalar(SK_ScalarHalf + cosh * 0.5)); 1362cb93a386Sopenharmony_ci return this->conicTo(x1, y1, x1 + after.fX, y1 + after.fY, weight); 1363cb93a386Sopenharmony_ci} 1364cb93a386Sopenharmony_ci 1365cb93a386Sopenharmony_ci/////////////////////////////////////////////////////////////////////////////// 1366cb93a386Sopenharmony_ci 1367cb93a386Sopenharmony_ciSkPath& SkPath::addPath(const SkPath& path, SkScalar dx, SkScalar dy, AddPathMode mode) { 1368cb93a386Sopenharmony_ci SkMatrix matrix; 1369cb93a386Sopenharmony_ci 1370cb93a386Sopenharmony_ci matrix.setTranslate(dx, dy); 1371cb93a386Sopenharmony_ci return this->addPath(path, matrix, mode); 1372cb93a386Sopenharmony_ci} 1373cb93a386Sopenharmony_ci 1374cb93a386Sopenharmony_ciSkPath& SkPath::addPath(const SkPath& srcPath, const SkMatrix& matrix, AddPathMode mode) { 1375cb93a386Sopenharmony_ci if (srcPath.isEmpty()) { 1376cb93a386Sopenharmony_ci return *this; 1377cb93a386Sopenharmony_ci } 1378cb93a386Sopenharmony_ci 1379cb93a386Sopenharmony_ci // Detect if we're trying to add ourself 1380cb93a386Sopenharmony_ci const SkPath* src = &srcPath; 1381cb93a386Sopenharmony_ci SkTLazy<SkPath> tmp; 1382cb93a386Sopenharmony_ci if (this == src) { 1383cb93a386Sopenharmony_ci src = tmp.set(srcPath); 1384cb93a386Sopenharmony_ci } 1385cb93a386Sopenharmony_ci 1386cb93a386Sopenharmony_ci if (kAppend_AddPathMode == mode && !matrix.hasPerspective()) { 1387cb93a386Sopenharmony_ci fLastMoveToIndex = this->countPoints() + src->fLastMoveToIndex; 1388cb93a386Sopenharmony_ci 1389cb93a386Sopenharmony_ci SkPathRef::Editor ed(&fPathRef); 1390cb93a386Sopenharmony_ci auto [newPts, newWeights] = ed.growForVerbsInPath(*src->fPathRef); 1391cb93a386Sopenharmony_ci matrix.mapPoints(newPts, src->fPathRef->points(), src->countPoints()); 1392cb93a386Sopenharmony_ci if (int numWeights = src->fPathRef->countWeights()) { 1393cb93a386Sopenharmony_ci memcpy(newWeights, src->fPathRef->conicWeights(), numWeights * sizeof(newWeights[0])); 1394cb93a386Sopenharmony_ci } 1395cb93a386Sopenharmony_ci // fiddle with fLastMoveToIndex, as we do in SkPath::close() 1396cb93a386Sopenharmony_ci if ((SkPathVerb)fPathRef->verbsEnd()[-1] == SkPathVerb::kClose) { 1397cb93a386Sopenharmony_ci fLastMoveToIndex ^= ~fLastMoveToIndex >> (8 * sizeof(fLastMoveToIndex) - 1); 1398cb93a386Sopenharmony_ci } 1399cb93a386Sopenharmony_ci return this->dirtyAfterEdit(); 1400cb93a386Sopenharmony_ci } 1401cb93a386Sopenharmony_ci 1402cb93a386Sopenharmony_ci SkMatrixPriv::MapPtsProc mapPtsProc = SkMatrixPriv::GetMapPtsProc(matrix); 1403cb93a386Sopenharmony_ci bool firstVerb = true; 1404cb93a386Sopenharmony_ci for (auto [verb, pts, w] : SkPathPriv::Iterate(*src)) { 1405cb93a386Sopenharmony_ci switch (verb) { 1406cb93a386Sopenharmony_ci SkPoint mappedPts[3]; 1407cb93a386Sopenharmony_ci case SkPathVerb::kMove: 1408cb93a386Sopenharmony_ci mapPtsProc(matrix, mappedPts, &pts[0], 1); 1409cb93a386Sopenharmony_ci if (firstVerb && mode == kExtend_AddPathMode && !isEmpty()) { 1410cb93a386Sopenharmony_ci injectMoveToIfNeeded(); // In case last contour is closed 1411cb93a386Sopenharmony_ci SkPoint lastPt; 1412cb93a386Sopenharmony_ci // don't add lineTo if it is degenerate 1413cb93a386Sopenharmony_ci if (fLastMoveToIndex < 0 || !this->getLastPt(&lastPt) || 1414cb93a386Sopenharmony_ci lastPt != mappedPts[0]) { 1415cb93a386Sopenharmony_ci this->lineTo(mappedPts[0]); 1416cb93a386Sopenharmony_ci } 1417cb93a386Sopenharmony_ci } else { 1418cb93a386Sopenharmony_ci this->moveTo(mappedPts[0]); 1419cb93a386Sopenharmony_ci } 1420cb93a386Sopenharmony_ci break; 1421cb93a386Sopenharmony_ci case SkPathVerb::kLine: 1422cb93a386Sopenharmony_ci mapPtsProc(matrix, mappedPts, &pts[1], 1); 1423cb93a386Sopenharmony_ci this->lineTo(mappedPts[0]); 1424cb93a386Sopenharmony_ci break; 1425cb93a386Sopenharmony_ci case SkPathVerb::kQuad: 1426cb93a386Sopenharmony_ci mapPtsProc(matrix, mappedPts, &pts[1], 2); 1427cb93a386Sopenharmony_ci this->quadTo(mappedPts[0], mappedPts[1]); 1428cb93a386Sopenharmony_ci break; 1429cb93a386Sopenharmony_ci case SkPathVerb::kConic: 1430cb93a386Sopenharmony_ci mapPtsProc(matrix, mappedPts, &pts[1], 2); 1431cb93a386Sopenharmony_ci this->conicTo(mappedPts[0], mappedPts[1], *w); 1432cb93a386Sopenharmony_ci break; 1433cb93a386Sopenharmony_ci case SkPathVerb::kCubic: 1434cb93a386Sopenharmony_ci mapPtsProc(matrix, mappedPts, &pts[1], 3); 1435cb93a386Sopenharmony_ci this->cubicTo(mappedPts[0], mappedPts[1], mappedPts[2]); 1436cb93a386Sopenharmony_ci break; 1437cb93a386Sopenharmony_ci case SkPathVerb::kClose: 1438cb93a386Sopenharmony_ci this->close(); 1439cb93a386Sopenharmony_ci break; 1440cb93a386Sopenharmony_ci } 1441cb93a386Sopenharmony_ci firstVerb = false; 1442cb93a386Sopenharmony_ci } 1443cb93a386Sopenharmony_ci return *this; 1444cb93a386Sopenharmony_ci} 1445cb93a386Sopenharmony_ci 1446cb93a386Sopenharmony_ci/////////////////////////////////////////////////////////////////////////////// 1447cb93a386Sopenharmony_ci 1448cb93a386Sopenharmony_ci// ignore the last point of the 1st contour 1449cb93a386Sopenharmony_ciSkPath& SkPath::reversePathTo(const SkPath& path) { 1450cb93a386Sopenharmony_ci if (path.fPathRef->fVerbs.count() == 0) { 1451cb93a386Sopenharmony_ci return *this; 1452cb93a386Sopenharmony_ci } 1453cb93a386Sopenharmony_ci 1454cb93a386Sopenharmony_ci const uint8_t* verbs = path.fPathRef->verbsEnd(); 1455cb93a386Sopenharmony_ci const uint8_t* verbsBegin = path.fPathRef->verbsBegin(); 1456cb93a386Sopenharmony_ci SkASSERT(verbsBegin[0] == kMove_Verb); 1457cb93a386Sopenharmony_ci const SkPoint* pts = path.fPathRef->pointsEnd() - 1; 1458cb93a386Sopenharmony_ci const SkScalar* conicWeights = path.fPathRef->conicWeightsEnd(); 1459cb93a386Sopenharmony_ci 1460cb93a386Sopenharmony_ci while (verbs > verbsBegin) { 1461cb93a386Sopenharmony_ci uint8_t v = *--verbs; 1462cb93a386Sopenharmony_ci pts -= SkPathPriv::PtsInVerb(v); 1463cb93a386Sopenharmony_ci switch (v) { 1464cb93a386Sopenharmony_ci case kMove_Verb: 1465cb93a386Sopenharmony_ci // if the path has multiple contours, stop after reversing the last 1466cb93a386Sopenharmony_ci return *this; 1467cb93a386Sopenharmony_ci case kLine_Verb: 1468cb93a386Sopenharmony_ci this->lineTo(pts[0]); 1469cb93a386Sopenharmony_ci break; 1470cb93a386Sopenharmony_ci case kQuad_Verb: 1471cb93a386Sopenharmony_ci this->quadTo(pts[1], pts[0]); 1472cb93a386Sopenharmony_ci break; 1473cb93a386Sopenharmony_ci case kConic_Verb: 1474cb93a386Sopenharmony_ci this->conicTo(pts[1], pts[0], *--conicWeights); 1475cb93a386Sopenharmony_ci break; 1476cb93a386Sopenharmony_ci case kCubic_Verb: 1477cb93a386Sopenharmony_ci this->cubicTo(pts[2], pts[1], pts[0]); 1478cb93a386Sopenharmony_ci break; 1479cb93a386Sopenharmony_ci case kClose_Verb: 1480cb93a386Sopenharmony_ci break; 1481cb93a386Sopenharmony_ci default: 1482cb93a386Sopenharmony_ci SkDEBUGFAIL("bad verb"); 1483cb93a386Sopenharmony_ci break; 1484cb93a386Sopenharmony_ci } 1485cb93a386Sopenharmony_ci } 1486cb93a386Sopenharmony_ci return *this; 1487cb93a386Sopenharmony_ci} 1488cb93a386Sopenharmony_ci 1489cb93a386Sopenharmony_ciSkPath& SkPath::reverseAddPath(const SkPath& srcPath) { 1490cb93a386Sopenharmony_ci // Detect if we're trying to add ourself 1491cb93a386Sopenharmony_ci const SkPath* src = &srcPath; 1492cb93a386Sopenharmony_ci SkTLazy<SkPath> tmp; 1493cb93a386Sopenharmony_ci if (this == src) { 1494cb93a386Sopenharmony_ci src = tmp.set(srcPath); 1495cb93a386Sopenharmony_ci } 1496cb93a386Sopenharmony_ci 1497cb93a386Sopenharmony_ci const uint8_t* verbsBegin = src->fPathRef->verbsBegin(); 1498cb93a386Sopenharmony_ci const uint8_t* verbs = src->fPathRef->verbsEnd(); 1499cb93a386Sopenharmony_ci const SkPoint* pts = src->fPathRef->pointsEnd(); 1500cb93a386Sopenharmony_ci const SkScalar* conicWeights = src->fPathRef->conicWeightsEnd(); 1501cb93a386Sopenharmony_ci 1502cb93a386Sopenharmony_ci bool needMove = true; 1503cb93a386Sopenharmony_ci bool needClose = false; 1504cb93a386Sopenharmony_ci while (verbs > verbsBegin) { 1505cb93a386Sopenharmony_ci uint8_t v = *--verbs; 1506cb93a386Sopenharmony_ci int n = SkPathPriv::PtsInVerb(v); 1507cb93a386Sopenharmony_ci 1508cb93a386Sopenharmony_ci if (needMove) { 1509cb93a386Sopenharmony_ci --pts; 1510cb93a386Sopenharmony_ci this->moveTo(pts->fX, pts->fY); 1511cb93a386Sopenharmony_ci needMove = false; 1512cb93a386Sopenharmony_ci } 1513cb93a386Sopenharmony_ci pts -= n; 1514cb93a386Sopenharmony_ci switch (v) { 1515cb93a386Sopenharmony_ci case kMove_Verb: 1516cb93a386Sopenharmony_ci if (needClose) { 1517cb93a386Sopenharmony_ci this->close(); 1518cb93a386Sopenharmony_ci needClose = false; 1519cb93a386Sopenharmony_ci } 1520cb93a386Sopenharmony_ci needMove = true; 1521cb93a386Sopenharmony_ci pts += 1; // so we see the point in "if (needMove)" above 1522cb93a386Sopenharmony_ci break; 1523cb93a386Sopenharmony_ci case kLine_Verb: 1524cb93a386Sopenharmony_ci this->lineTo(pts[0]); 1525cb93a386Sopenharmony_ci break; 1526cb93a386Sopenharmony_ci case kQuad_Verb: 1527cb93a386Sopenharmony_ci this->quadTo(pts[1], pts[0]); 1528cb93a386Sopenharmony_ci break; 1529cb93a386Sopenharmony_ci case kConic_Verb: 1530cb93a386Sopenharmony_ci this->conicTo(pts[1], pts[0], *--conicWeights); 1531cb93a386Sopenharmony_ci break; 1532cb93a386Sopenharmony_ci case kCubic_Verb: 1533cb93a386Sopenharmony_ci this->cubicTo(pts[2], pts[1], pts[0]); 1534cb93a386Sopenharmony_ci break; 1535cb93a386Sopenharmony_ci case kClose_Verb: 1536cb93a386Sopenharmony_ci needClose = true; 1537cb93a386Sopenharmony_ci break; 1538cb93a386Sopenharmony_ci default: 1539cb93a386Sopenharmony_ci SkDEBUGFAIL("unexpected verb"); 1540cb93a386Sopenharmony_ci } 1541cb93a386Sopenharmony_ci } 1542cb93a386Sopenharmony_ci return *this; 1543cb93a386Sopenharmony_ci} 1544cb93a386Sopenharmony_ci 1545cb93a386Sopenharmony_ci/////////////////////////////////////////////////////////////////////////////// 1546cb93a386Sopenharmony_ci 1547cb93a386Sopenharmony_civoid SkPath::offset(SkScalar dx, SkScalar dy, SkPath* dst) const { 1548cb93a386Sopenharmony_ci SkMatrix matrix; 1549cb93a386Sopenharmony_ci 1550cb93a386Sopenharmony_ci matrix.setTranslate(dx, dy); 1551cb93a386Sopenharmony_ci this->transform(matrix, dst); 1552cb93a386Sopenharmony_ci} 1553cb93a386Sopenharmony_ci 1554cb93a386Sopenharmony_cistatic void subdivide_cubic_to(SkPath* path, const SkPoint pts[4], 1555cb93a386Sopenharmony_ci int level = 2) { 1556cb93a386Sopenharmony_ci if (--level >= 0) { 1557cb93a386Sopenharmony_ci SkPoint tmp[7]; 1558cb93a386Sopenharmony_ci 1559cb93a386Sopenharmony_ci SkChopCubicAtHalf(pts, tmp); 1560cb93a386Sopenharmony_ci subdivide_cubic_to(path, &tmp[0], level); 1561cb93a386Sopenharmony_ci subdivide_cubic_to(path, &tmp[3], level); 1562cb93a386Sopenharmony_ci } else { 1563cb93a386Sopenharmony_ci path->cubicTo(pts[1], pts[2], pts[3]); 1564cb93a386Sopenharmony_ci } 1565cb93a386Sopenharmony_ci} 1566cb93a386Sopenharmony_ci 1567cb93a386Sopenharmony_civoid SkPath::transform(const SkMatrix& matrix, SkPath* dst, SkApplyPerspectiveClip pc) const { 1568cb93a386Sopenharmony_ci if (matrix.isIdentity()) { 1569cb93a386Sopenharmony_ci if (dst != nullptr && dst != this) { 1570cb93a386Sopenharmony_ci *dst = *this; 1571cb93a386Sopenharmony_ci } 1572cb93a386Sopenharmony_ci return; 1573cb93a386Sopenharmony_ci } 1574cb93a386Sopenharmony_ci 1575cb93a386Sopenharmony_ci SkDEBUGCODE(this->validate();) 1576cb93a386Sopenharmony_ci if (dst == nullptr) { 1577cb93a386Sopenharmony_ci dst = (SkPath*)this; 1578cb93a386Sopenharmony_ci } 1579cb93a386Sopenharmony_ci 1580cb93a386Sopenharmony_ci if (matrix.hasPerspective()) { 1581cb93a386Sopenharmony_ci SkPath tmp; 1582cb93a386Sopenharmony_ci tmp.fFillType = fFillType; 1583cb93a386Sopenharmony_ci 1584cb93a386Sopenharmony_ci SkPath clipped; 1585cb93a386Sopenharmony_ci const SkPath* src = this; 1586cb93a386Sopenharmony_ci if (pc == SkApplyPerspectiveClip::kYes && 1587cb93a386Sopenharmony_ci SkPathPriv::PerspectiveClip(*this, matrix, &clipped)) 1588cb93a386Sopenharmony_ci { 1589cb93a386Sopenharmony_ci src = &clipped; 1590cb93a386Sopenharmony_ci } 1591cb93a386Sopenharmony_ci 1592cb93a386Sopenharmony_ci SkPath::Iter iter(*src, false); 1593cb93a386Sopenharmony_ci SkPoint pts[4]; 1594cb93a386Sopenharmony_ci SkPath::Verb verb; 1595cb93a386Sopenharmony_ci 1596cb93a386Sopenharmony_ci while ((verb = iter.next(pts)) != kDone_Verb) { 1597cb93a386Sopenharmony_ci switch (verb) { 1598cb93a386Sopenharmony_ci case kMove_Verb: 1599cb93a386Sopenharmony_ci tmp.moveTo(pts[0]); 1600cb93a386Sopenharmony_ci break; 1601cb93a386Sopenharmony_ci case kLine_Verb: 1602cb93a386Sopenharmony_ci tmp.lineTo(pts[1]); 1603cb93a386Sopenharmony_ci break; 1604cb93a386Sopenharmony_ci case kQuad_Verb: 1605cb93a386Sopenharmony_ci // promote the quad to a conic 1606cb93a386Sopenharmony_ci tmp.conicTo(pts[1], pts[2], 1607cb93a386Sopenharmony_ci SkConic::TransformW(pts, SK_Scalar1, matrix)); 1608cb93a386Sopenharmony_ci break; 1609cb93a386Sopenharmony_ci case kConic_Verb: 1610cb93a386Sopenharmony_ci tmp.conicTo(pts[1], pts[2], 1611cb93a386Sopenharmony_ci SkConic::TransformW(pts, iter.conicWeight(), matrix)); 1612cb93a386Sopenharmony_ci break; 1613cb93a386Sopenharmony_ci case kCubic_Verb: 1614cb93a386Sopenharmony_ci subdivide_cubic_to(&tmp, pts); 1615cb93a386Sopenharmony_ci break; 1616cb93a386Sopenharmony_ci case kClose_Verb: 1617cb93a386Sopenharmony_ci tmp.close(); 1618cb93a386Sopenharmony_ci break; 1619cb93a386Sopenharmony_ci default: 1620cb93a386Sopenharmony_ci SkDEBUGFAIL("unknown verb"); 1621cb93a386Sopenharmony_ci break; 1622cb93a386Sopenharmony_ci } 1623cb93a386Sopenharmony_ci } 1624cb93a386Sopenharmony_ci 1625cb93a386Sopenharmony_ci dst->swap(tmp); 1626cb93a386Sopenharmony_ci SkPathRef::Editor ed(&dst->fPathRef); 1627cb93a386Sopenharmony_ci matrix.mapPoints(ed.writablePoints(), ed.pathRef()->countPoints()); 1628cb93a386Sopenharmony_ci dst->setFirstDirection(SkPathFirstDirection::kUnknown); 1629cb93a386Sopenharmony_ci } else { 1630cb93a386Sopenharmony_ci SkPathConvexity convexity = this->getConvexityOrUnknown(); 1631cb93a386Sopenharmony_ci 1632cb93a386Sopenharmony_ci SkPathRef::CreateTransformedCopy(&dst->fPathRef, *fPathRef, matrix); 1633cb93a386Sopenharmony_ci 1634cb93a386Sopenharmony_ci if (this != dst) { 1635cb93a386Sopenharmony_ci dst->fLastMoveToIndex = fLastMoveToIndex; 1636cb93a386Sopenharmony_ci dst->fFillType = fFillType; 1637cb93a386Sopenharmony_ci dst->fIsVolatile = fIsVolatile; 1638cb93a386Sopenharmony_ci } 1639cb93a386Sopenharmony_ci 1640cb93a386Sopenharmony_ci // Due to finite/fragile float numerics, we can't assume that a convex path remains 1641cb93a386Sopenharmony_ci // convex after a transformation, so mark it as unknown here. 1642cb93a386Sopenharmony_ci // However, some transformations are thought to be safe: 1643cb93a386Sopenharmony_ci // axis-aligned values under scale/translate. 1644cb93a386Sopenharmony_ci // 1645cb93a386Sopenharmony_ci if (convexity == SkPathConvexity::kConvex && 1646cb93a386Sopenharmony_ci (!matrix.isScaleTranslate() || !SkPathPriv::IsAxisAligned(*this))) { 1647cb93a386Sopenharmony_ci // Not safe to still assume we're convex... 1648cb93a386Sopenharmony_ci convexity = SkPathConvexity::kUnknown; 1649cb93a386Sopenharmony_ci } 1650cb93a386Sopenharmony_ci dst->setConvexity(convexity); 1651cb93a386Sopenharmony_ci 1652cb93a386Sopenharmony_ci if (this->getFirstDirection() == SkPathFirstDirection::kUnknown) { 1653cb93a386Sopenharmony_ci dst->setFirstDirection(SkPathFirstDirection::kUnknown); 1654cb93a386Sopenharmony_ci } else { 1655cb93a386Sopenharmony_ci SkScalar det2x2 = 1656cb93a386Sopenharmony_ci matrix.get(SkMatrix::kMScaleX) * matrix.get(SkMatrix::kMScaleY) - 1657cb93a386Sopenharmony_ci matrix.get(SkMatrix::kMSkewX) * matrix.get(SkMatrix::kMSkewY); 1658cb93a386Sopenharmony_ci if (det2x2 < 0) { 1659cb93a386Sopenharmony_ci dst->setFirstDirection( 1660cb93a386Sopenharmony_ci SkPathPriv::OppositeFirstDirection( 1661cb93a386Sopenharmony_ci (SkPathFirstDirection)this->getFirstDirection())); 1662cb93a386Sopenharmony_ci } else if (det2x2 > 0) { 1663cb93a386Sopenharmony_ci dst->setFirstDirection(this->getFirstDirection()); 1664cb93a386Sopenharmony_ci } else { 1665cb93a386Sopenharmony_ci dst->setFirstDirection(SkPathFirstDirection::kUnknown); 1666cb93a386Sopenharmony_ci } 1667cb93a386Sopenharmony_ci } 1668cb93a386Sopenharmony_ci 1669cb93a386Sopenharmony_ci SkDEBUGCODE(dst->validate();) 1670cb93a386Sopenharmony_ci } 1671cb93a386Sopenharmony_ci} 1672cb93a386Sopenharmony_ci 1673cb93a386Sopenharmony_ci/////////////////////////////////////////////////////////////////////////////// 1674cb93a386Sopenharmony_ci/////////////////////////////////////////////////////////////////////////////// 1675cb93a386Sopenharmony_ci 1676cb93a386Sopenharmony_ciSkPath::Iter::Iter() { 1677cb93a386Sopenharmony_ci#ifdef SK_DEBUG 1678cb93a386Sopenharmony_ci fPts = nullptr; 1679cb93a386Sopenharmony_ci fConicWeights = nullptr; 1680cb93a386Sopenharmony_ci fMoveTo.fX = fMoveTo.fY = fLastPt.fX = fLastPt.fY = 0; 1681cb93a386Sopenharmony_ci fForceClose = fCloseLine = false; 1682cb93a386Sopenharmony_ci#endif 1683cb93a386Sopenharmony_ci // need to init enough to make next() harmlessly return kDone_Verb 1684cb93a386Sopenharmony_ci fVerbs = nullptr; 1685cb93a386Sopenharmony_ci fVerbStop = nullptr; 1686cb93a386Sopenharmony_ci fNeedClose = false; 1687cb93a386Sopenharmony_ci} 1688cb93a386Sopenharmony_ci 1689cb93a386Sopenharmony_ciSkPath::Iter::Iter(const SkPath& path, bool forceClose) { 1690cb93a386Sopenharmony_ci this->setPath(path, forceClose); 1691cb93a386Sopenharmony_ci} 1692cb93a386Sopenharmony_ci 1693cb93a386Sopenharmony_civoid SkPath::Iter::setPath(const SkPath& path, bool forceClose) { 1694cb93a386Sopenharmony_ci fPts = path.fPathRef->points(); 1695cb93a386Sopenharmony_ci fVerbs = path.fPathRef->verbsBegin(); 1696cb93a386Sopenharmony_ci fVerbStop = path.fPathRef->verbsEnd(); 1697cb93a386Sopenharmony_ci fConicWeights = path.fPathRef->conicWeights(); 1698cb93a386Sopenharmony_ci if (fConicWeights) { 1699cb93a386Sopenharmony_ci fConicWeights -= 1; // begin one behind 1700cb93a386Sopenharmony_ci } 1701cb93a386Sopenharmony_ci fLastPt.fX = fLastPt.fY = 0; 1702cb93a386Sopenharmony_ci fMoveTo.fX = fMoveTo.fY = 0; 1703cb93a386Sopenharmony_ci fForceClose = SkToU8(forceClose); 1704cb93a386Sopenharmony_ci fNeedClose = false; 1705cb93a386Sopenharmony_ci} 1706cb93a386Sopenharmony_ci 1707cb93a386Sopenharmony_cibool SkPath::Iter::isClosedContour() const { 1708cb93a386Sopenharmony_ci if (fVerbs == nullptr || fVerbs == fVerbStop) { 1709cb93a386Sopenharmony_ci return false; 1710cb93a386Sopenharmony_ci } 1711cb93a386Sopenharmony_ci if (fForceClose) { 1712cb93a386Sopenharmony_ci return true; 1713cb93a386Sopenharmony_ci } 1714cb93a386Sopenharmony_ci 1715cb93a386Sopenharmony_ci const uint8_t* verbs = fVerbs; 1716cb93a386Sopenharmony_ci const uint8_t* stop = fVerbStop; 1717cb93a386Sopenharmony_ci 1718cb93a386Sopenharmony_ci if (kMove_Verb == *verbs) { 1719cb93a386Sopenharmony_ci verbs += 1; // skip the initial moveto 1720cb93a386Sopenharmony_ci } 1721cb93a386Sopenharmony_ci 1722cb93a386Sopenharmony_ci while (verbs < stop) { 1723cb93a386Sopenharmony_ci // verbs points one beyond the current verb, decrement first. 1724cb93a386Sopenharmony_ci unsigned v = *verbs++; 1725cb93a386Sopenharmony_ci if (kMove_Verb == v) { 1726cb93a386Sopenharmony_ci break; 1727cb93a386Sopenharmony_ci } 1728cb93a386Sopenharmony_ci if (kClose_Verb == v) { 1729cb93a386Sopenharmony_ci return true; 1730cb93a386Sopenharmony_ci } 1731cb93a386Sopenharmony_ci } 1732cb93a386Sopenharmony_ci return false; 1733cb93a386Sopenharmony_ci} 1734cb93a386Sopenharmony_ci 1735cb93a386Sopenharmony_ciSkPath::Verb SkPath::Iter::autoClose(SkPoint pts[2]) { 1736cb93a386Sopenharmony_ci SkASSERT(pts); 1737cb93a386Sopenharmony_ci if (fLastPt != fMoveTo) { 1738cb93a386Sopenharmony_ci // A special case: if both points are NaN, SkPoint::operation== returns 1739cb93a386Sopenharmony_ci // false, but the iterator expects that they are treated as the same. 1740cb93a386Sopenharmony_ci // (consider SkPoint is a 2-dimension float point). 1741cb93a386Sopenharmony_ci if (SkScalarIsNaN(fLastPt.fX) || SkScalarIsNaN(fLastPt.fY) || 1742cb93a386Sopenharmony_ci SkScalarIsNaN(fMoveTo.fX) || SkScalarIsNaN(fMoveTo.fY)) { 1743cb93a386Sopenharmony_ci return kClose_Verb; 1744cb93a386Sopenharmony_ci } 1745cb93a386Sopenharmony_ci 1746cb93a386Sopenharmony_ci pts[0] = fLastPt; 1747cb93a386Sopenharmony_ci pts[1] = fMoveTo; 1748cb93a386Sopenharmony_ci fLastPt = fMoveTo; 1749cb93a386Sopenharmony_ci fCloseLine = true; 1750cb93a386Sopenharmony_ci return kLine_Verb; 1751cb93a386Sopenharmony_ci } else { 1752cb93a386Sopenharmony_ci pts[0] = fMoveTo; 1753cb93a386Sopenharmony_ci return kClose_Verb; 1754cb93a386Sopenharmony_ci } 1755cb93a386Sopenharmony_ci} 1756cb93a386Sopenharmony_ci 1757cb93a386Sopenharmony_ciSkPath::Verb SkPath::Iter::next(SkPoint ptsParam[4]) { 1758cb93a386Sopenharmony_ci SkASSERT(ptsParam); 1759cb93a386Sopenharmony_ci 1760cb93a386Sopenharmony_ci if (fVerbs == fVerbStop) { 1761cb93a386Sopenharmony_ci // Close the curve if requested and if there is some curve to close 1762cb93a386Sopenharmony_ci if (fNeedClose) { 1763cb93a386Sopenharmony_ci if (kLine_Verb == this->autoClose(ptsParam)) { 1764cb93a386Sopenharmony_ci return kLine_Verb; 1765cb93a386Sopenharmony_ci } 1766cb93a386Sopenharmony_ci fNeedClose = false; 1767cb93a386Sopenharmony_ci return kClose_Verb; 1768cb93a386Sopenharmony_ci } 1769cb93a386Sopenharmony_ci return kDone_Verb; 1770cb93a386Sopenharmony_ci } 1771cb93a386Sopenharmony_ci 1772cb93a386Sopenharmony_ci unsigned verb = *fVerbs++; 1773cb93a386Sopenharmony_ci const SkPoint* SK_RESTRICT srcPts = fPts; 1774cb93a386Sopenharmony_ci SkPoint* SK_RESTRICT pts = ptsParam; 1775cb93a386Sopenharmony_ci 1776cb93a386Sopenharmony_ci switch (verb) { 1777cb93a386Sopenharmony_ci case kMove_Verb: 1778cb93a386Sopenharmony_ci if (fNeedClose) { 1779cb93a386Sopenharmony_ci fVerbs--; // move back one verb 1780cb93a386Sopenharmony_ci verb = this->autoClose(pts); 1781cb93a386Sopenharmony_ci if (verb == kClose_Verb) { 1782cb93a386Sopenharmony_ci fNeedClose = false; 1783cb93a386Sopenharmony_ci } 1784cb93a386Sopenharmony_ci return (Verb)verb; 1785cb93a386Sopenharmony_ci } 1786cb93a386Sopenharmony_ci if (fVerbs == fVerbStop) { // might be a trailing moveto 1787cb93a386Sopenharmony_ci return kDone_Verb; 1788cb93a386Sopenharmony_ci } 1789cb93a386Sopenharmony_ci fMoveTo = *srcPts; 1790cb93a386Sopenharmony_ci pts[0] = *srcPts; 1791cb93a386Sopenharmony_ci srcPts += 1; 1792cb93a386Sopenharmony_ci fLastPt = fMoveTo; 1793cb93a386Sopenharmony_ci fNeedClose = fForceClose; 1794cb93a386Sopenharmony_ci break; 1795cb93a386Sopenharmony_ci case kLine_Verb: 1796cb93a386Sopenharmony_ci pts[0] = fLastPt; 1797cb93a386Sopenharmony_ci pts[1] = srcPts[0]; 1798cb93a386Sopenharmony_ci fLastPt = srcPts[0]; 1799cb93a386Sopenharmony_ci fCloseLine = false; 1800cb93a386Sopenharmony_ci srcPts += 1; 1801cb93a386Sopenharmony_ci break; 1802cb93a386Sopenharmony_ci case kConic_Verb: 1803cb93a386Sopenharmony_ci fConicWeights += 1; 1804cb93a386Sopenharmony_ci [[fallthrough]]; 1805cb93a386Sopenharmony_ci case kQuad_Verb: 1806cb93a386Sopenharmony_ci pts[0] = fLastPt; 1807cb93a386Sopenharmony_ci memcpy(&pts[1], srcPts, 2 * sizeof(SkPoint)); 1808cb93a386Sopenharmony_ci fLastPt = srcPts[1]; 1809cb93a386Sopenharmony_ci srcPts += 2; 1810cb93a386Sopenharmony_ci break; 1811cb93a386Sopenharmony_ci case kCubic_Verb: 1812cb93a386Sopenharmony_ci pts[0] = fLastPt; 1813cb93a386Sopenharmony_ci memcpy(&pts[1], srcPts, 3 * sizeof(SkPoint)); 1814cb93a386Sopenharmony_ci fLastPt = srcPts[2]; 1815cb93a386Sopenharmony_ci srcPts += 3; 1816cb93a386Sopenharmony_ci break; 1817cb93a386Sopenharmony_ci case kClose_Verb: 1818cb93a386Sopenharmony_ci verb = this->autoClose(pts); 1819cb93a386Sopenharmony_ci if (verb == kLine_Verb) { 1820cb93a386Sopenharmony_ci fVerbs--; // move back one verb 1821cb93a386Sopenharmony_ci } else { 1822cb93a386Sopenharmony_ci fNeedClose = false; 1823cb93a386Sopenharmony_ci } 1824cb93a386Sopenharmony_ci fLastPt = fMoveTo; 1825cb93a386Sopenharmony_ci break; 1826cb93a386Sopenharmony_ci } 1827cb93a386Sopenharmony_ci fPts = srcPts; 1828cb93a386Sopenharmony_ci return (Verb)verb; 1829cb93a386Sopenharmony_ci} 1830cb93a386Sopenharmony_ci 1831cb93a386Sopenharmony_civoid SkPath::RawIter::setPath(const SkPath& path) { 1832cb93a386Sopenharmony_ci SkPathPriv::Iterate iterate(path); 1833cb93a386Sopenharmony_ci fIter = iterate.begin(); 1834cb93a386Sopenharmony_ci fEnd = iterate.end(); 1835cb93a386Sopenharmony_ci} 1836cb93a386Sopenharmony_ci 1837cb93a386Sopenharmony_ciSkPath::Verb SkPath::RawIter::next(SkPoint pts[4]) { 1838cb93a386Sopenharmony_ci if (!(fIter != fEnd)) { 1839cb93a386Sopenharmony_ci return kDone_Verb; 1840cb93a386Sopenharmony_ci } 1841cb93a386Sopenharmony_ci auto [verb, iterPts, weights] = *fIter; 1842cb93a386Sopenharmony_ci int numPts; 1843cb93a386Sopenharmony_ci switch (verb) { 1844cb93a386Sopenharmony_ci case SkPathVerb::kMove: numPts = 1; break; 1845cb93a386Sopenharmony_ci case SkPathVerb::kLine: numPts = 2; break; 1846cb93a386Sopenharmony_ci case SkPathVerb::kQuad: numPts = 3; break; 1847cb93a386Sopenharmony_ci case SkPathVerb::kConic: 1848cb93a386Sopenharmony_ci numPts = 3; 1849cb93a386Sopenharmony_ci fConicWeight = *weights; 1850cb93a386Sopenharmony_ci break; 1851cb93a386Sopenharmony_ci case SkPathVerb::kCubic: numPts = 4; break; 1852cb93a386Sopenharmony_ci case SkPathVerb::kClose: numPts = 0; break; 1853cb93a386Sopenharmony_ci } 1854cb93a386Sopenharmony_ci memcpy(pts, iterPts, sizeof(SkPoint) * numPts); 1855cb93a386Sopenharmony_ci ++fIter; 1856cb93a386Sopenharmony_ci return (Verb) verb; 1857cb93a386Sopenharmony_ci} 1858cb93a386Sopenharmony_ci 1859cb93a386Sopenharmony_ci/////////////////////////////////////////////////////////////////////////////// 1860cb93a386Sopenharmony_ci 1861cb93a386Sopenharmony_ci#include "include/core/SkStream.h" 1862cb93a386Sopenharmony_ci#include "include/core/SkString.h" 1863cb93a386Sopenharmony_ci#include "src/core/SkStringUtils.h" 1864cb93a386Sopenharmony_ci 1865cb93a386Sopenharmony_cistatic void append_params(SkString* str, const char label[], const SkPoint pts[], 1866cb93a386Sopenharmony_ci int count, SkScalarAsStringType strType, SkScalar conicWeight = -12345) { 1867cb93a386Sopenharmony_ci str->append(label); 1868cb93a386Sopenharmony_ci str->append("("); 1869cb93a386Sopenharmony_ci 1870cb93a386Sopenharmony_ci const SkScalar* values = &pts[0].fX; 1871cb93a386Sopenharmony_ci count *= 2; 1872cb93a386Sopenharmony_ci 1873cb93a386Sopenharmony_ci for (int i = 0; i < count; ++i) { 1874cb93a386Sopenharmony_ci SkAppendScalar(str, values[i], strType); 1875cb93a386Sopenharmony_ci if (i < count - 1) { 1876cb93a386Sopenharmony_ci str->append(", "); 1877cb93a386Sopenharmony_ci } 1878cb93a386Sopenharmony_ci } 1879cb93a386Sopenharmony_ci if (conicWeight != -12345) { 1880cb93a386Sopenharmony_ci str->append(", "); 1881cb93a386Sopenharmony_ci SkAppendScalar(str, conicWeight, strType); 1882cb93a386Sopenharmony_ci } 1883cb93a386Sopenharmony_ci str->append(");"); 1884cb93a386Sopenharmony_ci if (kHex_SkScalarAsStringType == strType) { 1885cb93a386Sopenharmony_ci str->append(" // "); 1886cb93a386Sopenharmony_ci for (int i = 0; i < count; ++i) { 1887cb93a386Sopenharmony_ci SkAppendScalarDec(str, values[i]); 1888cb93a386Sopenharmony_ci if (i < count - 1) { 1889cb93a386Sopenharmony_ci str->append(", "); 1890cb93a386Sopenharmony_ci } 1891cb93a386Sopenharmony_ci } 1892cb93a386Sopenharmony_ci if (conicWeight >= 0) { 1893cb93a386Sopenharmony_ci str->append(", "); 1894cb93a386Sopenharmony_ci SkAppendScalarDec(str, conicWeight); 1895cb93a386Sopenharmony_ci } 1896cb93a386Sopenharmony_ci } 1897cb93a386Sopenharmony_ci str->append("\n"); 1898cb93a386Sopenharmony_ci} 1899cb93a386Sopenharmony_ci 1900cb93a386Sopenharmony_civoid SkPath::dump(SkWStream* wStream, bool dumpAsHex) const { 1901cb93a386Sopenharmony_ci SkScalarAsStringType asType = dumpAsHex ? kHex_SkScalarAsStringType : kDec_SkScalarAsStringType; 1902cb93a386Sopenharmony_ci Iter iter(*this, false); 1903cb93a386Sopenharmony_ci SkPoint pts[4]; 1904cb93a386Sopenharmony_ci Verb verb; 1905cb93a386Sopenharmony_ci 1906cb93a386Sopenharmony_ci SkString builder; 1907cb93a386Sopenharmony_ci char const * const gFillTypeStrs[] = { 1908cb93a386Sopenharmony_ci "Winding", 1909cb93a386Sopenharmony_ci "EvenOdd", 1910cb93a386Sopenharmony_ci "InverseWinding", 1911cb93a386Sopenharmony_ci "InverseEvenOdd", 1912cb93a386Sopenharmony_ci }; 1913cb93a386Sopenharmony_ci builder.printf("path.setFillType(SkPathFillType::k%s);\n", 1914cb93a386Sopenharmony_ci gFillTypeStrs[(int) this->getFillType()]); 1915cb93a386Sopenharmony_ci while ((verb = iter.next(pts)) != kDone_Verb) { 1916cb93a386Sopenharmony_ci switch (verb) { 1917cb93a386Sopenharmony_ci case kMove_Verb: 1918cb93a386Sopenharmony_ci append_params(&builder, "path.moveTo", &pts[0], 1, asType); 1919cb93a386Sopenharmony_ci break; 1920cb93a386Sopenharmony_ci case kLine_Verb: 1921cb93a386Sopenharmony_ci append_params(&builder, "path.lineTo", &pts[1], 1, asType); 1922cb93a386Sopenharmony_ci break; 1923cb93a386Sopenharmony_ci case kQuad_Verb: 1924cb93a386Sopenharmony_ci append_params(&builder, "path.quadTo", &pts[1], 2, asType); 1925cb93a386Sopenharmony_ci break; 1926cb93a386Sopenharmony_ci case kConic_Verb: 1927cb93a386Sopenharmony_ci append_params(&builder, "path.conicTo", &pts[1], 2, asType, iter.conicWeight()); 1928cb93a386Sopenharmony_ci break; 1929cb93a386Sopenharmony_ci case kCubic_Verb: 1930cb93a386Sopenharmony_ci append_params(&builder, "path.cubicTo", &pts[1], 3, asType); 1931cb93a386Sopenharmony_ci break; 1932cb93a386Sopenharmony_ci case kClose_Verb: 1933cb93a386Sopenharmony_ci builder.append("path.close();\n"); 1934cb93a386Sopenharmony_ci break; 1935cb93a386Sopenharmony_ci default: 1936cb93a386Sopenharmony_ci SkDebugf(" path: UNKNOWN VERB %d, aborting dump...\n", verb); 1937cb93a386Sopenharmony_ci verb = kDone_Verb; // stop the loop 1938cb93a386Sopenharmony_ci break; 1939cb93a386Sopenharmony_ci } 1940cb93a386Sopenharmony_ci if (!wStream && builder.size()) { 1941cb93a386Sopenharmony_ci SkDebugf("%s", builder.c_str()); 1942cb93a386Sopenharmony_ci builder.reset(); 1943cb93a386Sopenharmony_ci } 1944cb93a386Sopenharmony_ci } 1945cb93a386Sopenharmony_ci if (wStream) { 1946cb93a386Sopenharmony_ci wStream->writeText(builder.c_str()); 1947cb93a386Sopenharmony_ci } 1948cb93a386Sopenharmony_ci} 1949cb93a386Sopenharmony_ci 1950cb93a386Sopenharmony_civoid SkPath::dump(std::string& desc, int depth) const { 1951cb93a386Sopenharmony_ci std::string split(depth, '\t'); 1952cb93a386Sopenharmony_ci desc += split + "\n SkPath:{ \n"; 1953cb93a386Sopenharmony_ci Iter iter(*this, false); 1954cb93a386Sopenharmony_ci SkPoint points[4]; 1955cb93a386Sopenharmony_ci Verb verb; 1956cb93a386Sopenharmony_ci 1957cb93a386Sopenharmony_ci SkString descSk; 1958cb93a386Sopenharmony_ci char const * const gFillTypeStrs[] = { 1959cb93a386Sopenharmony_ci "Winding", 1960cb93a386Sopenharmony_ci "EvenOdd", 1961cb93a386Sopenharmony_ci "InverseWinding", 1962cb93a386Sopenharmony_ci "InverseEvenOdd", 1963cb93a386Sopenharmony_ci }; 1964cb93a386Sopenharmony_ci descSk.printf("path.setFillType(SkPath::k%s_FillType);\n", gFillTypeStrs[(int) this->getFillType()]); 1965cb93a386Sopenharmony_ci while ((verb = iter.next(points)) != kDone_Verb) { 1966cb93a386Sopenharmony_ci switch (verb) { 1967cb93a386Sopenharmony_ci case kMove_Verb: 1968cb93a386Sopenharmony_ci append_params(&descSk, "path.moveTo", &points[0], 1, kDec_SkScalarAsStringType); 1969cb93a386Sopenharmony_ci break; 1970cb93a386Sopenharmony_ci case kLine_Verb: 1971cb93a386Sopenharmony_ci append_params(&descSk, "path.lineTo", &points[1], 1, kDec_SkScalarAsStringType); 1972cb93a386Sopenharmony_ci break; 1973cb93a386Sopenharmony_ci case kQuad_Verb: 1974cb93a386Sopenharmony_ci append_params(&descSk, "path.quadTo", &points[1], 2, kDec_SkScalarAsStringType); 1975cb93a386Sopenharmony_ci break; 1976cb93a386Sopenharmony_ci case kConic_Verb: 1977cb93a386Sopenharmony_ci append_params(&descSk, "path.conicTo", &points[1], 2, kDec_SkScalarAsStringType, iter.conicWeight()); 1978cb93a386Sopenharmony_ci break; 1979cb93a386Sopenharmony_ci case kCubic_Verb: 1980cb93a386Sopenharmony_ci append_params(&descSk, "path.cubicTo", &points[1], 3, kDec_SkScalarAsStringType); 1981cb93a386Sopenharmony_ci break; 1982cb93a386Sopenharmony_ci case kClose_Verb: 1983cb93a386Sopenharmony_ci descSk.append("path.close();\n"); 1984cb93a386Sopenharmony_ci break; 1985cb93a386Sopenharmony_ci default: 1986cb93a386Sopenharmony_ci break; 1987cb93a386Sopenharmony_ci } 1988cb93a386Sopenharmony_ci if (descSk.size()) { 1989cb93a386Sopenharmony_ci desc += split + std::string(descSk.c_str()); 1990cb93a386Sopenharmony_ci descSk.reset(); 1991cb93a386Sopenharmony_ci } 1992cb93a386Sopenharmony_ci } 1993cb93a386Sopenharmony_ci desc += split + "}\n"; 1994cb93a386Sopenharmony_ci} 1995cb93a386Sopenharmony_ci 1996cb93a386Sopenharmony_civoid SkPath::dumpArrays(SkWStream* wStream, bool dumpAsHex) const { 1997cb93a386Sopenharmony_ci SkString builder; 1998cb93a386Sopenharmony_ci 1999cb93a386Sopenharmony_ci auto bool_str = [](bool v) { return v ? "true" : "false"; }; 2000cb93a386Sopenharmony_ci 2001cb93a386Sopenharmony_ci builder.appendf("// fBoundsIsDirty = %s\n", bool_str(fPathRef->fBoundsIsDirty)); 2002cb93a386Sopenharmony_ci builder.appendf("// fGenerationID = %d\n", fPathRef->fGenerationID); 2003cb93a386Sopenharmony_ci builder.appendf("// fSegmentMask = %d\n", fPathRef->fSegmentMask); 2004cb93a386Sopenharmony_ci builder.appendf("// fIsOval = %s\n", bool_str(fPathRef->fIsOval)); 2005cb93a386Sopenharmony_ci builder.appendf("// fIsRRect = %s\n", bool_str(fPathRef->fIsRRect)); 2006cb93a386Sopenharmony_ci 2007cb93a386Sopenharmony_ci auto append_scalar = [&](SkScalar v) { 2008cb93a386Sopenharmony_ci if (dumpAsHex) { 2009cb93a386Sopenharmony_ci builder.appendf("SkBits2Float(0x%08X) /* %g */", SkFloat2Bits(v), v); 2010cb93a386Sopenharmony_ci } else { 2011cb93a386Sopenharmony_ci builder.appendf("%g", v); 2012cb93a386Sopenharmony_ci } 2013cb93a386Sopenharmony_ci }; 2014cb93a386Sopenharmony_ci 2015cb93a386Sopenharmony_ci builder.append("const SkPoint path_points[] = {\n"); 2016cb93a386Sopenharmony_ci for (int i = 0; i < this->countPoints(); ++i) { 2017cb93a386Sopenharmony_ci SkPoint p = this->getPoint(i); 2018cb93a386Sopenharmony_ci builder.append(" { "); 2019cb93a386Sopenharmony_ci append_scalar(p.fX); 2020cb93a386Sopenharmony_ci builder.append(", "); 2021cb93a386Sopenharmony_ci append_scalar(p.fY); 2022cb93a386Sopenharmony_ci builder.append(" },\n"); 2023cb93a386Sopenharmony_ci } 2024cb93a386Sopenharmony_ci builder.append("};\n"); 2025cb93a386Sopenharmony_ci 2026cb93a386Sopenharmony_ci const char* gVerbStrs[] = { 2027cb93a386Sopenharmony_ci "Move", "Line", "Quad", "Conic", "Cubic", "Close" 2028cb93a386Sopenharmony_ci }; 2029cb93a386Sopenharmony_ci builder.append("const uint8_t path_verbs[] = {\n "); 2030cb93a386Sopenharmony_ci for (auto v = fPathRef->verbsBegin(); v != fPathRef->verbsEnd(); ++v) { 2031cb93a386Sopenharmony_ci builder.appendf("(uint8_t)SkPathVerb::k%s, ", gVerbStrs[*v]); 2032cb93a386Sopenharmony_ci } 2033cb93a386Sopenharmony_ci builder.append("\n};\n"); 2034cb93a386Sopenharmony_ci 2035cb93a386Sopenharmony_ci const int nConics = fPathRef->conicWeightsEnd() - fPathRef->conicWeights(); 2036cb93a386Sopenharmony_ci if (nConics) { 2037cb93a386Sopenharmony_ci builder.append("const SkScalar path_conics[] = {\n "); 2038cb93a386Sopenharmony_ci for (auto c = fPathRef->conicWeights(); c != fPathRef->conicWeightsEnd(); ++c) { 2039cb93a386Sopenharmony_ci append_scalar(*c); 2040cb93a386Sopenharmony_ci builder.append(", "); 2041cb93a386Sopenharmony_ci } 2042cb93a386Sopenharmony_ci builder.append("\n};\n"); 2043cb93a386Sopenharmony_ci } 2044cb93a386Sopenharmony_ci 2045cb93a386Sopenharmony_ci char const * const gFillTypeStrs[] = { 2046cb93a386Sopenharmony_ci "Winding", 2047cb93a386Sopenharmony_ci "EvenOdd", 2048cb93a386Sopenharmony_ci "InverseWinding", 2049cb93a386Sopenharmony_ci "InverseEvenOdd", 2050cb93a386Sopenharmony_ci }; 2051cb93a386Sopenharmony_ci 2052cb93a386Sopenharmony_ci builder.appendf("SkPath path = SkPath::Make(path_points, %d, path_verbs, %d, %s, %d,\n", 2053cb93a386Sopenharmony_ci this->countPoints(), this->countVerbs(), 2054cb93a386Sopenharmony_ci nConics ? "path_conics" : "nullptr", nConics); 2055cb93a386Sopenharmony_ci builder.appendf(" SkPathFillType::k%s, %s);\n", 2056cb93a386Sopenharmony_ci gFillTypeStrs[(int)this->getFillType()], 2057cb93a386Sopenharmony_ci bool_str(fIsVolatile)); 2058cb93a386Sopenharmony_ci 2059cb93a386Sopenharmony_ci if (wStream) { 2060cb93a386Sopenharmony_ci wStream->writeText(builder.c_str()); 2061cb93a386Sopenharmony_ci } else { 2062cb93a386Sopenharmony_ci SkDebugf("%s\n", builder.c_str()); 2063cb93a386Sopenharmony_ci } 2064cb93a386Sopenharmony_ci} 2065cb93a386Sopenharmony_ci 2066cb93a386Sopenharmony_cibool SkPath::isValidImpl() const { 2067cb93a386Sopenharmony_ci if ((fFillType & ~3) != 0) { 2068cb93a386Sopenharmony_ci return false; 2069cb93a386Sopenharmony_ci } 2070cb93a386Sopenharmony_ci 2071cb93a386Sopenharmony_ci#ifdef SK_DEBUG_PATH 2072cb93a386Sopenharmony_ci if (!fBoundsIsDirty) { 2073cb93a386Sopenharmony_ci SkRect bounds; 2074cb93a386Sopenharmony_ci 2075cb93a386Sopenharmony_ci bool isFinite = compute_pt_bounds(&bounds, *fPathRef.get()); 2076cb93a386Sopenharmony_ci if (SkToBool(fIsFinite) != isFinite) { 2077cb93a386Sopenharmony_ci return false; 2078cb93a386Sopenharmony_ci } 2079cb93a386Sopenharmony_ci 2080cb93a386Sopenharmony_ci if (fPathRef->countPoints() <= 1) { 2081cb93a386Sopenharmony_ci // if we're empty, fBounds may be empty but translated, so we can't 2082cb93a386Sopenharmony_ci // necessarily compare to bounds directly 2083cb93a386Sopenharmony_ci // try path.addOval(2, 2, 2, 2) which is empty, but the bounds will 2084cb93a386Sopenharmony_ci // be [2, 2, 2, 2] 2085cb93a386Sopenharmony_ci if (!bounds.isEmpty() || !fBounds.isEmpty()) { 2086cb93a386Sopenharmony_ci return false; 2087cb93a386Sopenharmony_ci } 2088cb93a386Sopenharmony_ci } else { 2089cb93a386Sopenharmony_ci if (bounds.isEmpty()) { 2090cb93a386Sopenharmony_ci if (!fBounds.isEmpty()) { 2091cb93a386Sopenharmony_ci return false; 2092cb93a386Sopenharmony_ci } 2093cb93a386Sopenharmony_ci } else { 2094cb93a386Sopenharmony_ci if (!fBounds.isEmpty()) { 2095cb93a386Sopenharmony_ci if (!fBounds.contains(bounds)) { 2096cb93a386Sopenharmony_ci return false; 2097cb93a386Sopenharmony_ci } 2098cb93a386Sopenharmony_ci } 2099cb93a386Sopenharmony_ci } 2100cb93a386Sopenharmony_ci } 2101cb93a386Sopenharmony_ci } 2102cb93a386Sopenharmony_ci#endif // SK_DEBUG_PATH 2103cb93a386Sopenharmony_ci return true; 2104cb93a386Sopenharmony_ci} 2105cb93a386Sopenharmony_ci 2106cb93a386Sopenharmony_ci/////////////////////////////////////////////////////////////////////////////// 2107cb93a386Sopenharmony_ci 2108cb93a386Sopenharmony_cistatic int sign(SkScalar x) { return x < 0; } 2109cb93a386Sopenharmony_ci#define kValueNeverReturnedBySign 2 2110cb93a386Sopenharmony_ci 2111cb93a386Sopenharmony_cienum DirChange { 2112cb93a386Sopenharmony_ci kUnknown_DirChange, 2113cb93a386Sopenharmony_ci kLeft_DirChange, 2114cb93a386Sopenharmony_ci kRight_DirChange, 2115cb93a386Sopenharmony_ci kStraight_DirChange, 2116cb93a386Sopenharmony_ci kBackwards_DirChange, // if double back, allow simple lines to be convex 2117cb93a386Sopenharmony_ci kInvalid_DirChange 2118cb93a386Sopenharmony_ci}; 2119cb93a386Sopenharmony_ci 2120cb93a386Sopenharmony_ci// only valid for a single contour 2121cb93a386Sopenharmony_cistruct Convexicator { 2122cb93a386Sopenharmony_ci 2123cb93a386Sopenharmony_ci /** The direction returned is only valid if the path is determined convex */ 2124cb93a386Sopenharmony_ci SkPathFirstDirection getFirstDirection() const { return fFirstDirection; } 2125cb93a386Sopenharmony_ci 2126cb93a386Sopenharmony_ci void setMovePt(const SkPoint& pt) { 2127cb93a386Sopenharmony_ci fFirstPt = fLastPt = pt; 2128cb93a386Sopenharmony_ci fExpectedDir = kInvalid_DirChange; 2129cb93a386Sopenharmony_ci } 2130cb93a386Sopenharmony_ci 2131cb93a386Sopenharmony_ci bool addPt(const SkPoint& pt) { 2132cb93a386Sopenharmony_ci if (fLastPt == pt) { 2133cb93a386Sopenharmony_ci return true; 2134cb93a386Sopenharmony_ci } 2135cb93a386Sopenharmony_ci // should only be true for first non-zero vector after setMovePt was called. 2136cb93a386Sopenharmony_ci if (fFirstPt == fLastPt && fExpectedDir == kInvalid_DirChange) { 2137cb93a386Sopenharmony_ci fLastVec = pt - fLastPt; 2138cb93a386Sopenharmony_ci fFirstVec = fLastVec; 2139cb93a386Sopenharmony_ci } else if (!this->addVec(pt - fLastPt)) { 2140cb93a386Sopenharmony_ci return false; 2141cb93a386Sopenharmony_ci } 2142cb93a386Sopenharmony_ci fLastPt = pt; 2143cb93a386Sopenharmony_ci return true; 2144cb93a386Sopenharmony_ci } 2145cb93a386Sopenharmony_ci 2146cb93a386Sopenharmony_ci static SkPathConvexity BySign(const SkPoint points[], int count) { 2147cb93a386Sopenharmony_ci if (count <= 3) { 2148cb93a386Sopenharmony_ci // point, line, or triangle are always convex 2149cb93a386Sopenharmony_ci return SkPathConvexity::kConvex; 2150cb93a386Sopenharmony_ci } 2151cb93a386Sopenharmony_ci 2152cb93a386Sopenharmony_ci const SkPoint* last = points + count; 2153cb93a386Sopenharmony_ci SkPoint currPt = *points++; 2154cb93a386Sopenharmony_ci SkPoint firstPt = currPt; 2155cb93a386Sopenharmony_ci int dxes = 0; 2156cb93a386Sopenharmony_ci int dyes = 0; 2157cb93a386Sopenharmony_ci int lastSx = kValueNeverReturnedBySign; 2158cb93a386Sopenharmony_ci int lastSy = kValueNeverReturnedBySign; 2159cb93a386Sopenharmony_ci for (int outerLoop = 0; outerLoop < 2; ++outerLoop ) { 2160cb93a386Sopenharmony_ci while (points != last) { 2161cb93a386Sopenharmony_ci SkVector vec = *points - currPt; 2162cb93a386Sopenharmony_ci if (!vec.isZero()) { 2163cb93a386Sopenharmony_ci // give up if vector construction failed 2164cb93a386Sopenharmony_ci if (!vec.isFinite()) { 2165cb93a386Sopenharmony_ci return SkPathConvexity::kUnknown; 2166cb93a386Sopenharmony_ci } 2167cb93a386Sopenharmony_ci int sx = sign(vec.fX); 2168cb93a386Sopenharmony_ci int sy = sign(vec.fY); 2169cb93a386Sopenharmony_ci dxes += (sx != lastSx); 2170cb93a386Sopenharmony_ci dyes += (sy != lastSy); 2171cb93a386Sopenharmony_ci if (dxes > 3 || dyes > 3) { 2172cb93a386Sopenharmony_ci return SkPathConvexity::kConcave; 2173cb93a386Sopenharmony_ci } 2174cb93a386Sopenharmony_ci lastSx = sx; 2175cb93a386Sopenharmony_ci lastSy = sy; 2176cb93a386Sopenharmony_ci } 2177cb93a386Sopenharmony_ci currPt = *points++; 2178cb93a386Sopenharmony_ci if (outerLoop) { 2179cb93a386Sopenharmony_ci break; 2180cb93a386Sopenharmony_ci } 2181cb93a386Sopenharmony_ci } 2182cb93a386Sopenharmony_ci points = &firstPt; 2183cb93a386Sopenharmony_ci } 2184cb93a386Sopenharmony_ci return SkPathConvexity::kConvex; // that is, it may be convex, don't know yet 2185cb93a386Sopenharmony_ci } 2186cb93a386Sopenharmony_ci 2187cb93a386Sopenharmony_ci bool close() { 2188cb93a386Sopenharmony_ci // If this was an explicit close, there was already a lineTo to fFirstPoint, so this 2189cb93a386Sopenharmony_ci // addPt() is a no-op. Otherwise, the addPt implicitly closes the contour. In either case, 2190cb93a386Sopenharmony_ci // we have to check the direction change along the first vector in case it is concave. 2191cb93a386Sopenharmony_ci return this->addPt(fFirstPt) && this->addVec(fFirstVec); 2192cb93a386Sopenharmony_ci } 2193cb93a386Sopenharmony_ci 2194cb93a386Sopenharmony_ci bool isFinite() const { 2195cb93a386Sopenharmony_ci return fIsFinite; 2196cb93a386Sopenharmony_ci } 2197cb93a386Sopenharmony_ci 2198cb93a386Sopenharmony_ci int reversals() const { 2199cb93a386Sopenharmony_ci return fReversals; 2200cb93a386Sopenharmony_ci } 2201cb93a386Sopenharmony_ci 2202cb93a386Sopenharmony_ciprivate: 2203cb93a386Sopenharmony_ci DirChange directionChange(const SkVector& curVec) { 2204cb93a386Sopenharmony_ci SkScalar cross = SkPoint::CrossProduct(fLastVec, curVec); 2205cb93a386Sopenharmony_ci if (!SkScalarIsFinite(cross)) { 2206cb93a386Sopenharmony_ci return kUnknown_DirChange; 2207cb93a386Sopenharmony_ci } 2208cb93a386Sopenharmony_ci if (cross == 0) { 2209cb93a386Sopenharmony_ci return fLastVec.dot(curVec) < 0 ? kBackwards_DirChange : kStraight_DirChange; 2210cb93a386Sopenharmony_ci } 2211cb93a386Sopenharmony_ci return 1 == SkScalarSignAsInt(cross) ? kRight_DirChange : kLeft_DirChange; 2212cb93a386Sopenharmony_ci } 2213cb93a386Sopenharmony_ci 2214cb93a386Sopenharmony_ci bool addVec(const SkVector& curVec) { 2215cb93a386Sopenharmony_ci DirChange dir = this->directionChange(curVec); 2216cb93a386Sopenharmony_ci switch (dir) { 2217cb93a386Sopenharmony_ci case kLeft_DirChange: // fall through 2218cb93a386Sopenharmony_ci case kRight_DirChange: 2219cb93a386Sopenharmony_ci if (kInvalid_DirChange == fExpectedDir) { 2220cb93a386Sopenharmony_ci fExpectedDir = dir; 2221cb93a386Sopenharmony_ci fFirstDirection = (kRight_DirChange == dir) ? SkPathFirstDirection::kCW 2222cb93a386Sopenharmony_ci : SkPathFirstDirection::kCCW; 2223cb93a386Sopenharmony_ci } else if (dir != fExpectedDir) { 2224cb93a386Sopenharmony_ci fFirstDirection = SkPathFirstDirection::kUnknown; 2225cb93a386Sopenharmony_ci return false; 2226cb93a386Sopenharmony_ci } 2227cb93a386Sopenharmony_ci fLastVec = curVec; 2228cb93a386Sopenharmony_ci break; 2229cb93a386Sopenharmony_ci case kStraight_DirChange: 2230cb93a386Sopenharmony_ci break; 2231cb93a386Sopenharmony_ci case kBackwards_DirChange: 2232cb93a386Sopenharmony_ci // allow path to reverse direction twice 2233cb93a386Sopenharmony_ci // Given path.moveTo(0, 0); path.lineTo(1, 1); 2234cb93a386Sopenharmony_ci // - 1st reversal: direction change formed by line (0,0 1,1), line (1,1 0,0) 2235cb93a386Sopenharmony_ci // - 2nd reversal: direction change formed by line (1,1 0,0), line (0,0 1,1) 2236cb93a386Sopenharmony_ci fLastVec = curVec; 2237cb93a386Sopenharmony_ci return ++fReversals < 3; 2238cb93a386Sopenharmony_ci case kUnknown_DirChange: 2239cb93a386Sopenharmony_ci return (fIsFinite = false); 2240cb93a386Sopenharmony_ci case kInvalid_DirChange: 2241cb93a386Sopenharmony_ci SK_ABORT("Use of invalid direction change flag"); 2242cb93a386Sopenharmony_ci break; 2243cb93a386Sopenharmony_ci } 2244cb93a386Sopenharmony_ci return true; 2245cb93a386Sopenharmony_ci } 2246cb93a386Sopenharmony_ci 2247cb93a386Sopenharmony_ci SkPoint fFirstPt {0, 0}; // The first point of the contour, e.g. moveTo(x,y) 2248cb93a386Sopenharmony_ci SkVector fFirstVec {0, 0}; // The direction leaving fFirstPt to the next vertex 2249cb93a386Sopenharmony_ci 2250cb93a386Sopenharmony_ci SkPoint fLastPt {0, 0}; // The last point passed to addPt() 2251cb93a386Sopenharmony_ci SkVector fLastVec {0, 0}; // The direction that brought the path to fLastPt 2252cb93a386Sopenharmony_ci 2253cb93a386Sopenharmony_ci DirChange fExpectedDir { kInvalid_DirChange }; 2254cb93a386Sopenharmony_ci SkPathFirstDirection fFirstDirection { SkPathFirstDirection::kUnknown }; 2255cb93a386Sopenharmony_ci int fReversals { 0 }; 2256cb93a386Sopenharmony_ci bool fIsFinite { true }; 2257cb93a386Sopenharmony_ci}; 2258cb93a386Sopenharmony_ci 2259cb93a386Sopenharmony_ciSkPathConvexity SkPath::computeConvexity() const { 2260cb93a386Sopenharmony_ci auto setComputedConvexity = [=](SkPathConvexity convexity){ 2261cb93a386Sopenharmony_ci SkASSERT(SkPathConvexity::kUnknown != convexity); 2262cb93a386Sopenharmony_ci this->setConvexity(convexity); 2263cb93a386Sopenharmony_ci return convexity; 2264cb93a386Sopenharmony_ci }; 2265cb93a386Sopenharmony_ci 2266cb93a386Sopenharmony_ci auto setFail = [=](){ 2267cb93a386Sopenharmony_ci return setComputedConvexity(SkPathConvexity::kConcave); 2268cb93a386Sopenharmony_ci }; 2269cb93a386Sopenharmony_ci 2270cb93a386Sopenharmony_ci if (!this->isFinite()) { 2271cb93a386Sopenharmony_ci return setFail(); 2272cb93a386Sopenharmony_ci } 2273cb93a386Sopenharmony_ci 2274cb93a386Sopenharmony_ci // pointCount potentially includes a block of leading moveTos and trailing moveTos. Convexity 2275cb93a386Sopenharmony_ci // only cares about the last of the initial moveTos and the verbs before the final moveTos. 2276cb93a386Sopenharmony_ci int pointCount = this->countPoints(); 2277cb93a386Sopenharmony_ci int skipCount = SkPathPriv::LeadingMoveToCount(*this) - 1; 2278cb93a386Sopenharmony_ci 2279cb93a386Sopenharmony_ci if (fLastMoveToIndex >= 0) { 2280cb93a386Sopenharmony_ci if (fLastMoveToIndex == pointCount - 1) { 2281cb93a386Sopenharmony_ci // Find the last real verb that affects convexity 2282cb93a386Sopenharmony_ci auto verbs = fPathRef->verbsEnd() - 1; 2283cb93a386Sopenharmony_ci while(verbs > fPathRef->verbsBegin() && *verbs == Verb::kMove_Verb) { 2284cb93a386Sopenharmony_ci verbs--; 2285cb93a386Sopenharmony_ci pointCount--; 2286cb93a386Sopenharmony_ci } 2287cb93a386Sopenharmony_ci } else if (fLastMoveToIndex != skipCount) { 2288cb93a386Sopenharmony_ci // There's an additional moveTo between two blocks of other verbs, so the path must have 2289cb93a386Sopenharmony_ci // more than one contour and cannot be convex. 2290cb93a386Sopenharmony_ci return setComputedConvexity(SkPathConvexity::kConcave); 2291cb93a386Sopenharmony_ci } // else no trailing or intermediate moveTos to worry about 2292cb93a386Sopenharmony_ci } 2293cb93a386Sopenharmony_ci const SkPoint* points = fPathRef->points(); 2294cb93a386Sopenharmony_ci if (skipCount > 0) { 2295cb93a386Sopenharmony_ci points += skipCount; 2296cb93a386Sopenharmony_ci pointCount -= skipCount; 2297cb93a386Sopenharmony_ci } 2298cb93a386Sopenharmony_ci 2299cb93a386Sopenharmony_ci // Check to see if path changes direction more than three times as quick concave test 2300cb93a386Sopenharmony_ci SkPathConvexity convexity = Convexicator::BySign(points, pointCount); 2301cb93a386Sopenharmony_ci if (SkPathConvexity::kConvex != convexity) { 2302cb93a386Sopenharmony_ci return setComputedConvexity(SkPathConvexity::kConcave); 2303cb93a386Sopenharmony_ci } 2304cb93a386Sopenharmony_ci 2305cb93a386Sopenharmony_ci int contourCount = 0; 2306cb93a386Sopenharmony_ci bool needsClose = false; 2307cb93a386Sopenharmony_ci Convexicator state; 2308cb93a386Sopenharmony_ci 2309cb93a386Sopenharmony_ci for (auto [verb, pts, wt] : SkPathPriv::Iterate(*this)) { 2310cb93a386Sopenharmony_ci // Looking for the last moveTo before non-move verbs start 2311cb93a386Sopenharmony_ci if (contourCount == 0) { 2312cb93a386Sopenharmony_ci if (verb == SkPathVerb::kMove) { 2313cb93a386Sopenharmony_ci state.setMovePt(pts[0]); 2314cb93a386Sopenharmony_ci } else { 2315cb93a386Sopenharmony_ci // Starting the actual contour, fall through to c=1 to add the points 2316cb93a386Sopenharmony_ci contourCount++; 2317cb93a386Sopenharmony_ci needsClose = true; 2318cb93a386Sopenharmony_ci } 2319cb93a386Sopenharmony_ci } 2320cb93a386Sopenharmony_ci // Accumulating points into the Convexicator until we hit a close or another move 2321cb93a386Sopenharmony_ci if (contourCount == 1) { 2322cb93a386Sopenharmony_ci if (verb == SkPathVerb::kClose || verb == SkPathVerb::kMove) { 2323cb93a386Sopenharmony_ci if (!state.close()) { 2324cb93a386Sopenharmony_ci return setFail(); 2325cb93a386Sopenharmony_ci } 2326cb93a386Sopenharmony_ci needsClose = false; 2327cb93a386Sopenharmony_ci contourCount++; 2328cb93a386Sopenharmony_ci } else { 2329cb93a386Sopenharmony_ci // lines add 1 point, cubics add 3, conics and quads add 2 2330cb93a386Sopenharmony_ci int count = SkPathPriv::PtsInVerb((unsigned) verb); 2331cb93a386Sopenharmony_ci SkASSERT(count > 0); 2332cb93a386Sopenharmony_ci for (int i = 1; i <= count; ++i) { 2333cb93a386Sopenharmony_ci if (!state.addPt(pts[i])) { 2334cb93a386Sopenharmony_ci return setFail(); 2335cb93a386Sopenharmony_ci } 2336cb93a386Sopenharmony_ci } 2337cb93a386Sopenharmony_ci } 2338cb93a386Sopenharmony_ci } else { 2339cb93a386Sopenharmony_ci // The first contour has closed and anything other than spurious trailing moves means 2340cb93a386Sopenharmony_ci // there's multiple contours and the path can't be convex 2341cb93a386Sopenharmony_ci if (verb != SkPathVerb::kMove) { 2342cb93a386Sopenharmony_ci return setFail(); 2343cb93a386Sopenharmony_ci } 2344cb93a386Sopenharmony_ci } 2345cb93a386Sopenharmony_ci } 2346cb93a386Sopenharmony_ci 2347cb93a386Sopenharmony_ci // If the path isn't explicitly closed do so implicitly 2348cb93a386Sopenharmony_ci if (needsClose && !state.close()) { 2349cb93a386Sopenharmony_ci return setFail(); 2350cb93a386Sopenharmony_ci } 2351cb93a386Sopenharmony_ci 2352cb93a386Sopenharmony_ci if (this->getFirstDirection() == SkPathFirstDirection::kUnknown) { 2353cb93a386Sopenharmony_ci if (state.getFirstDirection() == SkPathFirstDirection::kUnknown 2354cb93a386Sopenharmony_ci && !this->getBounds().isEmpty()) { 2355cb93a386Sopenharmony_ci return setComputedConvexity(state.reversals() < 3 ? 2356cb93a386Sopenharmony_ci SkPathConvexity::kConvex : SkPathConvexity::kConcave); 2357cb93a386Sopenharmony_ci } 2358cb93a386Sopenharmony_ci this->setFirstDirection(state.getFirstDirection()); 2359cb93a386Sopenharmony_ci } 2360cb93a386Sopenharmony_ci return setComputedConvexity(SkPathConvexity::kConvex); 2361cb93a386Sopenharmony_ci} 2362cb93a386Sopenharmony_ci 2363cb93a386Sopenharmony_ci/////////////////////////////////////////////////////////////////////////////// 2364cb93a386Sopenharmony_ci 2365cb93a386Sopenharmony_ciclass ContourIter { 2366cb93a386Sopenharmony_cipublic: 2367cb93a386Sopenharmony_ci ContourIter(const SkPathRef& pathRef); 2368cb93a386Sopenharmony_ci 2369cb93a386Sopenharmony_ci bool done() const { return fDone; } 2370cb93a386Sopenharmony_ci // if !done() then these may be called 2371cb93a386Sopenharmony_ci int count() const { return fCurrPtCount; } 2372cb93a386Sopenharmony_ci const SkPoint* pts() const { return fCurrPt; } 2373cb93a386Sopenharmony_ci void next(); 2374cb93a386Sopenharmony_ci 2375cb93a386Sopenharmony_ciprivate: 2376cb93a386Sopenharmony_ci int fCurrPtCount; 2377cb93a386Sopenharmony_ci const SkPoint* fCurrPt; 2378cb93a386Sopenharmony_ci const uint8_t* fCurrVerb; 2379cb93a386Sopenharmony_ci const uint8_t* fStopVerbs; 2380cb93a386Sopenharmony_ci const SkScalar* fCurrConicWeight; 2381cb93a386Sopenharmony_ci bool fDone; 2382cb93a386Sopenharmony_ci SkDEBUGCODE(int fContourCounter;) 2383cb93a386Sopenharmony_ci}; 2384cb93a386Sopenharmony_ci 2385cb93a386Sopenharmony_ciContourIter::ContourIter(const SkPathRef& pathRef) { 2386cb93a386Sopenharmony_ci fStopVerbs = pathRef.verbsEnd(); 2387cb93a386Sopenharmony_ci fDone = false; 2388cb93a386Sopenharmony_ci fCurrPt = pathRef.points(); 2389cb93a386Sopenharmony_ci fCurrVerb = pathRef.verbsBegin(); 2390cb93a386Sopenharmony_ci fCurrConicWeight = pathRef.conicWeights(); 2391cb93a386Sopenharmony_ci fCurrPtCount = 0; 2392cb93a386Sopenharmony_ci SkDEBUGCODE(fContourCounter = 0;) 2393cb93a386Sopenharmony_ci this->next(); 2394cb93a386Sopenharmony_ci} 2395cb93a386Sopenharmony_ci 2396cb93a386Sopenharmony_civoid ContourIter::next() { 2397cb93a386Sopenharmony_ci if (fCurrVerb >= fStopVerbs) { 2398cb93a386Sopenharmony_ci fDone = true; 2399cb93a386Sopenharmony_ci } 2400cb93a386Sopenharmony_ci if (fDone) { 2401cb93a386Sopenharmony_ci return; 2402cb93a386Sopenharmony_ci } 2403cb93a386Sopenharmony_ci 2404cb93a386Sopenharmony_ci // skip pts of prev contour 2405cb93a386Sopenharmony_ci fCurrPt += fCurrPtCount; 2406cb93a386Sopenharmony_ci 2407cb93a386Sopenharmony_ci SkASSERT(SkPath::kMove_Verb == fCurrVerb[0]); 2408cb93a386Sopenharmony_ci int ptCount = 1; // moveTo 2409cb93a386Sopenharmony_ci const uint8_t* verbs = fCurrVerb; 2410cb93a386Sopenharmony_ci 2411cb93a386Sopenharmony_ci for (verbs++; verbs < fStopVerbs; verbs++) { 2412cb93a386Sopenharmony_ci switch (*verbs) { 2413cb93a386Sopenharmony_ci case SkPath::kMove_Verb: 2414cb93a386Sopenharmony_ci goto CONTOUR_END; 2415cb93a386Sopenharmony_ci case SkPath::kLine_Verb: 2416cb93a386Sopenharmony_ci ptCount += 1; 2417cb93a386Sopenharmony_ci break; 2418cb93a386Sopenharmony_ci case SkPath::kConic_Verb: 2419cb93a386Sopenharmony_ci fCurrConicWeight += 1; 2420cb93a386Sopenharmony_ci [[fallthrough]]; 2421cb93a386Sopenharmony_ci case SkPath::kQuad_Verb: 2422cb93a386Sopenharmony_ci ptCount += 2; 2423cb93a386Sopenharmony_ci break; 2424cb93a386Sopenharmony_ci case SkPath::kCubic_Verb: 2425cb93a386Sopenharmony_ci ptCount += 3; 2426cb93a386Sopenharmony_ci break; 2427cb93a386Sopenharmony_ci case SkPath::kClose_Verb: 2428cb93a386Sopenharmony_ci break; 2429cb93a386Sopenharmony_ci default: 2430cb93a386Sopenharmony_ci SkDEBUGFAIL("unexpected verb"); 2431cb93a386Sopenharmony_ci break; 2432cb93a386Sopenharmony_ci } 2433cb93a386Sopenharmony_ci } 2434cb93a386Sopenharmony_ciCONTOUR_END: 2435cb93a386Sopenharmony_ci fCurrPtCount = ptCount; 2436cb93a386Sopenharmony_ci fCurrVerb = verbs; 2437cb93a386Sopenharmony_ci SkDEBUGCODE(++fContourCounter;) 2438cb93a386Sopenharmony_ci} 2439cb93a386Sopenharmony_ci 2440cb93a386Sopenharmony_ci// returns cross product of (p1 - p0) and (p2 - p0) 2441cb93a386Sopenharmony_cistatic SkScalar cross_prod(const SkPoint& p0, const SkPoint& p1, const SkPoint& p2) { 2442cb93a386Sopenharmony_ci SkScalar cross = SkPoint::CrossProduct(p1 - p0, p2 - p0); 2443cb93a386Sopenharmony_ci // We may get 0 when the above subtracts underflow. We expect this to be 2444cb93a386Sopenharmony_ci // very rare and lazily promote to double. 2445cb93a386Sopenharmony_ci if (0 == cross) { 2446cb93a386Sopenharmony_ci double p0x = SkScalarToDouble(p0.fX); 2447cb93a386Sopenharmony_ci double p0y = SkScalarToDouble(p0.fY); 2448cb93a386Sopenharmony_ci 2449cb93a386Sopenharmony_ci double p1x = SkScalarToDouble(p1.fX); 2450cb93a386Sopenharmony_ci double p1y = SkScalarToDouble(p1.fY); 2451cb93a386Sopenharmony_ci 2452cb93a386Sopenharmony_ci double p2x = SkScalarToDouble(p2.fX); 2453cb93a386Sopenharmony_ci double p2y = SkScalarToDouble(p2.fY); 2454cb93a386Sopenharmony_ci 2455cb93a386Sopenharmony_ci cross = SkDoubleToScalar((p1x - p0x) * (p2y - p0y) - 2456cb93a386Sopenharmony_ci (p1y - p0y) * (p2x - p0x)); 2457cb93a386Sopenharmony_ci 2458cb93a386Sopenharmony_ci } 2459cb93a386Sopenharmony_ci return cross; 2460cb93a386Sopenharmony_ci} 2461cb93a386Sopenharmony_ci 2462cb93a386Sopenharmony_ci// Returns the first pt with the maximum Y coordinate 2463cb93a386Sopenharmony_cistatic int find_max_y(const SkPoint pts[], int count) { 2464cb93a386Sopenharmony_ci SkASSERT(count > 0); 2465cb93a386Sopenharmony_ci SkScalar max = pts[0].fY; 2466cb93a386Sopenharmony_ci int firstIndex = 0; 2467cb93a386Sopenharmony_ci for (int i = 1; i < count; ++i) { 2468cb93a386Sopenharmony_ci SkScalar y = pts[i].fY; 2469cb93a386Sopenharmony_ci if (y > max) { 2470cb93a386Sopenharmony_ci max = y; 2471cb93a386Sopenharmony_ci firstIndex = i; 2472cb93a386Sopenharmony_ci } 2473cb93a386Sopenharmony_ci } 2474cb93a386Sopenharmony_ci return firstIndex; 2475cb93a386Sopenharmony_ci} 2476cb93a386Sopenharmony_ci 2477cb93a386Sopenharmony_cistatic int find_diff_pt(const SkPoint pts[], int index, int n, int inc) { 2478cb93a386Sopenharmony_ci int i = index; 2479cb93a386Sopenharmony_ci for (;;) { 2480cb93a386Sopenharmony_ci i = (i + inc) % n; 2481cb93a386Sopenharmony_ci if (i == index) { // we wrapped around, so abort 2482cb93a386Sopenharmony_ci break; 2483cb93a386Sopenharmony_ci } 2484cb93a386Sopenharmony_ci if (pts[index] != pts[i]) { // found a different point, success! 2485cb93a386Sopenharmony_ci break; 2486cb93a386Sopenharmony_ci } 2487cb93a386Sopenharmony_ci } 2488cb93a386Sopenharmony_ci return i; 2489cb93a386Sopenharmony_ci} 2490cb93a386Sopenharmony_ci 2491cb93a386Sopenharmony_ci/** 2492cb93a386Sopenharmony_ci * Starting at index, and moving forward (incrementing), find the xmin and 2493cb93a386Sopenharmony_ci * xmax of the contiguous points that have the same Y. 2494cb93a386Sopenharmony_ci */ 2495cb93a386Sopenharmony_cistatic int find_min_max_x_at_y(const SkPoint pts[], int index, int n, 2496cb93a386Sopenharmony_ci int* maxIndexPtr) { 2497cb93a386Sopenharmony_ci const SkScalar y = pts[index].fY; 2498cb93a386Sopenharmony_ci SkScalar min = pts[index].fX; 2499cb93a386Sopenharmony_ci SkScalar max = min; 2500cb93a386Sopenharmony_ci int minIndex = index; 2501cb93a386Sopenharmony_ci int maxIndex = index; 2502cb93a386Sopenharmony_ci for (int i = index + 1; i < n; ++i) { 2503cb93a386Sopenharmony_ci if (pts[i].fY != y) { 2504cb93a386Sopenharmony_ci break; 2505cb93a386Sopenharmony_ci } 2506cb93a386Sopenharmony_ci SkScalar x = pts[i].fX; 2507cb93a386Sopenharmony_ci if (x < min) { 2508cb93a386Sopenharmony_ci min = x; 2509cb93a386Sopenharmony_ci minIndex = i; 2510cb93a386Sopenharmony_ci } else if (x > max) { 2511cb93a386Sopenharmony_ci max = x; 2512cb93a386Sopenharmony_ci maxIndex = i; 2513cb93a386Sopenharmony_ci } 2514cb93a386Sopenharmony_ci } 2515cb93a386Sopenharmony_ci *maxIndexPtr = maxIndex; 2516cb93a386Sopenharmony_ci return minIndex; 2517cb93a386Sopenharmony_ci} 2518cb93a386Sopenharmony_ci 2519cb93a386Sopenharmony_cistatic SkPathFirstDirection crossToDir(SkScalar cross) { 2520cb93a386Sopenharmony_ci return cross > 0 ? SkPathFirstDirection::kCW : SkPathFirstDirection::kCCW; 2521cb93a386Sopenharmony_ci} 2522cb93a386Sopenharmony_ci 2523cb93a386Sopenharmony_ci/* 2524cb93a386Sopenharmony_ci * We loop through all contours, and keep the computed cross-product of the 2525cb93a386Sopenharmony_ci * contour that contained the global y-max. If we just look at the first 2526cb93a386Sopenharmony_ci * contour, we may find one that is wound the opposite way (correctly) since 2527cb93a386Sopenharmony_ci * it is the interior of a hole (e.g. 'o'). Thus we must find the contour 2528cb93a386Sopenharmony_ci * that is outer most (or at least has the global y-max) before we can consider 2529cb93a386Sopenharmony_ci * its cross product. 2530cb93a386Sopenharmony_ci */ 2531cb93a386Sopenharmony_ciSkPathFirstDirection SkPathPriv::ComputeFirstDirection(const SkPath& path) { 2532cb93a386Sopenharmony_ci auto d = path.getFirstDirection(); 2533cb93a386Sopenharmony_ci if (d != SkPathFirstDirection::kUnknown) { 2534cb93a386Sopenharmony_ci return d; 2535cb93a386Sopenharmony_ci } 2536cb93a386Sopenharmony_ci 2537cb93a386Sopenharmony_ci // We don't want to pay the cost for computing convexity if it is unknown, 2538cb93a386Sopenharmony_ci // so we call getConvexityOrUnknown() instead of isConvex(). 2539cb93a386Sopenharmony_ci if (path.getConvexityOrUnknown() == SkPathConvexity::kConvex) { 2540cb93a386Sopenharmony_ci SkASSERT(d == SkPathFirstDirection::kUnknown); 2541cb93a386Sopenharmony_ci return d; 2542cb93a386Sopenharmony_ci } 2543cb93a386Sopenharmony_ci 2544cb93a386Sopenharmony_ci ContourIter iter(*path.fPathRef); 2545cb93a386Sopenharmony_ci 2546cb93a386Sopenharmony_ci // initialize with our logical y-min 2547cb93a386Sopenharmony_ci SkScalar ymax = path.getBounds().fTop; 2548cb93a386Sopenharmony_ci SkScalar ymaxCross = 0; 2549cb93a386Sopenharmony_ci 2550cb93a386Sopenharmony_ci for (; !iter.done(); iter.next()) { 2551cb93a386Sopenharmony_ci int n = iter.count(); 2552cb93a386Sopenharmony_ci if (n < 3) { 2553cb93a386Sopenharmony_ci continue; 2554cb93a386Sopenharmony_ci } 2555cb93a386Sopenharmony_ci 2556cb93a386Sopenharmony_ci const SkPoint* pts = iter.pts(); 2557cb93a386Sopenharmony_ci SkScalar cross = 0; 2558cb93a386Sopenharmony_ci int index = find_max_y(pts, n); 2559cb93a386Sopenharmony_ci if (pts[index].fY < ymax) { 2560cb93a386Sopenharmony_ci continue; 2561cb93a386Sopenharmony_ci } 2562cb93a386Sopenharmony_ci 2563cb93a386Sopenharmony_ci // If there is more than 1 distinct point at the y-max, we take the 2564cb93a386Sopenharmony_ci // x-min and x-max of them and just subtract to compute the dir. 2565cb93a386Sopenharmony_ci if (pts[(index + 1) % n].fY == pts[index].fY) { 2566cb93a386Sopenharmony_ci int maxIndex; 2567cb93a386Sopenharmony_ci int minIndex = find_min_max_x_at_y(pts, index, n, &maxIndex); 2568cb93a386Sopenharmony_ci if (minIndex == maxIndex) { 2569cb93a386Sopenharmony_ci goto TRY_CROSSPROD; 2570cb93a386Sopenharmony_ci } 2571cb93a386Sopenharmony_ci SkASSERT(pts[minIndex].fY == pts[index].fY); 2572cb93a386Sopenharmony_ci SkASSERT(pts[maxIndex].fY == pts[index].fY); 2573cb93a386Sopenharmony_ci SkASSERT(pts[minIndex].fX <= pts[maxIndex].fX); 2574cb93a386Sopenharmony_ci // we just subtract the indices, and let that auto-convert to 2575cb93a386Sopenharmony_ci // SkScalar, since we just want - or + to signal the direction. 2576cb93a386Sopenharmony_ci cross = minIndex - maxIndex; 2577cb93a386Sopenharmony_ci } else { 2578cb93a386Sopenharmony_ci TRY_CROSSPROD: 2579cb93a386Sopenharmony_ci // Find a next and prev index to use for the cross-product test, 2580cb93a386Sopenharmony_ci // but we try to find pts that form non-zero vectors from pts[index] 2581cb93a386Sopenharmony_ci // 2582cb93a386Sopenharmony_ci // Its possible that we can't find two non-degenerate vectors, so 2583cb93a386Sopenharmony_ci // we have to guard our search (e.g. all the pts could be in the 2584cb93a386Sopenharmony_ci // same place). 2585cb93a386Sopenharmony_ci 2586cb93a386Sopenharmony_ci // we pass n - 1 instead of -1 so we don't foul up % operator by 2587cb93a386Sopenharmony_ci // passing it a negative LH argument. 2588cb93a386Sopenharmony_ci int prev = find_diff_pt(pts, index, n, n - 1); 2589cb93a386Sopenharmony_ci if (prev == index) { 2590cb93a386Sopenharmony_ci // completely degenerate, skip to next contour 2591cb93a386Sopenharmony_ci continue; 2592cb93a386Sopenharmony_ci } 2593cb93a386Sopenharmony_ci int next = find_diff_pt(pts, index, n, 1); 2594cb93a386Sopenharmony_ci SkASSERT(next != index); 2595cb93a386Sopenharmony_ci cross = cross_prod(pts[prev], pts[index], pts[next]); 2596cb93a386Sopenharmony_ci // if we get a zero and the points are horizontal, then we look at the spread in 2597cb93a386Sopenharmony_ci // x-direction. We really should continue to walk away from the degeneracy until 2598cb93a386Sopenharmony_ci // there is a divergence. 2599cb93a386Sopenharmony_ci if (0 == cross && pts[prev].fY == pts[index].fY && pts[next].fY == pts[index].fY) { 2600cb93a386Sopenharmony_ci // construct the subtract so we get the correct Direction below 2601cb93a386Sopenharmony_ci cross = pts[index].fX - pts[next].fX; 2602cb93a386Sopenharmony_ci } 2603cb93a386Sopenharmony_ci } 2604cb93a386Sopenharmony_ci 2605cb93a386Sopenharmony_ci if (cross) { 2606cb93a386Sopenharmony_ci // record our best guess so far 2607cb93a386Sopenharmony_ci ymax = pts[index].fY; 2608cb93a386Sopenharmony_ci ymaxCross = cross; 2609cb93a386Sopenharmony_ci } 2610cb93a386Sopenharmony_ci } 2611cb93a386Sopenharmony_ci if (ymaxCross) { 2612cb93a386Sopenharmony_ci d = crossToDir(ymaxCross); 2613cb93a386Sopenharmony_ci path.setFirstDirection(d); 2614cb93a386Sopenharmony_ci } 2615cb93a386Sopenharmony_ci return d; // may still be kUnknown 2616cb93a386Sopenharmony_ci} 2617cb93a386Sopenharmony_ci 2618cb93a386Sopenharmony_ci/////////////////////////////////////////////////////////////////////////////// 2619cb93a386Sopenharmony_ci 2620cb93a386Sopenharmony_cistatic bool between(SkScalar a, SkScalar b, SkScalar c) { 2621cb93a386Sopenharmony_ci SkASSERT(((a <= b && b <= c) || (a >= b && b >= c)) == ((a - b) * (c - b) <= 0) 2622cb93a386Sopenharmony_ci || (SkScalarNearlyZero(a) && SkScalarNearlyZero(b) && SkScalarNearlyZero(c))); 2623cb93a386Sopenharmony_ci return (a - b) * (c - b) <= 0; 2624cb93a386Sopenharmony_ci} 2625cb93a386Sopenharmony_ci 2626cb93a386Sopenharmony_cistatic SkScalar eval_cubic_pts(SkScalar c0, SkScalar c1, SkScalar c2, SkScalar c3, 2627cb93a386Sopenharmony_ci SkScalar t) { 2628cb93a386Sopenharmony_ci SkScalar A = c3 + 3*(c1 - c2) - c0; 2629cb93a386Sopenharmony_ci SkScalar B = 3*(c2 - c1 - c1 + c0); 2630cb93a386Sopenharmony_ci SkScalar C = 3*(c1 - c0); 2631cb93a386Sopenharmony_ci SkScalar D = c0; 2632cb93a386Sopenharmony_ci return poly_eval(A, B, C, D, t); 2633cb93a386Sopenharmony_ci} 2634cb93a386Sopenharmony_ci 2635cb93a386Sopenharmony_citemplate <size_t N> static void find_minmax(const SkPoint pts[], 2636cb93a386Sopenharmony_ci SkScalar* minPtr, SkScalar* maxPtr) { 2637cb93a386Sopenharmony_ci SkScalar min, max; 2638cb93a386Sopenharmony_ci min = max = pts[0].fX; 2639cb93a386Sopenharmony_ci for (size_t i = 1; i < N; ++i) { 2640cb93a386Sopenharmony_ci min = std::min(min, pts[i].fX); 2641cb93a386Sopenharmony_ci max = std::max(max, pts[i].fX); 2642cb93a386Sopenharmony_ci } 2643cb93a386Sopenharmony_ci *minPtr = min; 2644cb93a386Sopenharmony_ci *maxPtr = max; 2645cb93a386Sopenharmony_ci} 2646cb93a386Sopenharmony_ci 2647cb93a386Sopenharmony_cistatic bool checkOnCurve(SkScalar x, SkScalar y, const SkPoint& start, const SkPoint& end) { 2648cb93a386Sopenharmony_ci if (start.fY == end.fY) { 2649cb93a386Sopenharmony_ci return between(start.fX, x, end.fX) && x != end.fX; 2650cb93a386Sopenharmony_ci } else { 2651cb93a386Sopenharmony_ci return x == start.fX && y == start.fY; 2652cb93a386Sopenharmony_ci } 2653cb93a386Sopenharmony_ci} 2654cb93a386Sopenharmony_ci 2655cb93a386Sopenharmony_cistatic int winding_mono_cubic(const SkPoint pts[], SkScalar x, SkScalar y, int* onCurveCount) { 2656cb93a386Sopenharmony_ci SkScalar y0 = pts[0].fY; 2657cb93a386Sopenharmony_ci SkScalar y3 = pts[3].fY; 2658cb93a386Sopenharmony_ci 2659cb93a386Sopenharmony_ci int dir = 1; 2660cb93a386Sopenharmony_ci if (y0 > y3) { 2661cb93a386Sopenharmony_ci using std::swap; 2662cb93a386Sopenharmony_ci swap(y0, y3); 2663cb93a386Sopenharmony_ci dir = -1; 2664cb93a386Sopenharmony_ci } 2665cb93a386Sopenharmony_ci if (y < y0 || y > y3) { 2666cb93a386Sopenharmony_ci return 0; 2667cb93a386Sopenharmony_ci } 2668cb93a386Sopenharmony_ci if (checkOnCurve(x, y, pts[0], pts[3])) { 2669cb93a386Sopenharmony_ci *onCurveCount += 1; 2670cb93a386Sopenharmony_ci return 0; 2671cb93a386Sopenharmony_ci } 2672cb93a386Sopenharmony_ci if (y == y3) { 2673cb93a386Sopenharmony_ci return 0; 2674cb93a386Sopenharmony_ci } 2675cb93a386Sopenharmony_ci 2676cb93a386Sopenharmony_ci // quickreject or quickaccept 2677cb93a386Sopenharmony_ci SkScalar min, max; 2678cb93a386Sopenharmony_ci find_minmax<4>(pts, &min, &max); 2679cb93a386Sopenharmony_ci if (x < min) { 2680cb93a386Sopenharmony_ci return 0; 2681cb93a386Sopenharmony_ci } 2682cb93a386Sopenharmony_ci if (x > max) { 2683cb93a386Sopenharmony_ci return dir; 2684cb93a386Sopenharmony_ci } 2685cb93a386Sopenharmony_ci 2686cb93a386Sopenharmony_ci // compute the actual x(t) value 2687cb93a386Sopenharmony_ci SkScalar t; 2688cb93a386Sopenharmony_ci if (!SkCubicClipper::ChopMonoAtY(pts, y, &t)) { 2689cb93a386Sopenharmony_ci return 0; 2690cb93a386Sopenharmony_ci } 2691cb93a386Sopenharmony_ci SkScalar xt = eval_cubic_pts(pts[0].fX, pts[1].fX, pts[2].fX, pts[3].fX, t); 2692cb93a386Sopenharmony_ci if (SkScalarNearlyEqual(xt, x)) { 2693cb93a386Sopenharmony_ci if (x != pts[3].fX || y != pts[3].fY) { // don't test end points; they're start points 2694cb93a386Sopenharmony_ci *onCurveCount += 1; 2695cb93a386Sopenharmony_ci return 0; 2696cb93a386Sopenharmony_ci } 2697cb93a386Sopenharmony_ci } 2698cb93a386Sopenharmony_ci return xt < x ? dir : 0; 2699cb93a386Sopenharmony_ci} 2700cb93a386Sopenharmony_ci 2701cb93a386Sopenharmony_cistatic int winding_cubic(const SkPoint pts[], SkScalar x, SkScalar y, int* onCurveCount) { 2702cb93a386Sopenharmony_ci SkPoint dst[10]; 2703cb93a386Sopenharmony_ci int n = SkChopCubicAtYExtrema(pts, dst); 2704cb93a386Sopenharmony_ci int w = 0; 2705cb93a386Sopenharmony_ci for (int i = 0; i <= n; ++i) { 2706cb93a386Sopenharmony_ci w += winding_mono_cubic(&dst[i * 3], x, y, onCurveCount); 2707cb93a386Sopenharmony_ci } 2708cb93a386Sopenharmony_ci return w; 2709cb93a386Sopenharmony_ci} 2710cb93a386Sopenharmony_ci 2711cb93a386Sopenharmony_cistatic double conic_eval_numerator(const SkScalar src[], SkScalar w, SkScalar t) { 2712cb93a386Sopenharmony_ci SkASSERT(src); 2713cb93a386Sopenharmony_ci SkASSERT(t >= 0 && t <= 1); 2714cb93a386Sopenharmony_ci SkScalar src2w = src[2] * w; 2715cb93a386Sopenharmony_ci SkScalar C = src[0]; 2716cb93a386Sopenharmony_ci SkScalar A = src[4] - 2 * src2w + C; 2717cb93a386Sopenharmony_ci SkScalar B = 2 * (src2w - C); 2718cb93a386Sopenharmony_ci return poly_eval(A, B, C, t); 2719cb93a386Sopenharmony_ci} 2720cb93a386Sopenharmony_ci 2721cb93a386Sopenharmony_ci 2722cb93a386Sopenharmony_cistatic double conic_eval_denominator(SkScalar w, SkScalar t) { 2723cb93a386Sopenharmony_ci SkScalar B = 2 * (w - 1); 2724cb93a386Sopenharmony_ci SkScalar C = 1; 2725cb93a386Sopenharmony_ci SkScalar A = -B; 2726cb93a386Sopenharmony_ci return poly_eval(A, B, C, t); 2727cb93a386Sopenharmony_ci} 2728cb93a386Sopenharmony_ci 2729cb93a386Sopenharmony_cistatic int winding_mono_conic(const SkConic& conic, SkScalar x, SkScalar y, int* onCurveCount) { 2730cb93a386Sopenharmony_ci const SkPoint* pts = conic.fPts; 2731cb93a386Sopenharmony_ci SkScalar y0 = pts[0].fY; 2732cb93a386Sopenharmony_ci SkScalar y2 = pts[2].fY; 2733cb93a386Sopenharmony_ci 2734cb93a386Sopenharmony_ci int dir = 1; 2735cb93a386Sopenharmony_ci if (y0 > y2) { 2736cb93a386Sopenharmony_ci using std::swap; 2737cb93a386Sopenharmony_ci swap(y0, y2); 2738cb93a386Sopenharmony_ci dir = -1; 2739cb93a386Sopenharmony_ci } 2740cb93a386Sopenharmony_ci if (y < y0 || y > y2) { 2741cb93a386Sopenharmony_ci return 0; 2742cb93a386Sopenharmony_ci } 2743cb93a386Sopenharmony_ci if (checkOnCurve(x, y, pts[0], pts[2])) { 2744cb93a386Sopenharmony_ci *onCurveCount += 1; 2745cb93a386Sopenharmony_ci return 0; 2746cb93a386Sopenharmony_ci } 2747cb93a386Sopenharmony_ci if (y == y2) { 2748cb93a386Sopenharmony_ci return 0; 2749cb93a386Sopenharmony_ci } 2750cb93a386Sopenharmony_ci 2751cb93a386Sopenharmony_ci SkScalar roots[2]; 2752cb93a386Sopenharmony_ci SkScalar A = pts[2].fY; 2753cb93a386Sopenharmony_ci SkScalar B = pts[1].fY * conic.fW - y * conic.fW + y; 2754cb93a386Sopenharmony_ci SkScalar C = pts[0].fY; 2755cb93a386Sopenharmony_ci A += C - 2 * B; // A = a + c - 2*(b*w - yCept*w + yCept) 2756cb93a386Sopenharmony_ci B -= C; // B = b*w - w * yCept + yCept - a 2757cb93a386Sopenharmony_ci C -= y; 2758cb93a386Sopenharmony_ci int n = SkFindUnitQuadRoots(A, 2 * B, C, roots); 2759cb93a386Sopenharmony_ci SkASSERT(n <= 1); 2760cb93a386Sopenharmony_ci SkScalar xt; 2761cb93a386Sopenharmony_ci if (0 == n) { 2762cb93a386Sopenharmony_ci // zero roots are returned only when y0 == y 2763cb93a386Sopenharmony_ci // Need [0] if dir == 1 2764cb93a386Sopenharmony_ci // and [2] if dir == -1 2765cb93a386Sopenharmony_ci xt = pts[1 - dir].fX; 2766cb93a386Sopenharmony_ci } else { 2767cb93a386Sopenharmony_ci SkScalar t = roots[0]; 2768cb93a386Sopenharmony_ci xt = conic_eval_numerator(&pts[0].fX, conic.fW, t) / conic_eval_denominator(conic.fW, t); 2769cb93a386Sopenharmony_ci } 2770cb93a386Sopenharmony_ci if (SkScalarNearlyEqual(xt, x)) { 2771cb93a386Sopenharmony_ci if (x != pts[2].fX || y != pts[2].fY) { // don't test end points; they're start points 2772cb93a386Sopenharmony_ci *onCurveCount += 1; 2773cb93a386Sopenharmony_ci return 0; 2774cb93a386Sopenharmony_ci } 2775cb93a386Sopenharmony_ci } 2776cb93a386Sopenharmony_ci return xt < x ? dir : 0; 2777cb93a386Sopenharmony_ci} 2778cb93a386Sopenharmony_ci 2779cb93a386Sopenharmony_cistatic bool is_mono_quad(SkScalar y0, SkScalar y1, SkScalar y2) { 2780cb93a386Sopenharmony_ci // return SkScalarSignAsInt(y0 - y1) + SkScalarSignAsInt(y1 - y2) != 0; 2781cb93a386Sopenharmony_ci if (y0 == y1) { 2782cb93a386Sopenharmony_ci return true; 2783cb93a386Sopenharmony_ci } 2784cb93a386Sopenharmony_ci if (y0 < y1) { 2785cb93a386Sopenharmony_ci return y1 <= y2; 2786cb93a386Sopenharmony_ci } else { 2787cb93a386Sopenharmony_ci return y1 >= y2; 2788cb93a386Sopenharmony_ci } 2789cb93a386Sopenharmony_ci} 2790cb93a386Sopenharmony_ci 2791cb93a386Sopenharmony_cistatic int winding_conic(const SkPoint pts[], SkScalar x, SkScalar y, SkScalar weight, 2792cb93a386Sopenharmony_ci int* onCurveCount) { 2793cb93a386Sopenharmony_ci SkConic conic(pts, weight); 2794cb93a386Sopenharmony_ci SkConic chopped[2]; 2795cb93a386Sopenharmony_ci // If the data points are very large, the conic may not be monotonic but may also 2796cb93a386Sopenharmony_ci // fail to chop. Then, the chopper does not split the original conic in two. 2797cb93a386Sopenharmony_ci bool isMono = is_mono_quad(pts[0].fY, pts[1].fY, pts[2].fY) || !conic.chopAtYExtrema(chopped); 2798cb93a386Sopenharmony_ci int w = winding_mono_conic(isMono ? conic : chopped[0], x, y, onCurveCount); 2799cb93a386Sopenharmony_ci if (!isMono) { 2800cb93a386Sopenharmony_ci w += winding_mono_conic(chopped[1], x, y, onCurveCount); 2801cb93a386Sopenharmony_ci } 2802cb93a386Sopenharmony_ci return w; 2803cb93a386Sopenharmony_ci} 2804cb93a386Sopenharmony_ci 2805cb93a386Sopenharmony_cistatic int winding_mono_quad(const SkPoint pts[], SkScalar x, SkScalar y, int* onCurveCount) { 2806cb93a386Sopenharmony_ci SkScalar y0 = pts[0].fY; 2807cb93a386Sopenharmony_ci SkScalar y2 = pts[2].fY; 2808cb93a386Sopenharmony_ci 2809cb93a386Sopenharmony_ci int dir = 1; 2810cb93a386Sopenharmony_ci if (y0 > y2) { 2811cb93a386Sopenharmony_ci using std::swap; 2812cb93a386Sopenharmony_ci swap(y0, y2); 2813cb93a386Sopenharmony_ci dir = -1; 2814cb93a386Sopenharmony_ci } 2815cb93a386Sopenharmony_ci if (y < y0 || y > y2) { 2816cb93a386Sopenharmony_ci return 0; 2817cb93a386Sopenharmony_ci } 2818cb93a386Sopenharmony_ci if (checkOnCurve(x, y, pts[0], pts[2])) { 2819cb93a386Sopenharmony_ci *onCurveCount += 1; 2820cb93a386Sopenharmony_ci return 0; 2821cb93a386Sopenharmony_ci } 2822cb93a386Sopenharmony_ci if (y == y2) { 2823cb93a386Sopenharmony_ci return 0; 2824cb93a386Sopenharmony_ci } 2825cb93a386Sopenharmony_ci // bounds check on X (not required. is it faster?) 2826cb93a386Sopenharmony_ci#if 0 2827cb93a386Sopenharmony_ci if (pts[0].fX > x && pts[1].fX > x && pts[2].fX > x) { 2828cb93a386Sopenharmony_ci return 0; 2829cb93a386Sopenharmony_ci } 2830cb93a386Sopenharmony_ci#endif 2831cb93a386Sopenharmony_ci 2832cb93a386Sopenharmony_ci SkScalar roots[2]; 2833cb93a386Sopenharmony_ci int n = SkFindUnitQuadRoots(pts[0].fY - 2 * pts[1].fY + pts[2].fY, 2834cb93a386Sopenharmony_ci 2 * (pts[1].fY - pts[0].fY), 2835cb93a386Sopenharmony_ci pts[0].fY - y, 2836cb93a386Sopenharmony_ci roots); 2837cb93a386Sopenharmony_ci SkASSERT(n <= 1); 2838cb93a386Sopenharmony_ci SkScalar xt; 2839cb93a386Sopenharmony_ci if (0 == n) { 2840cb93a386Sopenharmony_ci // zero roots are returned only when y0 == y 2841cb93a386Sopenharmony_ci // Need [0] if dir == 1 2842cb93a386Sopenharmony_ci // and [2] if dir == -1 2843cb93a386Sopenharmony_ci xt = pts[1 - dir].fX; 2844cb93a386Sopenharmony_ci } else { 2845cb93a386Sopenharmony_ci SkScalar t = roots[0]; 2846cb93a386Sopenharmony_ci SkScalar C = pts[0].fX; 2847cb93a386Sopenharmony_ci SkScalar A = pts[2].fX - 2 * pts[1].fX + C; 2848cb93a386Sopenharmony_ci SkScalar B = 2 * (pts[1].fX - C); 2849cb93a386Sopenharmony_ci xt = poly_eval(A, B, C, t); 2850cb93a386Sopenharmony_ci } 2851cb93a386Sopenharmony_ci if (SkScalarNearlyEqual(xt, x)) { 2852cb93a386Sopenharmony_ci if (x != pts[2].fX || y != pts[2].fY) { // don't test end points; they're start points 2853cb93a386Sopenharmony_ci *onCurveCount += 1; 2854cb93a386Sopenharmony_ci return 0; 2855cb93a386Sopenharmony_ci } 2856cb93a386Sopenharmony_ci } 2857cb93a386Sopenharmony_ci return xt < x ? dir : 0; 2858cb93a386Sopenharmony_ci} 2859cb93a386Sopenharmony_ci 2860cb93a386Sopenharmony_cistatic int winding_quad(const SkPoint pts[], SkScalar x, SkScalar y, int* onCurveCount) { 2861cb93a386Sopenharmony_ci SkPoint dst[5]; 2862cb93a386Sopenharmony_ci int n = 0; 2863cb93a386Sopenharmony_ci 2864cb93a386Sopenharmony_ci if (!is_mono_quad(pts[0].fY, pts[1].fY, pts[2].fY)) { 2865cb93a386Sopenharmony_ci n = SkChopQuadAtYExtrema(pts, dst); 2866cb93a386Sopenharmony_ci pts = dst; 2867cb93a386Sopenharmony_ci } 2868cb93a386Sopenharmony_ci int w = winding_mono_quad(pts, x, y, onCurveCount); 2869cb93a386Sopenharmony_ci if (n > 0) { 2870cb93a386Sopenharmony_ci w += winding_mono_quad(&pts[2], x, y, onCurveCount); 2871cb93a386Sopenharmony_ci } 2872cb93a386Sopenharmony_ci return w; 2873cb93a386Sopenharmony_ci} 2874cb93a386Sopenharmony_ci 2875cb93a386Sopenharmony_cistatic int winding_line(const SkPoint pts[], SkScalar x, SkScalar y, int* onCurveCount) { 2876cb93a386Sopenharmony_ci SkScalar x0 = pts[0].fX; 2877cb93a386Sopenharmony_ci SkScalar y0 = pts[0].fY; 2878cb93a386Sopenharmony_ci SkScalar x1 = pts[1].fX; 2879cb93a386Sopenharmony_ci SkScalar y1 = pts[1].fY; 2880cb93a386Sopenharmony_ci 2881cb93a386Sopenharmony_ci SkScalar dy = y1 - y0; 2882cb93a386Sopenharmony_ci 2883cb93a386Sopenharmony_ci int dir = 1; 2884cb93a386Sopenharmony_ci if (y0 > y1) { 2885cb93a386Sopenharmony_ci using std::swap; 2886cb93a386Sopenharmony_ci swap(y0, y1); 2887cb93a386Sopenharmony_ci dir = -1; 2888cb93a386Sopenharmony_ci } 2889cb93a386Sopenharmony_ci if (y < y0 || y > y1) { 2890cb93a386Sopenharmony_ci return 0; 2891cb93a386Sopenharmony_ci } 2892cb93a386Sopenharmony_ci if (checkOnCurve(x, y, pts[0], pts[1])) { 2893cb93a386Sopenharmony_ci *onCurveCount += 1; 2894cb93a386Sopenharmony_ci return 0; 2895cb93a386Sopenharmony_ci } 2896cb93a386Sopenharmony_ci if (y == y1) { 2897cb93a386Sopenharmony_ci return 0; 2898cb93a386Sopenharmony_ci } 2899cb93a386Sopenharmony_ci SkScalar cross = (x1 - x0) * (y - pts[0].fY) - dy * (x - x0); 2900cb93a386Sopenharmony_ci 2901cb93a386Sopenharmony_ci if (!cross) { 2902cb93a386Sopenharmony_ci // zero cross means the point is on the line, and since the case where 2903cb93a386Sopenharmony_ci // y of the query point is at the end point is handled above, we can be 2904cb93a386Sopenharmony_ci // sure that we're on the line (excluding the end point) here 2905cb93a386Sopenharmony_ci if (x != x1 || y != pts[1].fY) { 2906cb93a386Sopenharmony_ci *onCurveCount += 1; 2907cb93a386Sopenharmony_ci } 2908cb93a386Sopenharmony_ci dir = 0; 2909cb93a386Sopenharmony_ci } else if (SkScalarSignAsInt(cross) == dir) { 2910cb93a386Sopenharmony_ci dir = 0; 2911cb93a386Sopenharmony_ci } 2912cb93a386Sopenharmony_ci return dir; 2913cb93a386Sopenharmony_ci} 2914cb93a386Sopenharmony_ci 2915cb93a386Sopenharmony_cistatic void tangent_cubic(const SkPoint pts[], SkScalar x, SkScalar y, 2916cb93a386Sopenharmony_ci SkTDArray<SkVector>* tangents) { 2917cb93a386Sopenharmony_ci if (!between(pts[0].fY, y, pts[1].fY) && !between(pts[1].fY, y, pts[2].fY) 2918cb93a386Sopenharmony_ci && !between(pts[2].fY, y, pts[3].fY)) { 2919cb93a386Sopenharmony_ci return; 2920cb93a386Sopenharmony_ci } 2921cb93a386Sopenharmony_ci if (!between(pts[0].fX, x, pts[1].fX) && !between(pts[1].fX, x, pts[2].fX) 2922cb93a386Sopenharmony_ci && !between(pts[2].fX, x, pts[3].fX)) { 2923cb93a386Sopenharmony_ci return; 2924cb93a386Sopenharmony_ci } 2925cb93a386Sopenharmony_ci SkPoint dst[10]; 2926cb93a386Sopenharmony_ci int n = SkChopCubicAtYExtrema(pts, dst); 2927cb93a386Sopenharmony_ci for (int i = 0; i <= n; ++i) { 2928cb93a386Sopenharmony_ci SkPoint* c = &dst[i * 3]; 2929cb93a386Sopenharmony_ci SkScalar t; 2930cb93a386Sopenharmony_ci if (!SkCubicClipper::ChopMonoAtY(c, y, &t)) { 2931cb93a386Sopenharmony_ci continue; 2932cb93a386Sopenharmony_ci } 2933cb93a386Sopenharmony_ci SkScalar xt = eval_cubic_pts(c[0].fX, c[1].fX, c[2].fX, c[3].fX, t); 2934cb93a386Sopenharmony_ci if (!SkScalarNearlyEqual(x, xt)) { 2935cb93a386Sopenharmony_ci continue; 2936cb93a386Sopenharmony_ci } 2937cb93a386Sopenharmony_ci SkVector tangent; 2938cb93a386Sopenharmony_ci SkEvalCubicAt(c, t, nullptr, &tangent, nullptr); 2939cb93a386Sopenharmony_ci tangents->push_back(tangent); 2940cb93a386Sopenharmony_ci } 2941cb93a386Sopenharmony_ci} 2942cb93a386Sopenharmony_ci 2943cb93a386Sopenharmony_cistatic void tangent_conic(const SkPoint pts[], SkScalar x, SkScalar y, SkScalar w, 2944cb93a386Sopenharmony_ci SkTDArray<SkVector>* tangents) { 2945cb93a386Sopenharmony_ci if (!between(pts[0].fY, y, pts[1].fY) && !between(pts[1].fY, y, pts[2].fY)) { 2946cb93a386Sopenharmony_ci return; 2947cb93a386Sopenharmony_ci } 2948cb93a386Sopenharmony_ci if (!between(pts[0].fX, x, pts[1].fX) && !between(pts[1].fX, x, pts[2].fX)) { 2949cb93a386Sopenharmony_ci return; 2950cb93a386Sopenharmony_ci } 2951cb93a386Sopenharmony_ci SkScalar roots[2]; 2952cb93a386Sopenharmony_ci SkScalar A = pts[2].fY; 2953cb93a386Sopenharmony_ci SkScalar B = pts[1].fY * w - y * w + y; 2954cb93a386Sopenharmony_ci SkScalar C = pts[0].fY; 2955cb93a386Sopenharmony_ci A += C - 2 * B; // A = a + c - 2*(b*w - yCept*w + yCept) 2956cb93a386Sopenharmony_ci B -= C; // B = b*w - w * yCept + yCept - a 2957cb93a386Sopenharmony_ci C -= y; 2958cb93a386Sopenharmony_ci int n = SkFindUnitQuadRoots(A, 2 * B, C, roots); 2959cb93a386Sopenharmony_ci for (int index = 0; index < n; ++index) { 2960cb93a386Sopenharmony_ci SkScalar t = roots[index]; 2961cb93a386Sopenharmony_ci SkScalar xt = conic_eval_numerator(&pts[0].fX, w, t) / conic_eval_denominator(w, t); 2962cb93a386Sopenharmony_ci if (!SkScalarNearlyEqual(x, xt)) { 2963cb93a386Sopenharmony_ci continue; 2964cb93a386Sopenharmony_ci } 2965cb93a386Sopenharmony_ci SkConic conic(pts, w); 2966cb93a386Sopenharmony_ci tangents->push_back(conic.evalTangentAt(t)); 2967cb93a386Sopenharmony_ci } 2968cb93a386Sopenharmony_ci} 2969cb93a386Sopenharmony_ci 2970cb93a386Sopenharmony_cistatic void tangent_quad(const SkPoint pts[], SkScalar x, SkScalar y, 2971cb93a386Sopenharmony_ci SkTDArray<SkVector>* tangents) { 2972cb93a386Sopenharmony_ci if (!between(pts[0].fY, y, pts[1].fY) && !between(pts[1].fY, y, pts[2].fY)) { 2973cb93a386Sopenharmony_ci return; 2974cb93a386Sopenharmony_ci } 2975cb93a386Sopenharmony_ci if (!between(pts[0].fX, x, pts[1].fX) && !between(pts[1].fX, x, pts[2].fX)) { 2976cb93a386Sopenharmony_ci return; 2977cb93a386Sopenharmony_ci } 2978cb93a386Sopenharmony_ci SkScalar roots[2]; 2979cb93a386Sopenharmony_ci int n = SkFindUnitQuadRoots(pts[0].fY - 2 * pts[1].fY + pts[2].fY, 2980cb93a386Sopenharmony_ci 2 * (pts[1].fY - pts[0].fY), 2981cb93a386Sopenharmony_ci pts[0].fY - y, 2982cb93a386Sopenharmony_ci roots); 2983cb93a386Sopenharmony_ci for (int index = 0; index < n; ++index) { 2984cb93a386Sopenharmony_ci SkScalar t = roots[index]; 2985cb93a386Sopenharmony_ci SkScalar C = pts[0].fX; 2986cb93a386Sopenharmony_ci SkScalar A = pts[2].fX - 2 * pts[1].fX + C; 2987cb93a386Sopenharmony_ci SkScalar B = 2 * (pts[1].fX - C); 2988cb93a386Sopenharmony_ci SkScalar xt = poly_eval(A, B, C, t); 2989cb93a386Sopenharmony_ci if (!SkScalarNearlyEqual(x, xt)) { 2990cb93a386Sopenharmony_ci continue; 2991cb93a386Sopenharmony_ci } 2992cb93a386Sopenharmony_ci tangents->push_back(SkEvalQuadTangentAt(pts, t)); 2993cb93a386Sopenharmony_ci } 2994cb93a386Sopenharmony_ci} 2995cb93a386Sopenharmony_ci 2996cb93a386Sopenharmony_cistatic void tangent_line(const SkPoint pts[], SkScalar x, SkScalar y, 2997cb93a386Sopenharmony_ci SkTDArray<SkVector>* tangents) { 2998cb93a386Sopenharmony_ci SkScalar y0 = pts[0].fY; 2999cb93a386Sopenharmony_ci SkScalar y1 = pts[1].fY; 3000cb93a386Sopenharmony_ci if (!between(y0, y, y1)) { 3001cb93a386Sopenharmony_ci return; 3002cb93a386Sopenharmony_ci } 3003cb93a386Sopenharmony_ci SkScalar x0 = pts[0].fX; 3004cb93a386Sopenharmony_ci SkScalar x1 = pts[1].fX; 3005cb93a386Sopenharmony_ci if (!between(x0, x, x1)) { 3006cb93a386Sopenharmony_ci return; 3007cb93a386Sopenharmony_ci } 3008cb93a386Sopenharmony_ci SkScalar dx = x1 - x0; 3009cb93a386Sopenharmony_ci SkScalar dy = y1 - y0; 3010cb93a386Sopenharmony_ci if (!SkScalarNearlyEqual((x - x0) * dy, dx * (y - y0))) { 3011cb93a386Sopenharmony_ci return; 3012cb93a386Sopenharmony_ci } 3013cb93a386Sopenharmony_ci SkVector v; 3014cb93a386Sopenharmony_ci v.set(dx, dy); 3015cb93a386Sopenharmony_ci tangents->push_back(v); 3016cb93a386Sopenharmony_ci} 3017cb93a386Sopenharmony_ci 3018cb93a386Sopenharmony_cistatic bool contains_inclusive(const SkRect& r, SkScalar x, SkScalar y) { 3019cb93a386Sopenharmony_ci return r.fLeft <= x && x <= r.fRight && r.fTop <= y && y <= r.fBottom; 3020cb93a386Sopenharmony_ci} 3021cb93a386Sopenharmony_ci 3022cb93a386Sopenharmony_cibool SkPath::contains(SkScalar x, SkScalar y) const { 3023cb93a386Sopenharmony_ci bool isInverse = this->isInverseFillType(); 3024cb93a386Sopenharmony_ci if (this->isEmpty()) { 3025cb93a386Sopenharmony_ci return isInverse; 3026cb93a386Sopenharmony_ci } 3027cb93a386Sopenharmony_ci 3028cb93a386Sopenharmony_ci if (!contains_inclusive(this->getBounds(), x, y)) { 3029cb93a386Sopenharmony_ci return isInverse; 3030cb93a386Sopenharmony_ci } 3031cb93a386Sopenharmony_ci 3032cb93a386Sopenharmony_ci SkPath::Iter iter(*this, true); 3033cb93a386Sopenharmony_ci bool done = false; 3034cb93a386Sopenharmony_ci int w = 0; 3035cb93a386Sopenharmony_ci int onCurveCount = 0; 3036cb93a386Sopenharmony_ci do { 3037cb93a386Sopenharmony_ci SkPoint pts[4]; 3038cb93a386Sopenharmony_ci switch (iter.next(pts)) { 3039cb93a386Sopenharmony_ci case SkPath::kMove_Verb: 3040cb93a386Sopenharmony_ci case SkPath::kClose_Verb: 3041cb93a386Sopenharmony_ci break; 3042cb93a386Sopenharmony_ci case SkPath::kLine_Verb: 3043cb93a386Sopenharmony_ci w += winding_line(pts, x, y, &onCurveCount); 3044cb93a386Sopenharmony_ci break; 3045cb93a386Sopenharmony_ci case SkPath::kQuad_Verb: 3046cb93a386Sopenharmony_ci w += winding_quad(pts, x, y, &onCurveCount); 3047cb93a386Sopenharmony_ci break; 3048cb93a386Sopenharmony_ci case SkPath::kConic_Verb: 3049cb93a386Sopenharmony_ci w += winding_conic(pts, x, y, iter.conicWeight(), &onCurveCount); 3050cb93a386Sopenharmony_ci break; 3051cb93a386Sopenharmony_ci case SkPath::kCubic_Verb: 3052cb93a386Sopenharmony_ci w += winding_cubic(pts, x, y, &onCurveCount); 3053cb93a386Sopenharmony_ci break; 3054cb93a386Sopenharmony_ci case SkPath::kDone_Verb: 3055cb93a386Sopenharmony_ci done = true; 3056cb93a386Sopenharmony_ci break; 3057cb93a386Sopenharmony_ci } 3058cb93a386Sopenharmony_ci } while (!done); 3059cb93a386Sopenharmony_ci bool evenOddFill = SkPathFillType::kEvenOdd == this->getFillType() 3060cb93a386Sopenharmony_ci || SkPathFillType::kInverseEvenOdd == this->getFillType(); 3061cb93a386Sopenharmony_ci if (evenOddFill) { 3062cb93a386Sopenharmony_ci w &= 1; 3063cb93a386Sopenharmony_ci } 3064cb93a386Sopenharmony_ci if (w) { 3065cb93a386Sopenharmony_ci return !isInverse; 3066cb93a386Sopenharmony_ci } 3067cb93a386Sopenharmony_ci if (onCurveCount <= 1) { 3068cb93a386Sopenharmony_ci return SkToBool(onCurveCount) ^ isInverse; 3069cb93a386Sopenharmony_ci } 3070cb93a386Sopenharmony_ci if ((onCurveCount & 1) || evenOddFill) { 3071cb93a386Sopenharmony_ci return SkToBool(onCurveCount & 1) ^ isInverse; 3072cb93a386Sopenharmony_ci } 3073cb93a386Sopenharmony_ci // If the point touches an even number of curves, and the fill is winding, check for 3074cb93a386Sopenharmony_ci // coincidence. Count coincidence as places where the on curve points have identical tangents. 3075cb93a386Sopenharmony_ci iter.setPath(*this, true); 3076cb93a386Sopenharmony_ci done = false; 3077cb93a386Sopenharmony_ci SkTDArray<SkVector> tangents; 3078cb93a386Sopenharmony_ci do { 3079cb93a386Sopenharmony_ci SkPoint pts[4]; 3080cb93a386Sopenharmony_ci int oldCount = tangents.count(); 3081cb93a386Sopenharmony_ci switch (iter.next(pts)) { 3082cb93a386Sopenharmony_ci case SkPath::kMove_Verb: 3083cb93a386Sopenharmony_ci case SkPath::kClose_Verb: 3084cb93a386Sopenharmony_ci break; 3085cb93a386Sopenharmony_ci case SkPath::kLine_Verb: 3086cb93a386Sopenharmony_ci tangent_line(pts, x, y, &tangents); 3087cb93a386Sopenharmony_ci break; 3088cb93a386Sopenharmony_ci case SkPath::kQuad_Verb: 3089cb93a386Sopenharmony_ci tangent_quad(pts, x, y, &tangents); 3090cb93a386Sopenharmony_ci break; 3091cb93a386Sopenharmony_ci case SkPath::kConic_Verb: 3092cb93a386Sopenharmony_ci tangent_conic(pts, x, y, iter.conicWeight(), &tangents); 3093cb93a386Sopenharmony_ci break; 3094cb93a386Sopenharmony_ci case SkPath::kCubic_Verb: 3095cb93a386Sopenharmony_ci tangent_cubic(pts, x, y, &tangents); 3096cb93a386Sopenharmony_ci break; 3097cb93a386Sopenharmony_ci case SkPath::kDone_Verb: 3098cb93a386Sopenharmony_ci done = true; 3099cb93a386Sopenharmony_ci break; 3100cb93a386Sopenharmony_ci } 3101cb93a386Sopenharmony_ci if (tangents.count() > oldCount) { 3102cb93a386Sopenharmony_ci int last = tangents.count() - 1; 3103cb93a386Sopenharmony_ci const SkVector& tangent = tangents[last]; 3104cb93a386Sopenharmony_ci if (SkScalarNearlyZero(SkPointPriv::LengthSqd(tangent))) { 3105cb93a386Sopenharmony_ci tangents.remove(last); 3106cb93a386Sopenharmony_ci } else { 3107cb93a386Sopenharmony_ci for (int index = 0; index < last; ++index) { 3108cb93a386Sopenharmony_ci const SkVector& test = tangents[index]; 3109cb93a386Sopenharmony_ci if (SkScalarNearlyZero(test.cross(tangent)) 3110cb93a386Sopenharmony_ci && SkScalarSignAsInt(tangent.fX * test.fX) <= 0 3111cb93a386Sopenharmony_ci && SkScalarSignAsInt(tangent.fY * test.fY) <= 0) { 3112cb93a386Sopenharmony_ci tangents.remove(last); 3113cb93a386Sopenharmony_ci tangents.removeShuffle(index); 3114cb93a386Sopenharmony_ci break; 3115cb93a386Sopenharmony_ci } 3116cb93a386Sopenharmony_ci } 3117cb93a386Sopenharmony_ci } 3118cb93a386Sopenharmony_ci } 3119cb93a386Sopenharmony_ci } while (!done); 3120cb93a386Sopenharmony_ci return SkToBool(tangents.count()) ^ isInverse; 3121cb93a386Sopenharmony_ci} 3122cb93a386Sopenharmony_ci 3123cb93a386Sopenharmony_ciint SkPath::ConvertConicToQuads(const SkPoint& p0, const SkPoint& p1, const SkPoint& p2, 3124cb93a386Sopenharmony_ci SkScalar w, SkPoint pts[], int pow2) { 3125cb93a386Sopenharmony_ci const SkConic conic(p0, p1, p2, w); 3126cb93a386Sopenharmony_ci return conic.chopIntoQuadsPOW2(pts, pow2); 3127cb93a386Sopenharmony_ci} 3128cb93a386Sopenharmony_ci 3129cb93a386Sopenharmony_cibool SkPathPriv::IsSimpleRect(const SkPath& path, bool isSimpleFill, SkRect* rect, 3130cb93a386Sopenharmony_ci SkPathDirection* direction, unsigned* start) { 3131cb93a386Sopenharmony_ci if (path.getSegmentMasks() != SkPath::kLine_SegmentMask) { 3132cb93a386Sopenharmony_ci return false; 3133cb93a386Sopenharmony_ci } 3134cb93a386Sopenharmony_ci SkPoint rectPts[5]; 3135cb93a386Sopenharmony_ci int rectPtCnt = 0; 3136cb93a386Sopenharmony_ci bool needsClose = !isSimpleFill; 3137cb93a386Sopenharmony_ci for (auto [v, verbPts, w] : SkPathPriv::Iterate(path)) { 3138cb93a386Sopenharmony_ci switch (v) { 3139cb93a386Sopenharmony_ci case SkPathVerb::kMove: 3140cb93a386Sopenharmony_ci if (0 != rectPtCnt) { 3141cb93a386Sopenharmony_ci return false; 3142cb93a386Sopenharmony_ci } 3143cb93a386Sopenharmony_ci rectPts[0] = verbPts[0]; 3144cb93a386Sopenharmony_ci ++rectPtCnt; 3145cb93a386Sopenharmony_ci break; 3146cb93a386Sopenharmony_ci case SkPathVerb::kLine: 3147cb93a386Sopenharmony_ci if (5 == rectPtCnt) { 3148cb93a386Sopenharmony_ci return false; 3149cb93a386Sopenharmony_ci } 3150cb93a386Sopenharmony_ci rectPts[rectPtCnt] = verbPts[1]; 3151cb93a386Sopenharmony_ci ++rectPtCnt; 3152cb93a386Sopenharmony_ci break; 3153cb93a386Sopenharmony_ci case SkPathVerb::kClose: 3154cb93a386Sopenharmony_ci if (4 == rectPtCnt) { 3155cb93a386Sopenharmony_ci rectPts[4] = rectPts[0]; 3156cb93a386Sopenharmony_ci rectPtCnt = 5; 3157cb93a386Sopenharmony_ci } 3158cb93a386Sopenharmony_ci needsClose = false; 3159cb93a386Sopenharmony_ci break; 3160cb93a386Sopenharmony_ci case SkPathVerb::kQuad: 3161cb93a386Sopenharmony_ci case SkPathVerb::kConic: 3162cb93a386Sopenharmony_ci case SkPathVerb::kCubic: 3163cb93a386Sopenharmony_ci return false; 3164cb93a386Sopenharmony_ci } 3165cb93a386Sopenharmony_ci } 3166cb93a386Sopenharmony_ci if (needsClose) { 3167cb93a386Sopenharmony_ci return false; 3168cb93a386Sopenharmony_ci } 3169cb93a386Sopenharmony_ci if (rectPtCnt < 5) { 3170cb93a386Sopenharmony_ci return false; 3171cb93a386Sopenharmony_ci } 3172cb93a386Sopenharmony_ci if (rectPts[0] != rectPts[4]) { 3173cb93a386Sopenharmony_ci return false; 3174cb93a386Sopenharmony_ci } 3175cb93a386Sopenharmony_ci // Check for two cases of rectangles: pts 0 and 3 form a vertical edge or a horizontal edge ( 3176cb93a386Sopenharmony_ci // and pts 1 and 2 the opposite vertical or horizontal edge). 3177cb93a386Sopenharmony_ci bool vec03IsVertical; 3178cb93a386Sopenharmony_ci if (rectPts[0].fX == rectPts[3].fX && rectPts[1].fX == rectPts[2].fX && 3179cb93a386Sopenharmony_ci rectPts[0].fY == rectPts[1].fY && rectPts[3].fY == rectPts[2].fY) { 3180cb93a386Sopenharmony_ci // Make sure it has non-zero width and height 3181cb93a386Sopenharmony_ci if (rectPts[0].fX == rectPts[1].fX || rectPts[0].fY == rectPts[3].fY) { 3182cb93a386Sopenharmony_ci return false; 3183cb93a386Sopenharmony_ci } 3184cb93a386Sopenharmony_ci vec03IsVertical = true; 3185cb93a386Sopenharmony_ci } else if (rectPts[0].fY == rectPts[3].fY && rectPts[1].fY == rectPts[2].fY && 3186cb93a386Sopenharmony_ci rectPts[0].fX == rectPts[1].fX && rectPts[3].fX == rectPts[2].fX) { 3187cb93a386Sopenharmony_ci // Make sure it has non-zero width and height 3188cb93a386Sopenharmony_ci if (rectPts[0].fY == rectPts[1].fY || rectPts[0].fX == rectPts[3].fX) { 3189cb93a386Sopenharmony_ci return false; 3190cb93a386Sopenharmony_ci } 3191cb93a386Sopenharmony_ci vec03IsVertical = false; 3192cb93a386Sopenharmony_ci } else { 3193cb93a386Sopenharmony_ci return false; 3194cb93a386Sopenharmony_ci } 3195cb93a386Sopenharmony_ci // Set sortFlags so that it has the low bit set if pt index 0 is on right edge and second bit 3196cb93a386Sopenharmony_ci // set if it is on the bottom edge. 3197cb93a386Sopenharmony_ci unsigned sortFlags = 3198cb93a386Sopenharmony_ci ((rectPts[0].fX < rectPts[2].fX) ? 0b00 : 0b01) | 3199cb93a386Sopenharmony_ci ((rectPts[0].fY < rectPts[2].fY) ? 0b00 : 0b10); 3200cb93a386Sopenharmony_ci switch (sortFlags) { 3201cb93a386Sopenharmony_ci case 0b00: 3202cb93a386Sopenharmony_ci rect->setLTRB(rectPts[0].fX, rectPts[0].fY, rectPts[2].fX, rectPts[2].fY); 3203cb93a386Sopenharmony_ci *direction = vec03IsVertical ? SkPathDirection::kCW : SkPathDirection::kCCW; 3204cb93a386Sopenharmony_ci *start = 0; 3205cb93a386Sopenharmony_ci break; 3206cb93a386Sopenharmony_ci case 0b01: 3207cb93a386Sopenharmony_ci rect->setLTRB(rectPts[2].fX, rectPts[0].fY, rectPts[0].fX, rectPts[2].fY); 3208cb93a386Sopenharmony_ci *direction = vec03IsVertical ? SkPathDirection::kCCW : SkPathDirection::kCW; 3209cb93a386Sopenharmony_ci *start = 1; 3210cb93a386Sopenharmony_ci break; 3211cb93a386Sopenharmony_ci case 0b10: 3212cb93a386Sopenharmony_ci rect->setLTRB(rectPts[0].fX, rectPts[2].fY, rectPts[2].fX, rectPts[0].fY); 3213cb93a386Sopenharmony_ci *direction = vec03IsVertical ? SkPathDirection::kCCW : SkPathDirection::kCW; 3214cb93a386Sopenharmony_ci *start = 3; 3215cb93a386Sopenharmony_ci break; 3216cb93a386Sopenharmony_ci case 0b11: 3217cb93a386Sopenharmony_ci rect->setLTRB(rectPts[2].fX, rectPts[2].fY, rectPts[0].fX, rectPts[0].fY); 3218cb93a386Sopenharmony_ci *direction = vec03IsVertical ? SkPathDirection::kCW : SkPathDirection::kCCW; 3219cb93a386Sopenharmony_ci *start = 2; 3220cb93a386Sopenharmony_ci break; 3221cb93a386Sopenharmony_ci } 3222cb93a386Sopenharmony_ci return true; 3223cb93a386Sopenharmony_ci} 3224cb93a386Sopenharmony_ci 3225cb93a386Sopenharmony_cibool SkPathPriv::DrawArcIsConvex(SkScalar sweepAngle, bool useCenter, bool isFillNoPathEffect) { 3226cb93a386Sopenharmony_ci if (isFillNoPathEffect && SkScalarAbs(sweepAngle) >= 360.f) { 3227cb93a386Sopenharmony_ci // This gets converted to an oval. 3228cb93a386Sopenharmony_ci return true; 3229cb93a386Sopenharmony_ci } 3230cb93a386Sopenharmony_ci if (useCenter) { 3231cb93a386Sopenharmony_ci // This is a pie wedge. It's convex if the angle is <= 180. 3232cb93a386Sopenharmony_ci return SkScalarAbs(sweepAngle) <= 180.f; 3233cb93a386Sopenharmony_ci } 3234cb93a386Sopenharmony_ci // When the angle exceeds 360 this wraps back on top of itself. Otherwise it is a circle clipped 3235cb93a386Sopenharmony_ci // to a secant, i.e. convex. 3236cb93a386Sopenharmony_ci return SkScalarAbs(sweepAngle) <= 360.f; 3237cb93a386Sopenharmony_ci} 3238cb93a386Sopenharmony_ci 3239cb93a386Sopenharmony_civoid SkPathPriv::CreateDrawArcPath(SkPath* path, const SkRect& oval, SkScalar startAngle, 3240cb93a386Sopenharmony_ci SkScalar sweepAngle, bool useCenter, bool isFillNoPathEffect) { 3241cb93a386Sopenharmony_ci SkASSERT(!oval.isEmpty()); 3242cb93a386Sopenharmony_ci SkASSERT(sweepAngle); 3243cb93a386Sopenharmony_ci#if defined(SK_BUILD_FOR_FUZZER) 3244cb93a386Sopenharmony_ci if (sweepAngle > 3600.0f || sweepAngle < -3600.0f) { 3245cb93a386Sopenharmony_ci return; 3246cb93a386Sopenharmony_ci } 3247cb93a386Sopenharmony_ci#endif 3248cb93a386Sopenharmony_ci path->reset(); 3249cb93a386Sopenharmony_ci path->setIsVolatile(true); 3250cb93a386Sopenharmony_ci path->setFillType(SkPathFillType::kWinding); 3251cb93a386Sopenharmony_ci if (isFillNoPathEffect && SkScalarAbs(sweepAngle) >= 360.f) { 3252cb93a386Sopenharmony_ci path->addOval(oval); 3253cb93a386Sopenharmony_ci SkASSERT(path->isConvex() && DrawArcIsConvex(sweepAngle, false, isFillNoPathEffect)); 3254cb93a386Sopenharmony_ci return; 3255cb93a386Sopenharmony_ci } 3256cb93a386Sopenharmony_ci if (useCenter) { 3257cb93a386Sopenharmony_ci path->moveTo(oval.centerX(), oval.centerY()); 3258cb93a386Sopenharmony_ci } 3259cb93a386Sopenharmony_ci auto firstDir = 3260cb93a386Sopenharmony_ci sweepAngle > 0 ? SkPathFirstDirection::kCW : SkPathFirstDirection::kCCW; 3261cb93a386Sopenharmony_ci bool convex = DrawArcIsConvex(sweepAngle, useCenter, isFillNoPathEffect); 3262cb93a386Sopenharmony_ci // Arc to mods at 360 and drawArc is not supposed to. 3263cb93a386Sopenharmony_ci bool forceMoveTo = !useCenter; 3264cb93a386Sopenharmony_ci while (sweepAngle <= -360.f) { 3265cb93a386Sopenharmony_ci path->arcTo(oval, startAngle, -180.f, forceMoveTo); 3266cb93a386Sopenharmony_ci startAngle -= 180.f; 3267cb93a386Sopenharmony_ci path->arcTo(oval, startAngle, -180.f, false); 3268cb93a386Sopenharmony_ci startAngle -= 180.f; 3269cb93a386Sopenharmony_ci forceMoveTo = false; 3270cb93a386Sopenharmony_ci sweepAngle += 360.f; 3271cb93a386Sopenharmony_ci } 3272cb93a386Sopenharmony_ci while (sweepAngle >= 360.f) { 3273cb93a386Sopenharmony_ci path->arcTo(oval, startAngle, 180.f, forceMoveTo); 3274cb93a386Sopenharmony_ci startAngle += 180.f; 3275cb93a386Sopenharmony_ci path->arcTo(oval, startAngle, 180.f, false); 3276cb93a386Sopenharmony_ci startAngle += 180.f; 3277cb93a386Sopenharmony_ci forceMoveTo = false; 3278cb93a386Sopenharmony_ci sweepAngle -= 360.f; 3279cb93a386Sopenharmony_ci } 3280cb93a386Sopenharmony_ci path->arcTo(oval, startAngle, sweepAngle, forceMoveTo); 3281cb93a386Sopenharmony_ci if (useCenter) { 3282cb93a386Sopenharmony_ci path->close(); 3283cb93a386Sopenharmony_ci } 3284cb93a386Sopenharmony_ci path->setConvexity(convex ? SkPathConvexity::kConvex : SkPathConvexity::kConcave); 3285cb93a386Sopenharmony_ci path->setFirstDirection(firstDir); 3286cb93a386Sopenharmony_ci} 3287cb93a386Sopenharmony_ci 3288cb93a386Sopenharmony_ci/////////////////////////////////////////////////////////////////////////////////////////////////// 3289cb93a386Sopenharmony_ci#include "include/private/SkNx.h" 3290cb93a386Sopenharmony_ci 3291cb93a386Sopenharmony_cistatic int compute_quad_extremas(const SkPoint src[3], SkPoint extremas[3]) { 3292cb93a386Sopenharmony_ci SkScalar ts[2]; 3293cb93a386Sopenharmony_ci int n = SkFindQuadExtrema(src[0].fX, src[1].fX, src[2].fX, ts); 3294cb93a386Sopenharmony_ci n += SkFindQuadExtrema(src[0].fY, src[1].fY, src[2].fY, &ts[n]); 3295cb93a386Sopenharmony_ci SkASSERT(n >= 0 && n <= 2); 3296cb93a386Sopenharmony_ci for (int i = 0; i < n; ++i) { 3297cb93a386Sopenharmony_ci extremas[i] = SkEvalQuadAt(src, ts[i]); 3298cb93a386Sopenharmony_ci } 3299cb93a386Sopenharmony_ci extremas[n] = src[2]; 3300cb93a386Sopenharmony_ci return n + 1; 3301cb93a386Sopenharmony_ci} 3302cb93a386Sopenharmony_ci 3303cb93a386Sopenharmony_cistatic int compute_conic_extremas(const SkPoint src[3], SkScalar w, SkPoint extremas[3]) { 3304cb93a386Sopenharmony_ci SkConic conic(src[0], src[1], src[2], w); 3305cb93a386Sopenharmony_ci SkScalar ts[2]; 3306cb93a386Sopenharmony_ci int n = conic.findXExtrema(ts); 3307cb93a386Sopenharmony_ci n += conic.findYExtrema(&ts[n]); 3308cb93a386Sopenharmony_ci SkASSERT(n >= 0 && n <= 2); 3309cb93a386Sopenharmony_ci for (int i = 0; i < n; ++i) { 3310cb93a386Sopenharmony_ci extremas[i] = conic.evalAt(ts[i]); 3311cb93a386Sopenharmony_ci } 3312cb93a386Sopenharmony_ci extremas[n] = src[2]; 3313cb93a386Sopenharmony_ci return n + 1; 3314cb93a386Sopenharmony_ci} 3315cb93a386Sopenharmony_ci 3316cb93a386Sopenharmony_cistatic int compute_cubic_extremas(const SkPoint src[4], SkPoint extremas[5]) { 3317cb93a386Sopenharmony_ci SkScalar ts[4]; 3318cb93a386Sopenharmony_ci int n = SkFindCubicExtrema(src[0].fX, src[1].fX, src[2].fX, src[3].fX, ts); 3319cb93a386Sopenharmony_ci n += SkFindCubicExtrema(src[0].fY, src[1].fY, src[2].fY, src[3].fY, &ts[n]); 3320cb93a386Sopenharmony_ci SkASSERT(n >= 0 && n <= 4); 3321cb93a386Sopenharmony_ci for (int i = 0; i < n; ++i) { 3322cb93a386Sopenharmony_ci SkEvalCubicAt(src, ts[i], &extremas[i], nullptr, nullptr); 3323cb93a386Sopenharmony_ci } 3324cb93a386Sopenharmony_ci extremas[n] = src[3]; 3325cb93a386Sopenharmony_ci return n + 1; 3326cb93a386Sopenharmony_ci} 3327cb93a386Sopenharmony_ci 3328cb93a386Sopenharmony_ciSkRect SkPath::computeTightBounds() const { 3329cb93a386Sopenharmony_ci if (0 == this->countVerbs()) { 3330cb93a386Sopenharmony_ci return SkRect::MakeEmpty(); 3331cb93a386Sopenharmony_ci } 3332cb93a386Sopenharmony_ci 3333cb93a386Sopenharmony_ci if (this->getSegmentMasks() == SkPath::kLine_SegmentMask) { 3334cb93a386Sopenharmony_ci return this->getBounds(); 3335cb93a386Sopenharmony_ci } 3336cb93a386Sopenharmony_ci 3337cb93a386Sopenharmony_ci SkPoint extremas[5]; // big enough to hold worst-case curve type (cubic) extremas + 1 3338cb93a386Sopenharmony_ci 3339cb93a386Sopenharmony_ci // initial with the first MoveTo, so we don't have to check inside the switch 3340cb93a386Sopenharmony_ci Sk2s min, max; 3341cb93a386Sopenharmony_ci min = max = from_point(this->getPoint(0)); 3342cb93a386Sopenharmony_ci for (auto [verb, pts, w] : SkPathPriv::Iterate(*this)) { 3343cb93a386Sopenharmony_ci int count = 0; 3344cb93a386Sopenharmony_ci switch (verb) { 3345cb93a386Sopenharmony_ci case SkPathVerb::kMove: 3346cb93a386Sopenharmony_ci extremas[0] = pts[0]; 3347cb93a386Sopenharmony_ci count = 1; 3348cb93a386Sopenharmony_ci break; 3349cb93a386Sopenharmony_ci case SkPathVerb::kLine: 3350cb93a386Sopenharmony_ci extremas[0] = pts[1]; 3351cb93a386Sopenharmony_ci count = 1; 3352cb93a386Sopenharmony_ci break; 3353cb93a386Sopenharmony_ci case SkPathVerb::kQuad: 3354cb93a386Sopenharmony_ci count = compute_quad_extremas(pts, extremas); 3355cb93a386Sopenharmony_ci break; 3356cb93a386Sopenharmony_ci case SkPathVerb::kConic: 3357cb93a386Sopenharmony_ci count = compute_conic_extremas(pts, *w, extremas); 3358cb93a386Sopenharmony_ci break; 3359cb93a386Sopenharmony_ci case SkPathVerb::kCubic: 3360cb93a386Sopenharmony_ci count = compute_cubic_extremas(pts, extremas); 3361cb93a386Sopenharmony_ci break; 3362cb93a386Sopenharmony_ci case SkPathVerb::kClose: 3363cb93a386Sopenharmony_ci break; 3364cb93a386Sopenharmony_ci } 3365cb93a386Sopenharmony_ci for (int i = 0; i < count; ++i) { 3366cb93a386Sopenharmony_ci Sk2s tmp = from_point(extremas[i]); 3367cb93a386Sopenharmony_ci min = Sk2s::Min(min, tmp); 3368cb93a386Sopenharmony_ci max = Sk2s::Max(max, tmp); 3369cb93a386Sopenharmony_ci } 3370cb93a386Sopenharmony_ci } 3371cb93a386Sopenharmony_ci SkRect bounds; 3372cb93a386Sopenharmony_ci min.store((SkPoint*)&bounds.fLeft); 3373cb93a386Sopenharmony_ci max.store((SkPoint*)&bounds.fRight); 3374cb93a386Sopenharmony_ci return bounds; 3375cb93a386Sopenharmony_ci} 3376cb93a386Sopenharmony_ci 3377cb93a386Sopenharmony_cibool SkPath::IsLineDegenerate(const SkPoint& p1, const SkPoint& p2, bool exact) { 3378cb93a386Sopenharmony_ci return exact ? p1 == p2 : SkPointPriv::EqualsWithinTolerance(p1, p2); 3379cb93a386Sopenharmony_ci} 3380cb93a386Sopenharmony_ci 3381cb93a386Sopenharmony_cibool SkPath::IsQuadDegenerate(const SkPoint& p1, const SkPoint& p2, 3382cb93a386Sopenharmony_ci const SkPoint& p3, bool exact) { 3383cb93a386Sopenharmony_ci return exact ? p1 == p2 && p2 == p3 : SkPointPriv::EqualsWithinTolerance(p1, p2) && 3384cb93a386Sopenharmony_ci SkPointPriv::EqualsWithinTolerance(p2, p3); 3385cb93a386Sopenharmony_ci} 3386cb93a386Sopenharmony_ci 3387cb93a386Sopenharmony_cibool SkPath::IsCubicDegenerate(const SkPoint& p1, const SkPoint& p2, 3388cb93a386Sopenharmony_ci const SkPoint& p3, const SkPoint& p4, bool exact) { 3389cb93a386Sopenharmony_ci return exact ? p1 == p2 && p2 == p3 && p3 == p4 : 3390cb93a386Sopenharmony_ci SkPointPriv::EqualsWithinTolerance(p1, p2) && 3391cb93a386Sopenharmony_ci SkPointPriv::EqualsWithinTolerance(p2, p3) && 3392cb93a386Sopenharmony_ci SkPointPriv::EqualsWithinTolerance(p3, p4); 3393cb93a386Sopenharmony_ci} 3394cb93a386Sopenharmony_ci 3395cb93a386Sopenharmony_ci////////////////////////////////////////////////////////////////////////////////////////////////// 3396cb93a386Sopenharmony_ci 3397cb93a386Sopenharmony_ciSkPathVerbAnalysis sk_path_analyze_verbs(const uint8_t vbs[], int verbCount) { 3398cb93a386Sopenharmony_ci SkPathVerbAnalysis info = {false, 0, 0, 0}; 3399cb93a386Sopenharmony_ci 3400cb93a386Sopenharmony_ci bool needMove = true; 3401cb93a386Sopenharmony_ci bool invalid = false; 3402cb93a386Sopenharmony_ci for (int i = 0; i < verbCount; ++i) { 3403cb93a386Sopenharmony_ci switch ((SkPathVerb)vbs[i]) { 3404cb93a386Sopenharmony_ci case SkPathVerb::kMove: 3405cb93a386Sopenharmony_ci needMove = false; 3406cb93a386Sopenharmony_ci info.points += 1; 3407cb93a386Sopenharmony_ci break; 3408cb93a386Sopenharmony_ci case SkPathVerb::kLine: 3409cb93a386Sopenharmony_ci invalid |= needMove; 3410cb93a386Sopenharmony_ci info.segmentMask |= kLine_SkPathSegmentMask; 3411cb93a386Sopenharmony_ci info.points += 1; 3412cb93a386Sopenharmony_ci break; 3413cb93a386Sopenharmony_ci case SkPathVerb::kQuad: 3414cb93a386Sopenharmony_ci invalid |= needMove; 3415cb93a386Sopenharmony_ci info.segmentMask |= kQuad_SkPathSegmentMask; 3416cb93a386Sopenharmony_ci info.points += 2; 3417cb93a386Sopenharmony_ci break; 3418cb93a386Sopenharmony_ci case SkPathVerb::kConic: 3419cb93a386Sopenharmony_ci invalid |= needMove; 3420cb93a386Sopenharmony_ci info.segmentMask |= kConic_SkPathSegmentMask; 3421cb93a386Sopenharmony_ci info.points += 2; 3422cb93a386Sopenharmony_ci info.weights += 1; 3423cb93a386Sopenharmony_ci break; 3424cb93a386Sopenharmony_ci case SkPathVerb::kCubic: 3425cb93a386Sopenharmony_ci invalid |= needMove; 3426cb93a386Sopenharmony_ci info.segmentMask |= kCubic_SkPathSegmentMask; 3427cb93a386Sopenharmony_ci info.points += 3; 3428cb93a386Sopenharmony_ci break; 3429cb93a386Sopenharmony_ci case SkPathVerb::kClose: 3430cb93a386Sopenharmony_ci invalid |= needMove; 3431cb93a386Sopenharmony_ci needMove = true; 3432cb93a386Sopenharmony_ci break; 3433cb93a386Sopenharmony_ci default: 3434cb93a386Sopenharmony_ci invalid = true; 3435cb93a386Sopenharmony_ci break; 3436cb93a386Sopenharmony_ci } 3437cb93a386Sopenharmony_ci } 3438cb93a386Sopenharmony_ci info.valid = !invalid; 3439cb93a386Sopenharmony_ci return info; 3440cb93a386Sopenharmony_ci} 3441cb93a386Sopenharmony_ci 3442cb93a386Sopenharmony_ciSkPath SkPath::Make(const SkPoint pts[], int pointCount, 3443cb93a386Sopenharmony_ci const uint8_t vbs[], int verbCount, 3444cb93a386Sopenharmony_ci const SkScalar ws[], int wCount, 3445cb93a386Sopenharmony_ci SkPathFillType ft, bool isVolatile) { 3446cb93a386Sopenharmony_ci if (verbCount <= 0) { 3447cb93a386Sopenharmony_ci return SkPath(); 3448cb93a386Sopenharmony_ci } 3449cb93a386Sopenharmony_ci 3450cb93a386Sopenharmony_ci const auto info = sk_path_analyze_verbs(vbs, verbCount); 3451cb93a386Sopenharmony_ci if (!info.valid || info.points > pointCount || info.weights > wCount) { 3452cb93a386Sopenharmony_ci SkDEBUGFAIL("invalid verbs and number of points/weights"); 3453cb93a386Sopenharmony_ci return SkPath(); 3454cb93a386Sopenharmony_ci } 3455cb93a386Sopenharmony_ci 3456cb93a386Sopenharmony_ci return SkPath(sk_sp<SkPathRef>(new SkPathRef(SkTDArray<SkPoint>(pts, info.points), 3457cb93a386Sopenharmony_ci SkTDArray<uint8_t>(vbs, verbCount), 3458cb93a386Sopenharmony_ci SkTDArray<SkScalar>(ws, info.weights), 3459cb93a386Sopenharmony_ci info.segmentMask)), 3460cb93a386Sopenharmony_ci ft, isVolatile, SkPathConvexity::kUnknown, SkPathFirstDirection::kUnknown); 3461cb93a386Sopenharmony_ci} 3462cb93a386Sopenharmony_ci 3463cb93a386Sopenharmony_ciSkPath SkPath::Rect(const SkRect& r, SkPathDirection dir, unsigned startIndex) { 3464cb93a386Sopenharmony_ci return SkPathBuilder().addRect(r, dir, startIndex).detach(); 3465cb93a386Sopenharmony_ci} 3466cb93a386Sopenharmony_ci 3467cb93a386Sopenharmony_ciSkPath SkPath::Oval(const SkRect& r, SkPathDirection dir) { 3468cb93a386Sopenharmony_ci return SkPathBuilder().addOval(r, dir).detach(); 3469cb93a386Sopenharmony_ci} 3470cb93a386Sopenharmony_ci 3471cb93a386Sopenharmony_ciSkPath SkPath::Oval(const SkRect& r, SkPathDirection dir, unsigned startIndex) { 3472cb93a386Sopenharmony_ci return SkPathBuilder().addOval(r, dir, startIndex).detach(); 3473cb93a386Sopenharmony_ci} 3474cb93a386Sopenharmony_ci 3475cb93a386Sopenharmony_ciSkPath SkPath::Circle(SkScalar x, SkScalar y, SkScalar r, SkPathDirection dir) { 3476cb93a386Sopenharmony_ci return SkPathBuilder().addCircle(x, y, r, dir).detach(); 3477cb93a386Sopenharmony_ci} 3478cb93a386Sopenharmony_ci 3479cb93a386Sopenharmony_ciSkPath SkPath::RRect(const SkRRect& rr, SkPathDirection dir) { 3480cb93a386Sopenharmony_ci return SkPathBuilder().addRRect(rr, dir).detach(); 3481cb93a386Sopenharmony_ci} 3482cb93a386Sopenharmony_ci 3483cb93a386Sopenharmony_ciSkPath SkPath::RRect(const SkRRect& rr, SkPathDirection dir, unsigned startIndex) { 3484cb93a386Sopenharmony_ci return SkPathBuilder().addRRect(rr, dir, startIndex).detach(); 3485cb93a386Sopenharmony_ci} 3486cb93a386Sopenharmony_ci 3487cb93a386Sopenharmony_ciSkPath SkPath::RRect(const SkRect& r, SkScalar rx, SkScalar ry, SkPathDirection dir) { 3488cb93a386Sopenharmony_ci return SkPathBuilder().addRRect(SkRRect::MakeRectXY(r, rx, ry), dir).detach(); 3489cb93a386Sopenharmony_ci} 3490cb93a386Sopenharmony_ci 3491cb93a386Sopenharmony_ciSkPath SkPath::Polygon(const SkPoint pts[], int count, bool isClosed, 3492cb93a386Sopenharmony_ci SkPathFillType ft, bool isVolatile) { 3493cb93a386Sopenharmony_ci return SkPathBuilder().addPolygon(pts, count, isClosed) 3494cb93a386Sopenharmony_ci .setFillType(ft) 3495cb93a386Sopenharmony_ci .setIsVolatile(isVolatile) 3496cb93a386Sopenharmony_ci .detach(); 3497cb93a386Sopenharmony_ci} 3498cb93a386Sopenharmony_ci 3499cb93a386Sopenharmony_ci////////////////////////////////////////////////////////////////////////////////////////////////// 3500cb93a386Sopenharmony_ci 3501cb93a386Sopenharmony_cibool SkPathPriv::IsRectContour(const SkPath& path, bool allowPartial, int* currVerb, 3502cb93a386Sopenharmony_ci const SkPoint** ptsPtr, bool* isClosed, SkPathDirection* direction, 3503cb93a386Sopenharmony_ci SkRect* rect) { 3504cb93a386Sopenharmony_ci int corners = 0; 3505cb93a386Sopenharmony_ci SkPoint closeXY; // used to determine if final line falls on a diagonal 3506cb93a386Sopenharmony_ci SkPoint lineStart; // used to construct line from previous point 3507cb93a386Sopenharmony_ci const SkPoint* firstPt = nullptr; // first point in the rect (last of first moves) 3508cb93a386Sopenharmony_ci const SkPoint* lastPt = nullptr; // last point in the rect (last of lines or first if closed) 3509cb93a386Sopenharmony_ci SkPoint firstCorner; 3510cb93a386Sopenharmony_ci SkPoint thirdCorner; 3511cb93a386Sopenharmony_ci const SkPoint* pts = *ptsPtr; 3512cb93a386Sopenharmony_ci const SkPoint* savePts = nullptr; // used to allow caller to iterate through a pair of rects 3513cb93a386Sopenharmony_ci lineStart.set(0, 0); 3514cb93a386Sopenharmony_ci signed char directions[] = {-1, -1, -1, -1, -1}; // -1 to 3; -1 is uninitialized 3515cb93a386Sopenharmony_ci bool closedOrMoved = false; 3516cb93a386Sopenharmony_ci bool autoClose = false; 3517cb93a386Sopenharmony_ci bool insertClose = false; 3518cb93a386Sopenharmony_ci int verbCnt = path.fPathRef->countVerbs(); 3519cb93a386Sopenharmony_ci while (*currVerb < verbCnt && (!allowPartial || !autoClose)) { 3520cb93a386Sopenharmony_ci uint8_t verb = insertClose ? (uint8_t) SkPath::kClose_Verb : path.fPathRef->atVerb(*currVerb); 3521cb93a386Sopenharmony_ci switch (verb) { 3522cb93a386Sopenharmony_ci case SkPath::kClose_Verb: 3523cb93a386Sopenharmony_ci savePts = pts; 3524cb93a386Sopenharmony_ci autoClose = true; 3525cb93a386Sopenharmony_ci insertClose = false; 3526cb93a386Sopenharmony_ci [[fallthrough]]; 3527cb93a386Sopenharmony_ci case SkPath::kLine_Verb: { 3528cb93a386Sopenharmony_ci if (SkPath::kClose_Verb != verb) { 3529cb93a386Sopenharmony_ci lastPt = pts; 3530cb93a386Sopenharmony_ci } 3531cb93a386Sopenharmony_ci SkPoint lineEnd = SkPath::kClose_Verb == verb ? *firstPt : *pts++; 3532cb93a386Sopenharmony_ci SkVector lineDelta = lineEnd - lineStart; 3533cb93a386Sopenharmony_ci if (lineDelta.fX && lineDelta.fY) { 3534cb93a386Sopenharmony_ci return false; // diagonal 3535cb93a386Sopenharmony_ci } 3536cb93a386Sopenharmony_ci if (!lineDelta.isFinite()) { 3537cb93a386Sopenharmony_ci return false; // path contains infinity or NaN 3538cb93a386Sopenharmony_ci } 3539cb93a386Sopenharmony_ci if (lineStart == lineEnd) { 3540cb93a386Sopenharmony_ci break; // single point on side OK 3541cb93a386Sopenharmony_ci } 3542cb93a386Sopenharmony_ci int nextDirection = rect_make_dir(lineDelta.fX, lineDelta.fY); // 0 to 3 3543cb93a386Sopenharmony_ci if (0 == corners) { 3544cb93a386Sopenharmony_ci directions[0] = nextDirection; 3545cb93a386Sopenharmony_ci corners = 1; 3546cb93a386Sopenharmony_ci closedOrMoved = false; 3547cb93a386Sopenharmony_ci lineStart = lineEnd; 3548cb93a386Sopenharmony_ci break; 3549cb93a386Sopenharmony_ci } 3550cb93a386Sopenharmony_ci if (closedOrMoved) { 3551cb93a386Sopenharmony_ci return false; // closed followed by a line 3552cb93a386Sopenharmony_ci } 3553cb93a386Sopenharmony_ci if (autoClose && nextDirection == directions[0]) { 3554cb93a386Sopenharmony_ci break; // colinear with first 3555cb93a386Sopenharmony_ci } 3556cb93a386Sopenharmony_ci closedOrMoved = autoClose; 3557cb93a386Sopenharmony_ci if (directions[corners - 1] == nextDirection) { 3558cb93a386Sopenharmony_ci if (3 == corners && SkPath::kLine_Verb == verb) { 3559cb93a386Sopenharmony_ci thirdCorner = lineEnd; 3560cb93a386Sopenharmony_ci } 3561cb93a386Sopenharmony_ci lineStart = lineEnd; 3562cb93a386Sopenharmony_ci break; // colinear segment 3563cb93a386Sopenharmony_ci } 3564cb93a386Sopenharmony_ci directions[corners++] = nextDirection; 3565cb93a386Sopenharmony_ci // opposite lines must point in opposite directions; xoring them should equal 2 3566cb93a386Sopenharmony_ci switch (corners) { 3567cb93a386Sopenharmony_ci case 2: 3568cb93a386Sopenharmony_ci firstCorner = lineStart; 3569cb93a386Sopenharmony_ci break; 3570cb93a386Sopenharmony_ci case 3: 3571cb93a386Sopenharmony_ci if ((directions[0] ^ directions[2]) != 2) { 3572cb93a386Sopenharmony_ci return false; 3573cb93a386Sopenharmony_ci } 3574cb93a386Sopenharmony_ci thirdCorner = lineEnd; 3575cb93a386Sopenharmony_ci break; 3576cb93a386Sopenharmony_ci case 4: 3577cb93a386Sopenharmony_ci if ((directions[1] ^ directions[3]) != 2) { 3578cb93a386Sopenharmony_ci return false; 3579cb93a386Sopenharmony_ci } 3580cb93a386Sopenharmony_ci break; 3581cb93a386Sopenharmony_ci default: 3582cb93a386Sopenharmony_ci return false; // too many direction changes 3583cb93a386Sopenharmony_ci } 3584cb93a386Sopenharmony_ci lineStart = lineEnd; 3585cb93a386Sopenharmony_ci break; 3586cb93a386Sopenharmony_ci } 3587cb93a386Sopenharmony_ci case SkPath::kQuad_Verb: 3588cb93a386Sopenharmony_ci case SkPath::kConic_Verb: 3589cb93a386Sopenharmony_ci case SkPath::kCubic_Verb: 3590cb93a386Sopenharmony_ci return false; // quadratic, cubic not allowed 3591cb93a386Sopenharmony_ci case SkPath::kMove_Verb: 3592cb93a386Sopenharmony_ci if (allowPartial && !autoClose && directions[0] >= 0) { 3593cb93a386Sopenharmony_ci insertClose = true; 3594cb93a386Sopenharmony_ci *currVerb -= 1; // try move again afterwards 3595cb93a386Sopenharmony_ci goto addMissingClose; 3596cb93a386Sopenharmony_ci } 3597cb93a386Sopenharmony_ci if (!corners) { 3598cb93a386Sopenharmony_ci firstPt = pts; 3599cb93a386Sopenharmony_ci } else { 3600cb93a386Sopenharmony_ci closeXY = *firstPt - *lastPt; 3601cb93a386Sopenharmony_ci if (closeXY.fX && closeXY.fY) { 3602cb93a386Sopenharmony_ci return false; // we're diagonal, abort 3603cb93a386Sopenharmony_ci } 3604cb93a386Sopenharmony_ci } 3605cb93a386Sopenharmony_ci lineStart = *pts++; 3606cb93a386Sopenharmony_ci closedOrMoved = true; 3607cb93a386Sopenharmony_ci break; 3608cb93a386Sopenharmony_ci default: 3609cb93a386Sopenharmony_ci SkDEBUGFAIL("unexpected verb"); 3610cb93a386Sopenharmony_ci break; 3611cb93a386Sopenharmony_ci } 3612cb93a386Sopenharmony_ci *currVerb += 1; 3613cb93a386Sopenharmony_ci addMissingClose: 3614cb93a386Sopenharmony_ci ; 3615cb93a386Sopenharmony_ci } 3616cb93a386Sopenharmony_ci // Success if 4 corners and first point equals last 3617cb93a386Sopenharmony_ci if (corners < 3 || corners > 4) { 3618cb93a386Sopenharmony_ci return false; 3619cb93a386Sopenharmony_ci } 3620cb93a386Sopenharmony_ci if (savePts) { 3621cb93a386Sopenharmony_ci *ptsPtr = savePts; 3622cb93a386Sopenharmony_ci } 3623cb93a386Sopenharmony_ci // check if close generates diagonal 3624cb93a386Sopenharmony_ci closeXY = *firstPt - *lastPt; 3625cb93a386Sopenharmony_ci if (closeXY.fX && closeXY.fY) { 3626cb93a386Sopenharmony_ci return false; 3627cb93a386Sopenharmony_ci } 3628cb93a386Sopenharmony_ci if (rect) { 3629cb93a386Sopenharmony_ci rect->set(firstCorner, thirdCorner); 3630cb93a386Sopenharmony_ci } 3631cb93a386Sopenharmony_ci if (isClosed) { 3632cb93a386Sopenharmony_ci *isClosed = autoClose; 3633cb93a386Sopenharmony_ci } 3634cb93a386Sopenharmony_ci if (direction) { 3635cb93a386Sopenharmony_ci *direction = directions[0] == ((directions[1] + 1) & 3) ? 3636cb93a386Sopenharmony_ci SkPathDirection::kCW : SkPathDirection::kCCW; 3637cb93a386Sopenharmony_ci } 3638cb93a386Sopenharmony_ci return true; 3639cb93a386Sopenharmony_ci} 3640cb93a386Sopenharmony_ci 3641cb93a386Sopenharmony_ci 3642cb93a386Sopenharmony_cibool SkPathPriv::IsNestedFillRects(const SkPath& path, SkRect rects[2], SkPathDirection dirs[2]) { 3643cb93a386Sopenharmony_ci SkDEBUGCODE(path.validate();) 3644cb93a386Sopenharmony_ci int currVerb = 0; 3645cb93a386Sopenharmony_ci const SkPoint* pts = path.fPathRef->points(); 3646cb93a386Sopenharmony_ci SkPathDirection testDirs[2]; 3647cb93a386Sopenharmony_ci SkRect testRects[2]; 3648cb93a386Sopenharmony_ci if (!IsRectContour(path, true, &currVerb, &pts, nullptr, &testDirs[0], &testRects[0])) { 3649cb93a386Sopenharmony_ci return false; 3650cb93a386Sopenharmony_ci } 3651cb93a386Sopenharmony_ci if (IsRectContour(path, false, &currVerb, &pts, nullptr, &testDirs[1], &testRects[1])) { 3652cb93a386Sopenharmony_ci if (testRects[0].contains(testRects[1])) { 3653cb93a386Sopenharmony_ci if (rects) { 3654cb93a386Sopenharmony_ci rects[0] = testRects[0]; 3655cb93a386Sopenharmony_ci rects[1] = testRects[1]; 3656cb93a386Sopenharmony_ci } 3657cb93a386Sopenharmony_ci if (dirs) { 3658cb93a386Sopenharmony_ci dirs[0] = testDirs[0]; 3659cb93a386Sopenharmony_ci dirs[1] = testDirs[1]; 3660cb93a386Sopenharmony_ci } 3661cb93a386Sopenharmony_ci return true; 3662cb93a386Sopenharmony_ci } 3663cb93a386Sopenharmony_ci if (testRects[1].contains(testRects[0])) { 3664cb93a386Sopenharmony_ci if (rects) { 3665cb93a386Sopenharmony_ci rects[0] = testRects[1]; 3666cb93a386Sopenharmony_ci rects[1] = testRects[0]; 3667cb93a386Sopenharmony_ci } 3668cb93a386Sopenharmony_ci if (dirs) { 3669cb93a386Sopenharmony_ci dirs[0] = testDirs[1]; 3670cb93a386Sopenharmony_ci dirs[1] = testDirs[0]; 3671cb93a386Sopenharmony_ci } 3672cb93a386Sopenharmony_ci return true; 3673cb93a386Sopenharmony_ci } 3674cb93a386Sopenharmony_ci } 3675cb93a386Sopenharmony_ci return false; 3676cb93a386Sopenharmony_ci} 3677cb93a386Sopenharmony_ci 3678cb93a386Sopenharmony_ci/////////////////////////////////////////////////////////////////////////////////////////////////// 3679cb93a386Sopenharmony_ci 3680cb93a386Sopenharmony_ci#include "src/core/SkEdgeClipper.h" 3681cb93a386Sopenharmony_ci 3682cb93a386Sopenharmony_cistruct SkHalfPlane { 3683cb93a386Sopenharmony_ci SkScalar fA, fB, fC; 3684cb93a386Sopenharmony_ci 3685cb93a386Sopenharmony_ci SkScalar eval(SkScalar x, SkScalar y) const { 3686cb93a386Sopenharmony_ci return fA * x + fB * y + fC; 3687cb93a386Sopenharmony_ci } 3688cb93a386Sopenharmony_ci SkScalar operator()(SkScalar x, SkScalar y) const { return this->eval(x, y); } 3689cb93a386Sopenharmony_ci 3690cb93a386Sopenharmony_ci bool normalize() { 3691cb93a386Sopenharmony_ci double a = fA; 3692cb93a386Sopenharmony_ci double b = fB; 3693cb93a386Sopenharmony_ci double c = fC; 3694cb93a386Sopenharmony_ci double dmag = sqrt(a * a + b * b); 3695cb93a386Sopenharmony_ci // length of initial plane normal is zero 3696cb93a386Sopenharmony_ci if (dmag == 0) { 3697cb93a386Sopenharmony_ci fA = fB = 0; 3698cb93a386Sopenharmony_ci fC = SK_Scalar1; 3699cb93a386Sopenharmony_ci return true; 3700cb93a386Sopenharmony_ci } 3701cb93a386Sopenharmony_ci double dscale = sk_ieee_double_divide(1.0, dmag); 3702cb93a386Sopenharmony_ci a *= dscale; 3703cb93a386Sopenharmony_ci b *= dscale; 3704cb93a386Sopenharmony_ci c *= dscale; 3705cb93a386Sopenharmony_ci // check if we're not finite, or normal is zero-length 3706cb93a386Sopenharmony_ci if (!sk_float_isfinite(a) || !sk_float_isfinite(b) || !sk_float_isfinite(c) || 3707cb93a386Sopenharmony_ci (a == 0 && b == 0)) { 3708cb93a386Sopenharmony_ci fA = fB = 0; 3709cb93a386Sopenharmony_ci fC = SK_Scalar1; 3710cb93a386Sopenharmony_ci return false; 3711cb93a386Sopenharmony_ci } 3712cb93a386Sopenharmony_ci fA = a; 3713cb93a386Sopenharmony_ci fB = b; 3714cb93a386Sopenharmony_ci fC = c; 3715cb93a386Sopenharmony_ci return true; 3716cb93a386Sopenharmony_ci } 3717cb93a386Sopenharmony_ci 3718cb93a386Sopenharmony_ci enum Result { 3719cb93a386Sopenharmony_ci kAllNegative, 3720cb93a386Sopenharmony_ci kAllPositive, 3721cb93a386Sopenharmony_ci kMixed 3722cb93a386Sopenharmony_ci }; 3723cb93a386Sopenharmony_ci Result test(const SkRect& bounds) const { 3724cb93a386Sopenharmony_ci // check whether the diagonal aligned with the normal crosses the plane 3725cb93a386Sopenharmony_ci SkPoint diagMin, diagMax; 3726cb93a386Sopenharmony_ci if (fA >= 0) { 3727cb93a386Sopenharmony_ci diagMin.fX = bounds.fLeft; 3728cb93a386Sopenharmony_ci diagMax.fX = bounds.fRight; 3729cb93a386Sopenharmony_ci } else { 3730cb93a386Sopenharmony_ci diagMin.fX = bounds.fRight; 3731cb93a386Sopenharmony_ci diagMax.fX = bounds.fLeft; 3732cb93a386Sopenharmony_ci } 3733cb93a386Sopenharmony_ci if (fB >= 0) { 3734cb93a386Sopenharmony_ci diagMin.fY = bounds.fTop; 3735cb93a386Sopenharmony_ci diagMax.fY = bounds.fBottom; 3736cb93a386Sopenharmony_ci } else { 3737cb93a386Sopenharmony_ci diagMin.fY = bounds.fBottom; 3738cb93a386Sopenharmony_ci diagMax.fY = bounds.fTop; 3739cb93a386Sopenharmony_ci } 3740cb93a386Sopenharmony_ci SkScalar test = this->eval(diagMin.fX, diagMin.fY); 3741cb93a386Sopenharmony_ci SkScalar sign = test*this->eval(diagMax.fX, diagMax.fY); 3742cb93a386Sopenharmony_ci if (sign > 0) { 3743cb93a386Sopenharmony_ci // the path is either all on one side of the half-plane or the other 3744cb93a386Sopenharmony_ci if (test < 0) { 3745cb93a386Sopenharmony_ci return kAllNegative; 3746cb93a386Sopenharmony_ci } else { 3747cb93a386Sopenharmony_ci return kAllPositive; 3748cb93a386Sopenharmony_ci } 3749cb93a386Sopenharmony_ci } 3750cb93a386Sopenharmony_ci return kMixed; 3751cb93a386Sopenharmony_ci } 3752cb93a386Sopenharmony_ci}; 3753cb93a386Sopenharmony_ci 3754cb93a386Sopenharmony_ci// assumes plane is pre-normalized 3755cb93a386Sopenharmony_ci// If we fail in our calculations, we return the empty path 3756cb93a386Sopenharmony_cistatic SkPath clip(const SkPath& path, const SkHalfPlane& plane) { 3757cb93a386Sopenharmony_ci SkMatrix mx, inv; 3758cb93a386Sopenharmony_ci SkPoint p0 = { -plane.fA*plane.fC, -plane.fB*plane.fC }; 3759cb93a386Sopenharmony_ci mx.setAll( plane.fB, plane.fA, p0.fX, 3760cb93a386Sopenharmony_ci -plane.fA, plane.fB, p0.fY, 3761cb93a386Sopenharmony_ci 0, 0, 1); 3762cb93a386Sopenharmony_ci if (!mx.invert(&inv)) { 3763cb93a386Sopenharmony_ci return SkPath(); 3764cb93a386Sopenharmony_ci } 3765cb93a386Sopenharmony_ci 3766cb93a386Sopenharmony_ci SkPath rotated; 3767cb93a386Sopenharmony_ci path.transform(inv, &rotated); 3768cb93a386Sopenharmony_ci if (!rotated.isFinite()) { 3769cb93a386Sopenharmony_ci return SkPath(); 3770cb93a386Sopenharmony_ci } 3771cb93a386Sopenharmony_ci 3772cb93a386Sopenharmony_ci SkScalar big = SK_ScalarMax; 3773cb93a386Sopenharmony_ci SkRect clip = {-big, 0, big, big }; 3774cb93a386Sopenharmony_ci 3775cb93a386Sopenharmony_ci struct Rec { 3776cb93a386Sopenharmony_ci SkPathBuilder fResult; 3777cb93a386Sopenharmony_ci SkPoint fPrev = {0,0}; 3778cb93a386Sopenharmony_ci } rec; 3779cb93a386Sopenharmony_ci 3780cb93a386Sopenharmony_ci SkEdgeClipper::ClipPath(rotated, clip, false, 3781cb93a386Sopenharmony_ci [](SkEdgeClipper* clipper, bool newCtr, void* ctx) { 3782cb93a386Sopenharmony_ci Rec* rec = (Rec*)ctx; 3783cb93a386Sopenharmony_ci 3784cb93a386Sopenharmony_ci bool addLineTo = false; 3785cb93a386Sopenharmony_ci SkPoint pts[4]; 3786cb93a386Sopenharmony_ci SkPath::Verb verb; 3787cb93a386Sopenharmony_ci while ((verb = clipper->next(pts)) != SkPath::kDone_Verb) { 3788cb93a386Sopenharmony_ci if (newCtr) { 3789cb93a386Sopenharmony_ci rec->fResult.moveTo(pts[0]); 3790cb93a386Sopenharmony_ci rec->fPrev = pts[0]; 3791cb93a386Sopenharmony_ci newCtr = false; 3792cb93a386Sopenharmony_ci } 3793cb93a386Sopenharmony_ci 3794cb93a386Sopenharmony_ci if (addLineTo || pts[0] != rec->fPrev) { 3795cb93a386Sopenharmony_ci rec->fResult.lineTo(pts[0]); 3796cb93a386Sopenharmony_ci } 3797cb93a386Sopenharmony_ci 3798cb93a386Sopenharmony_ci switch (verb) { 3799cb93a386Sopenharmony_ci case SkPath::kLine_Verb: 3800cb93a386Sopenharmony_ci rec->fResult.lineTo(pts[1]); 3801cb93a386Sopenharmony_ci rec->fPrev = pts[1]; 3802cb93a386Sopenharmony_ci break; 3803cb93a386Sopenharmony_ci case SkPath::kQuad_Verb: 3804cb93a386Sopenharmony_ci rec->fResult.quadTo(pts[1], pts[2]); 3805cb93a386Sopenharmony_ci rec->fPrev = pts[2]; 3806cb93a386Sopenharmony_ci break; 3807cb93a386Sopenharmony_ci case SkPath::kCubic_Verb: 3808cb93a386Sopenharmony_ci rec->fResult.cubicTo(pts[1], pts[2], pts[3]); 3809cb93a386Sopenharmony_ci rec->fPrev = pts[3]; 3810cb93a386Sopenharmony_ci break; 3811cb93a386Sopenharmony_ci default: break; 3812cb93a386Sopenharmony_ci } 3813cb93a386Sopenharmony_ci addLineTo = true; 3814cb93a386Sopenharmony_ci } 3815cb93a386Sopenharmony_ci }, &rec); 3816cb93a386Sopenharmony_ci 3817cb93a386Sopenharmony_ci rec.fResult.setFillType(path.getFillType()); 3818cb93a386Sopenharmony_ci SkPath result = rec.fResult.detach().makeTransform(mx); 3819cb93a386Sopenharmony_ci if (!result.isFinite()) { 3820cb93a386Sopenharmony_ci result = SkPath(); 3821cb93a386Sopenharmony_ci } 3822cb93a386Sopenharmony_ci return result; 3823cb93a386Sopenharmony_ci} 3824cb93a386Sopenharmony_ci 3825cb93a386Sopenharmony_ci// true means we have written to clippedPath 3826cb93a386Sopenharmony_cibool SkPathPriv::PerspectiveClip(const SkPath& path, const SkMatrix& matrix, SkPath* clippedPath) { 3827cb93a386Sopenharmony_ci if (!matrix.hasPerspective()) { 3828cb93a386Sopenharmony_ci return false; 3829cb93a386Sopenharmony_ci } 3830cb93a386Sopenharmony_ci 3831cb93a386Sopenharmony_ci SkHalfPlane plane { 3832cb93a386Sopenharmony_ci matrix[SkMatrix::kMPersp0], 3833cb93a386Sopenharmony_ci matrix[SkMatrix::kMPersp1], 3834cb93a386Sopenharmony_ci matrix[SkMatrix::kMPersp2] - kW0PlaneDistance 3835cb93a386Sopenharmony_ci }; 3836cb93a386Sopenharmony_ci if (plane.normalize()) { 3837cb93a386Sopenharmony_ci switch (plane.test(path.getBounds())) { 3838cb93a386Sopenharmony_ci case SkHalfPlane::kAllPositive: 3839cb93a386Sopenharmony_ci return false; 3840cb93a386Sopenharmony_ci case SkHalfPlane::kMixed: { 3841cb93a386Sopenharmony_ci *clippedPath = clip(path, plane); 3842cb93a386Sopenharmony_ci return true; 3843cb93a386Sopenharmony_ci } break; 3844cb93a386Sopenharmony_ci default: break; // handled outside of the switch 3845cb93a386Sopenharmony_ci } 3846cb93a386Sopenharmony_ci } 3847cb93a386Sopenharmony_ci // clipped out (or failed) 3848cb93a386Sopenharmony_ci *clippedPath = SkPath(); 3849cb93a386Sopenharmony_ci return true; 3850cb93a386Sopenharmony_ci} 3851cb93a386Sopenharmony_ci 3852cb93a386Sopenharmony_ciint SkPathPriv::GenIDChangeListenersCount(const SkPath& path) { 3853cb93a386Sopenharmony_ci return path.fPathRef->genIDChangeListenerCount(); 3854cb93a386Sopenharmony_ci} 3855cb93a386Sopenharmony_ci 3856cb93a386Sopenharmony_cibool SkPathPriv::IsAxisAligned(const SkPath& path) { 3857cb93a386Sopenharmony_ci // Conservative (quick) test to see if all segments are axis-aligned. 3858cb93a386Sopenharmony_ci // Multiple contours might give a false-negative, but for speed, we ignore that 3859cb93a386Sopenharmony_ci // and just look at the raw points. 3860cb93a386Sopenharmony_ci 3861cb93a386Sopenharmony_ci const SkPoint* pts = path.fPathRef->points(); 3862cb93a386Sopenharmony_ci const int count = path.fPathRef->countPoints(); 3863cb93a386Sopenharmony_ci 3864cb93a386Sopenharmony_ci for (int i = 1; i < count; ++i) { 3865cb93a386Sopenharmony_ci if (pts[i-1].fX != pts[i].fX && pts[i-1].fY != pts[i].fY) { 3866cb93a386Sopenharmony_ci return false; 3867cb93a386Sopenharmony_ci } 3868cb93a386Sopenharmony_ci } 3869cb93a386Sopenharmony_ci return true; 3870cb93a386Sopenharmony_ci} 3871