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