Skip to content

Commit

Permalink
Add a few more functions and typed
Browse files Browse the repository at this point in the history
Adds `Image` type and a few image related functions.
  • Loading branch information
5ohue committed May 14, 2024
1 parent 1752b00 commit 8c15451
Show file tree
Hide file tree
Showing 4 changed files with 166 additions and 0 deletions.
21 changes: 21 additions & 0 deletions src/types/image.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
use std::marker::PhantomData;

use crate::bindings;

pub struct Image<'a> {
image: *mut bindings::Image,
phantom_data: PhantomData<&'a bindings::Image>,
}

impl Image<'_> {
pub unsafe fn new(img: *mut bindings::Image) -> Self {
Image {
image: img,
phantom_data: PhantomData
}
}

pub unsafe fn get_ptr(&self) -> *mut bindings::Image {
self.image
}
}
35 changes: 35 additions & 0 deletions src/types/layer_method.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
use crate::bindings;

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u32)]
pub enum LayerMethod {
Undefined = bindings::LayerMethod_UndefinedLayer,
Coalesce = bindings::LayerMethod_CoalesceLayer,
CompareAny = bindings::LayerMethod_CompareAnyLayer,
CompareClear = bindings::LayerMethod_CompareClearLayer,
CompareOverlay = bindings::LayerMethod_CompareOverlayLayer,
Dispose = bindings::LayerMethod_DisposeLayer,
Optimize = bindings::LayerMethod_OptimizeLayer,
OptimizeImage = bindings::LayerMethod_OptimizeImageLayer,
OptimizePlus = bindings::LayerMethod_OptimizePlusLayer,
OptimizeTrans = bindings::LayerMethod_OptimizeTransLayer,
RemoveDups = bindings::LayerMethod_RemoveDupsLayer,
RemoveZero = bindings::LayerMethod_RemoveZeroLayer,
Composite = bindings::LayerMethod_CompositeLayer,
Merge = bindings::LayerMethod_MergeLayer,
Flatten = bindings::LayerMethod_FlattenLayer,
Mosaic = bindings::LayerMethod_MosaicLayer,
TrimBounds = bindings::LayerMethod_TrimBoundsLayer,
}

impl Default for LayerMethod {
fn default() -> Self {
return LayerMethod::Undefined;
}
}

