1/*
2 * Copyright 2012 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7#include "SkPathOpsPoint.h"
8#include "SkPathWriter.h"
9
10// wrap path to keep track of whether the contour is initialized and non-empty
11SkPathWriter::SkPathWriter(SkPath& path)
12    : fPathPtr(&path)
13    , fCloses(0)
14    , fMoves(0)
15{
16    init();
17}
18
19void SkPathWriter::close() {
20    if (!fHasMove) {
21        return;
22    }
23    bool callClose = isClosed();
24    lineTo();
25    if (fEmpty) {
26        return;
27    }
28    if (callClose) {
29#if DEBUG_PATH_CONSTRUCTION
30        SkDebugf("path.close();\n");
31#endif
32        fPathPtr->close();
33        fCloses++;
34    }
35    init();
36}
37
38void SkPathWriter::cubicTo(const SkPoint& pt1, const SkPoint& pt2, const SkPoint& pt3) {
39    lineTo();
40    if (fEmpty && AlmostEqualUlps(fDefer[0], pt1) && AlmostEqualUlps(pt1, pt2)
41            && AlmostEqualUlps(pt2, pt3)) {
42        deferredLine(pt3);
43        return;
44    }
45    moveTo();
46    fDefer[1] = pt3;
47    nudge();
48    fDefer[0] = fDefer[1];
49#if DEBUG_PATH_CONSTRUCTION
50    SkDebugf("path.cubicTo(%1.9g,%1.9g, %1.9g,%1.9g, %1.9g,%1.9g);\n",
51            pt1.fX, pt1.fY, pt2.fX, pt2.fY, fDefer[1].fX, fDefer[1].fY);
52#endif
53    fPathPtr->cubicTo(pt1.fX, pt1.fY, pt2.fX, pt2.fY, fDefer[1].fX, fDefer[1].fY);
54    fEmpty = false;
55}
56
57void SkPathWriter::deferredLine(const SkPoint& pt) {
58    if (pt == fDefer[1]) {
59        return;
60    }
61    if (changedSlopes(pt)) {
62        lineTo();
63        fDefer[0] = fDefer[1];
64    }
65    fDefer[1] = pt;
66}
67
68void SkPathWriter::deferredMove(const SkPoint& pt) {
69    fMoved = true;
70    fHasMove = true;
71    fEmpty = true;
72    fDefer[0] = fDefer[1] = pt;
73}
74
75void SkPathWriter::deferredMoveLine(const SkPoint& pt) {
76    if (!fHasMove) {
77        deferredMove(pt);
78    }
79    deferredLine(pt);
80}
81
82bool SkPathWriter::hasMove() const {
83    return fHasMove;
84}
85
86void SkPathWriter::init() {
87    fEmpty = true;
88    fHasMove = false;
89    fMoved = false;
90}
91
92bool SkPathWriter::isClosed() const {
93    return !fEmpty && SkDPoint::ApproximatelyEqual(fFirstPt, fDefer[1]);
94}
95
96void SkPathWriter::lineTo() {
97    if (fDefer[0] == fDefer[1]) {
98        return;
99    }
100    moveTo();
101    nudge();
102    fEmpty = false;
103#if DEBUG_PATH_CONSTRUCTION
104    SkDebugf("path.lineTo(%1.9g,%1.9g);\n", fDefer[1].fX, fDefer[1].fY);
105#endif
106    fPathPtr->lineTo(fDefer[1].fX, fDefer[1].fY);
107    fDefer[0] = fDefer[1];
108}
109
110const SkPath* SkPathWriter::nativePath() const {
111    return fPathPtr;
112}
113
114void SkPathWriter::nudge() {
115    if (fEmpty || !AlmostEqualUlps(fDefer[1].fX, fFirstPt.fX)
116            || !AlmostEqualUlps(fDefer[1].fY, fFirstPt.fY)) {
117        return;
118    }
119    fDefer[1] = fFirstPt;
120}
121
122void SkPathWriter::quadTo(const SkPoint& pt1, const SkPoint& pt2) {
123    lineTo();
124    if (fEmpty && AlmostEqualUlps(fDefer[0], pt1) && AlmostEqualUlps(pt1, pt2)) {
125        deferredLine(pt2);
126        return;
127    }
128    moveTo();
129    fDefer[1] = pt2;
130    nudge();
131    fDefer[0] = fDefer[1];
132#if DEBUG_PATH_CONSTRUCTION
133    SkDebugf("path.quadTo(%1.9g,%1.9g, %1.9g,%1.9g);\n",
134            pt1.fX, pt1.fY, fDefer[1].fX, fDefer[1].fY);
135#endif
136    fPathPtr->quadTo(pt1.fX, pt1.fY, fDefer[1].fX, fDefer[1].fY);
137    fEmpty = false;
138}
139
140bool SkPathWriter::someAssemblyRequired() const {
141    return fCloses < fMoves;
142}
143
144bool SkPathWriter::changedSlopes(const SkPoint& pt) const {
145    if (fDefer[0] == fDefer[1]) {
146        return false;
147    }
148    SkScalar deferDx = fDefer[1].fX - fDefer[0].fX;
149    SkScalar deferDy = fDefer[1].fY - fDefer[0].fY;
150    SkScalar lineDx = pt.fX - fDefer[1].fX;
151    SkScalar lineDy = pt.fY - fDefer[1].fY;
152    return deferDx * lineDy != deferDy * lineDx;
153}
154
155void SkPathWriter::moveTo() {
156    if (!fMoved) {
157        return;
158    }
159    fFirstPt = fDefer[0];
160#if DEBUG_PATH_CONSTRUCTION
161    SkDebugf("path.moveTo(%1.9g,%1.9g);\n", fDefer[0].fX, fDefer[0].fY);
162#endif
163    fPathPtr->moveTo(fDefer[0].fX, fDefer[0].fY);
164    fMoved = false;
165    fMoves++;
166}
167