1 | //======================================================================== |
2 | // |
3 | // SplashPath.cc |
4 | // |
5 | //======================================================================== |
6 | |
7 | //======================================================================== |
8 | // |
9 | // Modified under the Poppler project - http://poppler.freedesktop.org |
10 | // |
11 | // All changes made under the Poppler project to this file are licensed |
12 | // under GPL version 2 or later |
13 | // |
14 | // Copyright (C) 2018 Stefan BrĂ¼ns <stefan.bruens@rwth-aachen.de> |
15 | // Copyright (C) 2018-2021 Albert Astals Cid <aacid@kde.org> |
16 | // Copyright (C) 2018 Adam Reichold <adam.reichold@t-online.de> |
17 | // |
18 | // To see a description of the changes please see the Changelog file that |
19 | // came with your tarball or type make ChangeLog if you are building from git |
20 | // |
21 | //======================================================================== |
22 | |
23 | #include <config.h> |
24 | |
25 | #include <cstring> |
26 | #include "goo/gmem.h" |
27 | #include "goo/GooLikely.h" |
28 | #include "SplashErrorCodes.h" |
29 | #include "SplashPath.h" |
30 | |
31 | //------------------------------------------------------------------------ |
32 | // SplashPath |
33 | //------------------------------------------------------------------------ |
34 | |
35 | // A path can be in three possible states: |
36 | // |
37 | // 1. no current point -- zero or more finished subpaths |
38 | // [curSubpath == length] |
39 | // |
40 | // 2. one point in subpath |
41 | // [curSubpath == length - 1] |
42 | // |
43 | // 3. open subpath with two or more points |
44 | // [curSubpath < length - 1] |
45 | |
46 | SplashPath::SplashPath() |
47 | { |
48 | pts = nullptr; |
49 | flags = nullptr; |
50 | length = size = 0; |
51 | curSubpath = 0; |
52 | hints = nullptr; |
53 | hintsLength = hintsSize = 0; |
54 | } |
55 | |
56 | SplashPath::SplashPath(SplashPath &&path) noexcept |
57 | { |
58 | length = path.length; |
59 | size = path.size; |
60 | pts = path.pts; |
61 | flags = path.flags; |
62 | curSubpath = path.curSubpath; |
63 | |
64 | hints = path.hints; |
65 | hintsLength = hintsSize = path.hintsLength; |
66 | |
67 | path.pts = nullptr; |
68 | path.flags = nullptr; |
69 | path.length = path.size = 0; |
70 | path.hints = nullptr; |
71 | path.hintsLength = path.hintsSize = 0; |
72 | } |
73 | |
74 | SplashPath::~SplashPath() |
75 | { |
76 | gfree(p: pts); |
77 | gfree(p: flags); |
78 | gfree(p: hints); |
79 | } |
80 | |
81 | void SplashPath::reserve(int nPts) |
82 | { |
83 | grow(nPts: nPts - size); |
84 | } |
85 | |
86 | // Add space for <nPts> more points. |
87 | void SplashPath::grow(int nPts) |
88 | { |
89 | if (length + nPts > size) { |
90 | if (size == 0) { |
91 | size = 32; |
92 | } |
93 | while (size < length + nPts) { |
94 | size *= 2; |
95 | } |
96 | pts = (SplashPathPoint *)greallocn_checkoverflow(p: pts, count: size, size: sizeof(SplashPathPoint)); |
97 | flags = (unsigned char *)greallocn_checkoverflow(p: flags, count: size, size: sizeof(unsigned char)); |
98 | if (unlikely(!pts || !flags)) { |
99 | length = size = curSubpath = 0; |
100 | } |
101 | } |
102 | } |
103 | |
104 | void SplashPath::append(SplashPath *path) |
105 | { |
106 | int i; |
107 | |
108 | grow(nPts: path->length); |
109 | if (unlikely(size == 0)) { |
110 | return; |
111 | } |
112 | |
113 | curSubpath = length + path->curSubpath; |
114 | for (i = 0; i < path->length; ++i) { |
115 | pts[length] = path->pts[i]; |
116 | flags[length] = path->flags[i]; |
117 | ++length; |
118 | } |
119 | } |
120 | |
121 | SplashError SplashPath::moveTo(SplashCoord x, SplashCoord y) |
122 | { |
123 | if (onePointSubpath()) { |
124 | return splashErrBogusPath; |
125 | } |
126 | grow(nPts: 1); |
127 | if (unlikely(size == 0)) { |
128 | return splashErrBogusPath; |
129 | } |
130 | pts[length].x = x; |
131 | pts[length].y = y; |
132 | flags[length] = splashPathFirst | splashPathLast; |
133 | curSubpath = length++; |
134 | return splashOk; |
135 | } |
136 | |
137 | SplashError SplashPath::lineTo(SplashCoord x, SplashCoord y) |
138 | { |
139 | if (noCurrentPoint()) { |
140 | return splashErrNoCurPt; |
141 | } |
142 | flags[length - 1] &= ~splashPathLast; |
143 | grow(nPts: 1); |
144 | if (unlikely(size == 0)) { |
145 | return splashErrBogusPath; |
146 | } |
147 | pts[length].x = x; |
148 | pts[length].y = y; |
149 | flags[length] = splashPathLast; |
150 | ++length; |
151 | return splashOk; |
152 | } |
153 | |
154 | SplashError SplashPath::curveTo(SplashCoord x1, SplashCoord y1, SplashCoord x2, SplashCoord y2, SplashCoord x3, SplashCoord y3) |
155 | { |
156 | if (noCurrentPoint()) { |
157 | return splashErrNoCurPt; |
158 | } |
159 | flags[length - 1] &= ~splashPathLast; |
160 | grow(nPts: 3); |
161 | if (unlikely(size == 0)) { |
162 | return splashErrBogusPath; |
163 | } |
164 | pts[length].x = x1; |
165 | pts[length].y = y1; |
166 | flags[length] = splashPathCurve; |
167 | ++length; |
168 | pts[length].x = x2; |
169 | pts[length].y = y2; |
170 | flags[length] = splashPathCurve; |
171 | ++length; |
172 | pts[length].x = x3; |
173 | pts[length].y = y3; |
174 | flags[length] = splashPathLast; |
175 | ++length; |
176 | return splashOk; |
177 | } |
178 | |
179 | SplashError SplashPath::close(bool force) |
180 | { |
181 | if (noCurrentPoint()) { |
182 | return splashErrNoCurPt; |
183 | } |
184 | if (force || curSubpath == length - 1 || pts[length - 1].x != pts[curSubpath].x || pts[length - 1].y != pts[curSubpath].y) { |
185 | const auto lineToStatus = lineTo(x: pts[curSubpath].x, y: pts[curSubpath].y); |
186 | if (lineToStatus != splashOk) { |
187 | return lineToStatus; |
188 | } |
189 | } |
190 | flags[curSubpath] |= splashPathClosed; |
191 | flags[length - 1] |= splashPathClosed; |
192 | curSubpath = length; |
193 | return splashOk; |
194 | } |
195 | |
196 | void SplashPath::addStrokeAdjustHint(int ctrl0, int ctrl1, int firstPt, int lastPt) |
197 | { |
198 | if (hintsLength == hintsSize) { |
199 | hintsSize = hintsLength ? 2 * hintsLength : 8; |
200 | hints = (SplashPathHint *)greallocn_checkoverflow(p: hints, count: hintsSize, size: sizeof(SplashPathHint)); |
201 | } |
202 | if (unlikely(!hints)) { |
203 | return; |
204 | } |
205 | hints[hintsLength].ctrl0 = ctrl0; |
206 | hints[hintsLength].ctrl1 = ctrl1; |
207 | hints[hintsLength].firstPt = firstPt; |
208 | hints[hintsLength].lastPt = lastPt; |
209 | ++hintsLength; |
210 | } |
211 | |
212 | void SplashPath::offset(SplashCoord dx, SplashCoord dy) |
213 | { |
214 | int i; |
215 | |
216 | for (i = 0; i < length; ++i) { |
217 | pts[i].x += dx; |
218 | pts[i].y += dy; |
219 | } |
220 | } |
221 | |
222 | bool SplashPath::getCurPt(SplashCoord *x, SplashCoord *y) |
223 | { |
224 | if (noCurrentPoint()) { |
225 | return false; |
226 | } |
227 | *x = pts[length - 1].x; |
228 | *y = pts[length - 1].y; |
229 | return true; |
230 | } |
231 | |