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: *mut 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 std::io::ErrorKind;
156
157 use super::*;
158 use crate::enums::Format;
159
160 struct IoErrorReader;
161
162 impl Read for IoErrorReader {
164 fn read(&mut self, _: &mut [u8]) -> Result<usize, io::Error> {
165 Err(io::Error::new(ErrorKind::Other, "yikes!"))
166 }
167 }
168
169 #[test]
170 fn valid_png_reads_correctly() {
171 let png_data: Vec<u8> = vec![
173 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48,
174 0x44, 0x52, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x08, 0x02, 0x00, 0x00,
175 0x00, 0x90, 0x77, 0x53, 0xde, 0x00, 0x00, 0x00, 0x0c, 0x49, 0x44, 0x41, 0x54, 0x08,
176 0xd7, 0x63, 0xd0, 0xd2, 0xd2, 0x02, 0x00, 0x01, 0x00, 0x00, 0x7f, 0x09, 0xa9, 0x5a,
177 0x4d, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82,
178 ];
179
180 let r = ImageSurface::create_from_png(&mut &png_data[..]);
181 assert!(r.is_ok());
182
183 let mut surface = r.unwrap();
184 assert_eq!(surface.width(), 1);
185 assert_eq!(surface.height(), 1);
186 assert_eq!(surface.format(), Format::Rgb24);
187
188 let data = surface.data().unwrap();
189 assert!(data.len() >= 3);
190
191 let slice = &data[0..3];
192 assert_eq!(slice[0], 42);
193 assert_eq!(slice[1], 42);
194 assert_eq!(slice[2], 42);
195 }
196
197 #[cfg(not(target_os = "macos"))]
198 #[test]
199 fn invalid_png_yields_error() {
200 let png_data: Vec<u8> = vec![
201 0x89, 0x40, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48,
203 0x44, 0x52, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x08, 0x02, 0x00, 0x00,
204 0x00, 0x90, 0x77, 0x53, 0xde, 0x00, 0x00, 0x00, 0x0c, 0x49, 0x44, 0x41, 0x54, 0x08,
205 0xd7, 0x63, 0xd0, 0xd2, 0xd2, 0x02, 0x00, 0x01, 0x00, 0x00, 0x7f, 0x09, 0xa9, 0x5a,
206 0x4d, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82,
207 ];
208
209 match ImageSurface::create_from_png(&mut &png_data[..]) {
210 Err(IoError::Cairo(_)) => (),
211 _ => unreachable!(),
212 }
213 }
214
215 #[cfg(not(target_os = "macos"))]
216 #[test]
217 fn io_error_yields_cairo_read_error() {
218 let mut r = IoErrorReader;
219
220 match ImageSurface::create_from_png(&mut r) {
221 Err(IoError::Cairo(Error::ReadError)) => (),
222 _ => unreachable!(),
223 }
224 }
225}