1use std::{
4 any::Any,
5 io::{self, Read, Write},
6 panic::AssertUnwindSafe,
7 slice,
8};
9
10use crate::ffi;
11use libc::{c_uint, c_void};
12
13use crate::{utils::status_to_result, Error, ImageSurface, IoError, Surface};
14
15struct ReadEnv<'a, R: 'a + Read> {
16 reader: &'a mut R,
17 io_error: Option<io::Error>,
18 unwind_payload: Option<Box<dyn Any + Send + 'static>>,
19}
20
21unsafe extern "C" fn read_func<R: Read>(
22 closure: *mut c_void,
23 data: *mut u8,
24 len: c_uint,
25) -> crate::ffi::cairo_status_t {
26 let read_env: &mut ReadEnv<R> = &mut *(closure as *mut ReadEnv<R>);
27
28 if read_env.io_error.is_some() || read_env.unwind_payload.is_some() {
30 return Error::ReadError.into();
31 }
32
33 let buffer = if data.is_null() || len == 0 {
34 &mut []
35 } else {
36 slice::from_raw_parts_mut(data, len as usize)
37 };
38 let result = std::panic::catch_unwind(AssertUnwindSafe(|| read_env.reader.read_exact(buffer)));
39 match result {
40 Ok(Ok(())) => ffi::STATUS_SUCCESS,
41 Ok(Err(error)) => {
42 read_env.io_error = Some(error);
43 Error::ReadError.into()
44 }
45 Err(payload) => {
46 read_env.unwind_payload = Some(payload);
47 Error::ReadError.into()
48 }
49 }
50}
51
52struct WriteEnv<'a, W: 'a + Write> {
53 writer: &'a mut W,
54 io_error: Option<io::Error>,
55 unwind_payload: Option<Box<dyn Any + Send + 'static>>,
56}
57
58unsafe extern "C" fn write_func<W: Write>(
59 closure: *mut c_void,
60 data: *const u8,
61 len: c_uint,
62) -> crate::ffi::cairo_status_t {
63 let write_env: &mut WriteEnv<W> = &mut *(closure as *mut WriteEnv<W>);
64
65 if write_env.io_error.is_some() || write_env.unwind_payload.is_some() {
67 return Error::WriteError.into();
68 }
69
70 let buffer = if data.is_null() || len == 0 {
71 &[]
72 } else {
73 slice::from_raw_parts(data, len as usize)
74 };
75 let result = std::panic::catch_unwind(AssertUnwindSafe(|| write_env.writer.write_all(buffer)));
76 match result {
77 Ok(Ok(())) => ffi::STATUS_SUCCESS,
78 Ok(Err(error)) => {
79 write_env.io_error = Some(error);
80 Error::WriteError.into()
81 }
82 Err(payload) => {
83 write_env.unwind_payload = Some(payload);
84 Error::WriteError.into()
85 }
86 }
87}
88
89impl ImageSurface {
90 #[doc(alias = "cairo_image_surface_create_from_png_stream")]
91 pub fn create_from_png<R: Read>(stream: &mut R) -> Result<ImageSurface, IoError> {
92 let mut env = ReadEnv {
93 reader: stream,
94 io_error: None,
95 unwind_payload: None,
96 };
97 unsafe {
98 let raw_surface = ffi::cairo_image_surface_create_from_png_stream(
99 Some(read_func::<R>),
100 &mut env as *mut ReadEnv<R> as *mut c_void,
101 );
102
103 let surface = ImageSurface::from_raw_full(raw_surface)?;
104
105 if let Some(payload) = env.unwind_payload {
106 std::panic::resume_unwind(payload)
107 }
108
109 match env.io_error {
110 None => Ok(surface),
111 Some(err) => Err(IoError::Io(err)),
112 }
113 }
114 }
115}
116
117impl Surface {
118 #[doc(alias = "cairo_surface_write_to_png_stream")]
124 #[doc(alias = "cairo_surface_write_to_png")]
125 pub fn write_to_png<W: Write>(&self, stream: &mut W) -> Result<(), IoError> {
126 let mut env = WriteEnv {
127 writer: stream,
128 io_error: None,
129 unwind_payload: None,
130 };
131 let status = unsafe {
132 ffi::cairo_surface_write_to_png_stream(
133 self.to_raw_none(),
134 Some(write_func::<W>),
135 &mut env as *mut WriteEnv<W> as *mut c_void,
136 )
137 };
138
139 if let Some(payload) = env.unwind_payload {
140 std::panic::resume_unwind(payload)
141 }
142
143 match env.io_error {
144 None => match status_to_result(status) {
145 Err(err) => Err(IoError::Cairo(err)),
146 Ok(_) => Ok(()),
147 },
148 Some(err) => Err(IoError::Io(err)),
149 }
150 }
151}
152
153#[cfg(test)]
154mod tests {
155 use super::*;
156 use crate::enums::Format;
157
158 struct IoErrorReader;
159
160 impl Read for IoErrorReader {
162 fn read(&mut self, _: &mut [u8]) -> Result<usize, io::Error> {
163 Err(io::Error::other("yikes!"))
164 }
165 }
166
167 #[test]
168 fn valid_png_reads_correctly() {
169 let png_data: Vec<u8> = vec![
171 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48,
172 0x44, 0x52, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x08, 0x02, 0x00, 0x00,
173 0x00, 0x90, 0x77, 0x53, 0xde, 0x00, 0x00, 0x00, 0x0c, 0x49, 0x44, 0x41, 0x54, 0x08,
174 0xd7, 0x63, 0xd0, 0xd2, 0xd2, 0x02, 0x00, 0x01, 0x00, 0x00, 0x7f, 0x09, 0xa9, 0x5a,
175 0x4d, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82,
176 ];
177
178 let r = ImageSurface::create_from_png(&mut &png_data[..]);
179 assert!(r.is_ok());
180
181 let mut surface = r.unwrap();
182 assert_eq!(surface.width(), 1);
183 assert_eq!(surface.height(), 1);
184 assert_eq!(surface.format(), Format::Rgb24);
185
186 let data = surface.data().unwrap();
187 assert!(data.len() >= 3);
188
189 let slice = &data[0..3];
190 assert_eq!(slice[0], 42);
191 assert_eq!(slice[1], 42);
192 assert_eq!(slice[2], 42);
193 }
194
195 #[cfg(not(target_os = "macos"))]
196 #[test]
197 fn invalid_png_yields_error() {
198 let png_data: Vec<u8> = vec![
199 0x89, 0x40, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48,
201 0x44, 0x52, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x08, 0x02, 0x00, 0x00,
202 0x00, 0x90, 0x77, 0x53, 0xde, 0x00, 0x00, 0x00, 0x0c, 0x49, 0x44, 0x41, 0x54, 0x08,
203 0xd7, 0x63, 0xd0, 0xd2, 0xd2, 0x02, 0x00, 0x01, 0x00, 0x00, 0x7f, 0x09, 0xa9, 0x5a,
204 0x4d, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82,
205 ];
206
207 match ImageSurface::create_from_png(&mut &png_data[..]) {
208 Err(IoError::Cairo(_)) => (),
209 _ => unreachable!(),
210 }
211 }
212
213 #[cfg(not(target_os = "macos"))]
214 #[test]
215 fn io_error_yields_cairo_read_error() {
216 let mut r = IoErrorReader;
217
218 match ImageSurface::create_from_png(&mut r) {
219 Err(IoError::Cairo(Error::ReadError)) => (),
220 _ => unreachable!(),
221 }
222 }
223}