From 48e0452e7493db8f6d88e13f38732f5c7ed03e7a Mon Sep 17 00:00:00 2001 From: 5ohue <86558263+5ohue@users.noreply.github.com> Date: Sat, 11 May 2024 12:59:39 +0300 Subject: [PATCH 01/37] Make `set_get!` macro usable with types module --- src/wand/macros.rs | 4 +-- src/wand/magick.rs | 62 ++++------------------------------------------ tests/lib.rs | 2 +- 3 files changed, 8 insertions(+), 60 deletions(-) diff --git a/src/wand/macros.rs b/src/wand/macros.rs index 24cf39a..2036f57 100644 --- a/src/wand/macros.rs +++ b/src/wand/macros.rs @@ -110,10 +110,10 @@ 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) } + unsafe { ::bindings::$c_get(self.wand).into() } } pub fn $set(&mut self, v: $typ) -> Result<()> { - match unsafe { ::bindings::$c_set(self.wand, v) } { + match unsafe { ::bindings::$c_set(self.wand, v.into()) } { ::bindings::MagickBooleanType_MagickTrue => Ok(()), _ => Err(MagickError(concat!(stringify!($set), " returned false"))) } diff --git a/src/wand/magick.rs b/src/wand/magick.rs index 1e594a9..31da0cf 100644 --- a/src/wand/magick.rs +++ b/src/wand/magick.rs @@ -224,19 +224,6 @@ impl MagickWand { (distortion, wand) } - pub fn get_image_compose(&self) -> CompositeOperator { - unsafe { bindings::MagickGetImageCompose(self.wand).into() } - } - - pub fn set_image_compose(&self, composite_operator: CompositeOperator) -> Result<()> { - match unsafe { bindings::MagickSetImageCompose(self.wand, composite_operator.into()) } { - bindings::MagickBooleanType_MagickTrue => Ok(()), - _ => Err(MagickError( - "Failed to set the image composite operator type", - )), - } - } - /// Compose another image onto self at (x, y) using composition_operator pub fn compose_images( &self, @@ -1143,50 +1130,6 @@ impl MagickWand { } } - pub fn get_colorspace(&self) -> ColorspaceType { - return unsafe { bindings::MagickGetColorspace(self.wand).into() }; - } - - pub fn set_colorspace(&mut self, colorspace: ColorspaceType) -> Result<()> { - match unsafe { bindings::MagickSetColorspace(self.wand, colorspace.into()) } { - bindings::MagickBooleanType_MagickTrue => Ok(()), - _ => Err(MagickError("failed to set colorspace")), - } - } - - pub fn get_image_colorspace(&self) -> ColorspaceType { - return unsafe { bindings::MagickGetImageColorspace(self.wand).into() }; - } - - pub fn set_image_colorspace(&self, colorspace: ColorspaceType) -> Result<()> { - match unsafe { bindings::MagickSetImageColorspace(self.wand, colorspace.into()) } { - bindings::MagickBooleanType_MagickTrue => Ok(()), - _ => Err(MagickError("failed to set image colorspace")), - } - } - - pub fn get_gravity(&self) -> GravityType { - return unsafe { bindings::MagickGetGravity(self.wand).into() }; - } - - pub fn set_gravity(&mut self, gravity: GravityType) -> Result<()> { - match unsafe { bindings::MagickSetGravity(self.wand, gravity.into()) } { - bindings::MagickBooleanType_MagickTrue => Ok(()), - _ => Err(MagickError("failed to set gravity")), - } - } - - pub fn get_image_gravity(&self) -> GravityType { - return unsafe { bindings::MagickGetImageGravity(self.wand).into() }; - } - - pub fn set_image_gravity(&mut self, gravity: GravityType) -> Result<()> { - match unsafe { bindings::MagickSetImageGravity(self.wand, gravity.into()) } { - bindings::MagickBooleanType_MagickTrue => Ok(()), - _ => Err(MagickError("failed to set image gravity")), - } - } - mutations!( /// Sets the image to the specified alpha level. MagickSetImageAlpha => set_image_alpha(alpha: f64) @@ -1225,8 +1168,12 @@ impl MagickWand { ); set_get!( + get_colorspace, set_colorspace, MagickGetColorspace, MagickSetColorspace, ColorspaceType + get_image_compose, set_image_compose, MagickGetImageCompose, MagickSetImageCompose, CompositeOperator get_compression, set_compression, MagickGetCompression, MagickSetCompression, bindings::CompressionType get_compression_quality, set_compression_quality, MagickGetCompressionQuality, MagickSetCompressionQuality, size_t + get_gravity, set_gravity, MagickGetGravity, MagickSetGravity, GravityType + get_image_colorspace, set_image_colorspace, MagickGetImageColorspace, MagickSetImageColorspace, ColorspaceType get_image_compression, set_image_compression, MagickGetImageCompression, MagickSetImageCompression, bindings::CompressionType get_image_compression_quality, set_image_compression_quality, MagickGetImageCompressionQuality, MagickSetImageCompressionQuality, size_t get_image_delay, set_image_delay, MagickGetImageDelay, MagickSetImageDelay, size_t @@ -1235,6 +1182,7 @@ impl MagickWand { get_image_endian, set_image_endian, MagickGetImageEndian, MagickSetImageEndian, bindings::EndianType 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, GravityType get_image_interlace_scheme, set_image_interlace_scheme, MagickGetImageInterlaceScheme, MagickSetImageInterlaceScheme, bindings::InterlaceType get_image_interpolate_method, set_image_interpolate_method, MagickGetImageInterpolateMethod, MagickSetImageInterpolateMethod, bindings::PixelInterpolateMethod get_image_iterations, set_image_iterations, MagickGetImageIterations, MagickSetImageIterations, size_t diff --git a/tests/lib.rs b/tests/lib.rs index f55c864..6d1708f 100644 --- a/tests/lib.rs +++ b/tests/lib.rs @@ -422,7 +422,7 @@ fn test_image_compose() { START.call_once(|| { magick_wand_genesis(); }); - let wand = MagickWand::new(); + let mut wand = MagickWand::new(); wand.new_image(4, 4, &PixelWand::new()).unwrap(); let operators = [ From 1914b95f7b89fa9bc723f8258cde73b6b6a74019 Mon Sep 17 00:00:00 2001 From: 5ohue <86558263+5ohue@users.noreply.github.com> Date: Sat, 11 May 2024 13:06:52 +0300 Subject: [PATCH 02/37] Add `PixelInterpolateMethod` type --- src/types/mod.rs | 2 ++ src/types/pixel_interpolate_method.rs | 47 +++++++++++++++++++++++++++ src/wand/magick.rs | 3 +- 3 files changed, 51 insertions(+), 1 deletion(-) create mode 100644 src/types/pixel_interpolate_method.rs diff --git a/src/types/mod.rs b/src/types/mod.rs index d1c3572..476bcba 100644 --- a/src/types/mod.rs +++ b/src/types/mod.rs @@ -3,6 +3,7 @@ mod composite_operator; mod dither_method; mod filter_type; mod gravity_type; +mod pixel_interpolate_method; mod metric_type; mod resource_type; @@ -11,5 +12,6 @@ pub use self::composite_operator::CompositeOperator; pub use self::dither_method::DitherMethod; pub use self::filter_type::FilterType; pub use self::gravity_type::GravityType; +pub use self::pixel_interpolate_method::PixelInterpolateMethod; pub use self::metric_type::MetricType; pub use self::resource_type::ResourceType; diff --git a/src/types/pixel_interpolate_method.rs b/src/types/pixel_interpolate_method.rs new file mode 100644 index 0000000..d99c6c4 --- /dev/null +++ b/src/types/pixel_interpolate_method.rs @@ -0,0 +1,47 @@ +use crate::bindings; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[repr(u32)] +pub enum PixelInterpolateMethod { + Undefined = bindings::PixelInterpolateMethod_UndefinedInterpolatePixel, + Average = bindings::PixelInterpolateMethod_AverageInterpolatePixel, + Average9 = bindings::PixelInterpolateMethod_Average9InterpolatePixel, + Average16 = bindings::PixelInterpolateMethod_Average16InterpolatePixel, + Background = bindings::PixelInterpolateMethod_BackgroundInterpolatePixel, + Bilinear = bindings::PixelInterpolateMethod_BilinearInterpolatePixel, + Blend = bindings::PixelInterpolateMethod_BlendInterpolatePixel, + Catrom = bindings::PixelInterpolateMethod_CatromInterpolatePixel, + Integer = bindings::PixelInterpolateMethod_IntegerInterpolatePixel, + Mesh = bindings::PixelInterpolateMethod_MeshInterpolatePixel, + Nearest = bindings::PixelInterpolateMethod_NearestInterpolatePixel, + Spline = bindings::PixelInterpolateMethod_SplineInterpolatePixel, +} + +impl Default for PixelInterpolateMethod { + fn default() -> Self { + return PixelInterpolateMethod::Undefined; + } +} + +impl From for bindings::PixelInterpolateMethod { + fn from(value: PixelInterpolateMethod) -> Self { + return value as bindings::PixelInterpolateMethod; + } +} + +impl From for PixelInterpolateMethod { + fn from(value: bindings::PixelInterpolateMethod) -> Self { + /* + * SAFETY: + * + * `PixelInterpolateMethod` has the same repr as `bindings::PixelInterpolateMethod` - u32 + * + * If `value` is less than Spline than it is in the vaild range and can be safely + * reinterpreted as `PixelInterpolateMethod` + */ + if value <= bindings::PixelInterpolateMethod_SplineInterpolatePixel { + return unsafe { std::mem::transmute(value) }; + } + return PixelInterpolateMethod::default(); + } +} diff --git a/src/wand/magick.rs b/src/wand/magick.rs index 31da0cf..2ec0b18 100644 --- a/src/wand/magick.rs +++ b/src/wand/magick.rs @@ -35,6 +35,7 @@ use crate::{ DitherMethod, FilterType, GravityType, + PixelInterpolateMethod, MetricType, ResourceType }; @@ -1184,7 +1185,7 @@ impl MagickWand { get_image_gamma, set_image_gamma, MagickGetImageGamma, MagickSetImageGamma, f64 get_image_gravity, set_image_gravity, MagickGetImageGravity, MagickSetImageGravity, GravityType get_image_interlace_scheme, set_image_interlace_scheme, MagickGetImageInterlaceScheme, MagickSetImageInterlaceScheme, bindings::InterlaceType - get_image_interpolate_method, set_image_interpolate_method, MagickGetImageInterpolateMethod, MagickSetImageInterpolateMethod, bindings::PixelInterpolateMethod + get_image_interpolate_method, set_image_interpolate_method, MagickGetImageInterpolateMethod, MagickSetImageInterpolateMethod, PixelInterpolateMethod get_image_iterations, set_image_iterations, MagickGetImageIterations, MagickSetImageIterations, size_t get_image_orientation, set_image_orientation, MagickGetImageOrientation, MagickSetImageOrientation, bindings::OrientationType get_image_rendering_intent, set_image_rendering_intent, MagickGetImageRenderingIntent, MagickSetImageRenderingIntent, bindings::RenderingIntent From 02ae78eba8d9ab598c7adb16e3270a0984abe908 Mon Sep 17 00:00:00 2001 From: 5ohue <86558263+5ohue@users.noreply.github.com> Date: Sat, 11 May 2024 13:18:47 +0300 Subject: [PATCH 03/37] Replace `size_t` with `usize` in the API --- src/lib.rs | 2 -- src/wand/macros.rs | 2 +- src/wand/magick.rs | 64 +++++++++++++++++++++++----------------------- 3 files changed, 33 insertions(+), 35 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 78a2dc1..b7d5877 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -33,8 +33,6 @@ extern crate libc; use libc::size_t; -#[cfg(not(target_os = "freebsd"))] -use libc::ssize_t; pub use conversions::ToMagick; pub use result::MagickError; diff --git a/src/wand/macros.rs b/src/wand/macros.rs index 2036f57..8a1e2bf 100644 --- a/src/wand/macros.rs +++ b/src/wand/macros.rs @@ -100,7 +100,7 @@ macro_rules! get { ($($get:ident, $c_get:ident, $typ:ty )*) => { $( pub fn $get(&self) -> $typ { - unsafe { ::bindings::$c_get(self.wand) } + unsafe { ::bindings::$c_get(self.wand).into() } } )* } diff --git a/src/wand/magick.rs b/src/wand/magick.rs index 2ec0b18..35af147 100644 --- a/src/wand/magick.rs +++ b/src/wand/magick.rs @@ -18,13 +18,13 @@ use std::{fmt, ptr, slice}; use libc::c_void; #[cfg(target_os = "freebsd")] -use libc::{size_t, ssize_t}; +use libc::size_t; use bindings; use conversions::*; use result::MagickError; #[cfg(not(target_os = "freebsd"))] -use {size_t, ssize_t}; +use size_t; use crate::result::Result; @@ -59,8 +59,8 @@ wand_common!( /// When the `MagickWand` is dropped, the ImageMagick wand will be /// destroyed as well. impl MagickWand { - pub fn new_image(&self, columns: size_t, rows: size_t, pixel_wand: &PixelWand) -> Result<()> { - match unsafe { bindings::MagickNewImage(self.wand, columns, rows, pixel_wand.wand) } { + pub fn new_image(&self, columns: usize, rows: usize, pixel_wand: &PixelWand) -> Result<()> { + match unsafe { bindings::MagickNewImage(self.wand, columns.into(), rows.into(), pixel_wand.wand) } { bindings::MagickBooleanType_MagickTrue => Ok(()), _ => Err(MagickError("Could not create image")), } @@ -168,7 +168,7 @@ impl MagickWand { bindings::MagickReadImageBlob( self.wand, int_slice.as_ptr() as *const c_void, - size as size_t, + size.into(), ) }; match result { @@ -197,7 +197,7 @@ impl MagickWand { bindings::MagickPingImageBlob( self.wand, int_slice.as_ptr() as *const c_void, - size as size_t, + size.into(), ) }; match result { @@ -319,8 +319,8 @@ impl MagickWand { MagickWand::new_from_wand(wand) } - pub fn set_size(&self, columns: size_t, rows: size_t) -> Result<()> { - let result = unsafe { bindings::MagickSetSize(self.wand, columns, rows) }; + pub fn set_size(&self, columns: usize, rows: usize) -> Result<()> { + let result = unsafe { bindings::MagickSetSize(self.wand, columns.into(), rows.into()) }; match result { bindings::MagickBooleanType_MagickTrue => Ok(()), _ => Err(MagickError("failed to set size of wand")), @@ -772,7 +772,7 @@ impl MagickWand { /// specified filter type. pub fn resize_image(&self, width: usize, height: usize, filter: FilterType) { unsafe { - bindings::MagickResizeImage(self.wand, width as size_t, height as size_t, filter.into()); + bindings::MagickResizeImage(self.wand, width.into(), height.into(), filter.into()); } } @@ -781,7 +781,7 @@ impl MagickWand { /// of producing small low cost images suited for display on the web. pub fn thumbnail_image(&self, width: usize, height: usize) { unsafe { - bindings::MagickThumbnailImage(self.wand, width as size_t, height as size_t); + bindings::MagickThumbnailImage(self.wand, width.into(), height.into()); } } @@ -846,7 +846,7 @@ impl MagickWand { /// Resize the image to fit within the given dimensions, maintaining /// the current aspect ratio. - pub fn fit(&self, width: size_t, height: size_t) { + 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; @@ -854,11 +854,11 @@ impl MagickWand { let (new_width, new_height) = if width_ratio < height_ratio { ( width, - (self.get_image_height() as f64 * width_ratio) as size_t, + (self.get_image_height() as f64 * width_ratio) as usize, ) } else { ( - (self.get_image_width() as f64 * height_ratio) as size_t, + (self.get_image_width() as f64 * height_ratio) as usize, height, ) }; @@ -867,8 +867,8 @@ impl MagickWand { while bindings::MagickNextImage(self.wand) != bindings::MagickBooleanType_MagickFalse { bindings::MagickResizeImage( self.wand, - new_width, - new_height, + new_width.into(), + new_height.into(), bindings::FilterType_LanczosFilter, ); } @@ -1094,16 +1094,16 @@ impl MagickWand { /// Reduce the number of colors in the image. pub fn quantize_image( &self, - number_of_colors: size_t, + number_of_colors: usize, colorspace: ColorspaceType, - tree_depth: size_t, + tree_depth: usize, dither_method: DitherMethod, measure_error: bool) -> Result<()> { match unsafe { bindings::MagickQuantizeImage( self.wand, - number_of_colors, + number_of_colors.into(), colorspace.into(), - tree_depth, + tree_depth.into(), dither_method.into(), measure_error.to_magick()) } { bindings::MagickBooleanType_MagickTrue => Ok(()), @@ -1114,16 +1114,16 @@ impl MagickWand { /// Reduce the number of colors in the images. pub fn quantize_images( &self, - number_of_colors: size_t, + number_of_colors: usize, colorspace: ColorspaceType, - tree_depth: size_t, + tree_depth: usize, dither_method: DitherMethod, measure_error: bool) -> Result<()> { match unsafe { bindings::MagickQuantizeImages( self.wand, - number_of_colors, + number_of_colors.into(), colorspace.into(), - tree_depth, + tree_depth.into(), dither_method.into(), measure_error.to_magick()) } { bindings::MagickBooleanType_MagickTrue => Ok(()), @@ -1149,7 +1149,7 @@ impl MagickWand { MagickUniqueImageColors => unique_image_colors() /// Applies k-means color reduction to the image. - MagickKmeansImage => kmeans(number_colors: size_t, max_iterations: size_t, tolerance: f64) + MagickKmeansImage => kmeans(number_colors: usize, max_iterations: usize, tolerance: f64) /// Extracts the 'mean' from the image and adjust the image to try make set its gamma appropriately. MagickAutoGammaImage => auto_gamma() @@ -1158,7 +1158,7 @@ impl MagickWand { MagickAutoLevelImage => auto_level() ); - get!(get_image_colors, MagickGetImageColors, size_t); + get!(get_image_colors, MagickGetImageColors, usize); string_set_get!( get_filename, set_filename, MagickGetFilename, MagickSetFilename @@ -1172,13 +1172,13 @@ impl MagickWand { get_colorspace, set_colorspace, MagickGetColorspace, MagickSetColorspace, ColorspaceType get_image_compose, set_image_compose, MagickGetImageCompose, MagickSetImageCompose, CompositeOperator get_compression, set_compression, MagickGetCompression, MagickSetCompression, bindings::CompressionType - get_compression_quality, set_compression_quality, MagickGetCompressionQuality, MagickSetCompressionQuality, size_t + get_compression_quality, set_compression_quality, MagickGetCompressionQuality, MagickSetCompressionQuality, usize get_gravity, set_gravity, MagickGetGravity, MagickSetGravity, GravityType get_image_colorspace, set_image_colorspace, MagickGetImageColorspace, MagickSetImageColorspace, ColorspaceType get_image_compression, set_image_compression, MagickGetImageCompression, MagickSetImageCompression, bindings::CompressionType - 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_compression_quality, set_image_compression_quality, MagickGetImageCompressionQuality, MagickSetImageCompressionQuality, usize + get_image_delay, set_image_delay, MagickGetImageDelay, MagickSetImageDelay, usize + get_image_depth, set_image_depth, MagickGetImageDepth, MagickSetImageDepth, usize get_image_dispose, set_image_dispose, MagickGetImageDispose, MagickSetImageDispose, bindings::DisposeType get_image_endian, set_image_endian, MagickGetImageEndian, MagickSetImageEndian, bindings::EndianType get_image_fuzz, set_image_fuzz, MagickGetImageFuzz, MagickSetImageFuzz, f64 @@ -1186,15 +1186,15 @@ impl MagickWand { get_image_gravity, set_image_gravity, MagickGetImageGravity, MagickSetImageGravity, GravityType get_image_interlace_scheme, set_image_interlace_scheme, MagickGetImageInterlaceScheme, MagickSetImageInterlaceScheme, bindings::InterlaceType get_image_interpolate_method, set_image_interpolate_method, MagickGetImageInterpolateMethod, MagickSetImageInterpolateMethod, PixelInterpolateMethod - get_image_iterations, set_image_iterations, MagickGetImageIterations, MagickSetImageIterations, size_t + get_image_iterations, set_image_iterations, MagickGetImageIterations, MagickSetImageIterations, usize get_image_orientation, set_image_orientation, MagickGetImageOrientation, MagickSetImageOrientation, bindings::OrientationType get_image_rendering_intent, set_image_rendering_intent, MagickGetImageRenderingIntent, MagickSetImageRenderingIntent, bindings::RenderingIntent - get_image_scene, set_image_scene, MagickGetImageScene, MagickSetImageScene, size_t + get_image_scene, set_image_scene, MagickGetImageScene, MagickSetImageScene, usize get_image_type, set_image_type, MagickGetImageType, MagickSetImageType, bindings::ImageType get_image_units, set_image_units, MagickGetImageUnits, MagickSetImageUnits, bindings::ResolutionType get_interlace_scheme, set_interlace_scheme, MagickGetInterlaceScheme, MagickSetInterlaceScheme, bindings::InterlaceType get_interpolate_method, set_interpolate_method, MagickGetInterpolateMethod, MagickSetInterpolateMethod, bindings::PixelInterpolateMethod - get_iterator_index, set_iterator_index, MagickGetIteratorIndex, MagickSetIteratorIndex, ssize_t + get_iterator_index, set_iterator_index, MagickGetIteratorIndex, MagickSetIteratorIndex, isize get_orientation, set_orientation, MagickGetOrientation, MagickSetOrientation, bindings::OrientationType get_pointsize, set_pointsize, MagickGetPointsize, MagickSetPointsize, f64 get_type, set_type, MagickGetType, MagickSetType, bindings::ImageType From 092acaa5d070fbbda27ce41aa06204673eff42ca Mon Sep 17 00:00:00 2001 From: 5ohue <86558263+5ohue@users.noreply.github.com> Date: Sat, 11 May 2024 13:37:55 +0300 Subject: [PATCH 04/37] Add `AlphaChannelOption` type --- src/types/alpha_channel_option.rs | 52 +++++++++++++++++++++++++++++++ src/types/mod.rs | 2 ++ src/wand/macros.rs | 2 +- src/wand/magick.rs | 4 +-- tests/lib.rs | 2 +- 5 files changed, 58 insertions(+), 4 deletions(-) create mode 100644 src/types/alpha_channel_option.rs diff --git a/src/types/alpha_channel_option.rs b/src/types/alpha_channel_option.rs new file mode 100644 index 0000000..51d8551 --- /dev/null +++ b/src/types/alpha_channel_option.rs @@ -0,0 +1,52 @@ +use crate::bindings; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[repr(u32)] +pub enum AlphaChannelOption { + Undefined = bindings::AlphaChannelOption_UndefinedAlphaChannel, + Activate = bindings::AlphaChannelOption_ActivateAlphaChannel, + Associate = bindings::AlphaChannelOption_AssociateAlphaChannel, + Background = bindings::AlphaChannelOption_BackgroundAlphaChannel, + Copy = bindings::AlphaChannelOption_CopyAlphaChannel, + Deactivate = bindings::AlphaChannelOption_DeactivateAlphaChannel, + Discrete = bindings::AlphaChannelOption_DiscreteAlphaChannel, + Disassociate = bindings::AlphaChannelOption_DisassociateAlphaChannel, + Extract = bindings::AlphaChannelOption_ExtractAlphaChannel, + Off = bindings::AlphaChannelOption_OffAlphaChannel, + On = bindings::AlphaChannelOption_OnAlphaChannel, + Opaque = bindings::AlphaChannelOption_OpaqueAlphaChannel, + Remove = bindings::AlphaChannelOption_RemoveAlphaChannel, + Set = bindings::AlphaChannelOption_SetAlphaChannel, + Shape = bindings::AlphaChannelOption_ShapeAlphaChannel, + Transparent = bindings::AlphaChannelOption_TransparentAlphaChannel, + OffIfOpaque = bindings::AlphaChannelOption_OffIfOpaqueAlphaChannel, +} + +impl Default for AlphaChannelOption { + fn default() -> Self { + return AlphaChannelOption::Undefined; + } +} + +impl From for bindings::AlphaChannelOption { + fn from(value: AlphaChannelOption) -> Self { + return value as bindings::AlphaChannelOption; + } +} + +impl From for AlphaChannelOption { + fn from(value: bindings::AlphaChannelOption) -> Self { + /* + * SAFETY: + * + * `AlphaChannelOption` has the same repr as `bindings::AlphaChannelOption` - u32 + * + * If `value` is less than OffIfOpaque than it is in the vaild range and can be safely + * reinterpreted as `AlphaChannelOption` + */ + if value <= bindings::AlphaChannelOption_OffIfOpaqueAlphaChannel { + return unsafe { std::mem::transmute(value) }; + } + return AlphaChannelOption::default(); + } +} diff --git a/src/types/mod.rs b/src/types/mod.rs index 476bcba..7a652b0 100644 --- a/src/types/mod.rs +++ b/src/types/mod.rs @@ -1,3 +1,4 @@ +mod alpha_channel_option; mod colorspace_type; mod composite_operator; mod dither_method; @@ -7,6 +8,7 @@ mod pixel_interpolate_method; mod metric_type; mod resource_type; +pub use self::alpha_channel_option::AlphaChannelOption; pub use self::colorspace_type::ColorspaceType; pub use self::composite_operator::CompositeOperator; pub use self::dither_method::DitherMethod; diff --git a/src/wand/macros.rs b/src/wand/macros.rs index 8a1e2bf..f7006f5 100644 --- a/src/wand/macros.rs +++ b/src/wand/macros.rs @@ -258,7 +258,7 @@ macro_rules! mutations { $( $(#[$attr])* pub fn $fun(&self $(, $arg: $ty)*) -> Result<()> { - match unsafe { bindings::$c_fun(self.wand $(, $arg)*) } { + match unsafe { bindings::$c_fun(self.wand $(, $arg.into())*) } { bindings::MagickBooleanType_MagickTrue => Ok(()), _ => Err(MagickError(concat!(stringify!($c_fun), " invocation failed"))) } diff --git a/src/wand/magick.rs b/src/wand/magick.rs index 35af147..56b8567 100644 --- a/src/wand/magick.rs +++ b/src/wand/magick.rs @@ -30,6 +30,7 @@ use crate::result::Result; use super::{DrawingWand, PixelWand}; use crate::{ + AlphaChannelOption, ColorspaceType, CompositeOperator, DitherMethod, @@ -1142,8 +1143,7 @@ impl MagickWand { MagickBrightnessContrastImage => brightness_contrast_image(brightness: f64, contrast: f64) /// Set the image alpha channel mode. - MagickSetImageAlphaChannel => set_image_alpha_channel( - alpha_channel: bindings::AlphaChannelOption) + MagickSetImageAlphaChannel => set_image_alpha_channel(alpha_channel: AlphaChannelOption) /// Discard all but one of any pixel color. MagickUniqueImageColors => unique_image_colors() diff --git a/tests/lib.rs b/tests/lib.rs index 6d1708f..2e2440d 100644 --- a/tests/lib.rs +++ b/tests/lib.rs @@ -317,7 +317,7 @@ fn test_set_image_background_color() { let mut pw = PixelWand::new(); pw.set_color("#0000FF").unwrap(); wand.set_image_background_color(&pw).unwrap(); - wand.set_image_alpha_channel(bindings::AlphaChannelOption_RemoveAlphaChannel) + wand.set_image_alpha_channel(magick_rust::AlphaChannelOption::Remove) .unwrap(); let blob = wand.write_image_blob("rgb").unwrap(); assert_eq!(0u8, blob[0]); From cac32a97602eeff8ebc88f1140be76472aa26a40 Mon Sep 17 00:00:00 2001 From: 5ohue <86558263+5ohue@users.noreply.github.com> Date: Sat, 11 May 2024 13:41:09 +0300 Subject: [PATCH 05/37] Add more `PixelInterpolateMethod` in API --- src/wand/magick.rs | 4 ++-- tests/lib.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/wand/magick.rs b/src/wand/magick.rs index 56b8567..d8e7123 100644 --- a/src/wand/magick.rs +++ b/src/wand/magick.rs @@ -293,9 +293,9 @@ impl MagickWand { pub fn clut_image( &self, clut_wand: &MagickWand, - method: bindings::PixelInterpolateMethod, + method: PixelInterpolateMethod, ) -> Result<()> { - let result = unsafe { bindings::MagickClutImage(self.wand, clut_wand.wand, method) }; + let result = unsafe { bindings::MagickClutImage(self.wand, clut_wand.wand, method.into()) }; match result { bindings::MagickBooleanType_MagickTrue => Ok(()), _ => Err(MagickError( diff --git a/tests/lib.rs b/tests/lib.rs index 2e2440d..5d01510 100644 --- a/tests/lib.rs +++ b/tests/lib.rs @@ -364,7 +364,7 @@ fn test_clut_image() { assert!(wand .clut_image( &gradient, - bindings::PixelInterpolateMethod_BilinearInterpolatePixel + magick_rust::PixelInterpolateMethod::Bilinear ) .is_ok()); } From eb982bbf1d37a4c32b68b068f515bf9a3ecbde9a Mon Sep 17 00:00:00 2001 From: 5ohue <86558263+5ohue@users.noreply.github.com> Date: Sat, 11 May 2024 13:57:29 +0300 Subject: [PATCH 06/37] Add alias to `bindings::MagickBoolean_*` Reduces the amount of repetitive `bindingc::MagickBoolean_*` in wrapper code --- src/wand/drawing.rs | 12 ++-- src/wand/macros.rs | 4 +- src/wand/magick.rs | 131 ++++++++++++++++++++++---------------------- src/wand/mod.rs | 3 + src/wand/pixel.rs | 5 +- tests/lib.rs | 2 +- 6 files changed, 79 insertions(+), 78 deletions(-) diff --git a/src/wand/drawing.rs b/src/wand/drawing.rs index 6740123..16012d8 100644 --- a/src/wand/drawing.rs +++ b/src/wand/drawing.rs @@ -16,15 +16,11 @@ use std::ffi::{CStr, CString}; use std::fmt; -#[cfg(target_os = "freebsd")] -use libc::size_t; -#[cfg(not(target_os = "freebsd"))] -use size_t; - use bindings; use crate::result::MagickError; use crate::result::Result; +use crate::GravityType; wand_common!( DrawingWand, @@ -91,7 +87,7 @@ impl DrawingWand { ); set_get_unchecked!( - get_gravity, set_gravity, DrawGetGravity, DrawSetGravity, bindings::GravityType + get_gravity, set_gravity, DrawGetGravity, DrawSetGravity, GravityType get_opacity, set_opacity, DrawGetOpacity, DrawSetOpacity, f64 get_clip_rule, set_clip_rule, DrawGetClipRule, DrawSetClipRule, bindings::FillRule get_clip_units, set_clip_units, DrawGetClipUnits, DrawSetClipUnits, bindings::ClipPathUnits @@ -100,13 +96,13 @@ impl DrawingWand { get_font_size, set_font_size, DrawGetFontSize, DrawSetFontSize, f64 get_font_style, set_font_style, DrawGetFontStyle, DrawSetFontStyle, bindings::StyleType - get_font_weight, set_font_weight, DrawGetFontWeight, DrawSetFontWeight, size_t + get_font_weight, set_font_weight, DrawGetFontWeight, DrawSetFontWeight, usize get_font_stretch, set_font_stretch, DrawGetFontStretch, DrawSetFontStretch, bindings::StretchType get_stroke_dash_offset, set_stroke_dash_offset, DrawGetStrokeDashOffset, DrawSetStrokeDashOffset, f64 get_stroke_line_cap, set_stroke_line_cap, DrawGetStrokeLineCap, DrawSetStrokeLineCap, bindings::LineCap get_stroke_line_join, set_stroke_line_join, DrawGetStrokeLineJoin, DrawSetStrokeLineJoin, bindings::LineJoin - get_stroke_miter_limit, set_stroke_miter_limit, DrawGetStrokeMiterLimit, DrawSetStrokeMiterLimit, size_t + get_stroke_miter_limit, set_stroke_miter_limit, DrawGetStrokeMiterLimit, DrawSetStrokeMiterLimit, usize 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, bindings::MagickBooleanType diff --git a/src/wand/macros.rs b/src/wand/macros.rs index f7006f5..437f6d2 100644 --- a/src/wand/macros.rs +++ b/src/wand/macros.rs @@ -130,10 +130,10 @@ 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) } + unsafe { ::bindings::$c_get(self.wand).into() } } pub fn $set(&mut self, v: $typ) { - unsafe { ::bindings::$c_set(self.wand, v) } + unsafe { ::bindings::$c_set(self.wand, v.into()) } } )* pub fn fmt_unchecked_settings(&self, f: &mut ::std::fmt::Formatter, prefix: &str) -> ::std::fmt::Result { diff --git a/src/wand/magick.rs b/src/wand/magick.rs index d8e7123..fd91938 100644 --- a/src/wand/magick.rs +++ b/src/wand/magick.rs @@ -27,6 +27,7 @@ use result::MagickError; use size_t; use crate::result::Result; +use super::{MagickTrue, MagickFalse}; use super::{DrawingWand, PixelWand}; use crate::{ @@ -62,7 +63,7 @@ wand_common!( impl MagickWand { pub fn new_image(&self, columns: usize, rows: usize, pixel_wand: &PixelWand) -> Result<()> { match unsafe { bindings::MagickNewImage(self.wand, columns.into(), rows.into(), pixel_wand.wand) } { - bindings::MagickBooleanType_MagickTrue => Ok(()), + MagickTrue => Ok(()), _ => Err(MagickError("Could not create image")), } } @@ -77,7 +78,7 @@ impl MagickWand { ) }; match result { - bindings::MagickBooleanType_MagickTrue => Ok(()), + MagickTrue => Ok(()), _ => Err(MagickError("failed to set resource limit")), } } @@ -88,7 +89,7 @@ impl MagickWand { let result = unsafe { bindings::MagickSetOption(self.wand, c_key.as_ptr(), c_value.as_ptr()) }; match result { - bindings::MagickBooleanType_MagickTrue => Ok(()), + MagickTrue => Ok(()), _ => Err(MagickError("failed to set option")), } } @@ -112,7 +113,7 @@ impl MagickWand { c_string.as_ptr() as *const _, ) } { - bindings::MagickBooleanType_MagickTrue => Ok(()), + MagickTrue => Ok(()), _ => Err(MagickError("unable to annotate image")), } } @@ -120,7 +121,7 @@ impl MagickWand { /// Add all images from another wand to this wand at the current index. pub fn add_image(&mut self, other_wand: &MagickWand) -> Result<()> { match unsafe { bindings::MagickAddImage(self.wand, other_wand.wand) } { - bindings::MagickBooleanType_MagickTrue => Ok(()), + MagickTrue => Ok(()), _ => Err(MagickError("unable to add images from another wand")), } } @@ -136,7 +137,7 @@ impl MagickWand { let c_label = CString::new(label).unwrap(); let result = unsafe { bindings::MagickLabelImage(self.wand, c_label.as_ptr()) }; match result { - bindings::MagickBooleanType_MagickTrue => Ok(()), + MagickTrue => Ok(()), _ => Err(MagickError("failed to add label")), } } @@ -146,7 +147,7 @@ impl MagickWand { let result = unsafe { bindings::MagickWriteImages(self.wand, c_name.as_ptr(), adjoin.to_magick()) }; match result { - bindings::MagickBooleanType_MagickTrue => Ok(()), + MagickTrue => Ok(()), _ => Err(MagickError("failed to write images")), } } @@ -156,7 +157,7 @@ impl MagickWand { let c_name = CString::new(path).unwrap(); let result = unsafe { bindings::MagickReadImage(self.wand, c_name.as_ptr()) }; match result { - bindings::MagickBooleanType_MagickTrue => Ok(()), + MagickTrue => Ok(()), _ => Err(MagickError("failed to read image")), } } @@ -173,7 +174,7 @@ impl MagickWand { ) }; match result { - bindings::MagickBooleanType_MagickTrue => Ok(()), + MagickTrue => Ok(()), _ => Err(MagickError("failed to read image")), } } @@ -184,7 +185,7 @@ impl MagickWand { let c_name = CString::new(path).unwrap(); let result = unsafe { bindings::MagickPingImage(self.wand, c_name.as_ptr()) }; match result { - bindings::MagickBooleanType_MagickTrue => Ok(()), + MagickTrue => Ok(()), _ => Err(MagickError("failed to ping image")), } } @@ -202,7 +203,7 @@ impl MagickWand { ) }; match result { - bindings::MagickBooleanType_MagickTrue => Ok(()), + MagickTrue => Ok(()), _ => Err(MagickError("failed to ping image")), } } @@ -236,9 +237,9 @@ impl MagickWand { y: isize, ) -> Result<()> { let native_clip_to_self = if clip_to_self { - bindings::MagickBooleanType_MagickTrue + MagickTrue } else { - bindings::MagickBooleanType_MagickFalse + MagickFalse }; let result = unsafe { bindings::MagickCompositeImage( @@ -251,7 +252,7 @@ impl MagickWand { ) }; match result { - bindings::MagickBooleanType_MagickTrue => Ok(()), + MagickTrue => Ok(()), _ => Err(MagickError("failed to compose images")), } } @@ -272,7 +273,7 @@ impl MagickWand { ) }; match result { - bindings::MagickBooleanType_MagickTrue => Ok(()), + MagickTrue => Ok(()), _ => Err(MagickError("failed to compose images")), } } @@ -297,7 +298,7 @@ impl MagickWand { ) -> Result<()> { let result = unsafe { bindings::MagickClutImage(self.wand, clut_wand.wand, method.into()) }; match result { - bindings::MagickBooleanType_MagickTrue => Ok(()), + MagickTrue => Ok(()), _ => Err(MagickError( "failed to replace colors in the image from color lookup table", )), @@ -307,7 +308,7 @@ impl MagickWand { pub fn hald_clut_image(&self, clut_wand: &MagickWand) -> Result<()> { let result = unsafe { bindings::MagickHaldClutImage(self.wand, clut_wand.wand) }; match result { - bindings::MagickBooleanType_MagickTrue => Ok(()), + MagickTrue => Ok(()), _ => Err(MagickError( "failed to replace colors in the image from color lookup table", )), @@ -323,7 +324,7 @@ impl MagickWand { pub fn set_size(&self, columns: usize, rows: usize) -> Result<()> { let result = unsafe { bindings::MagickSetSize(self.wand, columns.into(), rows.into()) }; match result { - bindings::MagickBooleanType_MagickTrue => Ok(()), + MagickTrue => Ok(()), _ => Err(MagickError("failed to set size of wand")), } } @@ -362,7 +363,7 @@ impl MagickWand { ) }; match result { - bindings::MagickBooleanType_MagickTrue => Ok(()), + MagickTrue => Ok(()), _ => Err(MagickError("Failed to level the image")), } } @@ -378,7 +379,7 @@ impl MagickWand { ) }; match result { - bindings::MagickBooleanType_MagickTrue => Ok(()), + MagickTrue => Ok(()), _ => Err(MagickError("Failed to apply contrast stretch to image")), } } @@ -399,7 +400,7 @@ impl MagickWand { ) }; match result { - bindings::MagickBooleanType_MagickTrue => Ok(()), + MagickTrue => Ok(()), _ => Err(MagickError("Failed to apply ordered dither to image")), } } @@ -423,7 +424,7 @@ impl MagickWand { ) }; match result { - bindings::MagickBooleanType_MagickTrue => Ok(()), + MagickTrue => Ok(()), _ => Err(MagickError("Failed to apply sigmoidal contrast to image")), } } @@ -433,7 +434,7 @@ impl MagickWand { pub fn extend_image(&self, width: usize, height: usize, x: isize, y: isize) -> Result<()> { let result = unsafe { bindings::MagickExtentImage(self.wand, width, height, x, y) }; match result { - bindings::MagickBooleanType_MagickTrue => Ok(()), + MagickTrue => Ok(()), _ => Err(MagickError("failed to extend image")), } } @@ -457,7 +458,7 @@ impl MagickWand { bindings::MagickProfileImage(self.wand, c_name.as_ptr(), profile_ptr, profile_len) }; match result { - bindings::MagickBooleanType_MagickTrue => Ok(()), + MagickTrue => Ok(()), _ => Err(MagickError("failed to profile image")), } } @@ -465,7 +466,7 @@ impl MagickWand { pub fn strip_image(&self) -> Result<()> { let result = unsafe { bindings::MagickStripImage(self.wand) }; match result { - bindings::MagickBooleanType_MagickTrue => Ok(()), + MagickTrue => Ok(()), _ => Err(MagickError("failed to strip image")), } } @@ -473,17 +474,17 @@ impl MagickWand { pub fn flip_image(&self) -> Result<()> { let result = unsafe { bindings::MagickFlipImage(self.wand) }; match result { - bindings::MagickBooleanType_MagickTrue => Ok(()), + MagickTrue => Ok(()), _ => Err(MagickError("failed to flip image")), } } pub fn negate_image(&self) -> Result<()> { let result = unsafe { - bindings::MagickNegateImage(self.wand, bindings::MagickBooleanType_MagickTrue) + bindings::MagickNegateImage(self.wand, MagickTrue) }; match result { - bindings::MagickBooleanType_MagickTrue => Ok(()), + MagickTrue => Ok(()), _ => Err(MagickError("failed to flip image")), } } @@ -491,7 +492,7 @@ impl MagickWand { pub fn flop_image(&self) -> Result<()> { let result = unsafe { bindings::MagickFlopImage(self.wand) }; match result { - bindings::MagickBooleanType_MagickTrue => Ok(()), + MagickTrue => Ok(()), _ => Err(MagickError("failed to flip image")), } } @@ -499,7 +500,7 @@ impl MagickWand { pub fn blur_image(&self, radius: f64, sigma: f64) -> Result<()> { let result = unsafe { bindings::MagickBlurImage(self.wand, radius, sigma) }; match result { - bindings::MagickBooleanType_MagickTrue => Ok(()), + MagickTrue => Ok(()), _ => Err(MagickError("failed to blur image")), } } @@ -507,7 +508,7 @@ impl MagickWand { pub fn gaussian_blur_image(&self, radius: f64, sigma: f64) -> Result<()> { let result = unsafe { bindings::MagickGaussianBlurImage(self.wand, radius, sigma) }; match result { - bindings::MagickBooleanType_MagickTrue => Ok(()), + MagickTrue => Ok(()), _ => Err(MagickError("failed to gaussian blur image")), } } @@ -515,7 +516,7 @@ impl MagickWand { /// Adaptively resize the currently selected image. pub fn adaptive_resize_image(&self, width: usize, height: usize) -> Result<()> { match unsafe { bindings::MagickAdaptiveResizeImage(self.wand, width, height) } { - bindings::MagickBooleanType_MagickTrue => Ok(()), + MagickTrue => Ok(()), _ => Err(MagickError("failed to adaptive-resize image")), } } @@ -524,7 +525,7 @@ impl MagickWand { /// filling any empty space with the background color of a given PixelWand pub fn rotate_image(&self, background: &PixelWand, degrees: f64) -> Result<()> { match unsafe { bindings::MagickRotateImage(self.wand, background.wand, degrees) } { - bindings::MagickBooleanType_MagickTrue => Ok(()), + MagickTrue => Ok(()), _ => Err(MagickError("failed to rotate image")), } } @@ -533,7 +534,7 @@ impl MagickWand { pub fn trim_image(&self, fuzz: f64) -> Result<()> { let result = unsafe { bindings::MagickTrimImage(self.wand, fuzz) }; match result { - bindings::MagickBooleanType_MagickTrue => Ok(()), + MagickTrue => Ok(()), _ => Err(MagickError("failed to trim image")), } } @@ -561,7 +562,7 @@ impl MagickWand { pub fn reset_image_page(&self, page_geometry: &str) -> Result<()> { let c_page_geometry = CString::new(page_geometry).unwrap(); let result = unsafe { bindings::MagickResetImagePage(self.wand, c_page_geometry.as_ptr()) }; - if result == bindings::MagickBooleanType_MagickTrue { + if result == MagickTrue { Ok(()) } else { Err(MagickError("Resetting page geometry failed.")) @@ -592,7 +593,7 @@ impl MagickWand { let result = unsafe { bindings::MagickSetImageProperty(self.wand, c_name.as_ptr(), c_value.as_ptr()) }; - if result == bindings::MagickBooleanType_MagickTrue { + if result == MagickTrue { Ok(()) } else { Err(MagickError("Setting image property failed.")) @@ -605,7 +606,7 @@ impl MagickWand { unsafe { if bindings::MagickGetImagePixelColor(self.wand, x, y, pw.wand) - == bindings::MagickBooleanType_MagickTrue + == MagickTrue { Some(pw) } else { @@ -625,7 +626,7 @@ impl MagickWand { &samplingFactors[0], ) } { - bindings::MagickBooleanType_MagickTrue => Ok(()), + MagickTrue => Ok(()), _ => Err(MagickError("SetSamplingFactors returned false")), } } @@ -657,7 +658,7 @@ impl MagickWand { /// pub fn sharpen_image(&self, radius: f64, sigma: f64) -> Result<()> { match unsafe { bindings::MagickSharpenImage(self.wand, radius, sigma) } { - bindings::MagickBooleanType_MagickTrue => Ok(()), + MagickTrue => Ok(()), _ => Err(MagickError("SharpenImage returned false")), } @@ -666,7 +667,7 @@ impl MagickWand { /// Set the background color. pub fn set_background_color(&self, pixel_wand: &PixelWand) -> Result<()> { match unsafe { bindings::MagickSetBackgroundColor(self.wand, pixel_wand.wand) } { - bindings::MagickBooleanType_MagickTrue => Ok(()), + MagickTrue => Ok(()), _ => Err(MagickError("SetBackgroundColor returned false")), } @@ -675,7 +676,7 @@ impl MagickWand { /// Set the image background color. pub fn set_image_background_color(&self, pixel_wand: &PixelWand) -> Result<()> { match unsafe { bindings::MagickSetImageBackgroundColor(self.wand, pixel_wand.wand) } { - bindings::MagickBooleanType_MagickTrue => Ok(()), + MagickTrue => Ok(()), _ => Err(MagickError("SetImageBackgroundColor returned false")), } @@ -687,7 +688,7 @@ impl MagickWand { let mut y_resolution = 0f64; unsafe { if bindings::MagickGetImageResolution(self.wand, &mut x_resolution, &mut y_resolution) - == bindings::MagickBooleanType_MagickTrue + == MagickTrue { Ok((x_resolution, y_resolution)) } else { @@ -700,7 +701,7 @@ impl MagickWand { pub fn set_image_resolution(&self, x_resolution: f64, y_resolution: f64) -> Result<()> { unsafe { if bindings::MagickSetImageResolution(self.wand, x_resolution, y_resolution) - == bindings::MagickBooleanType_MagickTrue + == MagickTrue { Ok(()) } else { @@ -713,7 +714,7 @@ impl MagickWand { pub fn set_resolution(&self, x_resolution: f64, y_resolution: f64) -> Result<()> { unsafe { if bindings::MagickSetResolution(self.wand, x_resolution, y_resolution) - == bindings::MagickBooleanType_MagickTrue + == MagickTrue { Ok(()) } else { @@ -726,7 +727,7 @@ impl MagickWand { pub fn sepia_tone_image(&self, threshold: f64) -> Result<()> { unsafe { if bindings::MagickSepiaToneImage(self.wand, threshold * self.quantum_range()?) - == bindings::MagickBooleanType_MagickTrue + == MagickTrue { Ok(()) } else { @@ -760,7 +761,7 @@ impl MagickWand { c_map.as_ptr(), bindings::StorageType_CharPixel, pixels.as_mut_ptr() as *mut c_void, - ) == bindings::MagickBooleanType_MagickTrue + ) == MagickTrue { Some(pixels) } else { @@ -791,7 +792,7 @@ impl MagickWand { pub fn crop_image(&self, width: usize, height: usize, x: isize, y: isize) -> Result<()> { let result = unsafe { bindings::MagickCropImage(self.wand, width, height, x, y) }; match result { - bindings::MagickBooleanType_MagickTrue => Ok(()), + MagickTrue => Ok(()), _ => Err(MagickError("failed to crop image")), } } @@ -803,7 +804,7 @@ impl MagickWand { pub fn sample_image(&self, width: usize, height: usize) -> Result<()> { let result = unsafe { bindings::MagickSampleImage(self.wand, width, height) }; match result { - bindings::MagickBooleanType_MagickTrue => Ok(()), + MagickTrue => Ok(()), _ => Err(MagickError("failed to sample image")), } } @@ -832,7 +833,7 @@ impl MagickWand { match unsafe { bindings::MagickLiquidRescaleImage(self.wand, width, height, delta_x, rigidity) } { - bindings::MagickBooleanType_MagickTrue => Ok(()), + MagickTrue => Ok(()), _ => Err(MagickError("failed to liquid-rescale image")), } } @@ -840,7 +841,7 @@ impl MagickWand { /// Implodes the image towards the center by the specified percentage pub fn implode(&self, amount: f64, method: bindings::PixelInterpolateMethod) -> Result<()> { match unsafe { bindings::MagickImplodeImage(self.wand, amount, method) } { - bindings::MagickBooleanType_MagickTrue => Ok(()), + MagickTrue => Ok(()), _ => Err(MagickError("failed to implode image")), } } @@ -865,7 +866,7 @@ impl MagickWand { }; unsafe { bindings::MagickResetIterator(self.wand); - while bindings::MagickNextImage(self.wand) != bindings::MagickBooleanType_MagickFalse { + while bindings::MagickNextImage(self.wand) != MagickFalse { bindings::MagickResizeImage( self.wand, new_width.into(), @@ -891,7 +892,7 @@ impl MagickWand { /// Returns `true` if successful or `false` if an error occurred. pub fn auto_orient(&self) -> bool { unsafe { - bindings::MagickAutoOrientImage(self.wand) == bindings::MagickBooleanType_MagickTrue + bindings::MagickAutoOrientImage(self.wand) == MagickTrue } } @@ -900,7 +901,7 @@ impl MagickWand { let c_name = CString::new(path).unwrap(); let result = unsafe { bindings::MagickWriteImage(self.wand, c_name.as_ptr()) }; match result { - bindings::MagickBooleanType_MagickTrue => Ok(()), + MagickTrue => Ok(()), _ => Err(MagickError("failed to write image")), } } @@ -955,13 +956,13 @@ impl MagickWand { /// That is, the image is RGB rather than RGBA or CMYK rather than CMYKA pub fn get_image_alpha_channel(&self) -> bool { let res = unsafe { bindings::MagickGetImageAlphaChannel(self.wand) }; - res == bindings::MagickBooleanType_MagickTrue + res == MagickTrue } /// Renders the drawing wand on the current image pub fn draw_image(&mut self, drawing_wand: &DrawingWand) -> Result<()> { match unsafe { bindings::MagickDrawImage(self.wand, drawing_wand.wand) } { - bindings::MagickBooleanType_MagickTrue => Ok(()), + MagickTrue => Ok(()), _ => Err(MagickError("unable to draw image")), } } @@ -972,7 +973,7 @@ impl MagickWand { /// not placed completely flat when scanned pub fn deskew_image(&mut self, threshold: f64) -> Result<()> { match unsafe { bindings::MagickDeskewImage(self.wand, threshold) } { - bindings::MagickBooleanType_MagickTrue => Ok(()), + MagickTrue => Ok(()), _ => Err(MagickError("unable to deskew image")), } } @@ -992,7 +993,7 @@ impl MagickWand { pub fn evaluate_image(&mut self, op: bindings::MagickEvaluateOperator, val: f64) -> Result<()> { let res = unsafe { bindings::MagickEvaluateImage(self.wand, op, val) }; match res { - bindings::MagickBooleanType_MagickTrue => Ok(()), + MagickTrue => Ok(()), _ => Err(MagickError("failed to evaluate image")), } } @@ -1009,7 +1010,7 @@ impl MagickWand { match unsafe { bindings::MagickBorderImage(self.wand, pixel_wand.wand, width, height, compose.into()) } { - bindings::MagickBooleanType_MagickTrue => Ok(()), + MagickTrue => Ok(()), _ => Err(MagickError("border image returned false")), } @@ -1019,7 +1020,7 @@ impl MagickWand { pub fn shadow_image(&self, alpha: f64, sigma: f64, x: isize, y: isize) -> Result<()> { unsafe { if bindings::MagickShadowImage(self.wand, alpha, sigma, x, y) - == bindings::MagickBooleanType_MagickTrue + == MagickTrue { Ok(()) } else { @@ -1052,7 +1053,7 @@ impl MagickWand { pixels.as_ptr() as *const libc::c_void, ) } { - bindings::MagickBooleanType_MagickTrue => Ok(()), + MagickTrue => Ok(()), _ => Err(MagickError("unable to import pixels")), } } @@ -1069,7 +1070,7 @@ impl MagickWand { /// See for more information. pub fn next_image(&self) -> bool { let res = unsafe { bindings::MagickNextImage(self.wand) }; - res == bindings::MagickBooleanType_MagickTrue + res == MagickTrue } /// Automatically performs threshold method to reduce grayscale data @@ -1078,7 +1079,7 @@ impl MagickWand { /// See for more information. pub fn auto_threshold(&self, method: bindings::AutoThresholdMethod) -> Result<()> { match unsafe { bindings::MagickAutoThresholdImage(self.wand, method) } { - bindings::MagickBooleanType_MagickTrue => Ok(()), + MagickTrue => Ok(()), _ => Err(MagickError("unable to auto threshold image")), } } @@ -1087,7 +1088,7 @@ impl MagickWand { /// the process. pub fn transform_image_colorspace(&self, colorspace: ColorspaceType) -> Result<()> { match unsafe { bindings::MagickTransformImageColorspace(self.wand, colorspace.into()) } { - bindings::MagickBooleanType_MagickTrue => Ok(()), + MagickTrue => Ok(()), _ => Err(MagickError("failed to transform image colorspace")), } } @@ -1107,7 +1108,7 @@ impl MagickWand { tree_depth.into(), dither_method.into(), measure_error.to_magick()) } { - bindings::MagickBooleanType_MagickTrue => Ok(()), + MagickTrue => Ok(()), _ => Err(MagickError("failed to quantize image")), } } @@ -1127,7 +1128,7 @@ impl MagickWand { tree_depth.into(), dither_method.into(), measure_error.to_magick()) } { - bindings::MagickBooleanType_MagickTrue => Ok(()), + MagickTrue => Ok(()), _ => Err(MagickError("failed to quantize images")), } } diff --git a/src/wand/mod.rs b/src/wand/mod.rs index 29601df..292a4ce 100644 --- a/src/wand/mod.rs +++ b/src/wand/mod.rs @@ -22,3 +22,6 @@ mod pixel; pub use self::drawing::DrawingWand; pub use self::magick::MagickWand; pub use self::pixel::{PixelWand, HSL}; + +use bindings::MagickBooleanType_MagickTrue as MagickTrue; +use bindings::MagickBooleanType_MagickFalse as MagickFalse; diff --git a/src/wand/pixel.rs b/src/wand/pixel.rs index b825404..d10ff0a 100644 --- a/src/wand/pixel.rs +++ b/src/wand/pixel.rs @@ -25,6 +25,7 @@ use bindings; use result::MagickError; use crate::result::Result; +use super::MagickTrue; #[derive(Default, Debug)] pub struct HSL { @@ -48,7 +49,7 @@ wand_common!( impl PixelWand { pub fn is_similar(&self, other: &PixelWand, fuzz: f64) -> Result<()> { match unsafe { bindings::IsPixelWandSimilar(self.wand, other.wand, fuzz) } { - bindings::MagickBooleanType_MagickTrue => Ok(()), + MagickTrue => Ok(()), _ => Err(MagickError("not similar")), } } @@ -86,7 +87,7 @@ impl PixelWand { pub fn set_color(&mut self, s: &str) -> Result<()> { let c_string = CString::new(s).map_err(|_| "could not convert to cstring")?; match unsafe { bindings::PixelSetColor(self.wand, c_string.as_ptr()) } { - bindings::MagickBooleanType_MagickTrue => Ok(()), + MagickTrue => Ok(()), _ => Err(MagickError("failed to set color")), } } diff --git a/tests/lib.rs b/tests/lib.rs index 5d01510..759fb0c 100644 --- a/tests/lib.rs +++ b/tests/lib.rs @@ -22,7 +22,7 @@ use std::io::Read; use std::path::Path; use std::sync::Once; -use magick_rust::{bindings, magick_wand_genesis, MagickWand, PixelWand}; +use magick_rust::{magick_wand_genesis, MagickWand, PixelWand}; use magick_rust::MagickError; // Used to make sure MagickWand is initialized exactly once. Note that we From 254ea6c77191c488ae0c7940ff299df1a9384f9c Mon Sep 17 00:00:00 2001 From: 5ohue <86558263+5ohue@users.noreply.github.com> Date: Sat, 11 May 2024 14:04:11 +0300 Subject: [PATCH 07/37] Add `CompressionType` type --- src/types/compression_type.rs | 64 +++++++++++++++++++++++++++++++++++ src/types/mod.rs | 2 ++ src/wand/magick.rs | 5 +-- src/wand/pixel.rs | 2 +- 4 files changed, 70 insertions(+), 3 deletions(-) create mode 100644 src/types/compression_type.rs diff --git a/src/types/compression_type.rs b/src/types/compression_type.rs new file mode 100644 index 0000000..3a3799d --- /dev/null +++ b/src/types/compression_type.rs @@ -0,0 +1,64 @@ +use crate::bindings; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[repr(u32)] +pub enum CompressionType { + Undefined = bindings::CompressionType_UndefinedCompression, + B44A = bindings::CompressionType_B44ACompression, + B44 = bindings::CompressionType_B44Compression, + BZip = bindings::CompressionType_BZipCompression, + DXT1 = bindings::CompressionType_DXT1Compression, + DXT3 = bindings::CompressionType_DXT3Compression, + DXT5 = bindings::CompressionType_DXT5Compression, + Fax = bindings::CompressionType_FaxCompression, + Group4 = bindings::CompressionType_Group4Compression, + JBIG1 = bindings::CompressionType_JBIG1Compression, + JBIG2 = bindings::CompressionType_JBIG2Compression, + JPEG2000 = bindings::CompressionType_JPEG2000Compression, + JPEG = bindings::CompressionType_JPEGCompression, + LosslessJPEG = bindings::CompressionType_LosslessJPEGCompression, + LZMA = bindings::CompressionType_LZMACompression, + LZW = bindings::CompressionType_LZWCompression, + No = bindings::CompressionType_NoCompression, + Piz = bindings::CompressionType_PizCompression, + Pxr24 = bindings::CompressionType_Pxr24Compression, + RLE = bindings::CompressionType_RLECompression, + Zip = bindings::CompressionType_ZipCompression, + ZipS = bindings::CompressionType_ZipSCompression, + Zstd = bindings::CompressionType_ZstdCompression, + WebP = bindings::CompressionType_WebPCompression, + DWAA = bindings::CompressionType_DWAACompression, + DWAB = bindings::CompressionType_DWABCompression, + BC7 = bindings::CompressionType_BC7Compression, + BC5 = bindings::CompressionType_BC5Compression, + LERC = bindings::CompressionType_LERCCompression, +} + +impl Default for CompressionType { + fn default() -> Self { + return CompressionType::Undefined; + } +} + +impl From for bindings::CompressionType { + fn from(value: CompressionType) -> Self { + return value as bindings::CompressionType; + } +} + +impl From for CompressionType { + fn from(value: bindings::CompressionType) -> Self { + /* + * SAFETY: + * + * `CompressionType` has the same repr as `bindings::CompressionType` - u32 + * + * If `value` is less than LERC than it is in the vaild range and can be safely + * reinterpreted as `CompressionType` + */ + if value <= bindings::CompressionType_LERCCompression { + return unsafe { std::mem::transmute(value) }; + } + return CompressionType::default(); + } +} diff --git a/src/types/mod.rs b/src/types/mod.rs index 7a652b0..463620c 100644 --- a/src/types/mod.rs +++ b/src/types/mod.rs @@ -1,6 +1,7 @@ mod alpha_channel_option; mod colorspace_type; mod composite_operator; +mod compression_type; mod dither_method; mod filter_type; mod gravity_type; @@ -11,6 +12,7 @@ mod resource_type; pub use self::alpha_channel_option::AlphaChannelOption; pub use self::colorspace_type::ColorspaceType; pub use self::composite_operator::CompositeOperator; +pub use self::compression_type::CompressionType; pub use self::dither_method::DitherMethod; pub use self::filter_type::FilterType; pub use self::gravity_type::GravityType; diff --git a/src/wand/magick.rs b/src/wand/magick.rs index fd91938..827deaf 100644 --- a/src/wand/magick.rs +++ b/src/wand/magick.rs @@ -34,6 +34,7 @@ use crate::{ AlphaChannelOption, ColorspaceType, CompositeOperator, + CompressionType, DitherMethod, FilterType, GravityType, @@ -1172,11 +1173,11 @@ impl MagickWand { set_get!( get_colorspace, set_colorspace, MagickGetColorspace, MagickSetColorspace, ColorspaceType get_image_compose, set_image_compose, MagickGetImageCompose, MagickSetImageCompose, CompositeOperator - get_compression, set_compression, MagickGetCompression, MagickSetCompression, bindings::CompressionType + get_compression, set_compression, MagickGetCompression, MagickSetCompression, CompressionType get_compression_quality, set_compression_quality, MagickGetCompressionQuality, MagickSetCompressionQuality, usize get_gravity, set_gravity, MagickGetGravity, MagickSetGravity, GravityType get_image_colorspace, set_image_colorspace, MagickGetImageColorspace, MagickSetImageColorspace, ColorspaceType - get_image_compression, set_image_compression, MagickGetImageCompression, MagickSetImageCompression, bindings::CompressionType + get_image_compression, set_image_compression, MagickGetImageCompression, MagickSetImageCompression, CompressionType get_image_compression_quality, set_image_compression_quality, MagickGetImageCompressionQuality, MagickSetImageCompressionQuality, usize get_image_delay, set_image_delay, MagickGetImageDelay, MagickSetImageDelay, usize get_image_depth, set_image_depth, MagickGetImageDepth, MagickSetImageDepth, usize diff --git a/src/wand/pixel.rs b/src/wand/pixel.rs index d10ff0a..d51f1d8 100644 --- a/src/wand/pixel.rs +++ b/src/wand/pixel.rs @@ -100,7 +100,7 @@ impl PixelWand { set_get_unchecked!( get_color_count, set_color_count, PixelGetColorCount, PixelSetColorCount, size_t - get_index, set_index, PixelGetIndex, PixelSetIndex, bindings::Quantum + get_index, set_index, PixelGetIndex, PixelSetIndex, f32 get_fuzz, set_fuzz, PixelGetFuzz, PixelSetFuzz, f64 ); From 8870068f27845031037fa2d23b4226e29a126290 Mon Sep 17 00:00:00 2001 From: 5ohue <86558263+5ohue@users.noreply.github.com> Date: Sat, 11 May 2024 14:08:59 +0300 Subject: [PATCH 08/37] Add `DisposeType` type --- src/types/dispose_type.rs | 43 +++++++++++++++++++++++++++++++++++++++ src/types/mod.rs | 2 ++ src/wand/magick.rs | 3 ++- 3 files changed, 47 insertions(+), 1 deletion(-) create mode 100644 src/types/dispose_type.rs diff --git a/src/types/dispose_type.rs b/src/types/dispose_type.rs new file mode 100644 index 0000000..46c632a --- /dev/null +++ b/src/types/dispose_type.rs @@ -0,0 +1,43 @@ +use crate::bindings; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[repr(u32)] +pub enum DisposeType { + /* + * Identical to `Undefined` + */ + // Unrecognized = bindings::DisposeType_UnrecognizedDispose, + Undefined = bindings::DisposeType_UndefinedDispose, + None = bindings::DisposeType_NoneDispose, + Background = bindings::DisposeType_BackgroundDispose, + Previous = bindings::DisposeType_PreviousDispose, +} + +impl Default for DisposeType { + fn default() -> Self { + return DisposeType::Undefined; + } +} + +impl From for bindings::DisposeType { + fn from(value: DisposeType) -> Self { + return value as bindings::DisposeType; + } +} + +impl From for DisposeType { + fn from(value: bindings::DisposeType) -> Self { + /* + * SAFETY: + * + * `DisposeType` has the same repr as `bindings::DisposeType` - u32 + * + * If `value` is less than Previous than it is in the vaild range and can be safely + * reinterpreted as `DisposeType` + */ + if value <= bindings::DisposeType_PreviousDispose { + return unsafe { std::mem::transmute(value) }; + } + return DisposeType::default(); + } +} diff --git a/src/types/mod.rs b/src/types/mod.rs index 463620c..5f01a35 100644 --- a/src/types/mod.rs +++ b/src/types/mod.rs @@ -2,6 +2,7 @@ mod alpha_channel_option; mod colorspace_type; mod composite_operator; mod compression_type; +mod dispose_type; mod dither_method; mod filter_type; mod gravity_type; @@ -13,6 +14,7 @@ pub use self::alpha_channel_option::AlphaChannelOption; pub use self::colorspace_type::ColorspaceType; pub use self::composite_operator::CompositeOperator; pub use self::compression_type::CompressionType; +pub use self::dispose_type::DisposeType; pub use self::dither_method::DitherMethod; pub use self::filter_type::FilterType; pub use self::gravity_type::GravityType; diff --git a/src/wand/magick.rs b/src/wand/magick.rs index 827deaf..5758dca 100644 --- a/src/wand/magick.rs +++ b/src/wand/magick.rs @@ -35,6 +35,7 @@ use crate::{ ColorspaceType, CompositeOperator, CompressionType, + DisposeType, DitherMethod, FilterType, GravityType, @@ -1181,7 +1182,7 @@ impl MagickWand { get_image_compression_quality, set_image_compression_quality, MagickGetImageCompressionQuality, MagickSetImageCompressionQuality, usize get_image_delay, set_image_delay, MagickGetImageDelay, MagickSetImageDelay, usize get_image_depth, set_image_depth, MagickGetImageDepth, MagickSetImageDepth, usize - get_image_dispose, set_image_dispose, MagickGetImageDispose, MagickSetImageDispose, bindings::DisposeType + get_image_dispose, set_image_dispose, MagickGetImageDispose, MagickSetImageDispose, DisposeType get_image_endian, set_image_endian, MagickGetImageEndian, MagickSetImageEndian, bindings::EndianType get_image_fuzz, set_image_fuzz, MagickGetImageFuzz, MagickSetImageFuzz, f64 get_image_gamma, set_image_gamma, MagickGetImageGamma, MagickSetImageGamma, f64 From 5a3cb8a04d02e3d9bb2aadf277ba49271c4f7fd7 Mon Sep 17 00:00:00 2001 From: 5ohue <86558263+5ohue@users.noreply.github.com> Date: Sat, 11 May 2024 14:12:31 +0300 Subject: [PATCH 09/37] Add `InterlaceType` type --- src/types/interlace_type.rs | 43 +++++++++++++++++++++++++++++++++++++ src/types/mod.rs | 2 ++ src/wand/magick.rs | 7 +++--- 3 files changed, 49 insertions(+), 3 deletions(-) create mode 100644 src/types/interlace_type.rs diff --git a/src/types/interlace_type.rs b/src/types/interlace_type.rs new file mode 100644 index 0000000..4ad792e --- /dev/null +++ b/src/types/interlace_type.rs @@ -0,0 +1,43 @@ +use crate::bindings; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[repr(u32)] +pub enum InterlaceType { + Undefined = bindings::InterlaceType_UndefinedInterlace, + No = bindings::InterlaceType_NoInterlace, + Line = bindings::InterlaceType_LineInterlace, + Plane = bindings::InterlaceType_PlaneInterlace, + Partition = bindings::InterlaceType_PartitionInterlace, + GIF = bindings::InterlaceType_GIFInterlace, + JPEG = bindings::InterlaceType_JPEGInterlace, + PNG = bindings::InterlaceType_PNGInterlace, +} + +impl Default for InterlaceType { + fn default() -> Self { + return InterlaceType::Undefined; + } +} + +impl From for bindings::InterlaceType { + fn from(value: InterlaceType) -> Self { + return value as bindings::InterlaceType; + } +} + +impl From for InterlaceType { + fn from(value: bindings::InterlaceType) -> Self { + /* + * SAFETY: + * + * `InterlaceType` has the same repr as `bindings::InterlaceType` - u32 + * + * If `value` is less than PNG than it is in the vaild range and can be safely + * reinterpreted as `InterlaceType` + */ + if value <= bindings::InterlaceType_PNGInterlace { + return unsafe { std::mem::transmute(value) }; + } + return InterlaceType::default(); + } +} diff --git a/src/types/mod.rs b/src/types/mod.rs index 5f01a35..d4792d5 100644 --- a/src/types/mod.rs +++ b/src/types/mod.rs @@ -6,6 +6,7 @@ mod dispose_type; mod dither_method; mod filter_type; mod gravity_type; +mod interlace_type; mod pixel_interpolate_method; mod metric_type; mod resource_type; @@ -18,6 +19,7 @@ pub use self::dispose_type::DisposeType; pub use self::dither_method::DitherMethod; pub use self::filter_type::FilterType; pub use self::gravity_type::GravityType; +pub use self::interlace_type::InterlaceType; pub use self::pixel_interpolate_method::PixelInterpolateMethod; pub use self::metric_type::MetricType; pub use self::resource_type::ResourceType; diff --git a/src/wand/magick.rs b/src/wand/magick.rs index 5758dca..beacb03 100644 --- a/src/wand/magick.rs +++ b/src/wand/magick.rs @@ -39,6 +39,7 @@ use crate::{ DitherMethod, FilterType, GravityType, + InterlaceType, PixelInterpolateMethod, MetricType, ResourceType @@ -1187,7 +1188,7 @@ impl MagickWand { 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, GravityType - get_image_interlace_scheme, set_image_interlace_scheme, MagickGetImageInterlaceScheme, MagickSetImageInterlaceScheme, bindings::InterlaceType + get_image_interlace_scheme, set_image_interlace_scheme, MagickGetImageInterlaceScheme, MagickSetImageInterlaceScheme, InterlaceType get_image_interpolate_method, set_image_interpolate_method, MagickGetImageInterpolateMethod, MagickSetImageInterpolateMethod, PixelInterpolateMethod get_image_iterations, set_image_iterations, MagickGetImageIterations, MagickSetImageIterations, usize get_image_orientation, set_image_orientation, MagickGetImageOrientation, MagickSetImageOrientation, bindings::OrientationType @@ -1195,8 +1196,8 @@ impl MagickWand { get_image_scene, set_image_scene, MagickGetImageScene, MagickSetImageScene, usize get_image_type, set_image_type, MagickGetImageType, MagickSetImageType, bindings::ImageType get_image_units, set_image_units, MagickGetImageUnits, MagickSetImageUnits, bindings::ResolutionType - get_interlace_scheme, set_interlace_scheme, MagickGetInterlaceScheme, MagickSetInterlaceScheme, bindings::InterlaceType - get_interpolate_method, set_interpolate_method, MagickGetInterpolateMethod, MagickSetInterpolateMethod, bindings::PixelInterpolateMethod + get_interlace_scheme, set_interlace_scheme, MagickGetInterlaceScheme, MagickSetInterlaceScheme, InterlaceType + get_interpolate_method, set_interpolate_method, MagickGetInterpolateMethod, MagickSetInterpolateMethod, PixelInterpolateMethod get_iterator_index, set_iterator_index, MagickGetIteratorIndex, MagickSetIteratorIndex, isize get_orientation, set_orientation, MagickGetOrientation, MagickSetOrientation, bindings::OrientationType get_pointsize, set_pointsize, MagickGetPointsize, MagickSetPointsize, f64 From 0c987e78607e8cefe8cd090448b7656bb088f91e Mon Sep 17 00:00:00 2001 From: 5ohue <86558263+5ohue@users.noreply.github.com> Date: Sat, 11 May 2024 14:16:05 +0300 Subject: [PATCH 10/37] Add `EndianType` type --- src/types/endian_type.rs | 38 ++++++++++++++++++++++++++++++++++++++ src/types/mod.rs | 2 ++ src/wand/magick.rs | 3 ++- 3 files changed, 42 insertions(+), 1 deletion(-) create mode 100644 src/types/endian_type.rs diff --git a/src/types/endian_type.rs b/src/types/endian_type.rs new file mode 100644 index 0000000..406bb3a --- /dev/null +++ b/src/types/endian_type.rs @@ -0,0 +1,38 @@ +use crate::bindings; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[repr(u32)] +pub enum EndianType { + Undefined = bindings::EndianType_UndefinedEndian, + LSB = bindings::EndianType_LSBEndian, + MSB = bindings::EndianType_MSBEndian, +} + +impl Default for EndianType { + fn default() -> Self { + return EndianType::Undefined; + } +} + +impl From for bindings::EndianType { + fn from(value: EndianType) -> Self { + return value as bindings::EndianType; + } +} + +impl From for EndianType { + fn from(value: bindings::EndianType) -> Self { + /* + * SAFETY: + * + * `EndianType` has the same repr as `bindings::EndianType` - u32 + * + * If `value` is less than MSB than it is in the vaild range and can be safely + * reinterpreted as `EndianType` + */ + if value <= bindings::EndianType_MSBEndian { + return unsafe { std::mem::transmute(value) }; + } + return EndianType::default(); + } +} diff --git a/src/types/mod.rs b/src/types/mod.rs index d4792d5..9b68edb 100644 --- a/src/types/mod.rs +++ b/src/types/mod.rs @@ -4,6 +4,7 @@ mod composite_operator; mod compression_type; mod dispose_type; mod dither_method; +mod endian_type; mod filter_type; mod gravity_type; mod interlace_type; @@ -17,6 +18,7 @@ pub use self::composite_operator::CompositeOperator; pub use self::compression_type::CompressionType; pub use self::dispose_type::DisposeType; pub use self::dither_method::DitherMethod; +pub use self::endian_type::EndianType; pub use self::filter_type::FilterType; pub use self::gravity_type::GravityType; pub use self::interlace_type::InterlaceType; diff --git a/src/wand/magick.rs b/src/wand/magick.rs index beacb03..d5f8a94 100644 --- a/src/wand/magick.rs +++ b/src/wand/magick.rs @@ -37,6 +37,7 @@ use crate::{ CompressionType, DisposeType, DitherMethod, + EndianType, FilterType, GravityType, InterlaceType, @@ -1184,7 +1185,7 @@ impl MagickWand { get_image_delay, set_image_delay, MagickGetImageDelay, MagickSetImageDelay, usize get_image_depth, set_image_depth, MagickGetImageDepth, MagickSetImageDepth, usize get_image_dispose, set_image_dispose, MagickGetImageDispose, MagickSetImageDispose, DisposeType - get_image_endian, set_image_endian, MagickGetImageEndian, MagickSetImageEndian, bindings::EndianType + get_image_endian, set_image_endian, MagickGetImageEndian, MagickSetImageEndian, EndianType 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, GravityType From db0044fa07e8635f410fc44025a20d9215afb3e7 Mon Sep 17 00:00:00 2001 From: 5ohue <86558263+5ohue@users.noreply.github.com> Date: Sat, 11 May 2024 14:22:49 +0300 Subject: [PATCH 11/37] Add `OrientationType` type --- src/types/mod.rs | 6 +++-- src/types/orientation_type.rs | 44 +++++++++++++++++++++++++++++++++++ src/wand/magick.rs | 12 ++++------ src/wand/pixel.rs | 7 +----- 4 files changed, 54 insertions(+), 15 deletions(-) create mode 100644 src/types/orientation_type.rs diff --git a/src/types/mod.rs b/src/types/mod.rs index 9b68edb..deb75bb 100644 --- a/src/types/mod.rs +++ b/src/types/mod.rs @@ -8,8 +8,9 @@ mod endian_type; mod filter_type; mod gravity_type; mod interlace_type; -mod pixel_interpolate_method; mod metric_type; +mod orientation_type; +mod pixel_interpolate_method; mod resource_type; pub use self::alpha_channel_option::AlphaChannelOption; @@ -22,6 +23,7 @@ pub use self::endian_type::EndianType; pub use self::filter_type::FilterType; pub use self::gravity_type::GravityType; pub use self::interlace_type::InterlaceType; -pub use self::pixel_interpolate_method::PixelInterpolateMethod; pub use self::metric_type::MetricType; +pub use self::orientation_type::OrientationType; +pub use self::pixel_interpolate_method::PixelInterpolateMethod; pub use self::resource_type::ResourceType; diff --git a/src/types/orientation_type.rs b/src/types/orientation_type.rs new file mode 100644 index 0000000..af2e8e7 --- /dev/null +++ b/src/types/orientation_type.rs @@ -0,0 +1,44 @@ +use crate::bindings; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[repr(u32)] +pub enum OrientationType { + Undefined = bindings::OrientationType_UndefinedOrientation, + TopLeft = bindings::OrientationType_TopLeftOrientation, + TopRight = bindings::OrientationType_TopRightOrientation, + BottomRight = bindings::OrientationType_BottomRightOrientation, + BottomLeft = bindings::OrientationType_BottomLeftOrientation, + LeftTop = bindings::OrientationType_LeftTopOrientation, + RightTop = bindings::OrientationType_RightTopOrientation, + RightBottom = bindings::OrientationType_RightBottomOrientation, + LeftBottom = bindings::OrientationType_LeftBottomOrientation, +} + +impl Default for OrientationType { + fn default() -> Self { + return OrientationType::Undefined; + } +} + +impl From for bindings::OrientationType { + fn from(value: OrientationType) -> Self { + return value as bindings::OrientationType; + } +} + +impl From for OrientationType { + fn from(value: bindings::OrientationType) -> Self { + /* + * SAFETY: + * + * `OrientationType` has the same repr as `bindings::OrientationType` - u32 + * + * If `value` is less than LeftBottom than it is in the vaild range and can be safely + * reinterpreted as `OrientationType` + */ + if value <= bindings::OrientationType_LeftBottomOrientation { + return unsafe { std::mem::transmute(value) }; + } + return OrientationType::default(); + } +} diff --git a/src/wand/magick.rs b/src/wand/magick.rs index d5f8a94..d9de4e2 100644 --- a/src/wand/magick.rs +++ b/src/wand/magick.rs @@ -41,8 +41,9 @@ use crate::{ FilterType, GravityType, InterlaceType, - PixelInterpolateMethod, MetricType, + OrientationType, + PixelInterpolateMethod, ResourceType }; @@ -884,10 +885,7 @@ impl MagickWand { /// 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::OrientationType_TopLeftOrientation - } + return self.get_image_orientation() != OrientationType::TopLeft; } /// Automatically adjusts the loaded image so that its orientation is @@ -1192,7 +1190,7 @@ impl MagickWand { get_image_interlace_scheme, set_image_interlace_scheme, MagickGetImageInterlaceScheme, MagickSetImageInterlaceScheme, InterlaceType get_image_interpolate_method, set_image_interpolate_method, MagickGetImageInterpolateMethod, MagickSetImageInterpolateMethod, PixelInterpolateMethod get_image_iterations, set_image_iterations, MagickGetImageIterations, MagickSetImageIterations, usize - get_image_orientation, set_image_orientation, MagickGetImageOrientation, MagickSetImageOrientation, bindings::OrientationType + get_image_orientation, set_image_orientation, MagickGetImageOrientation, MagickSetImageOrientation, OrientationType get_image_rendering_intent, set_image_rendering_intent, MagickGetImageRenderingIntent, MagickSetImageRenderingIntent, bindings::RenderingIntent get_image_scene, set_image_scene, MagickGetImageScene, MagickSetImageScene, usize get_image_type, set_image_type, MagickGetImageType, MagickSetImageType, bindings::ImageType @@ -1200,7 +1198,7 @@ impl MagickWand { get_interlace_scheme, set_interlace_scheme, MagickGetInterlaceScheme, MagickSetInterlaceScheme, InterlaceType get_interpolate_method, set_interpolate_method, MagickGetInterpolateMethod, MagickSetInterpolateMethod, PixelInterpolateMethod get_iterator_index, set_iterator_index, MagickGetIteratorIndex, MagickSetIteratorIndex, isize - get_orientation, set_orientation, MagickGetOrientation, MagickSetOrientation, bindings::OrientationType + get_orientation, set_orientation, MagickGetOrientation, MagickSetOrientation, OrientationType get_pointsize, set_pointsize, MagickGetPointsize, MagickSetPointsize, f64 get_type, set_type, MagickGetType, MagickSetType, bindings::ImageType ); diff --git a/src/wand/pixel.rs b/src/wand/pixel.rs index d51f1d8..c63ff23 100644 --- a/src/wand/pixel.rs +++ b/src/wand/pixel.rs @@ -16,11 +16,6 @@ use std::ffi::{CStr, CString}; use std::fmt; -#[cfg(target_os = "freebsd")] -use libc::size_t; -#[cfg(not(target_os = "freebsd"))] -use size_t; - use bindings; use result::MagickError; @@ -99,7 +94,7 @@ impl PixelWand { ); set_get_unchecked!( - get_color_count, set_color_count, PixelGetColorCount, PixelSetColorCount, size_t + get_color_count, set_color_count, PixelGetColorCount, PixelSetColorCount, usize get_index, set_index, PixelGetIndex, PixelSetIndex, f32 get_fuzz, set_fuzz, PixelGetFuzz, PixelSetFuzz, f64 ); From 7db8314e5ac1b688b0d8946369d60d150fb0499b Mon Sep 17 00:00:00 2001 From: 5ohue <86558263+5ohue@users.noreply.github.com> Date: Sat, 11 May 2024 14:27:45 +0300 Subject: [PATCH 12/37] Add `RenderingIntent` type --- src/types/mod.rs | 2 ++ src/types/rendering_intent.rs | 40 +++++++++++++++++++++++++++++++++++ src/wand/magick.rs | 3 ++- 3 files changed, 44 insertions(+), 1 deletion(-) create mode 100644 src/types/rendering_intent.rs diff --git a/src/types/mod.rs b/src/types/mod.rs index deb75bb..e746ed9 100644 --- a/src/types/mod.rs +++ b/src/types/mod.rs @@ -11,6 +11,7 @@ mod interlace_type; mod metric_type; mod orientation_type; mod pixel_interpolate_method; +mod rendering_intent; mod resource_type; pub use self::alpha_channel_option::AlphaChannelOption; @@ -26,4 +27,5 @@ pub use self::interlace_type::InterlaceType; pub use self::metric_type::MetricType; pub use self::orientation_type::OrientationType; pub use self::pixel_interpolate_method::PixelInterpolateMethod; +pub use self::rendering_intent::RenderingIntent; pub use self::resource_type::ResourceType; diff --git a/src/types/rendering_intent.rs b/src/types/rendering_intent.rs new file mode 100644 index 0000000..d63c9cc --- /dev/null +++ b/src/types/rendering_intent.rs @@ -0,0 +1,40 @@ +use crate::bindings; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[repr(u32)] +pub enum RenderingIntent { + Undefined = bindings::RenderingIntent_UndefinedIntent, + Saturation = bindings::RenderingIntent_SaturationIntent, + Perceptual = bindings::RenderingIntent_PerceptualIntent, + Absolute = bindings::RenderingIntent_AbsoluteIntent, + Relative = bindings::RenderingIntent_RelativeIntent, +} + +impl Default for RenderingIntent { + fn default() -> Self { + return RenderingIntent::Undefined; + } +} + +impl From for bindings::RenderingIntent { + fn from(value: RenderingIntent) -> Self { + return value as bindings::RenderingIntent; + } +} + +impl From for RenderingIntent { + fn from(value: bindings::RenderingIntent) -> Self { + /* + * SAFETY: + * + * `RenderingIntent` has the same repr as `bindings::RenderingIntent` - u32 + * + * If `value` is less than Relative than it is in the vaild range and can be safely + * reinterpreted as `RenderingIntent` + */ + if value <= bindings::RenderingIntent_RelativeIntent { + return unsafe { std::mem::transmute(value) }; + } + return RenderingIntent::default(); + } +} diff --git a/src/wand/magick.rs b/src/wand/magick.rs index d9de4e2..d323446 100644 --- a/src/wand/magick.rs +++ b/src/wand/magick.rs @@ -44,6 +44,7 @@ use crate::{ MetricType, OrientationType, PixelInterpolateMethod, + RenderingIntent, ResourceType }; @@ -1191,7 +1192,7 @@ impl MagickWand { get_image_interpolate_method, set_image_interpolate_method, MagickGetImageInterpolateMethod, MagickSetImageInterpolateMethod, PixelInterpolateMethod get_image_iterations, set_image_iterations, MagickGetImageIterations, MagickSetImageIterations, usize get_image_orientation, set_image_orientation, MagickGetImageOrientation, MagickSetImageOrientation, OrientationType - get_image_rendering_intent, set_image_rendering_intent, MagickGetImageRenderingIntent, MagickSetImageRenderingIntent, bindings::RenderingIntent + get_image_rendering_intent, set_image_rendering_intent, MagickGetImageRenderingIntent, MagickSetImageRenderingIntent, RenderingIntent get_image_scene, set_image_scene, MagickGetImageScene, MagickSetImageScene, usize get_image_type, set_image_type, MagickGetImageType, MagickSetImageType, bindings::ImageType get_image_units, set_image_units, MagickGetImageUnits, MagickSetImageUnits, bindings::ResolutionType From baac3cbf59d8c19602a650990eb977e16bc9dd6a Mon Sep 17 00:00:00 2001 From: 5ohue <86558263+5ohue@users.noreply.github.com> Date: Sat, 11 May 2024 14:31:53 +0300 Subject: [PATCH 13/37] Add `ResolutionType` type --- src/types/mod.rs | 2 ++ src/types/resolution_type.rs | 38 ++++++++++++++++++++++++++++++++++++ src/wand/magick.rs | 3 ++- 3 files changed, 42 insertions(+), 1 deletion(-) create mode 100644 src/types/resolution_type.rs diff --git a/src/types/mod.rs b/src/types/mod.rs index e746ed9..be350da 100644 --- a/src/types/mod.rs +++ b/src/types/mod.rs @@ -12,6 +12,7 @@ mod metric_type; mod orientation_type; mod pixel_interpolate_method; mod rendering_intent; +mod resolution_type; mod resource_type; pub use self::alpha_channel_option::AlphaChannelOption; @@ -28,4 +29,5 @@ pub use self::metric_type::MetricType; pub use self::orientation_type::OrientationType; pub use self::pixel_interpolate_method::PixelInterpolateMethod; pub use self::rendering_intent::RenderingIntent; +pub use self::resolution_type::ResolutionType; pub use self::resource_type::ResourceType; diff --git a/src/types/resolution_type.rs b/src/types/resolution_type.rs new file mode 100644 index 0000000..f94f55e --- /dev/null +++ b/src/types/resolution_type.rs @@ -0,0 +1,38 @@ +use crate::bindings; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[repr(u32)] +pub enum ResolutionType { + Undefined = bindings::ResolutionType_UndefinedResolution, + PixelsPerInch = bindings::ResolutionType_PixelsPerInchResolution, + PixelsPerCentimeter = bindings::ResolutionType_PixelsPerCentimeterResolution, +} + +impl Default for ResolutionType { + fn default() -> Self { + return ResolutionType::Undefined; + } +} + +impl From for bindings::ResolutionType { + fn from(value: ResolutionType) -> Self { + return value as bindings::ResolutionType; + } +} + +impl From for ResolutionType { + fn from(value: bindings::ResolutionType) -> Self { + /* + * SAFETY: + * + * `ResolutionType` has the same repr as `bindings::ResolutionType` - u32 + * + * If `value` is less than PixelsPerCentimeter than it is in the vaild range and can be safely + * reinterpreted as `ResolutionType` + */ + if value <= bindings::ResolutionType_PixelsPerCentimeterResolution { + return unsafe { std::mem::transmute(value) }; + } + return ResolutionType::default(); + } +} diff --git a/src/wand/magick.rs b/src/wand/magick.rs index d323446..220844b 100644 --- a/src/wand/magick.rs +++ b/src/wand/magick.rs @@ -45,6 +45,7 @@ use crate::{ OrientationType, PixelInterpolateMethod, RenderingIntent, + ResolutionType, ResourceType }; @@ -1195,7 +1196,7 @@ impl MagickWand { get_image_rendering_intent, set_image_rendering_intent, MagickGetImageRenderingIntent, MagickSetImageRenderingIntent, RenderingIntent get_image_scene, set_image_scene, MagickGetImageScene, MagickSetImageScene, usize get_image_type, set_image_type, MagickGetImageType, MagickSetImageType, bindings::ImageType - get_image_units, set_image_units, MagickGetImageUnits, MagickSetImageUnits, bindings::ResolutionType + get_image_units, set_image_units, MagickGetImageUnits, MagickSetImageUnits, ResolutionType get_interlace_scheme, set_interlace_scheme, MagickGetInterlaceScheme, MagickSetInterlaceScheme, InterlaceType get_interpolate_method, set_interpolate_method, MagickGetInterpolateMethod, MagickSetInterpolateMethod, PixelInterpolateMethod get_iterator_index, set_iterator_index, MagickGetIteratorIndex, MagickSetIteratorIndex, isize From 1a66649c478a7b8b024ff0f355879c9988f9f53f Mon Sep 17 00:00:00 2001 From: 5ohue <86558263+5ohue@users.noreply.github.com> Date: Sat, 11 May 2024 14:35:10 +0300 Subject: [PATCH 14/37] Add `ImageType` type --- src/types/image_type.rs | 47 +++++++++++++++++++++++++++++++++++++++++ src/types/mod.rs | 2 ++ src/wand/magick.rs | 5 +++-- 3 files changed, 52 insertions(+), 2 deletions(-) create mode 100644 src/types/image_type.rs diff --git a/src/types/image_type.rs b/src/types/image_type.rs new file mode 100644 index 0000000..2375868 --- /dev/null +++ b/src/types/image_type.rs @@ -0,0 +1,47 @@ +use crate::bindings; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[repr(u32)] +pub enum ImageType { + Undefined = bindings::ImageType_UndefinedType, + Bilevel = bindings::ImageType_BilevelType, + Grayscale = bindings::ImageType_GrayscaleType, + GrayscaleAlpha = bindings::ImageType_GrayscaleAlphaType, + Palette = bindings::ImageType_PaletteType, + PaletteAlpha = bindings::ImageType_PaletteAlphaType, + TrueColor = bindings::ImageType_TrueColorType, + TrueColorAlpha = bindings::ImageType_TrueColorAlphaType, + ColorSeparation = bindings::ImageType_ColorSeparationType, + ColorSeparationAlpha = bindings::ImageType_ColorSeparationAlphaType, + Optimize = bindings::ImageType_OptimizeType, + PaletteBilevelAlpha = bindings::ImageType_PaletteBilevelAlphaType, +} + +impl Default for ImageType { + fn default() -> Self { + return ImageType::Undefined; + } +} + +impl From for bindings::ImageType { + fn from(value: ImageType) -> Self { + return value as bindings::ImageType; + } +} + +impl From for ImageType { + fn from(value: bindings::ImageType) -> Self { + /* + * SAFETY: + * + * `ImageType` has the same repr as `bindings::ImageType` - u32 + * + * If `value` is less than SouthEast than it is in the vaild range and can be safely + * reinterpreted as `ImageType` + */ + if value <= bindings::ImageType_PaletteBilevelAlphaType { + return unsafe { std::mem::transmute(value) }; + } + return ImageType::default(); + } +} diff --git a/src/types/mod.rs b/src/types/mod.rs index be350da..550e0b9 100644 --- a/src/types/mod.rs +++ b/src/types/mod.rs @@ -7,6 +7,7 @@ mod dither_method; mod endian_type; mod filter_type; mod gravity_type; +mod image_type; mod interlace_type; mod metric_type; mod orientation_type; @@ -24,6 +25,7 @@ pub use self::dither_method::DitherMethod; pub use self::endian_type::EndianType; pub use self::filter_type::FilterType; pub use self::gravity_type::GravityType; +pub use self::image_type::ImageType; pub use self::interlace_type::InterlaceType; pub use self::metric_type::MetricType; pub use self::orientation_type::OrientationType; diff --git a/src/wand/magick.rs b/src/wand/magick.rs index 220844b..a02876b 100644 --- a/src/wand/magick.rs +++ b/src/wand/magick.rs @@ -40,6 +40,7 @@ use crate::{ EndianType, FilterType, GravityType, + ImageType, InterlaceType, MetricType, OrientationType, @@ -1195,14 +1196,14 @@ impl MagickWand { get_image_orientation, set_image_orientation, MagickGetImageOrientation, MagickSetImageOrientation, OrientationType get_image_rendering_intent, set_image_rendering_intent, MagickGetImageRenderingIntent, MagickSetImageRenderingIntent, RenderingIntent get_image_scene, set_image_scene, MagickGetImageScene, MagickSetImageScene, usize - get_image_type, set_image_type, MagickGetImageType, MagickSetImageType, bindings::ImageType + get_image_type, set_image_type, MagickGetImageType, MagickSetImageType, ImageType get_image_units, set_image_units, MagickGetImageUnits, MagickSetImageUnits, ResolutionType get_interlace_scheme, set_interlace_scheme, MagickGetInterlaceScheme, MagickSetInterlaceScheme, InterlaceType get_interpolate_method, set_interpolate_method, MagickGetInterpolateMethod, MagickSetInterpolateMethod, PixelInterpolateMethod get_iterator_index, set_iterator_index, MagickGetIteratorIndex, MagickSetIteratorIndex, isize get_orientation, set_orientation, MagickGetOrientation, MagickSetOrientation, OrientationType get_pointsize, set_pointsize, MagickGetPointsize, MagickSetPointsize, f64 - get_type, set_type, MagickGetType, MagickSetType, bindings::ImageType + get_type, set_type, MagickGetType, MagickSetType, ImageType ); } From b8147a2b068fe13d70fcbd59a032cd66b820bf6f Mon Sep 17 00:00:00 2001 From: 5ohue <86558263+5ohue@users.noreply.github.com> Date: Sat, 11 May 2024 21:29:36 +0300 Subject: [PATCH 15/37] Add artifact related functions This makes it possible to blend images with user configurable percent (see `set_image_artifact` documentation) --- src/types/mod.rs | 2 + src/types/statistic_type.rs | 46 ++++++++ src/wand/magick.rs | 207 +++++++++++++++++++++++++++++++++++- 3 files changed, 251 insertions(+), 4 deletions(-) create mode 100644 src/types/statistic_type.rs diff --git a/src/types/mod.rs b/src/types/mod.rs index 550e0b9..ccc17b5 100644 --- a/src/types/mod.rs +++ b/src/types/mod.rs @@ -15,6 +15,7 @@ mod pixel_interpolate_method; mod rendering_intent; mod resolution_type; mod resource_type; +mod statistic_type; pub use self::alpha_channel_option::AlphaChannelOption; pub use self::colorspace_type::ColorspaceType; @@ -33,3 +34,4 @@ pub use self::pixel_interpolate_method::PixelInterpolateMethod; pub use self::rendering_intent::RenderingIntent; pub use self::resolution_type::ResolutionType; pub use self::resource_type::ResourceType; +pub use self::statistic_type::StatisticType; diff --git a/src/types/statistic_type.rs b/src/types/statistic_type.rs new file mode 100644 index 0000000..2a73945 --- /dev/null +++ b/src/types/statistic_type.rs @@ -0,0 +1,46 @@ +use crate::bindings; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[repr(u32)] +pub enum StatisticType { + Undefined = bindings::StatisticType_UndefinedStatistic, + Gradient = bindings::StatisticType_GradientStatistic, + Maximum = bindings::StatisticType_MaximumStatistic, + Mean = bindings::StatisticType_MeanStatistic, + Median = bindings::StatisticType_MedianStatistic, + Minimum = bindings::StatisticType_MinimumStatistic, + Mode = bindings::StatisticType_ModeStatistic, + Nonpeak = bindings::StatisticType_NonpeakStatistic, + RootMeanSquare = bindings::StatisticType_RootMeanSquareStatistic, + StandardDeviation = bindings::StatisticType_StandardDeviationStatistic, + Contrast = bindings::StatisticType_ContrastStatistic, +} + +impl Default for StatisticType { + fn default() -> Self { + return StatisticType::Undefined; + } +} + +impl From for bindings::StatisticType { + fn from(value: StatisticType) -> Self { + return value as bindings::StatisticType; + } +} + +impl From for StatisticType { + fn from(value: bindings::StatisticType) -> Self { + /* + * SAFETY: + * + * `StatisticType` has the same repr as `bindings::StatisticType` - u32 + * + * If `value` is less than Contrast than it is in the vaild range and can be safely + * reinterpreted as `StatisticType` + */ + if value <= bindings::StatisticType_ContrastStatistic { + return unsafe { std::mem::transmute(value) }; + } + return StatisticType::default(); + } +} diff --git a/src/wand/magick.rs b/src/wand/magick.rs index a02876b..aac7b55 100644 --- a/src/wand/magick.rs +++ b/src/wand/magick.rs @@ -47,7 +47,8 @@ use crate::{ PixelInterpolateMethod, RenderingIntent, ResolutionType, - ResourceType + ResourceType, + StatisticType, }; wand_common!( @@ -414,11 +415,21 @@ impl MagickWand { } /// Apply sigmoidal contrast to the image - /// Midpoint is a number in range [0, 1] + /// + /// Adjusts the contrast of an image with a non-linear sigmoidal contrast algorithm. Increase + /// the contrast of the image using a sigmoidal transfer function without saturating highlights + /// or shadows. Contrast indicates how much to increase the contrast (0 is none; 3 is typical; + /// 20 is pushing it); mid-point indicates where midtones fall in the resultant image (0.0 is + /// white; 0.5 is middle-gray; 1.0 is black). Set sharpen to `true` to increase the image + /// contrast otherwise the contrast is reduced. + /// + /// * `sharpen`: increase or decrease image contrast + /// * `strength`: strength of the contrast, the larger the number the more 'threshold-like' it becomes. + /// * `midpoint`: midpoint of the function as a number in range [0, 1] pub fn sigmoidal_contrast_image( &self, sharpen: bool, - contrast: f64, + strength: f64, midpoint: f64, ) -> Result<()> { let quantum_range = self.quantum_range()?; @@ -427,7 +438,7 @@ impl MagickWand { bindings::MagickSigmoidalContrastImage( self.wand, sharpen.to_magick(), - contrast, + strength, midpoint * quantum_range, ) }; @@ -521,6 +532,37 @@ impl MagickWand { } } + /// Replace each pixel with corresponding statistic from the neighborhood of the specified width and height. + /// + /// * `statistic_type`: the statistic type (e.g. `StatisticType::Median`, `StatisticType::Mode`, etc.). + /// * `width`: the width of the pixel neighborhood. + /// * `height`: the height of the pixel neighborhood. + pub fn statistic_image( + &self, + statistic_type: StatisticType, + width: usize, + height: usize, + ) -> Result<()> { + match unsafe { + bindings::MagickStatisticImage( + self.wand, + statistic_type.into(), + width.into(), + height.into() + ) + } { + MagickTrue => Ok(()), + _ => Err(MagickError("failed to calculate statistics for image")), + } + } + + /// Calculate median for each pixel's neighborhood. + /// + /// See [statistic_image](Self::statistic_image) + pub fn median_blur_image(&self, width: usize, height: usize) -> Result<()> { + return self.statistic_image(StatisticType::Median, width, height); + } + /// Adaptively resize the currently selected image. pub fn adaptive_resize_image(&self, width: usize, height: usize) -> Result<()> { match unsafe { bindings::MagickAdaptiveResizeImage(self.wand, width, height) } { @@ -577,6 +619,131 @@ impl MagickWand { } } + /// Returns a value associated with the specified artifact. + /// + /// * `artifact`: the artifact. + pub fn get_image_artifact(&self, artifact: &str) -> Result { + let c_artifact = CString::new(artifact).map_err(|_| MagickError("artifact string contains null byte"))?; + + let result = unsafe { + bindings::MagickGetImageArtifact( + self.wand, + c_artifact.as_ptr() + ) + }; + + let value = if result.is_null() { + Err(MagickError("missing artifact")) + } 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 + } + + pub fn get_image_artifacts(&self, pattern: &str) -> Result> { + let c_pattern = CString::new(pattern).map_err(|_| MagickError("artifact string contains null byte"))?; + let mut num_of_artifacts: size_t = 0; + + let result = unsafe { + bindings::MagickGetImageArtifacts( + self.wand, + c_pattern.as_ptr(), + &mut num_of_artifacts + ) + }; + + let value = if result.is_null() { + Err(MagickError("no artifacts found")) + } else { + let mut artifacts: Vec = Vec::with_capacity(num_of_artifacts); + for i in 0..num_of_artifacts { + // convert (and copy) the C string to a Rust string + let cstr = unsafe { CStr::from_ptr(*result.add(i)) }; + artifacts.push(cstr.to_string_lossy().into_owned()); + } + + Ok(artifacts) + }; + + unsafe { + bindings::MagickRelinquishMemory(result as *mut c_void); + } + + value + } + + /// Sets a key-value pair in the image artifact namespace. Artifacts differ from properties. + /// Properties are public and are generally exported to an external image format if the format + /// supports it. Artifacts are private and are utilized by the internal ImageMagick API to + /// modify the behavior of certain algorithms. + /// + /// * `artifact`: the artifact. + /// * `value`: the value. + /// + /// # Example + /// + /// This example shows how you can blend an image with its blurred copy with 50% opacity by + /// setting "compose:args" to "50". This is equivalent to having `-define compose:args=50` when + /// using imagemagick cli. + /// + /// ``` + /// let mut wand1 = MagickWand::new(); + /// wand1.read_image("test.jpg")?; + /// let wand2 = wand1.clone(); + /// + /// wand1.median_blur_image(10, 10)?; + /// + /// wand1.set_image_artifact("compose:args", "50")?; + /// wand1.compose_images(&wand2, CompositeOperator::Blend, false, 0, 0)?; + /// + /// wand1.write_image("res.jpeg")?; + /// ``` + pub fn set_image_artifact( + &mut self, + artifact: &str, + value: &str + ) -> Result<()> { + let c_artifact = CString::new(artifact).map_err(|_| MagickError("artifact string contains null byte"))?; + let c_value = CString::new(value).map_err(|_| MagickError("value string contains null byte"))?; + + let result = unsafe { + bindings::MagickSetImageArtifact( + self.wand, + c_artifact.as_ptr(), + c_value.as_ptr() + ) + }; + + match result { + MagickTrue => Ok(()), + _ => Err(MagickError("failed to set image artifact")), + } + } + + /// Deletes a wand artifact. + /// + /// * `artifact`: the artifact. + pub fn delete_image_artifact(&mut self, artifact: &str) -> Result<()> { + let c_artifact = CString::new(artifact).map_err(|_| MagickError("artifact string contains null byte"))?; + + match unsafe { + bindings::MagickDeleteImageArtifact( + self.wand, + c_artifact.as_ptr() + ) + } { + MagickTrue => Ok(()), + _ => Err(MagickError("failed to delete image artifact")), + } + } + /// Retrieve the named image property value. pub fn get_image_property(&self, name: &str) -> Result { let c_name = CString::new(name).unwrap(); @@ -594,6 +761,38 @@ impl MagickWand { value } + pub fn get_image_properties(&self, pattern: &str) -> Result> { + let c_pattern = CString::new(pattern).map_err(|_| MagickError("artifact string contains null byte"))?; + let mut num_of_artifacts: size_t = 0; + + let result = unsafe { + bindings::MagickGetImageProperties( + self.wand, + c_pattern.as_ptr(), + &mut num_of_artifacts + ) + }; + + let value = if result.is_null() { + Err(MagickError("no artifacts found")) + } else { + let mut artifacts: Vec = Vec::with_capacity(num_of_artifacts); + for i in 0..num_of_artifacts { + // convert (and copy) the C string to a Rust string + let cstr = unsafe { CStr::from_ptr(*result.add(i)) }; + artifacts.push(cstr.to_string_lossy().into_owned()); + } + + Ok(artifacts) + }; + + unsafe { + bindings::MagickRelinquishMemory(result as *mut c_void); + } + + value + } + /// Set the named image property. pub fn set_image_property(&self, name: &str, value: &str) -> Result<()> { let c_name = CString::new(name).unwrap(); From 1f865a1c436eaf99ecd45d963f666b2fab5bdb3c Mon Sep 17 00:00:00 2001 From: 5ohue <86558263+5ohue@users.noreply.github.com> Date: Sat, 11 May 2024 21:43:19 +0300 Subject: [PATCH 16/37] Add `FillRule` type --- src/types/fill_rule.rs | 38 ++++++++++++++++++++++++++++++++++++++ src/types/mod.rs | 2 ++ src/wand/drawing.rs | 9 ++++++--- 3 files changed, 46 insertions(+), 3 deletions(-) create mode 100644 src/types/fill_rule.rs diff --git a/src/types/fill_rule.rs b/src/types/fill_rule.rs new file mode 100644 index 0000000..d48bf66 --- /dev/null +++ b/src/types/fill_rule.rs @@ -0,0 +1,38 @@ +use crate::bindings; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[repr(u32)] +pub enum FillRule { + Undefined = bindings::FillRule_UndefinedRule, + EvenOdd = bindings::FillRule_EvenOddRule, + NonZero = bindings::FillRule_NonZeroRule, +} + +impl Default for FillRule { + fn default() -> Self { + return FillRule::Undefined; + } +} + +impl From for bindings::FillRule { + fn from(value: FillRule) -> Self { + return value as bindings::FillRule; + } +} + +impl From for FillRule { + fn from(value: bindings::FillRule) -> Self { + /* + * SAFETY: + * + * `FillRule` has the same repr as `bindings::FillRule` - u32 + * + * If `value` is less than NonZero than it is in the vaild range and can be safely + * reinterpreted as `FillRule` + */ + if value <= bindings::FillRule_NonZeroRule { + return unsafe { std::mem::transmute(value) }; + } + return FillRule::default(); + } +} diff --git a/src/types/mod.rs b/src/types/mod.rs index ccc17b5..c21e5ed 100644 --- a/src/types/mod.rs +++ b/src/types/mod.rs @@ -5,6 +5,7 @@ mod compression_type; mod dispose_type; mod dither_method; mod endian_type; +mod fill_rule; mod filter_type; mod gravity_type; mod image_type; @@ -24,6 +25,7 @@ pub use self::compression_type::CompressionType; pub use self::dispose_type::DisposeType; pub use self::dither_method::DitherMethod; pub use self::endian_type::EndianType; +pub use self::fill_rule::FillRule; pub use self::filter_type::FilterType; pub use self::gravity_type::GravityType; pub use self::image_type::ImageType; diff --git a/src/wand/drawing.rs b/src/wand/drawing.rs index 16012d8..39b6bbf 100644 --- a/src/wand/drawing.rs +++ b/src/wand/drawing.rs @@ -20,7 +20,10 @@ use bindings; use crate::result::MagickError; use crate::result::Result; -use crate::GravityType; +use crate::{ + FillRule, + GravityType, +}; wand_common!( DrawingWand, @@ -89,9 +92,9 @@ impl DrawingWand { set_get_unchecked!( get_gravity, set_gravity, DrawGetGravity, DrawSetGravity, GravityType get_opacity, set_opacity, DrawGetOpacity, DrawSetOpacity, f64 - get_clip_rule, set_clip_rule, DrawGetClipRule, DrawSetClipRule, bindings::FillRule + get_clip_rule, set_clip_rule, DrawGetClipRule, DrawSetClipRule, FillRule get_clip_units, set_clip_units, DrawGetClipUnits, DrawSetClipUnits, bindings::ClipPathUnits - get_fill_rule, set_fill_rule, DrawGetFillRule, DrawSetFillRule, bindings::FillRule + get_fill_rule, set_fill_rule, DrawGetFillRule, DrawSetFillRule, FillRule get_fill_opacity, set_fill_opacity, DrawGetFillOpacity, DrawSetFillOpacity, f64 get_font_size, set_font_size, DrawGetFontSize, DrawSetFontSize, f64 From f83daebe167a8dfdb361888814cf46e5bb2f54cb Mon Sep 17 00:00:00 2001 From: 5ohue <86558263+5ohue@users.noreply.github.com> Date: Sat, 11 May 2024 21:43:37 +0300 Subject: [PATCH 17/37] Fix documentation test --- src/wand/magick.rs | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/src/wand/magick.rs b/src/wand/magick.rs index aac7b55..8937994 100644 --- a/src/wand/magick.rs +++ b/src/wand/magick.rs @@ -70,8 +70,8 @@ wand_common!( /// When the `MagickWand` is dropped, the ImageMagick wand will be /// destroyed as well. impl MagickWand { - pub fn new_image(&self, columns: usize, rows: usize, pixel_wand: &PixelWand) -> Result<()> { - match unsafe { bindings::MagickNewImage(self.wand, columns.into(), rows.into(), pixel_wand.wand) } { + 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(()), _ => Err(MagickError("Could not create image")), } @@ -694,16 +694,20 @@ impl MagickWand { /// using imagemagick cli. /// /// ``` - /// let mut wand1 = MagickWand::new(); - /// wand1.read_image("test.jpg")?; - /// let wand2 = wand1.clone(); + /// use magick_rust::{MagickWand, PixelWand, CompositeOperator}; /// - /// wand1.median_blur_image(10, 10)?; + /// fn main() -> Result<(), magick_rust::MagickError> { + /// let mut wand1 = MagickWand::new(); + /// wand1.new_image(4, 4, &PixelWand::new())?; + /// let wand2 = wand1.clone(); /// - /// wand1.set_image_artifact("compose:args", "50")?; - /// wand1.compose_images(&wand2, CompositeOperator::Blend, false, 0, 0)?; + /// wand1.median_blur_image(10, 10)?; /// - /// wand1.write_image("res.jpeg")?; + /// wand1.set_image_artifact("compose:args", "50")?; + /// wand1.compose_images(&wand2, CompositeOperator::Blend, false, 0, 0)?; + /// + /// Ok(()) + /// } /// ``` pub fn set_image_artifact( &mut self, From 81d6077928b33c58e9fd1bd3ae36a514966a6024 Mon Sep 17 00:00:00 2001 From: 5ohue <86558263+5ohue@users.noreply.github.com> Date: Sat, 11 May 2024 21:52:00 +0300 Subject: [PATCH 18/37] Add `ClipPathUnits` and `StyleType` types --- src/types/clip_path_units.rs | 39 ++++++++++++++++++++++++++++++++++ src/types/mod.rs | 4 ++++ src/types/style_type.rs | 41 ++++++++++++++++++++++++++++++++++++ src/wand/drawing.rs | 6 ++++-- 4 files changed, 88 insertions(+), 2 deletions(-) create mode 100644 src/types/clip_path_units.rs create mode 100644 src/types/style_type.rs diff --git a/src/types/clip_path_units.rs b/src/types/clip_path_units.rs new file mode 100644 index 0000000..762efe8 --- /dev/null +++ b/src/types/clip_path_units.rs @@ -0,0 +1,39 @@ +use crate::bindings; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[repr(u32)] +pub enum ClipPathUnits { + Undefined = bindings::ClipPathUnits_UndefinedPathUnits, + UserSpace = bindings::ClipPathUnits_UserSpace, + UserSpaceOnUse = bindings::ClipPathUnits_UserSpaceOnUse, + ObjectBoundingBox = bindings::ClipPathUnits_ObjectBoundingBox, +} + +impl Default for ClipPathUnits { + fn default() -> Self { + return ClipPathUnits::Undefined; + } +} + +impl From for bindings::ClipPathUnits { + fn from(value: ClipPathUnits) -> Self { + return value as bindings::ClipPathUnits; + } +} + +impl From for ClipPathUnits { + fn from(value: bindings::ClipPathUnits) -> Self { + /* + * SAFETY: + * + * `ClipPathUnits` has the same repr as `bindings::ClipPathUnits` - u32 + * + * If `value` is less than ObjectBoundingBox than it is in the vaild range and can be safely + * reinterpreted as `ClipPathUnits` + */ + if value <= bindings::ClipPathUnits_ObjectBoundingBox { + return unsafe { std::mem::transmute(value) }; + } + return ClipPathUnits::default(); + } +} diff --git a/src/types/mod.rs b/src/types/mod.rs index c21e5ed..7894e4d 100644 --- a/src/types/mod.rs +++ b/src/types/mod.rs @@ -1,4 +1,5 @@ mod alpha_channel_option; +mod clip_path_units; mod colorspace_type; mod composite_operator; mod compression_type; @@ -17,8 +18,10 @@ mod rendering_intent; mod resolution_type; mod resource_type; mod statistic_type; +mod style_type; pub use self::alpha_channel_option::AlphaChannelOption; +pub use self::clip_path_units::ClipPathUnits; pub use self::colorspace_type::ColorspaceType; pub use self::composite_operator::CompositeOperator; pub use self::compression_type::CompressionType; @@ -37,3 +40,4 @@ pub use self::rendering_intent::RenderingIntent; pub use self::resolution_type::ResolutionType; pub use self::resource_type::ResourceType; pub use self::statistic_type::StatisticType; +pub use self::style_type::StyleType; diff --git a/src/types/style_type.rs b/src/types/style_type.rs new file mode 100644 index 0000000..be22eaf --- /dev/null +++ b/src/types/style_type.rs @@ -0,0 +1,41 @@ +use crate::bindings; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[repr(u32)] +pub enum StyleType { + Undefined = bindings::StyleType_UndefinedStyle, + Normal = bindings::StyleType_NormalStyle, + Italic = bindings::StyleType_ItalicStyle, + Oblique = bindings::StyleType_ObliqueStyle, + Any = bindings::StyleType_AnyStyle, + Bold = bindings::StyleType_BoldStyle, +} + +impl Default for StyleType { + fn default() -> Self { + return StyleType::Undefined; + } +} + +impl From for bindings::StyleType { + fn from(value: StyleType) -> Self { + return value as bindings::StyleType; + } +} + +impl From for StyleType { + fn from(value: bindings::StyleType) -> Self { + /* + * SAFETY: + * + * `StyleType` has the same repr as `bindings::StyleType` - u32 + * + * If `value` is less than Bold than it is in the vaild range and can be safely + * reinterpreted as `StyleType` + */ + if value <= bindings::StyleType_BoldStyle { + return unsafe { std::mem::transmute(value) }; + } + return StyleType::default(); + } +} diff --git a/src/wand/drawing.rs b/src/wand/drawing.rs index 39b6bbf..5810b43 100644 --- a/src/wand/drawing.rs +++ b/src/wand/drawing.rs @@ -21,8 +21,10 @@ use bindings; use crate::result::MagickError; use crate::result::Result; use crate::{ + ClipPathUnits, FillRule, GravityType, + StyleType, }; wand_common!( @@ -93,12 +95,12 @@ impl DrawingWand { get_gravity, set_gravity, DrawGetGravity, DrawSetGravity, GravityType get_opacity, set_opacity, DrawGetOpacity, DrawSetOpacity, f64 get_clip_rule, set_clip_rule, DrawGetClipRule, DrawSetClipRule, FillRule - get_clip_units, set_clip_units, DrawGetClipUnits, DrawSetClipUnits, bindings::ClipPathUnits + get_clip_units, set_clip_units, DrawGetClipUnits, DrawSetClipUnits, ClipPathUnits get_fill_rule, set_fill_rule, DrawGetFillRule, DrawSetFillRule, FillRule 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, bindings::StyleType + get_font_style, set_font_style, DrawGetFontStyle, DrawSetFontStyle, StyleType get_font_weight, set_font_weight, DrawGetFontWeight, DrawSetFontWeight, usize get_font_stretch, set_font_stretch, DrawGetFontStretch, DrawSetFontStretch, bindings::StretchType From 79e2056386f0d3d79efc2e740a719ef67115279e Mon Sep 17 00:00:00 2001 From: 5ohue <86558263+5ohue@users.noreply.github.com> Date: Sat, 11 May 2024 21:54:33 +0300 Subject: [PATCH 19/37] Add `StretchType` type --- src/types/mod.rs | 2 ++ src/types/stretch_type.rs | 46 +++++++++++++++++++++++++++++++++++++++ src/wand/drawing.rs | 3 ++- 3 files changed, 50 insertions(+), 1 deletion(-) create mode 100644 src/types/stretch_type.rs diff --git a/src/types/mod.rs b/src/types/mod.rs index 7894e4d..5c95d93 100644 --- a/src/types/mod.rs +++ b/src/types/mod.rs @@ -18,6 +18,7 @@ mod rendering_intent; mod resolution_type; mod resource_type; mod statistic_type; +mod stretch_type; mod style_type; pub use self::alpha_channel_option::AlphaChannelOption; @@ -40,4 +41,5 @@ pub use self::rendering_intent::RenderingIntent; pub use self::resolution_type::ResolutionType; pub use self::resource_type::ResourceType; pub use self::statistic_type::StatisticType; +pub use self::stretch_type::StretchType; pub use self::style_type::StyleType; diff --git a/src/types/stretch_type.rs b/src/types/stretch_type.rs new file mode 100644 index 0000000..40b76d0 --- /dev/null +++ b/src/types/stretch_type.rs @@ -0,0 +1,46 @@ +use crate::bindings; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[repr(u32)] +pub enum StretchType { + Undefined = bindings::StretchType_UndefinedStretch, + Normal = bindings::StretchType_NormalStretch, + UltraCondensed = bindings::StretchType_UltraCondensedStretch, + ExtraCondensed = bindings::StretchType_ExtraCondensedStretch, + Condensed = bindings::StretchType_CondensedStretch, + SemiCondensed = bindings::StretchType_SemiCondensedStretch, + SemiExpanded = bindings::StretchType_SemiExpandedStretch, + Expanded = bindings::StretchType_ExpandedStretch, + ExtraExpanded = bindings::StretchType_ExtraExpandedStretch, + UltraExpanded = bindings::StretchType_UltraExpandedStretch, + Any = bindings::StretchType_AnyStretch, +} + +impl Default for StretchType { + fn default() -> Self { + return StretchType::Undefined; + } +} + +impl From for bindings::StretchType { + fn from(value: StretchType) -> Self { + return value as bindings::StretchType; + } +} + +impl From for StretchType { + fn from(value: bindings::StretchType) -> Self { + /* + * SAFETY: + * + * `StretchType` has the same repr as `bindings::StretchType` - u32 + * + * If `value` is less than Any than it is in the vaild range and can be safely + * reinterpreted as `StretchType` + */ + if value <= bindings::StretchType_AnyStretch { + return unsafe { std::mem::transmute(value) }; + } + return StretchType::default(); + } +} diff --git a/src/wand/drawing.rs b/src/wand/drawing.rs index 5810b43..a0ad764 100644 --- a/src/wand/drawing.rs +++ b/src/wand/drawing.rs @@ -24,6 +24,7 @@ use crate::{ ClipPathUnits, FillRule, GravityType, + StretchType, StyleType, }; @@ -102,7 +103,7 @@ impl DrawingWand { get_font_size, set_font_size, DrawGetFontSize, DrawSetFontSize, f64 get_font_style, set_font_style, DrawGetFontStyle, DrawSetFontStyle, StyleType get_font_weight, set_font_weight, DrawGetFontWeight, DrawSetFontWeight, usize - get_font_stretch, set_font_stretch, DrawGetFontStretch, DrawSetFontStretch, bindings::StretchType + get_font_stretch, set_font_stretch, DrawGetFontStretch, DrawSetFontStretch, StretchType get_stroke_dash_offset, set_stroke_dash_offset, DrawGetStrokeDashOffset, DrawSetStrokeDashOffset, f64 get_stroke_line_cap, set_stroke_line_cap, DrawGetStrokeLineCap, DrawSetStrokeLineCap, bindings::LineCap From d176efe4029c06cae3dd23d08f63ea9777c54157 Mon Sep 17 00:00:00 2001 From: 5ohue <86558263+5ohue@users.noreply.github.com> Date: Sat, 11 May 2024 21:57:22 +0300 Subject: [PATCH 20/37] Add `LineJoin` type --- src/types/line_join.rs | 39 +++++++++++++++++++++++++++++++++++++++ src/types/mod.rs | 2 ++ src/wand/drawing.rs | 3 ++- 3 files changed, 43 insertions(+), 1 deletion(-) create mode 100644 src/types/line_join.rs diff --git a/src/types/line_join.rs b/src/types/line_join.rs new file mode 100644 index 0000000..d356f3e --- /dev/null +++ b/src/types/line_join.rs @@ -0,0 +1,39 @@ +use crate::bindings; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[repr(u32)] +pub enum LineJoin { + Undefined = bindings::LineJoin_UndefinedJoin, + Miter = bindings::LineJoin_MiterJoin, + Round = bindings::LineJoin_RoundJoin, + Bevel = bindings::LineJoin_BevelJoin, +} + +impl Default for LineJoin { + fn default() -> Self { + return LineJoin::Undefined; + } +} + +impl From for bindings::LineJoin { + fn from(value: LineJoin) -> Self { + return value as bindings::LineJoin; + } +} + +impl From for LineJoin { + fn from(value: bindings::LineJoin) -> Self { + /* + * SAFETY: + * + * `LineJoin` has the same repr as `bindings::LineJoin` - u32 + * + * If `value` is less than Bevel than it is in the vaild range and can be safely + * reinterpreted as `LineJoin` + */ + if value <= bindings::LineJoin_BevelJoin { + return unsafe { std::mem::transmute(value) }; + } + return LineJoin::default(); + } +} diff --git a/src/types/mod.rs b/src/types/mod.rs index 5c95d93..606b2f7 100644 --- a/src/types/mod.rs +++ b/src/types/mod.rs @@ -11,6 +11,7 @@ mod filter_type; mod gravity_type; mod image_type; mod interlace_type; +mod line_join; mod metric_type; mod orientation_type; mod pixel_interpolate_method; @@ -34,6 +35,7 @@ pub use self::filter_type::FilterType; pub use self::gravity_type::GravityType; pub use self::image_type::ImageType; pub use self::interlace_type::InterlaceType; +pub use self::line_join::LineJoin; pub use self::metric_type::MetricType; pub use self::orientation_type::OrientationType; pub use self::pixel_interpolate_method::PixelInterpolateMethod; diff --git a/src/wand/drawing.rs b/src/wand/drawing.rs index a0ad764..60a4184 100644 --- a/src/wand/drawing.rs +++ b/src/wand/drawing.rs @@ -24,6 +24,7 @@ use crate::{ ClipPathUnits, FillRule, GravityType, + LineJoin, StretchType, StyleType, }; @@ -107,7 +108,7 @@ impl DrawingWand { get_stroke_dash_offset, set_stroke_dash_offset, DrawGetStrokeDashOffset, DrawSetStrokeDashOffset, f64 get_stroke_line_cap, set_stroke_line_cap, DrawGetStrokeLineCap, DrawSetStrokeLineCap, bindings::LineCap - get_stroke_line_join, set_stroke_line_join, DrawGetStrokeLineJoin, DrawSetStrokeLineJoin, bindings::LineJoin + get_stroke_line_join, set_stroke_line_join, DrawGetStrokeLineJoin, DrawSetStrokeLineJoin, LineJoin get_stroke_miter_limit, set_stroke_miter_limit, DrawGetStrokeMiterLimit, DrawSetStrokeMiterLimit, usize get_stroke_opacity, set_stroke_opacity, DrawGetStrokeOpacity, DrawSetStrokeOpacity, f64 get_stroke_width, set_stroke_width, DrawGetStrokeWidth, DrawSetStrokeWidth, f64 From 0156379d8a20928f0090862fa616c81a067cd839 Mon Sep 17 00:00:00 2001 From: 5ohue <86558263+5ohue@users.noreply.github.com> Date: Sat, 11 May 2024 21:59:54 +0300 Subject: [PATCH 21/37] Add `LineCap` type --- src/types/line_cap.rs | 39 +++++++++++++++++++++++++++++++++++++++ src/types/mod.rs | 2 ++ src/wand/drawing.rs | 3 ++- 3 files changed, 43 insertions(+), 1 deletion(-) create mode 100644 src/types/line_cap.rs diff --git a/src/types/line_cap.rs b/src/types/line_cap.rs new file mode 100644 index 0000000..a5384ac --- /dev/null +++ b/src/types/line_cap.rs @@ -0,0 +1,39 @@ +use crate::bindings; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[repr(u32)] +pub enum LineCap { + Undefined = bindings::LineCap_UndefinedCap, + Butt = bindings::LineCap_ButtCap, + Round = bindings::LineCap_RoundCap, + Square = bindings::LineCap_SquareCap, +} + +impl Default for LineCap { + fn default() -> Self { + return LineCap::Undefined; + } +} + +impl From for bindings::LineCap { + fn from(value: LineCap) -> Self { + return value as bindings::LineCap; + } +} + +impl From for LineCap { + fn from(value: bindings::LineCap) -> Self { + /* + * SAFETY: + * + * `LineCap` has the same repr as `bindings::LineCap` - u32 + * + * If `value` is less than Square than it is in the vaild range and can be safely + * reinterpreted as `LineCap` + */ + if value <= bindings::LineCap_SquareCap { + return unsafe { std::mem::transmute(value) }; + } + return LineCap::default(); + } +} diff --git a/src/types/mod.rs b/src/types/mod.rs index 606b2f7..f3c0a33 100644 --- a/src/types/mod.rs +++ b/src/types/mod.rs @@ -11,6 +11,7 @@ mod filter_type; mod gravity_type; mod image_type; mod interlace_type; +mod line_cap; mod line_join; mod metric_type; mod orientation_type; @@ -35,6 +36,7 @@ pub use self::filter_type::FilterType; pub use self::gravity_type::GravityType; pub use self::image_type::ImageType; pub use self::interlace_type::InterlaceType; +pub use self::line_cap::LineCap; pub use self::line_join::LineJoin; pub use self::metric_type::MetricType; pub use self::orientation_type::OrientationType; diff --git a/src/wand/drawing.rs b/src/wand/drawing.rs index 60a4184..805458f 100644 --- a/src/wand/drawing.rs +++ b/src/wand/drawing.rs @@ -24,6 +24,7 @@ use crate::{ ClipPathUnits, FillRule, GravityType, + LineCap, LineJoin, StretchType, StyleType, @@ -107,7 +108,7 @@ impl DrawingWand { get_font_stretch, set_font_stretch, DrawGetFontStretch, DrawSetFontStretch, StretchType get_stroke_dash_offset, set_stroke_dash_offset, DrawGetStrokeDashOffset, DrawSetStrokeDashOffset, f64 - get_stroke_line_cap, set_stroke_line_cap, DrawGetStrokeLineCap, DrawSetStrokeLineCap, bindings::LineCap + get_stroke_line_cap, set_stroke_line_cap, DrawGetStrokeLineCap, DrawSetStrokeLineCap, LineCap get_stroke_line_join, set_stroke_line_join, DrawGetStrokeLineJoin, DrawSetStrokeLineJoin, LineJoin get_stroke_miter_limit, set_stroke_miter_limit, DrawGetStrokeMiterLimit, DrawSetStrokeMiterLimit, usize get_stroke_opacity, set_stroke_opacity, DrawGetStrokeOpacity, DrawSetStrokeOpacity, f64 From dc80d78ce2391f678465931d33a1f3e51071269f Mon Sep 17 00:00:00 2001 From: 5ohue <86558263+5ohue@users.noreply.github.com> Date: Sat, 11 May 2024 22:01:46 +0300 Subject: [PATCH 22/37] Add `AlignType` type --- src/types/align_type.rs | 39 +++++++++++++++++++++++++++++++++++++++ src/types/mod.rs | 2 ++ src/wand/drawing.rs | 3 ++- 3 files changed, 43 insertions(+), 1 deletion(-) create mode 100644 src/types/align_type.rs diff --git a/src/types/align_type.rs b/src/types/align_type.rs new file mode 100644 index 0000000..c6a8c99 --- /dev/null +++ b/src/types/align_type.rs @@ -0,0 +1,39 @@ +use crate::bindings; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[repr(u32)] +pub enum AlignType { + Undefined = bindings::AlignType_UndefinedAlign, + Left = bindings::AlignType_LeftAlign, + Center = bindings::AlignType_CenterAlign, + Right = bindings::AlignType_RightAlign, +} + +impl Default for AlignType { + fn default() -> Self { + return AlignType::Undefined; + } +} + +impl From for bindings::AlignType { + fn from(value: AlignType) -> Self { + return value as bindings::AlignType; + } +} + +impl From for AlignType { + fn from(value: bindings::AlignType) -> Self { + /* + * SAFETY: + * + * `AlignType` has the same repr as `bindings::AlignType` - u32 + * + * If `value` is less than Right than it is in the vaild range and can be safely + * reinterpreted as `AlignType` + */ + if value <= bindings::AlignType_RightAlign { + return unsafe { std::mem::transmute(value) }; + } + return AlignType::default(); + } +} diff --git a/src/types/mod.rs b/src/types/mod.rs index f3c0a33..ca01e03 100644 --- a/src/types/mod.rs +++ b/src/types/mod.rs @@ -1,3 +1,4 @@ +mod align_type; mod alpha_channel_option; mod clip_path_units; mod colorspace_type; @@ -23,6 +24,7 @@ mod statistic_type; mod stretch_type; mod style_type; +pub use self::align_type::AlignType; pub use self::alpha_channel_option::AlphaChannelOption; pub use self::clip_path_units::ClipPathUnits; pub use self::colorspace_type::ColorspaceType; diff --git a/src/wand/drawing.rs b/src/wand/drawing.rs index 805458f..a4d3b99 100644 --- a/src/wand/drawing.rs +++ b/src/wand/drawing.rs @@ -21,6 +21,7 @@ use bindings; use crate::result::MagickError; use crate::result::Result; use crate::{ + AlignType, ClipPathUnits, FillRule, GravityType, @@ -115,7 +116,7 @@ impl DrawingWand { get_stroke_width, set_stroke_width, DrawGetStrokeWidth, DrawSetStrokeWidth, f64 get_stroke_antialias, set_stroke_antialias, DrawGetStrokeAntialias, DrawSetStrokeAntialias, bindings::MagickBooleanType - get_text_alignment, set_text_alignment, DrawGetTextAlignment, DrawSetTextAlignment, bindings::AlignType + get_text_alignment, set_text_alignment, DrawGetTextAlignment, DrawSetTextAlignment, AlignType get_text_antialias, set_text_antialias, DrawGetTextAntialias, DrawSetTextAntialias, bindings::MagickBooleanType get_text_decoration, set_text_decoration, DrawGetTextDecoration, DrawSetTextDecoration, bindings::DecorationType get_text_direction, set_text_direction, DrawGetTextDirection, DrawSetTextDirection, bindings::DirectionType From d8d6a12d02fc9650356811bab6a4ac766a4e2e92 Mon Sep 17 00:00:00 2001 From: 5ohue <86558263+5ohue@users.noreply.github.com> Date: Sat, 11 May 2024 22:04:49 +0300 Subject: [PATCH 23/37] Add `DecorationType` type --- src/types/decoration_type.rs | 40 ++++++++++++++++++++++++++++++++++++ src/types/mod.rs | 2 ++ src/wand/drawing.rs | 3 ++- 3 files changed, 44 insertions(+), 1 deletion(-) create mode 100644 src/types/decoration_type.rs diff --git a/src/types/decoration_type.rs b/src/types/decoration_type.rs new file mode 100644 index 0000000..a5d8efb --- /dev/null +++ b/src/types/decoration_type.rs @@ -0,0 +1,40 @@ +use crate::bindings; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[repr(u32)] +pub enum DecorationType { + Undefined = bindings::DecorationType_UndefinedDecoration, + No = bindings::DecorationType_NoDecoration, + Underline = bindings::DecorationType_UnderlineDecoration, + Overline = bindings::DecorationType_OverlineDecoration, + LineThrough = bindings::DecorationType_LineThroughDecoration, +} + +impl Default for DecorationType { + fn default() -> Self { + return DecorationType::Undefined; + } +} + +impl From for bindings::DecorationType { + fn from(value: DecorationType) -> Self { + return value as bindings::DecorationType; + } +} + +impl From for DecorationType { + fn from(value: bindings::DecorationType) -> Self { + /* + * SAFETY: + * + * `DecorationType` has the same repr as `bindings::DecorationType` - u32 + * + * If `value` is less than LineThrough than it is in the vaild range and can be safely + * reinterpreted as `DecorationType` + */ + if value <= bindings::DecorationType_LineThroughDecoration { + return unsafe { std::mem::transmute(value) }; + } + return DecorationType::default(); + } +} diff --git a/src/types/mod.rs b/src/types/mod.rs index ca01e03..a50eacc 100644 --- a/src/types/mod.rs +++ b/src/types/mod.rs @@ -4,6 +4,7 @@ mod clip_path_units; mod colorspace_type; mod composite_operator; mod compression_type; +mod decoration_type; mod dispose_type; mod dither_method; mod endian_type; @@ -30,6 +31,7 @@ pub use self::clip_path_units::ClipPathUnits; pub use self::colorspace_type::ColorspaceType; pub use self::composite_operator::CompositeOperator; pub use self::compression_type::CompressionType; +pub use self::decoration_type::DecorationType; pub use self::dispose_type::DisposeType; pub use self::dither_method::DitherMethod; pub use self::endian_type::EndianType; diff --git a/src/wand/drawing.rs b/src/wand/drawing.rs index a4d3b99..3637368 100644 --- a/src/wand/drawing.rs +++ b/src/wand/drawing.rs @@ -23,6 +23,7 @@ use crate::result::Result; use crate::{ AlignType, ClipPathUnits, + DecorationType, FillRule, GravityType, LineCap, @@ -118,7 +119,7 @@ impl DrawingWand { get_text_alignment, set_text_alignment, DrawGetTextAlignment, DrawSetTextAlignment, AlignType get_text_antialias, set_text_antialias, DrawGetTextAntialias, DrawSetTextAntialias, bindings::MagickBooleanType - get_text_decoration, set_text_decoration, DrawGetTextDecoration, DrawSetTextDecoration, bindings::DecorationType + get_text_decoration, set_text_decoration, DrawGetTextDecoration, DrawSetTextDecoration, DecorationType get_text_direction, set_text_direction, DrawGetTextDirection, DrawSetTextDirection, bindings::DirectionType get_text_kerning, set_text_kerning, DrawGetTextKerning, DrawSetTextKerning, f64 get_text_interline_spacing, set_text_interline_spacing, DrawGetTextInterlineSpacing, DrawSetTextInterlineSpacing, f64 From 24c2a3d2de74f060e311dcb58d1cf94bde5ddc93 Mon Sep 17 00:00:00 2001 From: 5ohue <86558263+5ohue@users.noreply.github.com> Date: Sat, 11 May 2024 22:07:01 +0300 Subject: [PATCH 24/37] Add `DirectionType` type --- src/types/direction_type.rs | 39 +++++++++++++++++++++++++++++++++++++ src/types/mod.rs | 2 ++ src/wand/drawing.rs | 3 ++- 3 files changed, 43 insertions(+), 1 deletion(-) create mode 100644 src/types/direction_type.rs diff --git a/src/types/direction_type.rs b/src/types/direction_type.rs new file mode 100644 index 0000000..7bcaa2b --- /dev/null +++ b/src/types/direction_type.rs @@ -0,0 +1,39 @@ +use crate::bindings; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[repr(u32)] +pub enum DirectionType { + Undefined = bindings::DirectionType_UndefinedDirection, + RightToLeft = bindings::DirectionType_RightToLeftDirection, + LeftToRight = bindings::DirectionType_LeftToRightDirection, + TopToBottom = bindings::DirectionType_TopToBottomDirection, +} + +impl Default for DirectionType { + fn default() -> Self { + return DirectionType::Undefined; + } +} + +impl From for bindings::DirectionType { + fn from(value: DirectionType) -> Self { + return value as bindings::DirectionType; + } +} + +impl From for DirectionType { + fn from(value: bindings::DirectionType) -> Self { + /* + * SAFETY: + * + * `DirectionType` has the same repr as `bindings::DirectionType` - u32 + * + * If `value` is less than TopToBottom than it is in the vaild range and can be safely + * reinterpreted as `DirectionType` + */ + if value <= bindings::DirectionType_TopToBottomDirection { + return unsafe { std::mem::transmute(value) }; + } + return DirectionType::default(); + } +} diff --git a/src/types/mod.rs b/src/types/mod.rs index a50eacc..762c77a 100644 --- a/src/types/mod.rs +++ b/src/types/mod.rs @@ -5,6 +5,7 @@ mod colorspace_type; mod composite_operator; mod compression_type; mod decoration_type; +mod direction_type; mod dispose_type; mod dither_method; mod endian_type; @@ -32,6 +33,7 @@ pub use self::colorspace_type::ColorspaceType; pub use self::composite_operator::CompositeOperator; pub use self::compression_type::CompressionType; pub use self::decoration_type::DecorationType; +pub use self::direction_type::DirectionType; pub use self::dispose_type::DisposeType; pub use self::dither_method::DitherMethod; pub use self::endian_type::EndianType; diff --git a/src/wand/drawing.rs b/src/wand/drawing.rs index 3637368..c918703 100644 --- a/src/wand/drawing.rs +++ b/src/wand/drawing.rs @@ -24,6 +24,7 @@ use crate::{ AlignType, ClipPathUnits, DecorationType, + DirectionType, FillRule, GravityType, LineCap, @@ -120,7 +121,7 @@ impl DrawingWand { get_text_alignment, set_text_alignment, DrawGetTextAlignment, DrawSetTextAlignment, AlignType get_text_antialias, set_text_antialias, DrawGetTextAntialias, DrawSetTextAntialias, bindings::MagickBooleanType get_text_decoration, set_text_decoration, DrawGetTextDecoration, DrawSetTextDecoration, DecorationType - get_text_direction, set_text_direction, DrawGetTextDirection, DrawSetTextDirection, bindings::DirectionType + get_text_direction, set_text_direction, DrawGetTextDirection, DrawSetTextDirection, DirectionType 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 From 5eb991ba695f73e9021dfb613b1f1e14bf46079b Mon Sep 17 00:00:00 2001 From: 5ohue <86558263+5ohue@users.noreply.github.com> Date: Sat, 11 May 2024 23:30:41 +0300 Subject: [PATCH 25/37] Add `function_image`, `polynomial_image` functions --- src/types/magick_function.rs | 23 ++++++++++++ src/types/mod.rs | 2 ++ src/wand/magick.rs | 68 +++++++++++++++++++++++++++++++++++- 3 files changed, 92 insertions(+), 1 deletion(-) create mode 100644 src/types/magick_function.rs diff --git a/src/types/magick_function.rs b/src/types/magick_function.rs new file mode 100644 index 0000000..ee68042 --- /dev/null +++ b/src/types/magick_function.rs @@ -0,0 +1,23 @@ +use crate::bindings; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[repr(u32)] +pub enum MagickFunction { + Undefined = bindings::MagickFunction_UndefinedFunction, + Arcsin = bindings::MagickFunction_ArcsinFunction, + Arctan = bindings::MagickFunction_ArctanFunction, + Polynomial = bindings::MagickFunction_PolynomialFunction, + Sinusoid = bindings::MagickFunction_SinusoidFunction, +} + +impl Default for MagickFunction { + fn default() -> Self { + return MagickFunction::Undefined; + } +} + +impl From for bindings::MagickFunction { + fn from(value: MagickFunction) -> Self { + return value as bindings::MagickFunction; + } +} diff --git a/src/types/mod.rs b/src/types/mod.rs index 762c77a..496bcef 100644 --- a/src/types/mod.rs +++ b/src/types/mod.rs @@ -16,6 +16,7 @@ mod image_type; mod interlace_type; mod line_cap; mod line_join; +mod magick_function; mod metric_type; mod orientation_type; mod pixel_interpolate_method; @@ -44,6 +45,7 @@ pub use self::image_type::ImageType; pub use self::interlace_type::InterlaceType; pub use self::line_cap::LineCap; pub use self::line_join::LineJoin; +pub use self::magick_function::MagickFunction; pub use self::metric_type::MetricType; pub use self::orientation_type::OrientationType; pub use self::pixel_interpolate_method::PixelInterpolateMethod; diff --git a/src/wand/magick.rs b/src/wand/magick.rs index 8937994..085a8d9 100644 --- a/src/wand/magick.rs +++ b/src/wand/magick.rs @@ -42,6 +42,7 @@ use crate::{ GravityType, ImageType, InterlaceType, + MagickFunction, MetricType, OrientationType, PixelInterpolateMethod, @@ -698,7 +699,7 @@ impl MagickWand { /// /// fn main() -> Result<(), magick_rust::MagickError> { /// let mut wand1 = MagickWand::new(); - /// wand1.new_image(4, 4, &PixelWand::new())?; + /// wand1.new_image(4, 4, &PixelWand::new())?; // Replace with `read_image` to open your image file /// let wand2 = wand1.clone(); /// /// wand1.median_blur_image(10, 10)?; @@ -1341,6 +1342,71 @@ impl MagickWand { } } + /// Applies an arithmetic, relational, or logical expression to an image. Use these operators + /// to lighten or darken an image, to increase or decrease contrast in an image, or to produce + /// the "negative" of an image. + /// + /// * `function`: the image function. + /// * `args`: the function arguments. + /// + /// # Example + /// + /// This example show how you can apply smoothstep function (a polynomial `-2x^3 + 3x^2`) to + /// every image pixel. + /// + /// ``` + /// use magick_rust::{MagickWand, PixelWand, MagickFunction}; + /// + /// fn main() -> Result<(), magick_rust::MagickError> { + /// let mut wand1 = MagickWand::new(); + /// wand1.new_image(4, 4, &PixelWand::new())?; // Replace with `read_image` to open your image file + /// + /// // Apply smoothstep polynomial + /// wand1.function_image(MagickFunction::Polynomial, &[-2.0, 3.0, 0.0, 0.0])?; + /// + /// Ok(()) + /// } + /// ``` + pub fn function_image( + &self, + function: MagickFunction, + args: &[f64] + ) -> Result<()> { + let num_of_args: size_t = args.len().into(); + match unsafe { + bindings::MagickFunctionImage( + self.wand, + function.into(), + num_of_args, + args.as_ptr() + ) + } { + MagickTrue => Ok(()), + _ => Err(MagickError("failed to apply function to image")), + } + } + + /// Returns an image where each pixel is the sum of the pixels in the image sequence after + /// applying its corresponding terms (coefficient and degree pairs). + /// + /// * `terms`: the list of polynomial coefficients and degree pairs and a constant. + pub fn polynomial_image(&self, terms: &[f64]) -> Result<()> { + if terms.len() & 1 != 1 { + return Err(MagickError("no constant coefficient given")); + } + let num_of_terms: size_t = (terms.len() >> 1).into(); + match unsafe { + bindings::MagickPolynomialImage( + self.wand, + num_of_terms, + terms.as_ptr() + ) + } { + MagickTrue => Ok(()), + _ => Err(MagickError("failed to apply polynomial to image")), + } + } + mutations!( /// Sets the image to the specified alpha level. MagickSetImageAlpha => set_image_alpha(alpha: f64) From 043616a87a5fec40e1987a14564209dc9e3f3b6c Mon Sep 17 00:00:00 2001 From: 5ohue <86558263+5ohue@users.noreply.github.com> Date: Sat, 11 May 2024 23:33:57 +0300 Subject: [PATCH 26/37] Add `AutoThresholdMethod` type --- src/types/auto_threshold_method.rs | 22 ++++++++++++++++++++++ src/types/mod.rs | 2 ++ src/wand/magick.rs | 5 +++-- 3 files changed, 27 insertions(+), 2 deletions(-) create mode 100644 src/types/auto_threshold_method.rs diff --git a/src/types/auto_threshold_method.rs b/src/types/auto_threshold_method.rs new file mode 100644 index 0000000..3bb7e7a --- /dev/null +++ b/src/types/auto_threshold_method.rs @@ -0,0 +1,22 @@ +use crate::bindings; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[repr(u32)] +pub enum AutoThresholdMethod { + Undefined = bindings::AutoThresholdMethod_UndefinedThresholdMethod, + Kapur = bindings::AutoThresholdMethod_KapurThresholdMethod, + OTSU = bindings::AutoThresholdMethod_OTSUThresholdMethod, + Triangle = bindings::AutoThresholdMethod_TriangleThresholdMethod, +} + +impl Default for AutoThresholdMethod { + fn default() -> Self { + return AutoThresholdMethod::Undefined; + } +} + +impl From for bindings::AutoThresholdMethod { + fn from(value: AutoThresholdMethod) -> Self { + return value as bindings::AutoThresholdMethod; + } +} diff --git a/src/types/mod.rs b/src/types/mod.rs index 496bcef..6a847c8 100644 --- a/src/types/mod.rs +++ b/src/types/mod.rs @@ -1,5 +1,6 @@ mod align_type; mod alpha_channel_option; +mod auto_threshold_method; mod clip_path_units; mod colorspace_type; mod composite_operator; @@ -29,6 +30,7 @@ mod style_type; pub use self::align_type::AlignType; pub use self::alpha_channel_option::AlphaChannelOption; +pub use self::auto_threshold_method::AutoThresholdMethod; pub use self::clip_path_units::ClipPathUnits; pub use self::colorspace_type::ColorspaceType; pub use self::composite_operator::CompositeOperator; diff --git a/src/wand/magick.rs b/src/wand/magick.rs index 085a8d9..2649e83 100644 --- a/src/wand/magick.rs +++ b/src/wand/magick.rs @@ -32,6 +32,7 @@ use super::{MagickTrue, MagickFalse}; use super::{DrawingWand, PixelWand}; use crate::{ AlphaChannelOption, + AutoThresholdMethod, ColorspaceType, CompositeOperator, CompressionType, @@ -1286,8 +1287,8 @@ impl MagickWand { /// down to a binary black & white image. Included algorithms are /// Kapur, Otsu, and Triangle methods. /// See for more information. - pub fn auto_threshold(&self, method: bindings::AutoThresholdMethod) -> Result<()> { - match unsafe { bindings::MagickAutoThresholdImage(self.wand, method) } { + pub fn auto_threshold(&self, method: AutoThresholdMethod) -> Result<()> { + match unsafe { bindings::MagickAutoThresholdImage(self.wand, method.into()) } { MagickTrue => Ok(()), _ => Err(MagickError("unable to auto threshold image")), } From 7c43f8018e915ec77e2b840b13da63cc528a3a09 Mon Sep 17 00:00:00 2001 From: 5ohue <86558263+5ohue@users.noreply.github.com> Date: Sat, 11 May 2024 23:54:34 +0300 Subject: [PATCH 27/37] Add `ChannelType` type --- src/types/channel_type.rs | 105 ++++++++++++++++++++++++++++++++++++++ src/types/mod.rs | 2 + src/wand/magick.rs | 11 ++-- 3 files changed, 113 insertions(+), 5 deletions(-) create mode 100644 src/types/channel_type.rs diff --git a/src/types/channel_type.rs b/src/types/channel_type.rs new file mode 100644 index 0000000..46e42cf --- /dev/null +++ b/src/types/channel_type.rs @@ -0,0 +1,105 @@ +use crate::bindings; + +#[derive(Debug, Clone, Copy)] +pub enum ChannelType { + Undefined, + RedChannel, + GrayChannel, + CyanChannel, + LChannel, + GreenChannel, + MagentaChannel, + aChannel, + BlueChannel, + bChannel, + YellowChannel, + BlackChannel, + AlphaChannel, + OpacityChannel, + IndexChannel, + ReadMaskChannel, + WriteMaskChannel, + MetaChannel, + CompositeMaskChannel, + CompositeChannels, + AllChannels, + TrueAlphaChannel, + RGBChannels, + GrayChannels, + SyncChannels, + DefaultChannels, +} + +impl Default for ChannelType { + fn default() -> Self { + return ChannelType::DefaultChannels; + } +} + +impl From for bindings::ChannelType { + fn from(value: ChannelType) -> Self { + match value { + ChannelType::Undefined => { bindings::ChannelType_UndefinedChannel }, + ChannelType::RedChannel => { bindings::ChannelType_RedChannel }, + ChannelType::GrayChannel => { bindings::ChannelType_GrayChannel }, + ChannelType::CyanChannel => { bindings::ChannelType_CyanChannel }, + ChannelType::LChannel => { bindings::ChannelType_LChannel }, + ChannelType::GreenChannel => { bindings::ChannelType_GreenChannel }, + ChannelType::MagentaChannel => { bindings::ChannelType_MagentaChannel }, + ChannelType::aChannel => { bindings::ChannelType_aChannel }, + ChannelType::BlueChannel => { bindings::ChannelType_BlueChannel }, + ChannelType::bChannel => { bindings::ChannelType_bChannel }, + ChannelType::YellowChannel => { bindings::ChannelType_YellowChannel }, + ChannelType::BlackChannel => { bindings::ChannelType_BlackChannel }, + ChannelType::AlphaChannel => { bindings::ChannelType_AlphaChannel }, + ChannelType::OpacityChannel => { bindings::ChannelType_OpacityChannel }, + ChannelType::IndexChannel => { bindings::ChannelType_IndexChannel }, + ChannelType::ReadMaskChannel => { bindings::ChannelType_ReadMaskChannel }, + ChannelType::WriteMaskChannel => { bindings::ChannelType_WriteMaskChannel }, + ChannelType::MetaChannel => { bindings::ChannelType_MetaChannel }, + ChannelType::CompositeMaskChannel => { bindings::ChannelType_CompositeMaskChannel }, + ChannelType::CompositeChannels => { bindings::ChannelType_CompositeChannels }, + ChannelType::AllChannels => { bindings::ChannelType_AllChannels }, + ChannelType::TrueAlphaChannel => { bindings::ChannelType_TrueAlphaChannel }, + ChannelType::RGBChannels => { bindings::ChannelType_RGBChannels }, + ChannelType::GrayChannels => { bindings::ChannelType_GrayChannels }, + ChannelType::SyncChannels => { bindings::ChannelType_SyncChannels }, + ChannelType::DefaultChannels => { bindings::ChannelType_DefaultChannels }, + } + } +} + +impl From for ChannelType { + fn from(value: bindings::ChannelType) -> Self { + // Unreachable match arms commented out + match value { + bindings::ChannelType_UndefinedChannel => { ChannelType::Undefined }, + bindings::ChannelType_RedChannel => { ChannelType::RedChannel }, + // bindings::ChannelType_GrayChannel => { ChannelType::GrayChannel }, + // bindings::ChannelType_CyanChannel => { ChannelType::CyanChannel }, + // bindings::ChannelType_LChannel => { ChannelType::LChannel }, + bindings::ChannelType_GreenChannel => { ChannelType::GreenChannel }, + // bindings::ChannelType_MagentaChannel => { ChannelType::MagentaChannel }, + // bindings::ChannelType_aChannel => { ChannelType::aChannel }, + bindings::ChannelType_BlueChannel => { ChannelType::BlueChannel }, + // bindings::ChannelType_bChannel => { ChannelType::bChannel }, + // bindings::ChannelType_YellowChannel => { ChannelType::YellowChannel }, + bindings::ChannelType_BlackChannel => { ChannelType::BlackChannel }, + bindings::ChannelType_AlphaChannel => { ChannelType::AlphaChannel }, + // bindings::ChannelType_OpacityChannel => { ChannelType::OpacityChannel }, + bindings::ChannelType_IndexChannel => { ChannelType::IndexChannel }, + bindings::ChannelType_ReadMaskChannel => { ChannelType::ReadMaskChannel }, + bindings::ChannelType_WriteMaskChannel => { ChannelType::WriteMaskChannel }, + bindings::ChannelType_MetaChannel => { ChannelType::MetaChannel }, + bindings::ChannelType_CompositeMaskChannel => { ChannelType::CompositeMaskChannel }, + bindings::ChannelType_CompositeChannels => { ChannelType::CompositeChannels }, + bindings::ChannelType_AllChannels => { ChannelType::AllChannels }, + // bindings::ChannelType_TrueAlphaChannel => { ChannelType::TrueAlphaChannel }, + // bindings::ChannelType_RGBChannels => { ChannelType::RGBChannels }, + bindings::ChannelType_GrayChannels => { ChannelType::GrayChannels }, + bindings::ChannelType_SyncChannels => { ChannelType::SyncChannels }, + // bindings::ChannelType_DefaultChannels => { ChannelType::DefaultChannels }, + _ => { ChannelType::Undefined }, + } + } +} diff --git a/src/types/mod.rs b/src/types/mod.rs index 6a847c8..ca39a90 100644 --- a/src/types/mod.rs +++ b/src/types/mod.rs @@ -1,6 +1,7 @@ mod align_type; mod alpha_channel_option; mod auto_threshold_method; +mod channel_type; mod clip_path_units; mod colorspace_type; mod composite_operator; @@ -31,6 +32,7 @@ mod style_type; pub use self::align_type::AlignType; pub use self::alpha_channel_option::AlphaChannelOption; pub use self::auto_threshold_method::AutoThresholdMethod; +pub use self::channel_type::ChannelType; pub use self::clip_path_units::ClipPathUnits; pub use self::colorspace_type::ColorspaceType; pub use self::composite_operator::CompositeOperator; diff --git a/src/wand/magick.rs b/src/wand/magick.rs index 2649e83..30e6399 100644 --- a/src/wand/magick.rs +++ b/src/wand/magick.rs @@ -33,6 +33,7 @@ use super::{DrawingWand, PixelWand}; use crate::{ AlphaChannelOption, AutoThresholdMethod, + ChannelType, ColorspaceType, CompositeOperator, CompressionType, @@ -1052,8 +1053,8 @@ impl MagickWand { } /// Implodes the image towards the center by the specified percentage - pub fn implode(&self, amount: f64, method: bindings::PixelInterpolateMethod) -> Result<()> { - match unsafe { bindings::MagickImplodeImage(self.wand, amount, method) } { + pub fn implode(&self, amount: f64, method: PixelInterpolateMethod) -> Result<()> { + match unsafe { bindings::MagickImplodeImage(self.wand, amount, method.into()) } { MagickTrue => Ok(()), _ => Err(MagickError("failed to implode image")), } @@ -1191,9 +1192,9 @@ impl MagickWand { /// Set image channel mask pub fn set_image_channel_mask( &mut self, - option: bindings::ChannelType, - ) -> bindings::ChannelType { - unsafe { bindings::MagickSetImageChannelMask(self.wand, option) } + option: ChannelType, + ) -> ChannelType { + unsafe { bindings::MagickSetImageChannelMask(self.wand, option.into()).into() } } /// Apply an arithmetic, relational, or logical From 26a8dd3df70584df0c1dee241f71eb9e0206768c Mon Sep 17 00:00:00 2001 From: 5ohue <86558263+5ohue@users.noreply.github.com> Date: Sat, 11 May 2024 23:59:16 +0300 Subject: [PATCH 28/37] Add `MagickEvaluateOperator` type --- src/types/magick_evaluate_operator.rs | 52 +++++++++++++++++++++++++++ src/types/mod.rs | 2 ++ src/wand/magick.rs | 5 +-- 3 files changed, 57 insertions(+), 2 deletions(-) create mode 100644 src/types/magick_evaluate_operator.rs diff --git a/src/types/magick_evaluate_operator.rs b/src/types/magick_evaluate_operator.rs new file mode 100644 index 0000000..bbfff5e --- /dev/null +++ b/src/types/magick_evaluate_operator.rs @@ -0,0 +1,52 @@ +use crate::bindings; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[repr(u32)] +pub enum MagickEvaluateOperator { + Undefined = bindings::MagickEvaluateOperator_UndefinedEvaluateOperator, + Abs = bindings::MagickEvaluateOperator_AbsEvaluateOperator, + Add = bindings::MagickEvaluateOperator_AddEvaluateOperator, + AddModulus = bindings::MagickEvaluateOperator_AddModulusEvaluateOperator, + And = bindings::MagickEvaluateOperator_AndEvaluateOperator, + Cosine = bindings::MagickEvaluateOperator_CosineEvaluateOperator, + Divide = bindings::MagickEvaluateOperator_DivideEvaluateOperator, + Exponential = bindings::MagickEvaluateOperator_ExponentialEvaluateOperator, + GaussianNoise = bindings::MagickEvaluateOperator_GaussianNoiseEvaluateOperator, + ImpulseNoise = bindings::MagickEvaluateOperator_ImpulseNoiseEvaluateOperator, + LaplacianNoise = bindings::MagickEvaluateOperator_LaplacianNoiseEvaluateOperator, + LeftShift = bindings::MagickEvaluateOperator_LeftShiftEvaluateOperator, + Log = bindings::MagickEvaluateOperator_LogEvaluateOperator, + Max = bindings::MagickEvaluateOperator_MaxEvaluateOperator, + Mean = bindings::MagickEvaluateOperator_MeanEvaluateOperator, + Median = bindings::MagickEvaluateOperator_MedianEvaluateOperator, + Min = bindings::MagickEvaluateOperator_MinEvaluateOperator, + MultiplicativeNoise = bindings::MagickEvaluateOperator_MultiplicativeNoiseEvaluateOperator, + Multiply = bindings::MagickEvaluateOperator_MultiplyEvaluateOperator, + Or = bindings::MagickEvaluateOperator_OrEvaluateOperator, + PoissonNoise = bindings::MagickEvaluateOperator_PoissonNoiseEvaluateOperator, + Pow = bindings::MagickEvaluateOperator_PowEvaluateOperator, + RightShift = bindings::MagickEvaluateOperator_RightShiftEvaluateOperator, + RootMeanSquare = bindings::MagickEvaluateOperator_RootMeanSquareEvaluateOperator, + Set = bindings::MagickEvaluateOperator_SetEvaluateOperator, + Sine = bindings::MagickEvaluateOperator_SineEvaluateOperator, + Subtract = bindings::MagickEvaluateOperator_SubtractEvaluateOperator, + Sum = bindings::MagickEvaluateOperator_SumEvaluateOperator, + ThresholdBlack = bindings::MagickEvaluateOperator_ThresholdBlackEvaluateOperator, + Threshold = bindings::MagickEvaluateOperator_ThresholdEvaluateOperator, + ThresholdWhite = bindings::MagickEvaluateOperator_ThresholdWhiteEvaluateOperator, + UniformNoise = bindings::MagickEvaluateOperator_UniformNoiseEvaluateOperator, + Xor = bindings::MagickEvaluateOperator_XorEvaluateOperator, + InverseLog = bindings::MagickEvaluateOperator_InverseLogEvaluateOperator, +} + +impl Default for MagickEvaluateOperator { + fn default() -> Self { + return MagickEvaluateOperator::Undefined; + } +} + +impl From for bindings::MagickEvaluateOperator { + fn from(value: MagickEvaluateOperator) -> Self { + return value as bindings::MagickEvaluateOperator; + } +} diff --git a/src/types/mod.rs b/src/types/mod.rs index ca39a90..ada8d0f 100644 --- a/src/types/mod.rs +++ b/src/types/mod.rs @@ -18,6 +18,7 @@ mod image_type; mod interlace_type; mod line_cap; mod line_join; +mod magick_evaluate_operator; mod magick_function; mod metric_type; mod orientation_type; @@ -49,6 +50,7 @@ pub use self::image_type::ImageType; pub use self::interlace_type::InterlaceType; pub use self::line_cap::LineCap; pub use self::line_join::LineJoin; +pub use self::magick_evaluate_operator::MagickEvaluateOperator; pub use self::magick_function::MagickFunction; pub use self::metric_type::MetricType; pub use self::orientation_type::OrientationType; diff --git a/src/wand/magick.rs b/src/wand/magick.rs index 30e6399..b3a177e 100644 --- a/src/wand/magick.rs +++ b/src/wand/magick.rs @@ -44,6 +44,7 @@ use crate::{ GravityType, ImageType, InterlaceType, + MagickEvaluateOperator, MagickFunction, MetricType, OrientationType, @@ -1201,8 +1202,8 @@ impl MagickWand { /// expression to an image. Use these operators to lighten or darken an image, /// to increase or decrease contrast in an image, or to produce the "negative" /// of an image. - pub fn evaluate_image(&mut self, op: bindings::MagickEvaluateOperator, val: f64) -> Result<()> { - let res = unsafe { bindings::MagickEvaluateImage(self.wand, op, val) }; + pub fn evaluate_image(&mut self, op: MagickEvaluateOperator, val: f64) -> Result<()> { + let res = unsafe { bindings::MagickEvaluateImage(self.wand, op.into(), val) }; match res { MagickTrue => Ok(()), _ => Err(MagickError("failed to evaluate image")), From e3edb225ec5f61a859b689cf5d23f0912b8a7e6d Mon Sep 17 00:00:00 2001 From: 5ohue <86558263+5ohue@users.noreply.github.com> Date: Sun, 12 May 2024 17:43:19 +0300 Subject: [PATCH 29/37] Add kernel and convolve functions --- src/types/kernel.rs | 325 ++++++++++++++++++++++++++++++++++++++++++++ src/types/mod.rs | 2 + src/wand/magick.rs | 61 +++++++++ 3 files changed, 388 insertions(+) create mode 100644 src/types/kernel.rs diff --git a/src/types/kernel.rs b/src/types/kernel.rs new file mode 100644 index 0000000..fb8a636 --- /dev/null +++ b/src/types/kernel.rs @@ -0,0 +1,325 @@ +use std::ffi::CString; + +use crate::bindings; +use crate::{Result, MagickError}; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[repr(u32)] +pub enum KernelInfoType { + Undefined = bindings::KernelInfoType_UndefinedKernel, + Unity = bindings::KernelInfoType_UnityKernel, + Gaussian = bindings::KernelInfoType_GaussianKernel, + DoG = bindings::KernelInfoType_DoGKernel, + LoG = bindings::KernelInfoType_LoGKernel, + Blur = bindings::KernelInfoType_BlurKernel, + Comet = bindings::KernelInfoType_CometKernel, + Binomial = bindings::KernelInfoType_BinomialKernel, + Laplacian = bindings::KernelInfoType_LaplacianKernel, + Sobel = bindings::KernelInfoType_SobelKernel, + FreiChen = bindings::KernelInfoType_FreiChenKernel, + Roberts = bindings::KernelInfoType_RobertsKernel, + Prewitt = bindings::KernelInfoType_PrewittKernel, + Compass = bindings::KernelInfoType_CompassKernel, + Kirsch = bindings::KernelInfoType_KirschKernel, + Diamond = bindings::KernelInfoType_DiamondKernel, + Square = bindings::KernelInfoType_SquareKernel, + Rectangle = bindings::KernelInfoType_RectangleKernel, + Octagon = bindings::KernelInfoType_OctagonKernel, + Disk = bindings::KernelInfoType_DiskKernel, + Plus = bindings::KernelInfoType_PlusKernel, + Cross = bindings::KernelInfoType_CrossKernel, + Ring = bindings::KernelInfoType_RingKernel, + Peaks = bindings::KernelInfoType_PeaksKernel, + Edges = bindings::KernelInfoType_EdgesKernel, + Corners = bindings::KernelInfoType_CornersKernel, + Diagonals = bindings::KernelInfoType_DiagonalsKernel, + LineEnds = bindings::KernelInfoType_LineEndsKernel, + LineJunctions = bindings::KernelInfoType_LineJunctionsKernel, + Ridges = bindings::KernelInfoType_RidgesKernel, + ConvexHull = bindings::KernelInfoType_ConvexHullKernel, + ThinSE = bindings::KernelInfoType_ThinSEKernel, + Skeleton = bindings::KernelInfoType_SkeletonKernel, + Chebyshev = bindings::KernelInfoType_ChebyshevKernel, + Manhattan = bindings::KernelInfoType_ManhattanKernel, + Octagonal = bindings::KernelInfoType_OctagonalKernel, + Euclidean = bindings::KernelInfoType_EuclideanKernel, + UserDefined = bindings::KernelInfoType_UserDefinedKernel, +} + +impl Default for KernelInfoType { + fn default() -> Self { + return KernelInfoType::Undefined; + } +} + +impl From for bindings::KernelInfoType { + fn from(value: KernelInfoType) -> Self { + return value as bindings::KernelInfoType; + } +} + + + + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[repr(u32)] +pub enum MorphologyMethod { + Undefined = bindings::MorphologyMethod_UndefinedMorphology, + Convolve = bindings::MorphologyMethod_ConvolveMorphology, + Correlate = bindings::MorphologyMethod_CorrelateMorphology, + Erode = bindings::MorphologyMethod_ErodeMorphology, + Dilate = bindings::MorphologyMethod_DilateMorphology, + ErodeIntensity = bindings::MorphologyMethod_ErodeIntensityMorphology, + DilateIntensity = bindings::MorphologyMethod_DilateIntensityMorphology, + IterativeDistance = bindings::MorphologyMethod_IterativeDistanceMorphology, + Open = bindings::MorphologyMethod_OpenMorphology, + Close = bindings::MorphologyMethod_CloseMorphology, + OpenIntensity = bindings::MorphologyMethod_OpenIntensityMorphology, + CloseIntensity = bindings::MorphologyMethod_CloseIntensityMorphology, + Smooth = bindings::MorphologyMethod_SmoothMorphology, + EdgeIn = bindings::MorphologyMethod_EdgeInMorphology, + EdgeOut = bindings::MorphologyMethod_EdgeOutMorphology, + Edge = bindings::MorphologyMethod_EdgeMorphology, + TopHat = bindings::MorphologyMethod_TopHatMorphology, + BottomHat = bindings::MorphologyMethod_BottomHatMorphology, + HitAndMiss = bindings::MorphologyMethod_HitAndMissMorphology, + Thinning = bindings::MorphologyMethod_ThinningMorphology, + Thicken = bindings::MorphologyMethod_ThickenMorphology, + Distance = bindings::MorphologyMethod_DistanceMorphology, + Voronoi = bindings::MorphologyMethod_VoronoiMorphology, +} + +impl Default for MorphologyMethod { + fn default() -> Self { + return MorphologyMethod::Undefined; + } +} + +impl From for bindings::KernelInfoType { + fn from(value: MorphologyMethod) -> Self { + return value as bindings::MorphologyMethod; + } +} + + + + +/// Builder, that creates instances of [KernelInfo](self::KernelInfo) +/// +/// # Example +/// +/// Here is an example of how you can use this struct to create a kernel to convolve an image: +/// +/// ``` +/// use magick_rust::{MagickWand, PixelWand, KernelBuilder}; +/// +/// fn main() -> Result<(), magick_rust::MagickError> { +/// let mut wand1 = MagickWand::new(); +/// wand1.new_image(4, 4, &PixelWand::new())?; // Replace with `read_image` to open your image file +/// let wand2 = wand1.clone(); +/// +/// let kernel_info = KernelBuilder::new() +/// .set_size((3, 3)) +/// .set_center((1, 1)) // Not really needed here - the center is in the middle of kernel +/// // by default +/// .set_values(&[0.111, 0.111, 0.111, +/// 0.111, 0.111, 0.111, +/// 0.111, 0.111, 0.111]) +/// .build()?; +/// +/// wand1.convolve_image(&kernel_info)?; +/// +/// Ok(()) +/// } +/// ``` +#[derive(Debug, Clone)] +pub struct KernelBuilder { + size: Option<(usize, usize)>, + center: Option<(usize, usize)>, + values: Option>, +} + +impl KernelBuilder { + pub fn new() -> KernelBuilder { + return KernelBuilder { + size: None, + center: None, + values: None, + }; + } + + pub fn set_size(mut self, size: (usize, usize)) -> KernelBuilder { + self.size = Some(size); + return self; + } + + pub fn set_center(mut self, center: (usize, usize)) -> KernelBuilder { + self.center = Some(center); + return self; + } + + pub fn set_values(mut self, values: &[f64]) -> KernelBuilder { + self.values = Some(values.into()); + return self; + } + + pub fn build(&self) -> Result { + let size = self.size.ok_or(MagickError("no kernel size given"))?; + let values = self.values.as_ref().ok_or(MagickError("no kernel values given"))?; + + if values.len() != size.0 * size.1 { + return Err(MagickError("kernel size doesn't match kernel values size")); + } + + // Create kernel string + let mut kernel_string = if let Some(center) = self.center { + format!( + "{}x{}+{}+{}:", + size.0, + size.1, + center.0, + center.1 + ) + } else { + format!( + "{}x{}:", + size.0, + size.1, + ) + }; + + // Add values + values.iter().for_each(|x| { + kernel_string.push_str(&format!("{x},")); + }); + + // Remove trailing "," + kernel_string.pop(); + + // Create null terminated string + let c_kernel_string = CString::new(kernel_string).expect("CString::new() has failed"); + + // Create kernel info + let kernel_info = unsafe { + bindings::AcquireKernelInfo( + c_kernel_string.as_ptr(), + std::ptr::null_mut() + ) + }; + + if kernel_info.is_null() { + return Err(MagickError("failed to acquire kernel info")); + } + + Ok(KernelInfo::new(kernel_info)) + } +} + +pub struct KernelInfo { + kernel_info: *mut bindings::KernelInfo, +} + +impl KernelInfo { + fn new(kernel_info: *mut bindings::KernelInfo) -> KernelInfo { + return KernelInfo { + kernel_info + }; + } + + /// The values within the kernel is scaled directly using given scaling factor without change. + pub fn scale(&mut self, factor: f64) { + unsafe { + bindings::ScaleKernelInfo( + self.kernel_info, + factor, + 0 + ) + } + } + + /// Kernel normalization is designed to ensure that any use of the kernel scaling factor with + /// 'Convolve' or 'Correlate' morphology methods will fall into -1.0 to +1.0 range. Note that + /// for non-HDRI versions of IM this may cause images to have any negative results clipped, + /// unless some 'bias' is used. + /// + /// More specifically. Kernels which only contain positive values (such as a 'Gaussian' kernel) + /// will be scaled so that those values sum to +1.0, ensuring a 0.0 to +1.0 output range for + /// non-HDRI images. + /// + /// For Kernels that contain some negative values, (such as 'Sharpen' kernels) the kernel will + /// be scaled by the absolute of the sum of kernel values, so that it will generally fall + /// within the +/- 1.0 range. + /// + /// For kernels whose values sum to zero, (such as 'Laplacian' kernels) kernel will be scaled + /// by just the sum of the positive values, so that its output range will again fall into the + /// +/- 1.0 range. + pub fn normalize(&mut self) { + unsafe { + bindings::ScaleKernelInfo( + self.kernel_info, + 1.0, + bindings::GeometryFlags_NormalizeValue + ) + } + } + + /// For special kernels designed for locating shapes using 'Correlate', (often only containing + /// +1 and -1 values, representing foreground/background matching) a special normalization + /// method is provided to scale the positive values separately to those of the negative values, + /// so the kernel will be forced to become a zero-sum kernel better suited to such searches. + pub fn correlate_normalize(&mut self) { + unsafe { + bindings::ScaleKernelInfo( + self.kernel_info, + 1.0, + bindings::GeometryFlags_CorrelateNormalizeValue + ) + } + } + + /// Adds a given amount of the 'Unity' Convolution Kernel to the given pre-scaled and + /// normalized Kernel. This in effect adds that amount of the original image into the resulting + /// convolution kernel. This value is usually provided by the user as a percentage value in the + /// 'convolve:scale' setting. + /// + /// The resulting effect is to convert the defined kernels into blended soft-blurs, unsharp + /// kernels or into sharpening kernels. + pub fn unity_add(&mut self, scale: f64) { + unsafe { + bindings::UnityAddKernelInfo( + self.kernel_info, + scale + ) + } + } + + pub unsafe fn get_ptr(&self) -> *mut bindings::KernelInfo { + return self.kernel_info; + } +} + +impl Drop for KernelInfo { + fn drop(&mut self) { + unsafe { bindings::DestroyKernelInfo(self.kernel_info) }; + } +} + +impl Clone for KernelInfo { + fn clone(&self) -> Self { + let kernel_info = unsafe { + bindings::CloneKernelInfo(self.kernel_info) + }; + + if kernel_info.is_null() { + panic!("failed to clone kernel info"); + } + + return KernelInfo::new(kernel_info); + } +} + +impl std::fmt::Debug for KernelInfo { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + unsafe { write!(f, "{:?}", *self.kernel_info) } + } +} diff --git a/src/types/mod.rs b/src/types/mod.rs index ada8d0f..50b2d7f 100644 --- a/src/types/mod.rs +++ b/src/types/mod.rs @@ -16,6 +16,7 @@ mod filter_type; mod gravity_type; mod image_type; mod interlace_type; +mod kernel; mod line_cap; mod line_join; mod magick_evaluate_operator; @@ -48,6 +49,7 @@ pub use self::filter_type::FilterType; pub use self::gravity_type::GravityType; pub use self::image_type::ImageType; pub use self::interlace_type::InterlaceType; +pub use self::kernel::*; pub use self::line_cap::LineCap; pub use self::line_join::LineJoin; pub use self::magick_evaluate_operator::MagickEvaluateOperator; diff --git a/src/wand/magick.rs b/src/wand/magick.rs index b3a177e..a0b30b0 100644 --- a/src/wand/magick.rs +++ b/src/wand/magick.rs @@ -44,9 +44,11 @@ use crate::{ GravityType, ImageType, InterlaceType, + KernelInfo, MagickEvaluateOperator, MagickFunction, MetricType, + MorphologyMethod, OrientationType, PixelInterpolateMethod, RenderingIntent, @@ -1410,6 +1412,65 @@ impl MagickWand { } } + /// Applies a custom convolution kernel to the image. + /// + /// * `kernel_info`: An array of doubles representing the convolution kernel. + pub fn convolve_image(&self, kernel_info: &KernelInfo) -> Result<()> { + match unsafe { + bindings::MagickConvolveImage( + self.wand, + kernel_info.get_ptr() + ) + } { + MagickTrue => Ok(()), + _ => Err(MagickError("failed to convolve image")), + } + } + + /// Applies a user supplied kernel to the image according to the given morphology method. + /// + /// * `morphology_method`: the morphology method to be applied. + /// * `iterations`: apply the operation this many times (or no change). A value of -1 means loop until no change found. How this is applied may depend on the morphology method. Typically this is a value of 1. + /// * `kernel_info`: An array of doubles representing the morphology kernel. + pub fn morphology_image( + &self, + morphology_method: MorphologyMethod, + iterations: isize, + kernel_info: &KernelInfo + ) -> Result<()> { + match unsafe { + bindings::MagickMorphologyImage( + self.wand, + morphology_method.into(), + iterations.into(), + kernel_info.get_ptr() + ) + } { + MagickTrue => Ok(()), + _ => Err(MagickError("failed to morphology image")), + } + } + + /// Apply color transformation to an image. The method permits saturation changes, hue rotation, + /// luminance to alpha, and various other effects. Although variable-sized transformation + /// matrices can be used, typically one uses a 5x5 matrix for an RGBA image and a 6x6 for CMYKA + /// (or RGBA with offsets). The matrix is similar to those used by Adobe Flash except offsets + /// are in column 6 rather than 5 (in support of CMYKA images) and offsets are normalized + /// (divide Flash offset by 255). + /// + /// * `color_matrix`: the color matrix. + pub fn color_matrix_image(&self, color_matrix: &KernelInfo) -> Result<()> { + match unsafe { + bindings::MagickColorMatrixImage( + self.wand, + color_matrix.get_ptr() + ) + } { + MagickTrue => Ok(()), + _ => Err(MagickError("failed to color matrix image")), + } + } + mutations!( /// Sets the image to the specified alpha level. MagickSetImageAlpha => set_image_alpha(alpha: f64) From 5c38d955eb039c5ced7574ec44213765356c7649 Mon Sep 17 00:00:00 2001 From: 5ohue <86558263+5ohue@users.noreply.github.com> Date: Sun, 12 May 2024 18:31:21 +0300 Subject: [PATCH 30/37] Add builtin kernels --- src/types/geometry_info.rs | 37 +++++++++++++++++++++ src/types/kernel.rs | 67 +++++++++++++++++++++++++++++++++++++- src/types/mod.rs | 2 ++ 3 files changed, 105 insertions(+), 1 deletion(-) create mode 100644 src/types/geometry_info.rs diff --git a/src/types/geometry_info.rs b/src/types/geometry_info.rs new file mode 100644 index 0000000..f9bee99 --- /dev/null +++ b/src/types/geometry_info.rs @@ -0,0 +1,37 @@ +use crate::bindings; + +pub type GeometryInfo = bindings::GeometryInfo; + +impl GeometryInfo { + pub fn new() -> GeometryInfo { + return GeometryInfo { + rho: 0.0, + sigma: 0.0, + xi: 0.0, + psi: 0.0, + chi: 0.0, + }; + } + + pub fn set_rho(&mut self, rho: f64) { + self.rho = rho; + } + pub fn set_sigma(&mut self, sigma: f64) { + self.sigma = sigma; + } + pub fn set_xi(&mut self, xi: f64) { + self.xi = xi; + } + pub fn set_psi(&mut self, psi: f64) { + self.psi = psi; + } + pub fn set_chi(&mut self, chi: f64) { + self.chi = chi; + } +} + +impl Default for GeometryInfo { + fn default() -> Self { + Self::new() + } +} diff --git a/src/types/kernel.rs b/src/types/kernel.rs index fb8a636..eea766e 100644 --- a/src/types/kernel.rs +++ b/src/types/kernel.rs @@ -106,7 +106,7 @@ impl From for bindings::KernelInfoType { /// Builder, that creates instances of [KernelInfo](self::KernelInfo) /// -/// # Example +/// # Examples /// /// Here is an example of how you can use this struct to create a kernel to convolve an image: /// @@ -132,11 +132,38 @@ impl From for bindings::KernelInfoType { /// Ok(()) /// } /// ``` +/// +/// Here is an example of how you can use this struct to create builtin kernel to gaussian blur an +/// image (not the best way to do it, just an example): +/// +/// ``` +/// use magick_rust::{MagickWand, PixelWand, KernelBuilder, KernelInfoType, GeometryInfo}; +/// +/// fn main() -> Result<(), magick_rust::MagickError> { +/// let mut wand1 = MagickWand::new(); +/// wand1.new_image(4, 4, &PixelWand::new())?; // Replace with `read_image` to open your image file +/// let wand2 = wand1.clone(); +/// +/// let mut geom_info = GeometryInfo::new(); +/// geom_info.set_sigma(15.0); +/// let kernel_info = KernelBuilder::new() +/// .set_info_type(KernelInfoType::Gaussian) +/// .set_geom_info(geom_info) +/// .build_builtin()?; +/// +/// wand1.convolve_image(&kernel_info)?; +/// +/// Ok(()) +/// } +/// ``` #[derive(Debug, Clone)] pub struct KernelBuilder { size: Option<(usize, usize)>, center: Option<(usize, usize)>, values: Option>, + + info_type: Option, + geom_info: Option, } impl KernelBuilder { @@ -145,19 +172,25 @@ impl KernelBuilder { size: None, center: None, values: None, + + info_type: None, + geom_info: None, }; } + /// Used for user defined kernels pub fn set_size(mut self, size: (usize, usize)) -> KernelBuilder { self.size = Some(size); return self; } + /// Used for user defined kernels pub fn set_center(mut self, center: (usize, usize)) -> KernelBuilder { self.center = Some(center); return self; } + /// Used for user defined kernels pub fn set_values(mut self, values: &[f64]) -> KernelBuilder { self.values = Some(values.into()); return self; @@ -213,6 +246,38 @@ impl KernelBuilder { Ok(KernelInfo::new(kernel_info)) } + + /// Used for builtin kernels + pub fn set_info_type(mut self, info_type: KernelInfoType) -> KernelBuilder { + self.info_type = Some(info_type); + return self; + } + + /// Used for builtin kernels + pub fn set_geom_info(mut self, geom_info: crate::GeometryInfo) -> KernelBuilder { + self.geom_info = Some(geom_info); + return self; + } + + pub fn build_builtin(&self) -> Result { + let info_type = self.info_type.ok_or(MagickError("no info type given"))?; + let mut geom_info = self.geom_info.ok_or(MagickError("no geometry info given"))?; + + // Create kernel info + let kernel_info = unsafe { + bindings::AcquireKernelBuiltIn( + info_type.into(), + &mut geom_info, + std::ptr::null_mut() + ) + }; + + if kernel_info.is_null() { + return Err(MagickError("failed to acquire builtin kernel info")); + } + + Ok(KernelInfo::new(kernel_info)) + } } pub struct KernelInfo { diff --git a/src/types/mod.rs b/src/types/mod.rs index 50b2d7f..db5a8da 100644 --- a/src/types/mod.rs +++ b/src/types/mod.rs @@ -13,6 +13,7 @@ mod dither_method; mod endian_type; mod fill_rule; mod filter_type; +mod geometry_info; mod gravity_type; mod image_type; mod interlace_type; @@ -46,6 +47,7 @@ pub use self::dither_method::DitherMethod; pub use self::endian_type::EndianType; 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_type::ImageType; pub use self::interlace_type::InterlaceType; From e5f0878f9a3cc3e19deefc14f4bcab784354625f Mon Sep 17 00:00:00 2001 From: 5ohue <86558263+5ohue@users.noreply.github.com> Date: Sun, 12 May 2024 19:13:06 +0300 Subject: [PATCH 31/37] Add error handling to some functions --- src/wand/magick.rs | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/src/wand/magick.rs b/src/wand/magick.rs index a0b30b0..07db581 100644 --- a/src/wand/magick.rs +++ b/src/wand/magick.rs @@ -989,18 +989,24 @@ impl MagickWand { /// Resize the image to the specified width and height, using the /// specified filter type. - pub fn resize_image(&self, width: usize, height: usize, filter: FilterType) { - unsafe { - bindings::MagickResizeImage(self.wand, width.into(), height.into(), filter.into()); + pub fn resize_image(&self, width: usize, height: usize, filter: FilterType) -> Result<()> { + match unsafe { + bindings::MagickResizeImage(self.wand, width.into(), height.into(), filter.into()) + } { + MagickTrue => Ok(()), + _ => Err(MagickError("failed to resize image")), } } /// Resize the image to the specified width and height, using the /// 'thumbnail' optimizations which remove a lot of image meta-data with the goal /// of producing small low cost images suited for display on the web. - pub fn thumbnail_image(&self, width: usize, height: usize) { - unsafe { - bindings::MagickThumbnailImage(self.wand, width.into(), height.into()); + pub fn thumbnail_image(&self, width: usize, height: usize) -> Result<()> { + match unsafe { + bindings::MagickThumbnailImage(self.wand, width.into(), height.into()) + } { + MagickTrue => Ok(()), + _ => Err(MagickError("failed to create thumbnail")), } } @@ -1033,9 +1039,12 @@ impl MagickWand { x_resolution: f64, y_resolution: f64, filter: FilterType, - ) { - unsafe { - bindings::MagickResampleImage(self.wand, x_resolution, y_resolution, filter.into()); + ) -> Result<()> { + match unsafe { + bindings::MagickResampleImage(self.wand, x_resolution, y_resolution, filter.into()) + } { + MagickTrue => Ok(()), + _ => Err(MagickError("failed to resample image")), } } From af4de40ddb2bb2995ff116e013d07906ff8b23a8 Mon Sep 17 00:00:00 2001 From: 5ohue <86558263+5ohue@users.noreply.github.com> Date: Sun, 12 May 2024 20:14:54 +0300 Subject: [PATCH 32/37] Add `scale_image` function --- src/wand/magick.rs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/wand/magick.rs b/src/wand/magick.rs index 07db581..d3a1fdd 100644 --- a/src/wand/magick.rs +++ b/src/wand/magick.rs @@ -998,6 +998,24 @@ impl MagickWand { } } + /// Resize image by specifying the new size in percent of last size. + /// + /// Effectively resizes image to (current width * `width_scale`, current height * + /// `height_scale`) + pub fn scale_image( + &self, + width_scale: f64, + height_scale: f64, + filter: FilterType) -> Result<()> { + let width = self.get_image_width(); + let height = self.get_image_height(); + + let width = ((width as f64) * width_scale) as usize; + let height = ((height as f64) * height_scale) as usize; + + return self.resize_image(width, height, filter); + } + /// Resize the image to the specified width and height, using the /// 'thumbnail' optimizations which remove a lot of image meta-data with the goal /// of producing small low cost images suited for display on the web. From e31675e47758bc79347d801eab636252215fb02e Mon Sep 17 00:00:00 2001 From: 5ohue <86558263+5ohue@users.noreply.github.com> Date: Sun, 12 May 2024 20:32:05 +0300 Subject: [PATCH 33/37] Add `levelize_image` function --- src/wand/magick.rs | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/wand/magick.rs b/src/wand/magick.rs index d3a1fdd..27674ca 100644 --- a/src/wand/magick.rs +++ b/src/wand/magick.rs @@ -383,6 +383,26 @@ impl MagickWand { } } + /// Applies the reversed [level_image](Self::level_image). It compresses the full range of color values, so + /// that they lie between the given black and white points. Gamma is applied before the values + /// are mapped. It can be used to de-contrast a greyscale image to the exact levels specified. + pub fn levelize_image(&self, black_point: f64, gamma: f64, white_point: f64) -> Result<()> { + let quantum_range = self.quantum_range()?; + + let result = unsafe { + bindings::MagickLevelizeImage( + self.wand, + black_point * quantum_range, + gamma, + white_point * quantum_range, + ) + }; + match result { + MagickTrue => Ok(()), + _ => Err(MagickError("Failed to level the image")), + } + } + //MagickNormalizeImage enhances the contrast of a color image by adjusting the pixels color //to span the entire range of colors available pub fn normalize_image( From 1752b0073c72a547da84109538de092a793a7509 Mon Sep 17 00:00:00 2001 From: 5ohue <86558263+5ohue@users.noreply.github.com> Date: Sun, 12 May 2024 22:06:10 +0300 Subject: [PATCH 34/37] Add `set_image_mask` function --- src/types/mod.rs | 2 ++ src/types/pixel_mask.rs | 22 ++++++++++++++++++++++ src/wand/magick.rs | 22 ++++++++++++++++++++++ 3 files changed, 46 insertions(+) create mode 100644 src/types/pixel_mask.rs diff --git a/src/types/mod.rs b/src/types/mod.rs index db5a8da..047d9b4 100644 --- a/src/types/mod.rs +++ b/src/types/mod.rs @@ -25,6 +25,7 @@ mod magick_function; mod metric_type; mod orientation_type; mod pixel_interpolate_method; +mod pixel_mask; mod rendering_intent; mod resolution_type; mod resource_type; @@ -59,6 +60,7 @@ pub use self::magick_function::MagickFunction; pub use self::metric_type::MetricType; pub use self::orientation_type::OrientationType; pub use self::pixel_interpolate_method::PixelInterpolateMethod; +pub use self::pixel_mask::PixelMask; pub use self::rendering_intent::RenderingIntent; pub use self::resolution_type::ResolutionType; pub use self::resource_type::ResourceType; diff --git a/src/types/pixel_mask.rs b/src/types/pixel_mask.rs new file mode 100644 index 0000000..52a3b4f --- /dev/null +++ b/src/types/pixel_mask.rs @@ -0,0 +1,22 @@ +use crate::bindings; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[repr(u32)] +pub enum PixelMask { + Undefined = bindings::PixelMask_UndefinedPixelMask, + Read = bindings::PixelMask_ReadPixelMask, + Write = bindings::PixelMask_WritePixelMask, + Composite = bindings::PixelMask_CompositePixelMask, +} + +impl Default for PixelMask { + fn default() -> Self { + return PixelMask::Undefined; + } +} + +impl From for bindings::PixelMask { + fn from(value: PixelMask) -> Self { + return value as bindings::PixelMask; + } +} diff --git a/src/wand/magick.rs b/src/wand/magick.rs index 27674ca..2f8c47e 100644 --- a/src/wand/magick.rs +++ b/src/wand/magick.rs @@ -51,6 +51,7 @@ use crate::{ MorphologyMethod, OrientationType, PixelInterpolateMethod, + PixelMask, RenderingIntent, ResolutionType, ResourceType, @@ -1239,6 +1240,27 @@ impl MagickWand { } } + /// Sets image clip mask. + /// + /// * `pixel_mask`: type of mask, Read or Write. + /// * `clip_mask`: the clip_mask wand. + pub fn set_image_mask( + &mut self, + pixel_mask: PixelMask, + clip_mask: &MagickWand + ) -> Result<()> { + match unsafe { + bindings::MagickSetImageMask( + self.wand, + pixel_mask.into(), + clip_mask.wand + ) + } { + MagickTrue => Ok(()), + _ => Err(MagickError("failed to set image mask")), + } + } + /// Set image channel mask pub fn set_image_channel_mask( &mut self, From 8c154511b1cdc0d61514d360e5aac8d4aa48aa76 Mon Sep 17 00:00:00 2001 From: 5ohue <86558263+5ohue@users.noreply.github.com> Date: Tue, 14 May 2024 12:18:24 +0300 Subject: [PATCH 35/37] Add a few more functions and typed Adds `Image` type and a few image related functions. --- src/types/image.rs | 21 ++++++++ src/types/layer_method.rs | 35 +++++++++++++ src/types/mod.rs | 4 ++ src/wand/magick.rs | 106 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 166 insertions(+) create mode 100644 src/types/image.rs create mode 100644 src/types/layer_method.rs diff --git a/src/types/image.rs b/src/types/image.rs new file mode 100644 index 0000000..17ae07a --- /dev/null +++ b/src/types/image.rs @@ -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 + } +} diff --git a/src/types/layer_method.rs b/src/types/layer_method.rs new file mode 100644 index 0000000..e502af9 --- /dev/null +++ b/src/types/layer_method.rs @@ -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 for bindings::LayerMethod { + fn from(value: LayerMethod) -> Self { + return value as bindings::LayerMethod; + } +} diff --git a/src/types/mod.rs b/src/types/mod.rs index 047d9b4..8edb988 100644 --- a/src/types/mod.rs +++ b/src/types/mod.rs @@ -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; @@ -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; diff --git a/src/wand/magick.rs b/src/wand/magick.rs index 2f8c47e..3335101 100644 --- a/src/wand/magick.rs +++ b/src/wand/magick.rs @@ -42,9 +42,11 @@ use crate::{ EndianType, FilterType, GravityType, + Image, ImageType, InterlaceType, KernelInfo, + LayerMethod, MagickEvaluateOperator, MagickFunction, MetricType, @@ -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 { + 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(()), @@ -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 { + 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( @@ -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 { + 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 { + 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> { + 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) From e87004101538b88e5281d9debfa0847f42b92f4d Mon Sep 17 00:00:00 2001 From: 5ohue <86558263+5ohue@users.noreply.github.com> Date: Tue, 14 May 2024 12:22:52 +0300 Subject: [PATCH 36/37] Fix warnings in tests --- tests/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/lib.rs b/tests/lib.rs index 759fb0c..f929aa4 100644 --- a/tests/lib.rs +++ b/tests/lib.rs @@ -54,7 +54,7 @@ fn test_resize_image() { 1 => 1, height => height / 2, }; - wand.resize_image(halfwidth, halfheight, magick_rust::FilterType::Lanczos); + assert!(wand.resize_image(halfwidth, halfheight, magick_rust::FilterType::Lanczos).is_ok()); assert_eq!(256, wand.get_image_width()); assert_eq!(192, wand.get_image_height()); } @@ -76,7 +76,7 @@ fn test_thumbnail_image() { 1 => 1, height => height / 2, }; - wand.thumbnail_image(halfwidth, halfheight); + assert!(wand.thumbnail_image(halfwidth, halfheight).is_ok()); assert_eq!(256, wand.get_image_width()); assert_eq!(192, wand.get_image_height()); } From b35f21cd894f9720e558d1bc3f3dc33270d82a51 Mon Sep 17 00:00:00 2001 From: 5ohue <86558263+5ohue@users.noreply.github.com> Date: Thu, 16 May 2024 18:38:04 +0300 Subject: [PATCH 37/37] Add methods to import and export pixels as f64 --- src/wand/magick.rs | 59 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/src/wand/magick.rs b/src/wand/magick.rs index 3335101..adca422 100644 --- a/src/wand/magick.rs +++ b/src/wand/magick.rs @@ -1056,6 +1056,38 @@ impl MagickWand { } } + pub fn export_image_pixels_double( + &self, + x: isize, + y: isize, + width: usize, + height: usize, + map: &str, + ) -> Option> { + let c_map = CString::new(map).unwrap(); + let capacity = width * height * map.len(); + let mut pixels = Vec::with_capacity(capacity); + pixels.resize(capacity, 0.0); + + unsafe { + if bindings::MagickExportImagePixels( + self.wand, + x, + y, + width, + height, + c_map.as_ptr(), + bindings::StorageType_DoublePixel, + pixels.as_mut_ptr() as *mut c_void, + ) == MagickTrue + { + Some(pixels) + } else { + None + } + } + } + /// Resize the image to the specified width and height, using the /// specified filter type. pub fn resize_image(&self, width: usize, height: usize, filter: FilterType) -> Result<()> { @@ -1389,6 +1421,33 @@ impl MagickWand { } } + pub fn import_image_pixels_double( + &mut self, + x: isize, + y: isize, + columns: usize, + rows: usize, + pixels: &[f64], + map: &str, + ) -> Result<()> { + let pixel_map = CString::new(map).unwrap(); + match unsafe { + bindings::MagickImportImagePixels( + self.wand, + x, + y, + columns, + rows, + pixel_map.as_ptr(), + bindings::StorageType_DoublePixel, + pixels.as_ptr() as *const libc::c_void, + ) + } { + MagickTrue => Ok(()), + _ => Err(MagickError("unable to import pixels")), + } + } + /// Set the wand iterator to the first image. /// See for more information. pub fn set_first_iterator(&self) {