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 | |
22 | static 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`. |
38 | struct BLInternalPathImpl : public BLPathImpl { |
39 | BLBox controlBox; |
40 | BLBox boundingBox; |
41 | }; |
42 | |
43 | template<> |
44 | struct BLInternalCastImpl<BLPathImpl> { typedef BLInternalPathImpl Type; }; |
45 | |
46 | BL_HIDDEN BLResult blPathImplDelete(BLPathImpl* impl_) noexcept; |
47 | BL_HIDDEN BLResult blPathAddTransformedPathWithType(BLPathCore* self, const BLPathCore* other, const BLRange* range, const BLMatrix2D* m, uint32_t mType) noexcept; |
48 | BL_HIDDEN BLResult blPathTransformWithType(BLPathCore* self, const BLRange* range, const BLMatrix2D* m, uint32_t mType) noexcept; |
49 | |
50 | // ============================================================================ |
51 | // [BLPathIterator] |
52 | // ============================================================================ |
53 | |
54 | struct 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 | //! ``` |
139 | class BLPathAppender { |
140 | public: |
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 | |