/* * Copyright 2015 Nathan Fiedler * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ //! //! "Safe" wrapper around the low-level bindings to ImageMagick. //! //! Prior to using the ImageMagick system, the application should invoke //! `magick_wand_genesis()`, which maps directly to `MagickWandGenesis`. //! Likewise, when an application is done using ImageMagick, invoke the //! `magick_wand_terminus()` function, which maps to `MagickWandTerminus`. //! // Make the Rust bindings compile cleanly, despite being very un-Rust-like // wrappers around C code. #![allow(dead_code)] #![allow(non_upper_case_globals)] #![allow(non_camel_case_types)] #![allow(non_snake_case)] extern crate libc; use std::ffi::CString; use std::ptr; use libc::{c_uint, size_t, c_double, c_void}; use filters::FilterType; mod 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 } } /// 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 size_t, height as size_t, filter as c_uint, blur_factor as c_double ); } } /// 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: size_t = 0; let blob = unsafe { bindings::MagickSetImageFormat(self.wand, c_format.as_ptr()); bindings::MagickResetIterator(self.wand); bindings::MagickGetImageBlob(self.wand, &mut length) }; // would have used Vec::from_raw_buf() but it is unstable 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. pub fn magick_wand_genesis() { unsafe { match bindings::IsMagickWandInstantiated() { bindings::MagickTrue => (), _ => bindings::MagickWandGenesis() } } } /// This function should be called when ImageMagick is no longer needed. /// This function is safe to be called repeatedly. pub fn magick_wand_terminus() { unsafe { match bindings::IsMagickWandInstantiated() { bindings::MagickTrue => bindings::MagickWandTerminus(), _ => () } } } 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 } }