use enums::MeshCorner;
use enums::{Extend, Filter, PatternType, Status};
use ffi;
use ffi::{cairo_pattern_t, cairo_surface_t};
use libc::{c_double, c_int, c_uint};
use std::convert::TryFrom;
use std::fmt;
use std::ops::Deref;
use std::ptr;
use {Matrix, Path, 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,
}
pub fn to_raw_none(&self) -> *mut cairo_pattern_t {
self.pointer
}
pub unsafe fn from_raw_none(pointer: *mut cairo_pattern_t) -> Pattern {
ffi::cairo_pattern_reference(pointer);
Self::from_raw_full(pointer)
}
pub unsafe fn from_raw_full(pointer: *mut cairo_pattern_t) -> Pattern {
Pattern { pointer }
}
pub fn get_type(&self) -> PatternType {
unsafe { ffi::cairo_pattern_get_type(self.pointer).into() }
}
pub fn ensure_status(&self) {
self.status().ensure_valid();
}
pub fn status(&self) -> Status {
unsafe { Status::from(ffi::cairo_pattern_status(self.pointer)) }
}
pub fn get_reference_count(&self) -> isize {
unsafe { ffi::cairo_pattern_get_reference_count(self.pointer) as isize }
}
pub fn set_extend(&self, extend: Extend) {
unsafe { ffi::cairo_pattern_set_extend(self.pointer, extend.into()) }
}
pub fn get_extend(&self) -> Extend {
unsafe { Extend::from(ffi::cairo_pattern_get_extend(self.pointer)) }
}
pub fn set_filter(&self, filter: Filter) {
unsafe { ffi::cairo_pattern_set_filter(self.pointer, filter.into()) }
}
pub fn get_filter(&self) -> Filter {
unsafe { Filter::from(ffi::cairo_pattern_get_filter(self.pointer)) }
}
pub fn set_matrix(&self, matrix: Matrix) {
unsafe { ffi::cairo_pattern_set_matrix(self.pointer, matrix.ptr()) }
}
pub fn get_matrix(&self) -> Matrix {
let mut matrix = Matrix::null();
unsafe {
ffi::cairo_pattern_get_matrix(self.pointer, matrix.mut_ptr());
}
matrix
}
}
impl Clone for Pattern {
fn clone(&self) -> Self {
Pattern {
pointer: unsafe { ffi::cairo_pattern_reference(self.pointer) },
}
}
}
impl Drop for Pattern {
fn drop(&mut self) {
unsafe { ffi::cairo_pattern_destroy(self.pointer) }
}
}
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.get_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;
fn deref(&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 {
pub fn from_rgb(red: f64, green: f64, blue: f64) -> SolidPattern {
unsafe {
SolidPattern(Pattern::from_raw_full(ffi::cairo_pattern_create_rgb(
red, green, blue,
)))
}
}
pub fn from_rgba(red: f64, green: f64, blue: f64, alpha: f64) -> SolidPattern {
unsafe {
SolidPattern(Pattern::from_raw_full(ffi::cairo_pattern_create_rgba(
red, green, blue, alpha,
)))
}
}
pub fn get_rgba(&self) -> (f64, f64, f64, f64) {
unsafe {
let mut red = 0.0;
let mut green = 0.0;
let mut blue = 0.0;
let mut alpha = 0.0;
Status::from(ffi::cairo_pattern_get_rgba(
self.pointer,
&mut red,
&mut green,
&mut blue,
&mut alpha,
))
.ensure_valid();
(red, green, blue, alpha)
}
}
}
pattern_type!(Gradient);
convert!(Pattern => Gradient = LinearGradient | RadialGradient);
impl Gradient {
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) }
}
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)
}
}
pub fn get_color_stop_count(&self) -> isize {
unsafe {
let mut count = 0;
let result = ffi::cairo_pattern_get_color_stop_count(self.pointer, &mut count);
Status::from(result).ensure_valid();
count as isize
}
}
pub fn get_color_stop_rgba(&self, index: isize) -> (f64, f64, f64, f64, f64) {
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;
Status::from(ffi::cairo_pattern_get_color_stop_rgba(
self.pointer,
index as c_int,
&mut offset,
&mut red,
&mut green,
&mut blue,
&mut alpha,
))
.ensure_valid();
(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;
fn deref(&self) -> &Gradient {
&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 {
pub fn new(x0: f64, y0: f64, x1: f64, y1: f64) -> LinearGradient {
unsafe {
LinearGradient(Gradient(Pattern::from_raw_full(
ffi::cairo_pattern_create_linear(x0, y0, x1, y1),
)))
}
}
pub fn get_linear_points(&self) -> (f64, f64, f64, f64) {
unsafe {
let mut x0 = 0.0;
let mut y0 = 0.0;
let mut x1 = 0.0;
let mut y1 = 0.0;
Status::from(ffi::cairo_pattern_get_linear_points(
self.pointer,
&mut x0,
&mut y0,
&mut x1,
&mut y1,
))
.ensure_valid();
(x0, y0, x1, y1)
}
}
}
gradient_type!(RadialGradient);
impl RadialGradient {
pub fn new(x0: f64, y0: f64, r0: f64, x1: f64, y1: f64, r1: f64) -> RadialGradient {
unsafe {
RadialGradient(Gradient(Pattern::from_raw_full(
ffi::cairo_pattern_create_radial(x0, y0, r0, x1, y1, r1),
)))
}
}
pub fn get_radial_circles(&self) -> (f64, f64, f64, f64, f64, f64) {
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;
Status::from(ffi::cairo_pattern_get_radial_circles(
self.pointer,
&mut x0,
&mut y0,
&mut r0,
&mut x1,
&mut y1,
&mut r1,
))
.ensure_valid();
(x0, y0, r0, x1, y1, r1)
}
}
}
pattern_type!(SurfacePattern = Surface);
impl SurfacePattern {
pub fn create(surface: &Surface) -> SurfacePattern {
unsafe {
SurfacePattern(Pattern::from_raw_full(
ffi::cairo_pattern_create_for_surface(surface.to_raw_none()),
))
}
}
pub fn get_surface(&self) -> Surface {
unsafe {
let mut surface_ptr: *mut cairo_surface_t = ptr::null_mut();
Status::from(ffi::cairo_pattern_get_surface(
self.pointer,
&mut surface_ptr,
))
.ensure_valid();
Surface::from_raw_none(surface_ptr)
}
}
}
pattern_type!(Mesh = Mesh);
impl Mesh {
pub fn new() -> Mesh {
unsafe { Mesh(Pattern::from_raw_full(ffi::cairo_pattern_create_mesh())) }
}
pub fn begin_patch(&self) {
unsafe { ffi::cairo_mesh_pattern_begin_patch(self.pointer) }
self.ensure_status();
}
pub fn end_patch(&self) {
unsafe { ffi::cairo_mesh_pattern_end_patch(self.pointer) }
self.ensure_status();
}
pub fn move_to(&self, x: f64, y: f64) {
unsafe { ffi::cairo_mesh_pattern_move_to(self.pointer, x, y) }
self.ensure_status();
}
pub fn line_to(&self, x: f64, y: f64) {
unsafe { ffi::cairo_mesh_pattern_line_to(self.pointer, x, y) }
self.ensure_status();
}
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) }
self.ensure_status();
}
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) }
self.ensure_status();
}
pub fn get_control_point(&self, patch_num: usize, corner: MeshCorner) -> (f64, f64) {
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::from(status).ensure_valid();
(x, y)
}
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,
)
}
self.ensure_status();
}
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,
)
}
self.ensure_status();
}
pub fn get_corner_color_rgba(
&self,
patch_num: usize,
corner: MeshCorner,
) -> (f64, f64, f64, f64) {
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::from(status).ensure_valid();
(red, green, blue, alpha)
}
pub fn get_patch_count(&self) -> usize {
let mut count: c_uint = 0;
unsafe {
Status::from(ffi::cairo_mesh_pattern_get_patch_count(
self.pointer,
&mut count,
))
.ensure_valid();
}
count as usize
}
pub fn get_path(&self, patch_num: usize) -> Path {
let path: Path = unsafe {
Path::from_raw_full(ffi::cairo_mesh_pattern_get_path(
self.pointer,
patch_num as c_uint,
))
};
path.ensure_status();
path
}
}
#[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());
}