use std::{convert::TryFrom, fmt, ops::Deref, ptr};
use libc::{c_double, c_int, c_uint};
use crate::{
ffi::{cairo_pattern_t, cairo_surface_t},
utils::status_to_result,
Error, Extend, Filter, Matrix, MeshCorner, Path, PatternType, Surface,
};
#[derive(Debug)]
pub struct Pattern {
pointer: *mut cairo_pattern_t,
}
impl fmt::Display for Pattern {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Pattern")
}
}
impl Pattern {
user_data_methods! {
ffi::cairo_pattern_get_user_data,
ffi::cairo_pattern_set_user_data,
}
#[inline]
pub fn to_raw_none(&self) -> *mut cairo_pattern_t {
self.pointer
}
#[inline]
pub unsafe fn from_raw_none(pointer: *mut cairo_pattern_t) -> Pattern {
ffi::cairo_pattern_reference(pointer);
Self::from_raw_full(pointer)
}
#[inline]
pub unsafe fn from_raw_full(pointer: *mut cairo_pattern_t) -> Pattern {
Self { pointer }
}
#[doc(alias = "cairo_pattern_get_type")]
#[doc(alias = "get_type")]
pub fn type_(&self) -> PatternType {
unsafe { ffi::cairo_pattern_get_type(self.pointer).into() }
}
#[doc(alias = "cairo_pattern_get_reference_count")]
#[doc(alias = "get_reference_count")]
pub fn reference_count(&self) -> isize {
unsafe { ffi::cairo_pattern_get_reference_count(self.pointer) as isize }
}
#[doc(alias = "cairo_pattern_set_extend")]
pub fn set_extend(&self, extend: Extend) {
unsafe { ffi::cairo_pattern_set_extend(self.pointer, extend.into()) }
}
#[doc(alias = "cairo_pattern_get_extend")]
#[doc(alias = "get_extend")]
pub fn extend(&self) -> Extend {
unsafe { Extend::from(ffi::cairo_pattern_get_extend(self.pointer)) }
}
#[doc(alias = "cairo_pattern_set_filter")]
pub fn set_filter(&self, filter: Filter) {
unsafe { ffi::cairo_pattern_set_filter(self.pointer, filter.into()) }
}
#[doc(alias = "cairo_pattern_get_filter")]
#[doc(alias = "get_filter")]
pub fn filter(&self) -> Filter {
unsafe { Filter::from(ffi::cairo_pattern_get_filter(self.pointer)) }
}
#[doc(alias = "cairo_pattern_set_matrix")]
pub fn set_matrix(&self, matrix: Matrix) {
unsafe { ffi::cairo_pattern_set_matrix(self.pointer, matrix.ptr()) }
}
#[doc(alias = "cairo_pattern_get_matrix")]
#[doc(alias = "get_matrix")]
pub fn matrix(&self) -> Matrix {
let mut matrix = Matrix::null();
unsafe {
ffi::cairo_pattern_get_matrix(self.pointer, matrix.mut_ptr());
}
matrix
}
#[doc(alias = "cairo_pattern_status")]
pub fn status(&self) -> Result<(), Error> {
let status = unsafe { ffi::cairo_pattern_status(self.pointer) };
status_to_result(status)
}
}
impl Clone for Pattern {
#[inline]
fn clone(&self) -> Self {
Self {
pointer: unsafe { ffi::cairo_pattern_reference(self.pointer) },
}
}
}
impl Drop for Pattern {
#[inline]
fn drop(&mut self) {
unsafe { ffi::cairo_pattern_destroy(self.pointer) }
}
}
impl AsRef<Pattern> for Pattern {
#[inline]
fn as_ref(&self) -> &Pattern {
self
}
}
macro_rules! convert {
($source: ident => $dest: ident = $( $variant: ident )|+ $( ($intermediate: ident) )*) => {
impl TryFrom<$source> for $dest {
type Error = $source;
fn try_from(pattern: $source) -> Result<Self, $source> {
if $( pattern.type_() == PatternType::$variant )||+ {
$(
let pattern = $intermediate(pattern);
)*
Ok($dest(pattern))
}
else {
Err(pattern)
}
}
}
};
}
macro_rules! pattern_type(
($pattern_type:ident $( = $variant: ident)*) => (
#[derive(Debug, Clone)]
pub struct $pattern_type(Pattern);
impl Deref for $pattern_type {
type Target = Pattern;
#[inline]
fn deref(&self) -> &Pattern {
&self.0
}
}
impl AsRef<Pattern> for $pattern_type {
#[inline]
fn as_ref(&self) -> &Pattern {
&self.0
}
}
impl fmt::Display for $pattern_type {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, stringify!($pattern_type))
}
}
$(
convert!(Pattern => $pattern_type = $variant);
)*
);
);
pattern_type!(SolidPattern = Solid);
impl SolidPattern {
#[doc(alias = "cairo_pattern_create_rgb")]
pub fn from_rgb(red: f64, green: f64, blue: f64) -> Self {
unsafe {
Self(Pattern::from_raw_full(ffi::cairo_pattern_create_rgb(
red, green, blue,
)))
}
}
#[doc(alias = "cairo_pattern_create_rgba")]
pub fn from_rgba(red: f64, green: f64, blue: f64, alpha: f64) -> Self {
unsafe {
Self(Pattern::from_raw_full(ffi::cairo_pattern_create_rgba(
red, green, blue, alpha,
)))
}
}
#[doc(alias = "cairo_pattern_get_rgba")]
#[doc(alias = "get_rgba")]
pub fn rgba(&self) -> Result<(f64, f64, f64, f64), Error> {
unsafe {
let mut red = 0.0;
let mut green = 0.0;
let mut blue = 0.0;
let mut alpha = 0.0;
let status = ffi::cairo_pattern_get_rgba(
self.pointer,
&mut red,
&mut green,
&mut blue,
&mut alpha,
);
status_to_result(status)?;
Ok((red, green, blue, alpha))
}
}
}
pattern_type!(Gradient);
convert!(Pattern => Gradient = LinearGradient | RadialGradient);
impl Gradient {
#[doc(alias = "cairo_pattern_add_color_stop_rgb")]
pub fn add_color_stop_rgb(&self, offset: f64, red: f64, green: f64, blue: f64) {
unsafe { ffi::cairo_pattern_add_color_stop_rgb(self.pointer, offset, red, green, blue) }
}
#[doc(alias = "cairo_pattern_add_color_stop_rgba")]
pub fn add_color_stop_rgba(&self, offset: f64, red: f64, green: f64, blue: f64, alpha: f64) {
unsafe {
ffi::cairo_pattern_add_color_stop_rgba(self.pointer, offset, red, green, blue, alpha)
}
}
#[doc(alias = "cairo_pattern_get_color_stop_count")]
#[doc(alias = "get_color_stop_count")]
pub fn color_stop_count(&self) -> Result<isize, Error> {
unsafe {
let mut count = 0;
let status = ffi::cairo_pattern_get_color_stop_count(self.pointer, &mut count);
status_to_result(status)?;
Ok(count as isize)
}
}
#[doc(alias = "cairo_pattern_get_color_stop_rgba")]
#[doc(alias = "get_color_stop_rgba")]
pub fn color_stop_rgba(&self, index: isize) -> Result<(f64, f64, f64, f64, f64), Error> {
unsafe {
let mut offset = 0.0;
let mut red = 0.0;
let mut green = 0.0;
let mut blue = 0.0;
let mut alpha = 0.0;
let status = ffi::cairo_pattern_get_color_stop_rgba(
self.pointer,
index as c_int,
&mut offset,
&mut red,
&mut green,
&mut blue,
&mut alpha,
);
status_to_result(status)?;
Ok((offset, red, green, blue, alpha))
}
}
}
macro_rules! gradient_type {
($gradient_type: ident) => {
#[derive(Debug, Clone)]
pub struct $gradient_type(Gradient);
impl Deref for $gradient_type {
type Target = Gradient;
#[inline]
fn deref(&self) -> &Gradient {
&self.0
}
}
impl AsRef<Gradient> for $gradient_type {
#[inline]
fn as_ref(&self) -> &Gradient {
&self.0
}
}
impl AsRef<Pattern> for $gradient_type {
#[inline]
fn as_ref(&self) -> &Pattern {
&self.0
}
}
impl fmt::Display for $gradient_type {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, stringify!($gradient_type))
}
}
convert!(Pattern => $gradient_type = $gradient_type (Gradient));
convert!(Gradient => $gradient_type = $gradient_type);
}
}
gradient_type!(LinearGradient);
impl LinearGradient {
#[doc(alias = "cairo_pattern_create_linear")]
pub fn new(x0: f64, y0: f64, x1: f64, y1: f64) -> Self {
unsafe {
Self(Gradient(Pattern::from_raw_full(
ffi::cairo_pattern_create_linear(x0, y0, x1, y1),
)))
}
}
#[doc(alias = "cairo_pattern_get_linear_points")]
#[doc(alias = "get_linear_points")]
pub fn linear_points(&self) -> Result<(f64, f64, f64, f64), Error> {
unsafe {
let mut x0 = 0.0;
let mut y0 = 0.0;
let mut x1 = 0.0;
let mut y1 = 0.0;
let status = ffi::cairo_pattern_get_linear_points(
self.pointer,
&mut x0,
&mut y0,
&mut x1,
&mut y1,
);
status_to_result(status)?;
Ok((x0, y0, x1, y1))
}
}
}
gradient_type!(RadialGradient);
impl RadialGradient {
#[doc(alias = "cairo_pattern_create_radial")]
pub fn new(x0: f64, y0: f64, r0: f64, x1: f64, y1: f64, r1: f64) -> Self {
unsafe {
Self(Gradient(Pattern::from_raw_full(
ffi::cairo_pattern_create_radial(x0, y0, r0, x1, y1, r1),
)))
}
}
#[doc(alias = "cairo_pattern_get_radial_circles")]
#[doc(alias = "get_radial_circles")]
pub fn radial_circles(&self) -> Result<(f64, f64, f64, f64, f64, f64), Error> {
unsafe {
let mut x0 = 0.0;
let mut y0 = 0.0;
let mut r0 = 0.0;
let mut x1 = 0.0;
let mut y1 = 0.0;
let mut r1 = 0.0;
let status = ffi::cairo_pattern_get_radial_circles(
self.pointer,
&mut x0,
&mut y0,
&mut r0,
&mut x1,
&mut y1,
&mut r1,
);
status_to_result(status)?;
Ok((x0, y0, r0, x1, y1, r1))
}
}
}
pattern_type!(SurfacePattern = Surface);
impl SurfacePattern {
#[doc(alias = "cairo_pattern_create_for_surface")]
pub fn create(surface: impl AsRef<Surface>) -> Self {
unsafe {
Self(Pattern::from_raw_full(
ffi::cairo_pattern_create_for_surface(surface.as_ref().to_raw_none()),
))
}
}
#[doc(alias = "cairo_pattern_get_surface")]
#[doc(alias = "get_surface")]
pub fn surface(&self) -> Result<Surface, Error> {
unsafe {
let mut surface_ptr: *mut cairo_surface_t = ptr::null_mut();
let status = ffi::cairo_pattern_get_surface(self.pointer, &mut surface_ptr);
status_to_result(status)?;
Ok(Surface::from_raw_none(surface_ptr))
}
}
}
pattern_type!(Mesh = Mesh);
impl Mesh {
#[doc(alias = "cairo_pattern_create_mesh")]
pub fn new() -> Self {
unsafe { Self(Pattern::from_raw_full(ffi::cairo_pattern_create_mesh())) }
}
#[doc(alias = "cairo_mesh_pattern_begin_patch")]
pub fn begin_patch(&self) {
unsafe { ffi::cairo_mesh_pattern_begin_patch(self.pointer) }
}
#[doc(alias = "cairo_mesh_pattern_end_patch")]
pub fn end_patch(&self) {
unsafe { ffi::cairo_mesh_pattern_end_patch(self.pointer) }
}
#[doc(alias = "cairo_mesh_pattern_move_to")]
pub fn move_to(&self, x: f64, y: f64) {
unsafe { ffi::cairo_mesh_pattern_move_to(self.pointer, x, y) }
}
#[doc(alias = "cairo_mesh_pattern_line_to")]
pub fn line_to(&self, x: f64, y: f64) {
unsafe { ffi::cairo_mesh_pattern_line_to(self.pointer, x, y) }
}
#[doc(alias = "cairo_mesh_pattern_curve_to")]
pub fn curve_to(&self, x1: f64, y1: f64, x2: f64, y2: f64, x3: f64, y3: f64) {
unsafe { ffi::cairo_mesh_pattern_curve_to(self.pointer, x1, y1, x2, y2, x3, y3) }
}
#[doc(alias = "cairo_mesh_pattern_set_control_point")]
pub fn set_control_point(&self, corner: MeshCorner, x: f64, y: f64) {
unsafe { ffi::cairo_mesh_pattern_set_control_point(self.pointer, corner.into(), x, y) }
}
#[doc(alias = "cairo_mesh_pattern_get_control_point")]
#[doc(alias = "get_control_point")]
pub fn control_point(&self, patch_num: usize, corner: MeshCorner) -> Result<(f64, f64), Error> {
let mut x: c_double = 0.0;
let mut y: c_double = 0.0;
let status = unsafe {
ffi::cairo_mesh_pattern_get_control_point(
self.pointer,
patch_num as c_uint,
corner.into(),
&mut x,
&mut y,
)
};
status_to_result(status)?;
Ok((x, y))
}
#[doc(alias = "cairo_mesh_pattern_set_corner_color_rgb")]
pub fn set_corner_color_rgb(&self, corner: MeshCorner, red: f64, green: f64, blue: f64) {
unsafe {
ffi::cairo_mesh_pattern_set_corner_color_rgb(
self.pointer,
corner.into(),
red,
green,
blue,
)
}
}
#[doc(alias = "cairo_mesh_pattern_set_corner_color_rgba")]
pub fn set_corner_color_rgba(
&self,
corner: MeshCorner,
red: f64,
green: f64,
blue: f64,
alpha: f64,
) {
unsafe {
ffi::cairo_mesh_pattern_set_corner_color_rgba(
self.pointer,
corner.into(),
red,
green,
blue,
alpha,
)
}
}
#[doc(alias = "cairo_mesh_pattern_get_corner_color_rgba")]
#[doc(alias = "get_corner_color_rgba")]
pub fn corner_color_rgba(
&self,
patch_num: usize,
corner: MeshCorner,
) -> Result<(f64, f64, f64, f64), Error> {
let mut red: c_double = 0.0;
let mut green: c_double = 0.0;
let mut blue: c_double = 0.0;
let mut alpha: c_double = 0.0;
let status = unsafe {
ffi::cairo_mesh_pattern_get_corner_color_rgba(
self.pointer,
patch_num as c_uint,
corner.into(),
&mut red,
&mut green,
&mut blue,
&mut alpha,
)
};
status_to_result(status)?;
Ok((red, green, blue, alpha))
}
#[doc(alias = "cairo_mesh_pattern_get_patch_count")]
#[doc(alias = "get_patch_count")]
pub fn patch_count(&self) -> Result<usize, Error> {
let mut count: c_uint = 0;
unsafe {
let status = ffi::cairo_mesh_pattern_get_patch_count(self.pointer, &mut count);
status_to_result(status)?;
}
Ok(count as usize)
}
#[doc(alias = "cairo_mesh_pattern_get_path")]
#[doc(alias = "get_path")]
pub fn path(&self, patch_num: usize) -> Result<Path, Error> {
let path: Path = unsafe {
Path::from_raw_full(ffi::cairo_mesh_pattern_get_path(
self.pointer,
patch_num as c_uint,
))
};
let status = unsafe {
let ptr: *mut ffi::cairo_path_t = path.as_ptr();
(*ptr).status
};
status_to_result(status)?;
Ok(path)
}
}
impl Default for Mesh {
fn default() -> Self {
Self::new()
}
}
#[test]
fn try_from() {
let linear = LinearGradient::new(0., 0., 1., 1.);
let gradient = Gradient::clone(&linear);
let pattern = Pattern::clone(&linear);
assert!(Gradient::try_from(pattern.clone()).is_ok());
assert!(LinearGradient::try_from(gradient).is_ok());
assert!(LinearGradient::try_from(pattern).is_ok());
}