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