1ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com/*
2ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com * Copyright 2006 The Android Open Source Project
3ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com *
4ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com * Use of this source code is governed by a BSD-style license that can be
5ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com * found in the LICENSE file.
6ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com */
7ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com
81978ce22eb488e3f05670189ea35d9dfae6a6784bsalomon#include <cmath>
994e75ee46a569cbcdf61fb7f04ee3a69d3ca0896djsollen@google.com#include "SkBuffer.h"
109aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark#include "SkCubicClipper.h"
11220f926d9d4b38a9018c922c095847bbd261f583reed#include "SkGeometry.h"
128a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com#include "SkMath.h"
13026beb52a29a620290fcfb24f1e7e9e75547b80freed#include "SkPathPriv.h"
141dfe88e00aeddf20690fd2469fd17e43f670ee3absalomon@google.com#include "SkPathRef.h"
154ed0fb768409bf97b79899c3990d8c15f5e9d784reed@google.com#include "SkRRect.h"
168a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
17a99b6ceff92183b424634f2e7276b9ea1d59e69dMike Reedstatic float poly_eval(float A, float B, float C, float t) {
18a99b6ceff92183b424634f2e7276b9ea1d59e69dMike Reed    return (A * t + B) * t + C;
19a99b6ceff92183b424634f2e7276b9ea1d59e69dMike Reed}
20a99b6ceff92183b424634f2e7276b9ea1d59e69dMike Reed
21a99b6ceff92183b424634f2e7276b9ea1d59e69dMike Reedstatic float poly_eval(float A, float B, float C, float D, float t) {
22a99b6ceff92183b424634f2e7276b9ea1d59e69dMike Reed    return ((A * t + B) * t + C) * t + D;
23a99b6ceff92183b424634f2e7276b9ea1d59e69dMike Reed}
24a99b6ceff92183b424634f2e7276b9ea1d59e69dMike Reed
258a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com////////////////////////////////////////////////////////////////////////////
268a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
273563c9ee527f524d421964b54d9b09e12ec0bf6breed@google.com/**
283563c9ee527f524d421964b54d9b09e12ec0bf6breed@google.com *  Path.bounds is defined to be the bounds of all the control points.
293563c9ee527f524d421964b54d9b09e12ec0bf6breed@google.com *  If we called bounds.join(r) we would skip r if r was empty, which breaks
303563c9ee527f524d421964b54d9b09e12ec0bf6breed@google.com *  our promise. Hence we have a custom joiner that doesn't look at emptiness
313563c9ee527f524d421964b54d9b09e12ec0bf6breed@google.com */
323563c9ee527f524d421964b54d9b09e12ec0bf6breed@google.comstatic void joinNoEmptyChecks(SkRect* dst, const SkRect& src) {
333563c9ee527f524d421964b54d9b09e12ec0bf6breed@google.com    dst->fLeft = SkMinScalar(dst->fLeft, src.fLeft);
343563c9ee527f524d421964b54d9b09e12ec0bf6breed@google.com    dst->fTop = SkMinScalar(dst->fTop, src.fTop);
353563c9ee527f524d421964b54d9b09e12ec0bf6breed@google.com    dst->fRight = SkMaxScalar(dst->fRight, src.fRight);
363563c9ee527f524d421964b54d9b09e12ec0bf6breed@google.com    dst->fBottom = SkMaxScalar(dst->fBottom, src.fBottom);
373563c9ee527f524d421964b54d9b09e12ec0bf6breed@google.com}
383563c9ee527f524d421964b54d9b09e12ec0bf6breed@google.com
39fb6f0f6f18cd515af85bb71e688e8530edd92827bsalomon@google.comstatic bool is_degenerate(const SkPath& path) {
40fb6f0f6f18cd515af85bb71e688e8530edd92827bsalomon@google.com    SkPath::Iter iter(path, false);
41fb6f0f6f18cd515af85bb71e688e8530edd92827bsalomon@google.com    SkPoint pts[4];
42fb6f0f6f18cd515af85bb71e688e8530edd92827bsalomon@google.com    return SkPath::kDone_Verb == iter.next(pts);
43fb6f0f6f18cd515af85bb71e688e8530edd92827bsalomon@google.com}
44fb6f0f6f18cd515af85bb71e688e8530edd92827bsalomon@google.com
4530c174b9ce6b9777ee50ae0d0565a01b2a060f01bsalomon@google.comclass SkAutoDisableDirectionCheck {
4630c174b9ce6b9777ee50ae0d0565a01b2a060f01bsalomon@google.compublic:
4730c174b9ce6b9777ee50ae0d0565a01b2a060f01bsalomon@google.com    SkAutoDisableDirectionCheck(SkPath* path) : fPath(path) {
489f4dbca3290cfaca7dd17b71eb6b5b3a0ba5323eherb        fSaved = static_cast<SkPathPriv::FirstDirection>(fPath->fFirstDirection.load());
4930c174b9ce6b9777ee50ae0d0565a01b2a060f01bsalomon@google.com    }
5030c174b9ce6b9777ee50ae0d0565a01b2a060f01bsalomon@google.com
5130c174b9ce6b9777ee50ae0d0565a01b2a060f01bsalomon@google.com    ~SkAutoDisableDirectionCheck() {
52026beb52a29a620290fcfb24f1e7e9e75547b80freed        fPath->fFirstDirection = fSaved;
5330c174b9ce6b9777ee50ae0d0565a01b2a060f01bsalomon@google.com    }
5430c174b9ce6b9777ee50ae0d0565a01b2a060f01bsalomon@google.com
5530c174b9ce6b9777ee50ae0d0565a01b2a060f01bsalomon@google.comprivate:
56026beb52a29a620290fcfb24f1e7e9e75547b80freed    SkPath*                     fPath;
57026beb52a29a620290fcfb24f1e7e9e75547b80freed    SkPathPriv::FirstDirection  fSaved;
5830c174b9ce6b9777ee50ae0d0565a01b2a060f01bsalomon@google.com};
59e61a86cfa00ea393ecc4a71fca94e1d476a37ecccommit-bot@chromium.org#define SkAutoDisableDirectionCheck(...) SK_REQUIRE_LOCAL_VAR(SkAutoDisableDirectionCheck)
6030c174b9ce6b9777ee50ae0d0565a01b2a060f01bsalomon@google.com
618a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com/*  This guy's constructor/destructor bracket a path editing operation. It is
628a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    used when we know the bounds of the amount we are going to add to the path
638a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    (usually a new contour, but not required).
64abf15c189b6333aa77a6c3f566cc67f4634ba338reed@google.com
658a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    It captures some state about the path up front (i.e. if it already has a
66ca0c8389e2fd1c7f528869beb77a6c8587d59f29robertphillips@google.com    cached bounds), and then if it can, it updates the cache bounds explicitly,
67d252db03d9650013b545ef9781fe993c07f8f314reed@android.com    avoiding the need to revisit all of the points in getBounds().
68abf15c189b6333aa77a6c3f566cc67f4634ba338reed@google.com
69fb6f0f6f18cd515af85bb71e688e8530edd92827bsalomon@google.com    It also notes if the path was originally degenerate, and if so, sets
70fb6f0f6f18cd515af85bb71e688e8530edd92827bsalomon@google.com    isConvex to true. Thus it can only be used if the contour being added is
71466310dbd3073add2ec934e336c30deaaf702eaerobertphillips@google.com    convex.
728a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com */
738a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.comclass SkAutoPathBoundsUpdate {
748a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.compublic:
758a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    SkAutoPathBoundsUpdate(SkPath* path, const SkRect& r) : fRect(r) {
768a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        this->init(path);
778a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    }
788a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
798a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    SkAutoPathBoundsUpdate(SkPath* path, SkScalar left, SkScalar top,
808a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com                           SkScalar right, SkScalar bottom) {
818a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        fRect.set(left, top, right, bottom);
828a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        this->init(path);
838a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    }
84abf15c189b6333aa77a6c3f566cc67f4634ba338reed@google.com
858a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    ~SkAutoPathBoundsUpdate() {
864469938e92d779dff05e745559e67907bbf21e78reed@google.com        fPath->setConvexity(fDegenerate ? SkPath::kConvex_Convexity
874469938e92d779dff05e745559e67907bbf21e78reed@google.com                                        : SkPath::kUnknown_Convexity);
88ca0c8389e2fd1c7f528869beb77a6c8587d59f29robertphillips@google.com        if (fEmpty || fHasValidBounds) {
89ca0c8389e2fd1c7f528869beb77a6c8587d59f29robertphillips@google.com            fPath->setBounds(fRect);
908a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        }
918a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    }
92abf15c189b6333aa77a6c3f566cc67f4634ba338reed@google.com
938a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.comprivate:
946b82d1adc6a4726e36674e468ff1157e0b75373freed@android.com    SkPath* fPath;
956b82d1adc6a4726e36674e468ff1157e0b75373freed@android.com    SkRect  fRect;
96ca0c8389e2fd1c7f528869beb77a6c8587d59f29robertphillips@google.com    bool    fHasValidBounds;
97fb6f0f6f18cd515af85bb71e688e8530edd92827bsalomon@google.com    bool    fDegenerate;
986b82d1adc6a4726e36674e468ff1157e0b75373freed@android.com    bool    fEmpty;
99abf15c189b6333aa77a6c3f566cc67f4634ba338reed@google.com
1006b82d1adc6a4726e36674e468ff1157e0b75373freed@android.com    void init(SkPath* path) {
101ca0c8389e2fd1c7f528869beb77a6c8587d59f29robertphillips@google.com        // Cannot use fRect for our bounds unless we know it is sorted
102ca0c8389e2fd1c7f528869beb77a6c8587d59f29robertphillips@google.com        fRect.sort();
1038a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        fPath = path;
104a8790debaab6c5e3b6a4a51d2cc91ae5aea9b2ddreed@google.com        // Mark the path's bounds as dirty if (1) they are, or (2) the path
105a8790debaab6c5e3b6a4a51d2cc91ae5aea9b2ddreed@google.com        // is non-finite, and therefore its bounds are not meaningful
106ca0c8389e2fd1c7f528869beb77a6c8587d59f29robertphillips@google.com        fHasValidBounds = path->hasComputedBounds() && path->isFinite();
1078a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        fEmpty = path->isEmpty();
108ca0c8389e2fd1c7f528869beb77a6c8587d59f29robertphillips@google.com        if (fHasValidBounds && !fEmpty) {
109ca0c8389e2fd1c7f528869beb77a6c8587d59f29robertphillips@google.com            joinNoEmptyChecks(&fRect, fPath->getBounds());
110ca0c8389e2fd1c7f528869beb77a6c8587d59f29robertphillips@google.com        }
111ca0c8389e2fd1c7f528869beb77a6c8587d59f29robertphillips@google.com        fDegenerate = is_degenerate(*path);
1128a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    }
1138a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com};
114e61a86cfa00ea393ecc4a71fca94e1d476a37ecccommit-bot@chromium.org#define SkAutoPathBoundsUpdate(...) SK_REQUIRE_LOCAL_VAR(SkAutoPathBoundsUpdate)
1158a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
1168a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com////////////////////////////////////////////////////////////////////////////
1178a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
1188a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com/*
1198a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    Stores the verbs and points as they are given to us, with exceptions:
1204da06ab3351f2a96f9216d96106db33a77b19644schenney@chromium.org    - we only record "Close" if it was immediately preceeded by Move | Line | Quad | Cubic
1218a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    - we insert a Move(0,0) if Line | Quad | Cubic is our first command
1228a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
1238a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    The iterator does more cleanup, especially if forceClose == true
1244da06ab3351f2a96f9216d96106db33a77b19644schenney@chromium.org    1. If we encounter degenerate segments, remove them
1254da06ab3351f2a96f9216d96106db33a77b19644schenney@chromium.org    2. if we encounter Close, return a cons'd up Line() first (if the curr-pt != start-pt)
1264da06ab3351f2a96f9216d96106db33a77b19644schenney@chromium.org    3. if we encounter Move without a preceeding Close, and forceClose is true, goto #2
1274da06ab3351f2a96f9216d96106db33a77b19644schenney@chromium.org    4. if we encounter Line | Quad | Cubic after Close, cons up a Move
1288a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com*/
1298a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
1308a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com////////////////////////////////////////////////////////////////////////////
1318a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
1325e1a7f2cc621d357da5c62a7bc4ef750d94b96f3commit-bot@chromium.org// flag to require a moveTo if we begin with something else, like lineTo etc.
1335e1a7f2cc621d357da5c62a7bc4ef750d94b96f3commit-bot@chromium.org#define INITIAL_LASTMOVETOINDEX_VALUE   ~0
1345e1a7f2cc621d357da5c62a7bc4ef750d94b96f3commit-bot@chromium.org
135fbfcd5602128ec010c82cb733c9cdc0a3254f9f3rmistry@google.comSkPath::SkPath()
136523cda39435256bcb3e5665f47612d661d3c6bf9djsollen    : fPathRef(SkPathRef::CreateEmpty()) {
137a5809a3e4cb356387c5201ab9c0a10edf11a01bebungeman@google.com    this->resetFields();
138b3eb687f8a89eb1eacd1afb4016401eb392f66abjvanverth    fIsVolatile = false;
139a5809a3e4cb356387c5201ab9c0a10edf11a01bebungeman@google.com}
140a5809a3e4cb356387c5201ab9c0a10edf11a01bebungeman@google.com
141a5809a3e4cb356387c5201ab9c0a10edf11a01bebungeman@google.comvoid SkPath::resetFields() {
142a5809a3e4cb356387c5201ab9c0a10edf11a01bebungeman@google.com    //fPathRef is assumed to have been emptied by the caller.
1435e1a7f2cc621d357da5c62a7bc4ef750d94b96f3commit-bot@chromium.org    fLastMoveToIndex = INITIAL_LASTMOVETOINDEX_VALUE;
144a5809a3e4cb356387c5201ab9c0a10edf11a01bebungeman@google.com    fFillType = kWinding_FillType;
14504863fa14a44ddf85acbc6268690ebc3f0d1d6dbreed@google.com    fConvexity = kUnknown_Convexity;
146026beb52a29a620290fcfb24f1e7e9e75547b80freed    fFirstDirection = SkPathPriv::kUnknown_FirstDirection;
1471ab9f737f000e530f0c7713c8fad282f39e26efecommit-bot@chromium.org
1481ab9f737f000e530f0c7713c8fad282f39e26efecommit-bot@chromium.org    // We don't touch Android's fSourcePath.  It's used to track texture garbage collection, so we
14996fcdcc219d2a0d3579719b84b28bede76efba64halcanary    // don't want to muck with it if it's been set to something non-nullptr.
1506b82d1adc6a4726e36674e468ff1157e0b75373freed@android.com}
1518a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
152a5809a3e4cb356387c5201ab9c0a10edf11a01bebungeman@google.comSkPath::SkPath(const SkPath& that)
1539c9d4a70028ef8dc33a46cfc0b22e254443effe3mtklein@google.com    : fPathRef(SkRef(that.fPathRef.get())) {
1549c9d4a70028ef8dc33a46cfc0b22e254443effe3mtklein@google.com    this->copyFields(that);
155a5809a3e4cb356387c5201ab9c0a10edf11a01bebungeman@google.com    SkDEBUGCODE(that.validate();)
1568a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com}
1578a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
1588a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.comSkPath::~SkPath() {
1598a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    SkDEBUGCODE(this->validate();)
1608a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com}
1618a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
162a5809a3e4cb356387c5201ab9c0a10edf11a01bebungeman@google.comSkPath& SkPath::operator=(const SkPath& that) {
163a5809a3e4cb356387c5201ab9c0a10edf11a01bebungeman@google.com    SkDEBUGCODE(that.validate();)
164a5809a3e4cb356387c5201ab9c0a10edf11a01bebungeman@google.com
165a5809a3e4cb356387c5201ab9c0a10edf11a01bebungeman@google.com    if (this != &that) {
166a5809a3e4cb356387c5201ab9c0a10edf11a01bebungeman@google.com        fPathRef.reset(SkRef(that.fPathRef.get()));
167a5809a3e4cb356387c5201ab9c0a10edf11a01bebungeman@google.com        this->copyFields(that);
1688a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    }
1698a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    SkDEBUGCODE(this->validate();)
1708a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    return *this;
1718a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com}
1728a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
173a5809a3e4cb356387c5201ab9c0a10edf11a01bebungeman@google.comvoid SkPath::copyFields(const SkPath& that) {
174a5809a3e4cb356387c5201ab9c0a10edf11a01bebungeman@google.com    //fPathRef is assumed to have been set by the caller.
1755e1a7f2cc621d357da5c62a7bc4ef750d94b96f3commit-bot@chromium.org    fLastMoveToIndex = that.fLastMoveToIndex;
176a5809a3e4cb356387c5201ab9c0a10edf11a01bebungeman@google.com    fFillType        = that.fFillType;
177a5809a3e4cb356387c5201ab9c0a10edf11a01bebungeman@google.com    fConvexity       = that.fConvexity;
1789f4dbca3290cfaca7dd17b71eb6b5b3a0ba5323eherb    // Simulate fFirstDirection  = that.fFirstDirection;
1799f4dbca3290cfaca7dd17b71eb6b5b3a0ba5323eherb    fFirstDirection.store(that.fFirstDirection.load());
180b3eb687f8a89eb1eacd1afb4016401eb392f66abjvanverth    fIsVolatile      = that.fIsVolatile;
181a5809a3e4cb356387c5201ab9c0a10edf11a01bebungeman@google.com}
182a5809a3e4cb356387c5201ab9c0a10edf11a01bebungeman@google.com
1839c1a967967e385a2c4c1607b8f997e72a18205bedjsollen@google.combool operator==(const SkPath& a, const SkPath& b) {
1846b82d1adc6a4726e36674e468ff1157e0b75373freed@android.com    // note: don't need to look at isConvex or bounds, since just comparing the
1856b82d1adc6a4726e36674e468ff1157e0b75373freed@android.com    // raw data is sufficient.
1863abec1d7c38e9bd786fc6057f9608f3eeec98c86reed@android.com    return &a == &b ||
1876b8dbb668f1f069270d35a47cfe98decd059c625robertphillips@google.com        (a.fFillType == b.fFillType && *a.fPathRef.get() == *b.fPathRef.get());
1883abec1d7c38e9bd786fc6057f9608f3eeec98c86reed@android.com}
1893abec1d7c38e9bd786fc6057f9608f3eeec98c86reed@android.com
190a5809a3e4cb356387c5201ab9c0a10edf11a01bebungeman@google.comvoid SkPath::swap(SkPath& that) {
191a5809a3e4cb356387c5201ab9c0a10edf11a01bebungeman@google.com    if (this != &that) {
19277a53de20d723ca21cc824fd97e68aaa60e022eabungeman        fPathRef.swap(that.fPathRef);
1935e1a7f2cc621d357da5c62a7bc4ef750d94b96f3commit-bot@chromium.org        SkTSwap<int>(fLastMoveToIndex, that.fLastMoveToIndex);
194a5809a3e4cb356387c5201ab9c0a10edf11a01bebungeman@google.com        SkTSwap<uint8_t>(fFillType, that.fFillType);
195a5809a3e4cb356387c5201ab9c0a10edf11a01bebungeman@google.com        SkTSwap<uint8_t>(fConvexity, that.fConvexity);
1969f4dbca3290cfaca7dd17b71eb6b5b3a0ba5323eherb        // Simulate SkTSwap<uint8_t>(fFirstDirection, that.fFirstDirection);
1979f4dbca3290cfaca7dd17b71eb6b5b3a0ba5323eherb        uint8_t temp = fFirstDirection;
1989f4dbca3290cfaca7dd17b71eb6b5b3a0ba5323eherb        fFirstDirection.store(that.fFirstDirection.load());
1999f4dbca3290cfaca7dd17b71eb6b5b3a0ba5323eherb        that.fFirstDirection.store(temp);
200b3eb687f8a89eb1eacd1afb4016401eb392f66abjvanverth        SkTSwap<SkBool8>(fIsVolatile, that.fIsVolatile);
2018a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    }
2028a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com}
2039bee33afbeca29f531c8455513b925f6e93da633bsalomon@google.com
2048e7b19d0f04f286ec283747ec128e9696c842858caryclarkbool SkPath::isInterpolatable(const SkPath& compare) const {
2058e7b19d0f04f286ec283747ec128e9696c842858caryclark    int count = fPathRef->countVerbs();
2068e7b19d0f04f286ec283747ec128e9696c842858caryclark    if (count != compare.fPathRef->countVerbs()) {
2078e7b19d0f04f286ec283747ec128e9696c842858caryclark        return false;
2088e7b19d0f04f286ec283747ec128e9696c842858caryclark    }
2098e7b19d0f04f286ec283747ec128e9696c842858caryclark    if (!count) {
2108e7b19d0f04f286ec283747ec128e9696c842858caryclark        return true;
2118e7b19d0f04f286ec283747ec128e9696c842858caryclark    }
2128e7b19d0f04f286ec283747ec128e9696c842858caryclark    if (memcmp(fPathRef->verbsMemBegin(), compare.fPathRef->verbsMemBegin(),
2138e7b19d0f04f286ec283747ec128e9696c842858caryclark               count)) {
2148e7b19d0f04f286ec283747ec128e9696c842858caryclark        return false;
2158e7b19d0f04f286ec283747ec128e9696c842858caryclark    }
216dcd1fcc6e9891e1427c8ce4500edac45a8687fbdcaryclark    return !fPathRef->countWeights() ||
217dcd1fcc6e9891e1427c8ce4500edac45a8687fbdcaryclark            !SkToBool(memcmp(fPathRef->conicWeights(), compare.fPathRef->conicWeights(),
2188e7b19d0f04f286ec283747ec128e9696c842858caryclark            fPathRef->countWeights() * sizeof(*fPathRef->conicWeights())));
2198e7b19d0f04f286ec283747ec128e9696c842858caryclark}
2208e7b19d0f04f286ec283747ec128e9696c842858caryclark
2218e7b19d0f04f286ec283747ec128e9696c842858caryclarkbool SkPath::interpolate(const SkPath& ending, SkScalar weight, SkPath* out) const {
2228e7b19d0f04f286ec283747ec128e9696c842858caryclark    int verbCount = fPathRef->countVerbs();
2238e7b19d0f04f286ec283747ec128e9696c842858caryclark    if (verbCount != ending.fPathRef->countVerbs()) {
2248e7b19d0f04f286ec283747ec128e9696c842858caryclark        return false;
2258e7b19d0f04f286ec283747ec128e9696c842858caryclark    }
226a11053891225b7c08466eef37068d8ff2dada168caryclark    if (!verbCount) {
227a11053891225b7c08466eef37068d8ff2dada168caryclark        return true;
228a11053891225b7c08466eef37068d8ff2dada168caryclark    }
2298e7b19d0f04f286ec283747ec128e9696c842858caryclark    out->reset();
2308e7b19d0f04f286ec283747ec128e9696c842858caryclark    out->addPath(*this);
2316bd5284415bd983b0628c4941dff5def40018f5abungeman    fPathRef->interpolate(*ending.fPathRef, weight, out->fPathRef.get());
2328e7b19d0f04f286ec283747ec128e9696c842858caryclark    return true;
2338e7b19d0f04f286ec283747ec128e9696c842858caryclark}
2348e7b19d0f04f286ec283747ec128e9696c842858caryclark
2359bee33afbeca29f531c8455513b925f6e93da633bsalomon@google.comstatic inline bool check_edge_against_rect(const SkPoint& p0,
2369bee33afbeca29f531c8455513b925f6e93da633bsalomon@google.com                                           const SkPoint& p1,
2379bee33afbeca29f531c8455513b925f6e93da633bsalomon@google.com                                           const SkRect& rect,
238026beb52a29a620290fcfb24f1e7e9e75547b80freed                                           SkPathPriv::FirstDirection dir) {
2399bee33afbeca29f531c8455513b925f6e93da633bsalomon@google.com    const SkPoint* edgeBegin;
2409bee33afbeca29f531c8455513b925f6e93da633bsalomon@google.com    SkVector v;
241026beb52a29a620290fcfb24f1e7e9e75547b80freed    if (SkPathPriv::kCW_FirstDirection == dir) {
2429bee33afbeca29f531c8455513b925f6e93da633bsalomon@google.com        v = p1 - p0;
2439bee33afbeca29f531c8455513b925f6e93da633bsalomon@google.com        edgeBegin = &p0;
2449bee33afbeca29f531c8455513b925f6e93da633bsalomon@google.com    } else {
2459bee33afbeca29f531c8455513b925f6e93da633bsalomon@google.com        v = p0 - p1;
2469bee33afbeca29f531c8455513b925f6e93da633bsalomon@google.com        edgeBegin = &p1;
2479bee33afbeca29f531c8455513b925f6e93da633bsalomon@google.com    }
2489bee33afbeca29f531c8455513b925f6e93da633bsalomon@google.com    if (v.fX || v.fY) {
2499bee33afbeca29f531c8455513b925f6e93da633bsalomon@google.com        // check the cross product of v with the vec from edgeBegin to each rect corner
250a99b6ceff92183b424634f2e7276b9ea1d59e69dMike Reed        SkScalar yL = v.fY * (rect.fLeft - edgeBegin->fX);
251a99b6ceff92183b424634f2e7276b9ea1d59e69dMike Reed        SkScalar xT = v.fX * (rect.fTop - edgeBegin->fY);
252a99b6ceff92183b424634f2e7276b9ea1d59e69dMike Reed        SkScalar yR = v.fY * (rect.fRight - edgeBegin->fX);
253a99b6ceff92183b424634f2e7276b9ea1d59e69dMike Reed        SkScalar xB = v.fX * (rect.fBottom - edgeBegin->fY);
2549bee33afbeca29f531c8455513b925f6e93da633bsalomon@google.com        if ((xT < yL) || (xT < yR) || (xB < yL) || (xB < yR)) {
2559bee33afbeca29f531c8455513b925f6e93da633bsalomon@google.com            return false;
2569bee33afbeca29f531c8455513b925f6e93da633bsalomon@google.com        }
2579bee33afbeca29f531c8455513b925f6e93da633bsalomon@google.com    }
2589bee33afbeca29f531c8455513b925f6e93da633bsalomon@google.com    return true;
2599bee33afbeca29f531c8455513b925f6e93da633bsalomon@google.com}
2609bee33afbeca29f531c8455513b925f6e93da633bsalomon@google.com
2619bee33afbeca29f531c8455513b925f6e93da633bsalomon@google.combool SkPath::conservativelyContainsRect(const SkRect& rect) const {
2629bee33afbeca29f531c8455513b925f6e93da633bsalomon@google.com    // This only handles non-degenerate convex paths currently.
2639bee33afbeca29f531c8455513b925f6e93da633bsalomon@google.com    if (kConvex_Convexity != this->getConvexity()) {
2649bee33afbeca29f531c8455513b925f6e93da633bsalomon@google.com        return false;
2659bee33afbeca29f531c8455513b925f6e93da633bsalomon@google.com    }
266cec8de68217186d0f5676a696de44343aaa61de7skia.committer@gmail.com
267026beb52a29a620290fcfb24f1e7e9e75547b80freed    SkPathPriv::FirstDirection direction;
268026beb52a29a620290fcfb24f1e7e9e75547b80freed    if (!SkPathPriv::CheapComputeFirstDirection(*this, &direction)) {
2699bee33afbeca29f531c8455513b925f6e93da633bsalomon@google.com        return false;
2709bee33afbeca29f531c8455513b925f6e93da633bsalomon@google.com    }
2719bee33afbeca29f531c8455513b925f6e93da633bsalomon@google.com
2729bee33afbeca29f531c8455513b925f6e93da633bsalomon@google.com    SkPoint firstPt;
2739bee33afbeca29f531c8455513b925f6e93da633bsalomon@google.com    SkPoint prevPt;
274a19f024953f8b85b5f5fbda759d74c75514ea515Lee Salzman    SkPath::Iter iter(*this, true);
2759bee33afbeca29f531c8455513b925f6e93da633bsalomon@google.com    SkPath::Verb verb;
2769bee33afbeca29f531c8455513b925f6e93da633bsalomon@google.com    SkPoint pts[4];
2779bee33afbeca29f531c8455513b925f6e93da633bsalomon@google.com    SkDEBUGCODE(int moveCnt = 0;)
27862df526042f8a753c6817ba9d809ff8dfc412d4acommit-bot@chromium.org    SkDEBUGCODE(int segmentCount = 0;)
27962df526042f8a753c6817ba9d809ff8dfc412d4acommit-bot@chromium.org    SkDEBUGCODE(int closeCount = 0;)
2809bee33afbeca29f531c8455513b925f6e93da633bsalomon@google.com
281a19f024953f8b85b5f5fbda759d74c75514ea515Lee Salzman    while ((verb = iter.next(pts, true, true)) != kDone_Verb) {
2829bee33afbeca29f531c8455513b925f6e93da633bsalomon@google.com        int nextPt = -1;
2839bee33afbeca29f531c8455513b925f6e93da633bsalomon@google.com        switch (verb) {
2849bee33afbeca29f531c8455513b925f6e93da633bsalomon@google.com            case kMove_Verb:
28562df526042f8a753c6817ba9d809ff8dfc412d4acommit-bot@chromium.org                SkASSERT(!segmentCount && !closeCount);
2869bee33afbeca29f531c8455513b925f6e93da633bsalomon@google.com                SkDEBUGCODE(++moveCnt);
2879bee33afbeca29f531c8455513b925f6e93da633bsalomon@google.com                firstPt = prevPt = pts[0];
2889bee33afbeca29f531c8455513b925f6e93da633bsalomon@google.com                break;
2899bee33afbeca29f531c8455513b925f6e93da633bsalomon@google.com            case kLine_Verb:
2909bee33afbeca29f531c8455513b925f6e93da633bsalomon@google.com                nextPt = 1;
29162df526042f8a753c6817ba9d809ff8dfc412d4acommit-bot@chromium.org                SkASSERT(moveCnt && !closeCount);
29262df526042f8a753c6817ba9d809ff8dfc412d4acommit-bot@chromium.org                SkDEBUGCODE(++segmentCount);
2939bee33afbeca29f531c8455513b925f6e93da633bsalomon@google.com                break;
2949bee33afbeca29f531c8455513b925f6e93da633bsalomon@google.com            case kQuad_Verb:
295277c3f87656c44e0a651ed0dd56efa16c0ab07b4reed@google.com            case kConic_Verb:
29662df526042f8a753c6817ba9d809ff8dfc412d4acommit-bot@chromium.org                SkASSERT(moveCnt && !closeCount);
29762df526042f8a753c6817ba9d809ff8dfc412d4acommit-bot@chromium.org                SkDEBUGCODE(++segmentCount);
2989bee33afbeca29f531c8455513b925f6e93da633bsalomon@google.com                nextPt = 2;
2999bee33afbeca29f531c8455513b925f6e93da633bsalomon@google.com                break;
3009bee33afbeca29f531c8455513b925f6e93da633bsalomon@google.com            case kCubic_Verb:
30162df526042f8a753c6817ba9d809ff8dfc412d4acommit-bot@chromium.org                SkASSERT(moveCnt && !closeCount);
30262df526042f8a753c6817ba9d809ff8dfc412d4acommit-bot@chromium.org                SkDEBUGCODE(++segmentCount);
3039bee33afbeca29f531c8455513b925f6e93da633bsalomon@google.com                nextPt = 3;
3049bee33afbeca29f531c8455513b925f6e93da633bsalomon@google.com                break;
3059bee33afbeca29f531c8455513b925f6e93da633bsalomon@google.com            case kClose_Verb:
30662df526042f8a753c6817ba9d809ff8dfc412d4acommit-bot@chromium.org                SkDEBUGCODE(++closeCount;)
3079bee33afbeca29f531c8455513b925f6e93da633bsalomon@google.com                break;
3089bee33afbeca29f531c8455513b925f6e93da633bsalomon@google.com            default:
3099bee33afbeca29f531c8455513b925f6e93da633bsalomon@google.com                SkDEBUGFAIL("unknown verb");
3109bee33afbeca29f531c8455513b925f6e93da633bsalomon@google.com        }
3119bee33afbeca29f531c8455513b925f6e93da633bsalomon@google.com        if (-1 != nextPt) {
312220f926d9d4b38a9018c922c095847bbd261f583reed            if (SkPath::kConic_Verb == verb) {
313220f926d9d4b38a9018c922c095847bbd261f583reed                SkConic orig;
314220f926d9d4b38a9018c922c095847bbd261f583reed                orig.set(pts, iter.conicWeight());
315220f926d9d4b38a9018c922c095847bbd261f583reed                SkPoint quadPts[5];
316220f926d9d4b38a9018c922c095847bbd261f583reed                int count = orig.chopIntoQuadsPOW2(quadPts, 1);
317f2b340fc885ad2a12d2d73974eff9c8f4c94192cdjsollen                SkASSERT_RELEASE(2 == count);
318220f926d9d4b38a9018c922c095847bbd261f583reed
319220f926d9d4b38a9018c922c095847bbd261f583reed                if (!check_edge_against_rect(quadPts[0], quadPts[2], rect, direction)) {
320220f926d9d4b38a9018c922c095847bbd261f583reed                    return false;
321220f926d9d4b38a9018c922c095847bbd261f583reed                }
322220f926d9d4b38a9018c922c095847bbd261f583reed                if (!check_edge_against_rect(quadPts[2], quadPts[4], rect, direction)) {
323220f926d9d4b38a9018c922c095847bbd261f583reed                    return false;
324220f926d9d4b38a9018c922c095847bbd261f583reed                }
325220f926d9d4b38a9018c922c095847bbd261f583reed            } else {
326220f926d9d4b38a9018c922c095847bbd261f583reed                if (!check_edge_against_rect(prevPt, pts[nextPt], rect, direction)) {
327220f926d9d4b38a9018c922c095847bbd261f583reed                    return false;
328220f926d9d4b38a9018c922c095847bbd261f583reed                }
3299bee33afbeca29f531c8455513b925f6e93da633bsalomon@google.com            }
3309bee33afbeca29f531c8455513b925f6e93da633bsalomon@google.com            prevPt = pts[nextPt];
3319bee33afbeca29f531c8455513b925f6e93da633bsalomon@google.com        }
3329bee33afbeca29f531c8455513b925f6e93da633bsalomon@google.com    }
3339bee33afbeca29f531c8455513b925f6e93da633bsalomon@google.com
3349bee33afbeca29f531c8455513b925f6e93da633bsalomon@google.com    return check_edge_against_rect(prevPt, firstPt, rect, direction);
3359bee33afbeca29f531c8455513b925f6e93da633bsalomon@google.com}
3368a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
3377101abe5b37d82ea222e971a42615a97a2419edbrobertphillips@google.comuint32_t SkPath::getGenerationID() const {
3381ab9f737f000e530f0c7713c8fad282f39e26efecommit-bot@chromium.org    uint32_t genID = fPathRef->genID();
339523cda39435256bcb3e5665f47612d661d3c6bf9djsollen#ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
3401ab9f737f000e530f0c7713c8fad282f39e26efecommit-bot@chromium.org    SkASSERT((unsigned)fFillType < (1 << (32 - kPathRefGenIDBitCnt)));
3411ab9f737f000e530f0c7713c8fad282f39e26efecommit-bot@chromium.org    genID |= static_cast<uint32_t>(fFillType) << kPathRefGenIDBitCnt;
3421ab9f737f000e530f0c7713c8fad282f39e26efecommit-bot@chromium.org#endif
3431ab9f737f000e530f0c7713c8fad282f39e26efecommit-bot@chromium.org    return genID;
344f5dbe2f00f853c6a1719924bdd0c33335a53423adjsollen@google.com}
345e63793a2c8d2871bf7d95195be7b93ff669688d7djsollen@google.com
3468a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.comvoid SkPath::reset() {
3478a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    SkDEBUGCODE(this->validate();)
3488a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
3491dfe88e00aeddf20690fd2469fd17e43f670ee3absalomon@google.com    fPathRef.reset(SkPathRef::CreateEmpty());
350a5809a3e4cb356387c5201ab9c0a10edf11a01bebungeman@google.com    this->resetFields();
3518a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com}
3528a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
3538a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.comvoid SkPath::rewind() {
3548a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    SkDEBUGCODE(this->validate();)
3558a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
3561dfe88e00aeddf20690fd2469fd17e43f670ee3absalomon@google.com    SkPathRef::Rewind(&fPathRef);
357a5809a3e4cb356387c5201ab9c0a10edf11a01bebungeman@google.com    this->resetFields();
3588a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com}
3598a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
360b1475b0d41efc580207183a4e25d14e920b57360fsbool SkPath::isLastContourClosed() const {
361b1475b0d41efc580207183a4e25d14e920b57360fs    int verbCount = fPathRef->countVerbs();
362b1475b0d41efc580207183a4e25d14e920b57360fs    if (0 == verbCount) {
363b1475b0d41efc580207183a4e25d14e920b57360fs        return false;
364b1475b0d41efc580207183a4e25d14e920b57360fs    }
365b1475b0d41efc580207183a4e25d14e920b57360fs    return kClose_Verb == fPathRef->atVerb(verbCount - 1);
366b1475b0d41efc580207183a4e25d14e920b57360fs}
367b1475b0d41efc580207183a4e25d14e920b57360fs
3687e6c4d16010550ee148f1c79cf088c0320fed5c1reed@google.combool SkPath::isLine(SkPoint line[2]) const {
3691dfe88e00aeddf20690fd2469fd17e43f670ee3absalomon@google.com    int verbCount = fPathRef->countVerbs();
370fbfcd5602128ec010c82cb733c9cdc0a3254f9f3rmistry@google.com
371a62efcc1e000e4b0ee4fdb3390cadd56452ce3c1commit-bot@chromium.org    if (2 == verbCount) {
372a62efcc1e000e4b0ee4fdb3390cadd56452ce3c1commit-bot@chromium.org        SkASSERT(kMove_Verb == fPathRef->atVerb(0));
373a62efcc1e000e4b0ee4fdb3390cadd56452ce3c1commit-bot@chromium.org        if (kLine_Verb == fPathRef->atVerb(1)) {
374a62efcc1e000e4b0ee4fdb3390cadd56452ce3c1commit-bot@chromium.org            SkASSERT(2 == fPathRef->countPoints());
3757e6c4d16010550ee148f1c79cf088c0320fed5c1reed@google.com            if (line) {
3761dfe88e00aeddf20690fd2469fd17e43f670ee3absalomon@google.com                const SkPoint* pts = fPathRef->points();
3777e6c4d16010550ee148f1c79cf088c0320fed5c1reed@google.com                line[0] = pts[0];
3787e6c4d16010550ee148f1c79cf088c0320fed5c1reed@google.com                line[1] = pts[1];
3797e6c4d16010550ee148f1c79cf088c0320fed5c1reed@google.com            }
3807e6c4d16010550ee148f1c79cf088c0320fed5c1reed@google.com            return true;
3817e6c4d16010550ee148f1c79cf088c0320fed5c1reed@google.com        }
3827e6c4d16010550ee148f1c79cf088c0320fed5c1reed@google.com    }
3837e6c4d16010550ee148f1c79cf088c0320fed5c1reed@google.com    return false;
3847e6c4d16010550ee148f1c79cf088c0320fed5c1reed@google.com}
3857e6c4d16010550ee148f1c79cf088c0320fed5c1reed@google.com
386f131694617ce0410eafcb01124459382576bb1d9caryclark@google.com/*
387f131694617ce0410eafcb01124459382576bb1d9caryclark@google.com Determines if path is a rect by keeping track of changes in direction
388f131694617ce0410eafcb01124459382576bb1d9caryclark@google.com and looking for a loop either clockwise or counterclockwise.
389fbfcd5602128ec010c82cb733c9cdc0a3254f9f3rmistry@google.com
390f131694617ce0410eafcb01124459382576bb1d9caryclark@google.com The direction is computed such that:
391f131694617ce0410eafcb01124459382576bb1d9caryclark@google.com  0: vertical up
392f68154a3cf43eb22d45be11f3b09e25440c366a6caryclark@google.com  1: horizontal left
393f131694617ce0410eafcb01124459382576bb1d9caryclark@google.com  2: vertical down
394f68154a3cf43eb22d45be11f3b09e25440c366a6caryclark@google.com  3: horizontal right
395fbfcd5602128ec010c82cb733c9cdc0a3254f9f3rmistry@google.com
396f131694617ce0410eafcb01124459382576bb1d9caryclark@google.comA rectangle cycles up/right/down/left or up/left/down/right.
397f131694617ce0410eafcb01124459382576bb1d9caryclark@google.com
398f131694617ce0410eafcb01124459382576bb1d9caryclark@google.comThe test fails if:
399f131694617ce0410eafcb01124459382576bb1d9caryclark@google.com  The path is closed, and followed by a line.
400f131694617ce0410eafcb01124459382576bb1d9caryclark@google.com  A second move creates a new endpoint.
401f131694617ce0410eafcb01124459382576bb1d9caryclark@google.com  A diagonal line is parsed.
402f131694617ce0410eafcb01124459382576bb1d9caryclark@google.com  There's more than four changes of direction.
403f131694617ce0410eafcb01124459382576bb1d9caryclark@google.com  There's a discontinuity on the line (e.g., a move in the middle)
404f131694617ce0410eafcb01124459382576bb1d9caryclark@google.com  The line reverses direction.
405f131694617ce0410eafcb01124459382576bb1d9caryclark@google.com  The path contains a quadratic or cubic.
406f131694617ce0410eafcb01124459382576bb1d9caryclark@google.com  The path contains fewer than four points.
40705ec2233e372cc11ad59b3337c96f0147a44ca29commit-bot@chromium.org *The rectangle doesn't complete a cycle.
40805ec2233e372cc11ad59b3337c96f0147a44ca29commit-bot@chromium.org *The final point isn't equal to the first point.
40905ec2233e372cc11ad59b3337c96f0147a44ca29commit-bot@chromium.org
41005ec2233e372cc11ad59b3337c96f0147a44ca29commit-bot@chromium.org  *These last two conditions we relax if we have a 3-edge path that would
41105ec2233e372cc11ad59b3337c96f0147a44ca29commit-bot@chromium.org   form a rectangle if it were closed (as we do when we fill a path)
412fbfcd5602128ec010c82cb733c9cdc0a3254f9f3rmistry@google.com
413f131694617ce0410eafcb01124459382576bb1d9caryclark@google.comIt's OK if the path has:
414f131694617ce0410eafcb01124459382576bb1d9caryclark@google.com  Several colinear line segments composing a rectangle side.
415f131694617ce0410eafcb01124459382576bb1d9caryclark@google.com  Single points on the rectangle side.
416fbfcd5602128ec010c82cb733c9cdc0a3254f9f3rmistry@google.com
417f131694617ce0410eafcb01124459382576bb1d9caryclark@google.comThe direction takes advantage of the corners found since opposite sides
418f131694617ce0410eafcb01124459382576bb1d9caryclark@google.commust travel in opposite directions.
419f131694617ce0410eafcb01124459382576bb1d9caryclark@google.com
420f131694617ce0410eafcb01124459382576bb1d9caryclark@google.comFIXME: Allow colinear quads and cubics to be treated like lines.
421f131694617ce0410eafcb01124459382576bb1d9caryclark@google.comFIXME: If the API passes fill-only, return true if the filled stroke
422f131694617ce0410eafcb01124459382576bb1d9caryclark@google.com       is a rectangle, though the caller failed to close the path.
42305ec2233e372cc11ad59b3337c96f0147a44ca29commit-bot@chromium.org
42405ec2233e372cc11ad59b3337c96f0147a44ca29commit-bot@chromium.org first,last,next direction state-machine:
42505ec2233e372cc11ad59b3337c96f0147a44ca29commit-bot@chromium.org    0x1 is set if the segment is horizontal
42605ec2233e372cc11ad59b3337c96f0147a44ca29commit-bot@chromium.org    0x2 is set if the segment is moving to the right or down
42705ec2233e372cc11ad59b3337c96f0147a44ca29commit-bot@chromium.org thus:
42805ec2233e372cc11ad59b3337c96f0147a44ca29commit-bot@chromium.org    two directions are opposites iff (dirA ^ dirB) == 0x2
42905ec2233e372cc11ad59b3337c96f0147a44ca29commit-bot@chromium.org    two directions are perpendicular iff (dirA ^ dirB) == 0x1
430f5e67c12c864084fa797d1e005d8472ee6f71322skia.committer@gmail.com
431f131694617ce0410eafcb01124459382576bb1d9caryclark@google.com */
43205ec2233e372cc11ad59b3337c96f0147a44ca29commit-bot@chromium.orgstatic int rect_make_dir(SkScalar dx, SkScalar dy) {
43305ec2233e372cc11ad59b3337c96f0147a44ca29commit-bot@chromium.org    return ((0 != dx) << 0) | ((dx > 0 || dy > 0) << 1);
43405ec2233e372cc11ad59b3337c96f0147a44ca29commit-bot@chromium.org}
435f68154a3cf43eb22d45be11f3b09e25440c366a6caryclark@google.combool SkPath::isRectContour(bool allowPartial, int* currVerb, const SkPoint** ptsPtr,
436f68154a3cf43eb22d45be11f3b09e25440c366a6caryclark@google.com        bool* isClosed, Direction* direction) const {
437f131694617ce0410eafcb01124459382576bb1d9caryclark@google.com    int corners = 0;
438f131694617ce0410eafcb01124459382576bb1d9caryclark@google.com    SkPoint first, last;
43956f233ab54d228f3ce05d0f7e15996424f9d5dd2caryclark@google.com    const SkPoint* pts = *ptsPtr;
44096fcdcc219d2a0d3579719b84b28bede76efba64halcanary    const SkPoint* savePts = nullptr;
4412c2508d2ede7e6a8eb43dba0ef2419905ccbb3d8tomhudson@google.com    first.set(0, 0);
4422c2508d2ede7e6a8eb43dba0ef2419905ccbb3d8tomhudson@google.com    last.set(0, 0);
4432c2508d2ede7e6a8eb43dba0ef2419905ccbb3d8tomhudson@google.com    int firstDirection = 0;
4442c2508d2ede7e6a8eb43dba0ef2419905ccbb3d8tomhudson@google.com    int lastDirection = 0;
4452c2508d2ede7e6a8eb43dba0ef2419905ccbb3d8tomhudson@google.com    int nextDirection = 0;
4462c2508d2ede7e6a8eb43dba0ef2419905ccbb3d8tomhudson@google.com    bool closedOrMoved = false;
447f131694617ce0410eafcb01124459382576bb1d9caryclark@google.com    bool autoClose = false;
44895bc5f349561fef2d6fbae71adb08cf5c2eec0c9caryclark    bool insertClose = false;
4491dfe88e00aeddf20690fd2469fd17e43f670ee3absalomon@google.com    int verbCnt = fPathRef->countVerbs();
45056f233ab54d228f3ce05d0f7e15996424f9d5dd2caryclark@google.com    while (*currVerb < verbCnt && (!allowPartial || !autoClose)) {
45195bc5f349561fef2d6fbae71adb08cf5c2eec0c9caryclark        uint8_t verb = insertClose ? (uint8_t) kClose_Verb : fPathRef->atVerb(*currVerb);
45295bc5f349561fef2d6fbae71adb08cf5c2eec0c9caryclark        switch (verb) {
453f131694617ce0410eafcb01124459382576bb1d9caryclark@google.com            case kClose_Verb:
45456f233ab54d228f3ce05d0f7e15996424f9d5dd2caryclark@google.com                savePts = pts;
45556f233ab54d228f3ce05d0f7e15996424f9d5dd2caryclark@google.com                pts = *ptsPtr;
456f131694617ce0410eafcb01124459382576bb1d9caryclark@google.com                autoClose = true;
45795bc5f349561fef2d6fbae71adb08cf5c2eec0c9caryclark                insertClose = false;
458f131694617ce0410eafcb01124459382576bb1d9caryclark@google.com            case kLine_Verb: {
459f131694617ce0410eafcb01124459382576bb1d9caryclark@google.com                SkScalar left = last.fX;
460f131694617ce0410eafcb01124459382576bb1d9caryclark@google.com                SkScalar top = last.fY;
461f131694617ce0410eafcb01124459382576bb1d9caryclark@google.com                SkScalar right = pts->fX;
462f131694617ce0410eafcb01124459382576bb1d9caryclark@google.com                SkScalar bottom = pts->fY;
463f131694617ce0410eafcb01124459382576bb1d9caryclark@google.com                ++pts;
464f131694617ce0410eafcb01124459382576bb1d9caryclark@google.com                if (left != right && top != bottom) {
465f131694617ce0410eafcb01124459382576bb1d9caryclark@google.com                    return false; // diagonal
466f131694617ce0410eafcb01124459382576bb1d9caryclark@google.com                }
467f131694617ce0410eafcb01124459382576bb1d9caryclark@google.com                if (left == right && top == bottom) {
468f131694617ce0410eafcb01124459382576bb1d9caryclark@google.com                    break; // single point on side OK
469f131694617ce0410eafcb01124459382576bb1d9caryclark@google.com                }
47005ec2233e372cc11ad59b3337c96f0147a44ca29commit-bot@chromium.org                nextDirection = rect_make_dir(right - left, bottom - top);
471f131694617ce0410eafcb01124459382576bb1d9caryclark@google.com                if (0 == corners) {
472f131694617ce0410eafcb01124459382576bb1d9caryclark@google.com                    firstDirection = nextDirection;
473f131694617ce0410eafcb01124459382576bb1d9caryclark@google.com                    first = last;
474f131694617ce0410eafcb01124459382576bb1d9caryclark@google.com                    last = pts[-1];
475f131694617ce0410eafcb01124459382576bb1d9caryclark@google.com                    corners = 1;
476f131694617ce0410eafcb01124459382576bb1d9caryclark@google.com                    closedOrMoved = false;
477f131694617ce0410eafcb01124459382576bb1d9caryclark@google.com                    break;
478f131694617ce0410eafcb01124459382576bb1d9caryclark@google.com                }
479f131694617ce0410eafcb01124459382576bb1d9caryclark@google.com                if (closedOrMoved) {
480f131694617ce0410eafcb01124459382576bb1d9caryclark@google.com                    return false; // closed followed by a line
481f131694617ce0410eafcb01124459382576bb1d9caryclark@google.com                }
482bfe90370ea68798b2b9b5ba44142db67d99555e8caryclark@google.com                if (autoClose && nextDirection == firstDirection) {
483bfe90370ea68798b2b9b5ba44142db67d99555e8caryclark@google.com                    break; // colinear with first
484bfe90370ea68798b2b9b5ba44142db67d99555e8caryclark@google.com                }
485f131694617ce0410eafcb01124459382576bb1d9caryclark@google.com                closedOrMoved = autoClose;
486f131694617ce0410eafcb01124459382576bb1d9caryclark@google.com                if (lastDirection != nextDirection) {
487f131694617ce0410eafcb01124459382576bb1d9caryclark@google.com                    if (++corners > 4) {
488f131694617ce0410eafcb01124459382576bb1d9caryclark@google.com                        return false; // too many direction changes
489f131694617ce0410eafcb01124459382576bb1d9caryclark@google.com                    }
490f131694617ce0410eafcb01124459382576bb1d9caryclark@google.com                }
491f131694617ce0410eafcb01124459382576bb1d9caryclark@google.com                last = pts[-1];
492f131694617ce0410eafcb01124459382576bb1d9caryclark@google.com                if (lastDirection == nextDirection) {
493f131694617ce0410eafcb01124459382576bb1d9caryclark@google.com                    break; // colinear segment
494f131694617ce0410eafcb01124459382576bb1d9caryclark@google.com                }
495f131694617ce0410eafcb01124459382576bb1d9caryclark@google.com                // Possible values for corners are 2, 3, and 4.
496f131694617ce0410eafcb01124459382576bb1d9caryclark@google.com                // When corners == 3, nextDirection opposes firstDirection.
497f131694617ce0410eafcb01124459382576bb1d9caryclark@google.com                // Otherwise, nextDirection at corner 2 opposes corner 4.
4982c2508d2ede7e6a8eb43dba0ef2419905ccbb3d8tomhudson@google.com                int turn = firstDirection ^ (corners - 1);
499f131694617ce0410eafcb01124459382576bb1d9caryclark@google.com                int directionCycle = 3 == corners ? 0 : nextDirection ^ turn;
500f131694617ce0410eafcb01124459382576bb1d9caryclark@google.com                if ((directionCycle ^ turn) != nextDirection) {
501f131694617ce0410eafcb01124459382576bb1d9caryclark@google.com                    return false; // direction didn't follow cycle
502f131694617ce0410eafcb01124459382576bb1d9caryclark@google.com                }
503f131694617ce0410eafcb01124459382576bb1d9caryclark@google.com                break;
504f131694617ce0410eafcb01124459382576bb1d9caryclark@google.com            }
505f131694617ce0410eafcb01124459382576bb1d9caryclark@google.com            case kQuad_Verb:
506277c3f87656c44e0a651ed0dd56efa16c0ab07b4reed@google.com            case kConic_Verb:
507f131694617ce0410eafcb01124459382576bb1d9caryclark@google.com            case kCubic_Verb:
508f131694617ce0410eafcb01124459382576bb1d9caryclark@google.com                return false; // quadratic, cubic not allowed
509f131694617ce0410eafcb01124459382576bb1d9caryclark@google.com            case kMove_Verb:
51095bc5f349561fef2d6fbae71adb08cf5c2eec0c9caryclark                if (allowPartial && !autoClose && firstDirection) {
51195bc5f349561fef2d6fbae71adb08cf5c2eec0c9caryclark                    insertClose = true;
51295bc5f349561fef2d6fbae71adb08cf5c2eec0c9caryclark                    *currVerb -= 1;  // try move again afterwards
51395bc5f349561fef2d6fbae71adb08cf5c2eec0c9caryclark                    goto addMissingClose;
51495bc5f349561fef2d6fbae71adb08cf5c2eec0c9caryclark                }
515f131694617ce0410eafcb01124459382576bb1d9caryclark@google.com                last = *pts++;
516f131694617ce0410eafcb01124459382576bb1d9caryclark@google.com                closedOrMoved = true;
517f131694617ce0410eafcb01124459382576bb1d9caryclark@google.com                break;
518277c3f87656c44e0a651ed0dd56efa16c0ab07b4reed@google.com            default:
519330313a8a8343876ee596da39da06a5d69badd9cmtklein@google.com                SkDEBUGFAIL("unexpected verb");
520277c3f87656c44e0a651ed0dd56efa16c0ab07b4reed@google.com                break;
521f131694617ce0410eafcb01124459382576bb1d9caryclark@google.com        }
52256f233ab54d228f3ce05d0f7e15996424f9d5dd2caryclark@google.com        *currVerb += 1;
523f131694617ce0410eafcb01124459382576bb1d9caryclark@google.com        lastDirection = nextDirection;
52495bc5f349561fef2d6fbae71adb08cf5c2eec0c9caryclarkaddMissingClose:
52595bc5f349561fef2d6fbae71adb08cf5c2eec0c9caryclark        ;
526f131694617ce0410eafcb01124459382576bb1d9caryclark@google.com    }
527f131694617ce0410eafcb01124459382576bb1d9caryclark@google.com    // Success if 4 corners and first point equals last
528bfe90370ea68798b2b9b5ba44142db67d99555e8caryclark@google.com    bool result = 4 == corners && (first == last || autoClose);
52905ec2233e372cc11ad59b3337c96f0147a44ca29commit-bot@chromium.org    if (!result) {
53005ec2233e372cc11ad59b3337c96f0147a44ca29commit-bot@chromium.org        // check if we are just an incomplete rectangle, in which case we can
53105ec2233e372cc11ad59b3337c96f0147a44ca29commit-bot@chromium.org        // return true, but not claim to be closed.
53205ec2233e372cc11ad59b3337c96f0147a44ca29commit-bot@chromium.org        // e.g.
53305ec2233e372cc11ad59b3337c96f0147a44ca29commit-bot@chromium.org        //    3 sided rectangle
53405ec2233e372cc11ad59b3337c96f0147a44ca29commit-bot@chromium.org        //    4 sided but the last edge is not long enough to reach the start
53505ec2233e372cc11ad59b3337c96f0147a44ca29commit-bot@chromium.org        //
53605ec2233e372cc11ad59b3337c96f0147a44ca29commit-bot@chromium.org        SkScalar closeX = first.x() - last.x();
53705ec2233e372cc11ad59b3337c96f0147a44ca29commit-bot@chromium.org        SkScalar closeY = first.y() - last.y();
53805ec2233e372cc11ad59b3337c96f0147a44ca29commit-bot@chromium.org        if (closeX && closeY) {
53905ec2233e372cc11ad59b3337c96f0147a44ca29commit-bot@chromium.org            return false;   // we're diagonal, abort (can we ever reach this?)
54005ec2233e372cc11ad59b3337c96f0147a44ca29commit-bot@chromium.org        }
54105ec2233e372cc11ad59b3337c96f0147a44ca29commit-bot@chromium.org        int closeDirection = rect_make_dir(closeX, closeY);
54205ec2233e372cc11ad59b3337c96f0147a44ca29commit-bot@chromium.org        // make sure the close-segment doesn't double-back on itself
54305ec2233e372cc11ad59b3337c96f0147a44ca29commit-bot@chromium.org        if (3 == corners || (4 == corners && closeDirection == lastDirection)) {
54405ec2233e372cc11ad59b3337c96f0147a44ca29commit-bot@chromium.org            result = true;
54505ec2233e372cc11ad59b3337c96f0147a44ca29commit-bot@chromium.org            autoClose = false;  // we are not closed
54605ec2233e372cc11ad59b3337c96f0147a44ca29commit-bot@chromium.org        }
54705ec2233e372cc11ad59b3337c96f0147a44ca29commit-bot@chromium.org    }
548bfe90370ea68798b2b9b5ba44142db67d99555e8caryclark@google.com    if (savePts) {
549bfe90370ea68798b2b9b5ba44142db67d99555e8caryclark@google.com        *ptsPtr = savePts;
550bfe90370ea68798b2b9b5ba44142db67d99555e8caryclark@google.com    }
551f68154a3cf43eb22d45be11f3b09e25440c366a6caryclark@google.com    if (result && isClosed) {
552f68154a3cf43eb22d45be11f3b09e25440c366a6caryclark@google.com        *isClosed = autoClose;
553f68154a3cf43eb22d45be11f3b09e25440c366a6caryclark@google.com    }
554f68154a3cf43eb22d45be11f3b09e25440c366a6caryclark@google.com    if (result && direction) {
55512b4e27ae1a29460e91a59f38122483e1faec697sugoi@google.com        *direction = firstDirection == ((lastDirection + 1) & 3) ? kCCW_Direction : kCW_Direction;
556f68154a3cf43eb22d45be11f3b09e25440c366a6caryclark@google.com    }
55756f233ab54d228f3ce05d0f7e15996424f9d5dd2caryclark@google.com    return result;
55856f233ab54d228f3ce05d0f7e15996424f9d5dd2caryclark@google.com}
55956f233ab54d228f3ce05d0f7e15996424f9d5dd2caryclark@google.com
5604f662e62cd44e302ef689fabdb2c0ae8d9471b02robertphillipsbool SkPath::isRect(SkRect* rect, bool* isClosed, Direction* direction) const {
56156f233ab54d228f3ce05d0f7e15996424f9d5dd2caryclark@google.com    SkDEBUGCODE(this->validate();)
56256f233ab54d228f3ce05d0f7e15996424f9d5dd2caryclark@google.com    int currVerb = 0;
56356f233ab54d228f3ce05d0f7e15996424f9d5dd2caryclark@google.com    const SkPoint* pts = fPathRef->points();
564fe7c427e3d1c2c98bce7a3fa0ae6b5864527f169robertphillips    const SkPoint* first = pts;
5654f662e62cd44e302ef689fabdb2c0ae8d9471b02robertphillips    if (!this->isRectContour(false, &currVerb, &pts, isClosed, direction)) {
566fe7c427e3d1c2c98bce7a3fa0ae6b5864527f169robertphillips        return false;
567f131694617ce0410eafcb01124459382576bb1d9caryclark@google.com    }
568fe7c427e3d1c2c98bce7a3fa0ae6b5864527f169robertphillips    if (rect) {
5694f662e62cd44e302ef689fabdb2c0ae8d9471b02robertphillips        int32_t num = SkToS32(pts - first);
5704f662e62cd44e302ef689fabdb2c0ae8d9471b02robertphillips        if (num) {
5714f662e62cd44e302ef689fabdb2c0ae8d9471b02robertphillips            rect->set(first, num);
572fe7c427e3d1c2c98bce7a3fa0ae6b5864527f169robertphillips        } else {
573fe7c427e3d1c2c98bce7a3fa0ae6b5864527f169robertphillips            // 'pts' isn't updated for open rects
574fe7c427e3d1c2c98bce7a3fa0ae6b5864527f169robertphillips            *rect = this->getBounds();
575fe7c427e3d1c2c98bce7a3fa0ae6b5864527f169robertphillips        }
576fe7c427e3d1c2c98bce7a3fa0ae6b5864527f169robertphillips    }
577fe7c427e3d1c2c98bce7a3fa0ae6b5864527f169robertphillips    return true;
5788fd160350ca5f57fbb1b2e03383c5778414a9b48robertphillips@google.com}
579020b25becb4a99061e8643780c887ad472eb0648skia.committer@gmail.com
58095bc5f349561fef2d6fbae71adb08cf5c2eec0c9caryclarkbool SkPath::isNestedFillRects(SkRect rects[2], Direction dirs[2]) const {
58156f233ab54d228f3ce05d0f7e15996424f9d5dd2caryclark@google.com    SkDEBUGCODE(this->validate();)
58256f233ab54d228f3ce05d0f7e15996424f9d5dd2caryclark@google.com    int currVerb = 0;
58356f233ab54d228f3ce05d0f7e15996424f9d5dd2caryclark@google.com    const SkPoint* pts = fPathRef->points();
58456f233ab54d228f3ce05d0f7e15996424f9d5dd2caryclark@google.com    const SkPoint* first = pts;
58583d1a68141830cbfa0d5fca6f9c9bccf9c978ad2robertphillips@google.com    Direction testDirs[2];
58696fcdcc219d2a0d3579719b84b28bede76efba64halcanary    if (!isRectContour(true, &currVerb, &pts, nullptr, &testDirs[0])) {
58756f233ab54d228f3ce05d0f7e15996424f9d5dd2caryclark@google.com        return false;
58856f233ab54d228f3ce05d0f7e15996424f9d5dd2caryclark@google.com    }
58956f233ab54d228f3ce05d0f7e15996424f9d5dd2caryclark@google.com    const SkPoint* last = pts;
59056f233ab54d228f3ce05d0f7e15996424f9d5dd2caryclark@google.com    SkRect testRects[2];
59195bc5f349561fef2d6fbae71adb08cf5c2eec0c9caryclark    bool isClosed;
59295bc5f349561fef2d6fbae71adb08cf5c2eec0c9caryclark    if (isRectContour(false, &currVerb, &pts, &isClosed, &testDirs[1])) {
593614f9e3a540b388c09eb96c1d43b8bfc6d28de81scroggo@google.com        testRects[0].set(first, SkToS32(last - first));
59495bc5f349561fef2d6fbae71adb08cf5c2eec0c9caryclark        if (!isClosed) {
59595bc5f349561fef2d6fbae71adb08cf5c2eec0c9caryclark            pts = fPathRef->points() + fPathRef->countPoints();
59695bc5f349561fef2d6fbae71adb08cf5c2eec0c9caryclark        }
597614f9e3a540b388c09eb96c1d43b8bfc6d28de81scroggo@google.com        testRects[1].set(last, SkToS32(pts - last));
59856f233ab54d228f3ce05d0f7e15996424f9d5dd2caryclark@google.com        if (testRects[0].contains(testRects[1])) {
59956f233ab54d228f3ce05d0f7e15996424f9d5dd2caryclark@google.com            if (rects) {
60056f233ab54d228f3ce05d0f7e15996424f9d5dd2caryclark@google.com                rects[0] = testRects[0];
60156f233ab54d228f3ce05d0f7e15996424f9d5dd2caryclark@google.com                rects[1] = testRects[1];
60256f233ab54d228f3ce05d0f7e15996424f9d5dd2caryclark@google.com            }
60383d1a68141830cbfa0d5fca6f9c9bccf9c978ad2robertphillips@google.com            if (dirs) {
60483d1a68141830cbfa0d5fca6f9c9bccf9c978ad2robertphillips@google.com                dirs[0] = testDirs[0];
60583d1a68141830cbfa0d5fca6f9c9bccf9c978ad2robertphillips@google.com                dirs[1] = testDirs[1];
60683d1a68141830cbfa0d5fca6f9c9bccf9c978ad2robertphillips@google.com            }
60756f233ab54d228f3ce05d0f7e15996424f9d5dd2caryclark@google.com            return true;
60856f233ab54d228f3ce05d0f7e15996424f9d5dd2caryclark@google.com        }
60956f233ab54d228f3ce05d0f7e15996424f9d5dd2caryclark@google.com        if (testRects[1].contains(testRects[0])) {
61056f233ab54d228f3ce05d0f7e15996424f9d5dd2caryclark@google.com            if (rects) {
61156f233ab54d228f3ce05d0f7e15996424f9d5dd2caryclark@google.com                rects[0] = testRects[1];
61256f233ab54d228f3ce05d0f7e15996424f9d5dd2caryclark@google.com                rects[1] = testRects[0];
61356f233ab54d228f3ce05d0f7e15996424f9d5dd2caryclark@google.com            }
61483d1a68141830cbfa0d5fca6f9c9bccf9c978ad2robertphillips@google.com            if (dirs) {
61583d1a68141830cbfa0d5fca6f9c9bccf9c978ad2robertphillips@google.com                dirs[0] = testDirs[1];
61683d1a68141830cbfa0d5fca6f9c9bccf9c978ad2robertphillips@google.com                dirs[1] = testDirs[0];
61783d1a68141830cbfa0d5fca6f9c9bccf9c978ad2robertphillips@google.com            }
61856f233ab54d228f3ce05d0f7e15996424f9d5dd2caryclark@google.com            return true;
61956f233ab54d228f3ce05d0f7e15996424f9d5dd2caryclark@google.com        }
62056f233ab54d228f3ce05d0f7e15996424f9d5dd2caryclark@google.com    }
62156f233ab54d228f3ce05d0f7e15996424f9d5dd2caryclark@google.com    return false;
62256f233ab54d228f3ce05d0f7e15996424f9d5dd2caryclark@google.com}
62356f233ab54d228f3ce05d0f7e15996424f9d5dd2caryclark@google.com
6241dfe88e00aeddf20690fd2469fd17e43f670ee3absalomon@google.comint SkPath::countPoints() const {
6251dfe88e00aeddf20690fd2469fd17e43f670ee3absalomon@google.com    return fPathRef->countPoints();
6261dfe88e00aeddf20690fd2469fd17e43f670ee3absalomon@google.com}
6271dfe88e00aeddf20690fd2469fd17e43f670ee3absalomon@google.com
628df9d656c352928f995abce0a62c4ec3255232a45bsalomon@google.comint SkPath::getPoints(SkPoint dst[], int max) const {
6298a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    SkDEBUGCODE(this->validate();)
6308a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
6318a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    SkASSERT(max >= 0);
632df9d656c352928f995abce0a62c4ec3255232a45bsalomon@google.com    SkASSERT(!max || dst);
6331dfe88e00aeddf20690fd2469fd17e43f670ee3absalomon@google.com    int count = SkMin32(max, fPathRef->countPoints());
6348bf5d1754f08a3070c953f31cf20cde27a390cd0mtklein    sk_careful_memcpy(dst, fPathRef->points(), count * sizeof(SkPoint));
6351dfe88e00aeddf20690fd2469fd17e43f670ee3absalomon@google.com    return fPathRef->countPoints();
6368a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com}
6378a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
638d3aa4ff7a564953dff9a15ff03fd42eebf64569freed@android.comSkPoint SkPath::getPoint(int index) const {
6391dfe88e00aeddf20690fd2469fd17e43f670ee3absalomon@google.com    if ((unsigned)index < (unsigned)fPathRef->countPoints()) {
6401dfe88e00aeddf20690fd2469fd17e43f670ee3absalomon@google.com        return fPathRef->atPoint(index);
641d3aa4ff7a564953dff9a15ff03fd42eebf64569freed@android.com    }
642d3aa4ff7a564953dff9a15ff03fd42eebf64569freed@android.com    return SkPoint::Make(0, 0);
643d3aa4ff7a564953dff9a15ff03fd42eebf64569freed@android.com}
644d3aa4ff7a564953dff9a15ff03fd42eebf64569freed@android.com
6451dfe88e00aeddf20690fd2469fd17e43f670ee3absalomon@google.comint SkPath::countVerbs() const {
6461dfe88e00aeddf20690fd2469fd17e43f670ee3absalomon@google.com    return fPathRef->countVerbs();
6471dfe88e00aeddf20690fd2469fd17e43f670ee3absalomon@google.com}
6481dfe88e00aeddf20690fd2469fd17e43f670ee3absalomon@google.com
6491dfe88e00aeddf20690fd2469fd17e43f670ee3absalomon@google.comstatic inline void copy_verbs_reverse(uint8_t* inorderDst,
6501dfe88e00aeddf20690fd2469fd17e43f670ee3absalomon@google.com                                      const uint8_t* reversedSrc,
6511dfe88e00aeddf20690fd2469fd17e43f670ee3absalomon@google.com                                      int count) {
6521dfe88e00aeddf20690fd2469fd17e43f670ee3absalomon@google.com    for (int i = 0; i < count; ++i) {
6531dfe88e00aeddf20690fd2469fd17e43f670ee3absalomon@google.com        inorderDst[i] = reversedSrc[~i];
6541dfe88e00aeddf20690fd2469fd17e43f670ee3absalomon@google.com    }
6551dfe88e00aeddf20690fd2469fd17e43f670ee3absalomon@google.com}
6561dfe88e00aeddf20690fd2469fd17e43f670ee3absalomon@google.com
657df9d656c352928f995abce0a62c4ec3255232a45bsalomon@google.comint SkPath::getVerbs(uint8_t dst[], int max) const {
658df9d656c352928f995abce0a62c4ec3255232a45bsalomon@google.com    SkDEBUGCODE(this->validate();)
659df9d656c352928f995abce0a62c4ec3255232a45bsalomon@google.com
660df9d656c352928f995abce0a62c4ec3255232a45bsalomon@google.com    SkASSERT(max >= 0);
661df9d656c352928f995abce0a62c4ec3255232a45bsalomon@google.com    SkASSERT(!max || dst);
6621dfe88e00aeddf20690fd2469fd17e43f670ee3absalomon@google.com    int count = SkMin32(max, fPathRef->countVerbs());
6631dfe88e00aeddf20690fd2469fd17e43f670ee3absalomon@google.com    copy_verbs_reverse(dst, fPathRef->verbs(), count);
6641dfe88e00aeddf20690fd2469fd17e43f670ee3absalomon@google.com    return fPathRef->countVerbs();
665df9d656c352928f995abce0a62c4ec3255232a45bsalomon@google.com}
666df9d656c352928f995abce0a62c4ec3255232a45bsalomon@google.com
667294dd7b3d7b55ba38881cd4cabb6636abda23eb9reed@google.combool SkPath::getLastPt(SkPoint* lastPt) const {
6688a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    SkDEBUGCODE(this->validate();)
6698a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
6701dfe88e00aeddf20690fd2469fd17e43f670ee3absalomon@google.com    int count = fPathRef->countPoints();
671294dd7b3d7b55ba38881cd4cabb6636abda23eb9reed@google.com    if (count > 0) {
672294dd7b3d7b55ba38881cd4cabb6636abda23eb9reed@google.com        if (lastPt) {
6731dfe88e00aeddf20690fd2469fd17e43f670ee3absalomon@google.com            *lastPt = fPathRef->atPoint(count - 1);
6748a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        }
675294dd7b3d7b55ba38881cd4cabb6636abda23eb9reed@google.com        return true;
676294dd7b3d7b55ba38881cd4cabb6636abda23eb9reed@google.com    }
677294dd7b3d7b55ba38881cd4cabb6636abda23eb9reed@google.com    if (lastPt) {
678294dd7b3d7b55ba38881cd4cabb6636abda23eb9reed@google.com        lastPt->set(0, 0);
6798a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    }
680294dd7b3d7b55ba38881cd4cabb6636abda23eb9reed@google.com    return false;
6818a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com}
6828a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
683aec251012542e971100e218bf463adbfb5d21d20caryclarkvoid SkPath::setPt(int index, SkScalar x, SkScalar y) {
684aec251012542e971100e218bf463adbfb5d21d20caryclark    SkDEBUGCODE(this->validate();)
685aec251012542e971100e218bf463adbfb5d21d20caryclark
686aec251012542e971100e218bf463adbfb5d21d20caryclark    int count = fPathRef->countPoints();
687aec251012542e971100e218bf463adbfb5d21d20caryclark    if (count <= index) {
688aec251012542e971100e218bf463adbfb5d21d20caryclark        return;
689aec251012542e971100e218bf463adbfb5d21d20caryclark    } else {
690aec251012542e971100e218bf463adbfb5d21d20caryclark        SkPathRef::Editor ed(&fPathRef);
691aec251012542e971100e218bf463adbfb5d21d20caryclark        ed.atPoint(index)->set(x, y);
692aec251012542e971100e218bf463adbfb5d21d20caryclark    }
693aec251012542e971100e218bf463adbfb5d21d20caryclark}
694aec251012542e971100e218bf463adbfb5d21d20caryclark
6958a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.comvoid SkPath::setLastPt(SkScalar x, SkScalar y) {
6968a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    SkDEBUGCODE(this->validate();)
6978a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
6981dfe88e00aeddf20690fd2469fd17e43f670ee3absalomon@google.com    int count = fPathRef->countPoints();
6998a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    if (count == 0) {
7008a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        this->moveTo(x, y);
7018a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    } else {
7021dfe88e00aeddf20690fd2469fd17e43f670ee3absalomon@google.com        SkPathRef::Editor ed(&fPathRef);
7031dfe88e00aeddf20690fd2469fd17e43f670ee3absalomon@google.com        ed.atPoint(count-1)->set(x, y);
7048a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    }
7058a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com}
7068a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
70704863fa14a44ddf85acbc6268690ebc3f0d1d6dbreed@google.comvoid SkPath::setConvexity(Convexity c) {
70804863fa14a44ddf85acbc6268690ebc3f0d1d6dbreed@google.com    if (fConvexity != c) {
70904863fa14a44ddf85acbc6268690ebc3f0d1d6dbreed@google.com        fConvexity = c;
71004863fa14a44ddf85acbc6268690ebc3f0d1d6dbreed@google.com    }
71104863fa14a44ddf85acbc6268690ebc3f0d1d6dbreed@google.com}
71204863fa14a44ddf85acbc6268690ebc3f0d1d6dbreed@google.com
7138a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com//////////////////////////////////////////////////////////////////////////////
7148a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com//  Construction methods
7158a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
716026beb52a29a620290fcfb24f1e7e9e75547b80freed#define DIRTY_AFTER_EDIT                                        \
717026beb52a29a620290fcfb24f1e7e9e75547b80freed    do {                                                        \
718026beb52a29a620290fcfb24f1e7e9e75547b80freed        fConvexity = kUnknown_Convexity;                        \
719026beb52a29a620290fcfb24f1e7e9e75547b80freed        fFirstDirection = SkPathPriv::kUnknown_FirstDirection;  \
720b54455e440e66e0b1c30954d226226f49aac26d6reed@google.com    } while (0)
721b54455e440e66e0b1c30954d226226f49aac26d6reed@google.com
7228a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.comvoid SkPath::incReserve(U16CPU inc) {
7238a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    SkDEBUGCODE(this->validate();)
7241dfe88e00aeddf20690fd2469fd17e43f670ee3absalomon@google.com    SkPathRef::Editor(&fPathRef, inc, inc);
7258a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    SkDEBUGCODE(this->validate();)
7268a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com}
7278a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
7288a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.comvoid SkPath::moveTo(SkScalar x, SkScalar y) {
7298a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    SkDEBUGCODE(this->validate();)
7308a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
7311dfe88e00aeddf20690fd2469fd17e43f670ee3absalomon@google.com    SkPathRef::Editor ed(&fPathRef);
7328a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
7335e1a7f2cc621d357da5c62a7bc4ef750d94b96f3commit-bot@chromium.org    // remember our index
7345e1a7f2cc621d357da5c62a7bc4ef750d94b96f3commit-bot@chromium.org    fLastMoveToIndex = fPathRef->countPoints();
7355e1a7f2cc621d357da5c62a7bc4ef750d94b96f3commit-bot@chromium.org
7361dfe88e00aeddf20690fd2469fd17e43f670ee3absalomon@google.com    ed.growForVerb(kMove_Verb)->set(x, y);
737b17c1291085e50819c0c46aae783067c30a67516bsalomon
738b17c1291085e50819c0c46aae783067c30a67516bsalomon    DIRTY_AFTER_EDIT;
7398a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com}
7408a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
7418a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.comvoid SkPath::rMoveTo(SkScalar x, SkScalar y) {
7428a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    SkPoint pt;
7438a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    this->getLastPt(&pt);
7448a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    this->moveTo(pt.fX + x, pt.fY + y);
7458a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com}
7468a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
7475e1a7f2cc621d357da5c62a7bc4ef750d94b96f3commit-bot@chromium.orgvoid SkPath::injectMoveToIfNeeded() {
7485e1a7f2cc621d357da5c62a7bc4ef750d94b96f3commit-bot@chromium.org    if (fLastMoveToIndex < 0) {
7495e1a7f2cc621d357da5c62a7bc4ef750d94b96f3commit-bot@chromium.org        SkScalar x, y;
7505e1a7f2cc621d357da5c62a7bc4ef750d94b96f3commit-bot@chromium.org        if (fPathRef->countVerbs() == 0) {
7515e1a7f2cc621d357da5c62a7bc4ef750d94b96f3commit-bot@chromium.org            x = y = 0;
7525e1a7f2cc621d357da5c62a7bc4ef750d94b96f3commit-bot@chromium.org        } else {
7535e1a7f2cc621d357da5c62a7bc4ef750d94b96f3commit-bot@chromium.org            const SkPoint& pt = fPathRef->atPoint(~fLastMoveToIndex);
7545e1a7f2cc621d357da5c62a7bc4ef750d94b96f3commit-bot@chromium.org            x = pt.fX;
7555e1a7f2cc621d357da5c62a7bc4ef750d94b96f3commit-bot@chromium.org            y = pt.fY;
7565e1a7f2cc621d357da5c62a7bc4ef750d94b96f3commit-bot@chromium.org        }
7575e1a7f2cc621d357da5c62a7bc4ef750d94b96f3commit-bot@chromium.org        this->moveTo(x, y);
7585e1a7f2cc621d357da5c62a7bc4ef750d94b96f3commit-bot@chromium.org    }
7595e1a7f2cc621d357da5c62a7bc4ef750d94b96f3commit-bot@chromium.org}
7605e1a7f2cc621d357da5c62a7bc4ef750d94b96f3commit-bot@chromium.org
7618a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.comvoid SkPath::lineTo(SkScalar x, SkScalar y) {
7628a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    SkDEBUGCODE(this->validate();)
7638a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
7645e1a7f2cc621d357da5c62a7bc4ef750d94b96f3commit-bot@chromium.org    this->injectMoveToIfNeeded();
7655e1a7f2cc621d357da5c62a7bc4ef750d94b96f3commit-bot@chromium.org
7661dfe88e00aeddf20690fd2469fd17e43f670ee3absalomon@google.com    SkPathRef::Editor ed(&fPathRef);
7671dfe88e00aeddf20690fd2469fd17e43f670ee3absalomon@google.com    ed.growForVerb(kLine_Verb)->set(x, y);
7688a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
769b54455e440e66e0b1c30954d226226f49aac26d6reed@google.com    DIRTY_AFTER_EDIT;
7708a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com}
7718a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
7728a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.comvoid SkPath::rLineTo(SkScalar x, SkScalar y) {
7739d54aeb8a192845f1f8122dba780d40ee6a0de1bcommit-bot@chromium.org    this->injectMoveToIfNeeded();  // This can change the result of this->getLastPt().
7748a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    SkPoint pt;
7758a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    this->getLastPt(&pt);
7768a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    this->lineTo(pt.fX + x, pt.fY + y);
7778a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com}
7788a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
7798a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.comvoid SkPath::quadTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2) {
7808a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    SkDEBUGCODE(this->validate();)
78126da7f00aedba107d4b3e382283034e265db09b6skia.committer@gmail.com
7825e1a7f2cc621d357da5c62a7bc4ef750d94b96f3commit-bot@chromium.org    this->injectMoveToIfNeeded();
7835e1a7f2cc621d357da5c62a7bc4ef750d94b96f3commit-bot@chromium.org
7841dfe88e00aeddf20690fd2469fd17e43f670ee3absalomon@google.com    SkPathRef::Editor ed(&fPathRef);
7851dfe88e00aeddf20690fd2469fd17e43f670ee3absalomon@google.com    SkPoint* pts = ed.growForVerb(kQuad_Verb);
7868a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    pts[0].set(x1, y1);
7878a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    pts[1].set(x2, y2);
78826da7f00aedba107d4b3e382283034e265db09b6skia.committer@gmail.com
789b54455e440e66e0b1c30954d226226f49aac26d6reed@google.com    DIRTY_AFTER_EDIT;
7908a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com}
7918a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
7928a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.comvoid SkPath::rQuadTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2) {
7939d54aeb8a192845f1f8122dba780d40ee6a0de1bcommit-bot@chromium.org    this->injectMoveToIfNeeded();  // This can change the result of this->getLastPt().
7948a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    SkPoint pt;
7958a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    this->getLastPt(&pt);
7968a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    this->quadTo(pt.fX + x1, pt.fY + y1, pt.fX + x2, pt.fY + y2);
7978a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com}
7988a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
799277c3f87656c44e0a651ed0dd56efa16c0ab07b4reed@google.comvoid SkPath::conicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2,
800277c3f87656c44e0a651ed0dd56efa16c0ab07b4reed@google.com                     SkScalar w) {
801277c3f87656c44e0a651ed0dd56efa16c0ab07b4reed@google.com    // check for <= 0 or NaN with this test
802277c3f87656c44e0a651ed0dd56efa16c0ab07b4reed@google.com    if (!(w > 0)) {
803277c3f87656c44e0a651ed0dd56efa16c0ab07b4reed@google.com        this->lineTo(x2, y2);
804277c3f87656c44e0a651ed0dd56efa16c0ab07b4reed@google.com    } else if (!SkScalarIsFinite(w)) {
805277c3f87656c44e0a651ed0dd56efa16c0ab07b4reed@google.com        this->lineTo(x1, y1);
806277c3f87656c44e0a651ed0dd56efa16c0ab07b4reed@google.com        this->lineTo(x2, y2);
807277c3f87656c44e0a651ed0dd56efa16c0ab07b4reed@google.com    } else if (SK_Scalar1 == w) {
808277c3f87656c44e0a651ed0dd56efa16c0ab07b4reed@google.com        this->quadTo(x1, y1, x2, y2);
809277c3f87656c44e0a651ed0dd56efa16c0ab07b4reed@google.com    } else {
810277c3f87656c44e0a651ed0dd56efa16c0ab07b4reed@google.com        SkDEBUGCODE(this->validate();)
81126da7f00aedba107d4b3e382283034e265db09b6skia.committer@gmail.com
8125e1a7f2cc621d357da5c62a7bc4ef750d94b96f3commit-bot@chromium.org        this->injectMoveToIfNeeded();
8135e1a7f2cc621d357da5c62a7bc4ef750d94b96f3commit-bot@chromium.org
814277c3f87656c44e0a651ed0dd56efa16c0ab07b4reed@google.com        SkPathRef::Editor ed(&fPathRef);
8156b8dbb668f1f069270d35a47cfe98decd059c625robertphillips@google.com        SkPoint* pts = ed.growForVerb(kConic_Verb, w);
816277c3f87656c44e0a651ed0dd56efa16c0ab07b4reed@google.com        pts[0].set(x1, y1);
817277c3f87656c44e0a651ed0dd56efa16c0ab07b4reed@google.com        pts[1].set(x2, y2);
81826da7f00aedba107d4b3e382283034e265db09b6skia.committer@gmail.com
819277c3f87656c44e0a651ed0dd56efa16c0ab07b4reed@google.com        DIRTY_AFTER_EDIT;
820277c3f87656c44e0a651ed0dd56efa16c0ab07b4reed@google.com    }
821277c3f87656c44e0a651ed0dd56efa16c0ab07b4reed@google.com}
822277c3f87656c44e0a651ed0dd56efa16c0ab07b4reed@google.com
823277c3f87656c44e0a651ed0dd56efa16c0ab07b4reed@google.comvoid SkPath::rConicTo(SkScalar dx1, SkScalar dy1, SkScalar dx2, SkScalar dy2,
824277c3f87656c44e0a651ed0dd56efa16c0ab07b4reed@google.com                      SkScalar w) {
8259d54aeb8a192845f1f8122dba780d40ee6a0de1bcommit-bot@chromium.org    this->injectMoveToIfNeeded();  // This can change the result of this->getLastPt().
826277c3f87656c44e0a651ed0dd56efa16c0ab07b4reed@google.com    SkPoint pt;
827277c3f87656c44e0a651ed0dd56efa16c0ab07b4reed@google.com    this->getLastPt(&pt);
828277c3f87656c44e0a651ed0dd56efa16c0ab07b4reed@google.com    this->conicTo(pt.fX + dx1, pt.fY + dy1, pt.fX + dx2, pt.fY + dy2, w);
829277c3f87656c44e0a651ed0dd56efa16c0ab07b4reed@google.com}
830277c3f87656c44e0a651ed0dd56efa16c0ab07b4reed@google.com
8318a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.comvoid SkPath::cubicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2,
8328a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com                     SkScalar x3, SkScalar y3) {
8338a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    SkDEBUGCODE(this->validate();)
8348a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
8355e1a7f2cc621d357da5c62a7bc4ef750d94b96f3commit-bot@chromium.org    this->injectMoveToIfNeeded();
8365e1a7f2cc621d357da5c62a7bc4ef750d94b96f3commit-bot@chromium.org
8371dfe88e00aeddf20690fd2469fd17e43f670ee3absalomon@google.com    SkPathRef::Editor ed(&fPathRef);
8381dfe88e00aeddf20690fd2469fd17e43f670ee3absalomon@google.com    SkPoint* pts = ed.growForVerb(kCubic_Verb);
8398a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    pts[0].set(x1, y1);
8408a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    pts[1].set(x2, y2);
8418a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    pts[2].set(x3, y3);
8428a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
843b54455e440e66e0b1c30954d226226f49aac26d6reed@google.com    DIRTY_AFTER_EDIT;
8448a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com}
8458a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
8468a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.comvoid SkPath::rCubicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2,
8478a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com                      SkScalar x3, SkScalar y3) {
8489d54aeb8a192845f1f8122dba780d40ee6a0de1bcommit-bot@chromium.org    this->injectMoveToIfNeeded();  // This can change the result of this->getLastPt().
8498a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    SkPoint pt;
8508a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    this->getLastPt(&pt);
8518a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    this->cubicTo(pt.fX + x1, pt.fY + y1, pt.fX + x2, pt.fY + y2,
8528a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com                  pt.fX + x3, pt.fY + y3);
8538a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com}
8548a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
8558a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.comvoid SkPath::close() {
8568a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    SkDEBUGCODE(this->validate();)
8578a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
8581dfe88e00aeddf20690fd2469fd17e43f670ee3absalomon@google.com    int count = fPathRef->countVerbs();
8598a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    if (count > 0) {
8601dfe88e00aeddf20690fd2469fd17e43f670ee3absalomon@google.com        switch (fPathRef->atVerb(count - 1)) {
8618a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            case kLine_Verb:
8628a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            case kQuad_Verb:
863277c3f87656c44e0a651ed0dd56efa16c0ab07b4reed@google.com            case kConic_Verb:
8648a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            case kCubic_Verb:
8651dfe88e00aeddf20690fd2469fd17e43f670ee3absalomon@google.com            case kMove_Verb: {
8661dfe88e00aeddf20690fd2469fd17e43f670ee3absalomon@google.com                SkPathRef::Editor ed(&fPathRef);
8671dfe88e00aeddf20690fd2469fd17e43f670ee3absalomon@google.com                ed.growForVerb(kClose_Verb);
8688a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com                break;
8691dfe88e00aeddf20690fd2469fd17e43f670ee3absalomon@google.com            }
870277c3f87656c44e0a651ed0dd56efa16c0ab07b4reed@google.com            case kClose_Verb:
871fa2f2a48f6822b88ab895fece1998af549c16ebereed@google.com                // don't add a close if it's the first verb or a repeat
8727950a9eba71f65365d88021680a16f245ad3fa68reed@google.com                break;
873277c3f87656c44e0a651ed0dd56efa16c0ab07b4reed@google.com            default:
874330313a8a8343876ee596da39da06a5d69badd9cmtklein@google.com                SkDEBUGFAIL("unexpected verb");
875277c3f87656c44e0a651ed0dd56efa16c0ab07b4reed@google.com                break;
8768a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        }
8778a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    }
8785e1a7f2cc621d357da5c62a7bc4ef750d94b96f3commit-bot@chromium.org
8795e1a7f2cc621d357da5c62a7bc4ef750d94b96f3commit-bot@chromium.org    // signal that we need a moveTo to follow us (unless we're done)
8805e1a7f2cc621d357da5c62a7bc4ef750d94b96f3commit-bot@chromium.org#if 0
8815e1a7f2cc621d357da5c62a7bc4ef750d94b96f3commit-bot@chromium.org    if (fLastMoveToIndex >= 0) {
8825e1a7f2cc621d357da5c62a7bc4ef750d94b96f3commit-bot@chromium.org        fLastMoveToIndex = ~fLastMoveToIndex;
8835e1a7f2cc621d357da5c62a7bc4ef750d94b96f3commit-bot@chromium.org    }
8845e1a7f2cc621d357da5c62a7bc4ef750d94b96f3commit-bot@chromium.org#else
8855e1a7f2cc621d357da5c62a7bc4ef750d94b96f3commit-bot@chromium.org    fLastMoveToIndex ^= ~fLastMoveToIndex >> (8 * sizeof(fLastMoveToIndex) - 1);
8865e1a7f2cc621d357da5c62a7bc4ef750d94b96f3commit-bot@chromium.org#endif
8878a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com}
8888a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
8898a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com///////////////////////////////////////////////////////////////////////////////
890abf15c189b6333aa77a6c3f566cc67f4634ba338reed@google.com
891c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalitanamespace {
892c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalita
893c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalitatemplate <unsigned N>
894c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalitaclass PointIterator {
895c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalitapublic:
896c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalita    PointIterator(SkPath::Direction dir, unsigned startIndex)
897c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalita        : fCurrent(startIndex % N)
898c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalita        , fAdvance(dir == SkPath::kCW_Direction ? 1 : N - 1) { }
899c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalita
900c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalita    const SkPoint& current() const {
901c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalita        SkASSERT(fCurrent < N);
902c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalita        return fPts[fCurrent];
903c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalita    }
904c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalita
905c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalita    const SkPoint& next() {
906c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalita        fCurrent = (fCurrent + fAdvance) % N;
907c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalita        return this->current();
908c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalita    }
909c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalita
910c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalitaprotected:
911c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalita    SkPoint fPts[N];
912c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalita
913c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalitaprivate:
914c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalita    unsigned fCurrent;
915c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalita    unsigned fAdvance;
916c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalita};
917c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalita
918c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalitaclass RectPointIterator : public PointIterator<4> {
919c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalitapublic:
920c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalita    RectPointIterator(const SkRect& rect, SkPath::Direction dir, unsigned startIndex)
921c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalita        : PointIterator(dir, startIndex) {
922c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalita
923c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalita        fPts[0] = SkPoint::Make(rect.fLeft, rect.fTop);
924c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalita        fPts[1] = SkPoint::Make(rect.fRight, rect.fTop);
925c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalita        fPts[2] = SkPoint::Make(rect.fRight, rect.fBottom);
926c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalita        fPts[3] = SkPoint::Make(rect.fLeft, rect.fBottom);
927c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalita    }
928c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalita};
929c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalita
930c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalitaclass OvalPointIterator : public PointIterator<4> {
931c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalitapublic:
932c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalita    OvalPointIterator(const SkRect& oval, SkPath::Direction dir, unsigned startIndex)
933c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalita        : PointIterator(dir, startIndex) {
934c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalita
935c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalita        const SkScalar cx = oval.centerX();
936c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalita        const SkScalar cy = oval.centerY();
937c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalita
938c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalita        fPts[0] = SkPoint::Make(cx, oval.fTop);
939c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalita        fPts[1] = SkPoint::Make(oval.fRight, cy);
940c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalita        fPts[2] = SkPoint::Make(cx, oval.fBottom);
941c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalita        fPts[3] = SkPoint::Make(oval.fLeft, cy);
942c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalita    }
943c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalita};
944c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalita
945c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalitaclass RRectPointIterator : public PointIterator<8> {
946c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalitapublic:
947c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalita    RRectPointIterator(const SkRRect& rrect, SkPath::Direction dir, unsigned startIndex)
948c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalita        : PointIterator(dir, startIndex) {
949c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalita
950c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalita        const SkRect& bounds = rrect.getBounds();
951c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalita        const SkScalar L = bounds.fLeft;
952c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalita        const SkScalar T = bounds.fTop;
953c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalita        const SkScalar R = bounds.fRight;
954c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalita        const SkScalar B = bounds.fBottom;
955c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalita
956c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalita        fPts[0] = SkPoint::Make(L + rrect.radii(SkRRect::kUpperLeft_Corner).fX, T);
957c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalita        fPts[1] = SkPoint::Make(R - rrect.radii(SkRRect::kUpperRight_Corner).fX, T);
958c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalita        fPts[2] = SkPoint::Make(R, T + rrect.radii(SkRRect::kUpperRight_Corner).fY);
959c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalita        fPts[3] = SkPoint::Make(R, B - rrect.radii(SkRRect::kLowerRight_Corner).fY);
960c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalita        fPts[4] = SkPoint::Make(R - rrect.radii(SkRRect::kLowerRight_Corner).fX, B);
961c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalita        fPts[5] = SkPoint::Make(L + rrect.radii(SkRRect::kLowerLeft_Corner).fX, B);
962c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalita        fPts[6] = SkPoint::Make(L, B - rrect.radii(SkRRect::kLowerLeft_Corner).fY);
963c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalita        fPts[7] = SkPoint::Make(L, T + rrect.radii(SkRRect::kUpperLeft_Corner).fY);
964c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalita    }
965c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalita};
966c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalita
967c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalita} // anonymous namespace
968c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalita
969a8a3b3d9a027ad54ce20f8b4ed7c577a176b31careed@google.comstatic void assert_known_direction(int dir) {
970a8a3b3d9a027ad54ce20f8b4ed7c577a176b31careed@google.com    SkASSERT(SkPath::kCW_Direction == dir || SkPath::kCCW_Direction == dir);
971a8a3b3d9a027ad54ce20f8b4ed7c577a176b31careed@google.com}
972a8a3b3d9a027ad54ce20f8b4ed7c577a176b31careed@google.com
9738a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.comvoid SkPath::addRect(const SkRect& rect, Direction dir) {
974c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalita    this->addRect(rect, dir, 0);
9758a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com}
9768a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
9778a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.comvoid SkPath::addRect(SkScalar left, SkScalar top, SkScalar right,
9788a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com                     SkScalar bottom, Direction dir) {
979c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalita    this->addRect(SkRect::MakeLTRB(left, top, right, bottom), dir, 0);
980c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalita}
981c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalita
982c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalitavoid SkPath::addRect(const SkRect &rect, Direction dir, unsigned startIndex) {
983a8a3b3d9a027ad54ce20f8b4ed7c577a176b31careed@google.com    assert_known_direction(dir);
984026beb52a29a620290fcfb24f1e7e9e75547b80freed    fFirstDirection = this->hasOnlyMoveTos() ?
985c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalita        (SkPathPriv::FirstDirection)dir : SkPathPriv::kUnknown_FirstDirection;
98630c174b9ce6b9777ee50ae0d0565a01b2a060f01bsalomon@google.com    SkAutoDisableDirectionCheck addc(this);
987c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalita    SkAutoPathBoundsUpdate apbu(this, rect);
98830c174b9ce6b9777ee50ae0d0565a01b2a060f01bsalomon@google.com
989c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalita    SkDEBUGCODE(int initialVerbCount = this->countVerbs());
9908a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
991c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalita    const int kVerbs = 5; // moveTo + 3x lineTo + close
992c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalita    this->incReserve(kVerbs);
9938a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
994c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalita    RectPointIterator iter(rect, dir, startIndex);
995c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalita
996c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalita    this->moveTo(iter.current());
997c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalita    this->lineTo(iter.next());
998c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalita    this->lineTo(iter.next());
999c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalita    this->lineTo(iter.next());
10008a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    this->close();
1001c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalita
1002c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalita    SkASSERT(this->countVerbs() == initialVerbCount + kVerbs);
10038a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com}
10048a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
1005744fabad474e3e111e7cbd8609cf7e209df17f32reed@google.comvoid SkPath::addPoly(const SkPoint pts[], int count, bool close) {
1006744fabad474e3e111e7cbd8609cf7e209df17f32reed@google.com    SkDEBUGCODE(this->validate();)
1007744fabad474e3e111e7cbd8609cf7e209df17f32reed@google.com    if (count <= 0) {
1008744fabad474e3e111e7cbd8609cf7e209df17f32reed@google.com        return;
1009744fabad474e3e111e7cbd8609cf7e209df17f32reed@google.com    }
1010744fabad474e3e111e7cbd8609cf7e209df17f32reed@google.com
10115e1a7f2cc621d357da5c62a7bc4ef750d94b96f3commit-bot@chromium.org    fLastMoveToIndex = fPathRef->countPoints();
10125e1a7f2cc621d357da5c62a7bc4ef750d94b96f3commit-bot@chromium.org
1013744fabad474e3e111e7cbd8609cf7e209df17f32reed@google.com    // +close makes room for the extra kClose_Verb
10146b8dbb668f1f069270d35a47cfe98decd059c625robertphillips@google.com    SkPathRef::Editor ed(&fPathRef, count+close, count);
1015744fabad474e3e111e7cbd8609cf7e209df17f32reed@google.com
10166b8dbb668f1f069270d35a47cfe98decd059c625robertphillips@google.com    ed.growForVerb(kMove_Verb)->set(pts[0].fX, pts[0].fY);
1017744fabad474e3e111e7cbd8609cf7e209df17f32reed@google.com    if (count > 1) {
10186b8dbb668f1f069270d35a47cfe98decd059c625robertphillips@google.com        SkPoint* p = ed.growForRepeatedVerb(kLine_Verb, count - 1);
10196b8dbb668f1f069270d35a47cfe98decd059c625robertphillips@google.com        memcpy(p, &pts[1], (count-1) * sizeof(SkPoint));
1020744fabad474e3e111e7cbd8609cf7e209df17f32reed@google.com    }
10216b8dbb668f1f069270d35a47cfe98decd059c625robertphillips@google.com
1022744fabad474e3e111e7cbd8609cf7e209df17f32reed@google.com    if (close) {
10236b8dbb668f1f069270d35a47cfe98decd059c625robertphillips@google.com        ed.growForVerb(kClose_Verb);
102463c684a8a609d39da11b4a656223cebf52ca85dccaryclark        fLastMoveToIndex ^= ~fLastMoveToIndex >> (8 * sizeof(fLastMoveToIndex) - 1);
1025744fabad474e3e111e7cbd8609cf7e209df17f32reed@google.com    }
1026744fabad474e3e111e7cbd8609cf7e209df17f32reed@google.com
1027744fabad474e3e111e7cbd8609cf7e209df17f32reed@google.com    DIRTY_AFTER_EDIT;
10281dfe88e00aeddf20690fd2469fd17e43f670ee3absalomon@google.com    SkDEBUGCODE(this->validate();)
1029744fabad474e3e111e7cbd8609cf7e209df17f32reed@google.com}
1030744fabad474e3e111e7cbd8609cf7e209df17f32reed@google.com
10311cc385b7a427ec8b511c56744a02046a60dd3fd8robertphillips@google.com#include "SkGeometry.h"
10321cc385b7a427ec8b511c56744a02046a60dd3fd8robertphillips@google.com
1033f90ea01522524b064141cf4ee5198b7cb053fd53reedstatic bool arc_is_lone_point(const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle,
1034f90ea01522524b064141cf4ee5198b7cb053fd53reed                              SkPoint* pt) {
1035f90ea01522524b064141cf4ee5198b7cb053fd53reed    if (0 == sweepAngle && (0 == startAngle || SkIntToScalar(360) == startAngle)) {
10361cc385b7a427ec8b511c56744a02046a60dd3fd8robertphillips@google.com        // Chrome uses this path to move into and out of ovals. If not
10371cc385b7a427ec8b511c56744a02046a60dd3fd8robertphillips@google.com        // treated as a special case the moves can distort the oval's
10381cc385b7a427ec8b511c56744a02046a60dd3fd8robertphillips@google.com        // bounding box (and break the circle special case).
1039f90ea01522524b064141cf4ee5198b7cb053fd53reed        pt->set(oval.fRight, oval.centerY());
1040f90ea01522524b064141cf4ee5198b7cb053fd53reed        return true;
10411cc385b7a427ec8b511c56744a02046a60dd3fd8robertphillips@google.com    } else if (0 == oval.width() && 0 == oval.height()) {
10421cc385b7a427ec8b511c56744a02046a60dd3fd8robertphillips@google.com        // Chrome will sometimes create 0 radius round rects. Having degenerate
10431cc385b7a427ec8b511c56744a02046a60dd3fd8robertphillips@google.com        // quad segments in the path prevents the path from being recognized as
10441cc385b7a427ec8b511c56744a02046a60dd3fd8robertphillips@google.com        // a rect.
10451cc385b7a427ec8b511c56744a02046a60dd3fd8robertphillips@google.com        // TODO: optimizing the case where only one of width or height is zero
10461cc385b7a427ec8b511c56744a02046a60dd3fd8robertphillips@google.com        // should also be considered. This case, however, doesn't seem to be
10471cc385b7a427ec8b511c56744a02046a60dd3fd8robertphillips@google.com        // as common as the single point case.
1048f90ea01522524b064141cf4ee5198b7cb053fd53reed        pt->set(oval.fRight, oval.fTop);
1049f90ea01522524b064141cf4ee5198b7cb053fd53reed        return true;
10501cc385b7a427ec8b511c56744a02046a60dd3fd8robertphillips@google.com    }
1051f90ea01522524b064141cf4ee5198b7cb053fd53reed    return false;
1052f90ea01522524b064141cf4ee5198b7cb053fd53reed}
10531cc385b7a427ec8b511c56744a02046a60dd3fd8robertphillips@google.com
1054d5d27d9b146731b871b1bcc6d6de36fba2d5ea44reed// Return the unit vectors pointing at the start/stop points for the given start/sweep angles
1055d5d27d9b146731b871b1bcc6d6de36fba2d5ea44reed//
1056d5d27d9b146731b871b1bcc6d6de36fba2d5ea44reedstatic void angles_to_unit_vectors(SkScalar startAngle, SkScalar sweepAngle,
1057d5d27d9b146731b871b1bcc6d6de36fba2d5ea44reed                                   SkVector* startV, SkVector* stopV, SkRotationDirection* dir) {
1058d5d27d9b146731b871b1bcc6d6de36fba2d5ea44reed    startV->fY = SkScalarSinCos(SkDegreesToRadians(startAngle), &startV->fX);
1059d5d27d9b146731b871b1bcc6d6de36fba2d5ea44reed    stopV->fY = SkScalarSinCos(SkDegreesToRadians(startAngle + sweepAngle), &stopV->fX);
10601cc385b7a427ec8b511c56744a02046a60dd3fd8robertphillips@google.com
10611cc385b7a427ec8b511c56744a02046a60dd3fd8robertphillips@google.com    /*  If the sweep angle is nearly (but less than) 360, then due to precision
1062d5d27d9b146731b871b1bcc6d6de36fba2d5ea44reed     loss in radians-conversion and/or sin/cos, we may end up with coincident
1063d5d27d9b146731b871b1bcc6d6de36fba2d5ea44reed     vectors, which will fool SkBuildQuadArc into doing nothing (bad) instead
1064d5d27d9b146731b871b1bcc6d6de36fba2d5ea44reed     of drawing a nearly complete circle (good).
1065d5d27d9b146731b871b1bcc6d6de36fba2d5ea44reed     e.g. canvas.drawArc(0, 359.99, ...)
1066d5d27d9b146731b871b1bcc6d6de36fba2d5ea44reed     -vs- canvas.drawArc(0, 359.9, ...)
1067d5d27d9b146731b871b1bcc6d6de36fba2d5ea44reed     We try to detect this edge case, and tweak the stop vector
10681cc385b7a427ec8b511c56744a02046a60dd3fd8robertphillips@google.com     */
1069d5d27d9b146731b871b1bcc6d6de36fba2d5ea44reed    if (*startV == *stopV) {
10701cc385b7a427ec8b511c56744a02046a60dd3fd8robertphillips@google.com        SkScalar sw = SkScalarAbs(sweepAngle);
10711cc385b7a427ec8b511c56744a02046a60dd3fd8robertphillips@google.com        if (sw < SkIntToScalar(360) && sw > SkIntToScalar(359)) {
10721cc385b7a427ec8b511c56744a02046a60dd3fd8robertphillips@google.com            SkScalar stopRad = SkDegreesToRadians(startAngle + sweepAngle);
10731cc385b7a427ec8b511c56744a02046a60dd3fd8robertphillips@google.com            // make a guess at a tiny angle (in radians) to tweak by
10741cc385b7a427ec8b511c56744a02046a60dd3fd8robertphillips@google.com            SkScalar deltaRad = SkScalarCopySign(SK_Scalar1/512, sweepAngle);
10751cc385b7a427ec8b511c56744a02046a60dd3fd8robertphillips@google.com            // not sure how much will be enough, so we use a loop
10761cc385b7a427ec8b511c56744a02046a60dd3fd8robertphillips@google.com            do {
10771cc385b7a427ec8b511c56744a02046a60dd3fd8robertphillips@google.com                stopRad -= deltaRad;
1078d5d27d9b146731b871b1bcc6d6de36fba2d5ea44reed                stopV->fY = SkScalarSinCos(stopRad, &stopV->fX);
1079d5d27d9b146731b871b1bcc6d6de36fba2d5ea44reed            } while (*startV == *stopV);
10801cc385b7a427ec8b511c56744a02046a60dd3fd8robertphillips@google.com        }
10811cc385b7a427ec8b511c56744a02046a60dd3fd8robertphillips@google.com    }
1082d5d27d9b146731b871b1bcc6d6de36fba2d5ea44reed    *dir = sweepAngle > 0 ? kCW_SkRotationDirection : kCCW_SkRotationDirection;
1083d5d27d9b146731b871b1bcc6d6de36fba2d5ea44reed}
10841cc385b7a427ec8b511c56744a02046a60dd3fd8robertphillips@google.com
10859e779d495130009926fc5394a8971eec20494e5freed/**
10869e779d495130009926fc5394a8971eec20494e5freed *  If this returns 0, then the caller should just line-to the singlePt, else it should
10879e779d495130009926fc5394a8971eec20494e5freed *  ignore singlePt and append the specified number of conics.
10889e779d495130009926fc5394a8971eec20494e5freed */
1089d5d27d9b146731b871b1bcc6d6de36fba2d5ea44reedstatic int build_arc_conics(const SkRect& oval, const SkVector& start, const SkVector& stop,
10909e779d495130009926fc5394a8971eec20494e5freed                            SkRotationDirection dir, SkConic conics[SkConic::kMaxConicsForArc],
10919e779d495130009926fc5394a8971eec20494e5freed                            SkPoint* singlePt) {
1092d5d27d9b146731b871b1bcc6d6de36fba2d5ea44reed    SkMatrix    matrix;
1093d5d27d9b146731b871b1bcc6d6de36fba2d5ea44reed
1094d5d27d9b146731b871b1bcc6d6de36fba2d5ea44reed    matrix.setScale(SkScalarHalf(oval.width()), SkScalarHalf(oval.height()));
1095d5d27d9b146731b871b1bcc6d6de36fba2d5ea44reed    matrix.postTranslate(oval.centerX(), oval.centerY());
1096d5d27d9b146731b871b1bcc6d6de36fba2d5ea44reed
10979e779d495130009926fc5394a8971eec20494e5freed    int count = SkConic::BuildUnitArc(start, stop, dir, &matrix, conics);
10989e779d495130009926fc5394a8971eec20494e5freed    if (0 == count) {
109995e34a3d847684692184daea4a887f7825d65e51xidachen        matrix.mapXY(stop.x(), stop.y(), singlePt);
11009e779d495130009926fc5394a8971eec20494e5freed    }
11019e779d495130009926fc5394a8971eec20494e5freed    return count;
1102d5d27d9b146731b871b1bcc6d6de36fba2d5ea44reed}
11031cc385b7a427ec8b511c56744a02046a60dd3fd8robertphillips@google.com
11044e18c7a9bbef6ac949d535aa61dfe1462ebb4452robertphillips@google.comvoid SkPath::addRoundRect(const SkRect& rect, const SkScalar radii[],
11058a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com                          Direction dir) {
11064e18c7a9bbef6ac949d535aa61dfe1462ebb4452robertphillips@google.com    SkRRect rrect;
11074e18c7a9bbef6ac949d535aa61dfe1462ebb4452robertphillips@google.com    rrect.setRectRadii(rect, (const SkVector*) radii);
11084e18c7a9bbef6ac949d535aa61dfe1462ebb4452robertphillips@google.com    this->addRRect(rrect, dir);
11094e18c7a9bbef6ac949d535aa61dfe1462ebb4452robertphillips@google.com}
11104e18c7a9bbef6ac949d535aa61dfe1462ebb4452robertphillips@google.com
11114e18c7a9bbef6ac949d535aa61dfe1462ebb4452robertphillips@google.comvoid SkPath::addRRect(const SkRRect& rrect, Direction dir) {
1112c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalita    // legacy start indices: 6 (CW) and 7(CCW)
1113c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalita    this->addRRect(rrect, dir, dir == kCW_Direction ? 6 : 7);
1114c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalita}
11151b28a3a4890e3b84b43181b3fe3690ac565930dcreed
1116c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalitavoid SkPath::addRRect(const SkRRect &rrect, Direction dir, unsigned startIndex) {
1117c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalita        assert_known_direction(dir);
11181b28a3a4890e3b84b43181b3fe3690ac565930dcreed
1119c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalita        if (rrect.isEmpty()) {
1120c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalita            return;
1121c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalita        }
11221b28a3a4890e3b84b43181b3fe3690ac565930dcreed
1123da707bf5635c70d4c3c284a0b05d92489b76788ecaryclark        bool isRRect = hasOnlyMoveTos();
1124c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalita        const SkRect& bounds = rrect.getBounds();
11251b28a3a4890e3b84b43181b3fe3690ac565930dcreed
1126c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalita        if (rrect.isRect()) {
1127c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalita            // degenerate(rect) => radii points are collapsing
1128c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalita            this->addRect(bounds, dir, (startIndex + 1) / 2);
1129c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalita        } else if (rrect.isOval()) {
1130c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalita            // degenerate(oval) => line points are collapsing
1131c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalita            this->addOval(bounds, dir, startIndex / 2);
11321b28a3a4890e3b84b43181b3fe3690ac565930dcreed        } else {
1133c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalita            fFirstDirection = this->hasOnlyMoveTos() ?
1134c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalita                                (SkPathPriv::FirstDirection)dir : SkPathPriv::kUnknown_FirstDirection;
1135c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalita
1136c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalita            SkAutoPathBoundsUpdate apbu(this, bounds);
1137c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalita            SkAutoDisableDirectionCheck addc(this);
1138c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalita
1139c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalita            // we start with a conic on odd indices when moving CW vs. even indices when moving CCW
1140c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalita            const bool startsWithConic = ((startIndex & 1) == (dir == kCW_Direction));
1141c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalita            const SkScalar weight = SK_ScalarRoot2Over2;
1142c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalita
1143c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalita            SkDEBUGCODE(int initialVerbCount = this->countVerbs());
1144c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalita            const int kVerbs = startsWithConic
1145c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalita                ? 9   // moveTo + 4x conicTo + 3x lineTo + close
1146c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalita                : 10; // moveTo + 4x lineTo + 4x conicTo + close
1147c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalita            this->incReserve(kVerbs);
1148c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalita
1149c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalita            RRectPointIterator rrectIter(rrect, dir, startIndex);
1150c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalita            // Corner iterator indices follow the collapsed radii model,
1151c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalita            // adjusted such that the start pt is "behind" the radii start pt.
1152c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalita            const unsigned rectStartIndex = startIndex / 2 + (dir == kCW_Direction ? 0 : 1);
1153c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalita            RectPointIterator rectIter(bounds, dir, rectStartIndex);
1154c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalita
1155c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalita            this->moveTo(rrectIter.current());
1156c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalita            if (startsWithConic) {
1157c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalita                for (unsigned i = 0; i < 3; ++i) {
1158c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalita                    this->conicTo(rectIter.next(), rrectIter.next(), weight);
1159c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalita                    this->lineTo(rrectIter.next());
1160c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalita                }
1161c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalita                this->conicTo(rectIter.next(), rrectIter.next(), weight);
1162c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalita                // final lineTo handled by close().
1163c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalita            } else {
1164c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalita                for (unsigned i = 0; i < 4; ++i) {
1165c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalita                    this->lineTo(rrectIter.next());
1166c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalita                    this->conicTo(rectIter.next(), rrectIter.next(), weight);
1167c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalita                }
1168c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalita            }
1169c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalita            this->close();
11701b28a3a4890e3b84b43181b3fe3690ac565930dcreed
1171da707bf5635c70d4c3c284a0b05d92489b76788ecaryclark            SkPathRef::Editor ed(&fPathRef);
117278d58d1084f0390c1c0f9123ac6e48efcd226f39bsalomon            ed.setIsRRect(isRRect, dir, startIndex % 8);
1173da707bf5635c70d4c3c284a0b05d92489b76788ecaryclark
1174c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalita            SkASSERT(this->countVerbs() == initialVerbCount + kVerbs);
11751b28a3a4890e3b84b43181b3fe3690ac565930dcreed        }
1176c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalita
1177c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalita        SkDEBUGCODE(fPathRef->validate();)
11784ed0fb768409bf97b79899c3990d8c15f5e9d784reed@google.com}
11794ed0fb768409bf97b79899c3990d8c15f5e9d784reed@google.com
11806aa2965ca814dd3329b65398b5c5af980e54b101bsalomon@google.combool SkPath::hasOnlyMoveTos() const {
11811dfe88e00aeddf20690fd2469fd17e43f670ee3absalomon@google.com    int count = fPathRef->countVerbs();
11821dfe88e00aeddf20690fd2469fd17e43f670ee3absalomon@google.com    const uint8_t* verbs = const_cast<const SkPathRef*>(fPathRef.get())->verbsMemBegin();
11831dfe88e00aeddf20690fd2469fd17e43f670ee3absalomon@google.com    for (int i = 0; i < count; ++i) {
11846aa2965ca814dd3329b65398b5c5af980e54b101bsalomon@google.com        if (*verbs == kLine_Verb ||
11856aa2965ca814dd3329b65398b5c5af980e54b101bsalomon@google.com            *verbs == kQuad_Verb ||
1186a1a097ee814d05a92487d85db8ad02e1d852fd6fcommit-bot@chromium.org            *verbs == kConic_Verb ||
11876aa2965ca814dd3329b65398b5c5af980e54b101bsalomon@google.com            *verbs == kCubic_Verb) {
11886aa2965ca814dd3329b65398b5c5af980e54b101bsalomon@google.com            return false;
11896aa2965ca814dd3329b65398b5c5af980e54b101bsalomon@google.com        }
11906aa2965ca814dd3329b65398b5c5af980e54b101bsalomon@google.com        ++verbs;
11916aa2965ca814dd3329b65398b5c5af980e54b101bsalomon@google.com    }
11926aa2965ca814dd3329b65398b5c5af980e54b101bsalomon@google.com    return true;
11936aa2965ca814dd3329b65398b5c5af980e54b101bsalomon@google.com}
11946aa2965ca814dd3329b65398b5c5af980e54b101bsalomon@google.com
1195a2318576d6510dc63513ce335ed0027666bd55bfBrian Osmanbool SkPath::isZeroLengthSincePoint(int startPtIndex) const {
1196a2318576d6510dc63513ce335ed0027666bd55bfBrian Osman    int count = fPathRef->countPoints() - startPtIndex;
1197d49a86ade0bab1fc3048d6ba5d8536abf25ed77ccaryclark    if (count < 2) {
1198d49a86ade0bab1fc3048d6ba5d8536abf25ed77ccaryclark        return true;
1199d49a86ade0bab1fc3048d6ba5d8536abf25ed77ccaryclark    }
1200a2318576d6510dc63513ce335ed0027666bd55bfBrian Osman    const SkPoint* pts = fPathRef.get()->points() + startPtIndex;
1201d49a86ade0bab1fc3048d6ba5d8536abf25ed77ccaryclark    const SkPoint& first = *pts;
1202d49a86ade0bab1fc3048d6ba5d8536abf25ed77ccaryclark    for (int index = 1; index < count; ++index) {
1203d49a86ade0bab1fc3048d6ba5d8536abf25ed77ccaryclark        if (first != pts[index]) {
1204d49a86ade0bab1fc3048d6ba5d8536abf25ed77ccaryclark            return false;
1205d49a86ade0bab1fc3048d6ba5d8536abf25ed77ccaryclark        }
1206d49a86ade0bab1fc3048d6ba5d8536abf25ed77ccaryclark    }
1207d49a86ade0bab1fc3048d6ba5d8536abf25ed77ccaryclark    return true;
1208d49a86ade0bab1fc3048d6ba5d8536abf25ed77ccaryclark}
1209d49a86ade0bab1fc3048d6ba5d8536abf25ed77ccaryclark
1210b16033a25b91f9e45d07bd4b955c8dbcfd23e8d5mike@reedtribe.orgvoid SkPath::addRoundRect(const SkRect& rect, SkScalar rx, SkScalar ry,
1211b16033a25b91f9e45d07bd4b955c8dbcfd23e8d5mike@reedtribe.org                          Direction dir) {
1212b16033a25b91f9e45d07bd4b955c8dbcfd23e8d5mike@reedtribe.org    assert_known_direction(dir);
12133284017a60ea4fc3dc5b95838ba0c301ee1e4e8dskia.committer@gmail.com
121475e3ca127cd14fffc9c8df7ea03d6529fb001831humper@google.com    if (rx < 0 || ry < 0) {
121575e3ca127cd14fffc9c8df7ea03d6529fb001831humper@google.com        return;
121675e3ca127cd14fffc9c8df7ea03d6529fb001831humper@google.com    }
1217d9f65e3df45c9b4994c70f6bf13d29985afd2f65skia.committer@gmail.com
121842feaaf0a5fbb508b237d5c844c484a1a3b0c865commit-bot@chromium.org    SkRRect rrect;
121942feaaf0a5fbb508b237d5c844c484a1a3b0c865commit-bot@chromium.org    rrect.setRectXY(rect, rx, ry);
122042feaaf0a5fbb508b237d5c844c484a1a3b0c865commit-bot@chromium.org    this->addRRect(rrect, dir);
1221b16033a25b91f9e45d07bd4b955c8dbcfd23e8d5mike@reedtribe.org}
1222b16033a25b91f9e45d07bd4b955c8dbcfd23e8d5mike@reedtribe.org
12238a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.comvoid SkPath::addOval(const SkRect& oval, Direction dir) {
1224c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalita    // legacy start index: 1
1225c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalita    this->addOval(oval, dir, 1);
1226c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalita}
1227c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalita
1228c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalitavoid SkPath::addOval(const SkRect &oval, Direction dir, unsigned startPointIndex) {
1229a8a3b3d9a027ad54ce20f8b4ed7c577a176b31careed@google.com    assert_known_direction(dir);
1230a8a3b3d9a027ad54ce20f8b4ed7c577a176b31careed@google.com
12316aa2965ca814dd3329b65398b5c5af980e54b101bsalomon@google.com    /* If addOval() is called after previous moveTo(),
12326aa2965ca814dd3329b65398b5c5af980e54b101bsalomon@google.com       this path is still marked as an oval. This is used to
12336aa2965ca814dd3329b65398b5c5af980e54b101bsalomon@google.com       fit into WebKit's calling sequences.
12346aa2965ca814dd3329b65398b5c5af980e54b101bsalomon@google.com       We can't simply check isEmpty() in this case, as additional
12356aa2965ca814dd3329b65398b5c5af980e54b101bsalomon@google.com       moveTo() would mark the path non empty.
12366aa2965ca814dd3329b65398b5c5af980e54b101bsalomon@google.com     */
1237466310dbd3073add2ec934e336c30deaaf702eaerobertphillips@google.com    bool isOval = hasOnlyMoveTos();
1238466310dbd3073add2ec934e336c30deaaf702eaerobertphillips@google.com    if (isOval) {
1239026beb52a29a620290fcfb24f1e7e9e75547b80freed        fFirstDirection = (SkPathPriv::FirstDirection)dir;
124030c174b9ce6b9777ee50ae0d0565a01b2a060f01bsalomon@google.com    } else {
1241026beb52a29a620290fcfb24f1e7e9e75547b80freed        fFirstDirection = SkPathPriv::kUnknown_FirstDirection;
124230c174b9ce6b9777ee50ae0d0565a01b2a060f01bsalomon@google.com    }
12436aa2965ca814dd3329b65398b5c5af980e54b101bsalomon@google.com
124430c174b9ce6b9777ee50ae0d0565a01b2a060f01bsalomon@google.com    SkAutoDisableDirectionCheck addc(this);
12458a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    SkAutoPathBoundsUpdate apbu(this, oval);
12468a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
1247c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalita    SkDEBUGCODE(int initialVerbCount = this->countVerbs());
1248c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalita    const int kVerbs = 6; // moveTo + 4x conicTo + close
1249c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalita    this->incReserve(kVerbs);
1250c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalita
1251c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalita    OvalPointIterator ovalIter(oval, dir, startPointIndex);
1252c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalita    // The corner iterator pts are tracking "behind" the oval/radii pts.
1253c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalita    RectPointIterator rectIter(oval, dir, startPointIndex + (dir == kCW_Direction ? 0 : 1));
1254220f926d9d4b38a9018c922c095847bbd261f583reed    const SkScalar weight = SK_ScalarRoot2Over2;
1255220f926d9d4b38a9018c922c095847bbd261f583reed
1256c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalita    this->moveTo(ovalIter.current());
1257c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalita    for (unsigned i = 0; i < 4; ++i) {
1258c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalita        this->conicTo(rectIter.next(), ovalIter.next(), weight);
1259220f926d9d4b38a9018c922c095847bbd261f583reed    }
12608a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    this->close();
12618a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
1262c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalita    SkASSERT(this->countVerbs() == initialVerbCount + kVerbs);
1263c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalita
1264466310dbd3073add2ec934e336c30deaaf702eaerobertphillips@google.com    SkPathRef::Editor ed(&fPathRef);
12656aa2965ca814dd3329b65398b5c5af980e54b101bsalomon@google.com
126678d58d1084f0390c1c0f9123ac6e48efcd226f39bsalomon    ed.setIsOval(isOval, kCCW_Direction == dir, startPointIndex % 4);
12676aa2965ca814dd3329b65398b5c5af980e54b101bsalomon@google.com}
12686aa2965ca814dd3329b65398b5c5af980e54b101bsalomon@google.com
12698a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.comvoid SkPath::addCircle(SkScalar x, SkScalar y, SkScalar r, Direction dir) {
12708a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    if (r > 0) {
1271c08d53ee175e190254d8fd6659d9ad051ac0ba46fmalita        this->addOval(SkRect::MakeLTRB(x - r, y - r, x + r, y + r), dir);
12728a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    }
12738a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com}
12748a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
12758a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.comvoid SkPath::arcTo(const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle,
12768a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com                   bool forceMoveTo) {
12778a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    if (oval.width() < 0 || oval.height() < 0) {
12788a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        return;
12798a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    }
12808a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
1281f90ea01522524b064141cf4ee5198b7cb053fd53reed    if (fPathRef->countVerbs() == 0) {
1282f90ea01522524b064141cf4ee5198b7cb053fd53reed        forceMoveTo = true;
1283f90ea01522524b064141cf4ee5198b7cb053fd53reed    }
1284f90ea01522524b064141cf4ee5198b7cb053fd53reed
1285f90ea01522524b064141cf4ee5198b7cb053fd53reed    SkPoint lonePt;
1286f90ea01522524b064141cf4ee5198b7cb053fd53reed    if (arc_is_lone_point(oval, startAngle, sweepAngle, &lonePt)) {
1287f90ea01522524b064141cf4ee5198b7cb053fd53reed        forceMoveTo ? this->moveTo(lonePt) : this->lineTo(lonePt);
1288f90ea01522524b064141cf4ee5198b7cb053fd53reed        return;
1289f90ea01522524b064141cf4ee5198b7cb053fd53reed    }
1290f90ea01522524b064141cf4ee5198b7cb053fd53reed
1291d5d27d9b146731b871b1bcc6d6de36fba2d5ea44reed    SkVector startV, stopV;
1292d5d27d9b146731b871b1bcc6d6de36fba2d5ea44reed    SkRotationDirection dir;
1293d5d27d9b146731b871b1bcc6d6de36fba2d5ea44reed    angles_to_unit_vectors(startAngle, sweepAngle, &startV, &stopV, &dir);
1294d5d27d9b146731b871b1bcc6d6de36fba2d5ea44reed
12959e779d495130009926fc5394a8971eec20494e5freed    SkPoint singlePt;
12966069ddabd8385ff838236dc25d7354e71649c9f3xidachen
12976069ddabd8385ff838236dc25d7354e71649c9f3xidachen    // At this point, we know that the arc is not a lone point, but startV == stopV
12986069ddabd8385ff838236dc25d7354e71649c9f3xidachen    // indicates that the sweepAngle is too small such that angles_to_unit_vectors
12996069ddabd8385ff838236dc25d7354e71649c9f3xidachen    // cannot handle it.
13006069ddabd8385ff838236dc25d7354e71649c9f3xidachen    if (startV == stopV) {
13016069ddabd8385ff838236dc25d7354e71649c9f3xidachen        SkScalar endAngle = SkDegreesToRadians(startAngle + sweepAngle);
13026069ddabd8385ff838236dc25d7354e71649c9f3xidachen        SkScalar radiusX = oval.width() / 2;
13036069ddabd8385ff838236dc25d7354e71649c9f3xidachen        SkScalar radiusY = oval.height() / 2;
13046069ddabd8385ff838236dc25d7354e71649c9f3xidachen        // We cannot use SkScalarSinCos function in the next line because
13056069ddabd8385ff838236dc25d7354e71649c9f3xidachen        // SkScalarSinCos has a threshold *SkScalarNearlyZero*. When sin(startAngle)
13066069ddabd8385ff838236dc25d7354e71649c9f3xidachen        // is 0 and sweepAngle is very small and radius is huge, the expected
1307e54c75f351c775201049743c506d7a508d0fef91Mike Klein        // behavior here is to draw a line. But calling SkScalarSinCos will
13086069ddabd8385ff838236dc25d7354e71649c9f3xidachen        // make sin(endAngle) to be 0 which will then draw a dot.
13096069ddabd8385ff838236dc25d7354e71649c9f3xidachen        singlePt.set(oval.centerX() + radiusX * sk_float_cos(endAngle),
13106069ddabd8385ff838236dc25d7354e71649c9f3xidachen            oval.centerY() + radiusY * sk_float_sin(endAngle));
13116069ddabd8385ff838236dc25d7354e71649c9f3xidachen        forceMoveTo ? this->moveTo(singlePt) : this->lineTo(singlePt);
13126069ddabd8385ff838236dc25d7354e71649c9f3xidachen        return;
13136069ddabd8385ff838236dc25d7354e71649c9f3xidachen    }
13146069ddabd8385ff838236dc25d7354e71649c9f3xidachen
1315d5d27d9b146731b871b1bcc6d6de36fba2d5ea44reed    SkConic conics[SkConic::kMaxConicsForArc];
13169e779d495130009926fc5394a8971eec20494e5freed    int count = build_arc_conics(oval, startV, stopV, dir, conics, &singlePt);
1317d5d27d9b146731b871b1bcc6d6de36fba2d5ea44reed    if (count) {
1318d5d27d9b146731b871b1bcc6d6de36fba2d5ea44reed        this->incReserve(count * 2 + 1);
1319d5d27d9b146731b871b1bcc6d6de36fba2d5ea44reed        const SkPoint& pt = conics[0].fPts[0];
1320d5d27d9b146731b871b1bcc6d6de36fba2d5ea44reed        forceMoveTo ? this->moveTo(pt) : this->lineTo(pt);
1321d5d27d9b146731b871b1bcc6d6de36fba2d5ea44reed        for (int i = 0; i < count; ++i) {
1322d5d27d9b146731b871b1bcc6d6de36fba2d5ea44reed            this->conicTo(conics[i].fPts[1], conics[i].fPts[2], conics[i].fW);
1323d5d27d9b146731b871b1bcc6d6de36fba2d5ea44reed        }
13249e779d495130009926fc5394a8971eec20494e5freed    } else {
13259e779d495130009926fc5394a8971eec20494e5freed        forceMoveTo ? this->moveTo(singlePt) : this->lineTo(singlePt);
1326d5d27d9b146731b871b1bcc6d6de36fba2d5ea44reed    }
13278a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com}
13288a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
132955d49053d1b6db42e013eb3409ffcfc7e235c685caryclark// This converts the SVG arc to conics.
133055d49053d1b6db42e013eb3409ffcfc7e235c685caryclark// Partly adapted from Niko's code in kdelibs/kdecore/svgicons.
133155d49053d1b6db42e013eb3409ffcfc7e235c685caryclark// Then transcribed from webkit/chrome's SVGPathNormalizer::decomposeArcToCubic()
133255d49053d1b6db42e013eb3409ffcfc7e235c685caryclark// See also SVG implementation notes:
133355d49053d1b6db42e013eb3409ffcfc7e235c685caryclark// http://www.w3.org/TR/SVG/implnote.html#ArcConversionEndpointToCenter
133455d49053d1b6db42e013eb3409ffcfc7e235c685caryclark// Note that arcSweep bool value is flipped from the original implementation.
133555d49053d1b6db42e013eb3409ffcfc7e235c685caryclarkvoid SkPath::arcTo(SkScalar rx, SkScalar ry, SkScalar angle, SkPath::ArcSize arcLarge,
133655d49053d1b6db42e013eb3409ffcfc7e235c685caryclark                   SkPath::Direction arcSweep, SkScalar x, SkScalar y) {
1337f1d415188ffb4c34e2886c2cfceb363a148333f1caryclark    this->injectMoveToIfNeeded();
133855d49053d1b6db42e013eb3409ffcfc7e235c685caryclark    SkPoint srcPts[2];
133955d49053d1b6db42e013eb3409ffcfc7e235c685caryclark    this->getLastPt(&srcPts[0]);
134055d49053d1b6db42e013eb3409ffcfc7e235c685caryclark    // If rx = 0 or ry = 0 then this arc is treated as a straight line segment (a "lineto")
134155d49053d1b6db42e013eb3409ffcfc7e235c685caryclark    // joining the endpoints.
134255d49053d1b6db42e013eb3409ffcfc7e235c685caryclark    // http://www.w3.org/TR/SVG/implnote.html#ArcOutOfRangeParameters
134355d49053d1b6db42e013eb3409ffcfc7e235c685caryclark    if (!rx || !ry) {
1344fc75253c82f838d0e687d05f5d7f82ebe6f26d5bcaryclark        this->lineTo(x, y);
134555d49053d1b6db42e013eb3409ffcfc7e235c685caryclark        return;
134655d49053d1b6db42e013eb3409ffcfc7e235c685caryclark    }
134755d49053d1b6db42e013eb3409ffcfc7e235c685caryclark    // If the current point and target point for the arc are identical, it should be treated as a
134855d49053d1b6db42e013eb3409ffcfc7e235c685caryclark    // zero length path. This ensures continuity in animations.
134955d49053d1b6db42e013eb3409ffcfc7e235c685caryclark    srcPts[1].set(x, y);
135055d49053d1b6db42e013eb3409ffcfc7e235c685caryclark    if (srcPts[0] == srcPts[1]) {
1351fc75253c82f838d0e687d05f5d7f82ebe6f26d5bcaryclark        this->lineTo(x, y);
135255d49053d1b6db42e013eb3409ffcfc7e235c685caryclark        return;
135355d49053d1b6db42e013eb3409ffcfc7e235c685caryclark    }
135455d49053d1b6db42e013eb3409ffcfc7e235c685caryclark    rx = SkScalarAbs(rx);
135555d49053d1b6db42e013eb3409ffcfc7e235c685caryclark    ry = SkScalarAbs(ry);
135655d49053d1b6db42e013eb3409ffcfc7e235c685caryclark    SkVector midPointDistance = srcPts[0] - srcPts[1];
135755d49053d1b6db42e013eb3409ffcfc7e235c685caryclark    midPointDistance *= 0.5f;
135855d49053d1b6db42e013eb3409ffcfc7e235c685caryclark
135955d49053d1b6db42e013eb3409ffcfc7e235c685caryclark    SkMatrix pointTransform;
136055d49053d1b6db42e013eb3409ffcfc7e235c685caryclark    pointTransform.setRotate(-angle);
136155d49053d1b6db42e013eb3409ffcfc7e235c685caryclark
136255d49053d1b6db42e013eb3409ffcfc7e235c685caryclark    SkPoint transformedMidPoint;
136355d49053d1b6db42e013eb3409ffcfc7e235c685caryclark    pointTransform.mapPoints(&transformedMidPoint, &midPointDistance, 1);
136455d49053d1b6db42e013eb3409ffcfc7e235c685caryclark    SkScalar squareRx = rx * rx;
136555d49053d1b6db42e013eb3409ffcfc7e235c685caryclark    SkScalar squareRy = ry * ry;
136655d49053d1b6db42e013eb3409ffcfc7e235c685caryclark    SkScalar squareX = transformedMidPoint.fX * transformedMidPoint.fX;
136755d49053d1b6db42e013eb3409ffcfc7e235c685caryclark    SkScalar squareY = transformedMidPoint.fY * transformedMidPoint.fY;
136855d49053d1b6db42e013eb3409ffcfc7e235c685caryclark
136955d49053d1b6db42e013eb3409ffcfc7e235c685caryclark    // Check if the radii are big enough to draw the arc, scale radii if not.
137055d49053d1b6db42e013eb3409ffcfc7e235c685caryclark    // http://www.w3.org/TR/SVG/implnote.html#ArcCorrectionOutOfRangeRadii
137155d49053d1b6db42e013eb3409ffcfc7e235c685caryclark    SkScalar radiiScale = squareX / squareRx + squareY / squareRy;
137255d49053d1b6db42e013eb3409ffcfc7e235c685caryclark    if (radiiScale > 1) {
137355d49053d1b6db42e013eb3409ffcfc7e235c685caryclark        radiiScale = SkScalarSqrt(radiiScale);
137455d49053d1b6db42e013eb3409ffcfc7e235c685caryclark        rx *= radiiScale;
137555d49053d1b6db42e013eb3409ffcfc7e235c685caryclark        ry *= radiiScale;
137655d49053d1b6db42e013eb3409ffcfc7e235c685caryclark    }
137755d49053d1b6db42e013eb3409ffcfc7e235c685caryclark
137855d49053d1b6db42e013eb3409ffcfc7e235c685caryclark    pointTransform.setScale(1 / rx, 1 / ry);
137955d49053d1b6db42e013eb3409ffcfc7e235c685caryclark    pointTransform.preRotate(-angle);
138055d49053d1b6db42e013eb3409ffcfc7e235c685caryclark
138155d49053d1b6db42e013eb3409ffcfc7e235c685caryclark    SkPoint unitPts[2];
138255d49053d1b6db42e013eb3409ffcfc7e235c685caryclark    pointTransform.mapPoints(unitPts, srcPts, (int) SK_ARRAY_COUNT(unitPts));
138355d49053d1b6db42e013eb3409ffcfc7e235c685caryclark    SkVector delta = unitPts[1] - unitPts[0];
138455d49053d1b6db42e013eb3409ffcfc7e235c685caryclark
138555d49053d1b6db42e013eb3409ffcfc7e235c685caryclark    SkScalar d = delta.fX * delta.fX + delta.fY * delta.fY;
138655d49053d1b6db42e013eb3409ffcfc7e235c685caryclark    SkScalar scaleFactorSquared = SkTMax(1 / d - 0.25f, 0.f);
138755d49053d1b6db42e013eb3409ffcfc7e235c685caryclark
138855d49053d1b6db42e013eb3409ffcfc7e235c685caryclark    SkScalar scaleFactor = SkScalarSqrt(scaleFactorSquared);
138955d49053d1b6db42e013eb3409ffcfc7e235c685caryclark    if (SkToBool(arcSweep) != SkToBool(arcLarge)) {  // flipped from the original implementation
139055d49053d1b6db42e013eb3409ffcfc7e235c685caryclark        scaleFactor = -scaleFactor;
139155d49053d1b6db42e013eb3409ffcfc7e235c685caryclark    }
139255d49053d1b6db42e013eb3409ffcfc7e235c685caryclark    delta.scale(scaleFactor);
139355d49053d1b6db42e013eb3409ffcfc7e235c685caryclark    SkPoint centerPoint = unitPts[0] + unitPts[1];
139455d49053d1b6db42e013eb3409ffcfc7e235c685caryclark    centerPoint *= 0.5f;
139555d49053d1b6db42e013eb3409ffcfc7e235c685caryclark    centerPoint.offset(-delta.fY, delta.fX);
139655d49053d1b6db42e013eb3409ffcfc7e235c685caryclark    unitPts[0] -= centerPoint;
139755d49053d1b6db42e013eb3409ffcfc7e235c685caryclark    unitPts[1] -= centerPoint;
139855d49053d1b6db42e013eb3409ffcfc7e235c685caryclark    SkScalar theta1 = SkScalarATan2(unitPts[0].fY, unitPts[0].fX);
139955d49053d1b6db42e013eb3409ffcfc7e235c685caryclark    SkScalar theta2 = SkScalarATan2(unitPts[1].fY, unitPts[1].fX);
140055d49053d1b6db42e013eb3409ffcfc7e235c685caryclark    SkScalar thetaArc = theta2 - theta1;
140155d49053d1b6db42e013eb3409ffcfc7e235c685caryclark    if (thetaArc < 0 && !arcSweep) {  // arcSweep flipped from the original implementation
140255d49053d1b6db42e013eb3409ffcfc7e235c685caryclark        thetaArc += SK_ScalarPI * 2;
140355d49053d1b6db42e013eb3409ffcfc7e235c685caryclark    } else if (thetaArc > 0 && arcSweep) {  // arcSweep flipped from the original implementation
140455d49053d1b6db42e013eb3409ffcfc7e235c685caryclark        thetaArc -= SK_ScalarPI * 2;
140555d49053d1b6db42e013eb3409ffcfc7e235c685caryclark    }
140655d49053d1b6db42e013eb3409ffcfc7e235c685caryclark    pointTransform.setRotate(angle);
140755d49053d1b6db42e013eb3409ffcfc7e235c685caryclark    pointTransform.preScale(rx, ry);
140855d49053d1b6db42e013eb3409ffcfc7e235c685caryclark
140955d49053d1b6db42e013eb3409ffcfc7e235c685caryclark    int segments = SkScalarCeilToInt(SkScalarAbs(thetaArc / (SK_ScalarPI / 2)));
141055d49053d1b6db42e013eb3409ffcfc7e235c685caryclark    SkScalar thetaWidth = thetaArc / segments;
141155d49053d1b6db42e013eb3409ffcfc7e235c685caryclark    SkScalar t = SkScalarTan(0.5f * thetaWidth);
141255d49053d1b6db42e013eb3409ffcfc7e235c685caryclark    if (!SkScalarIsFinite(t)) {
141355d49053d1b6db42e013eb3409ffcfc7e235c685caryclark        return;
141455d49053d1b6db42e013eb3409ffcfc7e235c685caryclark    }
141555d49053d1b6db42e013eb3409ffcfc7e235c685caryclark    SkScalar startTheta = theta1;
141655d49053d1b6db42e013eb3409ffcfc7e235c685caryclark    SkScalar w = SkScalarSqrt(SK_ScalarHalf + SkScalarCos(thetaWidth) * SK_ScalarHalf);
141755d49053d1b6db42e013eb3409ffcfc7e235c685caryclark    for (int i = 0; i < segments; ++i) {
141855d49053d1b6db42e013eb3409ffcfc7e235c685caryclark        SkScalar endTheta = startTheta + thetaWidth;
141955d49053d1b6db42e013eb3409ffcfc7e235c685caryclark        SkScalar cosEndTheta, sinEndTheta = SkScalarSinCos(endTheta, &cosEndTheta);
142055d49053d1b6db42e013eb3409ffcfc7e235c685caryclark
142155d49053d1b6db42e013eb3409ffcfc7e235c685caryclark        unitPts[1].set(cosEndTheta, sinEndTheta);
142255d49053d1b6db42e013eb3409ffcfc7e235c685caryclark        unitPts[1] += centerPoint;
142355d49053d1b6db42e013eb3409ffcfc7e235c685caryclark        unitPts[0] = unitPts[1];
142455d49053d1b6db42e013eb3409ffcfc7e235c685caryclark        unitPts[0].offset(t * sinEndTheta, -t * cosEndTheta);
142555d49053d1b6db42e013eb3409ffcfc7e235c685caryclark        SkPoint mapped[2];
142655d49053d1b6db42e013eb3409ffcfc7e235c685caryclark        pointTransform.mapPoints(mapped, unitPts, (int) SK_ARRAY_COUNT(unitPts));
142755d49053d1b6db42e013eb3409ffcfc7e235c685caryclark        this->conicTo(mapped[0], mapped[1], w);
142855d49053d1b6db42e013eb3409ffcfc7e235c685caryclark        startTheta = endTheta;
142955d49053d1b6db42e013eb3409ffcfc7e235c685caryclark    }
143055d49053d1b6db42e013eb3409ffcfc7e235c685caryclark}
143155d49053d1b6db42e013eb3409ffcfc7e235c685caryclark
143255d49053d1b6db42e013eb3409ffcfc7e235c685caryclarkvoid SkPath::rArcTo(SkScalar rx, SkScalar ry, SkScalar xAxisRotate, SkPath::ArcSize largeArc,
143355d49053d1b6db42e013eb3409ffcfc7e235c685caryclark                    SkPath::Direction sweep, SkScalar dx, SkScalar dy) {
143455d49053d1b6db42e013eb3409ffcfc7e235c685caryclark    SkPoint currentPoint;
143555d49053d1b6db42e013eb3409ffcfc7e235c685caryclark    this->getLastPt(&currentPoint);
143655d49053d1b6db42e013eb3409ffcfc7e235c685caryclark    this->arcTo(rx, ry, xAxisRotate, largeArc, sweep, currentPoint.fX + dx, currentPoint.fY + dy);
143755d49053d1b6db42e013eb3409ffcfc7e235c685caryclark}
143855d49053d1b6db42e013eb3409ffcfc7e235c685caryclark
14391cc385b7a427ec8b511c56744a02046a60dd3fd8robertphillips@google.comvoid SkPath::addArc(const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle) {
14408a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    if (oval.isEmpty() || 0 == sweepAngle) {
14418a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        return;
14428a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    }
14438a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
14448a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    const SkScalar kFullCircleAngle = SkIntToScalar(360);
14458a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
14468a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    if (sweepAngle >= kFullCircleAngle || sweepAngle <= -kFullCircleAngle) {
14471978ce22eb488e3f05670189ea35d9dfae6a6784bsalomon        // We can treat the arc as an oval if it begins at one of our legal starting positions.
14481978ce22eb488e3f05670189ea35d9dfae6a6784bsalomon        // See SkPath::addOval() docs.
14491978ce22eb488e3f05670189ea35d9dfae6a6784bsalomon        SkScalar startOver90 = startAngle / 90.f;
14501978ce22eb488e3f05670189ea35d9dfae6a6784bsalomon        SkScalar startOver90I = SkScalarRoundToScalar(startOver90);
14511978ce22eb488e3f05670189ea35d9dfae6a6784bsalomon        SkScalar error = startOver90 - startOver90I;
14521978ce22eb488e3f05670189ea35d9dfae6a6784bsalomon        if (SkScalarNearlyEqual(error, 0)) {
14531978ce22eb488e3f05670189ea35d9dfae6a6784bsalomon            // Index 1 is at startAngle == 0.
14541978ce22eb488e3f05670189ea35d9dfae6a6784bsalomon            SkScalar startIndex = std::fmod(startOver90I + 1.f, 4.f);
14551978ce22eb488e3f05670189ea35d9dfae6a6784bsalomon            startIndex = startIndex < 0 ? startIndex + 4.f : startIndex;
14561978ce22eb488e3f05670189ea35d9dfae6a6784bsalomon            this->addOval(oval, sweepAngle > 0 ? kCW_Direction : kCCW_Direction,
14571978ce22eb488e3f05670189ea35d9dfae6a6784bsalomon                          (unsigned) startIndex);
14581978ce22eb488e3f05670189ea35d9dfae6a6784bsalomon            return;
14591978ce22eb488e3f05670189ea35d9dfae6a6784bsalomon        }
14608a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    }
14611978ce22eb488e3f05670189ea35d9dfae6a6784bsalomon    this->arcTo(oval, startAngle, sweepAngle, true);
14628a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com}
14638a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
14648a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com/*
14658a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    Need to handle the case when the angle is sharp, and our computed end-points
14668a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    for the arc go behind pt1 and/or p2...
14678a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com*/
1468c778904a5b686617ad7fdec850ddc21e103dca0freedvoid SkPath::arcTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2, SkScalar radius) {
1469a8b326c01a92e7f331c2c5dcf75cd7ce7a99ce73reed    if (radius == 0) {
1470a8b326c01a92e7f331c2c5dcf75cd7ce7a99ce73reed        this->lineTo(x1, y1);
1471a8b326c01a92e7f331c2c5dcf75cd7ce7a99ce73reed        return;
1472a8b326c01a92e7f331c2c5dcf75cd7ce7a99ce73reed    }
1473a8b326c01a92e7f331c2c5dcf75cd7ce7a99ce73reed
1474a8b326c01a92e7f331c2c5dcf75cd7ce7a99ce73reed    SkVector before, after;
1475abf15c189b6333aa77a6c3f566cc67f4634ba338reed@google.com
14768a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    // need to know our prev pt so we can construct tangent vectors
14778a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    {
14788a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        SkPoint start;
14798a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        this->getLastPt(&start);
148060eaa398ebdded0fb7957724c170baabef811e17senorblanco@chromium.org        // Handle degenerate cases by adding a line to the first point and
148160eaa398ebdded0fb7957724c170baabef811e17senorblanco@chromium.org        // bailing out.
14828a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        before.setNormalize(x1 - start.fX, y1 - start.fY);
14838a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        after.setNormalize(x2 - x1, y2 - y1);
14848a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    }
1485abf15c189b6333aa77a6c3f566cc67f4634ba338reed@google.com
14868a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    SkScalar cosh = SkPoint::DotProduct(before, after);
14878a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    SkScalar sinh = SkPoint::CrossProduct(before, after);
14888a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
14898a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    if (SkScalarNearlyZero(sinh)) {   // angle is too tight
149060eaa398ebdded0fb7957724c170baabef811e17senorblanco@chromium.org        this->lineTo(x1, y1);
14918a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        return;
14928a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    }
1493abf15c189b6333aa77a6c3f566cc67f4634ba338reed@google.com
1494a99b6ceff92183b424634f2e7276b9ea1d59e69dMike Reed    SkScalar dist = SkScalarAbs(radius * (1 - cosh) / sinh);
14958a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
1496a99b6ceff92183b424634f2e7276b9ea1d59e69dMike Reed    SkScalar xx = x1 - dist * before.fX;
1497a99b6ceff92183b424634f2e7276b9ea1d59e69dMike Reed    SkScalar yy = y1 - dist * before.fY;
149888651aeb551fb02003a7600679e8e7df8a589e7fcaryclark    after.setLength(dist);
149988651aeb551fb02003a7600679e8e7df8a589e7fcaryclark    this->lineTo(xx, yy);
150088651aeb551fb02003a7600679e8e7df8a589e7fcaryclark    SkScalar weight = SkScalarSqrt(SK_ScalarHalf + cosh * SK_ScalarHalf);
150188651aeb551fb02003a7600679e8e7df8a589e7fcaryclark    this->conicTo(x1, y1, x1 + after.fX, y1 + after.fY, weight);
15028a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com}
15038a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
15048a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com///////////////////////////////////////////////////////////////////////////////
15058a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
150614747e58f8127a6d6b3c748bf0642b0d6a3a79e8commit-bot@chromium.orgvoid SkPath::addPath(const SkPath& path, SkScalar dx, SkScalar dy, AddPathMode mode) {
15078a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    SkMatrix matrix;
15088a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
15098a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    matrix.setTranslate(dx, dy);
151014747e58f8127a6d6b3c748bf0642b0d6a3a79e8commit-bot@chromium.org    this->addPath(path, matrix, mode);
15118a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com}
15128a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
151314747e58f8127a6d6b3c748bf0642b0d6a3a79e8commit-bot@chromium.orgvoid SkPath::addPath(const SkPath& path, const SkMatrix& matrix, AddPathMode mode) {
15141dfe88e00aeddf20690fd2469fd17e43f670ee3absalomon@google.com    SkPathRef::Editor(&fPathRef, path.countVerbs(), path.countPoints());
15158a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
15166630d8d8ea7a897a18e3d950bab9fa40f065804aschenney@chromium.org    RawIter iter(path);
15178a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    SkPoint pts[4];
15188a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    Verb    verb;
15198a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
15208a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    SkMatrix::MapPtsProc proc = matrix.getMapPtsProc();
152114747e58f8127a6d6b3c748bf0642b0d6a3a79e8commit-bot@chromium.org    bool firstVerb = true;
15228a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    while ((verb = iter.next(pts)) != kDone_Verb) {
15238a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        switch (verb) {
15248a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            case kMove_Verb:
15258a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com                proc(matrix, &pts[0], &pts[0], 1);
152614747e58f8127a6d6b3c748bf0642b0d6a3a79e8commit-bot@chromium.org                if (firstVerb && mode == kExtend_AddPathMode && !isEmpty()) {
152714747e58f8127a6d6b3c748bf0642b0d6a3a79e8commit-bot@chromium.org                    injectMoveToIfNeeded(); // In case last contour is closed
152814747e58f8127a6d6b3c748bf0642b0d6a3a79e8commit-bot@chromium.org                    this->lineTo(pts[0]);
152914747e58f8127a6d6b3c748bf0642b0d6a3a79e8commit-bot@chromium.org                } else {
153014747e58f8127a6d6b3c748bf0642b0d6a3a79e8commit-bot@chromium.org                    this->moveTo(pts[0]);
153114747e58f8127a6d6b3c748bf0642b0d6a3a79e8commit-bot@chromium.org                }
15328a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com                break;
15338a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            case kLine_Verb:
15348a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com                proc(matrix, &pts[1], &pts[1], 1);
15358a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com                this->lineTo(pts[1]);
15368a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com                break;
15378a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            case kQuad_Verb:
15388a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com                proc(matrix, &pts[1], &pts[1], 2);
15398a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com                this->quadTo(pts[1], pts[2]);
15408a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com                break;
1541277c3f87656c44e0a651ed0dd56efa16c0ab07b4reed@google.com            case kConic_Verb:
1542277c3f87656c44e0a651ed0dd56efa16c0ab07b4reed@google.com                proc(matrix, &pts[1], &pts[1], 2);
1543277c3f87656c44e0a651ed0dd56efa16c0ab07b4reed@google.com                this->conicTo(pts[1], pts[2], iter.conicWeight());
1544277c3f87656c44e0a651ed0dd56efa16c0ab07b4reed@google.com                break;
15458a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            case kCubic_Verb:
15468a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com                proc(matrix, &pts[1], &pts[1], 3);
15478a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com                this->cubicTo(pts[1], pts[2], pts[3]);
15488a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com                break;
15498a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            case kClose_Verb:
15508a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com                this->close();
15518a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com                break;
15528a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            default:
15530c00f21fee3f5cfa3aa7e5d46ff94cb8cf340451tomhudson@google.com                SkDEBUGFAIL("unknown verb");
15548a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        }
155514747e58f8127a6d6b3c748bf0642b0d6a3a79e8commit-bot@chromium.org        firstVerb = false;
15568a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    }
15578a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com}
15588a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
15598a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com///////////////////////////////////////////////////////////////////////////////
15608a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
1561277c3f87656c44e0a651ed0dd56efa16c0ab07b4reed@google.comstatic int pts_in_verb(unsigned verb) {
1562277c3f87656c44e0a651ed0dd56efa16c0ab07b4reed@google.com    static const uint8_t gPtsInVerb[] = {
1563277c3f87656c44e0a651ed0dd56efa16c0ab07b4reed@google.com        1,  // kMove
1564277c3f87656c44e0a651ed0dd56efa16c0ab07b4reed@google.com        1,  // kLine
1565277c3f87656c44e0a651ed0dd56efa16c0ab07b4reed@google.com        2,  // kQuad
1566277c3f87656c44e0a651ed0dd56efa16c0ab07b4reed@google.com        2,  // kConic
1567277c3f87656c44e0a651ed0dd56efa16c0ab07b4reed@google.com        3,  // kCubic
1568277c3f87656c44e0a651ed0dd56efa16c0ab07b4reed@google.com        0,  // kClose
1569277c3f87656c44e0a651ed0dd56efa16c0ab07b4reed@google.com        0   // kDone
1570277c3f87656c44e0a651ed0dd56efa16c0ab07b4reed@google.com    };
1571277c3f87656c44e0a651ed0dd56efa16c0ab07b4reed@google.com
1572277c3f87656c44e0a651ed0dd56efa16c0ab07b4reed@google.com    SkASSERT(verb < SK_ARRAY_COUNT(gPtsInVerb));
1573277c3f87656c44e0a651ed0dd56efa16c0ab07b4reed@google.com    return gPtsInVerb[verb];
1574277c3f87656c44e0a651ed0dd56efa16c0ab07b4reed@google.com}
15758a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
15768a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com// ignore the last point of the 1st contour
15778a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.comvoid SkPath::reversePathTo(const SkPath& path) {
157851c5678f258736736c4a5d48d4e82c73be225428caryclark    const uint8_t* verbs = path.fPathRef->verbsMemBegin(); // points at the last verb
157951c5678f258736736c4a5d48d4e82c73be225428caryclark    if (!verbs) {  // empty path returns nullptr
15808a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        return;
15818a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    }
158251c5678f258736736c4a5d48d4e82c73be225428caryclark    const uint8_t* verbsEnd = path.fPathRef->verbs() - 1; // points just past the first verb
158351c5678f258736736c4a5d48d4e82c73be225428caryclark    SkASSERT(verbsEnd[0] == kMove_Verb);
158451c5678f258736736c4a5d48d4e82c73be225428caryclark    const SkPoint*  pts = path.fPathRef->pointsEnd() - 1;
158551c5678f258736736c4a5d48d4e82c73be225428caryclark    const SkScalar* conicWeights = path.fPathRef->conicWeightsEnd();
15868a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
158751c5678f258736736c4a5d48d4e82c73be225428caryclark    while (verbs < verbsEnd) {
158851c5678f258736736c4a5d48d4e82c73be225428caryclark        uint8_t v = *verbs++;
158951c5678f258736736c4a5d48d4e82c73be225428caryclark        pts -= pts_in_verb(v);
159051c5678f258736736c4a5d48d4e82c73be225428caryclark        switch (v) {
159151c5678f258736736c4a5d48d4e82c73be225428caryclark            case kMove_Verb:
159251c5678f258736736c4a5d48d4e82c73be225428caryclark                // if the path has multiple contours, stop after reversing the last
159351c5678f258736736c4a5d48d4e82c73be225428caryclark                return;
15948a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            case kLine_Verb:
159551c5678f258736736c4a5d48d4e82c73be225428caryclark                this->lineTo(pts[0]);
15968a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com                break;
15978a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            case kQuad_Verb:
159851c5678f258736736c4a5d48d4e82c73be225428caryclark                this->quadTo(pts[1], pts[0]);
15998a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com                break;
1600277c3f87656c44e0a651ed0dd56efa16c0ab07b4reed@google.com            case kConic_Verb:
160151c5678f258736736c4a5d48d4e82c73be225428caryclark                this->conicTo(pts[1], pts[0], *--conicWeights);
1602277c3f87656c44e0a651ed0dd56efa16c0ab07b4reed@google.com                break;
16038a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            case kCubic_Verb:
160451c5678f258736736c4a5d48d4e82c73be225428caryclark                this->cubicTo(pts[2], pts[1], pts[0]);
160551c5678f258736736c4a5d48d4e82c73be225428caryclark                break;
160651c5678f258736736c4a5d48d4e82c73be225428caryclark            case kClose_Verb:
160751c5678f258736736c4a5d48d4e82c73be225428caryclark                SkASSERT(verbs - path.fPathRef->verbsMemBegin() == 1);
16088a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com                break;
16098a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            default:
16100c00f21fee3f5cfa3aa7e5d46ff94cb8cf340451tomhudson@google.com                SkDEBUGFAIL("bad verb");
16118a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com                break;
16128a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        }
16138a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    }
16148a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com}
16158a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
161663d73749fbe36491403ea521005fd298dc70a94creed@google.comvoid SkPath::reverseAddPath(const SkPath& src) {
16171dfe88e00aeddf20690fd2469fd17e43f670ee3absalomon@google.com    SkPathRef::Editor ed(&fPathRef, src.fPathRef->countPoints(), src.fPathRef->countVerbs());
161863d73749fbe36491403ea521005fd298dc70a94creed@google.com
16191dfe88e00aeddf20690fd2469fd17e43f670ee3absalomon@google.com    const SkPoint* pts = src.fPathRef->pointsEnd();
16201dfe88e00aeddf20690fd2469fd17e43f670ee3absalomon@google.com    // we will iterator through src's verbs backwards
16211dfe88e00aeddf20690fd2469fd17e43f670ee3absalomon@google.com    const uint8_t* verbs = src.fPathRef->verbsMemBegin(); // points at the last verb
16221dfe88e00aeddf20690fd2469fd17e43f670ee3absalomon@google.com    const uint8_t* verbsEnd = src.fPathRef->verbs(); // points just past the first verb
1623277c3f87656c44e0a651ed0dd56efa16c0ab07b4reed@google.com    const SkScalar* conicWeights = src.fPathRef->conicWeightsEnd();
162463d73749fbe36491403ea521005fd298dc70a94creed@google.com
162563d73749fbe36491403ea521005fd298dc70a94creed@google.com    bool needMove = true;
162663d73749fbe36491403ea521005fd298dc70a94creed@google.com    bool needClose = false;
16271dfe88e00aeddf20690fd2469fd17e43f670ee3absalomon@google.com    while (verbs < verbsEnd) {
16281dfe88e00aeddf20690fd2469fd17e43f670ee3absalomon@google.com        uint8_t v = *(verbs++);
1629277c3f87656c44e0a651ed0dd56efa16c0ab07b4reed@google.com        int n = pts_in_verb(v);
163063d73749fbe36491403ea521005fd298dc70a94creed@google.com
163163d73749fbe36491403ea521005fd298dc70a94creed@google.com        if (needMove) {
163263d73749fbe36491403ea521005fd298dc70a94creed@google.com            --pts;
163363d73749fbe36491403ea521005fd298dc70a94creed@google.com            this->moveTo(pts->fX, pts->fY);
163463d73749fbe36491403ea521005fd298dc70a94creed@google.com            needMove = false;
163563d73749fbe36491403ea521005fd298dc70a94creed@google.com        }
163663d73749fbe36491403ea521005fd298dc70a94creed@google.com        pts -= n;
163763d73749fbe36491403ea521005fd298dc70a94creed@google.com        switch (v) {
163863d73749fbe36491403ea521005fd298dc70a94creed@google.com            case kMove_Verb:
163963d73749fbe36491403ea521005fd298dc70a94creed@google.com                if (needClose) {
164063d73749fbe36491403ea521005fd298dc70a94creed@google.com                    this->close();
164163d73749fbe36491403ea521005fd298dc70a94creed@google.com                    needClose = false;
164263d73749fbe36491403ea521005fd298dc70a94creed@google.com                }
164363d73749fbe36491403ea521005fd298dc70a94creed@google.com                needMove = true;
164463d73749fbe36491403ea521005fd298dc70a94creed@google.com                pts += 1;   // so we see the point in "if (needMove)" above
164563d73749fbe36491403ea521005fd298dc70a94creed@google.com                break;
164663d73749fbe36491403ea521005fd298dc70a94creed@google.com            case kLine_Verb:
164763d73749fbe36491403ea521005fd298dc70a94creed@google.com                this->lineTo(pts[0]);
164863d73749fbe36491403ea521005fd298dc70a94creed@google.com                break;
164963d73749fbe36491403ea521005fd298dc70a94creed@google.com            case kQuad_Verb:
165063d73749fbe36491403ea521005fd298dc70a94creed@google.com                this->quadTo(pts[1], pts[0]);
165163d73749fbe36491403ea521005fd298dc70a94creed@google.com                break;
1652277c3f87656c44e0a651ed0dd56efa16c0ab07b4reed@google.com            case kConic_Verb:
1653277c3f87656c44e0a651ed0dd56efa16c0ab07b4reed@google.com                this->conicTo(pts[1], pts[0], *--conicWeights);
1654277c3f87656c44e0a651ed0dd56efa16c0ab07b4reed@google.com                break;
165563d73749fbe36491403ea521005fd298dc70a94creed@google.com            case kCubic_Verb:
165663d73749fbe36491403ea521005fd298dc70a94creed@google.com                this->cubicTo(pts[2], pts[1], pts[0]);
165763d73749fbe36491403ea521005fd298dc70a94creed@google.com                break;
165863d73749fbe36491403ea521005fd298dc70a94creed@google.com            case kClose_Verb:
165963d73749fbe36491403ea521005fd298dc70a94creed@google.com                needClose = true;
166063d73749fbe36491403ea521005fd298dc70a94creed@google.com                break;
166163d73749fbe36491403ea521005fd298dc70a94creed@google.com            default:
1662330313a8a8343876ee596da39da06a5d69badd9cmtklein@google.com                SkDEBUGFAIL("unexpected verb");
166363d73749fbe36491403ea521005fd298dc70a94creed@google.com        }
166463d73749fbe36491403ea521005fd298dc70a94creed@google.com    }
166563d73749fbe36491403ea521005fd298dc70a94creed@google.com}
166663d73749fbe36491403ea521005fd298dc70a94creed@google.com
16678a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com///////////////////////////////////////////////////////////////////////////////
16688a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
16698a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.comvoid SkPath::offset(SkScalar dx, SkScalar dy, SkPath* dst) const {
16708a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    SkMatrix    matrix;
16718a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
16728a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    matrix.setTranslate(dx, dy);
16738a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    this->transform(matrix, dst);
16748a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com}
16758a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
16768a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.comstatic void subdivide_cubic_to(SkPath* path, const SkPoint pts[4],
16778a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com                               int level = 2) {
16788a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    if (--level >= 0) {
16798a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        SkPoint tmp[7];
16808a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
16818a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        SkChopCubicAtHalf(pts, tmp);
16828a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        subdivide_cubic_to(path, &tmp[0], level);
16838a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        subdivide_cubic_to(path, &tmp[3], level);
16848a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    } else {
16858a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        path->cubicTo(pts[1], pts[2], pts[3]);
16868a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    }
16878a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com}
16888a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
16898a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.comvoid SkPath::transform(const SkMatrix& matrix, SkPath* dst) const {
16908a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    SkDEBUGCODE(this->validate();)
169196fcdcc219d2a0d3579719b84b28bede76efba64halcanary    if (dst == nullptr) {
16928a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        dst = (SkPath*)this;
16938a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    }
16948a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
16958d430185e08d2067584837a76b7193b803fee7a0tomhudson@google.com    if (matrix.hasPerspective()) {
16968a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        SkPath  tmp;
16978a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        tmp.fFillType = fFillType;
16988a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
16998a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        SkPath::Iter    iter(*this, false);
17008a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        SkPoint         pts[4];
17018a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        SkPath::Verb    verb;
17028a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
17034a3b714d73e585a3985d614600c6b79d5c8b1f1ereed@google.com        while ((verb = iter.next(pts, false)) != kDone_Verb) {
17048a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            switch (verb) {
17058a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com                case kMove_Verb:
17068a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com                    tmp.moveTo(pts[0]);
17078a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com                    break;
17088a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com                case kLine_Verb:
17098a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com                    tmp.lineTo(pts[1]);
17108a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com                    break;
17118a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com                case kQuad_Verb:
1712220f926d9d4b38a9018c922c095847bbd261f583reed                    // promote the quad to a conic
1713220f926d9d4b38a9018c922c095847bbd261f583reed                    tmp.conicTo(pts[1], pts[2],
1714220f926d9d4b38a9018c922c095847bbd261f583reed                                SkConic::TransformW(pts, SK_Scalar1, matrix));
17158a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com                    break;
1716277c3f87656c44e0a651ed0dd56efa16c0ab07b4reed@google.com                case kConic_Verb:
1717220f926d9d4b38a9018c922c095847bbd261f583reed                    tmp.conicTo(pts[1], pts[2],
1718220f926d9d4b38a9018c922c095847bbd261f583reed                                SkConic::TransformW(pts, iter.conicWeight(), matrix));
1719277c3f87656c44e0a651ed0dd56efa16c0ab07b4reed@google.com                    break;
17208a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com                case kCubic_Verb:
17218a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com                    subdivide_cubic_to(&tmp, pts);
17228a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com                    break;
17238a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com                case kClose_Verb:
17248a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com                    tmp.close();
17258a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com                    break;
17268a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com                default:
17270c00f21fee3f5cfa3aa7e5d46ff94cb8cf340451tomhudson@google.com                    SkDEBUGFAIL("unknown verb");
17288a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com                    break;
17298a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            }
17308a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        }
17318a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
17328a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        dst->swap(tmp);
17331dfe88e00aeddf20690fd2469fd17e43f670ee3absalomon@google.com        SkPathRef::Editor ed(&dst->fPathRef);
17341dfe88e00aeddf20690fd2469fd17e43f670ee3absalomon@google.com        matrix.mapPoints(ed.points(), ed.pathRef()->countPoints());
1735026beb52a29a620290fcfb24f1e7e9e75547b80freed        dst->fFirstDirection = SkPathPriv::kUnknown_FirstDirection;
17368a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    } else {
17371dfe88e00aeddf20690fd2469fd17e43f670ee3absalomon@google.com        SkPathRef::CreateTransformedCopy(&dst->fPathRef, *fPathRef.get(), matrix);
17381dfe88e00aeddf20690fd2469fd17e43f670ee3absalomon@google.com
17398a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        if (this != dst) {
17408a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            dst->fFillType = fFillType;
17412a6f8abf35ff8c2640c24478d9d2eaedff5e78eareed@google.com            dst->fConvexity = fConvexity;
1742b3eb687f8a89eb1eacd1afb4016401eb392f66abjvanverth            dst->fIsVolatile = fIsVolatile;
17438a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        }
17446aa2965ca814dd3329b65398b5c5af980e54b101bsalomon@google.com
1745026beb52a29a620290fcfb24f1e7e9e75547b80freed        if (SkPathPriv::kUnknown_FirstDirection == fFirstDirection) {
1746026beb52a29a620290fcfb24f1e7e9e75547b80freed            dst->fFirstDirection = SkPathPriv::kUnknown_FirstDirection;
174730c174b9ce6b9777ee50ae0d0565a01b2a060f01bsalomon@google.com        } else {
174830c174b9ce6b9777ee50ae0d0565a01b2a060f01bsalomon@google.com            SkScalar det2x2 =
1749a99b6ceff92183b424634f2e7276b9ea1d59e69dMike Reed                matrix.get(SkMatrix::kMScaleX) * matrix.get(SkMatrix::kMScaleY) -
1750a99b6ceff92183b424634f2e7276b9ea1d59e69dMike Reed                matrix.get(SkMatrix::kMSkewX)  * matrix.get(SkMatrix::kMSkewY);
175130c174b9ce6b9777ee50ae0d0565a01b2a060f01bsalomon@google.com            if (det2x2 < 0) {
17529f4dbca3290cfaca7dd17b71eb6b5b3a0ba5323eherb                dst->fFirstDirection = SkPathPriv::OppositeFirstDirection(
17539f4dbca3290cfaca7dd17b71eb6b5b3a0ba5323eherb                        (SkPathPriv::FirstDirection)fFirstDirection.load());
175430c174b9ce6b9777ee50ae0d0565a01b2a060f01bsalomon@google.com            } else if (det2x2 > 0) {
17559f4dbca3290cfaca7dd17b71eb6b5b3a0ba5323eherb                dst->fFirstDirection = fFirstDirection.load();
175630c174b9ce6b9777ee50ae0d0565a01b2a060f01bsalomon@google.com            } else {
1757a1a097ee814d05a92487d85db8ad02e1d852fd6fcommit-bot@chromium.org                dst->fConvexity = kUnknown_Convexity;
1758026beb52a29a620290fcfb24f1e7e9e75547b80freed                dst->fFirstDirection = SkPathPriv::kUnknown_FirstDirection;
175930c174b9ce6b9777ee50ae0d0565a01b2a060f01bsalomon@google.com            }
176030c174b9ce6b9777ee50ae0d0565a01b2a060f01bsalomon@google.com        }
176130c174b9ce6b9777ee50ae0d0565a01b2a060f01bsalomon@google.com
17628a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        SkDEBUGCODE(dst->validate();)
17638a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    }
17648a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com}
17658a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
17668a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com///////////////////////////////////////////////////////////////////////////////
17678a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com///////////////////////////////////////////////////////////////////////////////
17688a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
17694da06ab3351f2a96f9216d96106db33a77b19644schenney@chromium.orgenum SegmentState {
1770fde6b414d9cbfb69c7c3b046dab78e748d8d2ed4schenney@chromium.org    kEmptyContour_SegmentState,   // The current contour is empty. We may be
1771fde6b414d9cbfb69c7c3b046dab78e748d8d2ed4schenney@chromium.org                                  // starting processing or we may have just
1772fde6b414d9cbfb69c7c3b046dab78e748d8d2ed4schenney@chromium.org                                  // closed a contour.
17734da06ab3351f2a96f9216d96106db33a77b19644schenney@chromium.org    kAfterMove_SegmentState,      // We have seen a move, but nothing else.
17744da06ab3351f2a96f9216d96106db33a77b19644schenney@chromium.org    kAfterPrimitive_SegmentState  // We have seen a primitive but not yet
17754da06ab3351f2a96f9216d96106db33a77b19644schenney@chromium.org                                  // closed the path. Also the initial state.
17768a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com};
17778a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
17788a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.comSkPath::Iter::Iter() {
17798a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com#ifdef SK_DEBUG
178096fcdcc219d2a0d3579719b84b28bede76efba64halcanary    fPts = nullptr;
178196fcdcc219d2a0d3579719b84b28bede76efba64halcanary    fConicWeights = nullptr;
17828a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    fMoveTo.fX = fMoveTo.fY = fLastPt.fX = fLastPt.fY = 0;
17834da06ab3351f2a96f9216d96106db33a77b19644schenney@chromium.org    fForceClose = fCloseLine = false;
1784fde6b414d9cbfb69c7c3b046dab78e748d8d2ed4schenney@chromium.org    fSegmentState = kEmptyContour_SegmentState;
17858a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com#endif
17868a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    // need to init enough to make next() harmlessly return kDone_Verb
178796fcdcc219d2a0d3579719b84b28bede76efba64halcanary    fVerbs = nullptr;
178896fcdcc219d2a0d3579719b84b28bede76efba64halcanary    fVerbStop = nullptr;
17898a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    fNeedClose = false;
17908a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com}
17918a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
17928a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.comSkPath::Iter::Iter(const SkPath& path, bool forceClose) {
17938a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    this->setPath(path, forceClose);
17948a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com}
17958a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
17968a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.comvoid SkPath::Iter::setPath(const SkPath& path, bool forceClose) {
17971dfe88e00aeddf20690fd2469fd17e43f670ee3absalomon@google.com    fPts = path.fPathRef->points();
17981dfe88e00aeddf20690fd2469fd17e43f670ee3absalomon@google.com    fVerbs = path.fPathRef->verbs();
17991dfe88e00aeddf20690fd2469fd17e43f670ee3absalomon@google.com    fVerbStop = path.fPathRef->verbsMemBegin();
18006942442ef7cc018ac136dd379ad6a30902a060e5caryclark    fConicWeights = path.fPathRef->conicWeights();
18016942442ef7cc018ac136dd379ad6a30902a060e5caryclark    if (fConicWeights) {
18026942442ef7cc018ac136dd379ad6a30902a060e5caryclark      fConicWeights -= 1;  // begin one behind
18036942442ef7cc018ac136dd379ad6a30902a060e5caryclark    }
18044da06ab3351f2a96f9216d96106db33a77b19644schenney@chromium.org    fLastPt.fX = fLastPt.fY = 0;
180572785c4c8928a8b0fc5bbdb48929f9356554daceschenney@chromium.org    fMoveTo.fX = fMoveTo.fY = 0;
18068a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    fForceClose = SkToU8(forceClose);
18078a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    fNeedClose = false;
1808fde6b414d9cbfb69c7c3b046dab78e748d8d2ed4schenney@chromium.org    fSegmentState = kEmptyContour_SegmentState;
18098a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com}
18108a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
18118a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.combool SkPath::Iter::isClosedContour() const {
181296fcdcc219d2a0d3579719b84b28bede76efba64halcanary    if (fVerbs == nullptr || fVerbs == fVerbStop) {
18138a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        return false;
18148a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    }
18158a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    if (fForceClose) {
18168a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        return true;
18178a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    }
18188a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
18198a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    const uint8_t* verbs = fVerbs;
18208a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    const uint8_t* stop = fVerbStop;
1821abf15c189b6333aa77a6c3f566cc67f4634ba338reed@google.com
18221dfe88e00aeddf20690fd2469fd17e43f670ee3absalomon@google.com    if (kMove_Verb == *(verbs - 1)) {
18231dfe88e00aeddf20690fd2469fd17e43f670ee3absalomon@google.com        verbs -= 1; // skip the initial moveto
18248a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    }
18258a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
18261dfe88e00aeddf20690fd2469fd17e43f670ee3absalomon@google.com    while (verbs > stop) {
18271dfe88e00aeddf20690fd2469fd17e43f670ee3absalomon@google.com        // verbs points one beyond the current verb, decrement first.
18281dfe88e00aeddf20690fd2469fd17e43f670ee3absalomon@google.com        unsigned v = *(--verbs);
18298a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        if (kMove_Verb == v) {
18308a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            break;
18318a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        }
18328a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        if (kClose_Verb == v) {
18338a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            return true;
18348a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        }
18358a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    }
18368a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    return false;
18378a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com}
18388a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
18398a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.comSkPath::Verb SkPath::Iter::autoClose(SkPoint pts[2]) {
18409e25dbf589539dd44244bc2581590bd7591e17a2reed@google.com    SkASSERT(pts);
18418a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    if (fLastPt != fMoveTo) {
18424ddfe357da4a94f587b6ca18b398c23478fa1b24reed@android.com        // A special case: if both points are NaN, SkPoint::operation== returns
18434ddfe357da4a94f587b6ca18b398c23478fa1b24reed@android.com        // false, but the iterator expects that they are treated as the same.
18444ddfe357da4a94f587b6ca18b398c23478fa1b24reed@android.com        // (consider SkPoint is a 2-dimension float point).
18459da1ae3f35a6f25adf4f58ae2589129ceec6d11breed@android.com        if (SkScalarIsNaN(fLastPt.fX) || SkScalarIsNaN(fLastPt.fY) ||
18469da1ae3f35a6f25adf4f58ae2589129ceec6d11breed@android.com            SkScalarIsNaN(fMoveTo.fX) || SkScalarIsNaN(fMoveTo.fY)) {
18474ddfe357da4a94f587b6ca18b398c23478fa1b24reed@android.com            return kClose_Verb;
18484ddfe357da4a94f587b6ca18b398c23478fa1b24reed@android.com        }
18494ddfe357da4a94f587b6ca18b398c23478fa1b24reed@android.com
18509e25dbf589539dd44244bc2581590bd7591e17a2reed@google.com        pts[0] = fLastPt;
18519e25dbf589539dd44244bc2581590bd7591e17a2reed@google.com        pts[1] = fMoveTo;
18528a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        fLastPt = fMoveTo;
18538a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        fCloseLine = true;
18548a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        return kLine_Verb;
1855b3b8dfa31326c51dab8b5ed569e19ee715582d1bbsalomon@google.com    } else {
1856b3b8dfa31326c51dab8b5ed569e19ee715582d1bbsalomon@google.com        pts[0] = fMoveTo;
1857b3b8dfa31326c51dab8b5ed569e19ee715582d1bbsalomon@google.com        return kClose_Verb;
18588a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    }
18598a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com}
18608a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
18619e25dbf589539dd44244bc2581590bd7591e17a2reed@google.comconst SkPoint& SkPath::Iter::cons_moveTo() {
18624da06ab3351f2a96f9216d96106db33a77b19644schenney@chromium.org    if (fSegmentState == kAfterMove_SegmentState) {
18634da06ab3351f2a96f9216d96106db33a77b19644schenney@chromium.org        // Set the first return pt to the move pt
18644da06ab3351f2a96f9216d96106db33a77b19644schenney@chromium.org        fSegmentState = kAfterPrimitive_SegmentState;
18659e25dbf589539dd44244bc2581590bd7591e17a2reed@google.com        return fMoveTo;
18668a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    } else {
18674da06ab3351f2a96f9216d96106db33a77b19644schenney@chromium.org        SkASSERT(fSegmentState == kAfterPrimitive_SegmentState);
18684da06ab3351f2a96f9216d96106db33a77b19644schenney@chromium.org         // Set the first return pt to the last pt of the previous primitive.
18699e25dbf589539dd44244bc2581590bd7591e17a2reed@google.com        return fPts[-1];
18708a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    }
18718a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com}
18728a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
1873e8c5666e0387e70bd921e01558e627af3f1411dbcaryclarkvoid SkPath::Iter::consumeDegenerateSegments(bool exact) {
18744da06ab3351f2a96f9216d96106db33a77b19644schenney@chromium.org    // We need to step over anything that will not move the current draw point
18754da06ab3351f2a96f9216d96106db33a77b19644schenney@chromium.org    // forward before the next move is seen
18764da06ab3351f2a96f9216d96106db33a77b19644schenney@chromium.org    const uint8_t* lastMoveVerb = 0;
18774da06ab3351f2a96f9216d96106db33a77b19644schenney@chromium.org    const SkPoint* lastMovePt = 0;
187896fcdcc219d2a0d3579719b84b28bede76efba64halcanary    const SkScalar* lastMoveWeight = nullptr;
18794da06ab3351f2a96f9216d96106db33a77b19644schenney@chromium.org    SkPoint lastPt = fLastPt;
18804da06ab3351f2a96f9216d96106db33a77b19644schenney@chromium.org    while (fVerbs != fVerbStop) {
18811dfe88e00aeddf20690fd2469fd17e43f670ee3absalomon@google.com        unsigned verb = *(fVerbs - 1); // fVerbs is one beyond the current verb
18824da06ab3351f2a96f9216d96106db33a77b19644schenney@chromium.org        switch (verb) {
18834da06ab3351f2a96f9216d96106db33a77b19644schenney@chromium.org            case kMove_Verb:
18844da06ab3351f2a96f9216d96106db33a77b19644schenney@chromium.org                // Keep a record of this most recent move
18854da06ab3351f2a96f9216d96106db33a77b19644schenney@chromium.org                lastMoveVerb = fVerbs;
18864da06ab3351f2a96f9216d96106db33a77b19644schenney@chromium.org                lastMovePt = fPts;
1887b8de1f46594c3cd9c537f0b128c6d6eb30a9f463robertphillips                lastMoveWeight = fConicWeights;
1888b0af6dad94f3c51ea0d5d6426a9509354338c6b2schenney@chromium.org                lastPt = fPts[0];
18891dfe88e00aeddf20690fd2469fd17e43f670ee3absalomon@google.com                fVerbs--;
18904da06ab3351f2a96f9216d96106db33a77b19644schenney@chromium.org                fPts++;
18914da06ab3351f2a96f9216d96106db33a77b19644schenney@chromium.org                break;
18924da06ab3351f2a96f9216d96106db33a77b19644schenney@chromium.org
18934da06ab3351f2a96f9216d96106db33a77b19644schenney@chromium.org            case kClose_Verb:
18947e963605d5b8177c30afa0d8e5541b0fbe1b6e13schenney@chromium.org                // A close when we are in a segment is always valid except when it
18957e963605d5b8177c30afa0d8e5541b0fbe1b6e13schenney@chromium.org                // follows a move which follows a segment.
18967e963605d5b8177c30afa0d8e5541b0fbe1b6e13schenney@chromium.org                if (fSegmentState == kAfterPrimitive_SegmentState && !lastMoveVerb) {
18974da06ab3351f2a96f9216d96106db33a77b19644schenney@chromium.org                    return;
18984da06ab3351f2a96f9216d96106db33a77b19644schenney@chromium.org                }
18994da06ab3351f2a96f9216d96106db33a77b19644schenney@chromium.org                // A close at any other time must be ignored
19001dfe88e00aeddf20690fd2469fd17e43f670ee3absalomon@google.com                fVerbs--;
19014da06ab3351f2a96f9216d96106db33a77b19644schenney@chromium.org                break;
19024da06ab3351f2a96f9216d96106db33a77b19644schenney@chromium.org
19034da06ab3351f2a96f9216d96106db33a77b19644schenney@chromium.org            case kLine_Verb:
1904e8c5666e0387e70bd921e01558e627af3f1411dbcaryclark                if (!IsLineDegenerate(lastPt, fPts[0], exact)) {
19054da06ab3351f2a96f9216d96106db33a77b19644schenney@chromium.org                    if (lastMoveVerb) {
19064da06ab3351f2a96f9216d96106db33a77b19644schenney@chromium.org                        fVerbs = lastMoveVerb;
19074da06ab3351f2a96f9216d96106db33a77b19644schenney@chromium.org                        fPts = lastMovePt;
1908b8de1f46594c3cd9c537f0b128c6d6eb30a9f463robertphillips                        fConicWeights = lastMoveWeight;
19094da06ab3351f2a96f9216d96106db33a77b19644schenney@chromium.org                        return;
19104da06ab3351f2a96f9216d96106db33a77b19644schenney@chromium.org                    }
19114da06ab3351f2a96f9216d96106db33a77b19644schenney@chromium.org                    return;
19124da06ab3351f2a96f9216d96106db33a77b19644schenney@chromium.org                }
19134da06ab3351f2a96f9216d96106db33a77b19644schenney@chromium.org                // Ignore this line and continue
19141dfe88e00aeddf20690fd2469fd17e43f670ee3absalomon@google.com                fVerbs--;
19154da06ab3351f2a96f9216d96106db33a77b19644schenney@chromium.org                fPts++;
19164da06ab3351f2a96f9216d96106db33a77b19644schenney@chromium.org                break;
19174da06ab3351f2a96f9216d96106db33a77b19644schenney@chromium.org
1918277c3f87656c44e0a651ed0dd56efa16c0ab07b4reed@google.com            case kConic_Verb:
19194da06ab3351f2a96f9216d96106db33a77b19644schenney@chromium.org            case kQuad_Verb:
1920e8c5666e0387e70bd921e01558e627af3f1411dbcaryclark                if (!IsQuadDegenerate(lastPt, fPts[0], fPts[1], exact)) {
19214da06ab3351f2a96f9216d96106db33a77b19644schenney@chromium.org                    if (lastMoveVerb) {
19224da06ab3351f2a96f9216d96106db33a77b19644schenney@chromium.org                        fVerbs = lastMoveVerb;
19234da06ab3351f2a96f9216d96106db33a77b19644schenney@chromium.org                        fPts = lastMovePt;
1924b8de1f46594c3cd9c537f0b128c6d6eb30a9f463robertphillips                        fConicWeights = lastMoveWeight;
19254da06ab3351f2a96f9216d96106db33a77b19644schenney@chromium.org                        return;
19264da06ab3351f2a96f9216d96106db33a77b19644schenney@chromium.org                    }
19274da06ab3351f2a96f9216d96106db33a77b19644schenney@chromium.org                    return;
19284da06ab3351f2a96f9216d96106db33a77b19644schenney@chromium.org                }
19294da06ab3351f2a96f9216d96106db33a77b19644schenney@chromium.org                // Ignore this line and continue
19301dfe88e00aeddf20690fd2469fd17e43f670ee3absalomon@google.com                fVerbs--;
19314da06ab3351f2a96f9216d96106db33a77b19644schenney@chromium.org                fPts += 2;
1932277c3f87656c44e0a651ed0dd56efa16c0ab07b4reed@google.com                fConicWeights += (kConic_Verb == verb);
19334da06ab3351f2a96f9216d96106db33a77b19644schenney@chromium.org                break;
19344da06ab3351f2a96f9216d96106db33a77b19644schenney@chromium.org
19354da06ab3351f2a96f9216d96106db33a77b19644schenney@chromium.org            case kCubic_Verb:
1936e8c5666e0387e70bd921e01558e627af3f1411dbcaryclark                if (!IsCubicDegenerate(lastPt, fPts[0], fPts[1], fPts[2], exact)) {
19374da06ab3351f2a96f9216d96106db33a77b19644schenney@chromium.org                    if (lastMoveVerb) {
19384da06ab3351f2a96f9216d96106db33a77b19644schenney@chromium.org                        fVerbs = lastMoveVerb;
19394da06ab3351f2a96f9216d96106db33a77b19644schenney@chromium.org                        fPts = lastMovePt;
1940b8de1f46594c3cd9c537f0b128c6d6eb30a9f463robertphillips                        fConicWeights = lastMoveWeight;
19414da06ab3351f2a96f9216d96106db33a77b19644schenney@chromium.org                        return;
19424da06ab3351f2a96f9216d96106db33a77b19644schenney@chromium.org                    }
19434da06ab3351f2a96f9216d96106db33a77b19644schenney@chromium.org                    return;
19444da06ab3351f2a96f9216d96106db33a77b19644schenney@chromium.org                }
19454da06ab3351f2a96f9216d96106db33a77b19644schenney@chromium.org                // Ignore this line and continue
19461dfe88e00aeddf20690fd2469fd17e43f670ee3absalomon@google.com                fVerbs--;
19474da06ab3351f2a96f9216d96106db33a77b19644schenney@chromium.org                fPts += 3;
19484da06ab3351f2a96f9216d96106db33a77b19644schenney@chromium.org                break;
1949b0af6dad94f3c51ea0d5d6426a9509354338c6b2schenney@chromium.org
19504da06ab3351f2a96f9216d96106db33a77b19644schenney@chromium.org            default:
19510c00f21fee3f5cfa3aa7e5d46ff94cb8cf340451tomhudson@google.com                SkDEBUGFAIL("Should never see kDone_Verb");
19524da06ab3351f2a96f9216d96106db33a77b19644schenney@chromium.org        }
19534da06ab3351f2a96f9216d96106db33a77b19644schenney@chromium.org    }
19544da06ab3351f2a96f9216d96106db33a77b19644schenney@chromium.org}
19554da06ab3351f2a96f9216d96106db33a77b19644schenney@chromium.org
19564a3b714d73e585a3985d614600c6b79d5c8b1f1ereed@google.comSkPath::Verb SkPath::Iter::doNext(SkPoint ptsParam[4]) {
19579e25dbf589539dd44244bc2581590bd7591e17a2reed@google.com    SkASSERT(ptsParam);
19584da06ab3351f2a96f9216d96106db33a77b19644schenney@chromium.org
19598a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    if (fVerbs == fVerbStop) {
19604da06ab3351f2a96f9216d96106db33a77b19644schenney@chromium.org        // Close the curve if requested and if there is some curve to close
19614da06ab3351f2a96f9216d96106db33a77b19644schenney@chromium.org        if (fNeedClose && fSegmentState == kAfterPrimitive_SegmentState) {
19629e25dbf589539dd44244bc2581590bd7591e17a2reed@google.com            if (kLine_Verb == this->autoClose(ptsParam)) {
19638a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com                return kLine_Verb;
19648a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            }
19658a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            fNeedClose = false;
19668a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            return kClose_Verb;
19678a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        }
19688a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        return kDone_Verb;
19698a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    }
19708a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
19711dfe88e00aeddf20690fd2469fd17e43f670ee3absalomon@google.com    // fVerbs is one beyond the current verb, decrement first
19721dfe88e00aeddf20690fd2469fd17e43f670ee3absalomon@google.com    unsigned verb = *(--fVerbs);
19739e25dbf589539dd44244bc2581590bd7591e17a2reed@google.com    const SkPoint* SK_RESTRICT srcPts = fPts;
19749e25dbf589539dd44244bc2581590bd7591e17a2reed@google.com    SkPoint* SK_RESTRICT       pts = ptsParam;
19758a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
19768a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    switch (verb) {
19778a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        case kMove_Verb:
19788a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            if (fNeedClose) {
19791dfe88e00aeddf20690fd2469fd17e43f670ee3absalomon@google.com                fVerbs++; // move back one verb
19808a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com                verb = this->autoClose(pts);
19818a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com                if (verb == kClose_Verb) {
19828a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com                    fNeedClose = false;
19838a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com                }
19848a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com                return (Verb)verb;
19858a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            }
19868a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            if (fVerbs == fVerbStop) {    // might be a trailing moveto
19878a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com                return kDone_Verb;
19888a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            }
1989b0af6dad94f3c51ea0d5d6426a9509354338c6b2schenney@chromium.org            fMoveTo = *srcPts;
19909e25dbf589539dd44244bc2581590bd7591e17a2reed@google.com            pts[0] = *srcPts;
19918a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            srcPts += 1;
1992b0af6dad94f3c51ea0d5d6426a9509354338c6b2schenney@chromium.org            fSegmentState = kAfterMove_SegmentState;
19934da06ab3351f2a96f9216d96106db33a77b19644schenney@chromium.org            fLastPt = fMoveTo;
19948a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            fNeedClose = fForceClose;
19958a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            break;
19968a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        case kLine_Verb:
19979e25dbf589539dd44244bc2581590bd7591e17a2reed@google.com            pts[0] = this->cons_moveTo();
19989e25dbf589539dd44244bc2581590bd7591e17a2reed@google.com            pts[1] = srcPts[0];
19998a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            fLastPt = srcPts[0];
20008a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            fCloseLine = false;
20018a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            srcPts += 1;
20028a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            break;
2003277c3f87656c44e0a651ed0dd56efa16c0ab07b4reed@google.com        case kConic_Verb:
2004277c3f87656c44e0a651ed0dd56efa16c0ab07b4reed@google.com            fConicWeights += 1;
2005277c3f87656c44e0a651ed0dd56efa16c0ab07b4reed@google.com            // fall-through
20068a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        case kQuad_Verb:
20079e25dbf589539dd44244bc2581590bd7591e17a2reed@google.com            pts[0] = this->cons_moveTo();
20089e25dbf589539dd44244bc2581590bd7591e17a2reed@google.com            memcpy(&pts[1], srcPts, 2 * sizeof(SkPoint));
20098a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            fLastPt = srcPts[1];
20108a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            srcPts += 2;
20118a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            break;
20128a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        case kCubic_Verb:
20139e25dbf589539dd44244bc2581590bd7591e17a2reed@google.com            pts[0] = this->cons_moveTo();
20149e25dbf589539dd44244bc2581590bd7591e17a2reed@google.com            memcpy(&pts[1], srcPts, 3 * sizeof(SkPoint));
20158a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            fLastPt = srcPts[2];
20168a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            srcPts += 3;
20178a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            break;
20188a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        case kClose_Verb:
20198a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            verb = this->autoClose(pts);
20208a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            if (verb == kLine_Verb) {
20211dfe88e00aeddf20690fd2469fd17e43f670ee3absalomon@google.com                fVerbs++; // move back one verb
20228a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            } else {
20238a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com                fNeedClose = false;
2024fde6b414d9cbfb69c7c3b046dab78e748d8d2ed4schenney@chromium.org                fSegmentState = kEmptyContour_SegmentState;
20258a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            }
20264da06ab3351f2a96f9216d96106db33a77b19644schenney@chromium.org            fLastPt = fMoveTo;
20278a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            break;
20288a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    }
20298a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    fPts = srcPts;
20308a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    return (Verb)verb;
20318a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com}
20328a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
20338a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com///////////////////////////////////////////////////////////////////////////////
20348a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
20358a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com/*
203694e75ee46a569cbcdf61fb7f04ee3a69d3ca0896djsollen@google.com    Format in compressed buffer: [ptCount, verbCount, pts[], verbs[]]
20378a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com*/
20388a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
20394faa869cdabbdcf4867118b4a1272296baaeeb52commit-bot@chromium.orgsize_t SkPath::writeToMemory(void* storage) const {
20408a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    SkDEBUGCODE(this->validate();)
20418a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
204296fcdcc219d2a0d3579719b84b28bede76efba64halcanary    if (nullptr == storage) {
20436900641e02c3b6c7ee15d0aa5bd4af0d42db480fcaryclark        const int byteCount = sizeof(int32_t) * 2 + fPathRef->writeSize();
204494e75ee46a569cbcdf61fb7f04ee3a69d3ca0896djsollen@google.com        return SkAlign4(byteCount);
204594e75ee46a569cbcdf61fb7f04ee3a69d3ca0896djsollen@google.com    }
204694e75ee46a569cbcdf61fb7f04ee3a69d3ca0896djsollen@google.com
204794e75ee46a569cbcdf61fb7f04ee3a69d3ca0896djsollen@google.com    SkWBuffer   buffer(storage);
204801ec2eb42e9c64f8d06afd51f80c055710147141robertphillips@google.com
2049466310dbd3073add2ec934e336c30deaaf702eaerobertphillips@google.com    int32_t packed = (fConvexity << kConvexity_SerializationShift) |
205001ec2eb42e9c64f8d06afd51f80c055710147141robertphillips@google.com                     (fFillType << kFillType_SerializationShift) |
2051026beb52a29a620290fcfb24f1e7e9e75547b80freed                     (fFirstDirection << kDirection_SerializationShift) |
20528f086023bf615b2661b82bcf59cdedde78ad7374reed                     (fIsVolatile << kIsVolatile_SerializationShift) |
20538f086023bf615b2661b82bcf59cdedde78ad7374reed                     kCurrent_Version;
205401ec2eb42e9c64f8d06afd51f80c055710147141robertphillips@google.com
20552972bb5fd2441709026b350c6b9b66eecd80f868robertphillips@google.com    buffer.write32(packed);
20566900641e02c3b6c7ee15d0aa5bd4af0d42db480fcaryclark    buffer.write32(fLastMoveToIndex);
205701ec2eb42e9c64f8d06afd51f80c055710147141robertphillips@google.com
20581dfe88e00aeddf20690fd2469fd17e43f670ee3absalomon@google.com    fPathRef->writeToBuffer(&buffer);
205901ec2eb42e9c64f8d06afd51f80c055710147141robertphillips@google.com
206094e75ee46a569cbcdf61fb7f04ee3a69d3ca0896djsollen@google.com    buffer.padToAlign4();
20614faa869cdabbdcf4867118b4a1272296baaeeb52commit-bot@chromium.org    return buffer.pos();
20628a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com}
20638a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
20644faa869cdabbdcf4867118b4a1272296baaeeb52commit-bot@chromium.orgsize_t SkPath::readFromMemory(const void* storage, size_t length) {
20651026ccf1d2de57ae6e7d2f30ea92c245942121d3Mike Reed    SkRBuffer buffer(storage, length);
206601ec2eb42e9c64f8d06afd51f80c055710147141robertphillips@google.com
20678f457e3230f1a4ce737f512ffbb5c919b8d02407commit-bot@chromium.org    int32_t packed;
20688f457e3230f1a4ce737f512ffbb5c919b8d02407commit-bot@chromium.org    if (!buffer.readS32(&packed)) {
20698f457e3230f1a4ce737f512ffbb5c919b8d02407commit-bot@chromium.org        return 0;
20708f457e3230f1a4ce737f512ffbb5c919b8d02407commit-bot@chromium.org    }
20718f457e3230f1a4ce737f512ffbb5c919b8d02407commit-bot@chromium.org
20728f086023bf615b2661b82bcf59cdedde78ad7374reed    unsigned version = packed & 0xFF;
20736900641e02c3b6c7ee15d0aa5bd4af0d42db480fcaryclark    if (version >= kPathPrivLastMoveToIndex_Version && !buffer.readS32(&fLastMoveToIndex)) {
20746900641e02c3b6c7ee15d0aa5bd4af0d42db480fcaryclark        return 0;
20756900641e02c3b6c7ee15d0aa5bd4af0d42db480fcaryclark    }
20761b24933e52f50773de29332387a12721811f3012mtklein
207701ec2eb42e9c64f8d06afd51f80c055710147141robertphillips@google.com    fConvexity = (packed >> kConvexity_SerializationShift) & 0xFF;
20787070a3c44b24da5715dc4da618cb014f87e92451robertphillips    fFillType = (packed >> kFillType_SerializationShift) & 0x3;
20798f086023bf615b2661b82bcf59cdedde78ad7374reed    uint8_t dir = (packed >> kDirection_SerializationShift) & 0x3;
2080b3eb687f8a89eb1eacd1afb4016401eb392f66abjvanverth    fIsVolatile = (packed >> kIsVolatile_SerializationShift) & 0x1;
2081fed2ab648341ec153ad2af746a31d368963171e4commit-bot@chromium.org    SkPathRef* pathRef = SkPathRef::CreateFromBuffer(&buffer);
2082f8aec588bfd2df17130ee93593a8f4ae781afe1fajuma    if (!pathRef) {
2083f8aec588bfd2df17130ee93593a8f4ae781afe1fajuma        return 0;
2084f8aec588bfd2df17130ee93593a8f4ae781afe1fajuma    }
2085f8aec588bfd2df17130ee93593a8f4ae781afe1fajuma
2086f8aec588bfd2df17130ee93593a8f4ae781afe1fajuma    fPathRef.reset(pathRef);
2087f8aec588bfd2df17130ee93593a8f4ae781afe1fajuma    SkDEBUGCODE(this->validate();)
2088f8aec588bfd2df17130ee93593a8f4ae781afe1fajuma    buffer.skipToAlign4();
2089abf15c189b6333aa77a6c3f566cc67f4634ba338reed@google.com
20908f086023bf615b2661b82bcf59cdedde78ad7374reed    // compatibility check
20918f086023bf615b2661b82bcf59cdedde78ad7374reed    if (version < kPathPrivFirstDirection_Version) {
20928f086023bf615b2661b82bcf59cdedde78ad7374reed        switch (dir) {  // old values
20938f086023bf615b2661b82bcf59cdedde78ad7374reed            case 0:
20948f086023bf615b2661b82bcf59cdedde78ad7374reed                fFirstDirection = SkPathPriv::kUnknown_FirstDirection;
20958f086023bf615b2661b82bcf59cdedde78ad7374reed                break;
20968f086023bf615b2661b82bcf59cdedde78ad7374reed            case 1:
20978f086023bf615b2661b82bcf59cdedde78ad7374reed                fFirstDirection = SkPathPriv::kCW_FirstDirection;
20988f086023bf615b2661b82bcf59cdedde78ad7374reed                break;
20998f086023bf615b2661b82bcf59cdedde78ad7374reed            case 2:
21008f086023bf615b2661b82bcf59cdedde78ad7374reed                fFirstDirection = SkPathPriv::kCCW_FirstDirection;
21018f086023bf615b2661b82bcf59cdedde78ad7374reed                break;
21028f086023bf615b2661b82bcf59cdedde78ad7374reed            default:
21038f086023bf615b2661b82bcf59cdedde78ad7374reed                SkASSERT(false);
21048f086023bf615b2661b82bcf59cdedde78ad7374reed        }
21058f086023bf615b2661b82bcf59cdedde78ad7374reed    } else {
21068f086023bf615b2661b82bcf59cdedde78ad7374reed        fFirstDirection = dir;
21078f086023bf615b2661b82bcf59cdedde78ad7374reed    }
21088f086023bf615b2661b82bcf59cdedde78ad7374reed
2109f8aec588bfd2df17130ee93593a8f4ae781afe1fajuma    return buffer.pos();
21108a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com}
21118a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
21128a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com///////////////////////////////////////////////////////////////////////////////
21138a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
21144d1955c43aaab045511b74a495dfbea4ef0057c5Ben Wagner#include "SkString.h"
2115e05fed0d6339c63c8cceff74af0b8d120c07e54creed#include "SkStringUtils.h"
211666a5d8bf13fe98baae268db0211e9c25e5ece7facaryclark#include "SkStream.h"
211751bbe75875d654251c6e8bec21ca773ffd5f39d0reed@google.com
211851bbe75875d654251c6e8bec21ca773ffd5f39d0reed@google.comstatic void append_params(SkString* str, const char label[], const SkPoint pts[],
2119e05fed0d6339c63c8cceff74af0b8d120c07e54creed                          int count, SkScalarAsStringType strType, SkScalar conicWeight = -1) {
212051bbe75875d654251c6e8bec21ca773ffd5f39d0reed@google.com    str->append(label);
212151bbe75875d654251c6e8bec21ca773ffd5f39d0reed@google.com    str->append("(");
212215dd300ac6d7695b4d2aca81d8f3648eae704451skia.committer@gmail.com
212351bbe75875d654251c6e8bec21ca773ffd5f39d0reed@google.com    const SkScalar* values = &pts[0].fX;
212451bbe75875d654251c6e8bec21ca773ffd5f39d0reed@google.com    count *= 2;
212551bbe75875d654251c6e8bec21ca773ffd5f39d0reed@google.com
212651bbe75875d654251c6e8bec21ca773ffd5f39d0reed@google.com    for (int i = 0; i < count; ++i) {
2127e05fed0d6339c63c8cceff74af0b8d120c07e54creed        SkAppendScalar(str, values[i], strType);
212851bbe75875d654251c6e8bec21ca773ffd5f39d0reed@google.com        if (i < count - 1) {
212951bbe75875d654251c6e8bec21ca773ffd5f39d0reed@google.com            str->append(", ");
213051bbe75875d654251c6e8bec21ca773ffd5f39d0reed@google.com        }
213151bbe75875d654251c6e8bec21ca773ffd5f39d0reed@google.com    }
2132277c3f87656c44e0a651ed0dd56efa16c0ab07b4reed@google.com    if (conicWeight >= 0) {
2133277c3f87656c44e0a651ed0dd56efa16c0ab07b4reed@google.com        str->append(", ");
2134e05fed0d6339c63c8cceff74af0b8d120c07e54creed        SkAppendScalar(str, conicWeight, strType);
2135277c3f87656c44e0a651ed0dd56efa16c0ab07b4reed@google.com    }
213608fa28cd31c96b4ebd9cb532539c3a8c88803d90caryclark    str->append(");");
2137e05fed0d6339c63c8cceff74af0b8d120c07e54creed    if (kHex_SkScalarAsStringType == strType) {
213808fa28cd31c96b4ebd9cb532539c3a8c88803d90caryclark        str->append("  // ");
213908fa28cd31c96b4ebd9cb532539c3a8c88803d90caryclark        for (int i = 0; i < count; ++i) {
2140e05fed0d6339c63c8cceff74af0b8d120c07e54creed            SkAppendScalarDec(str, values[i]);
214108fa28cd31c96b4ebd9cb532539c3a8c88803d90caryclark            if (i < count - 1) {
214208fa28cd31c96b4ebd9cb532539c3a8c88803d90caryclark                str->append(", ");
214308fa28cd31c96b4ebd9cb532539c3a8c88803d90caryclark            }
214408fa28cd31c96b4ebd9cb532539c3a8c88803d90caryclark        }
214508fa28cd31c96b4ebd9cb532539c3a8c88803d90caryclark        if (conicWeight >= 0) {
214608fa28cd31c96b4ebd9cb532539c3a8c88803d90caryclark            str->append(", ");
2147e05fed0d6339c63c8cceff74af0b8d120c07e54creed            SkAppendScalarDec(str, conicWeight);
214808fa28cd31c96b4ebd9cb532539c3a8c88803d90caryclark        }
214908fa28cd31c96b4ebd9cb532539c3a8c88803d90caryclark    }
215008fa28cd31c96b4ebd9cb532539c3a8c88803d90caryclark    str->append("\n");
215151bbe75875d654251c6e8bec21ca773ffd5f39d0reed@google.com}
215251bbe75875d654251c6e8bec21ca773ffd5f39d0reed@google.com
2153e956259c5a4f71768afb34ec032eaed49dcbe9f2caryclarkvoid SkPath::dump(SkWStream* wStream, bool forceClose, bool dumpAsHex) const {
2154e05fed0d6339c63c8cceff74af0b8d120c07e54creed    SkScalarAsStringType asType = dumpAsHex ? kHex_SkScalarAsStringType : kDec_SkScalarAsStringType;
21558a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    Iter    iter(*this, forceClose);
21568a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    SkPoint pts[4];
21578a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    Verb    verb;
21588a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
215951bbe75875d654251c6e8bec21ca773ffd5f39d0reed@google.com    SkString builder;
21609f67f044dd8765184669af6fd38e7feeff8777f9Cary Clark    char const * const gFillTypeStrs[] = {
21619f67f044dd8765184669af6fd38e7feeff8777f9Cary Clark        "Winding",
21629f67f044dd8765184669af6fd38e7feeff8777f9Cary Clark        "EvenOdd",
21639f67f044dd8765184669af6fd38e7feeff8777f9Cary Clark        "InverseWinding",
21649f67f044dd8765184669af6fd38e7feeff8777f9Cary Clark        "InverseEvenOdd",
21659f67f044dd8765184669af6fd38e7feeff8777f9Cary Clark    };
21669f67f044dd8765184669af6fd38e7feeff8777f9Cary Clark    builder.printf("path.setFillType(SkPath::k%s_FillType);\n",
21679f67f044dd8765184669af6fd38e7feeff8777f9Cary Clark            gFillTypeStrs[(int) this->getFillType()]);
21684a3b714d73e585a3985d614600c6b79d5c8b1f1ereed@google.com    while ((verb = iter.next(pts, false)) != kDone_Verb) {
21698a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        switch (verb) {
21708a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            case kMove_Verb:
2171e05fed0d6339c63c8cceff74af0b8d120c07e54creed                append_params(&builder, "path.moveTo", &pts[0], 1, asType);
21728a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com                break;
21738a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            case kLine_Verb:
2174e05fed0d6339c63c8cceff74af0b8d120c07e54creed                append_params(&builder, "path.lineTo", &pts[1], 1, asType);
21758a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com                break;
21768a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            case kQuad_Verb:
2177e05fed0d6339c63c8cceff74af0b8d120c07e54creed                append_params(&builder, "path.quadTo", &pts[1], 2, asType);
21788a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com                break;
2179277c3f87656c44e0a651ed0dd56efa16c0ab07b4reed@google.com            case kConic_Verb:
2180e05fed0d6339c63c8cceff74af0b8d120c07e54creed                append_params(&builder, "path.conicTo", &pts[1], 2, asType, iter.conicWeight());
2181277c3f87656c44e0a651ed0dd56efa16c0ab07b4reed@google.com                break;
21828a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            case kCubic_Verb:
2183e05fed0d6339c63c8cceff74af0b8d120c07e54creed                append_params(&builder, "path.cubicTo", &pts[1], 3, asType);
21848a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com                break;
21858a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            case kClose_Verb:
218666a5d8bf13fe98baae268db0211e9c25e5ece7facaryclark                builder.append("path.close();\n");
21878a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com                break;
21888a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            default:
21898a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com                SkDebugf("  path: UNKNOWN VERB %d, aborting dump...\n", verb);
21908a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com                verb = kDone_Verb;  // stop the loop
21918a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com                break;
21928a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        }
21931049f1246e7be4ccb68001361efceb8933e6f81ccaryclark        if (!wStream && builder.size()) {
21941049f1246e7be4ccb68001361efceb8933e6f81ccaryclark            SkDebugf("%s", builder.c_str());
21951049f1246e7be4ccb68001361efceb8933e6f81ccaryclark            builder.reset();
21961049f1246e7be4ccb68001361efceb8933e6f81ccaryclark        }
21978a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    }
219866a5d8bf13fe98baae268db0211e9c25e5ece7facaryclark    if (wStream) {
219966a5d8bf13fe98baae268db0211e9c25e5ece7facaryclark        wStream->writeText(builder.c_str());
220066a5d8bf13fe98baae268db0211e9c25e5ece7facaryclark    }
22018a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com}
22028a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
2203e522ca5d5f249bd51a00cb68bb051f811d0a9e85reed@android.comvoid SkPath::dump() const {
220496fcdcc219d2a0d3579719b84b28bede76efba64halcanary    this->dump(nullptr, false, false);
2205e956259c5a4f71768afb34ec032eaed49dcbe9f2caryclark}
2206e956259c5a4f71768afb34ec032eaed49dcbe9f2caryclark
2207e956259c5a4f71768afb34ec032eaed49dcbe9f2caryclarkvoid SkPath::dumpHex() const {
220896fcdcc219d2a0d3579719b84b28bede76efba64halcanary    this->dump(nullptr, false, true);
2209e522ca5d5f249bd51a00cb68bb051f811d0a9e85reed@android.com}
2210e522ca5d5f249bd51a00cb68bb051f811d0a9e85reed@android.com
2211e522ca5d5f249bd51a00cb68bb051f811d0a9e85reed@android.com#ifdef SK_DEBUG
2212e522ca5d5f249bd51a00cb68bb051f811d0a9e85reed@android.comvoid SkPath::validate() const {
2213e522ca5d5f249bd51a00cb68bb051f811d0a9e85reed@android.com    SkASSERT((fFillType & ~3) == 0);
2214abf15c189b6333aa77a6c3f566cc67f4634ba338reed@google.com
2215077348cfd0b4c424393ce83cb9ceded8afe60216djsollen@google.com#ifdef SK_DEBUG_PATH
2216e522ca5d5f249bd51a00cb68bb051f811d0a9e85reed@android.com    if (!fBoundsIsDirty) {
2217e522ca5d5f249bd51a00cb68bb051f811d0a9e85reed@android.com        SkRect bounds;
2218ed02c4d05e3f2ed86dbf4276a69827ab23810598tomhudson@google.com
22191dfe88e00aeddf20690fd2469fd17e43f670ee3absalomon@google.com        bool isFinite = compute_pt_bounds(&bounds, *fPathRef.get());
22205d8d18651a64f62dbb8881794e23f53bf22c9a23robertphillips@google.com        SkASSERT(SkToBool(fIsFinite) == isFinite);
2221ed02c4d05e3f2ed86dbf4276a69827ab23810598tomhudson@google.com
22221dfe88e00aeddf20690fd2469fd17e43f670ee3absalomon@google.com        if (fPathRef->countPoints() <= 1) {
2223e522ca5d5f249bd51a00cb68bb051f811d0a9e85reed@android.com            // if we're empty, fBounds may be empty but translated, so we can't
2224e522ca5d5f249bd51a00cb68bb051f811d0a9e85reed@android.com            // necessarily compare to bounds directly
2225e522ca5d5f249bd51a00cb68bb051f811d0a9e85reed@android.com            // try path.addOval(2, 2, 2, 2) which is empty, but the bounds will
2226e522ca5d5f249bd51a00cb68bb051f811d0a9e85reed@android.com            // be [2, 2, 2, 2]
2227e522ca5d5f249bd51a00cb68bb051f811d0a9e85reed@android.com            SkASSERT(bounds.isEmpty());
2228e522ca5d5f249bd51a00cb68bb051f811d0a9e85reed@android.com            SkASSERT(fBounds.isEmpty());
2229e522ca5d5f249bd51a00cb68bb051f811d0a9e85reed@android.com        } else {
2230eac52bdb6c275a50832c4bba3a439d7fbf7f7807reed@google.com            if (bounds.isEmpty()) {
2231eac52bdb6c275a50832c4bba3a439d7fbf7f7807reed@google.com                SkASSERT(fBounds.isEmpty());
2232eac52bdb6c275a50832c4bba3a439d7fbf7f7807reed@google.com            } else {
22333563c9ee527f524d421964b54d9b09e12ec0bf6breed@google.com                if (!fBounds.isEmpty()) {
22343563c9ee527f524d421964b54d9b09e12ec0bf6breed@google.com                    SkASSERT(fBounds.contains(bounds));
22353563c9ee527f524d421964b54d9b09e12ec0bf6breed@google.com                }
2236eac52bdb6c275a50832c4bba3a439d7fbf7f7807reed@google.com            }
2237e522ca5d5f249bd51a00cb68bb051f811d0a9e85reed@android.com        }
2238e522ca5d5f249bd51a00cb68bb051f811d0a9e85reed@android.com    }
2239077348cfd0b4c424393ce83cb9ceded8afe60216djsollen@google.com#endif // SK_DEBUG_PATH
2240e522ca5d5f249bd51a00cb68bb051f811d0a9e85reed@android.com}
2241077348cfd0b4c424393ce83cb9ceded8afe60216djsollen@google.com#endif // SK_DEBUG
2242e522ca5d5f249bd51a00cb68bb051f811d0a9e85reed@android.com
224304863fa14a44ddf85acbc6268690ebc3f0d1d6dbreed@google.com///////////////////////////////////////////////////////////////////////////////
224404863fa14a44ddf85acbc6268690ebc3f0d1d6dbreed@google.com
22450b7b98260784da07b2b15ea06530ccd8fb6ad02breed@google.comstatic int sign(SkScalar x) { return x < 0; }
22460b7b98260784da07b2b15ea06530ccd8fb6ad02breed@google.com#define kValueNeverReturnedBySign   2
224785b6e399d56d2421980daa432f30910beda41922reed@google.com
2248c506e3007e53fe7d1a77991de11fdb50a86fd532robertphillipsenum DirChange {
2249c506e3007e53fe7d1a77991de11fdb50a86fd532robertphillips    kLeft_DirChange,
2250c506e3007e53fe7d1a77991de11fdb50a86fd532robertphillips    kRight_DirChange,
2251c506e3007e53fe7d1a77991de11fdb50a86fd532robertphillips    kStraight_DirChange,
2252c506e3007e53fe7d1a77991de11fdb50a86fd532robertphillips    kBackwards_DirChange,
2253c506e3007e53fe7d1a77991de11fdb50a86fd532robertphillips
2254c506e3007e53fe7d1a77991de11fdb50a86fd532robertphillips    kInvalid_DirChange
2255c506e3007e53fe7d1a77991de11fdb50a86fd532robertphillips};
2256c506e3007e53fe7d1a77991de11fdb50a86fd532robertphillips
2257c506e3007e53fe7d1a77991de11fdb50a86fd532robertphillips
22588be07bb12d1f7a2d08fa154320fbe6940b370ea1commit-bot@chromium.orgstatic bool almost_equal(SkScalar compA, SkScalar compB) {
2259f91aaecbe9d3630123d803d80f7b29f06c8976c7commit-bot@chromium.org    // The error epsilon was empirically derived; worse case round rects
2260f91aaecbe9d3630123d803d80f7b29f06c8976c7commit-bot@chromium.org    // with a mid point outset by 2x float epsilon in tests had an error
2261f91aaecbe9d3630123d803d80f7b29f06c8976c7commit-bot@chromium.org    // of 12.
2262f91aaecbe9d3630123d803d80f7b29f06c8976c7commit-bot@chromium.org    const int epsilon = 16;
2263f91aaecbe9d3630123d803d80f7b29f06c8976c7commit-bot@chromium.org    if (!SkScalarIsFinite(compA) || !SkScalarIsFinite(compB)) {
2264f91aaecbe9d3630123d803d80f7b29f06c8976c7commit-bot@chromium.org        return false;
2265f91aaecbe9d3630123d803d80f7b29f06c8976c7commit-bot@chromium.org    }
2266a1a097ee814d05a92487d85db8ad02e1d852fd6fcommit-bot@chromium.org    // no need to check for small numbers because SkPath::Iter has removed degenerate values
2267f91aaecbe9d3630123d803d80f7b29f06c8976c7commit-bot@chromium.org    int aBits = SkFloatAs2sCompliment(compA);
2268f91aaecbe9d3630123d803d80f7b29f06c8976c7commit-bot@chromium.org    int bBits = SkFloatAs2sCompliment(compB);
2269f91aaecbe9d3630123d803d80f7b29f06c8976c7commit-bot@chromium.org    return aBits < bBits + epsilon && bBits < aBits + epsilon;
227004863fa14a44ddf85acbc6268690ebc3f0d1d6dbreed@google.com}
227104863fa14a44ddf85acbc6268690ebc3f0d1d6dbreed@google.com
2272b421650e13faa2b77d29b018e78ab07ff693ca32caryclarkstatic bool approximately_zero_when_compared_to(double x, double y) {
2273b421650e13faa2b77d29b018e78ab07ff693ca32caryclark    return x == 0 || fabs(x) < fabs(y * FLT_EPSILON);
2274c506e3007e53fe7d1a77991de11fdb50a86fd532robertphillips}
2275c506e3007e53fe7d1a77991de11fdb50a86fd532robertphillips
2276b421650e13faa2b77d29b018e78ab07ff693ca32caryclark
227704863fa14a44ddf85acbc6268690ebc3f0d1d6dbreed@google.com// only valid for a single contour
227804863fa14a44ddf85acbc6268690ebc3f0d1d6dbreed@google.comstruct Convexicator {
227930c174b9ce6b9777ee50ae0d0565a01b2a060f01bsalomon@google.com    Convexicator()
228030c174b9ce6b9777ee50ae0d0565a01b2a060f01bsalomon@google.com    : fPtCount(0)
228130c174b9ce6b9777ee50ae0d0565a01b2a060f01bsalomon@google.com    , fConvexity(SkPath::kConvex_Convexity)
2282026beb52a29a620290fcfb24f1e7e9e75547b80freed    , fFirstDirection(SkPathPriv::kUnknown_FirstDirection)
22835ccef577902ce1aefa05fb8107ad5d0aba848e7dcaryclark    , fIsFinite(true)
22845ccef577902ce1aefa05fb8107ad5d0aba848e7dcaryclark    , fIsCurve(false) {
2285c506e3007e53fe7d1a77991de11fdb50a86fd532robertphillips        fExpectedDir = kInvalid_DirChange;
228604863fa14a44ddf85acbc6268690ebc3f0d1d6dbreed@google.com        // warnings
22872f124631583a838edaa1e48362031cc6c7f40764djsollen        fPriorPt.set(0,0);
2288f91aaecbe9d3630123d803d80f7b29f06c8976c7commit-bot@chromium.org        fLastPt.set(0, 0);
228904863fa14a44ddf85acbc6268690ebc3f0d1d6dbreed@google.com        fCurrPt.set(0, 0);
22908be07bb12d1f7a2d08fa154320fbe6940b370ea1commit-bot@chromium.org        fLastVec.set(0, 0);
229104863fa14a44ddf85acbc6268690ebc3f0d1d6dbreed@google.com        fFirstVec.set(0, 0);
229285b6e399d56d2421980daa432f30910beda41922reed@google.com
229385b6e399d56d2421980daa432f30910beda41922reed@google.com        fDx = fDy = 0;
22940b7b98260784da07b2b15ea06530ccd8fb6ad02breed@google.com        fSx = fSy = kValueNeverReturnedBySign;
229504863fa14a44ddf85acbc6268690ebc3f0d1d6dbreed@google.com    }
229604863fa14a44ddf85acbc6268690ebc3f0d1d6dbreed@google.com
229704863fa14a44ddf85acbc6268690ebc3f0d1d6dbreed@google.com    SkPath::Convexity getConvexity() const { return fConvexity; }
229804863fa14a44ddf85acbc6268690ebc3f0d1d6dbreed@google.com
229930c174b9ce6b9777ee50ae0d0565a01b2a060f01bsalomon@google.com    /** The direction returned is only valid if the path is determined convex */
2300026beb52a29a620290fcfb24f1e7e9e75547b80freed    SkPathPriv::FirstDirection getFirstDirection() const { return fFirstDirection; }
230130c174b9ce6b9777ee50ae0d0565a01b2a060f01bsalomon@google.com
230204863fa14a44ddf85acbc6268690ebc3f0d1d6dbreed@google.com    void addPt(const SkPoint& pt) {
2303d3d1a988b1ed144b0123dbe594c3a47a63d6451dcaryclark        if (SkPath::kConcave_Convexity == fConvexity || !fIsFinite) {
230404863fa14a44ddf85acbc6268690ebc3f0d1d6dbreed@google.com            return;
230504863fa14a44ddf85acbc6268690ebc3f0d1d6dbreed@google.com        }
230604863fa14a44ddf85acbc6268690ebc3f0d1d6dbreed@google.com
230704863fa14a44ddf85acbc6268690ebc3f0d1d6dbreed@google.com        if (0 == fPtCount) {
230804863fa14a44ddf85acbc6268690ebc3f0d1d6dbreed@google.com            fCurrPt = pt;
230904863fa14a44ddf85acbc6268690ebc3f0d1d6dbreed@google.com            ++fPtCount;
231004863fa14a44ddf85acbc6268690ebc3f0d1d6dbreed@google.com        } else {
231104863fa14a44ddf85acbc6268690ebc3f0d1d6dbreed@google.com            SkVector vec = pt - fCurrPt;
2312d3d1a988b1ed144b0123dbe594c3a47a63d6451dcaryclark            SkScalar lengthSqd = vec.lengthSqd();
2313d3d1a988b1ed144b0123dbe594c3a47a63d6451dcaryclark            if (!SkScalarIsFinite(lengthSqd)) {
2314d3d1a988b1ed144b0123dbe594c3a47a63d6451dcaryclark                fIsFinite = false;
2315e8c5666e0387e70bd921e01558e627af3f1411dbcaryclark            } else if (lengthSqd) {
2316b421650e13faa2b77d29b018e78ab07ff693ca32caryclark                fPriorPt = fLastPt;
2317f91aaecbe9d3630123d803d80f7b29f06c8976c7commit-bot@chromium.org                fLastPt = fCurrPt;
231804863fa14a44ddf85acbc6268690ebc3f0d1d6dbreed@google.com                fCurrPt = pt;
231904863fa14a44ddf85acbc6268690ebc3f0d1d6dbreed@google.com                if (++fPtCount == 2) {
23208be07bb12d1f7a2d08fa154320fbe6940b370ea1commit-bot@chromium.org                    fFirstVec = fLastVec = vec;
232104863fa14a44ddf85acbc6268690ebc3f0d1d6dbreed@google.com                } else {
232204863fa14a44ddf85acbc6268690ebc3f0d1d6dbreed@google.com                    SkASSERT(fPtCount > 2);
232304863fa14a44ddf85acbc6268690ebc3f0d1d6dbreed@google.com                    this->addVec(vec);
232404863fa14a44ddf85acbc6268690ebc3f0d1d6dbreed@google.com                }
2325fbfcd5602128ec010c82cb733c9cdc0a3254f9f3rmistry@google.com
232685b6e399d56d2421980daa432f30910beda41922reed@google.com                int sx = sign(vec.fX);
232785b6e399d56d2421980daa432f30910beda41922reed@google.com                int sy = sign(vec.fY);
232885b6e399d56d2421980daa432f30910beda41922reed@google.com                fDx += (sx != fSx);
232985b6e399d56d2421980daa432f30910beda41922reed@google.com                fDy += (sy != fSy);
233085b6e399d56d2421980daa432f30910beda41922reed@google.com                fSx = sx;
233185b6e399d56d2421980daa432f30910beda41922reed@google.com                fSy = sy;
2332fbfcd5602128ec010c82cb733c9cdc0a3254f9f3rmistry@google.com
233385b6e399d56d2421980daa432f30910beda41922reed@google.com                if (fDx > 3 || fDy > 3) {
233485b6e399d56d2421980daa432f30910beda41922reed@google.com                    fConvexity = SkPath::kConcave_Convexity;
233585b6e399d56d2421980daa432f30910beda41922reed@google.com                }
233604863fa14a44ddf85acbc6268690ebc3f0d1d6dbreed@google.com            }
233704863fa14a44ddf85acbc6268690ebc3f0d1d6dbreed@google.com        }
233804863fa14a44ddf85acbc6268690ebc3f0d1d6dbreed@google.com    }
233904863fa14a44ddf85acbc6268690ebc3f0d1d6dbreed@google.com
234004863fa14a44ddf85acbc6268690ebc3f0d1d6dbreed@google.com    void close() {
234104863fa14a44ddf85acbc6268690ebc3f0d1d6dbreed@google.com        if (fPtCount > 2) {
234204863fa14a44ddf85acbc6268690ebc3f0d1d6dbreed@google.com            this->addVec(fFirstVec);
234304863fa14a44ddf85acbc6268690ebc3f0d1d6dbreed@google.com        }
234404863fa14a44ddf85acbc6268690ebc3f0d1d6dbreed@google.com    }
234504863fa14a44ddf85acbc6268690ebc3f0d1d6dbreed@google.com
2346b421650e13faa2b77d29b018e78ab07ff693ca32caryclark    DirChange directionChange(const SkVector& curVec) {
2347b421650e13faa2b77d29b018e78ab07ff693ca32caryclark        SkScalar cross = SkPoint::CrossProduct(fLastVec, curVec);
2348b421650e13faa2b77d29b018e78ab07ff693ca32caryclark
2349b421650e13faa2b77d29b018e78ab07ff693ca32caryclark        SkScalar smallest = SkTMin(fCurrPt.fX, SkTMin(fCurrPt.fY, SkTMin(fLastPt.fX, fLastPt.fY)));
2350b421650e13faa2b77d29b018e78ab07ff693ca32caryclark        SkScalar largest = SkTMax(fCurrPt.fX, SkTMax(fCurrPt.fY, SkTMax(fLastPt.fX, fLastPt.fY)));
2351b421650e13faa2b77d29b018e78ab07ff693ca32caryclark        largest = SkTMax(largest, -smallest);
2352b421650e13faa2b77d29b018e78ab07ff693ca32caryclark
2353b421650e13faa2b77d29b018e78ab07ff693ca32caryclark        if (!almost_equal(largest, largest + cross)) {
2354b421650e13faa2b77d29b018e78ab07ff693ca32caryclark            int sign = SkScalarSignAsInt(cross);
2355b421650e13faa2b77d29b018e78ab07ff693ca32caryclark            if (sign) {
2356b421650e13faa2b77d29b018e78ab07ff693ca32caryclark                return (1 == sign) ? kRight_DirChange : kLeft_DirChange;
2357b421650e13faa2b77d29b018e78ab07ff693ca32caryclark            }
2358b421650e13faa2b77d29b018e78ab07ff693ca32caryclark        }
2359b421650e13faa2b77d29b018e78ab07ff693ca32caryclark
2360b421650e13faa2b77d29b018e78ab07ff693ca32caryclark        if (cross) {
2361b421650e13faa2b77d29b018e78ab07ff693ca32caryclark            double dLastVecX = SkScalarToDouble(fLastPt.fX) - SkScalarToDouble(fPriorPt.fX);
2362b421650e13faa2b77d29b018e78ab07ff693ca32caryclark            double dLastVecY = SkScalarToDouble(fLastPt.fY) - SkScalarToDouble(fPriorPt.fY);
2363b421650e13faa2b77d29b018e78ab07ff693ca32caryclark            double dCurrVecX = SkScalarToDouble(fCurrPt.fX) - SkScalarToDouble(fLastPt.fX);
2364b421650e13faa2b77d29b018e78ab07ff693ca32caryclark            double dCurrVecY = SkScalarToDouble(fCurrPt.fY) - SkScalarToDouble(fLastPt.fY);
2365b421650e13faa2b77d29b018e78ab07ff693ca32caryclark            double dCross = dLastVecX * dCurrVecY - dLastVecY * dCurrVecX;
2366b421650e13faa2b77d29b018e78ab07ff693ca32caryclark            if (!approximately_zero_when_compared_to(dCross, SkScalarToDouble(largest))) {
2367b421650e13faa2b77d29b018e78ab07ff693ca32caryclark                int sign = SkScalarSignAsInt(SkDoubleToScalar(dCross));
2368b421650e13faa2b77d29b018e78ab07ff693ca32caryclark                if (sign) {
2369b421650e13faa2b77d29b018e78ab07ff693ca32caryclark                    return (1 == sign) ? kRight_DirChange : kLeft_DirChange;
2370b421650e13faa2b77d29b018e78ab07ff693ca32caryclark                }
2371b421650e13faa2b77d29b018e78ab07ff693ca32caryclark            }
2372b421650e13faa2b77d29b018e78ab07ff693ca32caryclark        }
2373b421650e13faa2b77d29b018e78ab07ff693ca32caryclark
2374b421650e13faa2b77d29b018e78ab07ff693ca32caryclark        if (!SkScalarNearlyZero(fLastVec.lengthSqd(), SK_ScalarNearlyZero*SK_ScalarNearlyZero) &&
2375b421650e13faa2b77d29b018e78ab07ff693ca32caryclark            !SkScalarNearlyZero(curVec.lengthSqd(), SK_ScalarNearlyZero*SK_ScalarNearlyZero) &&
2376b421650e13faa2b77d29b018e78ab07ff693ca32caryclark            fLastVec.dot(curVec) < 0.0f) {
2377b421650e13faa2b77d29b018e78ab07ff693ca32caryclark            return kBackwards_DirChange;
2378b421650e13faa2b77d29b018e78ab07ff693ca32caryclark        }
2379b421650e13faa2b77d29b018e78ab07ff693ca32caryclark
2380b421650e13faa2b77d29b018e78ab07ff693ca32caryclark        return kStraight_DirChange;
2381b421650e13faa2b77d29b018e78ab07ff693ca32caryclark    }
2382b421650e13faa2b77d29b018e78ab07ff693ca32caryclark
2383b421650e13faa2b77d29b018e78ab07ff693ca32caryclark
2384d3d1a988b1ed144b0123dbe594c3a47a63d6451dcaryclark    bool isFinite() const {
2385d3d1a988b1ed144b0123dbe594c3a47a63d6451dcaryclark        return fIsFinite;
2386d3d1a988b1ed144b0123dbe594c3a47a63d6451dcaryclark    }
2387d3d1a988b1ed144b0123dbe594c3a47a63d6451dcaryclark
23885ccef577902ce1aefa05fb8107ad5d0aba848e7dcaryclark    void setCurve(bool isCurve) {
23895ccef577902ce1aefa05fb8107ad5d0aba848e7dcaryclark        fIsCurve = isCurve;
23905ccef577902ce1aefa05fb8107ad5d0aba848e7dcaryclark    }
23915ccef577902ce1aefa05fb8107ad5d0aba848e7dcaryclark
239204863fa14a44ddf85acbc6268690ebc3f0d1d6dbreed@google.comprivate:
239304863fa14a44ddf85acbc6268690ebc3f0d1d6dbreed@google.com    void addVec(const SkVector& vec) {
239404863fa14a44ddf85acbc6268690ebc3f0d1d6dbreed@google.com        SkASSERT(vec.fX || vec.fY);
2395b421650e13faa2b77d29b018e78ab07ff693ca32caryclark        DirChange dir = this->directionChange(vec);
2396c506e3007e53fe7d1a77991de11fdb50a86fd532robertphillips        switch (dir) {
2397c506e3007e53fe7d1a77991de11fdb50a86fd532robertphillips            case kLeft_DirChange:       // fall through
2398c506e3007e53fe7d1a77991de11fdb50a86fd532robertphillips            case kRight_DirChange:
2399c506e3007e53fe7d1a77991de11fdb50a86fd532robertphillips                if (kInvalid_DirChange == fExpectedDir) {
2400c506e3007e53fe7d1a77991de11fdb50a86fd532robertphillips                    fExpectedDir = dir;
2401026beb52a29a620290fcfb24f1e7e9e75547b80freed                    fFirstDirection = (kRight_DirChange == dir) ? SkPathPriv::kCW_FirstDirection
2402026beb52a29a620290fcfb24f1e7e9e75547b80freed                                                                : SkPathPriv::kCCW_FirstDirection;
2403c506e3007e53fe7d1a77991de11fdb50a86fd532robertphillips                } else if (dir != fExpectedDir) {
2404c506e3007e53fe7d1a77991de11fdb50a86fd532robertphillips                    fConvexity = SkPath::kConcave_Convexity;
2405026beb52a29a620290fcfb24f1e7e9e75547b80freed                    fFirstDirection = SkPathPriv::kUnknown_FirstDirection;
2406c506e3007e53fe7d1a77991de11fdb50a86fd532robertphillips                }
2407c506e3007e53fe7d1a77991de11fdb50a86fd532robertphillips                fLastVec = vec;
2408c506e3007e53fe7d1a77991de11fdb50a86fd532robertphillips                break;
2409c506e3007e53fe7d1a77991de11fdb50a86fd532robertphillips            case kStraight_DirChange:
2410c506e3007e53fe7d1a77991de11fdb50a86fd532robertphillips                break;
2411c506e3007e53fe7d1a77991de11fdb50a86fd532robertphillips            case kBackwards_DirChange:
24125ccef577902ce1aefa05fb8107ad5d0aba848e7dcaryclark                if (fIsCurve) {
2413bdabcc4cb873dc4de39263c995900a05e6a32cf4liyuqian                    // If any of the subsequent dir is non-backward, it'll be concave.
2414bdabcc4cb873dc4de39263c995900a05e6a32cf4liyuqian                    // Otherwise, it's still convex.
2415bdabcc4cb873dc4de39263c995900a05e6a32cf4liyuqian                    fExpectedDir = dir;
24165ccef577902ce1aefa05fb8107ad5d0aba848e7dcaryclark                }
2417c506e3007e53fe7d1a77991de11fdb50a86fd532robertphillips                fLastVec = vec;
2418c506e3007e53fe7d1a77991de11fdb50a86fd532robertphillips                break;
2419c506e3007e53fe7d1a77991de11fdb50a86fd532robertphillips            case kInvalid_DirChange:
2420c506e3007e53fe7d1a77991de11fdb50a86fd532robertphillips                SkFAIL("Use of invalid direction change flag");
2421c506e3007e53fe7d1a77991de11fdb50a86fd532robertphillips                break;
242204863fa14a44ddf85acbc6268690ebc3f0d1d6dbreed@google.com        }
242304863fa14a44ddf85acbc6268690ebc3f0d1d6dbreed@google.com    }
242404863fa14a44ddf85acbc6268690ebc3f0d1d6dbreed@google.com
2425b421650e13faa2b77d29b018e78ab07ff693ca32caryclark    SkPoint             fPriorPt;
2426f91aaecbe9d3630123d803d80f7b29f06c8976c7commit-bot@chromium.org    SkPoint             fLastPt;
242704863fa14a44ddf85acbc6268690ebc3f0d1d6dbreed@google.com    SkPoint             fCurrPt;
24288be07bb12d1f7a2d08fa154320fbe6940b370ea1commit-bot@chromium.org    // fLastVec does not necessarily start at fLastPt. We only advance it when the cross product
24298be07bb12d1f7a2d08fa154320fbe6940b370ea1commit-bot@chromium.org    // value with the current vec is deemed to be of a significant value.
24308be07bb12d1f7a2d08fa154320fbe6940b370ea1commit-bot@chromium.org    SkVector            fLastVec, fFirstVec;
243104863fa14a44ddf85acbc6268690ebc3f0d1d6dbreed@google.com    int                 fPtCount;   // non-degenerate points
2432c506e3007e53fe7d1a77991de11fdb50a86fd532robertphillips    DirChange           fExpectedDir;
243304863fa14a44ddf85acbc6268690ebc3f0d1d6dbreed@google.com    SkPath::Convexity   fConvexity;
2434026beb52a29a620290fcfb24f1e7e9e75547b80freed    SkPathPriv::FirstDirection   fFirstDirection;
24350b7b98260784da07b2b15ea06530ccd8fb6ad02breed@google.com    int                 fDx, fDy, fSx, fSy;
2436d3d1a988b1ed144b0123dbe594c3a47a63d6451dcaryclark    bool                fIsFinite;
24375ccef577902ce1aefa05fb8107ad5d0aba848e7dcaryclark    bool                fIsCurve;
243804863fa14a44ddf85acbc6268690ebc3f0d1d6dbreed@google.com};
243904863fa14a44ddf85acbc6268690ebc3f0d1d6dbreed@google.com
244030c174b9ce6b9777ee50ae0d0565a01b2a060f01bsalomon@google.comSkPath::Convexity SkPath::internalGetConvexity() const {
244130c174b9ce6b9777ee50ae0d0565a01b2a060f01bsalomon@google.com    SkASSERT(kUnknown_Convexity == fConvexity);
244204863fa14a44ddf85acbc6268690ebc3f0d1d6dbreed@google.com    SkPoint         pts[4];
244304863fa14a44ddf85acbc6268690ebc3f0d1d6dbreed@google.com    SkPath::Verb    verb;
244430c174b9ce6b9777ee50ae0d0565a01b2a060f01bsalomon@google.com    SkPath::Iter    iter(*this, true);
244504863fa14a44ddf85acbc6268690ebc3f0d1d6dbreed@google.com
244604863fa14a44ddf85acbc6268690ebc3f0d1d6dbreed@google.com    int             contourCount = 0;
244704863fa14a44ddf85acbc6268690ebc3f0d1d6dbreed@google.com    int             count;
244804863fa14a44ddf85acbc6268690ebc3f0d1d6dbreed@google.com    Convexicator    state;
244904863fa14a44ddf85acbc6268690ebc3f0d1d6dbreed@google.com
2450d3d1a988b1ed144b0123dbe594c3a47a63d6451dcaryclark    if (!isFinite()) {
2451d3d1a988b1ed144b0123dbe594c3a47a63d6451dcaryclark        return kUnknown_Convexity;
2452d3d1a988b1ed144b0123dbe594c3a47a63d6451dcaryclark    }
2453e8c5666e0387e70bd921e01558e627af3f1411dbcaryclark    while ((verb = iter.next(pts, true, true)) != SkPath::kDone_Verb) {
245404863fa14a44ddf85acbc6268690ebc3f0d1d6dbreed@google.com        switch (verb) {
245504863fa14a44ddf85acbc6268690ebc3f0d1d6dbreed@google.com            case kMove_Verb:
245604863fa14a44ddf85acbc6268690ebc3f0d1d6dbreed@google.com                if (++contourCount > 1) {
245730c174b9ce6b9777ee50ae0d0565a01b2a060f01bsalomon@google.com                    fConvexity = kConcave_Convexity;
245804863fa14a44ddf85acbc6268690ebc3f0d1d6dbreed@google.com                    return kConcave_Convexity;
245904863fa14a44ddf85acbc6268690ebc3f0d1d6dbreed@google.com                }
246004863fa14a44ddf85acbc6268690ebc3f0d1d6dbreed@google.com                pts[1] = pts[0];
24615ccef577902ce1aefa05fb8107ad5d0aba848e7dcaryclark                // fall through
24625ccef577902ce1aefa05fb8107ad5d0aba848e7dcaryclark            case kLine_Verb:
246304863fa14a44ddf85acbc6268690ebc3f0d1d6dbreed@google.com                count = 1;
24645ccef577902ce1aefa05fb8107ad5d0aba848e7dcaryclark                state.setCurve(false);
24655ccef577902ce1aefa05fb8107ad5d0aba848e7dcaryclark                break;
24665ccef577902ce1aefa05fb8107ad5d0aba848e7dcaryclark            case kQuad_Verb:
24675ccef577902ce1aefa05fb8107ad5d0aba848e7dcaryclark                // fall through
24685ccef577902ce1aefa05fb8107ad5d0aba848e7dcaryclark            case kConic_Verb:
24695ccef577902ce1aefa05fb8107ad5d0aba848e7dcaryclark                // fall through
24705ccef577902ce1aefa05fb8107ad5d0aba848e7dcaryclark            case kCubic_Verb:
24715ccef577902ce1aefa05fb8107ad5d0aba848e7dcaryclark                count = 2 + (kCubic_Verb == verb);
24725ccef577902ce1aefa05fb8107ad5d0aba848e7dcaryclark                // As an additional enhancement, this could set curve true only
24735ccef577902ce1aefa05fb8107ad5d0aba848e7dcaryclark                // if the curve is nonlinear
24745ccef577902ce1aefa05fb8107ad5d0aba848e7dcaryclark                state.setCurve(true);
247504863fa14a44ddf85acbc6268690ebc3f0d1d6dbreed@google.com                break;
247604863fa14a44ddf85acbc6268690ebc3f0d1d6dbreed@google.com            case kClose_Verb:
24775ccef577902ce1aefa05fb8107ad5d0aba848e7dcaryclark                state.setCurve(false);
247804863fa14a44ddf85acbc6268690ebc3f0d1d6dbreed@google.com                state.close();
247904863fa14a44ddf85acbc6268690ebc3f0d1d6dbreed@google.com                count = 0;
248004863fa14a44ddf85acbc6268690ebc3f0d1d6dbreed@google.com                break;
248104863fa14a44ddf85acbc6268690ebc3f0d1d6dbreed@google.com            default:
24820c00f21fee3f5cfa3aa7e5d46ff94cb8cf340451tomhudson@google.com                SkDEBUGFAIL("bad verb");
248330c174b9ce6b9777ee50ae0d0565a01b2a060f01bsalomon@google.com                fConvexity = kConcave_Convexity;
248404863fa14a44ddf85acbc6268690ebc3f0d1d6dbreed@google.com                return kConcave_Convexity;
248504863fa14a44ddf85acbc6268690ebc3f0d1d6dbreed@google.com        }
248604863fa14a44ddf85acbc6268690ebc3f0d1d6dbreed@google.com
248704863fa14a44ddf85acbc6268690ebc3f0d1d6dbreed@google.com        for (int i = 1; i <= count; i++) {
248804863fa14a44ddf85acbc6268690ebc3f0d1d6dbreed@google.com            state.addPt(pts[i]);
248904863fa14a44ddf85acbc6268690ebc3f0d1d6dbreed@google.com        }
249004863fa14a44ddf85acbc6268690ebc3f0d1d6dbreed@google.com        // early exit
2491d3d1a988b1ed144b0123dbe594c3a47a63d6451dcaryclark        if (!state.isFinite()) {
2492d3d1a988b1ed144b0123dbe594c3a47a63d6451dcaryclark            return kUnknown_Convexity;
2493d3d1a988b1ed144b0123dbe594c3a47a63d6451dcaryclark        }
249404863fa14a44ddf85acbc6268690ebc3f0d1d6dbreed@google.com        if (kConcave_Convexity == state.getConvexity()) {
249530c174b9ce6b9777ee50ae0d0565a01b2a060f01bsalomon@google.com            fConvexity = kConcave_Convexity;
249604863fa14a44ddf85acbc6268690ebc3f0d1d6dbreed@google.com            return kConcave_Convexity;
249704863fa14a44ddf85acbc6268690ebc3f0d1d6dbreed@google.com        }
249804863fa14a44ddf85acbc6268690ebc3f0d1d6dbreed@google.com    }
249930c174b9ce6b9777ee50ae0d0565a01b2a060f01bsalomon@google.com    fConvexity = state.getConvexity();
2500026beb52a29a620290fcfb24f1e7e9e75547b80freed    if (kConvex_Convexity == fConvexity && SkPathPriv::kUnknown_FirstDirection == fFirstDirection) {
2501026beb52a29a620290fcfb24f1e7e9e75547b80freed        fFirstDirection = state.getFirstDirection();
250230c174b9ce6b9777ee50ae0d0565a01b2a060f01bsalomon@google.com    }
250330c174b9ce6b9777ee50ae0d0565a01b2a060f01bsalomon@google.com    return static_cast<Convexity>(fConvexity);
250404863fa14a44ddf85acbc6268690ebc3f0d1d6dbreed@google.com}
250569a9943b67cc52c24beac853c6f8865dcb197b85reed@google.com
250669a9943b67cc52c24beac853c6f8865dcb197b85reed@google.com///////////////////////////////////////////////////////////////////////////////
250769a9943b67cc52c24beac853c6f8865dcb197b85reed@google.com
250869a9943b67cc52c24beac853c6f8865dcb197b85reed@google.comclass ContourIter {
250969a9943b67cc52c24beac853c6f8865dcb197b85reed@google.compublic:
25101dfe88e00aeddf20690fd2469fd17e43f670ee3absalomon@google.com    ContourIter(const SkPathRef& pathRef);
251169a9943b67cc52c24beac853c6f8865dcb197b85reed@google.com
251269a9943b67cc52c24beac853c6f8865dcb197b85reed@google.com    bool done() const { return fDone; }
251369a9943b67cc52c24beac853c6f8865dcb197b85reed@google.com    // if !done() then these may be called
251469a9943b67cc52c24beac853c6f8865dcb197b85reed@google.com    int count() const { return fCurrPtCount; }
251569a9943b67cc52c24beac853c6f8865dcb197b85reed@google.com    const SkPoint* pts() const { return fCurrPt; }
251669a9943b67cc52c24beac853c6f8865dcb197b85reed@google.com    void next();
251769a9943b67cc52c24beac853c6f8865dcb197b85reed@google.com
251869a9943b67cc52c24beac853c6f8865dcb197b85reed@google.comprivate:
251969a9943b67cc52c24beac853c6f8865dcb197b85reed@google.com    int fCurrPtCount;
252069a9943b67cc52c24beac853c6f8865dcb197b85reed@google.com    const SkPoint* fCurrPt;
252169a9943b67cc52c24beac853c6f8865dcb197b85reed@google.com    const uint8_t* fCurrVerb;
252269a9943b67cc52c24beac853c6f8865dcb197b85reed@google.com    const uint8_t* fStopVerbs;
2523277c3f87656c44e0a651ed0dd56efa16c0ab07b4reed@google.com    const SkScalar* fCurrConicWeight;
252469a9943b67cc52c24beac853c6f8865dcb197b85reed@google.com    bool fDone;
2525d1ab9320b09b922bd2f3b3d4f888c754fde5b58creed@google.com    SkDEBUGCODE(int fContourCounter;)
252669a9943b67cc52c24beac853c6f8865dcb197b85reed@google.com};
252769a9943b67cc52c24beac853c6f8865dcb197b85reed@google.com
25281dfe88e00aeddf20690fd2469fd17e43f670ee3absalomon@google.comContourIter::ContourIter(const SkPathRef& pathRef) {
25291dfe88e00aeddf20690fd2469fd17e43f670ee3absalomon@google.com    fStopVerbs = pathRef.verbsMemBegin();
253069a9943b67cc52c24beac853c6f8865dcb197b85reed@google.com    fDone = false;
25311dfe88e00aeddf20690fd2469fd17e43f670ee3absalomon@google.com    fCurrPt = pathRef.points();
25321dfe88e00aeddf20690fd2469fd17e43f670ee3absalomon@google.com    fCurrVerb = pathRef.verbs();
2533277c3f87656c44e0a651ed0dd56efa16c0ab07b4reed@google.com    fCurrConicWeight = pathRef.conicWeights();
253469a9943b67cc52c24beac853c6f8865dcb197b85reed@google.com    fCurrPtCount = 0;
2535d1ab9320b09b922bd2f3b3d4f888c754fde5b58creed@google.com    SkDEBUGCODE(fContourCounter = 0;)
253669a9943b67cc52c24beac853c6f8865dcb197b85reed@google.com    this->next();
253769a9943b67cc52c24beac853c6f8865dcb197b85reed@google.com}
253869a9943b67cc52c24beac853c6f8865dcb197b85reed@google.com
253969a9943b67cc52c24beac853c6f8865dcb197b85reed@google.comvoid ContourIter::next() {
25401dfe88e00aeddf20690fd2469fd17e43f670ee3absalomon@google.com    if (fCurrVerb <= fStopVerbs) {
254169a9943b67cc52c24beac853c6f8865dcb197b85reed@google.com        fDone = true;
254269a9943b67cc52c24beac853c6f8865dcb197b85reed@google.com    }
254369a9943b67cc52c24beac853c6f8865dcb197b85reed@google.com    if (fDone) {
254469a9943b67cc52c24beac853c6f8865dcb197b85reed@google.com        return;
254569a9943b67cc52c24beac853c6f8865dcb197b85reed@google.com    }
254669a9943b67cc52c24beac853c6f8865dcb197b85reed@google.com
254769a9943b67cc52c24beac853c6f8865dcb197b85reed@google.com    // skip pts of prev contour
254869a9943b67cc52c24beac853c6f8865dcb197b85reed@google.com    fCurrPt += fCurrPtCount;
254969a9943b67cc52c24beac853c6f8865dcb197b85reed@google.com
25501dfe88e00aeddf20690fd2469fd17e43f670ee3absalomon@google.com    SkASSERT(SkPath::kMove_Verb == fCurrVerb[~0]);
255169a9943b67cc52c24beac853c6f8865dcb197b85reed@google.com    int ptCount = 1;    // moveTo
255269a9943b67cc52c24beac853c6f8865dcb197b85reed@google.com    const uint8_t* verbs = fCurrVerb;
255369a9943b67cc52c24beac853c6f8865dcb197b85reed@google.com
25541dfe88e00aeddf20690fd2469fd17e43f670ee3absalomon@google.com    for (--verbs; verbs > fStopVerbs; --verbs) {
25551dfe88e00aeddf20690fd2469fd17e43f670ee3absalomon@google.com        switch (verbs[~0]) {
255669a9943b67cc52c24beac853c6f8865dcb197b85reed@google.com            case SkPath::kMove_Verb:
255769a9943b67cc52c24beac853c6f8865dcb197b85reed@google.com                goto CONTOUR_END;
255869a9943b67cc52c24beac853c6f8865dcb197b85reed@google.com            case SkPath::kLine_Verb:
255969a9943b67cc52c24beac853c6f8865dcb197b85reed@google.com                ptCount += 1;
256069a9943b67cc52c24beac853c6f8865dcb197b85reed@google.com                break;
2561277c3f87656c44e0a651ed0dd56efa16c0ab07b4reed@google.com            case SkPath::kConic_Verb:
2562277c3f87656c44e0a651ed0dd56efa16c0ab07b4reed@google.com                fCurrConicWeight += 1;
2563277c3f87656c44e0a651ed0dd56efa16c0ab07b4reed@google.com                // fall-through
256469a9943b67cc52c24beac853c6f8865dcb197b85reed@google.com            case SkPath::kQuad_Verb:
256569a9943b67cc52c24beac853c6f8865dcb197b85reed@google.com                ptCount += 2;
256669a9943b67cc52c24beac853c6f8865dcb197b85reed@google.com                break;
256769a9943b67cc52c24beac853c6f8865dcb197b85reed@google.com            case SkPath::kCubic_Verb:
256869a9943b67cc52c24beac853c6f8865dcb197b85reed@google.com                ptCount += 3;
256969a9943b67cc52c24beac853c6f8865dcb197b85reed@google.com                break;
2570277c3f87656c44e0a651ed0dd56efa16c0ab07b4reed@google.com            case SkPath::kClose_Verb:
2571277c3f87656c44e0a651ed0dd56efa16c0ab07b4reed@google.com                break;
2572277c3f87656c44e0a651ed0dd56efa16c0ab07b4reed@google.com            default:
2573330313a8a8343876ee596da39da06a5d69badd9cmtklein@google.com                SkDEBUGFAIL("unexpected verb");
257469a9943b67cc52c24beac853c6f8865dcb197b85reed@google.com                break;
257569a9943b67cc52c24beac853c6f8865dcb197b85reed@google.com        }
257669a9943b67cc52c24beac853c6f8865dcb197b85reed@google.com    }
257769a9943b67cc52c24beac853c6f8865dcb197b85reed@google.comCONTOUR_END:
257869a9943b67cc52c24beac853c6f8865dcb197b85reed@google.com    fCurrPtCount = ptCount;
257969a9943b67cc52c24beac853c6f8865dcb197b85reed@google.com    fCurrVerb = verbs;
2580d1ab9320b09b922bd2f3b3d4f888c754fde5b58creed@google.com    SkDEBUGCODE(++fContourCounter;)
258169a9943b67cc52c24beac853c6f8865dcb197b85reed@google.com}
258269a9943b67cc52c24beac853c6f8865dcb197b85reed@google.com
2583f0ed80a7ebef9b9c08093390b173e64bf300d7d7bsalomon@google.com// returns cross product of (p1 - p0) and (p2 - p0)
258469a9943b67cc52c24beac853c6f8865dcb197b85reed@google.comstatic SkScalar cross_prod(const SkPoint& p0, const SkPoint& p1, const SkPoint& p2) {
2585f0ed80a7ebef9b9c08093390b173e64bf300d7d7bsalomon@google.com    SkScalar cross = SkPoint::CrossProduct(p1 - p0, p2 - p0);
2586f0ed80a7ebef9b9c08093390b173e64bf300d7d7bsalomon@google.com    // We may get 0 when the above subtracts underflow. We expect this to be
2587f0ed80a7ebef9b9c08093390b173e64bf300d7d7bsalomon@google.com    // very rare and lazily promote to double.
2588f0ed80a7ebef9b9c08093390b173e64bf300d7d7bsalomon@google.com    if (0 == cross) {
2589f0ed80a7ebef9b9c08093390b173e64bf300d7d7bsalomon@google.com        double p0x = SkScalarToDouble(p0.fX);
2590f0ed80a7ebef9b9c08093390b173e64bf300d7d7bsalomon@google.com        double p0y = SkScalarToDouble(p0.fY);
2591f0ed80a7ebef9b9c08093390b173e64bf300d7d7bsalomon@google.com
2592f0ed80a7ebef9b9c08093390b173e64bf300d7d7bsalomon@google.com        double p1x = SkScalarToDouble(p1.fX);
2593f0ed80a7ebef9b9c08093390b173e64bf300d7d7bsalomon@google.com        double p1y = SkScalarToDouble(p1.fY);
2594f0ed80a7ebef9b9c08093390b173e64bf300d7d7bsalomon@google.com
2595f0ed80a7ebef9b9c08093390b173e64bf300d7d7bsalomon@google.com        double p2x = SkScalarToDouble(p2.fX);
2596f0ed80a7ebef9b9c08093390b173e64bf300d7d7bsalomon@google.com        double p2y = SkScalarToDouble(p2.fY);
2597f0ed80a7ebef9b9c08093390b173e64bf300d7d7bsalomon@google.com
2598f0ed80a7ebef9b9c08093390b173e64bf300d7d7bsalomon@google.com        cross = SkDoubleToScalar((p1x - p0x) * (p2y - p0y) -
2599f0ed80a7ebef9b9c08093390b173e64bf300d7d7bsalomon@google.com                                 (p1y - p0y) * (p2x - p0x));
2600f0ed80a7ebef9b9c08093390b173e64bf300d7d7bsalomon@google.com
2601f0ed80a7ebef9b9c08093390b173e64bf300d7d7bsalomon@google.com    }
2602f0ed80a7ebef9b9c08093390b173e64bf300d7d7bsalomon@google.com    return cross;
260369a9943b67cc52c24beac853c6f8865dcb197b85reed@google.com}
260469a9943b67cc52c24beac853c6f8865dcb197b85reed@google.com
2605c1ea60a9ea63fc590f11f49cd0d744e061891985reed@google.com// Returns the first pt with the maximum Y coordinate
260669a9943b67cc52c24beac853c6f8865dcb197b85reed@google.comstatic int find_max_y(const SkPoint pts[], int count) {
260769a9943b67cc52c24beac853c6f8865dcb197b85reed@google.com    SkASSERT(count > 0);
260869a9943b67cc52c24beac853c6f8865dcb197b85reed@google.com    SkScalar max = pts[0].fY;
2609c1ea60a9ea63fc590f11f49cd0d744e061891985reed@google.com    int firstIndex = 0;
261069a9943b67cc52c24beac853c6f8865dcb197b85reed@google.com    for (int i = 1; i < count; ++i) {
2611c1ea60a9ea63fc590f11f49cd0d744e061891985reed@google.com        SkScalar y = pts[i].fY;
2612c1ea60a9ea63fc590f11f49cd0d744e061891985reed@google.com        if (y > max) {
2613c1ea60a9ea63fc590f11f49cd0d744e061891985reed@google.com            max = y;
2614c1ea60a9ea63fc590f11f49cd0d744e061891985reed@google.com            firstIndex = i;
261569a9943b67cc52c24beac853c6f8865dcb197b85reed@google.com        }
261669a9943b67cc52c24beac853c6f8865dcb197b85reed@google.com    }
2617c1ea60a9ea63fc590f11f49cd0d744e061891985reed@google.com    return firstIndex;
261869a9943b67cc52c24beac853c6f8865dcb197b85reed@google.com}
261969a9943b67cc52c24beac853c6f8865dcb197b85reed@google.com
2620cabaf1daf3fdcc151c12d59b05bdbe136c178b3breed@google.comstatic int find_diff_pt(const SkPoint pts[], int index, int n, int inc) {
2621cabaf1daf3fdcc151c12d59b05bdbe136c178b3breed@google.com    int i = index;
2622cabaf1daf3fdcc151c12d59b05bdbe136c178b3breed@google.com    for (;;) {
2623cabaf1daf3fdcc151c12d59b05bdbe136c178b3breed@google.com        i = (i + inc) % n;
2624cabaf1daf3fdcc151c12d59b05bdbe136c178b3breed@google.com        if (i == index) {   // we wrapped around, so abort
2625cabaf1daf3fdcc151c12d59b05bdbe136c178b3breed@google.com            break;
2626cabaf1daf3fdcc151c12d59b05bdbe136c178b3breed@google.com        }
2627cabaf1daf3fdcc151c12d59b05bdbe136c178b3breed@google.com        if (pts[index] != pts[i]) { // found a different point, success!
2628cabaf1daf3fdcc151c12d59b05bdbe136c178b3breed@google.com            break;
2629cabaf1daf3fdcc151c12d59b05bdbe136c178b3breed@google.com        }
2630cabaf1daf3fdcc151c12d59b05bdbe136c178b3breed@google.com    }
2631cabaf1daf3fdcc151c12d59b05bdbe136c178b3breed@google.com    return i;
2632cabaf1daf3fdcc151c12d59b05bdbe136c178b3breed@google.com}
2633cabaf1daf3fdcc151c12d59b05bdbe136c178b3breed@google.com
2634c1ea60a9ea63fc590f11f49cd0d744e061891985reed@google.com/**
2635c1ea60a9ea63fc590f11f49cd0d744e061891985reed@google.com *  Starting at index, and moving forward (incrementing), find the xmin and
2636c1ea60a9ea63fc590f11f49cd0d744e061891985reed@google.com *  xmax of the contiguous points that have the same Y.
2637c1ea60a9ea63fc590f11f49cd0d744e061891985reed@google.com */
2638c1ea60a9ea63fc590f11f49cd0d744e061891985reed@google.comstatic int find_min_max_x_at_y(const SkPoint pts[], int index, int n,
2639c1ea60a9ea63fc590f11f49cd0d744e061891985reed@google.com                               int* maxIndexPtr) {
2640c1ea60a9ea63fc590f11f49cd0d744e061891985reed@google.com    const SkScalar y = pts[index].fY;
2641c1ea60a9ea63fc590f11f49cd0d744e061891985reed@google.com    SkScalar min = pts[index].fX;
2642c1ea60a9ea63fc590f11f49cd0d744e061891985reed@google.com    SkScalar max = min;
2643c1ea60a9ea63fc590f11f49cd0d744e061891985reed@google.com    int minIndex = index;
2644c1ea60a9ea63fc590f11f49cd0d744e061891985reed@google.com    int maxIndex = index;
2645c1ea60a9ea63fc590f11f49cd0d744e061891985reed@google.com    for (int i = index + 1; i < n; ++i) {
2646c1ea60a9ea63fc590f11f49cd0d744e061891985reed@google.com        if (pts[i].fY != y) {
2647c1ea60a9ea63fc590f11f49cd0d744e061891985reed@google.com            break;
2648c1ea60a9ea63fc590f11f49cd0d744e061891985reed@google.com        }
2649c1ea60a9ea63fc590f11f49cd0d744e061891985reed@google.com        SkScalar x = pts[i].fX;
2650c1ea60a9ea63fc590f11f49cd0d744e061891985reed@google.com        if (x < min) {
2651c1ea60a9ea63fc590f11f49cd0d744e061891985reed@google.com            min = x;
2652c1ea60a9ea63fc590f11f49cd0d744e061891985reed@google.com            minIndex = i;
2653c1ea60a9ea63fc590f11f49cd0d744e061891985reed@google.com        } else if (x > max) {
2654c1ea60a9ea63fc590f11f49cd0d744e061891985reed@google.com            max = x;
2655c1ea60a9ea63fc590f11f49cd0d744e061891985reed@google.com            maxIndex = i;
2656c1ea60a9ea63fc590f11f49cd0d744e061891985reed@google.com        }
2657c1ea60a9ea63fc590f11f49cd0d744e061891985reed@google.com    }
2658c1ea60a9ea63fc590f11f49cd0d744e061891985reed@google.com    *maxIndexPtr = maxIndex;
2659c1ea60a9ea63fc590f11f49cd0d744e061891985reed@google.com    return minIndex;
2660c1ea60a9ea63fc590f11f49cd0d744e061891985reed@google.com}
2661c1ea60a9ea63fc590f11f49cd0d744e061891985reed@google.com
2662026beb52a29a620290fcfb24f1e7e9e75547b80freedstatic void crossToDir(SkScalar cross, SkPathPriv::FirstDirection* dir) {
2663026beb52a29a620290fcfb24f1e7e9e75547b80freed    *dir = cross > 0 ? SkPathPriv::kCW_FirstDirection : SkPathPriv::kCCW_FirstDirection;
26644eefe6132cbf77696134f65762ebcae574227b77bsalomon@google.com}
26654eefe6132cbf77696134f65762ebcae574227b77bsalomon@google.com
2666ac8543ff574cb08ad46bee691d64e31fe31339e5reed@google.com/*
2667ac8543ff574cb08ad46bee691d64e31fe31339e5reed@google.com *  We loop through all contours, and keep the computed cross-product of the
2668ac8543ff574cb08ad46bee691d64e31fe31339e5reed@google.com *  contour that contained the global y-max. If we just look at the first
2669ac8543ff574cb08ad46bee691d64e31fe31339e5reed@google.com *  contour, we may find one that is wound the opposite way (correctly) since
2670ac8543ff574cb08ad46bee691d64e31fe31339e5reed@google.com *  it is the interior of a hole (e.g. 'o'). Thus we must find the contour
2671ac8543ff574cb08ad46bee691d64e31fe31339e5reed@google.com *  that is outer most (or at least has the global y-max) before we can consider
2672ac8543ff574cb08ad46bee691d64e31fe31339e5reed@google.com *  its cross product.
2673ac8543ff574cb08ad46bee691d64e31fe31339e5reed@google.com */
2674026beb52a29a620290fcfb24f1e7e9e75547b80freedbool SkPathPriv::CheapComputeFirstDirection(const SkPath& path, FirstDirection* dir) {
26759f4dbca3290cfaca7dd17b71eb6b5b3a0ba5323eherb    if (kUnknown_FirstDirection != path.fFirstDirection.load()) {
26769f4dbca3290cfaca7dd17b71eb6b5b3a0ba5323eherb        *dir = static_cast<FirstDirection>(path.fFirstDirection.load());
267730c174b9ce6b9777ee50ae0d0565a01b2a060f01bsalomon@google.com        return true;
267830c174b9ce6b9777ee50ae0d0565a01b2a060f01bsalomon@google.com    }
2679a1a097ee814d05a92487d85db8ad02e1d852fd6fcommit-bot@chromium.org
2680a1a097ee814d05a92487d85db8ad02e1d852fd6fcommit-bot@chromium.org    // don't want to pay the cost for computing this if it
2681a1a097ee814d05a92487d85db8ad02e1d852fd6fcommit-bot@chromium.org    // is unknown, so we don't call isConvex()
2682026beb52a29a620290fcfb24f1e7e9e75547b80freed    if (SkPath::kConvex_Convexity == path.getConvexityOrUnknown()) {
2683026beb52a29a620290fcfb24f1e7e9e75547b80freed        SkASSERT(kUnknown_FirstDirection == path.fFirstDirection);
26849f4dbca3290cfaca7dd17b71eb6b5b3a0ba5323eherb        *dir = static_cast<FirstDirection>(path.fFirstDirection.load());
2685a1a097ee814d05a92487d85db8ad02e1d852fd6fcommit-bot@chromium.org        return false;
2686a1a097ee814d05a92487d85db8ad02e1d852fd6fcommit-bot@chromium.org    }
268769a9943b67cc52c24beac853c6f8865dcb197b85reed@google.com
2688026beb52a29a620290fcfb24f1e7e9e75547b80freed    ContourIter iter(*path.fPathRef.get());
268969a9943b67cc52c24beac853c6f8865dcb197b85reed@google.com
2690ac8543ff574cb08ad46bee691d64e31fe31339e5reed@google.com    // initialize with our logical y-min
2691026beb52a29a620290fcfb24f1e7e9e75547b80freed    SkScalar ymax = path.getBounds().fTop;
2692ac8543ff574cb08ad46bee691d64e31fe31339e5reed@google.com    SkScalar ymaxCross = 0;
2693ac8543ff574cb08ad46bee691d64e31fe31339e5reed@google.com
269469a9943b67cc52c24beac853c6f8865dcb197b85reed@google.com    for (; !iter.done(); iter.next()) {
269569a9943b67cc52c24beac853c6f8865dcb197b85reed@google.com        int n = iter.count();
2696cabaf1daf3fdcc151c12d59b05bdbe136c178b3breed@google.com        if (n < 3) {
2697cabaf1daf3fdcc151c12d59b05bdbe136c178b3breed@google.com            continue;
2698cabaf1daf3fdcc151c12d59b05bdbe136c178b3breed@google.com        }
2699e63793a2c8d2871bf7d95195be7b93ff669688d7djsollen@google.com
2700cabaf1daf3fdcc151c12d59b05bdbe136c178b3breed@google.com        const SkPoint* pts = iter.pts();
270169a9943b67cc52c24beac853c6f8865dcb197b85reed@google.com        SkScalar cross = 0;
2702a1a097ee814d05a92487d85db8ad02e1d852fd6fcommit-bot@chromium.org        int index = find_max_y(pts, n);
2703a1a097ee814d05a92487d85db8ad02e1d852fd6fcommit-bot@chromium.org        if (pts[index].fY < ymax) {
2704a1a097ee814d05a92487d85db8ad02e1d852fd6fcommit-bot@chromium.org            continue;
2705a1a097ee814d05a92487d85db8ad02e1d852fd6fcommit-bot@chromium.org        }
2706a1a097ee814d05a92487d85db8ad02e1d852fd6fcommit-bot@chromium.org
2707a1a097ee814d05a92487d85db8ad02e1d852fd6fcommit-bot@chromium.org        // If there is more than 1 distinct point at the y-max, we take the
2708a1a097ee814d05a92487d85db8ad02e1d852fd6fcommit-bot@chromium.org        // x-min and x-max of them and just subtract to compute the dir.
2709a1a097ee814d05a92487d85db8ad02e1d852fd6fcommit-bot@chromium.org        if (pts[(index + 1) % n].fY == pts[index].fY) {
2710a1a097ee814d05a92487d85db8ad02e1d852fd6fcommit-bot@chromium.org            int maxIndex;
2711a1a097ee814d05a92487d85db8ad02e1d852fd6fcommit-bot@chromium.org            int minIndex = find_min_max_x_at_y(pts, index, n, &maxIndex);
2712a1a097ee814d05a92487d85db8ad02e1d852fd6fcommit-bot@chromium.org            if (minIndex == maxIndex) {
2713a1a097ee814d05a92487d85db8ad02e1d852fd6fcommit-bot@chromium.org                goto TRY_CROSSPROD;
271469a9943b67cc52c24beac853c6f8865dcb197b85reed@google.com            }
2715a1a097ee814d05a92487d85db8ad02e1d852fd6fcommit-bot@chromium.org            SkASSERT(pts[minIndex].fY == pts[index].fY);
2716a1a097ee814d05a92487d85db8ad02e1d852fd6fcommit-bot@chromium.org            SkASSERT(pts[maxIndex].fY == pts[index].fY);
2717a1a097ee814d05a92487d85db8ad02e1d852fd6fcommit-bot@chromium.org            SkASSERT(pts[minIndex].fX <= pts[maxIndex].fX);
2718a1a097ee814d05a92487d85db8ad02e1d852fd6fcommit-bot@chromium.org            // we just subtract the indices, and let that auto-convert to
2719a1a097ee814d05a92487d85db8ad02e1d852fd6fcommit-bot@chromium.org            // SkScalar, since we just want - or + to signal the direction.
2720a1a097ee814d05a92487d85db8ad02e1d852fd6fcommit-bot@chromium.org            cross = minIndex - maxIndex;
272169a9943b67cc52c24beac853c6f8865dcb197b85reed@google.com        } else {
2722a1a097ee814d05a92487d85db8ad02e1d852fd6fcommit-bot@chromium.org            TRY_CROSSPROD:
2723a1a097ee814d05a92487d85db8ad02e1d852fd6fcommit-bot@chromium.org            // Find a next and prev index to use for the cross-product test,
2724a1a097ee814d05a92487d85db8ad02e1d852fd6fcommit-bot@chromium.org            // but we try to find pts that form non-zero vectors from pts[index]
2725a1a097ee814d05a92487d85db8ad02e1d852fd6fcommit-bot@chromium.org            //
2726a1a097ee814d05a92487d85db8ad02e1d852fd6fcommit-bot@chromium.org            // Its possible that we can't find two non-degenerate vectors, so
2727a1a097ee814d05a92487d85db8ad02e1d852fd6fcommit-bot@chromium.org            // we have to guard our search (e.g. all the pts could be in the
2728a1a097ee814d05a92487d85db8ad02e1d852fd6fcommit-bot@chromium.org            // same place).
2729a1a097ee814d05a92487d85db8ad02e1d852fd6fcommit-bot@chromium.org
2730a1a097ee814d05a92487d85db8ad02e1d852fd6fcommit-bot@chromium.org            // we pass n - 1 instead of -1 so we don't foul up % operator by
2731a1a097ee814d05a92487d85db8ad02e1d852fd6fcommit-bot@chromium.org            // passing it a negative LH argument.
2732a1a097ee814d05a92487d85db8ad02e1d852fd6fcommit-bot@chromium.org            int prev = find_diff_pt(pts, index, n, n - 1);
2733a1a097ee814d05a92487d85db8ad02e1d852fd6fcommit-bot@chromium.org            if (prev == index) {
2734a1a097ee814d05a92487d85db8ad02e1d852fd6fcommit-bot@chromium.org                // completely degenerate, skip to next contour
2735ac8543ff574cb08ad46bee691d64e31fe31339e5reed@google.com                continue;
2736ac8543ff574cb08ad46bee691d64e31fe31339e5reed@google.com            }
2737a1a097ee814d05a92487d85db8ad02e1d852fd6fcommit-bot@chromium.org            int next = find_diff_pt(pts, index, n, 1);
2738a1a097ee814d05a92487d85db8ad02e1d852fd6fcommit-bot@chromium.org            SkASSERT(next != index);
2739a1a097ee814d05a92487d85db8ad02e1d852fd6fcommit-bot@chromium.org            cross = cross_prod(pts[prev], pts[index], pts[next]);
2740a1a097ee814d05a92487d85db8ad02e1d852fd6fcommit-bot@chromium.org            // if we get a zero and the points are horizontal, then we look at the spread in
2741a1a097ee814d05a92487d85db8ad02e1d852fd6fcommit-bot@chromium.org            // x-direction. We really should continue to walk away from the degeneracy until
2742a1a097ee814d05a92487d85db8ad02e1d852fd6fcommit-bot@chromium.org            // there is a divergence.
2743a1a097ee814d05a92487d85db8ad02e1d852fd6fcommit-bot@chromium.org            if (0 == cross && pts[prev].fY == pts[index].fY && pts[next].fY == pts[index].fY) {
2744a1a097ee814d05a92487d85db8ad02e1d852fd6fcommit-bot@chromium.org                // construct the subtract so we get the correct Direction below
2745a1a097ee814d05a92487d85db8ad02e1d852fd6fcommit-bot@chromium.org                cross = pts[index].fX - pts[next].fX;
2746188bfcf7666f4d264329063fd9bf5c44e7734fd8reed@google.com            }
2747a1a097ee814d05a92487d85db8ad02e1d852fd6fcommit-bot@chromium.org        }
2748fbfcd5602128ec010c82cb733c9cdc0a3254f9f3rmistry@google.com
2749a1a097ee814d05a92487d85db8ad02e1d852fd6fcommit-bot@chromium.org        if (cross) {
2750a1a097ee814d05a92487d85db8ad02e1d852fd6fcommit-bot@chromium.org            // record our best guess so far
2751a1a097ee814d05a92487d85db8ad02e1d852fd6fcommit-bot@chromium.org            ymax = pts[index].fY;
2752a1a097ee814d05a92487d85db8ad02e1d852fd6fcommit-bot@chromium.org            ymaxCross = cross;
275369a9943b67cc52c24beac853c6f8865dcb197b85reed@google.com        }
275469a9943b67cc52c24beac853c6f8865dcb197b85reed@google.com    }
275530c174b9ce6b9777ee50ae0d0565a01b2a060f01bsalomon@google.com    if (ymaxCross) {
275630c174b9ce6b9777ee50ae0d0565a01b2a060f01bsalomon@google.com        crossToDir(ymaxCross, dir);
2757026beb52a29a620290fcfb24f1e7e9e75547b80freed        path.fFirstDirection = *dir;
275830c174b9ce6b9777ee50ae0d0565a01b2a060f01bsalomon@google.com        return true;
275930c174b9ce6b9777ee50ae0d0565a01b2a060f01bsalomon@google.com    } else {
276030c174b9ce6b9777ee50ae0d0565a01b2a060f01bsalomon@google.com        return false;
276130c174b9ce6b9777ee50ae0d0565a01b2a060f01bsalomon@google.com    }
2762ac8543ff574cb08ad46bee691d64e31fe31339e5reed@google.com}
2763bad1b2ff1d34ff86693b776f89d7b46995746127mike@reedtribe.org
2764bad1b2ff1d34ff86693b776f89d7b46995746127mike@reedtribe.org///////////////////////////////////////////////////////////////////////////////
2765bad1b2ff1d34ff86693b776f89d7b46995746127mike@reedtribe.org
27669aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclarkstatic bool between(SkScalar a, SkScalar b, SkScalar c) {
27679aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark    SkASSERT(((a <= b && b <= c) || (a >= b && b >= c)) == ((a - b) * (c - b) <= 0)
27689aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark            || (SkScalarNearlyZero(a) && SkScalarNearlyZero(b) && SkScalarNearlyZero(c)));
27699aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark    return (a - b) * (c - b) <= 0;
27709aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark}
27719aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark
2772bad1b2ff1d34ff86693b776f89d7b46995746127mike@reedtribe.orgstatic SkScalar eval_cubic_pts(SkScalar c0, SkScalar c1, SkScalar c2, SkScalar c3,
2773bad1b2ff1d34ff86693b776f89d7b46995746127mike@reedtribe.org                               SkScalar t) {
2774bad1b2ff1d34ff86693b776f89d7b46995746127mike@reedtribe.org    SkScalar A = c3 + 3*(c1 - c2) - c0;
2775bad1b2ff1d34ff86693b776f89d7b46995746127mike@reedtribe.org    SkScalar B = 3*(c2 - c1 - c1 + c0);
2776bad1b2ff1d34ff86693b776f89d7b46995746127mike@reedtribe.org    SkScalar C = 3*(c1 - c0);
2777bad1b2ff1d34ff86693b776f89d7b46995746127mike@reedtribe.org    SkScalar D = c0;
2778a99b6ceff92183b424634f2e7276b9ea1d59e69dMike Reed    return poly_eval(A, B, C, D, t);
2779bad1b2ff1d34ff86693b776f89d7b46995746127mike@reedtribe.org}
2780bad1b2ff1d34ff86693b776f89d7b46995746127mike@reedtribe.org
2781bad1b2ff1d34ff86693b776f89d7b46995746127mike@reedtribe.orgtemplate <size_t N> static void find_minmax(const SkPoint pts[],
2782bad1b2ff1d34ff86693b776f89d7b46995746127mike@reedtribe.org                                            SkScalar* minPtr, SkScalar* maxPtr) {
2783bad1b2ff1d34ff86693b776f89d7b46995746127mike@reedtribe.org    SkScalar min, max;
2784bad1b2ff1d34ff86693b776f89d7b46995746127mike@reedtribe.org    min = max = pts[0].fX;
2785bad1b2ff1d34ff86693b776f89d7b46995746127mike@reedtribe.org    for (size_t i = 1; i < N; ++i) {
2786bad1b2ff1d34ff86693b776f89d7b46995746127mike@reedtribe.org        min = SkMinScalar(min, pts[i].fX);
2787bad1b2ff1d34ff86693b776f89d7b46995746127mike@reedtribe.org        max = SkMaxScalar(max, pts[i].fX);
2788bad1b2ff1d34ff86693b776f89d7b46995746127mike@reedtribe.org    }
2789bad1b2ff1d34ff86693b776f89d7b46995746127mike@reedtribe.org    *minPtr = min;
2790bad1b2ff1d34ff86693b776f89d7b46995746127mike@reedtribe.org    *maxPtr = max;
2791bad1b2ff1d34ff86693b776f89d7b46995746127mike@reedtribe.org}
2792bad1b2ff1d34ff86693b776f89d7b46995746127mike@reedtribe.org
27939cb5d755e7ea8647bcf8bb1ee151ca4c86051107caryclarkstatic bool checkOnCurve(SkScalar x, SkScalar y, const SkPoint& start, const SkPoint& end) {
27949cb5d755e7ea8647bcf8bb1ee151ca4c86051107caryclark    if (start.fY == end.fY) {
27959cb5d755e7ea8647bcf8bb1ee151ca4c86051107caryclark        return between(start.fX, x, end.fX) && x != end.fX;
27969cb5d755e7ea8647bcf8bb1ee151ca4c86051107caryclark    } else {
27979cb5d755e7ea8647bcf8bb1ee151ca4c86051107caryclark        return x == start.fX && y == start.fY;
27989cb5d755e7ea8647bcf8bb1ee151ca4c86051107caryclark    }
27999cb5d755e7ea8647bcf8bb1ee151ca4c86051107caryclark}
28009cb5d755e7ea8647bcf8bb1ee151ca4c86051107caryclark
28019aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclarkstatic int winding_mono_cubic(const SkPoint pts[], SkScalar x, SkScalar y, int* onCurveCount) {
28029cb5d755e7ea8647bcf8bb1ee151ca4c86051107caryclark    SkScalar y0 = pts[0].fY;
28039cb5d755e7ea8647bcf8bb1ee151ca4c86051107caryclark    SkScalar y3 = pts[3].fY;
28049cb5d755e7ea8647bcf8bb1ee151ca4c86051107caryclark
28059cb5d755e7ea8647bcf8bb1ee151ca4c86051107caryclark    int dir = 1;
28069cb5d755e7ea8647bcf8bb1ee151ca4c86051107caryclark    if (y0 > y3) {
28079cb5d755e7ea8647bcf8bb1ee151ca4c86051107caryclark        SkTSwap(y0, y3);
28089cb5d755e7ea8647bcf8bb1ee151ca4c86051107caryclark        dir = -1;
28099cb5d755e7ea8647bcf8bb1ee151ca4c86051107caryclark    }
28109cb5d755e7ea8647bcf8bb1ee151ca4c86051107caryclark    if (y < y0 || y > y3) {
28119aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark        return 0;
2812bad1b2ff1d34ff86693b776f89d7b46995746127mike@reedtribe.org    }
28139cb5d755e7ea8647bcf8bb1ee151ca4c86051107caryclark    if (checkOnCurve(x, y, pts[0], pts[3])) {
28149cb5d755e7ea8647bcf8bb1ee151ca4c86051107caryclark        *onCurveCount += 1;
28159cb5d755e7ea8647bcf8bb1ee151ca4c86051107caryclark        return 0;
28169cb5d755e7ea8647bcf8bb1ee151ca4c86051107caryclark    }
28179cb5d755e7ea8647bcf8bb1ee151ca4c86051107caryclark    if (y == y3) {
2818bad1b2ff1d34ff86693b776f89d7b46995746127mike@reedtribe.org        return 0;
2819bad1b2ff1d34ff86693b776f89d7b46995746127mike@reedtribe.org    }
28209cb5d755e7ea8647bcf8bb1ee151ca4c86051107caryclark
2821bad1b2ff1d34ff86693b776f89d7b46995746127mike@reedtribe.org    // quickreject or quickaccept
2822bad1b2ff1d34ff86693b776f89d7b46995746127mike@reedtribe.org    SkScalar min, max;
2823bad1b2ff1d34ff86693b776f89d7b46995746127mike@reedtribe.org    find_minmax<4>(pts, &min, &max);
2824bad1b2ff1d34ff86693b776f89d7b46995746127mike@reedtribe.org    if (x < min) {
2825bad1b2ff1d34ff86693b776f89d7b46995746127mike@reedtribe.org        return 0;
2826bad1b2ff1d34ff86693b776f89d7b46995746127mike@reedtribe.org    }
2827bad1b2ff1d34ff86693b776f89d7b46995746127mike@reedtribe.org    if (x > max) {
2828bad1b2ff1d34ff86693b776f89d7b46995746127mike@reedtribe.org        return dir;
2829bad1b2ff1d34ff86693b776f89d7b46995746127mike@reedtribe.org    }
2830fbfcd5602128ec010c82cb733c9cdc0a3254f9f3rmistry@google.com
2831bad1b2ff1d34ff86693b776f89d7b46995746127mike@reedtribe.org    // compute the actual x(t) value
2832a1a097ee814d05a92487d85db8ad02e1d852fd6fcommit-bot@chromium.org    SkScalar t;
2833276e63361c73fed6c6528b322400ece81fd1d067mbarbella    if (!SkCubicClipper::ChopMonoAtY(pts, y, &t)) {
283499600d0a158e3b2f1ff077a6fd102e78ce9db0e4mbarbella        return 0;
2835276e63361c73fed6c6528b322400ece81fd1d067mbarbella    }
2836a1a097ee814d05a92487d85db8ad02e1d852fd6fcommit-bot@chromium.org    SkScalar xt = eval_cubic_pts(pts[0].fX, pts[1].fX, pts[2].fX, pts[3].fX, t);
28379aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark    if (SkScalarNearlyEqual(xt, x)) {
28389aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark        if (x != pts[3].fX || y != pts[3].fY) {  // don't test end points; they're start points
28399aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark            *onCurveCount += 1;
28409cb5d755e7ea8647bcf8bb1ee151ca4c86051107caryclark            return 0;
28419aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark        }
28429aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark    }
2843bad1b2ff1d34ff86693b776f89d7b46995746127mike@reedtribe.org    return xt < x ? dir : 0;
2844bad1b2ff1d34ff86693b776f89d7b46995746127mike@reedtribe.org}
2845bad1b2ff1d34ff86693b776f89d7b46995746127mike@reedtribe.org
28469aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclarkstatic int winding_cubic(const SkPoint pts[], SkScalar x, SkScalar y, int* onCurveCount) {
2847bad1b2ff1d34ff86693b776f89d7b46995746127mike@reedtribe.org    SkPoint dst[10];
2848bad1b2ff1d34ff86693b776f89d7b46995746127mike@reedtribe.org    int n = SkChopCubicAtYExtrema(pts, dst);
2849bad1b2ff1d34ff86693b776f89d7b46995746127mike@reedtribe.org    int w = 0;
2850bad1b2ff1d34ff86693b776f89d7b46995746127mike@reedtribe.org    for (int i = 0; i <= n; ++i) {
28519aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark        w += winding_mono_cubic(&dst[i * 3], x, y, onCurveCount);
2852bad1b2ff1d34ff86693b776f89d7b46995746127mike@reedtribe.org    }
2853bad1b2ff1d34ff86693b776f89d7b46995746127mike@reedtribe.org    return w;
2854bad1b2ff1d34ff86693b776f89d7b46995746127mike@reedtribe.org}
2855bad1b2ff1d34ff86693b776f89d7b46995746127mike@reedtribe.org
28569aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclarkstatic double conic_eval_numerator(const SkScalar src[], SkScalar w, SkScalar t) {
28579aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark    SkASSERT(src);
28589aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark    SkASSERT(t >= 0 && t <= 1);
28599aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark    SkScalar src2w = src[2] * w;
28609aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark    SkScalar C = src[0];
28619aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark    SkScalar A = src[4] - 2 * src2w + C;
28629aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark    SkScalar B = 2 * (src2w - C);
2863a99b6ceff92183b424634f2e7276b9ea1d59e69dMike Reed    return poly_eval(A, B, C, t);
28649aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark}
28659aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark
28669aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark
28679aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclarkstatic double conic_eval_denominator(SkScalar w, SkScalar t) {
28689aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark    SkScalar B = 2 * (w - 1);
28699aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark    SkScalar C = 1;
28709aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark    SkScalar A = -B;
2871a99b6ceff92183b424634f2e7276b9ea1d59e69dMike Reed    return poly_eval(A, B, C, t);
28729aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark}
28739aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark
28749aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclarkstatic int winding_mono_conic(const SkConic& conic, SkScalar x, SkScalar y, int* onCurveCount) {
28759aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark    const SkPoint* pts = conic.fPts;
2876bad1b2ff1d34ff86693b776f89d7b46995746127mike@reedtribe.org    SkScalar y0 = pts[0].fY;
2877bad1b2ff1d34ff86693b776f89d7b46995746127mike@reedtribe.org    SkScalar y2 = pts[2].fY;
2878fbfcd5602128ec010c82cb733c9cdc0a3254f9f3rmistry@google.com
2879bad1b2ff1d34ff86693b776f89d7b46995746127mike@reedtribe.org    int dir = 1;
2880bad1b2ff1d34ff86693b776f89d7b46995746127mike@reedtribe.org    if (y0 > y2) {
2881bad1b2ff1d34ff86693b776f89d7b46995746127mike@reedtribe.org        SkTSwap(y0, y2);
2882bad1b2ff1d34ff86693b776f89d7b46995746127mike@reedtribe.org        dir = -1;
2883bad1b2ff1d34ff86693b776f89d7b46995746127mike@reedtribe.org    }
28849aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark    if (y < y0 || y > y2) {
28859aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark        return 0;
28869aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark    }
28879cb5d755e7ea8647bcf8bb1ee151ca4c86051107caryclark    if (checkOnCurve(x, y, pts[0], pts[2])) {
28889cb5d755e7ea8647bcf8bb1ee151ca4c86051107caryclark        *onCurveCount += 1;
28899cb5d755e7ea8647bcf8bb1ee151ca4c86051107caryclark        return 0;
28909cb5d755e7ea8647bcf8bb1ee151ca4c86051107caryclark    }
28919aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark    if (y == y2) {
2892bad1b2ff1d34ff86693b776f89d7b46995746127mike@reedtribe.org        return 0;
2893bad1b2ff1d34ff86693b776f89d7b46995746127mike@reedtribe.org    }
2894fbfcd5602128ec010c82cb733c9cdc0a3254f9f3rmistry@google.com
28959aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark    SkScalar roots[2];
28969aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark    SkScalar A = pts[2].fY;
28979aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark    SkScalar B = pts[1].fY * conic.fW - y * conic.fW + y;
28989aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark    SkScalar C = pts[0].fY;
28999aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark    A += C - 2 * B;  // A = a + c - 2*(b*w - yCept*w + yCept)
29009aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark    B -= C;  // B = b*w - w * yCept + yCept - a
29019aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark    C -= y;
29029aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark    int n = SkFindUnitQuadRoots(A, 2 * B, C, roots);
29039aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark    SkASSERT(n <= 1);
29049aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark    SkScalar xt;
29059aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark    if (0 == n) {
29069cb5d755e7ea8647bcf8bb1ee151ca4c86051107caryclark        // zero roots are returned only when y0 == y
29079cb5d755e7ea8647bcf8bb1ee151ca4c86051107caryclark        // Need [0] if dir == 1
29089cb5d755e7ea8647bcf8bb1ee151ca4c86051107caryclark        // and  [2] if dir == -1
29099cb5d755e7ea8647bcf8bb1ee151ca4c86051107caryclark        xt = pts[1 - dir].fX;
29109aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark    } else {
29119aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark        SkScalar t = roots[0];
29129aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark        xt = conic_eval_numerator(&pts[0].fX, conic.fW, t) / conic_eval_denominator(conic.fW, t);
29139aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark    }
29149aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark    if (SkScalarNearlyEqual(xt, x)) {
29159aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark        if (x != pts[2].fX || y != pts[2].fY) {  // don't test end points; they're start points
29169aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark            *onCurveCount += 1;
29179cb5d755e7ea8647bcf8bb1ee151ca4c86051107caryclark            return 0;
29189aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark        }
29199aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark    }
29209aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark    return xt < x ? dir : 0;
29219aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark}
29229aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark
29239aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclarkstatic bool is_mono_quad(SkScalar y0, SkScalar y1, SkScalar y2) {
29249aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark    //    return SkScalarSignAsInt(y0 - y1) + SkScalarSignAsInt(y1 - y2) != 0;
29259aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark    if (y0 == y1) {
29269aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark        return true;
29279aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark    }
29289aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark    if (y0 < y1) {
29299aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark        return y1 <= y2;
29309aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark    } else {
29319aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark        return y1 >= y2;
29329aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark    }
29339aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark}
29349aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark
29359aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclarkstatic int winding_conic(const SkPoint pts[], SkScalar x, SkScalar y, SkScalar weight,
29369aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark                         int* onCurveCount) {
29379aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark    SkConic conic(pts, weight);
29389aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark    SkConic chopped[2];
29391e1e5094e0fda27b09826c573a3a68d08e7b6ae1caryclark    // If the data points are very large, the conic may not be monotonic but may also
29401e1e5094e0fda27b09826c573a3a68d08e7b6ae1caryclark    // fail to chop. Then, the chopper does not split the original conic in two.
2941e114dd63161b5efffbffb17e4d3facad77f34507caryclark    bool isMono = is_mono_quad(pts[0].fY, pts[1].fY, pts[2].fY) || !conic.chopAtYExtrema(chopped);
2942e114dd63161b5efffbffb17e4d3facad77f34507caryclark    int w = winding_mono_conic(isMono ? conic : chopped[0], x, y, onCurveCount);
2943e114dd63161b5efffbffb17e4d3facad77f34507caryclark    if (!isMono) {
29449aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark        w += winding_mono_conic(chopped[1], x, y, onCurveCount);
29459aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark    }
29469aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark    return w;
29479aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark}
29489aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark
29499aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclarkstatic int winding_mono_quad(const SkPoint pts[], SkScalar x, SkScalar y, int* onCurveCount) {
29509aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark    SkScalar y0 = pts[0].fY;
29519aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark    SkScalar y2 = pts[2].fY;
29529aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark
29539aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark    int dir = 1;
29549aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark    if (y0 > y2) {
29559aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark        SkTSwap(y0, y2);
29569aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark        dir = -1;
29579aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark    }
29589aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark    if (y < y0 || y > y2) {
29599aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark        return 0;
29609aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark    }
29619cb5d755e7ea8647bcf8bb1ee151ca4c86051107caryclark    if (checkOnCurve(x, y, pts[0], pts[2])) {
29629cb5d755e7ea8647bcf8bb1ee151ca4c86051107caryclark        *onCurveCount += 1;
29639cb5d755e7ea8647bcf8bb1ee151ca4c86051107caryclark        return 0;
29649cb5d755e7ea8647bcf8bb1ee151ca4c86051107caryclark    }
29659aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark    if (y == y2) {
29669aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark        return 0;
29679aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark    }
2968bad1b2ff1d34ff86693b776f89d7b46995746127mike@reedtribe.org    // bounds check on X (not required. is it faster?)
2969bad1b2ff1d34ff86693b776f89d7b46995746127mike@reedtribe.org#if 0
2970bad1b2ff1d34ff86693b776f89d7b46995746127mike@reedtribe.org    if (pts[0].fX > x && pts[1].fX > x && pts[2].fX > x) {
2971bad1b2ff1d34ff86693b776f89d7b46995746127mike@reedtribe.org        return 0;
2972bad1b2ff1d34ff86693b776f89d7b46995746127mike@reedtribe.org    }
2973bad1b2ff1d34ff86693b776f89d7b46995746127mike@reedtribe.org#endif
2974fbfcd5602128ec010c82cb733c9cdc0a3254f9f3rmistry@google.com
2975bad1b2ff1d34ff86693b776f89d7b46995746127mike@reedtribe.org    SkScalar roots[2];
2976bad1b2ff1d34ff86693b776f89d7b46995746127mike@reedtribe.org    int n = SkFindUnitQuadRoots(pts[0].fY - 2 * pts[1].fY + pts[2].fY,
2977bad1b2ff1d34ff86693b776f89d7b46995746127mike@reedtribe.org                                2 * (pts[1].fY - pts[0].fY),
2978bad1b2ff1d34ff86693b776f89d7b46995746127mike@reedtribe.org                                pts[0].fY - y,
2979bad1b2ff1d34ff86693b776f89d7b46995746127mike@reedtribe.org                                roots);
2980bad1b2ff1d34ff86693b776f89d7b46995746127mike@reedtribe.org    SkASSERT(n <= 1);
2981bad1b2ff1d34ff86693b776f89d7b46995746127mike@reedtribe.org    SkScalar xt;
2982bad1b2ff1d34ff86693b776f89d7b46995746127mike@reedtribe.org    if (0 == n) {
29839cb5d755e7ea8647bcf8bb1ee151ca4c86051107caryclark        // zero roots are returned only when y0 == y
29849cb5d755e7ea8647bcf8bb1ee151ca4c86051107caryclark        // Need [0] if dir == 1
29859cb5d755e7ea8647bcf8bb1ee151ca4c86051107caryclark        // and  [2] if dir == -1
29869cb5d755e7ea8647bcf8bb1ee151ca4c86051107caryclark        xt = pts[1 - dir].fX;
2987bad1b2ff1d34ff86693b776f89d7b46995746127mike@reedtribe.org    } else {
2988bad1b2ff1d34ff86693b776f89d7b46995746127mike@reedtribe.org        SkScalar t = roots[0];
2989bad1b2ff1d34ff86693b776f89d7b46995746127mike@reedtribe.org        SkScalar C = pts[0].fX;
2990bad1b2ff1d34ff86693b776f89d7b46995746127mike@reedtribe.org        SkScalar A = pts[2].fX - 2 * pts[1].fX + C;
2991bad1b2ff1d34ff86693b776f89d7b46995746127mike@reedtribe.org        SkScalar B = 2 * (pts[1].fX - C);
2992a99b6ceff92183b424634f2e7276b9ea1d59e69dMike Reed        xt = poly_eval(A, B, C, t);
2993bad1b2ff1d34ff86693b776f89d7b46995746127mike@reedtribe.org    }
29949aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark    if (SkScalarNearlyEqual(xt, x)) {
29959aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark        if (x != pts[2].fX || y != pts[2].fY) {  // don't test end points; they're start points
29969aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark            *onCurveCount += 1;
29979cb5d755e7ea8647bcf8bb1ee151ca4c86051107caryclark            return 0;
29989aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark        }
2999bad1b2ff1d34ff86693b776f89d7b46995746127mike@reedtribe.org    }
30009aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark    return xt < x ? dir : 0;
3001bad1b2ff1d34ff86693b776f89d7b46995746127mike@reedtribe.org}
3002bad1b2ff1d34ff86693b776f89d7b46995746127mike@reedtribe.org
30039aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclarkstatic int winding_quad(const SkPoint pts[], SkScalar x, SkScalar y, int* onCurveCount) {
3004bad1b2ff1d34ff86693b776f89d7b46995746127mike@reedtribe.org    SkPoint dst[5];
3005bad1b2ff1d34ff86693b776f89d7b46995746127mike@reedtribe.org    int     n = 0;
3006fbfcd5602128ec010c82cb733c9cdc0a3254f9f3rmistry@google.com
3007bad1b2ff1d34ff86693b776f89d7b46995746127mike@reedtribe.org    if (!is_mono_quad(pts[0].fY, pts[1].fY, pts[2].fY)) {
3008bad1b2ff1d34ff86693b776f89d7b46995746127mike@reedtribe.org        n = SkChopQuadAtYExtrema(pts, dst);
3009bad1b2ff1d34ff86693b776f89d7b46995746127mike@reedtribe.org        pts = dst;
3010bad1b2ff1d34ff86693b776f89d7b46995746127mike@reedtribe.org    }
30119aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark    int w = winding_mono_quad(pts, x, y, onCurveCount);
3012bad1b2ff1d34ff86693b776f89d7b46995746127mike@reedtribe.org    if (n > 0) {
30139aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark        w += winding_mono_quad(&pts[2], x, y, onCurveCount);
3014bad1b2ff1d34ff86693b776f89d7b46995746127mike@reedtribe.org    }
3015bad1b2ff1d34ff86693b776f89d7b46995746127mike@reedtribe.org    return w;
3016bad1b2ff1d34ff86693b776f89d7b46995746127mike@reedtribe.org}
3017bad1b2ff1d34ff86693b776f89d7b46995746127mike@reedtribe.org
30189aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclarkstatic int winding_line(const SkPoint pts[], SkScalar x, SkScalar y, int* onCurveCount) {
3019bad1b2ff1d34ff86693b776f89d7b46995746127mike@reedtribe.org    SkScalar x0 = pts[0].fX;
3020bad1b2ff1d34ff86693b776f89d7b46995746127mike@reedtribe.org    SkScalar y0 = pts[0].fY;
3021bad1b2ff1d34ff86693b776f89d7b46995746127mike@reedtribe.org    SkScalar x1 = pts[1].fX;
3022bad1b2ff1d34ff86693b776f89d7b46995746127mike@reedtribe.org    SkScalar y1 = pts[1].fY;
3023fbfcd5602128ec010c82cb733c9cdc0a3254f9f3rmistry@google.com
3024bad1b2ff1d34ff86693b776f89d7b46995746127mike@reedtribe.org    SkScalar dy = y1 - y0;
3025fbfcd5602128ec010c82cb733c9cdc0a3254f9f3rmistry@google.com
3026bad1b2ff1d34ff86693b776f89d7b46995746127mike@reedtribe.org    int dir = 1;
3027bad1b2ff1d34ff86693b776f89d7b46995746127mike@reedtribe.org    if (y0 > y1) {
3028bad1b2ff1d34ff86693b776f89d7b46995746127mike@reedtribe.org        SkTSwap(y0, y1);
3029bad1b2ff1d34ff86693b776f89d7b46995746127mike@reedtribe.org        dir = -1;
3030bad1b2ff1d34ff86693b776f89d7b46995746127mike@reedtribe.org    }
30319aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark    if (y < y0 || y > y1) {
3032bad1b2ff1d34ff86693b776f89d7b46995746127mike@reedtribe.org        return 0;
3033bad1b2ff1d34ff86693b776f89d7b46995746127mike@reedtribe.org    }
30349cb5d755e7ea8647bcf8bb1ee151ca4c86051107caryclark    if (checkOnCurve(x, y, pts[0], pts[1])) {
30359cb5d755e7ea8647bcf8bb1ee151ca4c86051107caryclark        *onCurveCount += 1;
30369cb5d755e7ea8647bcf8bb1ee151ca4c86051107caryclark        return 0;
30379cb5d755e7ea8647bcf8bb1ee151ca4c86051107caryclark    }
30389cb5d755e7ea8647bcf8bb1ee151ca4c86051107caryclark    if (y == y1) {
30399aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark        return 0;
30409aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark    }
3041a99b6ceff92183b424634f2e7276b9ea1d59e69dMike Reed    SkScalar cross = (x1 - x0) * (y - pts[0].fY) - dy * (x - x0);
3042fbfcd5602128ec010c82cb733c9cdc0a3254f9f3rmistry@google.com
30439aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark    if (!cross) {
3044c91065d028472688ce15e635a29abe5256ff89effs        // zero cross means the point is on the line, and since the case where
3045c91065d028472688ce15e635a29abe5256ff89effs        // y of the query point is at the end point is handled above, we can be
3046c91065d028472688ce15e635a29abe5256ff89effs        // sure that we're on the line (excluding the end point) here
30479cb5d755e7ea8647bcf8bb1ee151ca4c86051107caryclark        if (x != x1 || y != pts[1].fY) {
30489cb5d755e7ea8647bcf8bb1ee151ca4c86051107caryclark            *onCurveCount += 1;
30499cb5d755e7ea8647bcf8bb1ee151ca4c86051107caryclark        }
30509aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark        dir = 0;
30519aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark    } else if (SkScalarSignAsInt(cross) == dir) {
3052bad1b2ff1d34ff86693b776f89d7b46995746127mike@reedtribe.org        dir = 0;
3053bad1b2ff1d34ff86693b776f89d7b46995746127mike@reedtribe.org    }
3054bad1b2ff1d34ff86693b776f89d7b46995746127mike@reedtribe.org    return dir;
3055bad1b2ff1d34ff86693b776f89d7b46995746127mike@reedtribe.org}
3056bad1b2ff1d34ff86693b776f89d7b46995746127mike@reedtribe.org
30579aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclarkstatic void tangent_cubic(const SkPoint pts[], SkScalar x, SkScalar y,
30589aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark        SkTDArray<SkVector>* tangents) {
30599aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark    if (!between(pts[0].fY, y, pts[1].fY) && !between(pts[1].fY, y, pts[2].fY)
30609aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark             && !between(pts[2].fY, y, pts[3].fY)) {
30619aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark        return;
30629aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark    }
30639aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark    if (!between(pts[0].fX, x, pts[1].fX) && !between(pts[1].fX, x, pts[2].fX)
30649aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark             && !between(pts[2].fX, x, pts[3].fX)) {
30659aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark        return;
30669aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark    }
30679aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark    SkPoint dst[10];
30689aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark    int n = SkChopCubicAtYExtrema(pts, dst);
30699aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark    for (int i = 0; i <= n; ++i) {
30709aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark        SkPoint* c = &dst[i * 3];
30719aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark        SkScalar t;
3072276e63361c73fed6c6528b322400ece81fd1d067mbarbella        if (!SkCubicClipper::ChopMonoAtY(c, y, &t)) {
307399600d0a158e3b2f1ff077a6fd102e78ce9db0e4mbarbella            continue;
3074276e63361c73fed6c6528b322400ece81fd1d067mbarbella        }
30759aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark        SkScalar xt = eval_cubic_pts(c[0].fX, c[1].fX, c[2].fX, c[3].fX, t);
30769aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark        if (!SkScalarNearlyEqual(x, xt)) {
30779aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark            continue;
30789aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark        }
30799aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark        SkVector tangent;
30809aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark        SkEvalCubicAt(c, t, nullptr, &tangent, nullptr);
30819aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark        tangents->push(tangent);
30829aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark    }
30839aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark}
30849aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark
30859aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclarkstatic void tangent_conic(const SkPoint pts[], SkScalar x, SkScalar y, SkScalar w,
30869aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark            SkTDArray<SkVector>* tangents) {
30879aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark    if (!between(pts[0].fY, y, pts[1].fY) && !between(pts[1].fY, y, pts[2].fY)) {
30889aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark        return;
30899aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark    }
30909aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark    if (!between(pts[0].fX, x, pts[1].fX) && !between(pts[1].fX, x, pts[2].fX)) {
30919aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark        return;
30929aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark    }
30939aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark    SkScalar roots[2];
30949aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark    SkScalar A = pts[2].fY;
30959aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark    SkScalar B = pts[1].fY * w - y * w + y;
30969aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark    SkScalar C = pts[0].fY;
30979aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark    A += C - 2 * B;  // A = a + c - 2*(b*w - yCept*w + yCept)
30989aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark    B -= C;  // B = b*w - w * yCept + yCept - a
30999aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark    C -= y;
31009aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark    int n = SkFindUnitQuadRoots(A, 2 * B, C, roots);
31019aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark    for (int index = 0; index < n; ++index) {
31029aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark        SkScalar t = roots[index];
31039aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark        SkScalar xt = conic_eval_numerator(&pts[0].fX, w, t) / conic_eval_denominator(w, t);
31049aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark        if (!SkScalarNearlyEqual(x, xt)) {
31059aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark            continue;
31069aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark        }
31079aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark        SkConic conic(pts, w);
31089aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark        tangents->push(conic.evalTangentAt(t));
31099aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark    }
31109aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark}
31119aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark
31129aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclarkstatic void tangent_quad(const SkPoint pts[], SkScalar x, SkScalar y,
31139aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark        SkTDArray<SkVector>* tangents) {
31149aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark    if (!between(pts[0].fY, y, pts[1].fY) && !between(pts[1].fY, y, pts[2].fY)) {
31159aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark        return;
31169aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark    }
31179aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark    if (!between(pts[0].fX, x, pts[1].fX) && !between(pts[1].fX, x, pts[2].fX)) {
31189aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark        return;
31199aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark    }
31209aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark    SkScalar roots[2];
31219aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark    int n = SkFindUnitQuadRoots(pts[0].fY - 2 * pts[1].fY + pts[2].fY,
31229aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark                                2 * (pts[1].fY - pts[0].fY),
31239aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark                                pts[0].fY - y,
31249aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark                                roots);
31259aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark    for (int index = 0; index < n; ++index) {
31269aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark        SkScalar t = roots[index];
31279aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark        SkScalar C = pts[0].fX;
31289aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark        SkScalar A = pts[2].fX - 2 * pts[1].fX + C;
31299aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark        SkScalar B = 2 * (pts[1].fX - C);
3130a99b6ceff92183b424634f2e7276b9ea1d59e69dMike Reed        SkScalar xt = poly_eval(A, B, C, t);
31319aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark        if (!SkScalarNearlyEqual(x, xt)) {
31329aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark            continue;
31339aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark        }
31349aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark        tangents->push(SkEvalQuadTangentAt(pts, t));
31359aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark    }
31369aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark}
31379aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark
31389aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclarkstatic void tangent_line(const SkPoint pts[], SkScalar x, SkScalar y,
31399aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark        SkTDArray<SkVector>* tangents) {
31409aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark    SkScalar y0 = pts[0].fY;
31419aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark    SkScalar y1 = pts[1].fY;
31429aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark    if (!between(y0, y, y1)) {
31439aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark        return;
31449aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark    }
31459aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark    SkScalar x0 = pts[0].fX;
31469aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark    SkScalar x1 = pts[1].fX;
31479aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark    if (!between(x0, x, x1)) {
31489aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark        return;
31499aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark    }
31509aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark    SkScalar dx = x1 - x0;
31519aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark    SkScalar dy = y1 - y0;
31529aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark    if (!SkScalarNearlyEqual((x - x0) * dy, dx * (y - y0))) {
31539aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark        return;
31549aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark    }
31559aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark    SkVector v;
31569aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark    v.set(dx, dy);
31579aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark    tangents->push(v);
31589aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark}
31599aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark
31604db592c4085afed2be27a208d778f9ee13e671abreed@google.comstatic bool contains_inclusive(const SkRect& r, SkScalar x, SkScalar y) {
31614db592c4085afed2be27a208d778f9ee13e671abreed@google.com    return r.fLeft <= x && x <= r.fRight && r.fTop <= y && y <= r.fBottom;
31624db592c4085afed2be27a208d778f9ee13e671abreed@google.com}
31634db592c4085afed2be27a208d778f9ee13e671abreed@google.com
3164bad1b2ff1d34ff86693b776f89d7b46995746127mike@reedtribe.orgbool SkPath::contains(SkScalar x, SkScalar y) const {
3165bad1b2ff1d34ff86693b776f89d7b46995746127mike@reedtribe.org    bool isInverse = this->isInverseFillType();
3166bad1b2ff1d34ff86693b776f89d7b46995746127mike@reedtribe.org    if (this->isEmpty()) {
3167bad1b2ff1d34ff86693b776f89d7b46995746127mike@reedtribe.org        return isInverse;
3168bad1b2ff1d34ff86693b776f89d7b46995746127mike@reedtribe.org    }
3169fbfcd5602128ec010c82cb733c9cdc0a3254f9f3rmistry@google.com
31704db592c4085afed2be27a208d778f9ee13e671abreed@google.com    if (!contains_inclusive(this->getBounds(), x, y)) {
3171bad1b2ff1d34ff86693b776f89d7b46995746127mike@reedtribe.org        return isInverse;
3172bad1b2ff1d34ff86693b776f89d7b46995746127mike@reedtribe.org    }
3173fbfcd5602128ec010c82cb733c9cdc0a3254f9f3rmistry@google.com
3174bad1b2ff1d34ff86693b776f89d7b46995746127mike@reedtribe.org    SkPath::Iter iter(*this, true);
3175bad1b2ff1d34ff86693b776f89d7b46995746127mike@reedtribe.org    bool done = false;
3176bad1b2ff1d34ff86693b776f89d7b46995746127mike@reedtribe.org    int w = 0;
31779aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark    int onCurveCount = 0;
3178bad1b2ff1d34ff86693b776f89d7b46995746127mike@reedtribe.org    do {
3179bad1b2ff1d34ff86693b776f89d7b46995746127mike@reedtribe.org        SkPoint pts[4];
3180bad1b2ff1d34ff86693b776f89d7b46995746127mike@reedtribe.org        switch (iter.next(pts, false)) {
3181bad1b2ff1d34ff86693b776f89d7b46995746127mike@reedtribe.org            case SkPath::kMove_Verb:
3182bad1b2ff1d34ff86693b776f89d7b46995746127mike@reedtribe.org            case SkPath::kClose_Verb:
3183bad1b2ff1d34ff86693b776f89d7b46995746127mike@reedtribe.org                break;
3184bad1b2ff1d34ff86693b776f89d7b46995746127mike@reedtribe.org            case SkPath::kLine_Verb:
31859aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark                w += winding_line(pts, x, y, &onCurveCount);
3186bad1b2ff1d34ff86693b776f89d7b46995746127mike@reedtribe.org                break;
3187bad1b2ff1d34ff86693b776f89d7b46995746127mike@reedtribe.org            case SkPath::kQuad_Verb:
31889aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark                w += winding_quad(pts, x, y, &onCurveCount);
3189bad1b2ff1d34ff86693b776f89d7b46995746127mike@reedtribe.org                break;
3190277c3f87656c44e0a651ed0dd56efa16c0ab07b4reed@google.com            case SkPath::kConic_Verb:
31919aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark                w += winding_conic(pts, x, y, iter.conicWeight(), &onCurveCount);
3192277c3f87656c44e0a651ed0dd56efa16c0ab07b4reed@google.com                break;
3193bad1b2ff1d34ff86693b776f89d7b46995746127mike@reedtribe.org            case SkPath::kCubic_Verb:
31949aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark                w += winding_cubic(pts, x, y, &onCurveCount);
3195bad1b2ff1d34ff86693b776f89d7b46995746127mike@reedtribe.org                break;
3196bad1b2ff1d34ff86693b776f89d7b46995746127mike@reedtribe.org            case SkPath::kDone_Verb:
3197bad1b2ff1d34ff86693b776f89d7b46995746127mike@reedtribe.org                done = true;
3198bad1b2ff1d34ff86693b776f89d7b46995746127mike@reedtribe.org                break;
3199277c3f87656c44e0a651ed0dd56efa16c0ab07b4reed@google.com       }
3200bad1b2ff1d34ff86693b776f89d7b46995746127mike@reedtribe.org    } while (!done);
32019aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark    bool evenOddFill = SkPath::kEvenOdd_FillType == this->getFillType()
32029aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark            || SkPath::kInverseEvenOdd_FillType == this->getFillType();
32039aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark    if (evenOddFill) {
32049aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark        w &= 1;
32059aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark    }
32069aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark    if (w) {
32079aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark        return !isInverse;
32089aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark    }
32099aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark    if (onCurveCount <= 1) {
32109aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark        return SkToBool(onCurveCount) ^ isInverse;
3211bad1b2ff1d34ff86693b776f89d7b46995746127mike@reedtribe.org    }
32129aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark    if ((onCurveCount & 1) || evenOddFill) {
32139aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark        return SkToBool(onCurveCount & 1) ^ isInverse;
32149aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark    }
32159d524f22bfde5dc3dc8f48e1be39bdebd3bb0304halcanary    // If the point touches an even number of curves, and the fill is winding, check for
32169aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark    // coincidence. Count coincidence as places where the on curve points have identical tangents.
32179aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark    iter.setPath(*this, true);
32189cb5d755e7ea8647bcf8bb1ee151ca4c86051107caryclark    done = false;
32199aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark    SkTDArray<SkVector> tangents;
32209aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark    do {
32219aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark        SkPoint pts[4];
32229aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark        int oldCount = tangents.count();
32239aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark        switch (iter.next(pts, false)) {
32249aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark            case SkPath::kMove_Verb:
32259aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark            case SkPath::kClose_Verb:
32269aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark                break;
32279aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark            case SkPath::kLine_Verb:
32289aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark                tangent_line(pts, x, y, &tangents);
32299aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark                break;
32309aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark            case SkPath::kQuad_Verb:
32319aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark                tangent_quad(pts, x, y, &tangents);
32329aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark                break;
32339aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark            case SkPath::kConic_Verb:
32349aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark                tangent_conic(pts, x, y, iter.conicWeight(), &tangents);
32359aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark                break;
32369aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark            case SkPath::kCubic_Verb:
32379aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark                tangent_cubic(pts, x, y, &tangents);
32389aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark                break;
32399aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark            case SkPath::kDone_Verb:
32409aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark                done = true;
32419aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark                break;
32429aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark       }
32439aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark       if (tangents.count() > oldCount) {
32449aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark            int last = tangents.count() - 1;
32459aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark            const SkVector& tangent = tangents[last];
32469aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark            if (SkScalarNearlyZero(tangent.lengthSqd())) {
32479aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark                tangents.remove(last);
32489aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark            } else {
32499aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark                for (int index = 0; index < last; ++index) {
32509aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark                    const SkVector& test = tangents[index];
32519aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark                    if (SkScalarNearlyZero(test.cross(tangent))
32529cb5d755e7ea8647bcf8bb1ee151ca4c86051107caryclark                            && SkScalarSignAsInt(tangent.fX * test.fX) <= 0
32539cb5d755e7ea8647bcf8bb1ee151ca4c86051107caryclark                            && SkScalarSignAsInt(tangent.fY * test.fY) <= 0) {
32549aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark                        tangents.remove(last);
32559aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark                        tangents.removeShuffle(index);
32569aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark                        break;
32579aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark                    }
32589aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark                }
32599aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark            }
32609aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark        }
32619aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark    } while (!done);
32629aacd9029c7076d5c0f0e62338b82ce91de68ef9caryclark    return SkToBool(tangents.count()) ^ isInverse;
3263bad1b2ff1d34ff86693b776f89d7b46995746127mike@reedtribe.org}
3264aa0df4e98d39cf0691fbaf0766c9f5f7ec72177ffmalita
3265aa0df4e98d39cf0691fbaf0766c9f5f7ec72177ffmalitaint SkPath::ConvertConicToQuads(const SkPoint& p0, const SkPoint& p1, const SkPoint& p2,
3266aa0df4e98d39cf0691fbaf0766c9f5f7ec72177ffmalita                                SkScalar w, SkPoint pts[], int pow2) {
3267aa0df4e98d39cf0691fbaf0766c9f5f7ec72177ffmalita    const SkConic conic(p0, p1, p2, w);
3268aa0df4e98d39cf0691fbaf0766c9f5f7ec72177ffmalita    return conic.chopIntoQuadsPOW2(pts, pow2);
3269aa0df4e98d39cf0691fbaf0766c9f5f7ec72177ffmalita}
3270edc743a57657107b873ed2fc2efeeff1b1efcd23bsalomon
3271edc743a57657107b873ed2fc2efeeff1b1efcd23bsalomonbool SkPathPriv::IsSimpleClosedRect(const SkPath& path, SkRect* rect, SkPath::Direction* direction,
3272edc743a57657107b873ed2fc2efeeff1b1efcd23bsalomon                                    unsigned* start) {
3273edc743a57657107b873ed2fc2efeeff1b1efcd23bsalomon    if (path.getSegmentMasks() != SkPath::kLine_SegmentMask) {
3274edc743a57657107b873ed2fc2efeeff1b1efcd23bsalomon        return false;
3275edc743a57657107b873ed2fc2efeeff1b1efcd23bsalomon    }
3276edc743a57657107b873ed2fc2efeeff1b1efcd23bsalomon    SkPath::RawIter iter(path);
3277edc743a57657107b873ed2fc2efeeff1b1efcd23bsalomon    SkPoint verbPts[4];
3278edc743a57657107b873ed2fc2efeeff1b1efcd23bsalomon    SkPath::Verb v;
3279edc743a57657107b873ed2fc2efeeff1b1efcd23bsalomon    SkPoint rectPts[5];
3280edc743a57657107b873ed2fc2efeeff1b1efcd23bsalomon    int rectPtCnt = 0;
3281edc743a57657107b873ed2fc2efeeff1b1efcd23bsalomon    while ((v = iter.next(verbPts)) != SkPath::kDone_Verb) {
3282edc743a57657107b873ed2fc2efeeff1b1efcd23bsalomon        switch (v) {
3283edc743a57657107b873ed2fc2efeeff1b1efcd23bsalomon            case SkPath::kMove_Verb:
3284edc743a57657107b873ed2fc2efeeff1b1efcd23bsalomon                if (0 != rectPtCnt) {
3285edc743a57657107b873ed2fc2efeeff1b1efcd23bsalomon                    return false;
3286edc743a57657107b873ed2fc2efeeff1b1efcd23bsalomon                }
3287edc743a57657107b873ed2fc2efeeff1b1efcd23bsalomon                rectPts[0] = verbPts[0];
3288edc743a57657107b873ed2fc2efeeff1b1efcd23bsalomon                ++rectPtCnt;
3289edc743a57657107b873ed2fc2efeeff1b1efcd23bsalomon                break;
3290edc743a57657107b873ed2fc2efeeff1b1efcd23bsalomon            case SkPath::kLine_Verb:
3291edc743a57657107b873ed2fc2efeeff1b1efcd23bsalomon                if (5 == rectPtCnt) {
3292edc743a57657107b873ed2fc2efeeff1b1efcd23bsalomon                    return false;
3293edc743a57657107b873ed2fc2efeeff1b1efcd23bsalomon                }
3294edc743a57657107b873ed2fc2efeeff1b1efcd23bsalomon                rectPts[rectPtCnt] = verbPts[1];
3295edc743a57657107b873ed2fc2efeeff1b1efcd23bsalomon                ++rectPtCnt;
3296edc743a57657107b873ed2fc2efeeff1b1efcd23bsalomon                break;
3297edc743a57657107b873ed2fc2efeeff1b1efcd23bsalomon            case SkPath::kClose_Verb:
3298edc743a57657107b873ed2fc2efeeff1b1efcd23bsalomon                if (4 == rectPtCnt) {
3299edc743a57657107b873ed2fc2efeeff1b1efcd23bsalomon                    rectPts[4] = rectPts[0];
3300edc743a57657107b873ed2fc2efeeff1b1efcd23bsalomon                    rectPtCnt = 5;
3301edc743a57657107b873ed2fc2efeeff1b1efcd23bsalomon                }
3302edc743a57657107b873ed2fc2efeeff1b1efcd23bsalomon                break;
3303edc743a57657107b873ed2fc2efeeff1b1efcd23bsalomon            default:
3304edc743a57657107b873ed2fc2efeeff1b1efcd23bsalomon                return false;
3305edc743a57657107b873ed2fc2efeeff1b1efcd23bsalomon        }
3306edc743a57657107b873ed2fc2efeeff1b1efcd23bsalomon    }
3307edc743a57657107b873ed2fc2efeeff1b1efcd23bsalomon    if (rectPtCnt < 5) {
3308edc743a57657107b873ed2fc2efeeff1b1efcd23bsalomon        return false;
3309edc743a57657107b873ed2fc2efeeff1b1efcd23bsalomon    }
3310edc743a57657107b873ed2fc2efeeff1b1efcd23bsalomon    if (rectPts[0] != rectPts[4]) {
3311edc743a57657107b873ed2fc2efeeff1b1efcd23bsalomon        return false;
3312edc743a57657107b873ed2fc2efeeff1b1efcd23bsalomon    }
3313057ae8a15ddd2af639a829d63aca29cbc6b1bb57bsalomon    // Check for two cases of rectangles: pts 0 and 3 form a vertical edge or a horizontal edge (
3314057ae8a15ddd2af639a829d63aca29cbc6b1bb57bsalomon    // and pts 1 and 2 the opposite vertical or horizontal edge).
3315057ae8a15ddd2af639a829d63aca29cbc6b1bb57bsalomon    bool vec03IsVertical;
3316057ae8a15ddd2af639a829d63aca29cbc6b1bb57bsalomon    if (rectPts[0].fX == rectPts[3].fX && rectPts[1].fX == rectPts[2].fX &&
3317057ae8a15ddd2af639a829d63aca29cbc6b1bb57bsalomon        rectPts[0].fY == rectPts[1].fY && rectPts[3].fY == rectPts[2].fY) {
3318057ae8a15ddd2af639a829d63aca29cbc6b1bb57bsalomon        // Make sure it has non-zero width and height
3319057ae8a15ddd2af639a829d63aca29cbc6b1bb57bsalomon        if (rectPts[0].fX == rectPts[1].fX || rectPts[0].fY == rectPts[3].fY) {
3320057ae8a15ddd2af639a829d63aca29cbc6b1bb57bsalomon            return false;
3321edc743a57657107b873ed2fc2efeeff1b1efcd23bsalomon        }
3322057ae8a15ddd2af639a829d63aca29cbc6b1bb57bsalomon        vec03IsVertical = true;
3323057ae8a15ddd2af639a829d63aca29cbc6b1bb57bsalomon    } else if (rectPts[0].fY == rectPts[3].fY && rectPts[1].fY == rectPts[2].fY &&
3324057ae8a15ddd2af639a829d63aca29cbc6b1bb57bsalomon               rectPts[0].fX == rectPts[1].fX && rectPts[3].fX == rectPts[2].fX) {
3325057ae8a15ddd2af639a829d63aca29cbc6b1bb57bsalomon        // Make sure it has non-zero width and height
3326057ae8a15ddd2af639a829d63aca29cbc6b1bb57bsalomon        if (rectPts[0].fY == rectPts[1].fY || rectPts[0].fX == rectPts[3].fX) {
3327edc743a57657107b873ed2fc2efeeff1b1efcd23bsalomon            return false;
3328edc743a57657107b873ed2fc2efeeff1b1efcd23bsalomon        }
3329057ae8a15ddd2af639a829d63aca29cbc6b1bb57bsalomon        vec03IsVertical = false;
3330057ae8a15ddd2af639a829d63aca29cbc6b1bb57bsalomon    } else {
3331edc743a57657107b873ed2fc2efeeff1b1efcd23bsalomon        return false;
3332edc743a57657107b873ed2fc2efeeff1b1efcd23bsalomon    }
3333057ae8a15ddd2af639a829d63aca29cbc6b1bb57bsalomon    // Set sortFlags so that it has the low bit set if pt index 0 is on right edge and second bit
3334057ae8a15ddd2af639a829d63aca29cbc6b1bb57bsalomon    // set if it is on the bottom edge.
3335057ae8a15ddd2af639a829d63aca29cbc6b1bb57bsalomon    unsigned sortFlags =
3336057ae8a15ddd2af639a829d63aca29cbc6b1bb57bsalomon            ((rectPts[0].fX < rectPts[2].fX) ? 0b00 : 0b01) |
3337057ae8a15ddd2af639a829d63aca29cbc6b1bb57bsalomon            ((rectPts[0].fY < rectPts[2].fY) ? 0b00 : 0b10);
3338057ae8a15ddd2af639a829d63aca29cbc6b1bb57bsalomon    switch (sortFlags) {
3339057ae8a15ddd2af639a829d63aca29cbc6b1bb57bsalomon        case 0b00:
3340057ae8a15ddd2af639a829d63aca29cbc6b1bb57bsalomon            rect->set(rectPts[0].fX, rectPts[0].fY, rectPts[2].fX, rectPts[2].fY);
3341057ae8a15ddd2af639a829d63aca29cbc6b1bb57bsalomon            *direction = vec03IsVertical ? SkPath::kCW_Direction : SkPath::kCCW_Direction;
3342057ae8a15ddd2af639a829d63aca29cbc6b1bb57bsalomon            *start = 0;
3343057ae8a15ddd2af639a829d63aca29cbc6b1bb57bsalomon            break;
3344057ae8a15ddd2af639a829d63aca29cbc6b1bb57bsalomon        case 0b01:
3345057ae8a15ddd2af639a829d63aca29cbc6b1bb57bsalomon            rect->set(rectPts[2].fX, rectPts[0].fY, rectPts[0].fX, rectPts[2].fY);
3346057ae8a15ddd2af639a829d63aca29cbc6b1bb57bsalomon            *direction = vec03IsVertical ? SkPath::kCCW_Direction : SkPath::kCW_Direction;
3347057ae8a15ddd2af639a829d63aca29cbc6b1bb57bsalomon            *start = 1;
3348057ae8a15ddd2af639a829d63aca29cbc6b1bb57bsalomon            break;
3349057ae8a15ddd2af639a829d63aca29cbc6b1bb57bsalomon        case 0b10:
3350057ae8a15ddd2af639a829d63aca29cbc6b1bb57bsalomon            rect->set(rectPts[0].fX, rectPts[2].fY, rectPts[2].fX, rectPts[0].fY);
3351057ae8a15ddd2af639a829d63aca29cbc6b1bb57bsalomon            *direction = vec03IsVertical ? SkPath::kCCW_Direction : SkPath::kCW_Direction;
3352057ae8a15ddd2af639a829d63aca29cbc6b1bb57bsalomon            *start = 3;
3353057ae8a15ddd2af639a829d63aca29cbc6b1bb57bsalomon            break;
3354057ae8a15ddd2af639a829d63aca29cbc6b1bb57bsalomon        case 0b11:
3355057ae8a15ddd2af639a829d63aca29cbc6b1bb57bsalomon            rect->set(rectPts[2].fX, rectPts[2].fY, rectPts[0].fX, rectPts[0].fY);
3356057ae8a15ddd2af639a829d63aca29cbc6b1bb57bsalomon            *direction = vec03IsVertical ? SkPath::kCW_Direction : SkPath::kCCW_Direction;
3357057ae8a15ddd2af639a829d63aca29cbc6b1bb57bsalomon            *start = 2;
3358057ae8a15ddd2af639a829d63aca29cbc6b1bb57bsalomon            break;
3359edc743a57657107b873ed2fc2efeeff1b1efcd23bsalomon    }
3360edc743a57657107b873ed2fc2efeeff1b1efcd23bsalomon    return true;
3361edc743a57657107b873ed2fc2efeeff1b1efcd23bsalomon}
336221af9ca1b1f54d9ba1de055aa8475928d5c8ecdfbsalomon
336321af9ca1b1f54d9ba1de055aa8475928d5c8ecdfbsalomonvoid SkPathPriv::CreateDrawArcPath(SkPath* path, const SkRect& oval, SkScalar startAngle,
336421af9ca1b1f54d9ba1de055aa8475928d5c8ecdfbsalomon                                   SkScalar sweepAngle, bool useCenter, bool isFillNoPathEffect) {
336521af9ca1b1f54d9ba1de055aa8475928d5c8ecdfbsalomon    SkASSERT(!oval.isEmpty());
336621af9ca1b1f54d9ba1de055aa8475928d5c8ecdfbsalomon    SkASSERT(sweepAngle);
336721af9ca1b1f54d9ba1de055aa8475928d5c8ecdfbsalomon
336821af9ca1b1f54d9ba1de055aa8475928d5c8ecdfbsalomon    path->reset();
336921af9ca1b1f54d9ba1de055aa8475928d5c8ecdfbsalomon    path->setIsVolatile(true);
337021af9ca1b1f54d9ba1de055aa8475928d5c8ecdfbsalomon    path->setFillType(SkPath::kWinding_FillType);
337121af9ca1b1f54d9ba1de055aa8475928d5c8ecdfbsalomon    if (isFillNoPathEffect && SkScalarAbs(sweepAngle) >= 360.f) {
337221af9ca1b1f54d9ba1de055aa8475928d5c8ecdfbsalomon        path->addOval(oval);
337321af9ca1b1f54d9ba1de055aa8475928d5c8ecdfbsalomon        return;
337421af9ca1b1f54d9ba1de055aa8475928d5c8ecdfbsalomon    }
337521af9ca1b1f54d9ba1de055aa8475928d5c8ecdfbsalomon    if (useCenter) {
337621af9ca1b1f54d9ba1de055aa8475928d5c8ecdfbsalomon        path->moveTo(oval.centerX(), oval.centerY());
337721af9ca1b1f54d9ba1de055aa8475928d5c8ecdfbsalomon    }
337821af9ca1b1f54d9ba1de055aa8475928d5c8ecdfbsalomon    // Arc to mods at 360 and drawArc is not supposed to.
337921af9ca1b1f54d9ba1de055aa8475928d5c8ecdfbsalomon    bool forceMoveTo = !useCenter;
338021af9ca1b1f54d9ba1de055aa8475928d5c8ecdfbsalomon    while (sweepAngle <= -360.f) {
338121af9ca1b1f54d9ba1de055aa8475928d5c8ecdfbsalomon        path->arcTo(oval, startAngle, -180.f, forceMoveTo);
338221af9ca1b1f54d9ba1de055aa8475928d5c8ecdfbsalomon        startAngle -= 180.f;
338321af9ca1b1f54d9ba1de055aa8475928d5c8ecdfbsalomon        path->arcTo(oval, startAngle, -180.f, false);
338421af9ca1b1f54d9ba1de055aa8475928d5c8ecdfbsalomon        startAngle -= 180.f;
338521af9ca1b1f54d9ba1de055aa8475928d5c8ecdfbsalomon        forceMoveTo = false;
338621af9ca1b1f54d9ba1de055aa8475928d5c8ecdfbsalomon        sweepAngle += 360.f;
338721af9ca1b1f54d9ba1de055aa8475928d5c8ecdfbsalomon    }
338821af9ca1b1f54d9ba1de055aa8475928d5c8ecdfbsalomon    while (sweepAngle >= 360.f) {
338921af9ca1b1f54d9ba1de055aa8475928d5c8ecdfbsalomon        path->arcTo(oval, startAngle, 180.f, forceMoveTo);
339021af9ca1b1f54d9ba1de055aa8475928d5c8ecdfbsalomon        startAngle += 180.f;
339121af9ca1b1f54d9ba1de055aa8475928d5c8ecdfbsalomon        path->arcTo(oval, startAngle, 180.f, false);
339221af9ca1b1f54d9ba1de055aa8475928d5c8ecdfbsalomon        startAngle += 180.f;
339321af9ca1b1f54d9ba1de055aa8475928d5c8ecdfbsalomon        forceMoveTo = false;
339421af9ca1b1f54d9ba1de055aa8475928d5c8ecdfbsalomon        sweepAngle -= 360.f;
339521af9ca1b1f54d9ba1de055aa8475928d5c8ecdfbsalomon    }
339621af9ca1b1f54d9ba1de055aa8475928d5c8ecdfbsalomon    path->arcTo(oval, startAngle, sweepAngle, forceMoveTo);
339721af9ca1b1f54d9ba1de055aa8475928d5c8ecdfbsalomon    if (useCenter) {
339821af9ca1b1f54d9ba1de055aa8475928d5c8ecdfbsalomon        path->close();
339921af9ca1b1f54d9ba1de055aa8475928d5c8ecdfbsalomon    }
340021af9ca1b1f54d9ba1de055aa8475928d5c8ecdfbsalomon}
34010d7dac8fb8c404cada8d46646a980772b9dc55d6Mike Reed
34020d7dac8fb8c404cada8d46646a980772b9dc55d6Mike Reed///////////////////////////////////////////////////////////////////////////////////////////////////
34030d7dac8fb8c404cada8d46646a980772b9dc55d6Mike Reed#include "SkNx.h"
34040d7dac8fb8c404cada8d46646a980772b9dc55d6Mike Reed
34050d7dac8fb8c404cada8d46646a980772b9dc55d6Mike Reedstatic int compute_quad_extremas(const SkPoint src[3], SkPoint extremas[3]) {
34060d7dac8fb8c404cada8d46646a980772b9dc55d6Mike Reed    SkScalar ts[2];
34070d7dac8fb8c404cada8d46646a980772b9dc55d6Mike Reed    int n  = SkFindQuadExtrema(src[0].fX, src[1].fX, src[2].fX, ts);
34080d7dac8fb8c404cada8d46646a980772b9dc55d6Mike Reed        n += SkFindQuadExtrema(src[0].fY, src[1].fY, src[2].fY, &ts[n]);
34090d7dac8fb8c404cada8d46646a980772b9dc55d6Mike Reed    SkASSERT(n >= 0 && n <= 2);
34100d7dac8fb8c404cada8d46646a980772b9dc55d6Mike Reed    for (int i = 0; i < n; ++i) {
34110d7dac8fb8c404cada8d46646a980772b9dc55d6Mike Reed        extremas[i] = SkEvalQuadAt(src, ts[i]);
34120d7dac8fb8c404cada8d46646a980772b9dc55d6Mike Reed    }
34130d7dac8fb8c404cada8d46646a980772b9dc55d6Mike Reed    extremas[n] = src[2];
34140d7dac8fb8c404cada8d46646a980772b9dc55d6Mike Reed    return n + 1;
34150d7dac8fb8c404cada8d46646a980772b9dc55d6Mike Reed}
34160d7dac8fb8c404cada8d46646a980772b9dc55d6Mike Reed
34170d7dac8fb8c404cada8d46646a980772b9dc55d6Mike Reedstatic int compute_conic_extremas(const SkPoint src[3], SkScalar w, SkPoint extremas[3]) {
34180d7dac8fb8c404cada8d46646a980772b9dc55d6Mike Reed    SkConic conic(src[0], src[1], src[2], w);
34190d7dac8fb8c404cada8d46646a980772b9dc55d6Mike Reed    SkScalar ts[2];
34200d7dac8fb8c404cada8d46646a980772b9dc55d6Mike Reed    int n  = conic.findXExtrema(ts);
34210d7dac8fb8c404cada8d46646a980772b9dc55d6Mike Reed        n += conic.findYExtrema(&ts[n]);
34220d7dac8fb8c404cada8d46646a980772b9dc55d6Mike Reed    SkASSERT(n >= 0 && n <= 2);
34230d7dac8fb8c404cada8d46646a980772b9dc55d6Mike Reed    for (int i = 0; i < n; ++i) {
34240d7dac8fb8c404cada8d46646a980772b9dc55d6Mike Reed        extremas[i] = conic.evalAt(ts[i]);
34250d7dac8fb8c404cada8d46646a980772b9dc55d6Mike Reed    }
34260d7dac8fb8c404cada8d46646a980772b9dc55d6Mike Reed    extremas[n] = src[2];
34270d7dac8fb8c404cada8d46646a980772b9dc55d6Mike Reed    return n + 1;
34280d7dac8fb8c404cada8d46646a980772b9dc55d6Mike Reed}
34290d7dac8fb8c404cada8d46646a980772b9dc55d6Mike Reed
34300d7dac8fb8c404cada8d46646a980772b9dc55d6Mike Reedstatic int compute_cubic_extremas(const SkPoint src[3], SkPoint extremas[5]) {
34310d7dac8fb8c404cada8d46646a980772b9dc55d6Mike Reed    SkScalar ts[4];
34320d7dac8fb8c404cada8d46646a980772b9dc55d6Mike Reed    int n  = SkFindCubicExtrema(src[0].fX, src[1].fX, src[2].fX, src[3].fX, ts);
34330d7dac8fb8c404cada8d46646a980772b9dc55d6Mike Reed        n += SkFindCubicExtrema(src[0].fY, src[1].fY, src[2].fY, src[3].fY, &ts[n]);
34340d7dac8fb8c404cada8d46646a980772b9dc55d6Mike Reed    SkASSERT(n >= 0 && n <= 4);
34350d7dac8fb8c404cada8d46646a980772b9dc55d6Mike Reed    for (int i = 0; i < n; ++i) {
34360d7dac8fb8c404cada8d46646a980772b9dc55d6Mike Reed        SkEvalCubicAt(src, ts[i], &extremas[i], nullptr, nullptr);
34370d7dac8fb8c404cada8d46646a980772b9dc55d6Mike Reed    }
34380d7dac8fb8c404cada8d46646a980772b9dc55d6Mike Reed    extremas[n] = src[3];
34390d7dac8fb8c404cada8d46646a980772b9dc55d6Mike Reed    return n + 1;
34400d7dac8fb8c404cada8d46646a980772b9dc55d6Mike Reed}
34410d7dac8fb8c404cada8d46646a980772b9dc55d6Mike Reed
34428d3196bdfcf478982bec9885d21e1d664ab9a72bMike ReedSkRect SkPath::computeTightBounds() const {
34438d3196bdfcf478982bec9885d21e1d664ab9a72bMike Reed    if (0 == this->countVerbs()) {
34448d3196bdfcf478982bec9885d21e1d664ab9a72bMike Reed        return SkRect::MakeEmpty();
34450d7dac8fb8c404cada8d46646a980772b9dc55d6Mike Reed    }
34460d7dac8fb8c404cada8d46646a980772b9dc55d6Mike Reed
34478d3196bdfcf478982bec9885d21e1d664ab9a72bMike Reed    if (this->getSegmentMasks() == SkPath::kLine_SegmentMask) {
34488d3196bdfcf478982bec9885d21e1d664ab9a72bMike Reed        return this->getBounds();
34490d7dac8fb8c404cada8d46646a980772b9dc55d6Mike Reed    }
34500d7dac8fb8c404cada8d46646a980772b9dc55d6Mike Reed
34510d7dac8fb8c404cada8d46646a980772b9dc55d6Mike Reed    SkPoint extremas[5]; // big enough to hold worst-case curve type (cubic) extremas + 1
34520d7dac8fb8c404cada8d46646a980772b9dc55d6Mike Reed    SkPoint pts[4];
34538d3196bdfcf478982bec9885d21e1d664ab9a72bMike Reed    SkPath::RawIter iter(*this);
34540d7dac8fb8c404cada8d46646a980772b9dc55d6Mike Reed
34550d7dac8fb8c404cada8d46646a980772b9dc55d6Mike Reed    // initial with the first MoveTo, so we don't have to check inside the switch
34560d7dac8fb8c404cada8d46646a980772b9dc55d6Mike Reed    Sk2s min, max;
34578d3196bdfcf478982bec9885d21e1d664ab9a72bMike Reed    min = max = from_point(this->getPoint(0));
34580d7dac8fb8c404cada8d46646a980772b9dc55d6Mike Reed    for (;;) {
34590d7dac8fb8c404cada8d46646a980772b9dc55d6Mike Reed        int count = 0;
34600d7dac8fb8c404cada8d46646a980772b9dc55d6Mike Reed        switch (iter.next(pts)) {
34610d7dac8fb8c404cada8d46646a980772b9dc55d6Mike Reed            case SkPath::kMove_Verb:
34620d7dac8fb8c404cada8d46646a980772b9dc55d6Mike Reed                extremas[0] = pts[0];
34630d7dac8fb8c404cada8d46646a980772b9dc55d6Mike Reed                count = 1;
34640d7dac8fb8c404cada8d46646a980772b9dc55d6Mike Reed                break;
34650d7dac8fb8c404cada8d46646a980772b9dc55d6Mike Reed            case SkPath::kLine_Verb:
34660d7dac8fb8c404cada8d46646a980772b9dc55d6Mike Reed                extremas[0] = pts[1];
34670d7dac8fb8c404cada8d46646a980772b9dc55d6Mike Reed                count = 1;
34680d7dac8fb8c404cada8d46646a980772b9dc55d6Mike Reed                break;
34690d7dac8fb8c404cada8d46646a980772b9dc55d6Mike Reed            case SkPath::kQuad_Verb:
34700d7dac8fb8c404cada8d46646a980772b9dc55d6Mike Reed                count = compute_quad_extremas(pts, extremas);
34710d7dac8fb8c404cada8d46646a980772b9dc55d6Mike Reed                break;
34720d7dac8fb8c404cada8d46646a980772b9dc55d6Mike Reed            case SkPath::kConic_Verb:
34730d7dac8fb8c404cada8d46646a980772b9dc55d6Mike Reed                count = compute_conic_extremas(pts, iter.conicWeight(), extremas);
34740d7dac8fb8c404cada8d46646a980772b9dc55d6Mike Reed                break;
34750d7dac8fb8c404cada8d46646a980772b9dc55d6Mike Reed            case SkPath::kCubic_Verb:
34760d7dac8fb8c404cada8d46646a980772b9dc55d6Mike Reed                count = compute_cubic_extremas(pts, extremas);
34770d7dac8fb8c404cada8d46646a980772b9dc55d6Mike Reed                break;
34780d7dac8fb8c404cada8d46646a980772b9dc55d6Mike Reed            case SkPath::kClose_Verb:
34790d7dac8fb8c404cada8d46646a980772b9dc55d6Mike Reed                break;
34800d7dac8fb8c404cada8d46646a980772b9dc55d6Mike Reed            case SkPath::kDone_Verb:
34810d7dac8fb8c404cada8d46646a980772b9dc55d6Mike Reed                goto DONE;
34820d7dac8fb8c404cada8d46646a980772b9dc55d6Mike Reed        }
34830d7dac8fb8c404cada8d46646a980772b9dc55d6Mike Reed        for (int i = 0; i < count; ++i) {
34840d7dac8fb8c404cada8d46646a980772b9dc55d6Mike Reed            Sk2s tmp = from_point(extremas[i]);
34850d7dac8fb8c404cada8d46646a980772b9dc55d6Mike Reed            min = Sk2s::Min(min, tmp);
34860d7dac8fb8c404cada8d46646a980772b9dc55d6Mike Reed            max = Sk2s::Max(max, tmp);
34870d7dac8fb8c404cada8d46646a980772b9dc55d6Mike Reed        }
34880d7dac8fb8c404cada8d46646a980772b9dc55d6Mike Reed    }
34890d7dac8fb8c404cada8d46646a980772b9dc55d6Mike ReedDONE:
34908d3196bdfcf478982bec9885d21e1d664ab9a72bMike Reed    SkRect bounds;
34918d3196bdfcf478982bec9885d21e1d664ab9a72bMike Reed    min.store((SkPoint*)&bounds.fLeft);
34928d3196bdfcf478982bec9885d21e1d664ab9a72bMike Reed    max.store((SkPoint*)&bounds.fRight);
34938d3196bdfcf478982bec9885d21e1d664ab9a72bMike Reed    return bounds;
34940d7dac8fb8c404cada8d46646a980772b9dc55d6Mike Reed}
3495