1cb93a386Sopenharmony_ci/* 2cb93a386Sopenharmony_ci * Copyright 2014 Google Inc. 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/SkPathMeasure.h" 9cb93a386Sopenharmony_ci#include "include/core/SkStrokeRec.h" 10cb93a386Sopenharmony_ci#include "src/core/SkPathPriv.h" 11cb93a386Sopenharmony_ci#include "src/core/SkPointPriv.h" 12cb93a386Sopenharmony_ci#include "src/utils/SkDashPathPriv.h" 13cb93a386Sopenharmony_ci 14cb93a386Sopenharmony_ci#include <utility> 15cb93a386Sopenharmony_ci 16cb93a386Sopenharmony_cistatic inline int is_even(int x) { 17cb93a386Sopenharmony_ci return !(x & 1); 18cb93a386Sopenharmony_ci} 19cb93a386Sopenharmony_ci 20cb93a386Sopenharmony_cistatic SkScalar find_first_interval(const SkScalar intervals[], SkScalar phase, 21cb93a386Sopenharmony_ci int32_t* index, int count) { 22cb93a386Sopenharmony_ci for (int i = 0; i < count; ++i) { 23cb93a386Sopenharmony_ci SkScalar gap = intervals[i]; 24cb93a386Sopenharmony_ci if (phase > gap || (phase == gap && gap)) { 25cb93a386Sopenharmony_ci phase -= gap; 26cb93a386Sopenharmony_ci } else { 27cb93a386Sopenharmony_ci *index = i; 28cb93a386Sopenharmony_ci return gap - phase; 29cb93a386Sopenharmony_ci } 30cb93a386Sopenharmony_ci } 31cb93a386Sopenharmony_ci // If we get here, phase "appears" to be larger than our length. This 32cb93a386Sopenharmony_ci // shouldn't happen with perfect precision, but we can accumulate errors 33cb93a386Sopenharmony_ci // during the initial length computation (rounding can make our sum be too 34cb93a386Sopenharmony_ci // big or too small. In that event, we just have to eat the error here. 35cb93a386Sopenharmony_ci *index = 0; 36cb93a386Sopenharmony_ci return intervals[0]; 37cb93a386Sopenharmony_ci} 38cb93a386Sopenharmony_ci 39cb93a386Sopenharmony_civoid SkDashPath::CalcDashParameters(SkScalar phase, const SkScalar intervals[], int32_t count, 40cb93a386Sopenharmony_ci SkScalar* initialDashLength, int32_t* initialDashIndex, 41cb93a386Sopenharmony_ci SkScalar* intervalLength, SkScalar* adjustedPhase) { 42cb93a386Sopenharmony_ci SkScalar len = 0; 43cb93a386Sopenharmony_ci for (int i = 0; i < count; i++) { 44cb93a386Sopenharmony_ci len += intervals[i]; 45cb93a386Sopenharmony_ci } 46cb93a386Sopenharmony_ci *intervalLength = len; 47cb93a386Sopenharmony_ci // Adjust phase to be between 0 and len, "flipping" phase if negative. 48cb93a386Sopenharmony_ci // e.g., if len is 100, then phase of -20 (or -120) is equivalent to 80 49cb93a386Sopenharmony_ci if (adjustedPhase) { 50cb93a386Sopenharmony_ci if (phase < 0) { 51cb93a386Sopenharmony_ci phase = -phase; 52cb93a386Sopenharmony_ci if (phase > len) { 53cb93a386Sopenharmony_ci phase = SkScalarMod(phase, len); 54cb93a386Sopenharmony_ci } 55cb93a386Sopenharmony_ci phase = len - phase; 56cb93a386Sopenharmony_ci 57cb93a386Sopenharmony_ci // Due to finite precision, it's possible that phase == len, 58cb93a386Sopenharmony_ci // even after the subtract (if len >>> phase), so fix that here. 59cb93a386Sopenharmony_ci // This fixes http://crbug.com/124652 . 60cb93a386Sopenharmony_ci SkASSERT(phase <= len); 61cb93a386Sopenharmony_ci if (phase == len) { 62cb93a386Sopenharmony_ci phase = 0; 63cb93a386Sopenharmony_ci } 64cb93a386Sopenharmony_ci } else if (phase >= len) { 65cb93a386Sopenharmony_ci phase = SkScalarMod(phase, len); 66cb93a386Sopenharmony_ci } 67cb93a386Sopenharmony_ci *adjustedPhase = phase; 68cb93a386Sopenharmony_ci } 69cb93a386Sopenharmony_ci SkASSERT(phase >= 0 && phase < len); 70cb93a386Sopenharmony_ci 71cb93a386Sopenharmony_ci *initialDashLength = find_first_interval(intervals, phase, 72cb93a386Sopenharmony_ci initialDashIndex, count); 73cb93a386Sopenharmony_ci 74cb93a386Sopenharmony_ci SkASSERT(*initialDashLength >= 0); 75cb93a386Sopenharmony_ci SkASSERT(*initialDashIndex >= 0 && *initialDashIndex < count); 76cb93a386Sopenharmony_ci} 77cb93a386Sopenharmony_ci 78cb93a386Sopenharmony_cistatic void outset_for_stroke(SkRect* rect, const SkStrokeRec& rec) { 79cb93a386Sopenharmony_ci SkScalar radius = SkScalarHalf(rec.getWidth()); 80cb93a386Sopenharmony_ci if (0 == radius) { 81cb93a386Sopenharmony_ci radius = SK_Scalar1; // hairlines 82cb93a386Sopenharmony_ci } 83cb93a386Sopenharmony_ci if (SkPaint::kMiter_Join == rec.getJoin()) { 84cb93a386Sopenharmony_ci radius *= rec.getMiter(); 85cb93a386Sopenharmony_ci } 86cb93a386Sopenharmony_ci rect->outset(radius, radius); 87cb93a386Sopenharmony_ci} 88cb93a386Sopenharmony_ci 89cb93a386Sopenharmony_ci// If line is zero-length, bump out the end by a tiny amount 90cb93a386Sopenharmony_ci// to draw endcaps. The bump factor is sized so that 91cb93a386Sopenharmony_ci// SkPoint::Distance() computes a non-zero length. 92cb93a386Sopenharmony_ci// Offsets SK_ScalarNearlyZero or smaller create empty paths when Iter measures length. 93cb93a386Sopenharmony_ci// Large values are scaled by SK_ScalarNearlyZero so significant bits change. 94cb93a386Sopenharmony_cistatic void adjust_zero_length_line(SkPoint pts[2]) { 95cb93a386Sopenharmony_ci SkASSERT(pts[0] == pts[1]); 96cb93a386Sopenharmony_ci pts[1].fX += std::max(1.001f, pts[1].fX) * SK_ScalarNearlyZero; 97cb93a386Sopenharmony_ci} 98cb93a386Sopenharmony_ci 99cb93a386Sopenharmony_cistatic bool clip_line(SkPoint pts[2], const SkRect& bounds, SkScalar intervalLength, 100cb93a386Sopenharmony_ci SkScalar priorPhase) { 101cb93a386Sopenharmony_ci SkVector dxy = pts[1] - pts[0]; 102cb93a386Sopenharmony_ci 103cb93a386Sopenharmony_ci // only horizontal or vertical lines 104cb93a386Sopenharmony_ci if (dxy.fX && dxy.fY) { 105cb93a386Sopenharmony_ci return false; 106cb93a386Sopenharmony_ci } 107cb93a386Sopenharmony_ci int xyOffset = SkToBool(dxy.fY); // 0 to adjust horizontal, 1 to adjust vertical 108cb93a386Sopenharmony_ci 109cb93a386Sopenharmony_ci SkScalar minXY = (&pts[0].fX)[xyOffset]; 110cb93a386Sopenharmony_ci SkScalar maxXY = (&pts[1].fX)[xyOffset]; 111cb93a386Sopenharmony_ci bool swapped = maxXY < minXY; 112cb93a386Sopenharmony_ci if (swapped) { 113cb93a386Sopenharmony_ci using std::swap; 114cb93a386Sopenharmony_ci swap(minXY, maxXY); 115cb93a386Sopenharmony_ci } 116cb93a386Sopenharmony_ci 117cb93a386Sopenharmony_ci SkASSERT(minXY <= maxXY); 118cb93a386Sopenharmony_ci SkScalar leftTop = (&bounds.fLeft)[xyOffset]; 119cb93a386Sopenharmony_ci SkScalar rightBottom = (&bounds.fRight)[xyOffset]; 120cb93a386Sopenharmony_ci if (maxXY < leftTop || minXY > rightBottom) { 121cb93a386Sopenharmony_ci return false; 122cb93a386Sopenharmony_ci } 123cb93a386Sopenharmony_ci 124cb93a386Sopenharmony_ci // Now we actually perform the chop, removing the excess to the left/top and 125cb93a386Sopenharmony_ci // right/bottom of the bounds (keeping our new line "in phase" with the dash, 126cb93a386Sopenharmony_ci // hence the (mod intervalLength). 127cb93a386Sopenharmony_ci 128cb93a386Sopenharmony_ci if (minXY < leftTop) { 129cb93a386Sopenharmony_ci minXY = leftTop - SkScalarMod(leftTop - minXY, intervalLength); 130cb93a386Sopenharmony_ci if (!swapped) { 131cb93a386Sopenharmony_ci minXY -= priorPhase; // for rectangles, adjust by prior phase 132cb93a386Sopenharmony_ci } 133cb93a386Sopenharmony_ci } 134cb93a386Sopenharmony_ci if (maxXY > rightBottom) { 135cb93a386Sopenharmony_ci maxXY = rightBottom + SkScalarMod(maxXY - rightBottom, intervalLength); 136cb93a386Sopenharmony_ci if (swapped) { 137cb93a386Sopenharmony_ci maxXY += priorPhase; // for rectangles, adjust by prior phase 138cb93a386Sopenharmony_ci } 139cb93a386Sopenharmony_ci } 140cb93a386Sopenharmony_ci 141cb93a386Sopenharmony_ci SkASSERT(maxXY >= minXY); 142cb93a386Sopenharmony_ci if (swapped) { 143cb93a386Sopenharmony_ci using std::swap; 144cb93a386Sopenharmony_ci swap(minXY, maxXY); 145cb93a386Sopenharmony_ci } 146cb93a386Sopenharmony_ci (&pts[0].fX)[xyOffset] = minXY; 147cb93a386Sopenharmony_ci (&pts[1].fX)[xyOffset] = maxXY; 148cb93a386Sopenharmony_ci 149cb93a386Sopenharmony_ci if (minXY == maxXY) { 150cb93a386Sopenharmony_ci adjust_zero_length_line(pts); 151cb93a386Sopenharmony_ci } 152cb93a386Sopenharmony_ci return true; 153cb93a386Sopenharmony_ci} 154cb93a386Sopenharmony_ci 155cb93a386Sopenharmony_ci// Handles only lines and rects. 156cb93a386Sopenharmony_ci// If cull_path() returns true, dstPath is the new smaller path, 157cb93a386Sopenharmony_ci// otherwise dstPath may have been changed but you should ignore it. 158cb93a386Sopenharmony_cistatic bool cull_path(const SkPath& srcPath, const SkStrokeRec& rec, 159cb93a386Sopenharmony_ci const SkRect* cullRect, SkScalar intervalLength, SkPath* dstPath) { 160cb93a386Sopenharmony_ci if (!cullRect) { 161cb93a386Sopenharmony_ci SkPoint pts[2]; 162cb93a386Sopenharmony_ci if (srcPath.isLine(pts) && pts[0] == pts[1]) { 163cb93a386Sopenharmony_ci adjust_zero_length_line(pts); 164cb93a386Sopenharmony_ci dstPath->moveTo(pts[0]); 165cb93a386Sopenharmony_ci dstPath->lineTo(pts[1]); 166cb93a386Sopenharmony_ci return true; 167cb93a386Sopenharmony_ci } 168cb93a386Sopenharmony_ci return false; 169cb93a386Sopenharmony_ci } 170cb93a386Sopenharmony_ci 171cb93a386Sopenharmony_ci SkRect bounds; 172cb93a386Sopenharmony_ci bounds = *cullRect; 173cb93a386Sopenharmony_ci outset_for_stroke(&bounds, rec); 174cb93a386Sopenharmony_ci 175cb93a386Sopenharmony_ci { 176cb93a386Sopenharmony_ci SkPoint pts[2]; 177cb93a386Sopenharmony_ci if (srcPath.isLine(pts)) { 178cb93a386Sopenharmony_ci if (clip_line(pts, bounds, intervalLength, 0)) { 179cb93a386Sopenharmony_ci dstPath->moveTo(pts[0]); 180cb93a386Sopenharmony_ci dstPath->lineTo(pts[1]); 181cb93a386Sopenharmony_ci return true; 182cb93a386Sopenharmony_ci } 183cb93a386Sopenharmony_ci return false; 184cb93a386Sopenharmony_ci } 185cb93a386Sopenharmony_ci } 186cb93a386Sopenharmony_ci 187cb93a386Sopenharmony_ci if (srcPath.isRect(nullptr)) { 188cb93a386Sopenharmony_ci // We'll break the rect into four lines, culling each separately. 189cb93a386Sopenharmony_ci SkPath::Iter iter(srcPath, false); 190cb93a386Sopenharmony_ci 191cb93a386Sopenharmony_ci SkPoint pts[4]; // Rects are all moveTo and lineTo, so we'll only use pts[0] and pts[1]. 192cb93a386Sopenharmony_ci SkAssertResult(SkPath::kMove_Verb == iter.next(pts)); 193cb93a386Sopenharmony_ci 194cb93a386Sopenharmony_ci double accum = 0; // Sum of unculled edge lengths to keep the phase correct. 195cb93a386Sopenharmony_ci // Intentionally a double to minimize the risk of overflow and drift. 196cb93a386Sopenharmony_ci while (iter.next(pts) == SkPath::kLine_Verb) { 197cb93a386Sopenharmony_ci // Notice this vector v and accum work with the original unclipped length. 198cb93a386Sopenharmony_ci SkVector v = pts[1] - pts[0]; 199cb93a386Sopenharmony_ci 200cb93a386Sopenharmony_ci if (clip_line(pts, bounds, intervalLength, std::fmod(accum, intervalLength))) { 201cb93a386Sopenharmony_ci // pts[0] may have just been changed by clip_line(). 202cb93a386Sopenharmony_ci // If that's not where we ended the previous lineTo(), we need to moveTo() there. 203cb93a386Sopenharmony_ci SkPoint last; 204cb93a386Sopenharmony_ci if (!dstPath->getLastPt(&last) || last != pts[0]) { 205cb93a386Sopenharmony_ci dstPath->moveTo(pts[0]); 206cb93a386Sopenharmony_ci } 207cb93a386Sopenharmony_ci dstPath->lineTo(pts[1]); 208cb93a386Sopenharmony_ci } 209cb93a386Sopenharmony_ci 210cb93a386Sopenharmony_ci // We either just traveled v.fX horizontally or v.fY vertically. 211cb93a386Sopenharmony_ci SkASSERT(v.fX == 0 || v.fY == 0); 212cb93a386Sopenharmony_ci accum += SkScalarAbs(v.fX + v.fY); 213cb93a386Sopenharmony_ci } 214cb93a386Sopenharmony_ci return !dstPath->isEmpty(); 215cb93a386Sopenharmony_ci } 216cb93a386Sopenharmony_ci 217cb93a386Sopenharmony_ci return false; 218cb93a386Sopenharmony_ci} 219cb93a386Sopenharmony_ci 220cb93a386Sopenharmony_ciclass SpecialLineRec { 221cb93a386Sopenharmony_cipublic: 222cb93a386Sopenharmony_ci bool init(const SkPath& src, SkPath* dst, SkStrokeRec* rec, 223cb93a386Sopenharmony_ci int intervalCount, SkScalar intervalLength) { 224cb93a386Sopenharmony_ci if (rec->isHairlineStyle() || !src.isLine(fPts)) { 225cb93a386Sopenharmony_ci return false; 226cb93a386Sopenharmony_ci } 227cb93a386Sopenharmony_ci 228cb93a386Sopenharmony_ci // can relax this in the future, if we handle square and round caps 229cb93a386Sopenharmony_ci if (SkPaint::kButt_Cap != rec->getCap()) { 230cb93a386Sopenharmony_ci return false; 231cb93a386Sopenharmony_ci } 232cb93a386Sopenharmony_ci 233cb93a386Sopenharmony_ci SkScalar pathLength = SkPoint::Distance(fPts[0], fPts[1]); 234cb93a386Sopenharmony_ci 235cb93a386Sopenharmony_ci fTangent = fPts[1] - fPts[0]; 236cb93a386Sopenharmony_ci if (fTangent.isZero()) { 237cb93a386Sopenharmony_ci return false; 238cb93a386Sopenharmony_ci } 239cb93a386Sopenharmony_ci 240cb93a386Sopenharmony_ci fPathLength = pathLength; 241cb93a386Sopenharmony_ci fTangent.scale(SkScalarInvert(pathLength)); 242cb93a386Sopenharmony_ci SkPointPriv::RotateCCW(fTangent, &fNormal); 243cb93a386Sopenharmony_ci fNormal.scale(SkScalarHalf(rec->getWidth())); 244cb93a386Sopenharmony_ci 245cb93a386Sopenharmony_ci // now estimate how many quads will be added to the path 246cb93a386Sopenharmony_ci // resulting segments = pathLen * intervalCount / intervalLen 247cb93a386Sopenharmony_ci // resulting points = 4 * segments 248cb93a386Sopenharmony_ci 249cb93a386Sopenharmony_ci SkScalar ptCount = pathLength * intervalCount / (float)intervalLength; 250cb93a386Sopenharmony_ci ptCount = std::min(ptCount, SkDashPath::kMaxDashCount); 251cb93a386Sopenharmony_ci if (SkScalarIsNaN(ptCount)) { 252cb93a386Sopenharmony_ci return false; 253cb93a386Sopenharmony_ci } 254cb93a386Sopenharmony_ci int n = SkScalarCeilToInt(ptCount) << 2; 255cb93a386Sopenharmony_ci dst->incReserve(n); 256cb93a386Sopenharmony_ci 257cb93a386Sopenharmony_ci // we will take care of the stroking 258cb93a386Sopenharmony_ci rec->setFillStyle(); 259cb93a386Sopenharmony_ci return true; 260cb93a386Sopenharmony_ci } 261cb93a386Sopenharmony_ci 262cb93a386Sopenharmony_ci void addSegment(SkScalar d0, SkScalar d1, SkPath* path) const { 263cb93a386Sopenharmony_ci SkASSERT(d0 <= fPathLength); 264cb93a386Sopenharmony_ci // clamp the segment to our length 265cb93a386Sopenharmony_ci if (d1 > fPathLength) { 266cb93a386Sopenharmony_ci d1 = fPathLength; 267cb93a386Sopenharmony_ci } 268cb93a386Sopenharmony_ci 269cb93a386Sopenharmony_ci SkScalar x0 = fPts[0].fX + fTangent.fX * d0; 270cb93a386Sopenharmony_ci SkScalar x1 = fPts[0].fX + fTangent.fX * d1; 271cb93a386Sopenharmony_ci SkScalar y0 = fPts[0].fY + fTangent.fY * d0; 272cb93a386Sopenharmony_ci SkScalar y1 = fPts[0].fY + fTangent.fY * d1; 273cb93a386Sopenharmony_ci 274cb93a386Sopenharmony_ci SkPoint pts[4]; 275cb93a386Sopenharmony_ci pts[0].set(x0 + fNormal.fX, y0 + fNormal.fY); // moveTo 276cb93a386Sopenharmony_ci pts[1].set(x1 + fNormal.fX, y1 + fNormal.fY); // lineTo 277cb93a386Sopenharmony_ci pts[2].set(x1 - fNormal.fX, y1 - fNormal.fY); // lineTo 278cb93a386Sopenharmony_ci pts[3].set(x0 - fNormal.fX, y0 - fNormal.fY); // lineTo 279cb93a386Sopenharmony_ci 280cb93a386Sopenharmony_ci path->addPoly(pts, SK_ARRAY_COUNT(pts), false); 281cb93a386Sopenharmony_ci } 282cb93a386Sopenharmony_ci 283cb93a386Sopenharmony_ciprivate: 284cb93a386Sopenharmony_ci SkPoint fPts[2]; 285cb93a386Sopenharmony_ci SkVector fTangent; 286cb93a386Sopenharmony_ci SkVector fNormal; 287cb93a386Sopenharmony_ci SkScalar fPathLength; 288cb93a386Sopenharmony_ci}; 289cb93a386Sopenharmony_ci 290cb93a386Sopenharmony_ci 291cb93a386Sopenharmony_cibool SkDashPath::InternalFilter(SkPath* dst, const SkPath& src, SkStrokeRec* rec, 292cb93a386Sopenharmony_ci const SkRect* cullRect, const SkScalar aIntervals[], 293cb93a386Sopenharmony_ci int32_t count, SkScalar initialDashLength, int32_t initialDashIndex, 294cb93a386Sopenharmony_ci SkScalar intervalLength, 295cb93a386Sopenharmony_ci StrokeRecApplication strokeRecApplication) { 296cb93a386Sopenharmony_ci // we must always have an even number of intervals 297cb93a386Sopenharmony_ci SkASSERT(is_even(count)); 298cb93a386Sopenharmony_ci 299cb93a386Sopenharmony_ci // we do nothing if the src wants to be filled 300cb93a386Sopenharmony_ci SkStrokeRec::Style style = rec->getStyle(); 301cb93a386Sopenharmony_ci if (SkStrokeRec::kFill_Style == style || SkStrokeRec::kStrokeAndFill_Style == style) { 302cb93a386Sopenharmony_ci return false; 303cb93a386Sopenharmony_ci } 304cb93a386Sopenharmony_ci 305cb93a386Sopenharmony_ci const SkScalar* intervals = aIntervals; 306cb93a386Sopenharmony_ci SkScalar dashCount = 0; 307cb93a386Sopenharmony_ci int segCount = 0; 308cb93a386Sopenharmony_ci 309cb93a386Sopenharmony_ci SkPath cullPathStorage; 310cb93a386Sopenharmony_ci const SkPath* srcPtr = &src; 311cb93a386Sopenharmony_ci if (cull_path(src, *rec, cullRect, intervalLength, &cullPathStorage)) { 312cb93a386Sopenharmony_ci // if rect is closed, starts in a dash, and ends in a dash, add the initial join 313cb93a386Sopenharmony_ci // potentially a better fix is described here: bug.skia.org/7445 314cb93a386Sopenharmony_ci if (src.isRect(nullptr) && src.isLastContourClosed() && is_even(initialDashIndex)) { 315cb93a386Sopenharmony_ci SkScalar pathLength = SkPathMeasure(src, false, rec->getResScale()).getLength(); 316cb93a386Sopenharmony_ci SkScalar endPhase = SkScalarMod(pathLength + initialDashLength, intervalLength); 317cb93a386Sopenharmony_ci int index = 0; 318cb93a386Sopenharmony_ci while (endPhase > intervals[index]) { 319cb93a386Sopenharmony_ci endPhase -= intervals[index++]; 320cb93a386Sopenharmony_ci SkASSERT(index <= count); 321cb93a386Sopenharmony_ci if (index == count) { 322cb93a386Sopenharmony_ci // We have run out of intervals. endPhase "should" never get to this point, 323cb93a386Sopenharmony_ci // but it could if the subtracts underflowed. Hence we will pin it as if it 324cb93a386Sopenharmony_ci // perfectly ran through the intervals. 325cb93a386Sopenharmony_ci // See crbug.com/875494 (and skbug.com/8274) 326cb93a386Sopenharmony_ci endPhase = 0; 327cb93a386Sopenharmony_ci break; 328cb93a386Sopenharmony_ci } 329cb93a386Sopenharmony_ci } 330cb93a386Sopenharmony_ci // if dash ends inside "on", or ends at beginning of "off" 331cb93a386Sopenharmony_ci if (is_even(index) == (endPhase > 0)) { 332cb93a386Sopenharmony_ci SkPoint midPoint = src.getPoint(0); 333cb93a386Sopenharmony_ci // get vector at end of rect 334cb93a386Sopenharmony_ci int last = src.countPoints() - 1; 335cb93a386Sopenharmony_ci while (midPoint == src.getPoint(last)) { 336cb93a386Sopenharmony_ci --last; 337cb93a386Sopenharmony_ci SkASSERT(last >= 0); 338cb93a386Sopenharmony_ci } 339cb93a386Sopenharmony_ci // get vector at start of rect 340cb93a386Sopenharmony_ci int next = 1; 341cb93a386Sopenharmony_ci while (midPoint == src.getPoint(next)) { 342cb93a386Sopenharmony_ci ++next; 343cb93a386Sopenharmony_ci SkASSERT(next < last); 344cb93a386Sopenharmony_ci } 345cb93a386Sopenharmony_ci SkVector v = midPoint - src.getPoint(last); 346cb93a386Sopenharmony_ci const SkScalar kTinyOffset = SK_ScalarNearlyZero; 347cb93a386Sopenharmony_ci // scale vector to make start of tiny right angle 348cb93a386Sopenharmony_ci v *= kTinyOffset; 349cb93a386Sopenharmony_ci cullPathStorage.moveTo(midPoint - v); 350cb93a386Sopenharmony_ci cullPathStorage.lineTo(midPoint); 351cb93a386Sopenharmony_ci v = midPoint - src.getPoint(next); 352cb93a386Sopenharmony_ci // scale vector to make end of tiny right angle 353cb93a386Sopenharmony_ci v *= kTinyOffset; 354cb93a386Sopenharmony_ci cullPathStorage.lineTo(midPoint - v); 355cb93a386Sopenharmony_ci } 356cb93a386Sopenharmony_ci } 357cb93a386Sopenharmony_ci srcPtr = &cullPathStorage; 358cb93a386Sopenharmony_ci } 359cb93a386Sopenharmony_ci 360cb93a386Sopenharmony_ci SpecialLineRec lineRec; 361cb93a386Sopenharmony_ci bool specialLine = (StrokeRecApplication::kAllow == strokeRecApplication) && 362cb93a386Sopenharmony_ci lineRec.init(*srcPtr, dst, rec, count >> 1, intervalLength); 363cb93a386Sopenharmony_ci 364cb93a386Sopenharmony_ci SkPathMeasure meas(*srcPtr, false, rec->getResScale()); 365cb93a386Sopenharmony_ci 366cb93a386Sopenharmony_ci do { 367cb93a386Sopenharmony_ci bool skipFirstSegment = meas.isClosed(); 368cb93a386Sopenharmony_ci bool addedSegment = false; 369cb93a386Sopenharmony_ci SkScalar length = meas.getLength(); 370cb93a386Sopenharmony_ci int index = initialDashIndex; 371cb93a386Sopenharmony_ci 372cb93a386Sopenharmony_ci // Since the path length / dash length ratio may be arbitrarily large, we can exert 373cb93a386Sopenharmony_ci // significant memory pressure while attempting to build the filtered path. To avoid this, 374cb93a386Sopenharmony_ci // we simply give up dashing beyond a certain threshold. 375cb93a386Sopenharmony_ci // 376cb93a386Sopenharmony_ci // The original bug report (http://crbug.com/165432) is based on a path yielding more than 377cb93a386Sopenharmony_ci // 90 million dash segments and crashing the memory allocator. A limit of 1 million 378cb93a386Sopenharmony_ci // segments seems reasonable: at 2 verbs per segment * 9 bytes per verb, this caps the 379cb93a386Sopenharmony_ci // maximum dash memory overhead at roughly 17MB per path. 380cb93a386Sopenharmony_ci dashCount += length * (count >> 1) / intervalLength; 381cb93a386Sopenharmony_ci if (dashCount > kMaxDashCount) { 382cb93a386Sopenharmony_ci dst->reset(); 383cb93a386Sopenharmony_ci return false; 384cb93a386Sopenharmony_ci } 385cb93a386Sopenharmony_ci 386cb93a386Sopenharmony_ci // Using double precision to avoid looping indefinitely due to single precision rounding 387cb93a386Sopenharmony_ci // (for extreme path_length/dash_length ratios). See test_infinite_dash() unittest. 388cb93a386Sopenharmony_ci double distance = 0; 389cb93a386Sopenharmony_ci double dlen = initialDashLength; 390cb93a386Sopenharmony_ci 391cb93a386Sopenharmony_ci while (distance < length) { 392cb93a386Sopenharmony_ci SkASSERT(dlen >= 0); 393cb93a386Sopenharmony_ci addedSegment = false; 394cb93a386Sopenharmony_ci if (is_even(index) && !skipFirstSegment) { 395cb93a386Sopenharmony_ci addedSegment = true; 396cb93a386Sopenharmony_ci ++segCount; 397cb93a386Sopenharmony_ci 398cb93a386Sopenharmony_ci if (specialLine) { 399cb93a386Sopenharmony_ci lineRec.addSegment(SkDoubleToScalar(distance), 400cb93a386Sopenharmony_ci SkDoubleToScalar(distance + dlen), 401cb93a386Sopenharmony_ci dst); 402cb93a386Sopenharmony_ci } else { 403cb93a386Sopenharmony_ci meas.getSegment(SkDoubleToScalar(distance), 404cb93a386Sopenharmony_ci SkDoubleToScalar(distance + dlen), 405cb93a386Sopenharmony_ci dst, true); 406cb93a386Sopenharmony_ci } 407cb93a386Sopenharmony_ci } 408cb93a386Sopenharmony_ci distance += dlen; 409cb93a386Sopenharmony_ci 410cb93a386Sopenharmony_ci // clear this so we only respect it the first time around 411cb93a386Sopenharmony_ci skipFirstSegment = false; 412cb93a386Sopenharmony_ci 413cb93a386Sopenharmony_ci // wrap around our intervals array if necessary 414cb93a386Sopenharmony_ci index += 1; 415cb93a386Sopenharmony_ci SkASSERT(index <= count); 416cb93a386Sopenharmony_ci if (index == count) { 417cb93a386Sopenharmony_ci index = 0; 418cb93a386Sopenharmony_ci } 419cb93a386Sopenharmony_ci 420cb93a386Sopenharmony_ci // fetch our next dlen 421cb93a386Sopenharmony_ci dlen = intervals[index]; 422cb93a386Sopenharmony_ci } 423cb93a386Sopenharmony_ci 424cb93a386Sopenharmony_ci // extend if we ended on a segment and we need to join up with the (skipped) initial segment 425cb93a386Sopenharmony_ci if (meas.isClosed() && is_even(initialDashIndex) && 426cb93a386Sopenharmony_ci initialDashLength >= 0) { 427cb93a386Sopenharmony_ci meas.getSegment(0, initialDashLength, dst, !addedSegment); 428cb93a386Sopenharmony_ci ++segCount; 429cb93a386Sopenharmony_ci } 430cb93a386Sopenharmony_ci } while (meas.nextContour()); 431cb93a386Sopenharmony_ci 432cb93a386Sopenharmony_ci // TODO: do we still need this? 433cb93a386Sopenharmony_ci if (segCount > 1) { 434cb93a386Sopenharmony_ci SkPathPriv::SetConvexity(*dst, SkPathConvexity::kConcave); 435cb93a386Sopenharmony_ci } 436cb93a386Sopenharmony_ci 437cb93a386Sopenharmony_ci return true; 438cb93a386Sopenharmony_ci} 439cb93a386Sopenharmony_ci 440cb93a386Sopenharmony_cibool SkDashPath::FilterDashPath(SkPath* dst, const SkPath& src, SkStrokeRec* rec, 441cb93a386Sopenharmony_ci const SkRect* cullRect, const SkPathEffect::DashInfo& info) { 442cb93a386Sopenharmony_ci if (!ValidDashPath(info.fPhase, info.fIntervals, info.fCount)) { 443cb93a386Sopenharmony_ci return false; 444cb93a386Sopenharmony_ci } 445cb93a386Sopenharmony_ci SkScalar initialDashLength = 0; 446cb93a386Sopenharmony_ci int32_t initialDashIndex = 0; 447cb93a386Sopenharmony_ci SkScalar intervalLength = 0; 448cb93a386Sopenharmony_ci CalcDashParameters(info.fPhase, info.fIntervals, info.fCount, 449cb93a386Sopenharmony_ci &initialDashLength, &initialDashIndex, &intervalLength); 450cb93a386Sopenharmony_ci return InternalFilter(dst, src, rec, cullRect, info.fIntervals, info.fCount, initialDashLength, 451cb93a386Sopenharmony_ci initialDashIndex, intervalLength); 452cb93a386Sopenharmony_ci} 453cb93a386Sopenharmony_ci 454cb93a386Sopenharmony_cibool SkDashPath::ValidDashPath(SkScalar phase, const SkScalar intervals[], int32_t count) { 455cb93a386Sopenharmony_ci if (count < 2 || !SkIsAlign2(count)) { 456cb93a386Sopenharmony_ci return false; 457cb93a386Sopenharmony_ci } 458cb93a386Sopenharmony_ci SkScalar length = 0; 459cb93a386Sopenharmony_ci for (int i = 0; i < count; i++) { 460cb93a386Sopenharmony_ci if (intervals[i] < 0) { 461cb93a386Sopenharmony_ci return false; 462cb93a386Sopenharmony_ci } 463cb93a386Sopenharmony_ci length += intervals[i]; 464cb93a386Sopenharmony_ci } 465cb93a386Sopenharmony_ci // watch out for values that might make us go out of bounds 466cb93a386Sopenharmony_ci return length > 0 && SkScalarIsFinite(phase) && SkScalarIsFinite(length); 467cb93a386Sopenharmony_ci} 468