1 | use std::{fmt, ops::Deref, pin::Pin, ptr}; |
2 | |
3 | use skia_bindings::{self as sb, SkCanvas}; |
4 | |
5 | use crate::{interop::DynamicMemoryWStream, prelude::*, Data, Rect}; |
6 | |
7 | pub struct Canvas { |
8 | canvas: *mut SkCanvas, |
9 | stream: Pin<Box<DynamicMemoryWStream>>, |
10 | } |
11 | |
12 | impl Drop for Canvas { |
13 | fn drop(&mut self) { |
14 | unsafe { |
15 | sb::C_SkCanvas_delete(self.canvas); |
16 | } |
17 | } |
18 | } |
19 | |
20 | impl 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 | |
28 | bitflags! { |
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 | |
37 | impl 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 | |
49 | impl 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)] |
80 | mod 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 | |