Basic API in place, some tests working
Reading an image, getting its dimensions, and resizing are working. cargo test passes
This commit is contained in:
@ -12,9 +12,9 @@ A "safe" Rust interface to the [ImageMagick](http://www.imagemagick.org/) system
|
|||||||
1. Write unit tests
|
1. Write unit tests
|
||||||
1. Test it on lots of images in batches to stress test it; should not crash
|
1. Test it on lots of images in batches to stress test it; should not crash
|
||||||
|
|
||||||
## Building the Bindings
|
## Generating Bindings
|
||||||
|
|
||||||
To build the ImageMagick bindings, we use [rust-bindgen](https://github.com/crabtw/rust-bindgen), which reads the C header files and produces a suitable wrapper in Rust.
|
To generate the ImageMagick bindings, we use [rust-bindgen](https://github.com/crabtw/rust-bindgen), which reads the C header files and produces a suitable wrapper in Rust.
|
||||||
|
|
||||||
This example is using the [Homebrew](http://brew.sh) installed version of ImageMagick, and the LLVM compiler suite provided in the Command Line Tools from Apple. The only real difference for Mac OS X is the `DYLD_LIBRARY_PATH` that is needed to work around [issue #89](https://github.com/crabtw/rust-bindgen/issues/89) in rust-bindgen. Otherwise, the same basic steps should work on any Rust-supported system.
|
This example is using the [Homebrew](http://brew.sh) installed version of ImageMagick, and the LLVM compiler suite provided in the Command Line Tools from Apple. The only real difference for Mac OS X is the `DYLD_LIBRARY_PATH` that is needed to work around [issue #89](https://github.com/crabtw/rust-bindgen/issues/89) in rust-bindgen. Otherwise, the same basic steps should work on any Rust-supported system.
|
||||||
|
|
||||||
@ -32,4 +32,4 @@ $ DYLD_LIBRARY_PATH=/Library/Developer/CommandLineTools/usr/lib \
|
|||||||
~/gen.h
|
~/gen.h
|
||||||
```
|
```
|
||||||
|
|
||||||
Then copy the `~/bindings.rs` file into the `src` directory of this project, and rebuild everything. Hopefully it still works.
|
Then copy the `~/bindings.rs` file into the `src` directory of this project, and rebuild everything (`cargo clean` and `cargo test`). Hopefully it still works.
|
||||||
|
|||||||
127
src/lib.rs
127
src/lib.rs
@ -26,6 +26,10 @@
|
|||||||
|
|
||||||
extern crate libc;
|
extern crate libc;
|
||||||
|
|
||||||
|
use std::ffi::CString;
|
||||||
|
use libc::{c_uint, size_t, c_double};
|
||||||
|
use filters::FilterType;
|
||||||
|
|
||||||
mod bindings;
|
mod bindings;
|
||||||
|
|
||||||
/// MagickWand is a Rustic wrapper to the Rust bindings to ImageMagick.
|
/// MagickWand is a Rustic wrapper to the Rust bindings to ImageMagick.
|
||||||
@ -43,16 +47,65 @@ impl MagickWand {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// wand-of-rust wrapper around MagickResizeImage
|
/// Read the image data from the named file.
|
||||||
// pub fn resize_image(&self, width: uint, height: uint,
|
pub fn read_image(&self, path: &str) -> Result<(), &'static str> {
|
||||||
// filter: FilterType, blur_factor: f64) {
|
let c_name = CString::new(path).unwrap();
|
||||||
// unsafe {
|
let result = unsafe {
|
||||||
// bindings::MagickResizeImage(
|
bindings::MagickReadImage(self.wand, c_name.as_ptr())
|
||||||
// self.wand, width as size_t, height as size_t,
|
};
|
||||||
// filter as c_uint, blur_factor as c_double
|
match result {
|
||||||
// );
|
bindings::MagickTrue => Ok(()),
|
||||||
// }
|
_ => Err("failed to read image")
|
||||||
// }
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: make a Rustic wrapper for reading a blob from bytes
|
||||||
|
// pub fn MagickReadImageBlob(arg1: *mut MagickWand,
|
||||||
|
// arg2: *const ::libc::c_void, arg3: size_t)
|
||||||
|
// -> MagickBooleanType;
|
||||||
|
|
||||||
|
/// 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
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: get the image from the wand somehow (maybe GetImageFromMagickWand())
|
||||||
|
|
||||||
|
/// 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")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Automate safe cleanup for MagickWand instances.
|
// Automate safe cleanup for MagickWand instances.
|
||||||
@ -68,27 +121,63 @@ impl Drop for MagickWand {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// This function must be called before any other ImageMagick operations
|
/// This function must be called before any other ImageMagick operations
|
||||||
/// are attempted.
|
/// are attempted. This function is safe to be called repeatedly.
|
||||||
pub fn magick_wand_genesis() {
|
pub fn magick_wand_genesis() {
|
||||||
unsafe {
|
unsafe {
|
||||||
bindings::MagickWandGenesis();
|
match bindings::IsMagickWandInstantiated() {
|
||||||
|
bindings::MagickTrue => (),
|
||||||
|
_ => bindings::MagickWandGenesis()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This function should be called when ImageMagick is no longer needed.
|
/// This function should be called when ImageMagick is no longer needed.
|
||||||
|
/// This function is safe to be called repeatedly.
|
||||||
pub fn magick_wand_terminus() {
|
pub fn magick_wand_terminus() {
|
||||||
unsafe {
|
unsafe {
|
||||||
bindings::MagickWandTerminus();
|
match bindings::IsMagickWandInstantiated() {
|
||||||
|
bindings::MagickTrue => bindings::MagickWandTerminus(),
|
||||||
|
_ => ()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
pub mod filters {
|
||||||
mod test {
|
|
||||||
|
|
||||||
use super::{MagickWand};
|
use bindings;
|
||||||
|
|
||||||
#[test]
|
pub enum FilterType {
|
||||||
fn test_new_drop() {
|
UndefinedFilter = bindings::UndefinedFilter as isize,
|
||||||
MagickWand::new();
|
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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
BIN
tests/data/IMG_5745.JPG
Normal file
BIN
tests/data/IMG_5745.JPG
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 105 KiB |
56
tests/lib.rs
Normal file
56
tests/lib.rs
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
extern crate magick_rust;
|
||||||
|
|
||||||
|
use magick_rust::{MagickWand, magick_wand_genesis};
|
||||||
|
use magick_rust::filters::{FilterType};
|
||||||
|
|
||||||
|
use std::sync::{Once, ONCE_INIT};
|
||||||
|
|
||||||
|
// Used to make sure MagickWand is initialized exactly once. Note that we
|
||||||
|
// do not bother shutting down, we simply exit when the tests are done.
|
||||||
|
static START: Once = ONCE_INIT;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_new_drop() {
|
||||||
|
START.call_once(|| {
|
||||||
|
magick_wand_genesis();
|
||||||
|
});
|
||||||
|
MagickWand::new();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_resize_image() {
|
||||||
|
START.call_once(|| {
|
||||||
|
magick_wand_genesis();
|
||||||
|
});
|
||||||
|
let wand = MagickWand::new();
|
||||||
|
assert!(wand.read_image("tests/data/IMG_5745.JPG").is_ok());
|
||||||
|
assert_eq!(512, wand.get_image_width());
|
||||||
|
assert_eq!(384, wand.get_image_height());
|
||||||
|
let halfwidth = match wand.get_image_width() {
|
||||||
|
1 => 1,
|
||||||
|
width => width / 2
|
||||||
|
};
|
||||||
|
let halfheight = match wand.get_image_height() {
|
||||||
|
1 => 1,
|
||||||
|
height => height / 2
|
||||||
|
};
|
||||||
|
wand.resize_image(halfwidth, halfheight, FilterType::LanczosFilter, 1.0);
|
||||||
|
assert_eq!(256, wand.get_image_width());
|
||||||
|
assert_eq!(192, wand.get_image_height());
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user