diff --git a/src/filters.rs b/src/filters.rs new file mode 100644 index 0000000..73a8e6e --- /dev/null +++ b/src/filters.rs @@ -0,0 +1,36 @@ +use ::bindings; + +pub enum FilterType { + UndefinedFilter = bindings::UndefinedFilter as isize, + PointFilter = bindings::PointFilter as isize, + BoxFilter = bindings::BoxFilter as isize, + TriangleFilter = bindings::TriangleFilter as isize, + HermiteFilter = bindings::HermiteFilter as isize, + HanningFilter = bindings::HanningFilter as isize, + HammingFilter = bindings::HammingFilter as isize, + BlackmanFilter = bindings::BlackmanFilter as isize, + GaussianFilter = bindings::GaussianFilter as isize, + QuadraticFilter = bindings::QuadraticFilter as isize, + CubicFilter = bindings::CubicFilter as isize, + CatromFilter = bindings::CatromFilter as isize, + MitchellFilter = bindings::MitchellFilter as isize, + JincFilter = bindings::JincFilter as isize, + SincFilter = bindings::SincFilter as isize, + SincFastFilter = bindings::SincFastFilter as isize, + KaiserFilter = bindings::KaiserFilter as isize, + WelshFilter = bindings::WelshFilter as isize, + ParzenFilter = bindings::ParzenFilter as isize, + BohmanFilter = bindings::BohmanFilter as isize, + BartlettFilter = bindings::BartlettFilter as isize, + LagrangeFilter = bindings::LagrangeFilter as isize, + LanczosFilter = bindings::LanczosFilter as isize, + LanczosSharpFilter = bindings::LanczosSharpFilter as isize, + Lanczos2Filter = bindings::Lanczos2Filter as isize, + Lanczos2SharpFilter = bindings::Lanczos2SharpFilter as isize, + RobidouxFilter = bindings::RobidouxFilter as isize, + RobidouxSharpFilter = bindings::RobidouxSharpFilter as isize, + CosineFilter = bindings::CosineFilter as isize, + SplineFilter = bindings::SplineFilter as isize, + LanczosRadiusFilter = bindings::LanczosRadiusFilter as isize, + SentinelFilter = bindings::SentinelFilter as isize +} diff --git a/src/lib.rs b/src/lib.rs index ff29860..8be898a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -31,197 +31,12 @@ extern crate libc; -use std::ffi::{CStr, CString}; -use std::ptr; -use libc::{c_uint, c_double, c_void}; -use filters::FilterType; +mod wand; +mod bindings { include!(concat!(env!("OUT_DIR"), "/bindings.rs")); } +pub mod filters; -mod bindings { - include!(concat!(env!("OUT_DIR"), "/bindings.rs")); -} +pub use wand::*; -/// MagickWand is a Rustic wrapper to the Rust bindings to ImageMagick. -/// -/// Instantiating a `MagickWand` will construct an ImageMagick "wand" -/// on which operations can be performed via the `MagickWand` functions. -/// When the `MagickWand` is dropped, the ImageMagick wand will be -/// destroyed as well. -pub struct MagickWand { - wand: *mut bindings::MagickWand -} - -impl MagickWand { - - /// Create a new MagickWand instance. This instance will be properly - /// cleaned up once it falls out of scope. - pub fn new() -> MagickWand { - MagickWand { - wand: unsafe { bindings::NewMagickWand() } - } - } - - /// Read the image data from the named file. - pub fn read_image(&self, path: &str) -> Result<(), &'static str> { - let c_name = CString::new(path).unwrap(); - let result = unsafe { - bindings::MagickReadImage(self.wand, c_name.as_ptr()) - }; - match result { - bindings::MagickTrue => Ok(()), - _ => Err("failed to read image") - } - } - - /// Read the image data from the vector of bytes. - pub fn read_image_blob(&self, data: Vec) -> Result<(), &'static str> { - let int_slice = &data[..]; - let size = data.len(); - let result = unsafe { - bindings::MagickReadImageBlob( - self.wand, int_slice.as_ptr() as *const c_void, size as u64) - }; - match result { - bindings::MagickTrue => Ok(()), - _ => Err("failed to read image") - } - } - - /// Retrieve the width of the image. - pub fn get_image_width(&self) -> usize { - unsafe { - bindings::MagickGetImageWidth(self.wand) as usize - } - } - - /// Retrieve the height of the image. - pub fn get_image_height(&self) -> usize { - unsafe { - bindings::MagickGetImageHeight(self.wand) as usize - } - } - - /// Retrieve the named image property value. - pub fn get_image_property(&self, name: &str) -> Result { - let c_name = CString::new(name).unwrap(); - let result = unsafe { - bindings::MagickGetImageProperty(self.wand, c_name.as_ptr()) - }; - let value = if result.is_null() { - Err("missing property") - } else { - // convert (and copy) the C string to a Rust string - let cstr = unsafe { CStr::from_ptr(result) }; - Ok(cstr.to_string_lossy().into_owned()) - }; - unsafe { - bindings::MagickRelinquishMemory(result as *mut c_void); - } - value - } - - /// Resize the image to the specified width and height, using the - /// specified filter type with the specified blur / sharpness factor. - /// - /// blur_factor values greater than 1 create blurriness, while values - /// less than 1 create sharpness. - pub fn resize_image(&self, width: usize, height: usize, - filter: FilterType, blur_factor: f64) { - unsafe { - bindings::MagickResizeImage( - self.wand, width as u64, height as u64, - filter as c_uint, blur_factor as c_double - ); - } - } - - /// Resize the image to find within the given dimensions, maintaining - /// the current aspect ratio. - pub fn fit(&self, width: usize, height: usize) { - let mut width_ratio = width as f64; - width_ratio /= self.get_image_width() as f64; - let mut height_ratio = height as f64; - height_ratio /= self.get_image_height() as f64; - let new_width: usize; - let new_height: usize; - if width_ratio < height_ratio { - new_width = width; - new_height = (self.get_image_height() as f64 * width_ratio) as usize; - } else { - new_width = (self.get_image_width() as f64 * height_ratio) as usize; - new_height = height; - } - unsafe { - bindings::MagickResetIterator(self.wand); - while bindings::MagickNextImage(self.wand) != bindings::MagickFalse { - bindings::MagickResizeImage(self.wand, new_width as u64, new_height as u64, - FilterType::LanczosFilter as c_uint, 1.0); - } - } - } - - /// Detect if the loaded image is not in top-left orientation, and - /// hence should be "auto" oriented so it is suitable for viewing. - pub fn requires_orientation(&self) -> bool { - unsafe { - bindings::MagickGetImageOrientation(self.wand) != bindings::TopLeftOrientation - } - } - - /// Automatically adjusts the loaded image so that its orientation is - /// suitable for viewing (i.e. top-left orientation). - /// - /// Returns `true` if successful or `false` if an error occurred. - pub fn auto_orient(&self) -> bool { - unsafe { - bindings::MagickAutoOrientImage(self.wand) == bindings::MagickTrue - } - } - - /// Write the current image to the provided path. - pub fn write_image(&self, path: &str) -> Result<(), &'static str> { - let c_name = CString::new(path).unwrap(); - let result = unsafe { - bindings::MagickWriteImage(self.wand, c_name.as_ptr()) - }; - match result { - bindings::MagickTrue => Ok(()), - _ => Err("failed to write image") - } - } - - /// Write the image in the desired format to a new blob. - /// - /// The `format` argument may be any ImageMagick supported image - /// format (e.g. GIF, JPEG, PNG, etc). - pub fn write_image_blob(&self, format: &str) -> Result, &'static str> { - let c_format = CString::new(format).unwrap(); - let mut length: u64 = 0; - let blob = unsafe { - bindings::MagickSetImageFormat(self.wand, c_format.as_ptr()); - bindings::MagickResetIterator(self.wand); - bindings::MagickGetImageBlob(self.wand, &mut length) - }; - let mut bytes = Vec::with_capacity(length as usize); - unsafe { - bytes.set_len(length as usize); - ptr::copy_nonoverlapping(blob, bytes.as_mut_ptr(), length as usize); - bindings::MagickRelinquishMemory(blob as *mut c_void); - }; - Ok(bytes) - } -} - -// Automate safe cleanup for MagickWand instances. -impl Drop for MagickWand { - - /// Clear any exceptions and destroy the magic wand. - fn drop(&mut self) { - unsafe { - bindings::MagickClearException(self.wand); - bindings::DestroyMagickWand(self.wand); - } - } -} /// This function must be called before any other ImageMagick operations /// are attempted. This function is safe to be called repeatedly. @@ -244,43 +59,3 @@ pub fn magick_wand_terminus() { } } } - -pub mod filters { - - use bindings; - - pub enum FilterType { - UndefinedFilter = bindings::UndefinedFilter as isize, - PointFilter = bindings::PointFilter as isize, - BoxFilter = bindings::BoxFilter as isize, - TriangleFilter = bindings::TriangleFilter as isize, - HermiteFilter = bindings::HermiteFilter as isize, - HanningFilter = bindings::HanningFilter as isize, - HammingFilter = bindings::HammingFilter as isize, - BlackmanFilter = bindings::BlackmanFilter as isize, - GaussianFilter = bindings::GaussianFilter as isize, - QuadraticFilter = bindings::QuadraticFilter as isize, - CubicFilter = bindings::CubicFilter as isize, - CatromFilter = bindings::CatromFilter as isize, - MitchellFilter = bindings::MitchellFilter as isize, - JincFilter = bindings::JincFilter as isize, - SincFilter = bindings::SincFilter as isize, - SincFastFilter = bindings::SincFastFilter as isize, - KaiserFilter = bindings::KaiserFilter as isize, - WelshFilter = bindings::WelshFilter as isize, - ParzenFilter = bindings::ParzenFilter as isize, - BohmanFilter = bindings::BohmanFilter as isize, - BartlettFilter = bindings::BartlettFilter as isize, - LagrangeFilter = bindings::LagrangeFilter as isize, - LanczosFilter = bindings::LanczosFilter as isize, - LanczosSharpFilter = bindings::LanczosSharpFilter as isize, - Lanczos2Filter = bindings::Lanczos2Filter as isize, - Lanczos2SharpFilter = bindings::Lanczos2SharpFilter as isize, - RobidouxFilter = bindings::RobidouxFilter as isize, - RobidouxSharpFilter = bindings::RobidouxSharpFilter as isize, - CosineFilter = bindings::CosineFilter as isize, - SplineFilter = bindings::SplineFilter as isize, - LanczosRadiusFilter = bindings::LanczosRadiusFilter as isize, - SentinelFilter = bindings::SentinelFilter as isize - } -} diff --git a/src/wand/magick.rs b/src/wand/magick.rs new file mode 100644 index 0000000..a744956 --- /dev/null +++ b/src/wand/magick.rs @@ -0,0 +1,188 @@ +use std::ffi::{CStr, CString}; +use std::ptr; +use libc::{c_uint, c_double, c_void}; +use ::filters::FilterType; +use ::bindings; + +/// MagickWand is a Rustic wrapper to the Rust bindings to ImageMagick. +/// +/// Instantiating a `MagickWand` will construct an ImageMagick "wand" +/// on which operations can be performed via the `MagickWand` functions. +/// When the `MagickWand` is dropped, the ImageMagick wand will be +/// destroyed as well. +pub struct MagickWand { + wand: *mut bindings::MagickWand +} + +impl MagickWand { + + /// Create a new MagickWand instance. This instance will be properly + /// cleaned up once it falls out of scope. + pub fn new() -> MagickWand { + MagickWand { + wand: unsafe { bindings::NewMagickWand() } + } + } + + /// Read the image data from the named file. + pub fn read_image(&self, path: &str) -> Result<(), &'static str> { + let c_name = CString::new(path).unwrap(); + let result = unsafe { + bindings::MagickReadImage(self.wand, c_name.as_ptr()) + }; + match result { + bindings::MagickTrue => Ok(()), + _ => Err("failed to read image") + } + } + + /// Read the image data from the vector of bytes. + pub fn read_image_blob(&self, data: Vec) -> Result<(), &'static str> { + let int_slice = &data[..]; + let size = data.len(); + let result = unsafe { + bindings::MagickReadImageBlob( + self.wand, int_slice.as_ptr() as *const c_void, size as u64) + }; + match result { + bindings::MagickTrue => Ok(()), + _ => Err("failed to read image") + } + } + + /// Retrieve the width of the image. + pub fn get_image_width(&self) -> usize { + unsafe { + bindings::MagickGetImageWidth(self.wand) as usize + } + } + + /// Retrieve the height of the image. + pub fn get_image_height(&self) -> usize { + unsafe { + bindings::MagickGetImageHeight(self.wand) as usize + } + } + + /// Retrieve the named image property value. + pub fn get_image_property(&self, name: &str) -> Result { + let c_name = CString::new(name).unwrap(); + let result = unsafe { + bindings::MagickGetImageProperty(self.wand, c_name.as_ptr()) + }; + let value = if result.is_null() { + Err("missing property") + } else { + // convert (and copy) the C string to a Rust string + let cstr = unsafe { CStr::from_ptr(result) }; + Ok(cstr.to_string_lossy().into_owned()) + }; + unsafe { + bindings::MagickRelinquishMemory(result as *mut c_void); + } + value + } + + /// Resize the image to the specified width and height, using the + /// specified filter type with the specified blur / sharpness factor. + /// + /// blur_factor values greater than 1 create blurriness, while values + /// less than 1 create sharpness. + pub fn resize_image(&self, width: usize, height: usize, + filter: FilterType, blur_factor: f64) { + unsafe { + bindings::MagickResizeImage( + self.wand, width as u64, height as u64, + filter as c_uint, blur_factor as c_double + ); + } + } + + /// Resize the image to find within the given dimensions, maintaining + /// the current aspect ratio. + pub fn fit(&self, width: usize, height: usize) { + let mut width_ratio = width as f64; + width_ratio /= self.get_image_width() as f64; + let mut height_ratio = height as f64; + height_ratio /= self.get_image_height() as f64; + let new_width: usize; + let new_height: usize; + if width_ratio < height_ratio { + new_width = width; + new_height = (self.get_image_height() as f64 * width_ratio) as usize; + } else { + new_width = (self.get_image_width() as f64 * height_ratio) as usize; + new_height = height; + } + unsafe { + bindings::MagickResetIterator(self.wand); + while bindings::MagickNextImage(self.wand) != bindings::MagickFalse { + bindings::MagickResizeImage(self.wand, new_width as u64, new_height as u64, + FilterType::LanczosFilter as c_uint, 1.0); + } + } + } + + /// Detect if the loaded image is not in top-left orientation, and + /// hence should be "auto" oriented so it is suitable for viewing. + pub fn requires_orientation(&self) -> bool { + unsafe { + bindings::MagickGetImageOrientation(self.wand) != bindings::TopLeftOrientation + } + } + + /// Automatically adjusts the loaded image so that its orientation is + /// suitable for viewing (i.e. top-left orientation). + /// + /// Returns `true` if successful or `false` if an error occurred. + pub fn auto_orient(&self) -> bool { + unsafe { + bindings::MagickAutoOrientImage(self.wand) == bindings::MagickTrue + } + } + + /// Write the current image to the provided path. + pub fn write_image(&self, path: &str) -> Result<(), &'static str> { + let c_name = CString::new(path).unwrap(); + let result = unsafe { + bindings::MagickWriteImage(self.wand, c_name.as_ptr()) + }; + match result { + bindings::MagickTrue => Ok(()), + _ => Err("failed to write image") + } + } + + /// Write the image in the desired format to a new blob. + /// + /// The `format` argument may be any ImageMagick supported image + /// format (e.g. GIF, JPEG, PNG, etc). + pub fn write_image_blob(&self, format: &str) -> Result, &'static str> { + let c_format = CString::new(format).unwrap(); + let mut length: u64 = 0; + let blob = unsafe { + bindings::MagickSetImageFormat(self.wand, c_format.as_ptr()); + bindings::MagickResetIterator(self.wand); + bindings::MagickGetImageBlob(self.wand, &mut length) + }; + let mut bytes = Vec::with_capacity(length as usize); + unsafe { + bytes.set_len(length as usize); + ptr::copy_nonoverlapping(blob, bytes.as_mut_ptr(), length as usize); + bindings::MagickRelinquishMemory(blob as *mut c_void); + }; + Ok(bytes) + } +} + +// Automate safe cleanup for MagickWand instances. +impl Drop for MagickWand { + + /// Clear any exceptions and destroy the magic wand. + fn drop(&mut self) { + unsafe { + bindings::MagickClearException(self.wand); + bindings::DestroyMagickWand(self.wand); + } + } +} diff --git a/src/wand/mod.rs b/src/wand/mod.rs new file mode 100644 index 0000000..5ac3b6d --- /dev/null +++ b/src/wand/mod.rs @@ -0,0 +1,3 @@ +mod magick; + +pub use self::magick::MagickWand;