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