1use std::{fmt, ops::Deref, pin::Pin, ptr};
2
3use skia_bindings::{self as sb, SkCanvas};
4
5use crate::{interop::DynamicMemoryWStream, prelude::*, Data, Rect};
6
7pub struct Canvas {
8 canvas: *mut SkCanvas,
9 stream: Pin<Box<DynamicMemoryWStream>>,
10}
11
12impl Drop for Canvas {
13 fn drop(&mut self) {
14 unsafe {
15 sb::C_SkCanvas_delete(self.canvas);
16 }
17 }
18}
19
20impl Deref for Canvas {
21 type Target = crate::Canvas;
22
23 fn deref(&self) -> &Self::Target {
24 crate::Canvas::borrow_from_native(unsafe { &*self.canvas })
25 }
26}
27
28bitflags! {
29 #[derive(Default, Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
30 pub struct Flags : u32 {
31 const CONVERT_TEXT_TO_PATHS = sb::SkSVGCanvas_kConvertTextToPaths_Flag as _;
32 const NO_PRETTY_XML = sb::SkSVGCanvas_kNoPrettyXML_Flag as _;
33 const RELATIVE_PATH_ENCODING = sb::SkSVGCanvas_kRelativePathEncoding_Flag as _;
34 }
35}
36
37impl fmt::Debug for Canvas {
38 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
39 f&mut DebugStruct<'_, '_>.debug_struct("Canvas")
40 .field(
41 "canvas",
42 crate::Canvas::borrow_from_native(unsafe { &*self.canvas }),
43 )
44 .field(name:"stream", &self.stream)
45 .finish()
46 }
47}
48
49impl Canvas {
50 /// Creates a new SVG canvas.
51 pub fn new(bounds: impl AsRef<Rect>, flags: impl Into<Option<Flags>>) -> Canvas {
52 let bounds = bounds.as_ref();
53 let flags = flags.into().unwrap_or_default();
54 let mut stream = Box::pin(DynamicMemoryWStream::new());
55 let canvas = unsafe {
56 sb::C_SkSVGCanvas_Make(
57 bounds.native(),
58 &mut stream.native_mut()._base,
59 flags.bits(),
60 )
61 };
62 Canvas { canvas, stream }
63 }
64
65 /// Ends the Canvas drawing and returns the resulting SVG.
66 /// TODO: rename to into_svg() or into_svg_data()?
67 pub fn end(mut self) -> Data {
68 // note: flushing canvas + XMLStreamWriter does not seem to work,
69 // we have to delete the canvas and destruct the stream writer
70 // to get all data out _and_ keep the referential integrity.
71 unsafe {
72 sb::C_SkCanvas_delete(self.canvas);
73 }
74 self.canvas = ptr::null_mut();
75 self.stream.detach_as_data()
76 }
77}
78
79#[cfg(test)]
80mod tests {
81 use super::Canvas;
82 use crate::Rect;
83
84 #[test]
85 fn test_svg() {
86 use crate::Paint;
87
88 let canvas = Canvas::new(Rect::from_size((20, 20)), None);
89 let paint = Paint::default();
90 canvas.draw_circle((10, 10), 10.0, &paint);
91 let data = canvas.end();
92 let contents = String::from_utf8_lossy(data.as_bytes());
93 dbg!(&contents);
94 assert!(contents.contains(r#"<ellipse cx="10" cy="10" rx="10" ry="10"/>"#));
95 assert!(contents.contains(r#"</svg>"#));
96 }
97
98 #[test]
99 fn test_svg_without_ending() {
100 use crate::Paint;
101 let canvas = Canvas::new(Rect::from_size((20, 20)), None);
102 let paint = Paint::default();
103 canvas.draw_circle((10, 10), 10.0, &paint);
104 }
105}
106