1 // Take a look at the license at the top of the repository in the LICENSE file.
2
3 use std::convert::TryFrom;
4 use std::ops::{Deref, DerefMut};
5 use std::rc::Rc;
6 use std::slice;
7
8 use crate::enums::{Format, SurfaceType};
9 use crate::error::Error;
10 #[cfg(feature = "use_glib")]
11 use glib::translate::*;
12
13 use crate::surface::Surface;
14 use crate::utils::status_to_result;
15 use crate::BorrowError;
16 use std::fmt;
17
18 declare_surface!(ImageSurface, SurfaceType::Image);
19
20 impl ImageSurface {
21 #[doc(alias = "cairo_image_surface_create")]
create(format: Format, width: i32, height: i32) -> Result<ImageSurface, Error>22 pub fn create(format: Format, width: i32, height: i32) -> Result<ImageSurface, Error> {
23 unsafe {
24 Self::from_raw_full(ffi::cairo_image_surface_create(
25 format.into(),
26 width,
27 height,
28 ))
29 }
30 }
31
32 // rustdoc-stripper-ignore-next
33 /// Creates an image surface for the provided pixel data.
34 /// - The pointer `data` is the beginning of the underlying slice,
35 /// and at least `width * stride` succeeding bytes should be allocated.
36 /// - `data` must live longer than any reference to the returned surface.
37 /// - You have to free `data` by yourself.
38 #[doc(alias = "cairo_image_surface_create_for_data")]
create_for_data_unsafe( data: *mut u8, format: Format, width: i32, height: i32, stride: i32, ) -> Result<ImageSurface, Error>39 pub unsafe fn create_for_data_unsafe(
40 data: *mut u8,
41 format: Format,
42 width: i32,
43 height: i32,
44 stride: i32,
45 ) -> Result<ImageSurface, Error> {
46 ImageSurface::from_raw_full(ffi::cairo_image_surface_create_for_data(
47 data,
48 format.into(),
49 width,
50 height,
51 stride,
52 ))
53 }
54
55 #[doc(alias = "cairo_image_surface_create_for_data")]
create_for_data<D: AsMut<[u8]> + 'static>( data: D, format: Format, width: i32, height: i32, stride: i32, ) -> Result<ImageSurface, Error>56 pub fn create_for_data<D: AsMut<[u8]> + 'static>(
57 data: D,
58 format: Format,
59 width: i32,
60 height: i32,
61 stride: i32,
62 ) -> Result<ImageSurface, Error> {
63 let mut data: Box<dyn AsMut<[u8]>> = Box::new(data);
64
65 let (ptr, len) = {
66 let data: &mut [u8] = (*data).as_mut();
67
68 (data.as_mut_ptr(), data.len())
69 };
70
71 assert!(len >= (height * stride) as usize);
72 let result = unsafe {
73 ImageSurface::from_raw_full(ffi::cairo_image_surface_create_for_data(
74 ptr,
75 format.into(),
76 width,
77 height,
78 stride,
79 ))
80 };
81 if let Ok(surface) = &result {
82 static IMAGE_SURFACE_DATA: crate::UserDataKey<Box<dyn AsMut<[u8]>>> =
83 crate::UserDataKey::new();
84 surface.set_user_data(&IMAGE_SURFACE_DATA, Rc::new(data))?;
85 }
86 result
87 }
88
89 #[doc(alias = "cairo_image_surface_get_data")]
90 #[doc(alias = "get_data")]
data(&mut self) -> Result<ImageSurfaceData, BorrowError>91 pub fn data(&mut self) -> Result<ImageSurfaceData, BorrowError> {
92 unsafe {
93 if ffi::cairo_surface_get_reference_count(self.to_raw_none()) > 1 {
94 return Err(BorrowError::NonExclusive);
95 }
96
97 self.flush();
98 let status = ffi::cairo_surface_status(self.to_raw_none());
99 if let Some(err) = status_to_result(status).err() {
100 return Err(BorrowError::from(err));
101 }
102 if ffi::cairo_image_surface_get_data(self.to_raw_none()).is_null() || is_finished(self)
103 {
104 return Err(BorrowError::from(Error::SurfaceFinished));
105 }
106 Ok(ImageSurfaceData::new(self))
107 }
108 }
109
take_data(self) -> Result<ImageSurfaceDataOwned, BorrowError>110 pub fn take_data(self) -> Result<ImageSurfaceDataOwned, BorrowError> {
111 unsafe {
112 if ffi::cairo_surface_get_reference_count(self.to_raw_none()) > 1 {
113 return Err(BorrowError::NonExclusive);
114 }
115
116 self.flush();
117 let status = ffi::cairo_surface_status(self.to_raw_none());
118 if let Some(err) = status_to_result(status).err() {
119 return Err(BorrowError::from(err));
120 }
121 if ffi::cairo_image_surface_get_data(self.to_raw_none()).is_null() || is_finished(&self)
122 {
123 return Err(BorrowError::from(Error::SurfaceFinished));
124 }
125 Ok(ImageSurfaceDataOwned { surface: self })
126 }
127 }
128
with_data<F: FnOnce(&[u8])>(&self, f: F) -> Result<(), BorrowError>129 pub fn with_data<F: FnOnce(&[u8])>(&self, f: F) -> Result<(), BorrowError> {
130 self.flush();
131 unsafe {
132 let status = ffi::cairo_surface_status(self.to_raw_none());
133 if let Some(err) = status_to_result(status).err() {
134 return Err(BorrowError::from(err));
135 }
136 let ptr = ffi::cairo_image_surface_get_data(self.to_raw_none());
137 if ptr.is_null() || is_finished(self) {
138 return Err(BorrowError::from(Error::SurfaceFinished));
139 }
140 let len = self.height() as usize * self.stride() as usize;
141 f(slice::from_raw_parts(ptr, len));
142 }
143 Ok(())
144 }
145
146 #[doc(alias = "cairo_image_surface_get_format")]
147 #[doc(alias = "get_format")]
format(&self) -> Format148 pub fn format(&self) -> Format {
149 unsafe { Format::from(ffi::cairo_image_surface_get_format(self.to_raw_none())) }
150 }
151
152 #[doc(alias = "cairo_image_surface_get_height")]
153 #[doc(alias = "get_height")]
height(&self) -> i32154 pub fn height(&self) -> i32 {
155 unsafe { ffi::cairo_image_surface_get_height(self.to_raw_none()) }
156 }
157
158 #[doc(alias = "cairo_image_surface_get_stride")]
159 #[doc(alias = "get_stride")]
stride(&self) -> i32160 pub fn stride(&self) -> i32 {
161 unsafe { ffi::cairo_image_surface_get_stride(self.to_raw_none()) }
162 }
163
164 #[doc(alias = "cairo_image_surface_get_width")]
165 #[doc(alias = "get_width")]
width(&self) -> i32166 pub fn width(&self) -> i32 {
167 unsafe { ffi::cairo_image_surface_get_width(self.to_raw_none()) }
168 }
169 }
170
171 pub struct ImageSurfaceDataOwned {
172 surface: ImageSurface,
173 }
174
175 impl AsRef<[u8]> for ImageSurfaceDataOwned {
as_ref(&self) -> &[u8]176 fn as_ref(&self) -> &[u8] {
177 let len = (self.surface.stride() as usize) * (self.surface.height() as usize);
178 unsafe {
179 let ptr = ffi::cairo_image_surface_get_data(self.surface.to_raw_none());
180 debug_assert!(!ptr.is_null());
181 slice::from_raw_parts(ptr, len)
182 }
183 }
184 }
185
186 impl AsMut<[u8]> for ImageSurfaceDataOwned {
as_mut(&mut self) -> &mut [u8]187 fn as_mut(&mut self) -> &mut [u8] {
188 let len = (self.surface.stride() as usize) * (self.surface.height() as usize);
189 unsafe {
190 let ptr = ffi::cairo_image_surface_get_data(self.surface.to_raw_none());
191 debug_assert!(!ptr.is_null());
192 slice::from_raw_parts_mut(ptr, len)
193 }
194 }
195 }
196
197 impl Deref for ImageSurfaceDataOwned {
198 type Target = [u8];
199
deref(&self) -> &Self::Target200 fn deref(&self) -> &Self::Target {
201 self.as_ref()
202 }
203 }
204
205 impl DerefMut for ImageSurfaceDataOwned {
deref_mut(&mut self) -> &mut Self::Target206 fn deref_mut(&mut self) -> &mut Self::Target {
207 self.as_mut()
208 }
209 }
210
211 #[derive(Debug)]
212 pub struct ImageSurfaceData<'a> {
213 surface: &'a mut ImageSurface,
214 slice: &'a mut [u8],
215 dirty: bool,
216 }
217
218 impl<'a> ImageSurfaceData<'a> {
new(surface: &'a mut ImageSurface) -> ImageSurfaceData<'a>219 fn new(surface: &'a mut ImageSurface) -> ImageSurfaceData<'a> {
220 unsafe {
221 let ptr = ffi::cairo_image_surface_get_data(surface.to_raw_none());
222 debug_assert!(!ptr.is_null());
223 let len = (surface.stride() as usize) * (surface.height() as usize);
224 ImageSurfaceData {
225 surface,
226 slice: slice::from_raw_parts_mut(ptr, len),
227 dirty: false,
228 }
229 }
230 }
231 }
232
233 impl<'a> Drop for ImageSurfaceData<'a> {
drop(&mut self)234 fn drop(&mut self) {
235 if self.dirty {
236 self.surface.mark_dirty()
237 }
238 }
239 }
240
241 impl<'a> Deref for ImageSurfaceData<'a> {
242 type Target = [u8];
243
deref(&self) -> &[u8]244 fn deref(&self) -> &[u8] {
245 self.slice
246 }
247 }
248
249 impl<'a> DerefMut for ImageSurfaceData<'a> {
deref_mut(&mut self) -> &mut [u8]250 fn deref_mut(&mut self) -> &mut [u8] {
251 self.dirty = true;
252 self.slice
253 }
254 }
255
256 impl<'a> fmt::Display for ImageSurfaceData<'a> {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result257 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
258 write!(f, "ImageSurfaceData")
259 }
260 }
261
262 // Workaround for cairo not having a direct way to check if the surface is finished.
263 // See: https://gitlab.freedesktop.org/cairo/cairo/-/issues/406
is_finished(surface: &ImageSurface) -> bool264 fn is_finished(surface: &ImageSurface) -> bool {
265 use super::Context;
266 Context::new(surface).is_err()
267 }
268
269 #[cfg(test)]
270 mod tests {
271 use super::*;
272
273 #[test]
create_with_invalid_size_yields_error()274 fn create_with_invalid_size_yields_error() {
275 let result = ImageSurface::create(Format::ARgb32, 50000, 50000);
276 assert!(result.is_err());
277 }
278
279 #[test]
create_for_data_with_invalid_stride_yields_error()280 fn create_for_data_with_invalid_stride_yields_error() {
281 let result = ImageSurface::create_for_data(vec![0u8; 10], Format::ARgb32, 1, 2, 5); // unaligned stride
282 assert!(result.is_err());
283 }
284
285 #[test]
create_with_valid_size()286 fn create_with_valid_size() {
287 let result = ImageSurface::create(Format::ARgb32, 10, 10);
288 assert!(result.is_ok());
289
290 let result = ImageSurface::create_for_data(vec![0u8; 40 * 10], Format::ARgb32, 10, 10, 40);
291 assert!(result.is_ok());
292 }
293
294 #[test]
no_crash_after_finish()295 fn no_crash_after_finish() {
296 let mut surf = ImageSurface::create(Format::ARgb32, 1024, 1024).unwrap();
297
298 surf.finish();
299
300 assert!(surf.data().is_err());
301 }
302
303 #[test]
create_from_owned()304 fn create_from_owned() {
305 let result = ImageSurface::create(Format::ARgb32, 10, 10);
306 assert!(result.is_ok());
307 let image_surface = result.unwrap();
308 let stride = image_surface.stride();
309 let data = image_surface.take_data().unwrap();
310 let second = ImageSurface::create_for_data(data, Format::ARgb32, 10, 10, stride);
311 assert!(second.is_ok())
312 }
313 }
314