Merge pull request #5 from marjakm/master
add drawing and pixel wands; add some methods to MagickWand
This commit is contained in:
79
build.rs
79
build.rs
@ -28,13 +28,16 @@ fn main() {
|
|||||||
// If the MagickWand bindings are missing, generate them using
|
// If the MagickWand bindings are missing, generate them using
|
||||||
// rust-bindgen.
|
// rust-bindgen.
|
||||||
//
|
//
|
||||||
let bindings_path = Path::new("src/bindings.rs");
|
let out_dir = ::std::env::var("OUT_DIR").unwrap();
|
||||||
if !bindings_path.exists() {
|
let bindings_path_str = out_dir.clone() + "/bindings.rs";
|
||||||
let bindgen_path = Path::new("rust-bindgen");
|
if !Path::new(&bindings_path_str).exists() {
|
||||||
|
let bindgen_path_str = out_dir.clone() + "/rust-bindgen";
|
||||||
|
let bindgen_path = Path::new(&bindgen_path_str);
|
||||||
if !bindgen_path.exists() {
|
if !bindgen_path.exists() {
|
||||||
Command::new("git")
|
Command::new("git")
|
||||||
.arg("clone")
|
.arg("clone")
|
||||||
.arg("https://github.com/crabtw/rust-bindgen.git")
|
.arg("https://github.com/crabtw/rust-bindgen.git")
|
||||||
|
.arg(bindgen_path)
|
||||||
.status().unwrap();
|
.status().unwrap();
|
||||||
// Checkout a version of rust-bindgen that is known to work;
|
// Checkout a version of rust-bindgen that is known to work;
|
||||||
// more recent versions produce code that does not compile (the
|
// more recent versions produce code that does not compile (the
|
||||||
@ -42,39 +45,47 @@ fn main() {
|
|||||||
Command::new("git")
|
Command::new("git")
|
||||||
.arg("checkout")
|
.arg("checkout")
|
||||||
.arg("8a51860")
|
.arg("8a51860")
|
||||||
.current_dir("rust-bindgen")
|
.current_dir(bindgen_path)
|
||||||
.status().unwrap();
|
|
||||||
Command::new("cargo")
|
|
||||||
.arg("build")
|
|
||||||
.current_dir("rust-bindgen")
|
|
||||||
.status().unwrap();
|
.status().unwrap();
|
||||||
|
|
||||||
}
|
}
|
||||||
// Ensure MagickWand-config is in the PATH and report clearly if not.
|
let mut bindgen_bin = bindgen_path.to_path_buf();
|
||||||
if !Command::new("which").arg("MagickWand-config").status().unwrap().success() {
|
bindgen_bin.push("target/debug/bindgen");
|
||||||
panic!("MagickWand-config not in the PATH, please install ImageMagick");
|
if !bindgen_bin.exists() {
|
||||||
|
let mut cmd = Command::new("cargo");
|
||||||
|
cmd.arg("build").current_dir(bindgen_path);
|
||||||
|
println!("BINDGEN_BUILD={:?}", cmd);
|
||||||
|
cmd.status().unwrap();
|
||||||
}
|
}
|
||||||
// Create the header file that rust-bindgen needs as input.
|
|
||||||
let mut gen_h = match File::create("gen.h") {
|
|
||||||
Err(why) => panic!("could not create gen.h file: {}", Error::description(&why)),
|
|
||||||
Ok(file) => file
|
|
||||||
};
|
|
||||||
match gen_h.write_all(HEADER.as_bytes()) {
|
|
||||||
Err(why) => panic!("could not write to gen.h: {}", Error::description(&why)),
|
|
||||||
Ok(_) => ()
|
|
||||||
};
|
|
||||||
// Get the compiler and linker flags for the MagickWand library.
|
// Get the compiler and linker flags for the MagickWand library.
|
||||||
let mw_cflags_output = Command::new("MagickWand-config")
|
let mw_cflags_output = Command::new("pkg-config")
|
||||||
.arg("--cflags")
|
.arg("--cflags")
|
||||||
|
.arg("MagickWand")
|
||||||
.output().unwrap();
|
.output().unwrap();
|
||||||
let mw_cflags = std::str::from_utf8(&mw_cflags_output.stdout).unwrap().trim();
|
let mw_cflags = std::str::from_utf8(&mw_cflags_output.stdout).unwrap().trim();
|
||||||
let mw_cflags_arr: Vec<&str> = mw_cflags.split_whitespace().collect();
|
let mw_cflags_arr: Vec<&str> = mw_cflags.split_whitespace().collect();
|
||||||
let mw_ldflags_output = Command::new("MagickWand-config")
|
println!("CFLAGS={:?}", mw_cflags_arr);
|
||||||
.arg("--ldflags")
|
let mw_ldflags_output = Command::new("pkg-config")
|
||||||
|
.arg("--libs")
|
||||||
|
.arg("MagickWand")
|
||||||
.output().unwrap();
|
.output().unwrap();
|
||||||
let mw_ldflags = std::str::from_utf8(&mw_ldflags_output.stdout).unwrap().trim();
|
let mw_ldflags = std::str::from_utf8(&mw_ldflags_output.stdout).unwrap().trim();
|
||||||
let mw_ldflags_arr: Vec<&str> = mw_ldflags.split_whitespace().collect();
|
let mw_ldflags_arr: Vec<&str> = mw_ldflags.split_whitespace().collect();
|
||||||
|
println!("LDFLAGS={:?}", mw_ldflags_arr);
|
||||||
|
|
||||||
|
let gen_h_path = out_dir.clone() + "/gen.h";
|
||||||
|
// Create the header file that rust-bindgen needs as input.
|
||||||
|
let mut gen_h = match File::create(&gen_h_path) {
|
||||||
|
Err(why) => panic!("could not create {} file: {}", gen_h_path, Error::description(&why)),
|
||||||
|
Ok(file) => file
|
||||||
|
};
|
||||||
|
match gen_h.write_all(HEADER.as_bytes()) {
|
||||||
|
Err(why) => panic!("could not write to {}: {}", gen_h_path, Error::description(&why)),
|
||||||
|
Ok(_) => ()
|
||||||
|
};
|
||||||
|
|
||||||
// Combine all of that in the invocation of rust-bindgen.
|
// Combine all of that in the invocation of rust-bindgen.
|
||||||
let mut cmd = &mut Command::new("./rust-bindgen/target/debug/bindgen");
|
let mut cmd = &mut Command::new(bindgen_bin);
|
||||||
if cfg!(target_os = "macos") {
|
if cfg!(target_os = "macos") {
|
||||||
// Mac requires that the xcode tools are installed so that
|
// Mac requires that the xcode tools are installed so that
|
||||||
// rustc can find the clang.dylib file. See also issue
|
// rustc can find the clang.dylib file. See also issue
|
||||||
@ -84,27 +95,29 @@ fn main() {
|
|||||||
panic!("missing {}, run xcode-select --install", LIBPATH);
|
panic!("missing {}, run xcode-select --install", LIBPATH);
|
||||||
}
|
}
|
||||||
cmd.env("DYLD_LIBRARY_PATH", LIBPATH);
|
cmd.env("DYLD_LIBRARY_PATH", LIBPATH);
|
||||||
|
|
||||||
|
// For the sake of easily building and testing on Mac, include the path
|
||||||
|
// to MagickWand. Chances are MagickWand is in /usr/local/lib, or
|
||||||
|
// somewhere else that rustc can find it.
|
||||||
|
println!("cargo:rustc-link-search=native=/usr/local/lib");
|
||||||
}
|
}
|
||||||
cmd.args(&mw_cflags_arr[..])
|
cmd.args(&mw_cflags_arr[..])
|
||||||
.arg("-builtins")
|
.arg("-builtins")
|
||||||
.arg("-o")
|
.arg("-o")
|
||||||
.arg("src/bindings.rs")
|
.arg(bindings_path_str)
|
||||||
.args(&mw_ldflags_arr[..])
|
.args(&mw_ldflags_arr[..])
|
||||||
.arg("gen.h")
|
.arg(&gen_h_path);
|
||||||
.status().unwrap();
|
println!("BINDING_GENERATION={:?}", cmd);
|
||||||
|
cmd.status().unwrap();
|
||||||
// how to get the output of the command...
|
// how to get the output of the command...
|
||||||
// let output = Commad::new(...).output().unwrap();
|
// let output = Commad::new(...).output().unwrap();
|
||||||
// let out = std::str::from_utf8(&output.stdout).unwrap();
|
// let out = std::str::from_utf8(&output.stdout).unwrap();
|
||||||
// println!("cargo:output={}", out);
|
// println!("cargo:output={}", out);
|
||||||
// let err = std::str::from_utf8(&output.stderr).unwrap();
|
// let err = std::str::from_utf8(&output.stderr).unwrap();
|
||||||
// println!("cargo:error={}", err);
|
// println!("cargo:error={}", err);
|
||||||
match std::fs::remove_file("gen.h") {
|
match std::fs::remove_file(&gen_h_path) {
|
||||||
Err(why) => panic!("could not remove gen.h: {}", Error::description(&why)),
|
Err(why) => panic!("could not remove {}: {}", gen_h_path, Error::description(&why)),
|
||||||
Ok(_) => ()
|
Ok(_) => ()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// For the sake of easily building and testing on Mac, include the path
|
|
||||||
// to MagickWand. Chances are MagickWand is in /usr/local/lib, or
|
|
||||||
// somewhere else that rustc can find it.
|
|
||||||
println!("cargo:rustc-link-search=native=/usr/local/lib");
|
|
||||||
}
|
}
|
||||||
|
|||||||
27
src/conversions.rs
Normal file
27
src/conversions.rs
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
use super::bindings;
|
||||||
|
|
||||||
|
pub trait FromRust<T> {
|
||||||
|
fn from_rust(t: T) -> Self;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromRust<bool> for bindings::MagickBooleanType {
|
||||||
|
fn from_rust(b: bool) -> Self {
|
||||||
|
if b {
|
||||||
|
bindings::MagickTrue
|
||||||
|
} else {
|
||||||
|
bindings::MagickFalse
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait ToMagick<T> {
|
||||||
|
fn to_magick(self) -> T;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, E> ToMagick<T> for E
|
||||||
|
where T: FromRust<E>
|
||||||
|
{
|
||||||
|
fn to_magick(self) -> T {
|
||||||
|
<T as FromRust<E>>::from_rust(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
36
src/filters.rs
Normal file
36
src/filters.rs
Normal file
@ -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
|
||||||
|
}
|
||||||
245
src/lib.rs
245
src/lib.rs
@ -31,195 +31,16 @@
|
|||||||
|
|
||||||
extern crate libc;
|
extern crate libc;
|
||||||
|
|
||||||
use std::ffi::{CStr, CString};
|
mod wand;
|
||||||
use std::ptr;
|
mod conversions;
|
||||||
use libc::{c_uint, c_double, c_void};
|
pub mod filters;
|
||||||
use filters::FilterType;
|
mod bindings { include!(concat!(env!("OUT_DIR"), "/bindings.rs")); }
|
||||||
|
|
||||||
mod bindings;
|
pub use wand::*;
|
||||||
|
|
||||||
/// MagickWand is a Rustic wrapper to the Rust bindings to ImageMagick.
|
pub type size_t = ::bindings::size_t;
|
||||||
///
|
pub type ssize_t = ::bindings::ssize_t;
|
||||||
/// 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<u8>) -> 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<String, &'static str> {
|
|
||||||
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<Vec<u8>, &'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
|
/// This function must be called before any other ImageMagick operations
|
||||||
/// are attempted. This function is safe to be called repeatedly.
|
/// are attempted. This function is safe to be called repeatedly.
|
||||||
@ -243,42 +64,20 @@ pub fn magick_wand_terminus() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub mod filters {
|
pub fn magick_query_fonts(pattern: &str) -> Result<Vec<String>, &'static str> {
|
||||||
|
let mut number_fonts: size_t = 0;
|
||||||
use bindings;
|
let c_string = try!(::std::ffi::CString::new(pattern).map_err(|_| "could not convert to cstring"));
|
||||||
|
let ptr = unsafe { bindings::MagickQueryFonts(c_string.as_ptr(), &mut number_fonts as *mut size_t) };
|
||||||
pub enum FilterType {
|
if ptr.is_null() {
|
||||||
UndefinedFilter = bindings::UndefinedFilter as isize,
|
Err("null ptr returned by magick_query_fonts")
|
||||||
PointFilter = bindings::PointFilter as isize,
|
} else {
|
||||||
BoxFilter = bindings::BoxFilter as isize,
|
let mut v = Vec::new();
|
||||||
TriangleFilter = bindings::TriangleFilter as isize,
|
let c_str_ptr_slice = unsafe { ::std::slice::from_raw_parts(ptr, number_fonts as usize) };
|
||||||
HermiteFilter = bindings::HermiteFilter as isize,
|
for c_str_ptr in c_str_ptr_slice {
|
||||||
HanningFilter = bindings::HanningFilter as isize,
|
let c_str = unsafe { ::std::ffi::CStr::from_ptr(*c_str_ptr) };
|
||||||
HammingFilter = bindings::HammingFilter as isize,
|
v.push(c_str.to_string_lossy().into_owned())
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
Ok(v)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
79
src/wand/drawing.rs
Normal file
79
src/wand/drawing.rs
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
use std::fmt;
|
||||||
|
use std::ffi::{CStr, CString};
|
||||||
|
use ::bindings;
|
||||||
|
use ::size_t;
|
||||||
|
|
||||||
|
wand_common!(
|
||||||
|
DrawingWand,
|
||||||
|
NewDrawingWand, ClearDrawingWand, IsDrawingWand, CloneDrawingWand, DestroyDrawingWand,
|
||||||
|
DrawClearException, DrawGetExceptionType, DrawGetException
|
||||||
|
);
|
||||||
|
|
||||||
|
impl DrawingWand {
|
||||||
|
pub fn draw_annotation(&mut self, x: f64, y: f64, text: &str) -> Result<(), &'static str> {
|
||||||
|
let c_string = try!(CString::new(text).map_err(|_| "could not convert to cstring"));
|
||||||
|
unsafe { bindings::DrawAnnotation(self.wand, x, y, c_string.as_ptr() as *const _) };
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
string_set_get!(
|
||||||
|
get_font, set_font, DrawGetFont, DrawSetFont
|
||||||
|
get_font_family, set_font_family, DrawGetFontFamily, DrawSetFontFamily
|
||||||
|
get_vector_graphics, set_vector_graphics, DrawGetVectorGraphics, DrawSetVectorGraphics
|
||||||
|
get_clip_path, set_clip_path, DrawGetClipPath, DrawSetClipPath
|
||||||
|
);
|
||||||
|
|
||||||
|
string_set_get_unchecked!(
|
||||||
|
get_text_encoding, set_text_encoding, DrawGetTextEncoding, DrawSetTextEncoding
|
||||||
|
);
|
||||||
|
|
||||||
|
pixel_set_get!(
|
||||||
|
get_border_color, set_border_color, DrawGetBorderColor, DrawSetBorderColor
|
||||||
|
get_fill_color, set_fill_color, DrawGetFillColor, DrawSetFillColor
|
||||||
|
get_stroke_color, set_stroke_color, DrawGetStrokeColor, DrawSetStrokeColor
|
||||||
|
get_text_under_color, set_text_under_color, DrawGetTextUnderColor, DrawSetTextUnderColor
|
||||||
|
);
|
||||||
|
|
||||||
|
set_get_unchecked!(
|
||||||
|
get_gravity, set_gravity, DrawGetGravity, DrawSetGravity, u32
|
||||||
|
get_opacity, set_opacity, DrawGetOpacity, DrawSetOpacity, f64
|
||||||
|
get_clip_rule, set_clip_rule, DrawGetClipRule, DrawSetClipRule, u32
|
||||||
|
get_clip_units, set_clip_units, DrawGetClipUnits, DrawSetClipUnits, u32
|
||||||
|
get_fill_rule, set_fill_rule, DrawGetFillRule, DrawSetFillRule, u32
|
||||||
|
get_fill_opacity, set_fill_opacity, DrawGetFillOpacity, DrawSetFillOpacity, f64
|
||||||
|
|
||||||
|
get_font_size, set_font_size, DrawGetFontSize, DrawSetFontSize, f64
|
||||||
|
get_font_style, set_font_style, DrawGetFontStyle, DrawSetFontStyle, u32
|
||||||
|
get_font_weight, set_font_weight, DrawGetFontWeight, DrawSetFontWeight, size_t
|
||||||
|
get_font_stretch, set_font_stretch, DrawGetFontStretch, DrawSetFontStretch, u32
|
||||||
|
|
||||||
|
get_stroke_dash_offset, set_stroke_dash_offset, DrawGetStrokeDashOffset, DrawSetStrokeDashOffset, f64
|
||||||
|
get_stroke_line_cap, set_stroke_line_cap, DrawGetStrokeLineCap, DrawSetStrokeLineCap, u32
|
||||||
|
get_stroke_line_join, set_stroke_line_join, DrawGetStrokeLineJoin, DrawSetStrokeLineJoin, u32
|
||||||
|
get_stroke_miter_limit, set_stroke_miter_limit, DrawGetStrokeMiterLimit, DrawSetStrokeMiterLimit, size_t
|
||||||
|
get_stroke_opacity, set_stroke_opacity, DrawGetStrokeOpacity, DrawSetStrokeOpacity, f64
|
||||||
|
get_stroke_width, set_stroke_width, DrawGetStrokeWidth, DrawSetStrokeWidth, f64
|
||||||
|
get_stroke_antialias, set_stroke_antialias, DrawGetStrokeAntialias, DrawSetStrokeAntialias, u32
|
||||||
|
|
||||||
|
get_text_alignment, set_text_alignment, DrawGetTextAlignment, DrawSetTextAlignment, u32
|
||||||
|
get_text_antialias, set_text_antialias, DrawGetTextAntialias, DrawSetTextAntialias, u32
|
||||||
|
get_text_decoration, set_text_decoration, DrawGetTextDecoration, DrawSetTextDecoration, u32
|
||||||
|
get_text_direction, set_text_direction, DrawGetTextDirection, DrawSetTextDirection, u32
|
||||||
|
get_text_kerning, set_text_kerning, DrawGetTextKerning, DrawSetTextKerning, f64
|
||||||
|
get_text_interline_spacing, set_text_interline_spacing, DrawGetTextInterlineSpacing, DrawSetTextInterlineSpacing, f64
|
||||||
|
get_text_interword_spacing, set_text_interword_spacing, DrawGetTextInterwordSpacing, DrawSetTextInterwordSpacing, f64
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for DrawingWand {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
try!(writeln!(f, "DrawingWand {{"));
|
||||||
|
try!(writeln!(f, " Exception: {:?}", self.get_exception()));
|
||||||
|
try!(writeln!(f, " IsWand: {:?}", self.is_wand()));
|
||||||
|
try!(self.fmt_unchecked_settings(f, " "));
|
||||||
|
try!(self.fmt_string_settings(f, " "));
|
||||||
|
try!(self.fmt_string_unchecked_settings(f, " "));
|
||||||
|
try!(self.fmt_pixel_settings(f, " "));
|
||||||
|
writeln!(f, "}}")
|
||||||
|
}
|
||||||
|
}
|
||||||
214
src/wand/macros.rs
Normal file
214
src/wand/macros.rs
Normal file
@ -0,0 +1,214 @@
|
|||||||
|
macro_rules! wand_common {
|
||||||
|
( $wand:ident,
|
||||||
|
$new_wand:ident, $clear_wand:ident, $is_wand:ident, $clone:ident, $destroy:ident,
|
||||||
|
$clear_exc:ident, $get_exc_type:ident, $get_exc:ident
|
||||||
|
) => {
|
||||||
|
pub struct $wand {
|
||||||
|
pub wand: *mut ::bindings::$wand
|
||||||
|
}
|
||||||
|
|
||||||
|
impl $wand {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
$wand {
|
||||||
|
wand: unsafe { ::bindings::$new_wand() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn clear(&mut self) {
|
||||||
|
unsafe { ::bindings::$clear_wand(self.wand) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn clear_exception(&mut self) -> Result<(), &'static str> {
|
||||||
|
match unsafe { ::bindings::$clear_exc(self.wand) } {
|
||||||
|
::bindings::MagickTrue => Ok(()),
|
||||||
|
_ => Err(concat!("failed to clear", stringify!($wand), "exception"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_exception_type(&self) -> u32 {
|
||||||
|
unsafe { ::bindings::$get_exc_type(self.wand) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_exception(&self) -> Result<(String, u32), &'static str> {
|
||||||
|
let mut severity: u32 = 0;
|
||||||
|
// TODO: memory management
|
||||||
|
let ptr = unsafe { ::bindings::$get_exc(self.wand, &mut severity as *mut _) };
|
||||||
|
if ptr.is_null() {
|
||||||
|
Err(concat!("null ptr returned by", stringify!($wand), "get_exception"))
|
||||||
|
} else {
|
||||||
|
let c_str = unsafe { CStr::from_ptr(ptr) };
|
||||||
|
Ok((c_str.to_string_lossy().into_owned(), severity))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_wand(&self) -> Result<(), &'static str> {
|
||||||
|
match unsafe { ::bindings::$is_wand(self.wand) } {
|
||||||
|
::bindings::MagickTrue => Ok(()),
|
||||||
|
_ => Err(concat!(stringify!($wand), " not a wand"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Clone for $wand {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
$wand {
|
||||||
|
wand: unsafe { ::bindings::$clone(self.wand) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for $wand {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
unsafe {
|
||||||
|
::bindings::$clear_exc(self.wand);
|
||||||
|
::bindings::$destroy(self.wand);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! set_get {
|
||||||
|
($($get:ident, $set:ident, $c_get:ident, $c_set:ident, $typ:ty )*) => {
|
||||||
|
$(
|
||||||
|
pub fn $get(&self) -> $typ {
|
||||||
|
unsafe { ::bindings::$c_get(self.wand) }
|
||||||
|
}
|
||||||
|
pub fn $set(&mut self, v: $typ) -> Result<(), &'static str> {
|
||||||
|
match unsafe { ::bindings::$c_set(self.wand, v) } {
|
||||||
|
::bindings::MagickTrue => Ok(()),
|
||||||
|
_ => Err(concat!(stringify!($set), " returned false"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)*
|
||||||
|
pub fn fmt_checked_settings(&self, f: &mut ::std::fmt::Formatter, prefix: &str) -> ::std::fmt::Result {
|
||||||
|
$( try!(writeln!(f, "{}{:<50}: {:?}", prefix, stringify!($c_get), self.$get())); )*
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! set_get_unchecked {
|
||||||
|
($($get:ident, $set:ident, $c_get:ident, $c_set:ident, $typ:ty )*) => {
|
||||||
|
$(
|
||||||
|
pub fn $get(&self) -> $typ {
|
||||||
|
unsafe { ::bindings::$c_get(self.wand) }
|
||||||
|
}
|
||||||
|
pub fn $set(&mut self, v: $typ) {
|
||||||
|
unsafe { ::bindings::$c_set(self.wand, v) }
|
||||||
|
}
|
||||||
|
)*
|
||||||
|
pub fn fmt_unchecked_settings(&self, f: &mut ::std::fmt::Formatter, prefix: &str) -> ::std::fmt::Result {
|
||||||
|
$( try!(writeln!(f, "{}{:<50}: {:?}", prefix, stringify!($c_get), self.$get())); )*
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! string_get {
|
||||||
|
($get:ident, $c_get:ident) => {
|
||||||
|
pub fn $get(&self) -> Result<String, &'static str> {
|
||||||
|
// TODO: memory management
|
||||||
|
let ptr = unsafe { ::bindings::$c_get(self.wand) };
|
||||||
|
if ptr.is_null() {
|
||||||
|
Err(concat!("null ptr returned by ", stringify!($get)))
|
||||||
|
} else {
|
||||||
|
let c_str = unsafe { ::std::ffi::CStr::from_ptr(ptr) };
|
||||||
|
Ok(c_str.to_string_lossy().into_owned())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! string_set_get {
|
||||||
|
($($get:ident, $set:ident, $c_get:ident, $c_set:ident)*) => {
|
||||||
|
$(
|
||||||
|
string_get!($get, $c_get);
|
||||||
|
pub fn $set(&mut self, s: &str) -> Result<(), &'static str> {
|
||||||
|
let c_string = try!(::std::ffi::CString::new(s).map_err(|_| "could not convert to cstring"));
|
||||||
|
match unsafe { ::bindings::$c_set(self.wand, c_string.as_ptr()) } {
|
||||||
|
::bindings::MagickTrue => Ok(()),
|
||||||
|
_ => Err(concat!(stringify!($set), " returned false"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)*
|
||||||
|
pub fn fmt_string_settings(&self, f: &mut ::std::fmt::Formatter, prefix: &str) -> ::std::fmt::Result {
|
||||||
|
$( try!(writeln!(f, "{}{:<50}: {:?}", prefix, stringify!($c_get), self.$get())); )*
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
macro_rules! string_set_get_unchecked {
|
||||||
|
($($get:ident, $set:ident, $c_get:ident, $c_set:ident )*) => {
|
||||||
|
$(
|
||||||
|
string_get!($get, $c_get);
|
||||||
|
pub fn $set(&mut self, s: &str) -> Result<(), &'static str> {
|
||||||
|
let c_string = try!(::std::ffi::CString::new(s).map_err(|_| "could not convert to cstring"));
|
||||||
|
unsafe { ::bindings::$c_set(self.wand, c_string.as_ptr()) };
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
)*
|
||||||
|
pub fn fmt_string_unchecked_settings(&self, f: &mut ::std::fmt::Formatter, prefix: &str) -> ::std::fmt::Result {
|
||||||
|
$( try!(writeln!(f, "{}{:<50}: {:?}", prefix, stringify!($c_get), self.$get())); )*
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
macro_rules! pixel_set_get {
|
||||||
|
($($get:ident, $set:ident, $c_get:ident, $c_set:ident )*) => {
|
||||||
|
$(
|
||||||
|
pub fn $get(&self) -> ::PixelWand {
|
||||||
|
let pw = ::PixelWand::new();
|
||||||
|
unsafe { ::bindings::$c_get(self.wand, pw.wand) };
|
||||||
|
pw
|
||||||
|
}
|
||||||
|
pub fn $set(&mut self, pw: &::PixelWand) {
|
||||||
|
unsafe { ::bindings::$c_set(self.wand, pw.wand) }
|
||||||
|
}
|
||||||
|
)*
|
||||||
|
pub fn fmt_pixel_settings(&self, f: &mut ::std::fmt::Formatter, prefix: &str) -> ::std::fmt::Result {
|
||||||
|
$(
|
||||||
|
try!(writeln!(f, "{}{:<50}: ", prefix, stringify!($c_get)));
|
||||||
|
try!(self.$get().fmt_w_prefix(f, &format!("{}{:<53}", prefix, " ") ));
|
||||||
|
)*
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! color_set_get {
|
||||||
|
($(
|
||||||
|
$get:ident, $get_quantum:ident, $set:ident, $set_quantum:ident,
|
||||||
|
$c_get:ident, $c_get_quantum:ident, $c_set:ident, $c_set_quantum:ident
|
||||||
|
)*) => {
|
||||||
|
$(
|
||||||
|
pub fn $get(&self) -> f64 {
|
||||||
|
unsafe { ::bindings::$c_get(self.wand) }
|
||||||
|
}
|
||||||
|
pub fn $get_quantum(&self) -> u16 {
|
||||||
|
unsafe { ::bindings::$c_get_quantum(self.wand) }
|
||||||
|
}
|
||||||
|
pub fn $set(&mut self, v: f64) {
|
||||||
|
unsafe { ::bindings::$c_set(self.wand, v) }
|
||||||
|
}
|
||||||
|
pub fn $set_quantum(&mut self, v: u16) {
|
||||||
|
unsafe { ::bindings::$c_set_quantum(self.wand, v) }
|
||||||
|
}
|
||||||
|
)*
|
||||||
|
pub fn fmt_color_settings(&self, f: &mut ::std::fmt::Formatter, prefix: &str) -> ::std::fmt::Result {
|
||||||
|
try!(writeln!(f, "{}Color: {:?}, normalized: {:?}\n{}hsl: {:?}",
|
||||||
|
prefix,
|
||||||
|
self.get_color_as_string(),
|
||||||
|
self.get_color_as_normalized_string(),
|
||||||
|
prefix,
|
||||||
|
self.get_hsl()
|
||||||
|
));
|
||||||
|
$( try!(writeln!(f, "{}{:<10}: {:>} quantum: {}", prefix, stringify!($c_get).split_at(8).1, self.$get(), self.$get_quantum())); )*
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
273
src/wand/magick.rs
Normal file
273
src/wand/magick.rs
Normal file
@ -0,0 +1,273 @@
|
|||||||
|
use std::fmt;
|
||||||
|
use std::ptr;
|
||||||
|
use std::ffi::{CStr, CString};
|
||||||
|
use libc::{c_uint, c_double, c_void};
|
||||||
|
|
||||||
|
use ::{size_t, ssize_t};
|
||||||
|
use ::filters::FilterType;
|
||||||
|
use ::bindings;
|
||||||
|
use ::conversions::*;
|
||||||
|
use super::{DrawingWand, PixelWand};
|
||||||
|
|
||||||
|
|
||||||
|
/// 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.
|
||||||
|
wand_common!(
|
||||||
|
MagickWand,
|
||||||
|
NewMagickWand, ClearMagickWand, IsMagickWand, CloneMagickWand, DestroyMagickWand,
|
||||||
|
MagickClearException, MagickGetExceptionType, MagickGetException
|
||||||
|
);
|
||||||
|
|
||||||
|
impl MagickWand {
|
||||||
|
|
||||||
|
pub fn new_image(&self, columns: size_t, rows: size_t, pixel_wand: &PixelWand) -> Result<(), &'static str> {
|
||||||
|
match unsafe { bindings::MagickNewImage(self.wand, columns, rows, pixel_wand.wand) } {
|
||||||
|
bindings::MagickTrue => Ok(()),
|
||||||
|
_ => Err("Could not create image"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn annotate_image(&mut self, drawing_wand: &DrawingWand, x: f64, y: f64, angle: f64, text: &str) -> Result<(), &'static str> {
|
||||||
|
let c_string = try!(CString::new(text).map_err(|_| "could not convert to cstring"));
|
||||||
|
match unsafe { bindings::MagickAnnotateImage(self.wand, drawing_wand.wand, x, y, angle, c_string.as_ptr() as *const _) } {
|
||||||
|
bindings::MagickTrue => Ok(()),
|
||||||
|
_ => Err("unable to annotate image")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn append_all(&mut self, stack: bool) -> MagickWand {
|
||||||
|
unsafe { bindings::MagickResetIterator(self.wand) };
|
||||||
|
MagickWand {
|
||||||
|
wand: unsafe { bindings::MagickAppendImages(self.wand, stack.to_magick()) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn label_image(&self, label: &str) -> Result<(), &'static str> {
|
||||||
|
let c_label = CString::new(label).unwrap();
|
||||||
|
let result = unsafe {
|
||||||
|
bindings::MagickLabelImage(self.wand, c_label.as_ptr())
|
||||||
|
};
|
||||||
|
match result {
|
||||||
|
bindings::MagickTrue => Ok(()),
|
||||||
|
_ => Err("failed to add label")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write_images(&self, path: &str, adjoin: bool) -> Result<(), &'static str> {
|
||||||
|
let c_name = CString::new(path).unwrap();
|
||||||
|
let result = unsafe {
|
||||||
|
bindings::MagickWriteImages(self.wand, c_name.as_ptr(), adjoin.to_magick())
|
||||||
|
};
|
||||||
|
match result {
|
||||||
|
bindings::MagickTrue => Ok(()),
|
||||||
|
_ => Err("failed to write images")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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<u8>) -> 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 size_t)
|
||||||
|
};
|
||||||
|
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<String, &'static str> {
|
||||||
|
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 size_t, height as size_t,
|
||||||
|
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: size_t, height: size_t) {
|
||||||
|
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: size_t;
|
||||||
|
let new_height: size_t;
|
||||||
|
if width_ratio < height_ratio {
|
||||||
|
new_width = width;
|
||||||
|
new_height = (self.get_image_height() as f64 * width_ratio) as size_t;
|
||||||
|
} else {
|
||||||
|
new_width = (self.get_image_width() as f64 * height_ratio) as size_t;
|
||||||
|
new_height = height;
|
||||||
|
}
|
||||||
|
unsafe {
|
||||||
|
bindings::MagickResetIterator(self.wand);
|
||||||
|
while bindings::MagickNextImage(self.wand) != bindings::MagickFalse {
|
||||||
|
bindings::MagickResizeImage(self.wand, new_width, new_height,
|
||||||
|
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<Vec<u8>, &'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)
|
||||||
|
};
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
string_set_get!(
|
||||||
|
get_filename, set_filename, MagickGetFilename, MagickSetFilename
|
||||||
|
get_font, set_font, MagickGetFont, MagickSetFont
|
||||||
|
get_format, set_format, MagickGetFormat, MagickSetFormat
|
||||||
|
get_image_filename, set_image_filename, MagickGetImageFilename, MagickSetImageFilename
|
||||||
|
get_image_format, set_image_format, MagickGetImageFormat, MagickSetImageFormat
|
||||||
|
);
|
||||||
|
|
||||||
|
set_get!(
|
||||||
|
get_colorspace, set_colorspace, MagickGetColorspace, MagickSetColorspace, u32
|
||||||
|
get_compression, set_compression, MagickGetCompression, MagickSetCompression, u32
|
||||||
|
get_compression_quality, set_compression_quality, MagickGetCompressionQuality, MagickSetCompressionQuality, size_t
|
||||||
|
get_gravity, set_gravity, MagickGetGravity, MagickSetGravity, u32
|
||||||
|
get_image_colorspace, set_image_colorspace, MagickGetImageColorspace, MagickSetImageColorspace, u32
|
||||||
|
get_image_compose, set_image_compose, MagickGetImageCompose, MagickSetImageCompose, u32
|
||||||
|
get_image_compression, set_image_compression, MagickGetImageCompression, MagickSetImageCompression, u32
|
||||||
|
get_image_compression_quality, set_image_compression_quality, MagickGetImageCompressionQuality, MagickSetImageCompressionQuality, size_t
|
||||||
|
get_image_delay, set_image_delay, MagickGetImageDelay, MagickSetImageDelay, size_t
|
||||||
|
get_image_depth, set_image_depth, MagickGetImageDepth, MagickSetImageDepth, size_t
|
||||||
|
get_image_dispose, set_image_dispose, MagickGetImageDispose, MagickSetImageDispose, u32
|
||||||
|
get_image_endian, set_image_endian, MagickGetImageEndian, MagickSetImageEndian, u32
|
||||||
|
get_image_fuzz, set_image_fuzz, MagickGetImageFuzz, MagickSetImageFuzz, f64
|
||||||
|
get_image_gamma, set_image_gamma, MagickGetImageGamma, MagickSetImageGamma, f64
|
||||||
|
get_image_gravity, set_image_gravity, MagickGetImageGravity, MagickSetImageGravity, u32
|
||||||
|
get_image_index, set_image_index, MagickGetImageIndex, MagickSetImageIndex, ssize_t
|
||||||
|
get_image_interlace_scheme, set_image_interlace_scheme, MagickGetImageInterlaceScheme, MagickSetImageInterlaceScheme, u32
|
||||||
|
get_image_interpolate_method, set_image_interpolate_method, MagickGetImageInterpolateMethod, MagickSetImageInterpolateMethod, u32
|
||||||
|
get_image_iterations, set_image_iterations, MagickGetImageIterations, MagickSetImageIterations, size_t
|
||||||
|
get_image_orientation, set_image_orientation, MagickGetImageOrientation, MagickSetImageOrientation, u32
|
||||||
|
get_image_rendering_intent, set_image_rendering_intent, MagickGetImageRenderingIntent, MagickSetImageRenderingIntent, u32
|
||||||
|
get_image_scene, set_image_scene, MagickGetImageScene, MagickSetImageScene, size_t
|
||||||
|
get_image_type, set_image_type, MagickGetImageType, MagickSetImageType, u32
|
||||||
|
get_image_units, set_image_units, MagickGetImageUnits, MagickSetImageUnits, u32
|
||||||
|
get_interlace_scheme, set_interlace_scheme, MagickGetInterlaceScheme, MagickSetInterlaceScheme, u32
|
||||||
|
get_interpolate_method, set_interpolate_method, MagickGetInterpolateMethod, MagickSetInterpolateMethod, u32
|
||||||
|
get_iterator_index, set_iterator_index, MagickGetIteratorIndex, MagickSetIteratorIndex, ssize_t
|
||||||
|
get_orientation, set_orientation, MagickGetOrientation, MagickSetOrientation, u32
|
||||||
|
get_pointsize, set_pointsize, MagickGetPointsize, MagickSetPointsize, f64
|
||||||
|
get_type, set_type, MagickGetType, MagickSetType, u32
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for MagickWand {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
try!(writeln!(f, "MagickWand {{"));
|
||||||
|
try!(writeln!(f, " Exception: {:?}", self.get_exception()));
|
||||||
|
try!(writeln!(f, " IsWand: {:?}", self.is_wand()));
|
||||||
|
try!(self.fmt_string_settings(f, " "));
|
||||||
|
try!(self.fmt_checked_settings(f, " "));
|
||||||
|
writeln!(f, "}}")
|
||||||
|
}
|
||||||
|
}
|
||||||
8
src/wand/mod.rs
Normal file
8
src/wand/mod.rs
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
#[macro_use] mod macros;
|
||||||
|
mod magick;
|
||||||
|
mod drawing;
|
||||||
|
mod pixel;
|
||||||
|
|
||||||
|
pub use self::magick::MagickWand;
|
||||||
|
pub use self::drawing::DrawingWand;
|
||||||
|
pub use self::pixel::{HSL, PixelWand};
|
||||||
100
src/wand/pixel.rs
Normal file
100
src/wand/pixel.rs
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
use std::fmt;
|
||||||
|
use std::ffi::{CStr, CString};
|
||||||
|
use ::bindings;
|
||||||
|
use ::size_t;
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Default, Debug)]
|
||||||
|
pub struct HSL {
|
||||||
|
hue: f64,
|
||||||
|
saturation: f64,
|
||||||
|
lightness: f64
|
||||||
|
}
|
||||||
|
|
||||||
|
wand_common!(
|
||||||
|
PixelWand,
|
||||||
|
NewPixelWand, ClearPixelWand, IsPixelWand, ClonePixelWand, DestroyPixelWand,
|
||||||
|
PixelClearException, PixelGetExceptionType, PixelGetException
|
||||||
|
);
|
||||||
|
|
||||||
|
impl PixelWand {
|
||||||
|
pub fn is_similar(&self, other: &PixelWand, fuzz: f64) -> Result<(), &'static str> {
|
||||||
|
match unsafe { bindings::IsPixelWandSimilar(self.wand, other.wand, fuzz) } {
|
||||||
|
bindings::MagickTrue => Ok(()),
|
||||||
|
_ => Err("not similar")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_hsl(&self) -> HSL {
|
||||||
|
let mut hsl = HSL::default();
|
||||||
|
unsafe { bindings::PixelGetHSL(
|
||||||
|
self.wand,
|
||||||
|
&mut hsl.hue as *mut _,
|
||||||
|
&mut hsl.saturation as *mut _,
|
||||||
|
&mut hsl.lightness as *mut _
|
||||||
|
);}
|
||||||
|
hsl
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_hsl(&self, hsl: &HSL) {
|
||||||
|
unsafe { bindings::PixelSetHSL(
|
||||||
|
self.wand,
|
||||||
|
hsl.hue,
|
||||||
|
hsl.saturation,
|
||||||
|
hsl.lightness
|
||||||
|
);}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn fmt_w_prefix(&self, f: &mut fmt::Formatter, prefix: &str) -> fmt::Result {
|
||||||
|
let mut prf = prefix.to_string();
|
||||||
|
prf.push_str(" ");
|
||||||
|
try!(writeln!(f, "{}PixelWand {{", prefix));
|
||||||
|
try!(writeln!(f, "{}Exception: {:?}", prf, self.get_exception()));
|
||||||
|
try!(writeln!(f, "{}IsWand: {:?}", prf, self.is_wand()));
|
||||||
|
try!(self.fmt_unchecked_settings(f, &prf));
|
||||||
|
try!(self.fmt_color_settings(f, &prf));
|
||||||
|
writeln!(f, "{}}}", prefix)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_color(&mut self, s: &str) -> Result<(), &'static str> {
|
||||||
|
let c_string = try!(CString::new(s).map_err(|_| "could not convert to cstring"));
|
||||||
|
match unsafe { bindings::PixelSetColor(self.wand, c_string.as_ptr())} {
|
||||||
|
bindings::MagickTrue => Ok(()),
|
||||||
|
_ => Err("failed to set color")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
string_get!(get_color_as_string, PixelGetColorAsString);
|
||||||
|
string_get!(get_color_as_normalized_string, PixelGetColorAsNormalizedString);
|
||||||
|
|
||||||
|
set_get_unchecked!(
|
||||||
|
get_color_count, set_color_count, PixelGetColorCount, PixelSetColorCount, size_t
|
||||||
|
get_index, set_index, PixelGetIndex, PixelSetIndex, u16
|
||||||
|
get_fuzz, set_fuzz, PixelGetFuzz, PixelSetFuzz, f64
|
||||||
|
);
|
||||||
|
|
||||||
|
color_set_get!(
|
||||||
|
get_alpha, get_alpha_quantum, set_alpha, set_alpha_quantum,
|
||||||
|
PixelGetAlpha, PixelGetAlphaQuantum, PixelSetAlpha, PixelSetAlphaQuantum
|
||||||
|
get_black, get_black_quantum, set_black, set_black_quantum,
|
||||||
|
PixelGetBlack, PixelGetBlackQuantum, PixelSetBlack, PixelSetBlackQuantum
|
||||||
|
get_blue, get_blue_quantum, set_blue, set_blue_quantum,
|
||||||
|
PixelGetBlue, PixelGetBlueQuantum, PixelSetBlue, PixelSetBlueQuantum
|
||||||
|
get_cyan, get_cyan_quantum, set_cyan, set_cyan_quantum,
|
||||||
|
PixelGetCyan, PixelGetCyanQuantum, PixelSetCyan, PixelSetCyanQuantum
|
||||||
|
get_green, get_green_quantum, set_green, set_green_quantum,
|
||||||
|
PixelGetGreen, PixelGetGreenQuantum, PixelSetGreen, PixelSetGreenQuantum
|
||||||
|
get_magenta, get_magenta_quantum, set_magenta, set_magenta_quantum,
|
||||||
|
PixelGetMagenta, PixelGetMagentaQuantum, PixelSetMagenta, PixelSetMagentaQuantum
|
||||||
|
get_red, get_red_quantum, set_red, set_red_quantum,
|
||||||
|
PixelGetRed, PixelGetRedQuantum, PixelSetRed, PixelSetRedQuantum
|
||||||
|
get_yellow, get_yellow_quantum, set_yellow, set_yellow_quantum,
|
||||||
|
PixelGetYellow, PixelGetYellowQuantum, PixelSetYellow, PixelSetYellowQuantum
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for PixelWand {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
self.fmt_w_prefix(f, "")
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user