use std::{
ops::{Deref, DerefMut},
rc::Rc,
slice,
};
#[cfg(feature = "use_glib")]
use glib::translate::*;
use crate::{ffi, utils::status_to_result, BorrowError, Error, Format, Surface, SurfaceType};
declare_surface!(ImageSurface, SurfaceType::Image);
impl ImageSurface {
#[doc(alias = "cairo_image_surface_create")]
pub fn create(format: Format, width: i32, height: i32) -> Result<ImageSurface, Error> {
unsafe {
Self::from_raw_full(ffi::cairo_image_surface_create(
format.into(),
width,
height,
))
}
}
#[doc(alias = "cairo_image_surface_create_for_data")]
pub unsafe fn create_for_data_unsafe(
data: *mut u8,
format: Format,
width: i32,
height: i32,
stride: i32,
) -> Result<ImageSurface, Error> {
ImageSurface::from_raw_full(ffi::cairo_image_surface_create_for_data(
data,
format.into(),
width,
height,
stride,
))
}
#[doc(alias = "cairo_image_surface_create_for_data")]
pub fn create_for_data<D: AsMut<[u8]> + 'static>(
data: D,
format: Format,
width: i32,
height: i32,
stride: i32,
) -> Result<ImageSurface, Error> {
let mut data: Box<dyn AsMut<[u8]>> = Box::new(data);
let (ptr, len) = {
let data: &mut [u8] = (*data).as_mut();
(data.as_mut_ptr(), data.len())
};
assert!(len >= (height * stride) as usize);
let result = unsafe {
ImageSurface::from_raw_full(ffi::cairo_image_surface_create_for_data(
ptr,
format.into(),
width,
height,
stride,
))
};
if let Ok(surface) = &result {
static IMAGE_SURFACE_DATA: crate::UserDataKey<Box<dyn AsMut<[u8]>>> =
crate::UserDataKey::new();
surface.set_user_data(&IMAGE_SURFACE_DATA, Rc::new(data))?;
}
result
}
#[doc(alias = "cairo_image_surface_get_data")]
#[doc(alias = "get_data")]
pub fn data(&mut self) -> Result<ImageSurfaceData, BorrowError> {
unsafe {
if ffi::cairo_surface_get_reference_count(self.to_raw_none()) > 1 {
return Err(BorrowError::NonExclusive);
}
self.flush();
let status = ffi::cairo_surface_status(self.to_raw_none());
if let Some(err) = status_to_result(status).err() {
return Err(BorrowError::from(err));
}
if ffi::cairo_image_surface_get_data(self.to_raw_none()).is_null() || is_finished(self)
{
return Err(BorrowError::from(Error::SurfaceFinished));
}
Ok(ImageSurfaceData::new(self))
}
}
pub fn take_data(self) -> Result<ImageSurfaceDataOwned, BorrowError> {
unsafe {
if ffi::cairo_surface_get_reference_count(self.to_raw_none()) > 1 {
return Err(BorrowError::NonExclusive);
}
self.flush();
let status = ffi::cairo_surface_status(self.to_raw_none());
if let Some(err) = status_to_result(status).err() {
return Err(BorrowError::from(err));
}
if ffi::cairo_image_surface_get_data(self.to_raw_none()).is_null() || is_finished(&self)
{
return Err(BorrowError::from(Error::SurfaceFinished));
}
Ok(ImageSurfaceDataOwned { surface: self })
}
}
pub fn with_data<F: FnOnce(&[u8])>(&self, f: F) -> Result<(), BorrowError> {
self.flush();
unsafe {
let status = ffi::cairo_surface_status(self.to_raw_none());
if let Some(err) = status_to_result(status).err() {
return Err(BorrowError::from(err));
}
let ptr = ffi::cairo_image_surface_get_data(self.to_raw_none());
if ptr.is_null() || is_finished(self) {
return Err(BorrowError::from(Error::SurfaceFinished));
}
let len = self.height() as usize * self.stride() as usize;
let data = if len == 0 {
&[]
} else {
slice::from_raw_parts(ptr, len)
};
f(data);
}
Ok(())
}
#[doc(alias = "cairo_image_surface_get_format")]
#[doc(alias = "get_format")]
pub fn format(&self) -> Format {
unsafe { Format::from(ffi::cairo_image_surface_get_format(self.to_raw_none())) }
}
#[doc(alias = "cairo_image_surface_get_height")]
#[doc(alias = "get_height")]
pub fn height(&self) -> i32 {
unsafe { ffi::cairo_image_surface_get_height(self.to_raw_none()) }
}
#[doc(alias = "cairo_image_surface_get_stride")]
#[doc(alias = "get_stride")]
pub fn stride(&self) -> i32 {
unsafe { ffi::cairo_image_surface_get_stride(self.to_raw_none()) }
}
#[doc(alias = "cairo_image_surface_get_width")]
#[doc(alias = "get_width")]
pub fn width(&self) -> i32 {
unsafe { ffi::cairo_image_surface_get_width(self.to_raw_none()) }
}
}
pub struct ImageSurfaceDataOwned {
surface: ImageSurface,
}
unsafe impl Send for ImageSurfaceDataOwned {}
unsafe impl Sync for ImageSurfaceDataOwned {}
impl ImageSurfaceDataOwned {
#[inline]
pub fn into_inner(self) -> ImageSurface {
self.surface
}
}
impl AsRef<[u8]> for ImageSurfaceDataOwned {
#[inline]
fn as_ref(&self) -> &[u8] {
let len = (self.surface.stride() as usize) * (self.surface.height() as usize);
unsafe {
let ptr = ffi::cairo_image_surface_get_data(self.surface.to_raw_none());
if ptr.is_null() || len == 0 {
return &[];
}
slice::from_raw_parts(ptr, len)
}
}
}
impl AsMut<[u8]> for ImageSurfaceDataOwned {
#[inline]
fn as_mut(&mut self) -> &mut [u8] {
let len = (self.surface.stride() as usize) * (self.surface.height() as usize);
unsafe {
let ptr = ffi::cairo_image_surface_get_data(self.surface.to_raw_none());
if ptr.is_null() || len == 0 {
return &mut [];
}
slice::from_raw_parts_mut(ptr, len)
}
}
}
impl Deref for ImageSurfaceDataOwned {
type Target = [u8];
#[inline]
fn deref(&self) -> &Self::Target {
self.as_ref()
}
}
impl DerefMut for ImageSurfaceDataOwned {
#[inline]
fn deref_mut(&mut self) -> &mut Self::Target {
self.as_mut()
}
}
#[derive(Debug)]
pub struct ImageSurfaceData<'a> {
surface: &'a mut ImageSurface,
slice: &'a mut [u8],
dirty: bool,
}
unsafe impl Send for ImageSurfaceData<'_> {}
unsafe impl Sync for ImageSurfaceData<'_> {}
impl<'a> ImageSurfaceData<'a> {
fn new(surface: &'a mut ImageSurface) -> ImageSurfaceData<'a> {
unsafe {
let ptr = ffi::cairo_image_surface_get_data(surface.to_raw_none());
let len = (surface.stride() as usize) * (surface.height() as usize);
ImageSurfaceData {
surface,
slice: if ptr.is_null() || len == 0 {
&mut []
} else {
slice::from_raw_parts_mut(ptr, len)
},
dirty: false,
}
}
}
}
impl Drop for ImageSurfaceData<'_> {
#[inline]
fn drop(&mut self) {
if self.dirty {
self.surface.mark_dirty()
}
}
}
impl Deref for ImageSurfaceData<'_> {
type Target = [u8];
#[inline]
fn deref(&self) -> &[u8] {
self.slice
}
}
impl DerefMut for ImageSurfaceData<'_> {
#[inline]
fn deref_mut(&mut self) -> &mut [u8] {
self.dirty = true;
self.slice
}
}
fn is_finished(surface: &ImageSurface) -> bool {
use super::Context;
Context::new(surface).is_err()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn create_with_invalid_size_yields_error() {
let result = ImageSurface::create(Format::ARgb32, 50000, 50000);
assert!(result.is_err());
}
#[test]
fn create_for_data_with_invalid_stride_yields_error() {
let result = ImageSurface::create_for_data(vec![0u8; 10], Format::ARgb32, 1, 2, 5); assert!(result.is_err());
}
#[test]
fn create_with_valid_size() {
let result = ImageSurface::create(Format::ARgb32, 10, 10);
assert!(result.is_ok());
let result = ImageSurface::create_for_data(vec![0u8; 40 * 10], Format::ARgb32, 10, 10, 40);
assert!(result.is_ok());
}
#[test]
fn no_crash_after_finish() {
let mut surf = ImageSurface::create(Format::ARgb32, 1024, 1024).unwrap();
surf.finish();
assert!(surf.data().is_err());
}
#[test]
fn create_from_owned() {
let result = ImageSurface::create(Format::ARgb32, 10, 10);
assert!(result.is_ok());
let image_surface = result.unwrap();
let stride = image_surface.stride();
let data = image_surface.take_data().unwrap();
let second = ImageSurface::create_for_data(data, Format::ARgb32, 10, 10, stride);
assert!(second.is_ok())
}
#[cfg(feature = "use_glib")]
#[test]
fn surface_gvalues() {
use glib::prelude::*;
let surface = ImageSurface::create(Format::ARgb32, 10, 10).unwrap();
let value = surface.to_value();
assert_eq!(value.get::<ImageSurface>().unwrap().width(), 10);
let _ = surface.to_value();
let surface = Some(surface);
let value = surface.to_value();
assert_eq!(
value
.get::<Option<ImageSurface>>()
.unwrap()
.map(|s| s.width()),
Some(10)
);
let _ = surface.as_ref().to_value();
assert_eq!(
value
.get::<Option<&ImageSurface>>()
.unwrap()
.map(|s| s.width()),
Some(10)
);
}
}