Write image to a vector of bytes (in memory write)

cargo test passes
This commit is contained in:
Nathan Fiedler
2015-06-09 22:18:57 -07:00
parent 78d681608d
commit 56877aaad3
3 changed files with 61 additions and 4 deletions

View File

@ -1,6 +1,6 @@
# magick-rust # magick-rust
A "safe" Rust interface to the [ImageMagick](http://www.imagemagick.org/) system, in particular, the MagickWand library. The *safe* is in scarequotes because honestly nearly everything is little more than calls into a C library with `unsafe` wrapped around it. A "safe" Rust interface to the [ImageMagick](http://www.imagemagick.org/) system, in particular, the MagickWand library. The word *safe* is in scarequotes because, honestly, nearly everything is little more than a call into a C function with `unsafe` wrapped around it.
## TODO ## TODO
@ -9,9 +9,18 @@ A "safe" Rust interface to the [ImageMagick](http://www.imagemagick.org/) system
1. Develop Rustic wrappers to the MagickWand library. 1. Develop Rustic wrappers to the MagickWand library.
* Old Rust bindings: https://github.com/influenza/wand-of-rust * Old Rust bindings: https://github.com/influenza/wand-of-rust
* Wand API: http://www.imagemagick.org/script/magick-wand.php * Wand API: http://www.imagemagick.org/script/magick-wand.php
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
## Build and Test
Pretty simple for now.
```
$ cargo build
$ cargo test
```
## Generating Bindings ## Generating Bindings
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. 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.

View File

@ -16,6 +16,11 @@
//! //!
//! "Safe" wrapper around the low-level bindings to ImageMagick. //! "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 // Make the Rust bindings compile cleanly, despite being very un-Rust-like
// wrappers around C code. // wrappers around C code.
@ -27,12 +32,18 @@
extern crate libc; extern crate libc;
use std::ffi::CString; use std::ffi::CString;
use std::ptr;
use libc::{c_uint, size_t, c_double, c_void}; use libc::{c_uint, size_t, c_double, c_void};
use filters::FilterType; 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.
///
/// 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 { pub struct MagickWand {
wand: *mut bindings::MagickWand wand: *mut bindings::MagickWand
} }
@ -102,8 +113,6 @@ impl MagickWand {
} }
} }
// TODO: get the image from the wand somehow (maybe GetImageFromMagickWand())
/// Write the current image to the provided path. /// Write the current image to the provided path.
pub fn write_image(&self, path: &str) -> Result<(), &'static str> { pub fn write_image(&self, path: &str) -> Result<(), &'static str> {
let c_name = CString::new(path).unwrap(); let c_name = CString::new(path).unwrap();
@ -115,6 +124,28 @@ impl MagickWand {
_ => Err("failed to write image") _ => 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)
};
// 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. // Automate safe cleanup for MagickWand instances.

View File

@ -80,3 +80,20 @@ fn test_read_from_blob() {
assert_eq!(512, wand.get_image_width()); assert_eq!(512, wand.get_image_width());
assert_eq!(384, wand.get_image_height()); assert_eq!(384, wand.get_image_height());
} }
#[test]
fn test_write_to_blob() {
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 blob = wand.write_image_blob("jpeg").unwrap();
assert_eq!(104061, blob.len());
// should be able to read it back again
assert!(wand.read_image_blob(blob).is_ok());
assert_eq!(512, wand.get_image_width());
assert_eq!(384, wand.get_image_height());
}