impl From<LayerMethod> for bindings::LayerMethod {
fn from(value: LayerMethod) -> Self {
return value as bindings::LayerMethod;
}
}
4 changes: 4 additions & 0 deletions src/types/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,11 @@ mod fill_rule;
mod filter_type;
mod geometry_info;
mod gravity_type;
mod image;
mod image_type;
mod interlace_type;
mod kernel;
mod layer_method;
mod line_cap;
mod line_join;
mod magick_evaluate_operator;
Expand Down Expand Up @@ -50,9 +52,11 @@ pub use self::fill_rule::FillRule;
pub use self::filter_type::FilterType;
pub use self::geometry_info::GeometryInfo;
pub use self::gravity_type::GravityType;
pub use self::image::Image;
pub use self::image_type::ImageType;
pub use self::interlace_type::InterlaceType;
pub use self::kernel::*;
pub use self::layer_method::LayerMethod;
pub use self::line_cap::LineCap;
pub use self::line_join::LineJoin;
pub use self::magick_evaluate_operator::MagickEvaluateOperator;
Expand Down
106 changes: 106 additions & 0 deletions src/wand/magick.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,11 @@ use crate::{
EndianType,
FilterType,
GravityType,
Image,
ImageType,
InterlaceType,
KernelInfo,
LayerMethod,
MagickEvaluateOperator,
MagickFunction,
MetricType,
Expand Down Expand Up @@ -77,6 +79,21 @@ wand_common!(
/// When the `MagickWand` is dropped, the ImageMagick wand will be
/// destroyed as well.
impl MagickWand {
/// Creates new wand by cloning the image.
///
/// * `img`: the image.
pub fn new_from_image(img: &Image<'_>) -> Result<MagickWand> {
let result = unsafe {
bindings::NewMagickWandFromImage(img.get_ptr())
};

return if result.is_null() {
Err(MagickError("failed to create wand from image"))
} else {
Ok(MagickWand { wand: result })
}
}

pub fn new_image(&self, columns: usize, rows: usize, background: &PixelWand) -> Result<()> {
match unsafe { bindings::MagickNewImage(self.wand, columns.into(), rows.into(), background.wand) } {
MagickTrue => Ok(()),
Expand Down Expand Up @@ -224,6 +241,37 @@ impl MagickWand {
}
}

/// Composes all the image layers from the current given image onward to produce a single image
/// of the merged layers.
///
/// The inital canvas's size depends on the given LayerMethod, and is initialized using the
/// first images background color. The images are then composited onto that image in sequence
/// using the given composition that has been assigned to each individual image.
///
/// * `method`: the method of selecting the size of the initial canvas.
/// MergeLayer: Merge all layers onto a canvas just large enough to hold all the actual
/// images. The virtual canvas of the first image is preserved but otherwise ignored.
///
/// FlattenLayer: Use the virtual canvas size of first image. Images which fall outside
/// this canvas is clipped. This can be used to 'fill out' a given virtual canvas.
///
/// MosaicLayer: Start with the virtual canvas of the first image, enlarging left and right
/// edges to contain all images. Images with negative offsets will be clipped.
pub fn merge_image_layers(&self, method: LayerMethod) -> Result<MagickWand> {
let result = unsafe {
bindings::MagickMergeImageLayers(self.wand, method.into())
};
if result.is_null() {
return Err(MagickError("failed to merge image layres"));
}
return Ok(MagickWand { wand: result });
}

/// Returns the number of images associated with a magick wand.
pub fn get_number_images(&self) -> usize {
return unsafe { bindings::MagickGetNumberImages(self.wand).into() };
}

/// Compare two images and return tuple `(distortion, diffImage)`
/// `diffImage` is `None` if `distortion == 0`
pub fn compare_images(
Expand Down Expand Up @@ -1540,6 +1588,64 @@ impl MagickWand {
}
}

/// Applies a channel expression to the specified image. The expression
/// consists of one or more channels, either mnemonic or numeric (e.g. red, 1), separated by
/// actions as follows:
///
/// <=> exchange two channels (e.g. red<=>blue) => transfer a channel to another (e.g.
/// red=>green) , separate channel operations (e.g. red, green) | read channels from next input
/// image (e.g. red | green) ; write channels to next output image (e.g. red; green; blue) A
/// channel without a operation symbol implies extract. For example, to create 3 grayscale
/// images from the red, green, and blue channels of an image, use:
///
/// * `expression`: the expression.
pub fn channel_fx_image(&self, expression: &str) -> Result<MagickWand> {
let c_expression = CString::new(expression).map_err(|_| MagickError("artifact string contains null byte"))?;

let result = unsafe {
bindings::MagickChannelFxImage(
self.wand,
c_expression.as_ptr()
)
};

return if result.is_null() {
Err(MagickError("failed to apply expression to image"))
} else {
Ok(MagickWand{ wand: result })
};
}

/// Combines one or more images into a single image. The grayscale value of the pixels of each
/// image in the sequence is assigned in order to the specified channels of the combined image.
/// The typical ordering would be image 1 => Red, 2 => Green, 3 => Blue, etc.
///
/// * `colorspace`: the colorspace.
pub fn combine_images(&self, colorspace: ColorspaceType) -> Result<MagickWand> {
let result = unsafe {
bindings::MagickCombineImages(self.wand, colorspace.into())
};

return if result.is_null() {
Err(MagickError("failed to combine images"))
} else {
Ok(MagickWand{ wand: result })
}
}

/// Returns the current image from the magick wand.
pub fn get_image<'wand>(&'wand self) -> Result<Image<'wand>> {
let result = unsafe {
bindings::GetImageFromMagickWand(self.wand)
};

return if result.is_null() {
Err(MagickError("no image in wand"))
} else {
unsafe { Ok(Image::new(result)) }
}
}

mutations!(
/// Sets the image to the specified alpha level.
MagickSetImageAlpha => set_image_alpha(alpha: f64)
Expand Down

0 comments on commit 8c15451

Please sign in to comment.