NovaSVG 0.10.0
A header-only SVG library
Loading...
Searching...
No Matches
canvas.h
Go to the documentation of this file.
1#ifndef NOVASVG_CANVAS_H
2#define NOVASVG_CANVAS_H
3
4#include <cstdint>
5#include <cstring>
6#include <cstdlib>
7#include <cmath>
8#include <algorithm>
9#include <utility>
10#include <memory>
11#include <vector>
12#include <array>
13#include <string>
14
15// Canvas: novasvg's 2D drawing surface (paths, fills, strokes, gradients,
16// text, blending), plus the Path/Rect/Transform/Point primitives that go
17// with it. Self-contained -- pulls in exactly the render:: engine pieces
18// it needs plus Font (for fillText/strokeText) -- so it's usable without
19// the SVG/parsing layer, e.g. to draw directly onto a Bitmap.
20#include "color.h"
21#include "font.h"
22#include "bitmap.h"
23#include "render/blend.h"
24#include "render/matrix.h"
25#include "render/paint.h"
26#include "render/path.h"
27#include "render/rasterize.h"
28#include "render/surface.h"
29#include "render/canvas.h"
30
31namespace novasvg {
32using namespace render; // render/ layer (novasvg::render), the former plutovg
33
39
45
50
56
57// `Color` now lives in color.h (a single merged class covering both
58// this engine's needs and the richer string-parsing/blending API) --
59// no separate definition here anymore.
60
61class Point {
62public:
63 constexpr Point() = default;
64 constexpr Point(const point_t& point) : Point(point.x, point.y) {}
65 constexpr Point(float x, float y) : x(x), y(y) {}
66
67 constexpr void move(float dx, float dy) { x += dx; y += dy; }
68 constexpr void move(float d) { move(d, d); }
69 constexpr void move(const Point& p) { move(p.x, p.y); }
70
71 constexpr void scale(float sx, float sy) { x *= sx; y *= sy; }
72 constexpr void scale(float s) { scale(s, s); }
73
74 constexpr float dot(const Point& p) const { return x * p.x + y * p.y; }
75
76public:
77 float x{0};
78 float y{0};
79};
80
81constexpr Point operator+(const Point& a, const Point& b)
82{
83 return Point(a.x + b.x, a.y + b.y);
84}
85
86constexpr Point operator-(const Point& a, const Point& b)
87{
88 return Point(a.x - b.x, a.y - b.y);
89}
90
91constexpr Point operator-(const Point& a)
92{
93 return Point(-a.x, -a.y);
94}
95
96constexpr Point& operator+=(Point& a, const Point& b)
97{
98 a.move(b);
99 return a;
100}
101
102constexpr Point& operator-=(Point& a, const Point& b)
103{
104 a.move(-b);
105 return a;
106}
107
108constexpr float operator*(const Point& a, const Point& b)
109{
110 return a.dot(b);
111}
112
113class Size {
114public:
115 constexpr Size() = default;
116 constexpr Size(float w, float h) : w(w), h(h) {}
117
118 constexpr void expand(float dw, float dh) { w += dw; h += dh; }
119 constexpr void expand(float d) { expand(d, d); }
120 constexpr void expand(const Size& s) { expand(s.w, s.h); }
121
122 constexpr void scale(float sw, float sh) { w *= sw; h *= sh; }
123 constexpr void scale(float s) { scale(s, s); }
124
125 constexpr bool isEmpty() const { return w <= 0.f || h <= 0.f; }
126 constexpr bool isZero() const { return w <= 0.f && h <= 0.f; }
127 constexpr bool isValid() const { return w >= 0.f && h >= 0.f; }
128
129public:
130 float w{0};
131 float h{0};
132};
133
134constexpr Size operator+(const Size& a, const Size& b)
135{
136 return Size(a.w + b.w, a.h + b.h);
137}
138
139constexpr Size operator-(const Size& a, const Size& b)
140{
141 return Size(a.w - b.w, a.h - b.h);
142}
143
144constexpr Size operator-(const Size& a)
145{
146 return Size(-a.w, -a.h);
147}
148
149constexpr Size& operator+=(Size& a, const Size& b)
150{
151 a.expand(b);
152 return a;
153}
154
155constexpr Size& operator-=(Size& a, const Size& b)
156{
157 a.expand(-b);
158 return a;
159}
160
161class Box;
162
163class Rect {
164public:
165 constexpr Rect() = default;
166 constexpr explicit Rect(const Size& size) : Rect(size.w, size.h) {}
167 constexpr Rect(float width, float height) : Rect(0, 0, width, height) {}
168 constexpr Rect(const Point& origin, const Size& size) : Rect(origin.x, origin.y, size.w, size.h) {}
169 constexpr Rect(const rect_t& rect) : Rect(rect.x, rect.y, rect.w, rect.h) {}
170 constexpr Rect(float x, float y, float w, float h) : x(x), y(y), w(w), h(h) {}
171
172 Rect(const Box& box);
173
174 constexpr void move(float dx, float dy) { x += dx; y += dy; }
175 constexpr void move(float d) { move(d, d); }
176 constexpr void move(const Point& p) { move(p.x, p.y); }
177
178 constexpr void scale(float sx, float sy) { x *= sx; y *= sy; w *= sx; h *= sy; }
179 constexpr void scale(float s) { scale(s, s); }
180
181 constexpr void inflate(float dx, float dy) { x -= dx; y -= dy; w += dx * 2.f; h += dy * 2.f; }
182 constexpr void inflate(float d) { inflate(d, d); }
183
184 constexpr bool contains(float px, float py) const { return px >= x && px <= x + w && py >= y && py <= y + h; }
185 constexpr bool contains(const Point& p) const { return contains(p.x, p.y); }
186
187 constexpr Rect intersected(const Rect& rect) const;
188 constexpr Rect united(const Rect& rect) const;
189
190 constexpr Rect& intersect(const Rect& o);
191 constexpr Rect& unite(const Rect& o);
192
193 constexpr Point origin() const { return Point(x, y); }
194 constexpr Size size() const { return Size(w, h); }
195
196 constexpr float right() const { return x + w; }
197 constexpr float bottom() const { return y + h; }
198
199 constexpr bool isEmpty() const { return w <= 0.f || h <= 0.f; }
200 constexpr bool isZero() const { return w <= 0.f && h <= 0.f; }
201 constexpr bool isValid() const { return w >= 0.f && h >= 0.f; }
202
203 static const Rect Empty;
204 static const Rect Invalid;
205 static const Rect Infinite;
206
207public:
208 float x{0};
209 float y{0};
210 float w{0};
211 float h{0};
212};
213
214constexpr Rect Rect::intersected(const Rect& rect) const
215{
216 if(!rect.isValid())
217 return *this;
218 if(!isValid())
219 return rect;
220 auto l = NOVASVG_MAX(x, rect.x);
221 auto t = NOVASVG_MAX(y, rect.y);
222 auto r = NOVASVG_MIN(x + w, rect.x + rect.w);
223 auto b = NOVASVG_MIN(y + h, rect.y + rect.h);
224 if(l >= r || t >= b)
225 return Rect::Empty;
226 return Rect(l, t, r - l, b - t);
227}
228
229constexpr Rect Rect::united(const Rect& rect) const
230{
231 if(!rect.isValid())
232 return *this;
233 if(!isValid())
234 return rect;
235 auto l = NOVASVG_MIN(x, rect.x);
236 auto t = NOVASVG_MIN(y, rect.y);
237 auto r = NOVASVG_MAX(x + w, rect.x + rect.w);
238 auto b = NOVASVG_MAX(y + h, rect.y + rect.h);
239 return Rect(l, t, r - l, b - t);
240}
241
242constexpr Rect& Rect::intersect(const Rect& o)
243{
244 *this = intersected(o);
245 return *this;
246}
247
248constexpr Rect& Rect::unite(const Rect& o)
249{
250 *this = united(o);
251 return *this;
252}
253
254class Matrix;
255
257public:
258 Transform();
259 Transform(const Matrix& matrix);
260 Transform(float a, float b, float c, float d, float e, float f);
261 Transform(const matrix_t& matrix) : m_matrix(matrix) {}
262
263 Transform operator*(const Transform& transform) const;
264 Transform& operator*=(const Transform& transform);
265
266 Transform& multiply(const Transform& transform);
267 Transform& translate(float tx, float ty);
268 Transform& scale(float sx, float sy);
269 Transform& rotate(float angle, float cx = 0.f, float cy = 0.f);
270 Transform& shear(float shx, float shy);
271
272 Transform& postMultiply(const Transform& transform);
273 Transform& postTranslate(float tx, float ty);
274 Transform& postScale(float sx, float sy);
275 Transform& postRotate(float angle, float cx = 0.f, float cy = 0.f);
276 Transform& postShear(float shx, float shy);
277
278 Transform inverse() const;
279 Transform& invert();
280
281 void reset();
282
283 Point mapPoint(float x, float y) const;
284 Point mapPoint(const Point& point) const;
285 Rect mapRect(const Rect& rect) const;
286
287 float xScale() const;
288 float yScale() const;
289
290 const matrix_t& matrix() const { return m_matrix; }
291 matrix_t& matrix() { return m_matrix; }
292
293 bool parse(const char* data, size_t length);
294
295 static Transform translated(float tx, float ty);
296 static Transform scaled(float sx, float sy);
297 static Transform rotated(float angle, float cx, float cy);
298 static Transform sheared(float shx, float shy);
299
300 static const Transform Identity;
301
302private:
303 matrix_t m_matrix;
304};
305
312
313class Path {
314public:
315 Path() = default;
316 Path(const Path& path);
317 Path(Path&& path);
318 ~Path();
319
320 Path& operator=(const Path& path);
322
323 void swap(Path& path);
324
325 void moveTo(float x, float y);
326 void lineTo(float x, float y);
327 void quadTo(float x1, float y1, float x2, float y2);
328 void cubicTo(float x1, float y1, float x2, float y2, float x3, float y3);
329 void arcTo(float rx, float ry, float xAxisRotation, bool largeArcFlag, bool sweepFlag, float x, float y);
330 void close();
331
332 void addEllipse(float cx, float cy, float rx, float ry);
333 void addRoundRect(float x, float y, float w, float h, float rx, float ry);
334 void addRect(float x, float y, float w, float h);
335
336 void addEllipse(const Point& center, const Size& radii);
337 void addRoundRect(const Rect& rect, const Size& radii);
338 void addRect(const Rect& rect);
339
340 void reset();
341
342 Rect boundingRect() const;
343 bool isEmpty() const;
344 bool isUnique() const;
345 bool isNull() const { return m_data == nullptr; }
346 path_t* data() const { return m_data; }
347
348 bool parse(const char* data, size_t length);
349
350private:
351 path_t* release();
352 path_t* ensure();
353 path_t* m_data = nullptr;
354};
355
356inline void Path::swap(Path& path)
357{
358 std::swap(m_data, path.m_data);
359}
360
361inline path_t* Path::release()
362{
363 return std::exchange(m_data, nullptr);
364}
365
367public:
368 PathIterator(const Path& path);
369
370 PathCommand currentSegment(std::array<Point, 3>& points) const;
371 bool isDone() const { return m_index >= m_size; }
372 void next();
373
374private:
375 const path_element_t* m_elements;
376 const int m_size;
377 int m_index;
378};
379
380} // namespace novasvg
381
382namespace novasvg {
383using namespace render;
384
385enum class TextureType {
388};
389
390enum class BlendMode {
392 Src_Over = NOVASVG_OPERATOR_SRC_OVER,
394 Dst_Out = NOVASVG_OPERATOR_DST_OUT,
395 Multiply = NOVASVG_OPERATOR_MULTIPLY,
398 Lighten = NOVASVG_OPERATOR_LIGHTEN,
399 Difference = NOVASVG_OPERATOR_DIFFERENCE,
401};
402
403using DashArray = std::vector<float>;
404
405class StrokeData {
406public:
407 explicit StrokeData(float lineWidth = 1.f) : m_lineWidth(lineWidth) {}
408
409 void setLineWidth(float lineWidth) { m_lineWidth = lineWidth; }
410 float lineWidth() const { return m_lineWidth; }
411
412 void setMiterLimit(float miterLimit) { m_miterLimit = miterLimit; }
413 float miterLimit() const { return m_miterLimit; }
414
415 void setDashOffset(float dashOffset) { m_dashOffset = dashOffset; }
416 float dashOffset() const { return m_dashOffset; }
417
418 void setDashArray(DashArray dashArray) { m_dashArray = std::move(dashArray); }
419 const DashArray& dashArray() const { return m_dashArray; }
420
421 void setLineCap(LineCap lineCap) { m_lineCap = lineCap; }
422 LineCap lineCap() const { return m_lineCap; }
423
424 void setLineJoin(LineJoin lineJoin) { m_lineJoin = lineJoin; }
425 LineJoin lineJoin() const { return m_lineJoin; }
426
427private:
428 float m_lineWidth;
429 float m_miterLimit{4.f};
430 float m_dashOffset{0.f};
431 LineCap m_lineCap{LineCap::Butt};
432 LineJoin m_lineJoin{LineJoin::Miter};
433 DashArray m_dashArray;
434};
435
436using GradientStop = gradient_stop_t;
437using GradientStops = std::vector<GradientStop>;
438
439class Bitmap;
440
441class Canvas {
442public:
443 static std::shared_ptr<Canvas> create(const Bitmap& bitmap);
444 static std::shared_ptr<Canvas> create(float x, float y, float width, float height);
445 static std::shared_ptr<Canvas> create(const Rect& extents);
446
447 void setColor(const Color& color);
448 void setColor(float r, float g, float b, float a);
449 void setLinearGradient(float x1, float y1, float x2, float y2, SpreadMethod spread, const GradientStops& stops, const Transform& transform);
450 void setRadialGradient(float cx, float cy, float r, float fx, float fy, SpreadMethod spread, const GradientStops& stops, const Transform& transform);
451 void setTexture(const Canvas& source, TextureType type, float opacity, const Transform& transform);
452
453 void fillPath(const Path& path, FillRule fillRule, const Transform& transform);
454 void strokePath(const Path& path, const StrokeData& strokeData, const Transform& transform);
455
456 void fillText(const std::u32string_view& text, const Font& font, const Point& origin, const Transform& transform);
457 void strokeText(const std::u32string_view& text, float strokeWidth, const Font& font, const Point& origin, const Transform& transform);
458
459 void clipPath(const Path& path, FillRule clipRule, const Transform& transform);
460 void clipRect(const Rect& rect, FillRule clipRule, const Transform& transform);
461
462 void drawImage(const Bitmap& image, const Rect& dstRect, const Rect& srcRect, const Transform& transform);
463 void blendCanvas(const Canvas& canvas, BlendMode blendMode, float opacity);
464
465 void save();
466 void restore();
467
468 void convertToLuminanceMask();
469 // standard "fast almost-gaussian" trick browsers/thorvg also use)
470 // rather than a true gaussian convolution kernel -- visually
471 // indistinguishable for the stdDeviation ranges SVG content actually
472 // uses, and O(w*h) instead of O(w*h*kernel). Upgrade path: replace the
473 // naive per-pixel box blur loop with a moving-sum/summed-area version
474 // if profiling ever shows this mattering for very large canvases.
475 void boxBlur(float stdDeviationX, float stdDeviationY);
476
477 // Composites `this` (already blurred, tinted to floodColor/floodOpacity
478 // by the caller) as a drop shadow: offsets by (dx, dy) and draws
479 // `original` on top of it, replacing this canvas's contents in place.
480 void compositeDropShadowUnder(const Canvas& original, float dx, float dy);
481
482 // Recolors every pixel to floodColor while preserving its alpha
483 // (scaled by floodOpacity), for feDropShadow/feFlood.
484 void tintToFloodColor(const Color& floodColor, float floodOpacity);
485
486 // Fills every pixel fully opaque white; paired with tintToFloodColor()
487 // this gives feFlood's "solid flood-color rectangle" result.
488 void fillOpaqueWhite();
489
490 // Shifts this canvas's pixels by (dx, dy) in place; area exposed at
491 // the edges becomes transparent.
492 void shift(float dx, float dy);
493
494 // Porter-Duff compositing operators needed by feComposite/feMerge.
495 // (arithmetic is intentionally not offered -- see SVGFECompositeElement)
496 enum class PorterDuff { Over, In, Out, Atop, Xor };
497 void compositeWith(const Canvas& src, PorterDuff mode);
498 void compositeOver(const Canvas& src) ;
499
500 // feComposite operator="arithmetic": result = k1*i1*i2 + k2*i1 + k3*i2 + k4
501 // per channel, in normalized [0,1] premultiplied space, clamped back to
502 // [0,1]. `src` is i1, `this` (already holding in2's pixels) is i2 --
503 // the result replaces `this` in place.
504 void compositeArithmetic(const Canvas& src, float k1, float k2, float k3, float k4);
505
506 // Raw memcpy of `other`'s pixel buffer into this one -- both must be
507 // the same width/height/stride (caller's responsibility).
508 void copyPixelsFrom(const Canvas& other);
509
510 int x() const { return m_x; }
511 int y() const { return m_y; }
512 int width() const;
513 int height() const;
514
515 Rect extents() const { return Rect(m_x, m_y, width(), height()); }
516
517 surface_t* surface() const { return m_surface; }
518 canvas_t* canvas() const { return m_canvas; }
519
520 ~Canvas();
521
522private:
523 Canvas(const Bitmap& bitmap);
524 Canvas(int x, int y, int width, int height);
525 surface_t* m_surface;
526 canvas_t* m_canvas;
527 matrix_t m_translation;
528 const int m_x;
529 const int m_y;
530};
531
532#include <cfloat>
533#include <cmath>
534
535// ---------------------------------------------------------------
536// Implementation (formerly graphics.hpp, merged in -- everything
537// here is NOVASVG_INLINE, so there is no more reason to keep
538// declarations and definitions in separate files).
539// ---------------------------------------------------------------
540using namespace render; // render/ layer (novasvg::render), the former plutovg
541
542// Color::Black/White/Transparent are defined once, in color.h.
543
544NOVASVG_INLINE const Rect Rect::Empty(0, 0, 0, 0);
545NOVASVG_INLINE const Rect Rect::Invalid(0, 0, -1, -1);
546NOVASVG_INLINE const Rect Rect::Infinite(-FLT_MAX / 2.f, -FLT_MAX / 2.f, FLT_MAX, FLT_MAX);
547
548// Rect::Rect(const Box&) is defined in novasvg.h, right after Box itself --
549// Box belongs to the SVG/Document layer, not here, so this file (which
550// stays usable without that layer) only declares the conversion, it
551// doesn't define it.
552
553NOVASVG_INLINE const Transform Transform::Identity(1, 0, 0, 1, 0, 0);
554
559
560NOVASVG_INLINE Transform::Transform(float a, float b, float c, float d, float e, float f)
561{
562 matrix_init(&m_matrix, a, b, c, d, e, f);
563}
564
565// Transform::Transform(const Matrix&) is defined in novasvg.h, right after
566// Matrix itself -- Matrix belongs to the SVG/Document layer, not here, so
567// this file (which stays usable without that layer) only declares the
568// conversion, it doesn't define it.
569
571{
572 matrix_t result;
573 matrix_multiply(&result, &transform.m_matrix, &m_matrix);
574 return result;
575}
576
578{
579 return (*this = *this * transform);
580}
581
583{
584 return (*this *= transform);
585}
586
588{
589 return multiply(translated(tx, ty));
590}
591
593{
594 return multiply(scaled(sx, sy));
595}
596
597NOVASVG_INLINE Transform& Transform::rotate(float angle, float cx, float cy)
598{
599 return multiply(rotated(angle, cx, cy));
600}
601
603{
604 return multiply(sheared(shx, shy));
605}
606
608{
609 return (*this = transform * *this);
610}
611
613{
614 return postMultiply(translated(tx, ty));
615}
616
618{
619 return postMultiply(scaled(sx, sy));
620}
621
622NOVASVG_INLINE Transform& Transform::postRotate(float angle, float cx, float cy)
623{
624 return postMultiply(rotated(angle, cx, cy));
625}
626
628{
629 return postMultiply(sheared(shx, shy));
630}
631
633{
635 matrix_invert(&m_matrix, &inverse);
636 return inverse;
637}
638
640{
641 matrix_invert(&m_matrix, &m_matrix);
642 return *this;
643}
644
646{
647 matrix_init_identity(&m_matrix);
648}
649
651{
652 matrix_map(&m_matrix, x, y, &x, &y);
653 return Point(x, y);
654}
655
657{
658 return mapPoint(point.x, point.y);
659}
660
662{
663 if(!rect.isValid()) {
664 return Rect::Invalid;
665 }
666
667 rect_t result = {rect.x, rect.y, rect.w, rect.h};
668 matrix_map_rect(&m_matrix, &result, &result);
669 return result;
670}
671
673{
674 return std::sqrt(m_matrix.a * m_matrix.a + m_matrix.b * m_matrix.b);
675}
676
678{
679 return std::sqrt(m_matrix.c * m_matrix.c + m_matrix.d * m_matrix.d);
680}
681
682NOVASVG_INLINE bool Transform::parse(const char* data, size_t length)
683{
684 return matrix_parse(&m_matrix, data, length);
685}
686
687NOVASVG_INLINE Transform Transform::rotated(float angle, float cx, float cy)
688{
690 if(cx == 0.f && cy == 0.f) {
692 } else {
695 matrix_translate(&matrix, -cx, -cy);
696 }
697
698 return matrix;
699}
700
702{
704 matrix_init_scale(&matrix, sx, sy);
705 return matrix;
706}
707
714
716{
719 return matrix;
720}
721
723 : m_data(path_reference(path.data()))
724{
725}
726
728 : m_data(path.release())
729{
730}
731
733{
734 path_destroy(m_data);
735}
736
738{
739 Path(path).swap(*this);
740 return *this;
741}
742
744{
745 Path(std::move(path)).swap(*this);
746 return *this;
747}
748
749NOVASVG_INLINE void Path::moveTo(float x, float y)
750{
751 path_move_to(ensure(), x, y);
752}
753
754NOVASVG_INLINE void Path::lineTo(float x, float y)
755{
756 path_line_to(ensure(), x, y);
757}
758
759NOVASVG_INLINE void Path::quadTo(float x1, float y1, float x2, float y2)
760{
761 path_quad_to(ensure(), x1, y1, x2, y2);
762}
763
764NOVASVG_INLINE void Path::cubicTo(float x1, float y1, float x2, float y2, float x3, float y3)
765{
766 path_cubic_to(ensure(), x1, y1, x2, y2, x3, y3);
767}
768
769NOVASVG_INLINE void Path::arcTo(float rx, float ry, float xAxisRotation, bool largeArcFlag, bool sweepFlag, float x, float y)
770{
771 path_arc_to(ensure(), rx, ry, NOVASVG_DEG2RAD(xAxisRotation), largeArcFlag, sweepFlag, x, y);
772}
773
775{
776 path_close(ensure());
777}
778
779NOVASVG_INLINE void Path::addEllipse(float cx, float cy, float rx, float ry)
780{
781 path_add_ellipse(ensure(), cx, cy, rx, ry);
782}
783
784NOVASVG_INLINE void Path::addRoundRect(float x, float y, float w, float h, float rx, float ry)
785{
786 path_add_round_rect(ensure(), x, y, w, h, rx, ry);
787}
788
789NOVASVG_INLINE void Path::addRect(float x, float y, float w, float h)
790{
791 path_add_rect(ensure(), x, y, w, h);
792}
793
794NOVASVG_INLINE void Path::addEllipse(const Point& center, const Size& radii)
795{
796 addEllipse(center.x, center.y, radii.w, radii.h);
797}
798
800{
801 addRoundRect(rect.x, rect.y, rect.w, rect.h, radii.w, radii.h);
802}
803
805{
806 addRect(rect.x, rect.y, rect.w, rect.h);
807}
808
810{
811 if(m_data == nullptr)
812 return;
813 if(isUnique()) {
814 path_reset(m_data);
815 } else {
816 path_destroy(m_data);
817 m_data = nullptr;
818 }
819}
820
822{
823 if(m_data == nullptr)
824 return Rect::Empty;
825 rect_t extents;
826 path_extents(m_data, &extents, false);
827 return extents;
828}
829
831{
832 if(m_data)
833 return path_get_elements(m_data, nullptr) == 0;
834 return true;
835}
836
838{
839 return path_get_reference_count(m_data) == 1;
840}
841
842NOVASVG_INLINE bool Path::parse(const char* data, size_t length)
843{
844 path_reset(ensure());
845 return path_parse(m_data, data, length);
846}
847
848NOVASVG_INLINE path_t* Path::ensure()
849{
850 if(isNull()) {
851 m_data = path_create();
852 } else if(!isUnique()) {
853 path_destroy(m_data);
854 m_data = path_clone(m_data);
855 }
856
857 return m_data;
858}
859
861 : m_size(path_get_elements(path.data(), &m_elements))
862 , m_index(0)
863{
864}
865
866NOVASVG_INLINE PathCommand PathIterator::currentSegment(std::array<Point, 3>& points) const
867{
868 auto command = m_elements[m_index].header.command;
869 switch(command) {
871 points[0] = m_elements[m_index + 1].point;
872 break;
874 points[0] = m_elements[m_index + 1].point;
875 break;
877 points[0] = m_elements[m_index + 1].point;
878 points[1] = m_elements[m_index + 2].point;
879 points[2] = m_elements[m_index + 3].point;
880 break;
882 points[0] = m_elements[m_index + 1].point;
883 break;
884 }
885
886 return PathCommand(command);
887}
888
890{
891 m_index += m_elements[m_index].header.length;
892}
893
894
895NOVASVG_INLINE std::shared_ptr<Canvas> Canvas::create(const Bitmap& bitmap)
896{
897 return std::shared_ptr<Canvas>(new Canvas(bitmap));
898}
899
900NOVASVG_INLINE std::shared_ptr<Canvas> Canvas::create(float x, float y, float width, float height)
901{
902 constexpr int kMaxSize = 1 << 15;
903 if(width <= 0 || height <= 0 || width >= kMaxSize || height >= kMaxSize)
904 return std::shared_ptr<Canvas>(new Canvas(0, 0, 1, 1));
905 auto l = static_cast<int>(std::floor(x));
906 auto t = static_cast<int>(std::floor(y));
907 auto r = static_cast<int>(std::ceil(x + width));
908 auto b = static_cast<int>(std::ceil(y + height));
909 return std::shared_ptr<Canvas>(new Canvas(l, t, r - l, b - t));
910}
911
912NOVASVG_INLINE std::shared_ptr<Canvas> Canvas::create(const Rect& extents)
913{
914 return create(extents.x, extents.y, extents.w, extents.h);
915}
916
917NOVASVG_INLINE void Canvas::setColor(const Color& color)
918{
919 setColor(color.getRF(), color.getGF(), color.getBF(), color.getAF());
920}
921
922NOVASVG_INLINE void Canvas::setColor(float r, float g, float b, float a)
923{
924 canvas_set_rgba(m_canvas, r, g, b, a);
925}
926
927NOVASVG_INLINE void Canvas::setLinearGradient(float x1, float y1, float x2, float y2, SpreadMethod spread, const GradientStops& stops, const Transform& transform)
928{
929 canvas_set_linear_gradient(m_canvas, x1, y1, x2, y2, static_cast<spread_method_t>(spread), stops.data(), stops.size(), &transform.matrix());
930}
931
932NOVASVG_INLINE void Canvas::setRadialGradient(float cx, float cy, float r, float fx, float fy, SpreadMethod spread, const GradientStops& stops, const Transform& transform)
933{
934 canvas_set_radial_gradient(m_canvas, cx, cy, r, fx, fy, 0.f, static_cast<spread_method_t>(spread), stops.data(), stops.size(), &transform.matrix());
935}
936
937NOVASVG_INLINE void Canvas::setTexture(const Canvas& source, TextureType type, float opacity, const Transform& transform)
938{
939 canvas_set_texture(m_canvas, source.surface(), static_cast<texture_type_t>(type), opacity, &transform.matrix());
940}
941
942NOVASVG_INLINE void Canvas::fillPath(const Path& path, FillRule fillRule, const Transform& transform)
943{
944 canvas_set_matrix(m_canvas, &m_translation);
945 canvas_transform(m_canvas, &transform.matrix());
946 canvas_set_fill_rule(m_canvas, static_cast<fill_rule_t>(fillRule));
948 canvas_fill_path(m_canvas, path.data());
949}
950
951NOVASVG_INLINE void Canvas::strokePath(const Path& path, const StrokeData& strokeData, const Transform& transform)
952{
953 canvas_set_matrix(m_canvas, &m_translation);
954 canvas_transform(m_canvas, &transform.matrix());
955 canvas_set_line_width(m_canvas, strokeData.lineWidth());
956 canvas_set_miter_limit(m_canvas, strokeData.miterLimit());
957 canvas_set_line_cap(m_canvas, static_cast<line_cap_t>(strokeData.lineCap()));
958 canvas_set_line_join(m_canvas, static_cast<line_join_t>(strokeData.lineJoin()));
959 canvas_set_dash_offset(m_canvas, strokeData.dashOffset());
960 canvas_set_dash_array(m_canvas, strokeData.dashArray().data(), strokeData.dashArray().size());
962 canvas_stroke_path(m_canvas, path.data());
963}
964
965NOVASVG_INLINE void Canvas::fillText(const std::u32string_view& text, const Font& font, const Point& origin, const Transform& transform)
966{
967 canvas_set_matrix(m_canvas, &m_translation);
968 canvas_transform(m_canvas, &transform.matrix());
971 canvas_set_font(m_canvas, font.face().get(), font.size());
972 canvas_fill_text(m_canvas, text.data(), text.length(), NOVASVG_TEXT_ENCODING_UTF32, origin.x, origin.y);
973}
974
975NOVASVG_INLINE void Canvas::strokeText(const std::u32string_view& text, float strokeWidth, const Font& font, const Point& origin, const Transform& transform)
976{
977 canvas_set_matrix(m_canvas, &m_translation);
978 canvas_transform(m_canvas, &transform.matrix());
979 canvas_set_line_width(m_canvas, strokeWidth);
980 canvas_set_miter_limit(m_canvas, 4.f);
983 canvas_set_dash_offset(m_canvas, 0.f);
984 canvas_set_dash_array(m_canvas, nullptr, 0);
986 canvas_set_font(m_canvas, font.face().get(), font.size());
987 canvas_stroke_text(m_canvas, text.data(), text.length(), NOVASVG_TEXT_ENCODING_UTF32, origin.x, origin.y);
988}
989
990NOVASVG_INLINE void Canvas::clipPath(const Path& path, FillRule clipRule, const Transform& transform)
991{
992 canvas_set_matrix(m_canvas, &m_translation);
993 canvas_transform(m_canvas, &transform.matrix());
994 canvas_set_fill_rule(m_canvas, static_cast<fill_rule_t>(clipRule));
995 canvas_clip_path(m_canvas, path.data());
996}
997
998NOVASVG_INLINE void Canvas::clipRect(const Rect& rect, FillRule clipRule, const Transform& transform)
999{
1000 canvas_set_matrix(m_canvas, &m_translation);
1001 canvas_transform(m_canvas, &transform.matrix());
1002 canvas_set_fill_rule(m_canvas, static_cast<fill_rule_t>(clipRule));
1003 canvas_clip_rect(m_canvas, rect.x, rect.y, rect.w, rect.h);
1004}
1005
1006NOVASVG_INLINE void Canvas::drawImage(const Bitmap& image, const Rect& dstRect, const Rect& srcRect, const Transform& transform)
1007{
1008 auto xScale = dstRect.w / srcRect.w;
1009 auto yScale = dstRect.h / srcRect.h;
1010 matrix_t matrix = { xScale, 0, 0, yScale, -srcRect.x * xScale, -srcRect.y * yScale };
1011 canvas_set_matrix(m_canvas, &m_translation);
1012 canvas_transform(m_canvas, &transform.matrix());
1013 canvas_translate(m_canvas, dstRect.x, dstRect.y);
1016 canvas_set_texture(m_canvas, image.surface(), NOVASVG_TEXTURE_TYPE_PLAIN, 1.f, &matrix);
1017 canvas_fill_rect(m_canvas, 0, 0, dstRect.w, dstRect.h);
1018}
1019
1020NOVASVG_INLINE void Canvas::blendCanvas(const Canvas& canvas, BlendMode blendMode, float opacity)
1021{
1022 matrix_t matrix = { 1, 0, 0, 1, static_cast<float>(canvas.x()), static_cast<float>(canvas.y()) };
1023 canvas_set_matrix(m_canvas, &m_translation);
1024 canvas_set_operator(m_canvas, static_cast<operator_t>(blendMode));
1025 canvas_set_texture(m_canvas, canvas.surface(), NOVASVG_TEXTURE_TYPE_PLAIN, opacity, &matrix);
1026 canvas_paint(m_canvas);
1027}
1028
1029NOVASVG_INLINE void Canvas::save()
1030{
1031 canvas_save(m_canvas);
1032}
1033
1034NOVASVG_INLINE void Canvas::restore()
1035{
1036 canvas_restore(m_canvas);
1037}
1038
1039NOVASVG_INLINE int Canvas::width() const
1040{
1041 return surface_get_width(m_surface);
1042}
1043
1044NOVASVG_INLINE int Canvas::height() const
1045{
1046 return surface_get_height(m_surface);
1047}
1048
1049NOVASVG_INLINE void Canvas::convertToLuminanceMask()
1050{
1051 auto width = surface_get_width(m_surface);
1052 auto height = surface_get_height(m_surface);
1053 auto stride = surface_get_stride(m_surface);
1054 auto data = surface_get_data(m_surface);
1055 for(int y = 0; y < height; y++) {
1056 auto pixels = reinterpret_cast<uint32_t*>(data + stride * y);
1057 for(int x = 0; x < width; x++) {
1058 auto pixel = pixels[x];
1059 auto a = (pixel >> 24) & 0xFF;
1060 auto r = (pixel >> 16) & 0xFF;
1061 auto g = (pixel >> 8) & 0xFF;
1062 auto b = (pixel >> 0) & 0xFF;
1063 if(a) {
1064 r = (r * 255) / a;
1065 g = (g * 255) / a;
1066 b = (b * 255) / a;
1067 }
1068
1069 auto l = (r * 0.2125 + g * 0.7154 + b * 0.0721);
1070 pixels[x] = static_cast<uint32_t>(l * (a / 255.0)) << 24;
1071 }
1072 }
1073}
1074
1075// ponytail: single-axis box blur pass, edge-extended, operating in place
1076// on premultiplied ARGB. Blurring premultiplied channels directly (rather
1077// than un-premultiplying first) is correct and is what real
1078// implementations do too.
1079// One box-blur pass over an ARGB32 surface: every pixel becomes the average of the (2*radius+1) pixels along its
1080// row (horizontal) or column, with the edge pixel standing in for anything past the edge.
1081//
1082// Runs a sliding window -- add the pixel entering the window, drop the one leaving -- so the cost per pixel does
1083// not depend on the radius. The sums are exact integers, and each is converted back with the same
1084// `uint32_t(float(sum) * (1 / (2*radius+1)))` as a window summed from scratch would be, so the output is
1085// identical to the straightforward version.
1086static void boxBlurPass(unsigned char* data, int width, int height, int stride, int radius, bool horizontal)
1087{
1088 if(radius <= 0 || width <= 0 || height <= 0)
1089 return;
1090 auto* scratch = static_cast<unsigned char*>(malloc(size_t(stride) * size_t(height)));
1091 if(!scratch)
1092 return;
1093 memcpy(scratch, data, size_t(stride) * size_t(height));
1094
1095 const float iarr = 1.f / float(radius * 2 + 1);
1096 auto pack = [iarr](int32_t a, int32_t r, int32_t g, int32_t b) {
1097 return (uint32_t(float(a) * iarr) << 24) | (uint32_t(float(r) * iarr) << 16) | (uint32_t(float(g) * iarr) << 8)
1098 | uint32_t(float(b) * iarr);
1099 };
1100
1101 if(horizontal) {
1102 for(int y = 0; y < height; ++y) {
1103 const auto* in = reinterpret_cast<const uint32_t*>(scratch + size_t(y) * size_t(stride));
1104 auto* out = reinterpret_cast<uint32_t*>(data + size_t(y) * size_t(stride));
1105 auto at = [in, width](int x) { return in[x < 0 ? 0 : (x >= width ? width - 1 : x)]; };
1106
1107 int32_t a = 0, r = 0, g = 0, b = 0;
1108 for(int k = -radius; k <= radius; ++k) {
1109 const uint32_t pixel = at(k);
1110 a += (pixel >> 24) & 0xFF;
1111 r += (pixel >> 16) & 0xFF;
1112 g += (pixel >> 8) & 0xFF;
1113 b += pixel & 0xFF;
1114 }
1115
1116 for(int x = 0; x < width; ++x) {
1117 out[x] = pack(a, r, g, b);
1118 const uint32_t leaving = at(x - radius);
1119 const uint32_t entering = at(x + radius + 1);
1120 a += int32_t((entering >> 24) & 0xFF) - int32_t((leaving >> 24) & 0xFF);
1121 r += int32_t((entering >> 16) & 0xFF) - int32_t((leaving >> 16) & 0xFF);
1122 g += int32_t((entering >> 8) & 0xFF) - int32_t((leaving >> 8) & 0xFF);
1123 b += int32_t(entering & 0xFF) - int32_t(leaving & 0xFF);
1124 }
1125 }
1126 } else {
1127 // Column sums for the whole row at once, so memory is read row by row rather than down a column.
1128 std::vector<int32_t> sums(size_t(width) * 4, 0);
1129 auto row = [&](int y) {
1130 y = y < 0 ? 0 : (y >= height ? height - 1 : y);
1131 return reinterpret_cast<const uint32_t*>(scratch + size_t(y) * size_t(stride));
1132 };
1133
1134 for(int k = -radius; k <= radius; ++k) {
1135 const uint32_t* p = row(k);
1136 for(int x = 0; x < width; ++x) {
1137 const uint32_t pixel = p[x];
1138 sums[size_t(x) * 4 + 0] += (pixel >> 24) & 0xFF;
1139 sums[size_t(x) * 4 + 1] += (pixel >> 16) & 0xFF;
1140 sums[size_t(x) * 4 + 2] += (pixel >> 8) & 0xFF;
1141 sums[size_t(x) * 4 + 3] += pixel & 0xFF;
1142 }
1143 }
1144
1145 for(int y = 0; y < height; ++y) {
1146 auto* out = reinterpret_cast<uint32_t*>(data + size_t(y) * size_t(stride));
1147 const uint32_t* leaving = row(y - radius);
1148 const uint32_t* entering = row(y + radius + 1);
1149 for(int x = 0; x < width; ++x) {
1150 int32_t* sum = &sums[size_t(x) * 4];
1151 out[x] = pack(sum[0], sum[1], sum[2], sum[3]);
1152 const uint32_t lp = leaving[x], ep = entering[x];
1153 sum[0] += int32_t((ep >> 24) & 0xFF) - int32_t((lp >> 24) & 0xFF);
1154 sum[1] += int32_t((ep >> 16) & 0xFF) - int32_t((lp >> 16) & 0xFF);
1155 sum[2] += int32_t((ep >> 8) & 0xFF) - int32_t((lp >> 8) & 0xFF);
1156 sum[3] += int32_t(ep & 0xFF) - int32_t(lp & 0xFF);
1157 }
1158 }
1159 }
1160
1161 free(scratch);
1162}
1163
1164// Converts a true gaussian stdDeviation to an equivalent box-blur radius
1165// (the well-known 3-box approximation formula), then runs 3 passes.
1166//
1167// The SVG spec's formula gives `d`, the box *width* each pass should use
1168// (https://www.w3.org/TR/SVG11/filters.html#feGaussianBlurElement) --
1169// boxBlurPass below takes a *radius* (box width = 2*radius+1), so d needs
1170// halving before rounding. Using d itself as the radius (i.e. a box width
1171// of 2d+1) doubles the effective blur: for stdDeviation=4 that's an
1172// effective sigma of ~8.5 instead of 4, visibly wider/softer than every
1173// other engine's render of the same filter.
1174static int gaussianRadiusForSigma(float sigma)
1175{
1176 if(sigma <= 0.f)
1177 return 0;
1178 auto d = sigma * 3.f * std::sqrt(2.f * 3.14159265f) / 4.f;
1179 return NOVASVG_MAX(1, int(d / 2.f + 0.5f));
1180}
1181
1182NOVASVG_INLINE void Canvas::boxBlur(float stdDeviationX, float stdDeviationY)
1183{
1184 auto width = surface_get_width(m_surface);
1185 auto height = surface_get_height(m_surface);
1186 auto stride = surface_get_stride(m_surface);
1187 auto data = surface_get_data(m_surface);
1188
1189 auto rx = gaussianRadiusForSigma(stdDeviationX);
1190 auto ry = gaussianRadiusForSigma(stdDeviationY);
1191 for(int pass = 0; pass < 3; ++pass) {
1192 if(rx > 0) boxBlurPass(data, width, height, stride, rx, true);
1193 if(ry > 0) boxBlurPass(data, width, height, stride, ry, false);
1194 }
1195}
1196
1197NOVASVG_INLINE void Canvas::fillOpaqueWhite()
1198{
1199 auto width = surface_get_width(m_surface);
1200 auto height = surface_get_height(m_surface);
1201 auto stride = surface_get_stride(m_surface);
1202 auto data = surface_get_data(m_surface);
1203 for(int y = 0; y < height; ++y) {
1204 auto* pixels = reinterpret_cast<uint32_t*>(data + stride * y);
1205 for(int x = 0; x < width; ++x)
1206 pixels[x] = 0xFFFFFFFFu;
1207 }
1208}
1209
1210NOVASVG_INLINE void Canvas::tintToFloodColor(const Color& floodColor, float floodOpacity)
1211{
1212 auto width = surface_get_width(m_surface);
1213 auto height = surface_get_height(m_surface);
1214 auto stride = surface_get_stride(m_surface);
1215 auto data = surface_get_data(m_surface);
1216
1217 auto r = uint32_t(floodColor.getR());
1218 auto g = uint32_t(floodColor.getG());
1219 auto b = uint32_t(floodColor.getB());
1220 for(int y = 0; y < height; ++y) {
1221 auto* pixels = reinterpret_cast<uint32_t*>(data + stride * y);
1222 for(int x = 0; x < width; ++x) {
1223 auto srcAlpha = (pixels[x] >> 24) & 0xFF;
1224 auto alpha = uint32_t(srcAlpha * floodOpacity);
1225 // premultiplied: scale color channels by (alpha / 255)
1226 pixels[x] = (alpha << 24) | (((r * alpha) / 255) << 16) | (((g * alpha) / 255) << 8) | ((b * alpha) / 255);
1227 }
1228 }
1229}
1230
1231NOVASVG_INLINE void Canvas::shift(float dx, float dy)
1232{
1233 auto width = surface_get_width(m_surface);
1234 auto height = surface_get_height(m_surface);
1235 auto stride = surface_get_stride(m_surface);
1236 auto data = surface_get_data(m_surface);
1237 auto idx = int(dx);
1238 auto idy = int(dy);
1239 if(idx == 0 && idy == 0)
1240 return;
1241
1242 auto* scratch = static_cast<unsigned char*>(malloc(size_t(stride) * size_t(height)));
1243 if(!scratch)
1244 return;
1245 memset(scratch, 0, size_t(stride) * size_t(height));
1246 for(int y = 0; y < height; ++y) {
1247 auto sy = y - idy;
1248 if(sy < 0 || sy >= height) continue;
1249 auto* src = reinterpret_cast<uint32_t*>(data + sy * stride);
1250 auto* dst = reinterpret_cast<uint32_t*>(scratch + y * stride);
1251 for(int x = 0; x < width; ++x) {
1252 auto sx = x - idx;
1253 if(sx < 0 || sx >= width) continue;
1254 dst[x] = src[sx];
1255 }
1256 }
1257 memcpy(data, scratch, size_t(stride) * size_t(height));
1258 free(scratch);
1259}
1260
1261NOVASVG_INLINE void Canvas::compositeWith(const Canvas& src, PorterDuff mode)
1262{
1263 auto width = surface_get_width(m_surface);
1264 auto height = surface_get_height(m_surface);
1265 auto stride = surface_get_stride(m_surface);
1266 auto data = surface_get_data(m_surface);
1267
1268 auto sWidth = surface_get_width(src.m_surface);
1269 auto sHeight = surface_get_height(src.m_surface);
1270 auto sStride = surface_get_stride(src.m_surface);
1271 auto sData = surface_get_data(src.m_surface);
1272
1273 for(int y = 0; y < height && y < sHeight; ++y) {
1274 auto* dst = reinterpret_cast<uint32_t*>(data + y * stride);
1275 auto* srcRow = reinterpret_cast<uint32_t*>(sData + y * sStride);
1276 for(int x = 0; x < width && x < sWidth; ++x) {
1277 auto s = srcRow[x];
1278 auto d = dst[x];
1279 auto sA = (s >> 24) & 0xFF;
1280 auto dA = (d >> 24) & 0xFF;
1281
1282 // Porter-Duff coefficients (premultiplied): result = s*Fs + d*Fd
1283 int Fs, Fd;
1284 switch(mode) {
1285 case PorterDuff::In: Fs = dA; Fd = 0; break;
1286 case PorterDuff::Out: Fs = 255 - dA; Fd = 0; break;
1287 case PorterDuff::Atop: Fs = dA; Fd = 255 - sA; break;
1288 case PorterDuff::Xor: Fs = 255 - dA; Fd = 255 - sA; break;
1289 case PorterDuff::Over: default: Fs = 255; Fd = 255 - sA; break;
1290 }
1291
1292 auto sr = (s >> 16) & 0xFF, sg = (s >> 8) & 0xFF, sb = s & 0xFF;
1293 auto dr = (d >> 16) & 0xFF, dg = (d >> 8) & 0xFF, db = d & 0xFF;
1294 auto outA = NOVASVG_MIN(255, int(sA * Fs + dA * Fd) / 255);
1295 auto outR = NOVASVG_MIN(255, int(sr * Fs + dr * Fd) / 255);
1296 auto outG = NOVASVG_MIN(255, int(sg * Fs + dg * Fd) / 255);
1297 auto outB = NOVASVG_MIN(255, int(sb * Fs + db * Fd) / 255);
1298 dst[x] = (uint32_t(outA) << 24) | (uint32_t(outR) << 16) | (uint32_t(outG) << 8) | uint32_t(outB);
1299 }
1300 }
1301}
1302
1303NOVASVG_INLINE void Canvas::compositeOver(const Canvas& src)
1304{
1305 compositeWith(src, PorterDuff::Over);
1306}
1307
1308NOVASVG_INLINE void Canvas::compositeArithmetic(const Canvas& src, float k1, float k2, float k3, float k4)
1309{
1310 auto width = surface_get_width(m_surface);
1311 auto height = surface_get_height(m_surface);
1312 auto stride = surface_get_stride(m_surface);
1313 auto data = surface_get_data(m_surface);
1314
1315 auto sWidth = surface_get_width(src.m_surface);
1316 auto sHeight = surface_get_height(src.m_surface);
1317 auto sStride = surface_get_stride(src.m_surface);
1318 auto sData = surface_get_data(src.m_surface);
1319
1320 auto solve = [&](int i1, int i2) -> int {
1321 auto f1 = float(i1) / 255.f;
1322 auto f2 = float(i2) / 255.f;
1323 auto result = k1 * f1 * f2 + k2 * f1 + k3 * f2 + k4;
1324 return int(std::clamp(result, 0.f, 1.f) * 255.f + 0.5f);
1325 };
1326
1327 for(int y = 0; y < height; ++y) {
1328 auto* dst = reinterpret_cast<uint32_t*>(data + y * stride);
1329 auto* srcRow = y < sHeight ? reinterpret_cast<uint32_t*>(sData + y * sStride) : nullptr;
1330 for(int x = 0; x < width; ++x) {
1331 auto s = (srcRow && x < sWidth) ? srcRow[x] : 0u;
1332 auto d = dst[x];
1333 auto outA = solve((s >> 24) & 0xFF, (d >> 24) & 0xFF);
1334 auto outR = solve((s >> 16) & 0xFF, (d >> 16) & 0xFF);
1335 auto outG = solve((s >> 8) & 0xFF, (d >> 8) & 0xFF);
1336 auto outB = solve(s & 0xFF, d & 0xFF);
1337 dst[x] = (uint32_t(outA) << 24) | (uint32_t(outR) << 16) | (uint32_t(outG) << 8) | uint32_t(outB);
1338 }
1339 }
1340}
1341
1342NOVASVG_INLINE void Canvas::compositeDropShadowUnder(const Canvas& original, float dx, float dy)
1343{
1344 shift(dx, dy);
1345 compositeOver(original);
1346}
1347
1348NOVASVG_INLINE void Canvas::copyPixelsFrom(const Canvas& other)
1349{
1350 auto stride = surface_get_stride(m_surface);
1351 auto height = surface_get_height(m_surface);
1352 memcpy(surface_get_data(m_surface), surface_get_data(other.m_surface), size_t(stride) * size_t(height));
1353}
1354
1355NOVASVG_INLINE Canvas::~Canvas()
1356{
1357 canvas_destroy(m_canvas);
1358 surface_destroy(m_surface);
1359}
1360
1361NOVASVG_INLINE Canvas::Canvas(const Bitmap& bitmap)
1362 : m_surface(surface_reference(bitmap.surface()))
1363 , m_canvas(canvas_create(m_surface))
1364 , m_translation({1, 0, 0, 1, 0, 0})
1365 , m_x(0), m_y(0)
1366{
1367}
1368
1369NOVASVG_INLINE Canvas::Canvas(int x, int y, int width, int height)
1370 : m_surface(surface_create(width, height))
1371 , m_canvas(canvas_create(m_surface))
1372 , m_translation({1, 0, 0, 1, -static_cast<float>(x), -static_cast<float>(y)})
1373 , m_x(x), m_y(y)
1374{
1375}
1376
1377} // namespace novasvg
1378
1379#endif // NOVASVG_CANVAS_H
Represents a 2D axis-aligned bounding box.
Definition novasvg.h:170
Represents a 2D transformation matrix.
Definition novasvg.h:216
bool isDone() const
Definition canvas.h:371
PathCommand currentSegment(std::array< Point, 3 > &points) const
Definition canvas.h:866
PathIterator(const Path &path)
Definition canvas.h:860
void arcTo(float rx, float ry, float xAxisRotation, bool largeArcFlag, bool sweepFlag, float x, float y)
Definition canvas.h:769
Rect boundingRect() const
Definition canvas.h:821
path_t * data() const
Definition canvas.h:346
Path & operator=(const Path &path)
Definition canvas.h:737
bool isUnique() const
Definition canvas.h:837
void cubicTo(float x1, float y1, float x2, float y2, float x3, float y3)
Definition canvas.h:764
void lineTo(float x, float y)
Definition canvas.h:754
void close()
Definition canvas.h:774
void addRect(float x, float y, float w, float h)
Definition canvas.h:789
void quadTo(float x1, float y1, float x2, float y2)
Definition canvas.h:759
void moveTo(float x, float y)
Definition canvas.h:749
void addRoundRect(float x, float y, float w, float h, float rx, float ry)
Definition canvas.h:784
bool isEmpty() const
Definition canvas.h:830
bool isNull() const
Definition canvas.h:345
void swap(Path &path)
Definition canvas.h:356
bool parse(const char *data, size_t length)
Definition canvas.h:842
void reset()
Definition canvas.h:809
Path()=default
void addEllipse(float cx, float cy, float rx, float ry)
Definition canvas.h:779
constexpr void move(const Point &p)
Definition canvas.h:69
constexpr void scale(float s)
Definition canvas.h:72
constexpr Point(float x, float y)
Definition canvas.h:65
constexpr float dot(const Point &p) const
Definition canvas.h:74
constexpr Point()=default
constexpr void scale(float sx, float sy)
Definition canvas.h:71
constexpr void move(float d)
Definition canvas.h:68
constexpr void move(float dx, float dy)
Definition canvas.h:67
constexpr Point(const point_t &point)
Definition canvas.h:64
constexpr Rect(const Size &size)
Definition canvas.h:166
constexpr bool contains(float px, float py) const
Definition canvas.h:184
constexpr float right() const
Definition canvas.h:196
constexpr void inflate(float d)
Definition canvas.h:182
constexpr bool isValid() const
Definition canvas.h:201
constexpr Rect(const rect_t &rect)
Definition canvas.h:169
constexpr void scale(float sx, float sy)
Definition canvas.h:178
constexpr Rect & unite(const Rect &o)
Definition canvas.h:248
constexpr Rect()=default
constexpr Rect(const Point &origin, const Size &size)
Definition canvas.h:168
constexpr bool contains(const Point &p) const
Definition canvas.h:185
static const Rect Empty
Definition canvas.h:203
static const Rect Infinite
Definition canvas.h:205
constexpr Rect intersected(const Rect &rect) const
Definition canvas.h:214
constexpr void move(float d)
Definition canvas.h:175
constexpr void move(float dx, float dy)
Definition canvas.h:174
constexpr Rect(float x, float y, float w, float h)
Definition canvas.h:170
constexpr void move(const Point &p)
Definition canvas.h:176
constexpr bool isZero() const
Definition canvas.h:200
constexpr Size size() const
Definition canvas.h:194
constexpr float bottom() const
Definition canvas.h:197
constexpr void scale(float s)
Definition canvas.h:179
constexpr Rect & intersect(const Rect &o)
Definition canvas.h:242
constexpr Rect united(const Rect &rect) const
Definition canvas.h:229
constexpr void inflate(float dx, float dy)
Definition canvas.h:181
constexpr bool isEmpty() const
Definition canvas.h:199
constexpr Point origin() const
Definition canvas.h:193
static const Rect Invalid
Definition canvas.h:204
constexpr Rect(float width, float height)
Definition canvas.h:167
constexpr void expand(const Size &s)
Definition canvas.h:120
constexpr bool isValid() const
Definition canvas.h:127
constexpr void expand(float dw, float dh)
Definition canvas.h:118
constexpr bool isEmpty() const
Definition canvas.h:125
constexpr Size()=default
constexpr Size(float w, float h)
Definition canvas.h:116
constexpr void scale(float s)
Definition canvas.h:123
constexpr bool isZero() const
Definition canvas.h:126
constexpr void scale(float sw, float sh)
Definition canvas.h:122
constexpr void expand(float d)
Definition canvas.h:119
bool parse(const char *data, size_t length)
Definition canvas.h:682
static const Transform Identity
Definition canvas.h:300
Transform & invert()
Definition canvas.h:639
Transform & scale(float sx, float sy)
Definition canvas.h:592
Transform inverse() const
Definition canvas.h:632
Transform & postScale(float sx, float sy)
Definition canvas.h:617
Transform & multiply(const Transform &transform)
Definition canvas.h:582
Transform & rotate(float angle, float cx=0.f, float cy=0.f)
Definition canvas.h:597
Transform & shear(float shx, float shy)
Definition canvas.h:602
Transform & translate(float tx, float ty)
Definition canvas.h:587
matrix_t & matrix()
Definition canvas.h:291
float yScale() const
Definition canvas.h:677
Transform & postMultiply(const Transform &transform)
Definition canvas.h:607
static Transform translated(float tx, float ty)
Definition canvas.h:715
float xScale() const
Definition canvas.h:672
static Transform sheared(float shx, float shy)
Definition canvas.h:708
Transform & operator*=(const Transform &transform)
Definition canvas.h:577
static Transform scaled(float sx, float sy)
Definition canvas.h:701
Rect mapRect(const Rect &rect) const
Definition canvas.h:661
Transform & postTranslate(float tx, float ty)
Definition canvas.h:612
static Transform rotated(float angle, float cx, float cy)
Definition canvas.h:687
Transform & postShear(float shx, float shy)
Definition canvas.h:627
Point mapPoint(float x, float y) const
Definition canvas.h:650
Transform & postRotate(float angle, float cx=0.f, float cy=0.f)
Definition canvas.h:622
const matrix_t & matrix() const
Definition canvas.h:290
Transform(const matrix_t &matrix)
Definition canvas.h:261
Transform operator*(const Transform &transform) const
Definition canvas.h:570
#define NOVASVG_MAX(a, b)
Definition color.h:11
#define NOVASVG_MIN(a, b)
Definition color.h:14
NOVASVG_INLINE void matrix_map(const matrix_t *matrix, float x, float y, float *xx, float *yy)
Transforms the point (x, y) using matrix and stores the result in (xx, yy).
Definition matrix.h:100
NOVASVG_INLINE path_t * path_create(void)
Creates a new path object.
Definition path.h:43
@ NOVASVG_PATH_COMMAND_CUBIC_TO
Draws a cubic Bézier curve to a new point.
Definition render.h:313
@ NOVASVG_PATH_COMMAND_CLOSE
Closes the current path by drawing a line to the starting point.
Definition render.h:314
@ NOVASVG_PATH_COMMAND_LINE_TO
Draws a straight line to a new point.
Definition render.h:312
@ NOVASVG_PATH_COMMAND_MOVE_TO
Moves the current point to a new position.
Definition render.h:311
NOVASVG_INLINE void canvas_set_rgba(canvas_t *canvas, float r, float g, float b, float a)
Sets the current paint to a solid color.
Definition canvas.h:170
texture_type_t
Defines the type of texture, either plain or tiled.
Definition render.h:1294
@ NOVASVG_TEXTURE_TYPE_TILED
Tiled texture.
Definition render.h:1296
@ NOVASVG_TEXTURE_TYPE_PLAIN
Plain texture.
Definition render.h:1295
NOVASVG_INLINE void canvas_fill_rect(canvas_t *canvas, float x, float y, float w, float h)
Fills a rectangle according to the current fill rule.
Definition canvas.h:622
struct novasvg::render::matrix matrix_t
A structure representing a 2D transformation matrix.
Definition novasvg.h:110
NOVASVG_INLINE void canvas_set_matrix(canvas_t *canvas, const matrix_t *matrix)
Resets the current transformation matrix to the specified matrix.
Definition canvas.h:410
NOVASVG_INLINE void canvas_destroy(canvas_t *canvas)
Decreases the reference count and destroys the canvas when it reaches zero.
Definition canvas.h:108
NOVASVG_INLINE void path_move_to(path_t *path, float x, float y)
Moves the current point to a new position.
Definition path.h:93
NOVASVG_INLINE void canvas_set_line_join(canvas_t *canvas, line_join_t line_join)
Sets the line join style.
Definition canvas.h:331
NOVASVG_INLINE void matrix_init_rotate(matrix_t *matrix, float angle)
Initializes a 2D transformation matrix for rotation.
Definition matrix.h:30
NOVASVG_INLINE void path_arc_to(path_t *path, float rx, float ry, float angle, bool large_arc_flag, bool sweep_flag, float x, float y)
Adds an elliptical arc to the path.
Definition path.h:131
NOVASVG_INLINE void path_cubic_to(path_t *path, float x1, float y1, float x2, float y2, float x3, float y3)
Adds a cubic Bézier curve to the path.
Definition path.h:120
int surface_get_width(const surface_t *surface)
Gets the width of the surface.
Definition surface.h:7943
NOVASVG_INLINE float path_extents(const path_t *path, rect_t *extents, bool tight)
Computes the bounding box and total length of the path.
Definition path.h:701
struct surface surface_t
Represents an image surface for drawing operations.
Definition render.h:1152
NOVASVG_INLINE void canvas_stroke_path(canvas_t *canvas, const path_t *path)
Strokes a path with the current stroke settings.
Definition canvas.h:643
int surface_get_height(const surface_t *surface)
Gets the height of the surface.
Definition surface.h:7948
NOVASVG_INLINE void canvas_save(canvas_t *canvas)
Saves the current state of the canvas.
Definition canvas.h:142
NOVASVG_INLINE float canvas_stroke_text(canvas_t *canvas, const void *text, int length, text_encoding_t encoding, float x, float y)
Strokes a text at the specified origin.
Definition canvas.h:703
NOVASVG_INLINE void path_destroy(path_t *path)
Decreases the reference count of a path object.
Definition path.h:61
NOVASVG_INLINE void path_close(path_t *path)
Closes the current sub-path.
Definition path.h:217
NOVASVG_INLINE void canvas_set_dash_offset(canvas_t *canvas, float offset)
Sets the dash offset.
Definition canvas.h:357
int surface_get_stride(const surface_t *surface)
Gets the stride of the surface.
Definition surface.h:7953
surface_t * surface_create(int width, int height)
Creates a new image surface with the specified dimensions.
Definition surface.h:7753
NOVASVG_INLINE bool path_parse(path_t *path, const char *data, int length)
Parses SVG path data into a path_t object.
Definition path.h:748
NOVASVG_INLINE void canvas_set_fill_rule(canvas_t *canvas, fill_rule_t winding)
Sets the fill rule.
Definition canvas.h:281
void surface_destroy(surface_t *surface)
Decrements the reference count and destroys the surface if the count reaches zero.
Definition surface.h:7926
NOVASVG_INLINE void matrix_init_identity(matrix_t *matrix)
Initializes a 2D transformation matrix to the identity matrix.
Definition matrix.h:15
NOVASVG_INLINE bool matrix_parse(matrix_t *matrix, const char *data, int length)
Parses an SVG transform string into a matrix.
Definition matrix.h:171
NOVASVG_INLINE void canvas_set_radial_gradient(canvas_t *canvas, float cx, float cy, float cr, float fx, float fy, float fr, spread_method_t spread, const gradient_stop_t *stops, int nstops, const matrix_t *matrix)
Sets the current paint to a radial gradient.
Definition canvas.h:188
line_join_t
Defines the shape used at the corners of paths.
Definition render.h:1464
@ NOVASVG_LINE_JOIN_BEVEL
Beveled join with a flattened corner.
Definition render.h:1467
@ NOVASVG_LINE_JOIN_MITER
Miter join with sharp corners.
Definition render.h:1465
@ NOVASVG_LINE_JOIN_ROUND
Rounded join.
Definition render.h:1466
NOVASVG_INLINE path_t * path_reference(path_t *path)
Increases the reference count of a path object.
Definition path.h:55
fill_rule_t
Defines fill rule types for filling paths.
Definition render.h:1421
@ NOVASVG_FILL_RULE_NON_ZERO
Non-zero winding fill rule.
Definition render.h:1422
@ NOVASVG_FILL_RULE_EVEN_ODD
Even-odd fill rule.
Definition render.h:1423
NOVASVG_INLINE canvas_t * canvas_create(surface_t *surface)
Creates a drawing context on a surface.
Definition canvas.h:87
NOVASVG_INLINE void matrix_init_scale(matrix_t *matrix, float sx, float sy)
Initializes a 2D transformation matrix for scaling.
Definition matrix.h:25
NOVASVG_INLINE bool matrix_invert(const matrix_t *matrix, matrix_t *inverse)
Calculates the inverse of matrix and stores it in inverse.
Definition matrix.h:81
NOVASVG_INLINE void path_add_ellipse(path_t *path, float cx, float cy, float rx, float ry)
Adds an ellipse to the path.
Definition path.h:291
NOVASVG_INLINE void canvas_paint(canvas_t *canvas)
A drawing operator that paints the current clipping region using the current paint.
Definition canvas.h:578
NOVASVG_INLINE void canvas_fill_path(canvas_t *canvas, const path_t *path)
Fills a path according to the current fill rule.
Definition canvas.h:629
NOVASVG_INLINE void path_quad_to(path_t *path, float x1, float y1, float x2, float y2)
Adds a quadratic Bézier curve to the path.
Definition path.h:109
NOVASVG_INLINE void canvas_translate(canvas_t *canvas, float tx, float ty)
Translates the current transformation matrix by offsets tx and ty.
Definition canvas.h:380
line_cap_t
Defines the shape used at the ends of open subpaths.
Definition render.h:1455
@ NOVASVG_LINE_CAP_SQUARE
Square ends at the end of the stroke.
Definition render.h:1458
@ NOVASVG_LINE_CAP_ROUND
Rounded ends at the end of the stroke.
Definition render.h:1457
@ NOVASVG_LINE_CAP_BUTT
Flat edge at the end of the stroke.
Definition render.h:1456
NOVASVG_INLINE void canvas_transform(canvas_t *canvas, const matrix_t *matrix)
Multiplies the current transformation matrix with the specified matrix.
Definition canvas.h:400
NOVASVG_INLINE void path_reset(path_t *path)
Resets the path.
Definition path.h:243
NOVASVG_INLINE void path_add_round_rect(path_t *path, float x, float y, float w, float h, float rx, float ry)
Adds a rounded rectangle to the path.
Definition path.h:263
NOVASVG_INLINE void canvas_set_texture(canvas_t *canvas, surface_t *surface, texture_type_t type, float opacity, const matrix_t *matrix)
Sets the current paint to a texture.
Definition canvas.h:195
operator_t
Defines compositing operations.
Definition render.h:1429
@ NOVASVG_OPERATOR_DST_IN
Destination within source.
Definition render.h:1436
@ NOVASVG_OPERATOR_EXCLUSION
Like difference, with lower contrast.
Definition render.h:1449
@ NOVASVG_OPERATOR_SRC
Source replaces destination.
Definition render.h:1431
@ NOVASVG_OPERATOR_MULTIPLY
Backdrop * source: darkens, white is neutral.
Definition render.h:1444
@ NOVASVG_OPERATOR_LIGHTEN
The lighter of backdrop and source, per channel.
Definition render.h:1447
@ NOVASVG_OPERATOR_DST_OUT
Destination outside source.
Definition render.h:1438
@ NOVASVG_OPERATOR_DARKEN
The darker of backdrop and source, per channel.
Definition render.h:1446
@ NOVASVG_OPERATOR_SRC_OVER
Source is composited over destination.
Definition render.h:1433
@ NOVASVG_OPERATOR_DIFFERENCE
Absolute difference of backdrop and source, per channel.
Definition render.h:1448
@ NOVASVG_OPERATOR_SCREEN
Inverse of multiply: lightens, black is neutral.
Definition render.h:1445
@ NOVASVG_TEXT_ENCODING_UTF32
UTF-32 encoding.
Definition render.h:752
NOVASVG_INLINE void canvas_set_miter_limit(canvas_t *canvas, float miter_limit)
Sets the miter limit.
Definition canvas.h:341
NOVASVG_INLINE void canvas_set_operator(canvas_t *canvas, operator_t op)
Sets the compositing operator.
Definition canvas.h:291
NOVASVG_INLINE void canvas_set_linear_gradient(canvas_t *canvas, float x1, float y1, float x2, float y2, spread_method_t spread, const gradient_stop_t *stops, int nstops, const matrix_t *matrix)
Sets the current paint to a linear gradient.
Definition canvas.h:181
NOVASVG_INLINE int path_get_reference_count(const path_t *path)
Retrieves the reference count of a path object.
Definition path.h:69
NOVASVG_INLINE void canvas_set_line_width(canvas_t *canvas, float line_width)
Sets the line width.
Definition canvas.h:311
NOVASVG_INLINE void canvas_set_line_cap(canvas_t *canvas, line_cap_t line_cap)
Sets the line cap style.
Definition canvas.h:321
NOVASVG_INLINE void path_line_to(path_t *path, float x, float y)
Adds a straight line segment to the path.
Definition path.h:101
NOVASVG_INLINE void matrix_init_shear(matrix_t *matrix, float shx, float shy)
Initializes a 2D transformation matrix for shearing.
Definition matrix.h:37
NOVASVG_INLINE void matrix_multiply(matrix_t *matrix, const matrix_t *left, const matrix_t *right)
Multiplies left and right matrices and stores the result in matrix.
Definition matrix.h:70
spread_method_t
Defines the spread method for gradients.
Definition render.h:1302
@ NOVASVG_SPREAD_METHOD_PAD
Pad the gradient's edges.
Definition render.h:1303
@ NOVASVG_SPREAD_METHOD_REFLECT
Reflect the gradient beyond its bounds.
Definition render.h:1304
@ NOVASVG_SPREAD_METHOD_REPEAT
Repeat the gradient pattern.
Definition render.h:1305
NOVASVG_INLINE void matrix_rotate(matrix_t *matrix, float angle)
Rotates the matrix by the specified angle (in radians).
Definition matrix.h:56
struct canvas canvas_t
Represents a drawing context.
Definition render.h:1473
NOVASVG_INLINE void canvas_set_font(canvas_t *canvas, font_face_t *face, float size)
Sets the font face and size for text rendering on the canvas.
Definition canvas.h:253
NOVASVG_INLINE void canvas_restore(canvas_t *canvas)
Restores the canvas to the most recently saved state.
Definition canvas.h:154
NOVASVG_INLINE void matrix_init(matrix_t *matrix, float a, float b, float c, float d, float e, float f)
Initializes a 2D transformation matrix.
Definition matrix.h:8
surface_t * surface_reference(surface_t *surface)
Increments the reference count for a surface.
Definition surface.h:7920
NOVASVG_INLINE void canvas_set_dash_array(canvas_t *canvas, const float *dashes, int ndashes)
Sets the dash pattern.
Definition canvas.h:367
NOVASVG_INLINE void matrix_init_translate(matrix_t *matrix, float tx, float ty)
Initializes a 2D transformation matrix for translation.
Definition matrix.h:20
NOVASVG_INLINE float canvas_fill_text(canvas_t *canvas, const void *text, int length, text_encoding_t encoding, float x, float y)
Fills a text at the specified origin.
Definition canvas.h:695
NOVASVG_INLINE void matrix_translate(matrix_t *matrix, float tx, float ty)
Adds a translation with offsets tx and ty to the matrix.
Definition matrix.h:42
NOVASVG_INLINE path_t * path_clone(const path_t *path)
Creates a copy of the path.
Definition path.h:619
NOVASVG_INLINE void matrix_map_rect(const matrix_t *matrix, const rect_t *src, rect_t *dst)
Transforms the src rectangle using matrix and stores the result in dst.
Definition matrix.h:118
NOVASVG_INLINE int path_get_elements(const path_t *path, const path_element_t **elements)
Retrieves the elements of a path.
Definition path.h:74
NOVASVG_INLINE void canvas_clip_path(canvas_t *canvas, const path_t *path)
Intersects the current clipping region with a path according to the current fill rule.
Definition canvas.h:657
NOVASVG_INLINE void path_add_rect(path_t *path, float x, float y, float w, float h)
Adds a rectangle to the path.
Definition path.h:252
NOVASVG_INLINE void canvas_clip_rect(canvas_t *canvas, float x, float y, float w, float h)
Intersects the current clipping region with a rectangle according to the current fill rule.
Definition canvas.h:650
unsigned char * surface_get_data(const surface_t *surface)
Gets the pixel data of the surface.
Definition surface.h:7938
Main namespace for the NovaSVG library.
Definition bitmap.h:23
constexpr Point & operator-=(Point &a, const Point &b)
Definition canvas.h:102
LineJoin
Definition canvas.h:40
PathCommand
Definition canvas.h:306
constexpr Point & operator+=(Point &a, const Point &b)
Definition canvas.h:96
constexpr float operator*(const Point &a, const Point &b)
Definition canvas.h:108
SpreadMethod
Definition canvas.h:51
constexpr Point operator-(const Point &a, const Point &b)
Definition canvas.h:86
FillRule
Definition canvas.h:46
LineCap
Definition canvas.h:34
constexpr Point operator+(const Point &a, const Point &b)
Definition canvas.h:81
#define NOVASVG_INLINE
Definition novasvg.h:42
#define NOVASVG_DEG2RAD(x)
Definition render.h:108
A structure representing a 2D transformation matrix.
Definition render.h:138
float d
The vertical scaling factor.
Definition render.h:142
float c
The horizontal shearing factor.
Definition render.h:141
float b
The vertical shearing factor.
Definition render.h:140
float a
The horizontal scaling factor.
Definition render.h:139
A structure representing a point in 2D space.
Definition render.h:114
float y
The y-coordinate of the point.
Definition render.h:116
float x
The x-coordinate of the point.
Definition render.h:115
A structure representing a rectangle in 2D space.
Definition render.h:125
float x
The x-coordinate of the top-left corner of the rectangle.
Definition render.h:126
float w
The width of the rectangle.
Definition render.h:128
float h
The height of the rectangle.
Definition render.h:129
float y
The y-coordinate of the top-left corner of the rectangle.
Definition render.h:127
std::string text
UTF-8; ' ' marks a forced line break ( ,.
point_t point
A coordinate point in the path.
Definition render.h:356
struct novasvg::render::path_element::@14 header
Header for path commands.
int length
Number of elements including the header.
Definition render.h:354
path_command_t command
The path command.
Definition render.h:353