1// [Blend2D]
2// 2D Vector Graphics Powered by a JIT Compiler.
3//
4// [License]
5// Zlib - See LICENSE.md file in the package.
6
7#ifndef BLEND2D_BLPATH_P_H
8#define BLEND2D_BLPATH_P_H
9
10#include "./blapi-internal_p.h"
11#include "./blmath_p.h"
12#include "./blpath.h"
13
14//! \cond INTERNAL
15//! \addtogroup blend2d_internal
16//! \{
17
18// ============================================================================
19// [BLApproximationOptions]
20// ============================================================================
21
22static BL_INLINE constexpr BLApproximationOptions blMakeDefaultApproximationOptions() noexcept {
23 return BLApproximationOptions {
24 BL_FLATTEN_MODE_DEFAULT, // Default flattening mode.
25 BL_OFFSET_MODE_DEFAULT, // Default offset mode.
26 { 0, 0, 0, 0, 0, 0 }, // Reserved.
27 0.20, // Default curve flattening tolerance.
28 0.05, // Default curve simplification tolerance.
29 0.414213562 // Default offset parameter.
30 };
31}
32
33// ============================================================================
34// [BLInternalPathImpl]
35// ============================================================================
36
37//! Internal implementation that extends `BLPathImpl`.
38struct BLInternalPathImpl : public BLPathImpl {
39 BLBox controlBox;
40 BLBox boundingBox;
41};
42
43template<>
44struct BLInternalCastImpl<BLPathImpl> { typedef BLInternalPathImpl Type; };
45
46BL_HIDDEN BLResult blPathImplDelete(BLPathImpl* impl_) noexcept;
47BL_HIDDEN BLResult blPathAddTransformedPathWithType(BLPathCore* self, const BLPathCore* other, const BLRange* range, const BLMatrix2D* m, uint32_t mType) noexcept;
48BL_HIDDEN BLResult blPathTransformWithType(BLPathCore* self, const BLRange* range, const BLMatrix2D* m, uint32_t mType) noexcept;
49
50// ============================================================================
51// [BLPathIterator]
52// ============================================================================
53
54struct BLPathIterator {
55 const uint8_t* cmd;
56 const uint8_t* end;
57 const BLPoint* vtx;
58
59 BL_INLINE BLPathIterator() noexcept = default;
60 BL_INLINE BLPathIterator(const BLPathView& view) noexcept { reset(view); }
61 BL_INLINE BLPathIterator(const uint8_t* cmd_, const BLPoint* vtx_, size_t n) noexcept { reset(cmd_, vtx_, n); }
62
63 BL_INLINE BLPathIterator operator++(int) noexcept { BLPathIterator out(*this); cmd++; vtx++; return out; }
64 BL_INLINE BLPathIterator operator--(int) noexcept { BLPathIterator out(*this); cmd--; vtx--; return out; }
65
66 BL_INLINE BLPathIterator& operator++() noexcept { cmd++; vtx++; return *this; }
67 BL_INLINE BLPathIterator& operator--() noexcept { cmd--; vtx--; return *this; }
68
69 BL_INLINE BLPathIterator& operator+=(size_t n) noexcept { cmd += n; vtx += n; return *this; }
70 BL_INLINE BLPathIterator& operator-=(size_t n) noexcept { cmd -= n; vtx -= n; return *this; }
71
72 BL_INLINE bool atEnd() const noexcept { return cmd == end; }
73 BL_INLINE bool afterEnd() const noexcept { return cmd > end; }
74 BL_INLINE bool beforeEnd() const noexcept { return cmd < end; }
75
76 BL_INLINE size_t remainingForward() const noexcept { return (size_t)(end - cmd); }
77 BL_INLINE size_t remainingBackward() const noexcept { return (size_t)(cmd - end); }
78
79 BL_INLINE void reset(const BLPathView& view) noexcept {
80 reset(view.commandData, view.vertexData, view.size);
81 }
82
83 BL_INLINE void reset(const uint8_t* cmd_, const BLPoint* vtx_, size_t n) noexcept {
84 cmd = cmd_;
85 end = cmd_ + n;
86 vtx = vtx_;
87 }
88
89 BL_INLINE void reverse() noexcept {
90 intptr_t n = intptr_t(remainingForward()) - 1;
91
92 end = cmd - 1;
93 cmd += n;
94 vtx += n;
95 }
96};
97
98// ============================================================================
99// [BLPathAppender]
100// ============================================================================
101
102//! Low-level interface that can be used to append vertices & commands to an
103//! existing path fast. The interface is designed in a way that the user using
104//! it must reserve enough space and then call `append...()` functions that
105//! can only be called when there is enough storage left for that command. The
106//! storage requirements are specified by `begin()` or by `ensure()`. The latter
107//! is mostly used to reallocate the array in case more vertices are needed than
108//! initially passed to `begin()`.
109//!
110//! When designing a loop the appender can be used in the following way, where
111//! `SourceT` is an iterable object that can provide us some path vertices and
112//! commands:
113//!
114//! ```
115//! template<typename Iterator>
116//! BLResult appendToPath(BLPath& dst, const Iterator& iter) noexcept {
117//! BLPathAppender appender;
118//! BL_PROPAGATE(appender.beginAssign(dst, 32));
119//!
120//! while (!iter.end()) {
121//! // Number of vertices required by a cubic curve is 3.
122//! BL_PROPAGATE(appender.ensure(dst, 3));
123//!
124//! switch (iter.command()) {
125//! case BL_PATH_CMD_MOVE : appender.moveTo(iter[0]); break;
126//! case BL_PATH_CMD_ON : appender.lineTo(iter[0]); break;
127//! case BL_PATH_CMD_QUAD : appender.quadTo(iter[0], iter[1]); break;
128//! case BL_PATH_CMD_CUBIC: appender.cubicTo(iter[0], iter[1], iter[2]); break;
129//! case BL_PATH_CMD_CLOSE: appender.close(); break;
130//! }
131//!
132//! iter.advance();
133//! }
134//!
135//! appender.done(dst);
136//! return BL_SUCCESS;
137//! }
138//! ```
139class BLPathAppender {
140public:
141 uint8_t* cmd;
142 uint8_t* end;
143 BLPoint* vtx;
144
145 BL_INLINE BLPathAppender() noexcept
146 : cmd(nullptr) {}
147
148 BL_INLINE void reset() noexcept { cmd = nullptr; }
149 BL_INLINE bool empty() const noexcept { return cmd == nullptr; }
150 BL_INLINE size_t remainingSize() const noexcept { return (size_t)(end - cmd); }
151
152 BL_INLINE size_t currentIndex(const BLPath& dst) const noexcept {
153 return (size_t)(cmd - dst.impl->commandData);
154 }
155
156 BL_INLINE BLResult begin(BLPathCore* dst, uint32_t op, size_t n) noexcept {
157 BLPoint* vtxPtrLocal;
158 uint8_t* cmdPtrLocal;
159 BL_PROPAGATE(blPathModifyOp(dst, op, n, &cmdPtrLocal, &vtxPtrLocal));
160
161 BLPathImpl* dstI = dst->impl;
162 vtx = vtxPtrLocal;
163 cmd = cmdPtrLocal;
164 end = dstI->commandData + dstI->capacity;
165
166 BL_ASSERT(remainingSize() >= n);
167 return BL_SUCCESS;
168 }
169
170 BL_INLINE BLResult beginAssign(BLPathCore* dst, size_t n) noexcept { return begin(dst, BL_MODIFY_OP_ASSIGN_GROW, n); }
171 BL_INLINE BLResult beginAppend(BLPathCore* dst, size_t n) noexcept { return begin(dst, BL_MODIFY_OP_APPEND_GROW, n); }
172
173 BL_INLINE BLResult ensure(BLPathCore* dst, size_t n) noexcept {
174 if (BL_LIKELY(remainingSize() >= n))
175 return BL_SUCCESS;
176
177 BLInternalPathImpl* dstI = blInternalCast(dst->impl);
178 dstI->size = (size_t)(cmd - dstI->commandData);
179 BL_ASSERT(dstI->size <= dstI->capacity);
180
181 uint8_t* cmdPtrLocal;
182 BLPoint* vtxPtrLocal;
183 BL_PROPAGATE(blPathModifyOp(dst, BL_MODIFY_OP_APPEND_GROW, n, &cmdPtrLocal, &vtxPtrLocal));
184
185 dstI = blInternalCast(dst->impl);
186 vtx = vtxPtrLocal;
187 cmd = cmdPtrLocal;
188 end = dstI->commandData + dstI->capacity;
189
190 BL_ASSERT(remainingSize() >= n);
191 return BL_SUCCESS;
192 }
193
194 BL_INLINE void back(size_t n = 1) noexcept {
195 cmd -= n;
196 vtx -= n;
197 }
198
199 BL_INLINE void sync(BLPathCore* dst) noexcept {
200 BL_ASSERT(!empty());
201
202 BLInternalPathImpl* dstI = blInternalCast(dst->impl);
203 dstI->size = (size_t)(cmd - dstI->commandData);
204 BL_ASSERT(dstI->size <= dstI->capacity);
205 }
206
207 BL_INLINE void done(BLPathCore* dst) noexcept {
208 sync(dst);
209 reset();
210 }
211
212 BL_INLINE void moveTo(const BLPoint& p0) noexcept { moveTo(p0.x, p0.y); }
213 BL_INLINE void moveTo(const BLPointI& p0) noexcept { moveTo(p0.x, p0.y); }
214
215 BL_INLINE void moveTo(double x0, double y0) noexcept {
216 BL_ASSERT(remainingSize() >= 1);
217 cmd[0] = BL_PATH_CMD_MOVE;
218 cmd++;
219 vtx[0].reset(x0, y0);
220 vtx++;
221 }
222
223 BL_INLINE void lineTo(const BLPoint& p1) noexcept { lineTo(p1.x, p1.y); }
224 BL_INLINE void lineTo(const BLPointI& p1) noexcept { lineTo(p1.x, p1.y); }
225
226 BL_INLINE void lineTo(double x1, double y1) noexcept {
227 BL_ASSERT(remainingSize() >= 1);
228 cmd[0] = BL_PATH_CMD_ON;
229 cmd++;
230 vtx[0].reset(x1, y1);
231 vtx++;
232 }
233
234 BL_INLINE void quadTo(const BLPoint& p1, const BLPoint& p2) noexcept {
235 quadTo(p1.x, p1.y, p2.x, p2.y);
236 }
237
238 BL_INLINE void quadTo(double x1, double y1, double x2, double y2) noexcept {
239 BL_ASSERT(remainingSize() >= 2);
240 cmd[0] = BL_PATH_CMD_QUAD;
241 cmd[1] = BL_PATH_CMD_ON;
242 cmd += 2;
243 vtx[0].reset(x1, y1);
244 vtx[1].reset(x2, y2);
245 vtx += 2;
246 }
247
248 BL_INLINE void cubicTo(const BLPoint& p1, const BLPoint& p2, const BLPoint& p3) noexcept {
249 return cubicTo(p1.x, p1.y, p2.x, p2.y, p3.x, p3.y);
250 }
251
252 BL_INLINE void cubicTo(double x1, double y1, double x2, double y2, double x3, double y3) noexcept {
253 BL_ASSERT(remainingSize() >= 3);
254 cmd[0] = BL_PATH_CMD_CUBIC;
255 cmd[1] = BL_PATH_CMD_CUBIC;
256 cmd[2] = BL_PATH_CMD_ON;
257 cmd += 3;
258 vtx[0].reset(x1, y1);
259 vtx[1].reset(x2, y2);
260 vtx[2].reset(x3, y3);
261 vtx += 3;
262 }
263
264 BL_INLINE void arcQuadrantTo(const BLPoint& p1, const BLPoint& p2) noexcept {
265 BL_ASSERT(remainingSize() >= 3);
266
267 cmd[0] = BL_PATH_CMD_CUBIC;
268 cmd[1] = BL_PATH_CMD_CUBIC;
269 cmd[2] = BL_PATH_CMD_ON;
270 cmd += 3;
271
272 BLPoint p0 = vtx[-1];
273 vtx[0] = p0 + (p1 - p0) * BL_MATH_KAPPA;
274 vtx[1] = p2 + (p1 - p2) * BL_MATH_KAPPA;
275 vtx[2] = p2;
276 vtx += 3;
277 }
278
279 BL_INLINE void conicTo(const BLPoint& p1, const BLPoint& p2, double w) noexcept {
280 BL_ASSERT(remainingSize() >= 3);
281 double k = 4.0 * w / (3.0 * (1.0 + w));
282
283 cmd[0] = BL_PATH_CMD_CUBIC;
284 cmd[1] = BL_PATH_CMD_CUBIC;
285 cmd[2] = BL_PATH_CMD_ON;
286 cmd += 3;
287
288 BLPoint p0 = vtx[-1];
289 vtx[0] = p0 + (p1 - p0) * k;
290 vtx[1] = p2 + (p1 - p2) * k;
291 vtx[2] = p2;
292 vtx += 3;
293 }
294
295 BL_INLINE void addVertex(uint8_t cmd_, const BLPoint& p) noexcept {
296 BL_ASSERT(remainingSize() >= 1);
297
298 cmd[0] = cmd_;
299 cmd++;
300 vtx[0] = p;
301 vtx++;
302 }
303
304 BL_INLINE void addVertex(uint8_t cmd_, double x, double y) noexcept {
305 BL_ASSERT(remainingSize() >= 1);
306
307 cmd[0] = cmd_;
308 cmd++;
309 vtx[0].reset(x, y);
310 vtx++;
311 }
312
313 BL_INLINE void close() noexcept {
314 BL_ASSERT(remainingSize() >= 1);
315
316 cmd[0] = BL_PATH_CMD_CLOSE;
317 cmd++;
318 vtx[0].reset(blNaN<double>(), blNaN<double>());
319 vtx++;
320 }
321
322 BL_INLINE void addBox(double x0, double y0, double x1, double y1, uint32_t dir) noexcept {
323 BL_ASSERT(remainingSize() >= 5);
324
325 cmd[0] = BL_PATH_CMD_MOVE;
326 cmd[1] = BL_PATH_CMD_ON;
327 cmd[2] = BL_PATH_CMD_ON;
328 cmd[3] = BL_PATH_CMD_ON;
329 cmd[4] = BL_PATH_CMD_CLOSE;
330
331 vtx[0].reset(x0, y0);
332 vtx[1].reset(x1, y0);
333 vtx[2].reset(x1, y1);
334 vtx[3].reset(x0, y1);
335 vtx[4].reset(blNaN<double>(), blNaN<double>());
336
337 if (dir != BL_GEOMETRY_DIRECTION_CW) {
338 vtx[1].reset(x0, y1);
339 vtx[3].reset(x1, y0);
340 }
341
342 cmd += 5;
343 vtx += 5;
344 }
345
346 BL_INLINE void addBoxCW(double x0, double y0, double x1, double y1) noexcept {
347 addBox(x0, y0, x1, y1, BL_GEOMETRY_DIRECTION_CW);
348 }
349
350 BL_INLINE void addBoxCCW(double x0, double y0, double x1, double y1) noexcept {
351 addBox(x0, y0, x1, y1, BL_GEOMETRY_DIRECTION_CCW);
352 }
353};
354
355//! \}
356//! \endcond
357
358#endif // BLEND2D_BLPATH_P_H
359