Skip to main content

magick_rust/wand/
magick.rs

1/*
2 * Copyright 2016 Mattis Marjak
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *     http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16use std::ffi::{CStr, CString};
17use std::{fmt, ptr, slice};
18
19#[cfg(target_os = "freebsd")]
20use libc::size_t;
21use libc::{c_char, c_uchar, c_void};
22
23use crate::bindings;
24use crate::result::MagickError;
25#[cfg(not(target_os = "freebsd"))]
26use crate::size_t;
27
28use super::{MagickFalse, MagickTrue};
29use crate::result::Result;
30
31use super::{DrawingWand, PixelWand};
32#[cfg(any(target_os = "linux", target_os = "macos"))]
33use crate::ResourceType;
34use crate::bindings::MagickBooleanType;
35use crate::{
36    AlphaChannelOption, AutoThresholdMethod, ChannelType, ColorspaceType, CompositeOperator,
37    CompressionType, DisposeType, DitherMethod, EndianType, FilterType, GravityType, Image,
38    ImageType, Images, ImagesMut, InterlaceType, KernelInfo, LayerMethod, MagickEvaluateOperator,
39    MagickFunction, MetricType, MorphologyMethod, OrientationType, PixelInterpolateMethod,
40    PixelMask, RenderingIntent, ResolutionType, StatisticType, VirtualPixelMethod,
41};
42
43wand_common!(
44    MagickWand,
45    NewMagickWand,
46    ClearMagickWand,
47    IsMagickWand,
48    CloneMagickWand,
49    DestroyMagickWand,
50    MagickClearException,
51    MagickGetExceptionType,
52    MagickGetException
53);
54
55/// MagickWand is a Rustic wrapper to the Rust bindings to ImageMagick.
56///
57/// Instantiating a `MagickWand` will construct an ImageMagick "wand"
58/// on which operations can be performed via the `MagickWand` functions.
59/// When the `MagickWand` is dropped, the ImageMagick wand will be
60/// destroyed as well.
61impl MagickWand {
62    /// Creates new wand by cloning the image.
63    ///
64    /// * `img`: the image.
65    pub fn new_from_image(img: &Image<'_>) -> Result<MagickWand> {
66        let wand_ptr = unsafe { bindings::NewMagickWandFromImage(img.get_ptr()) };
67        Self::result_from_ptr_with_error_message(
68            wand_ptr,
69            MagickWand::from_ptr,
70            "failed to create magick wand from image",
71        )
72    }
73
74    /// Add a blank image canvas of the given dimensions and background color.
75    pub fn new_image(&self, columns: usize, rows: usize, background: &PixelWand) -> Result<()> {
76        self.result_from_boolean(unsafe {
77            bindings::MagickNewImage(self.wand, columns, rows, background.as_ptr())
78        })
79    }
80
81    /// opt-in platforms that have resource limits support
82    #[cfg(any(target_os = "linux", target_os = "macos"))]
83    pub fn set_resource_limit(resource: ResourceType, limit: u64) -> Result<()> {
84        Self::result_from_boolean_with_error_message(
85            unsafe {
86                bindings::MagickSetResourceLimit(resource, limit as bindings::MagickSizeType)
87            },
88            "failed to set resource limit",
89        )
90    }
91
92    /// Returns the current limit for the given resource (e.g. the maximum number
93    /// of threads when `resource` is `ResourceType::Thread`), as previously set
94    /// by [`MagickWand::set_resource_limit`] or the ImageMagick defaults.
95    pub fn get_resource_limit(resource: ResourceType) -> u64 {
96        unsafe { bindings::MagickGetResourceLimit(resource) as u64 }
97    }
98
99    /// Returns the amount of the given resource currently in use.
100    pub fn get_resource(resource: ResourceType) -> u64 {
101        unsafe { bindings::MagickGetResource(resource) as u64 }
102    }
103
104    /// Associate one option name/value pair with the wand.
105    pub fn set_option(&mut self, key: &str, value: &str) -> Result<()> {
106        let c_key = CString::new(key).map_err(|_| "key string contains null byte")?;
107        let c_value = CString::new(value).map_err(|_| "value string contains null byte")?;
108        self.result_from_boolean(unsafe {
109            bindings::MagickSetOption(self.wand, c_key.as_ptr(), c_value.as_ptr())
110        })
111    }
112
113    /// Annotate the image with text drawn using the given drawing wand, at
114    /// position `(x, y)` and rotated by `angle` degrees.
115    pub fn annotate_image(
116        &mut self,
117        drawing_wand: &DrawingWand,
118        x: f64,
119        y: f64,
120        angle: f64,
121        text: &str,
122    ) -> Result<()> {
123        let c_string = CString::new(text).map_err(|_| "could not convert to cstring")?;
124        self.result_from_boolean(unsafe {
125            bindings::MagickAnnotateImage(
126                self.wand,
127                drawing_wand.as_ptr(),
128                x,
129                y,
130                angle,
131                c_string.as_ptr() as *const _,
132            )
133        })
134    }
135
136    /// Add all images from another wand to this wand at the current index.
137    pub fn add_image(&mut self, other_wand: &MagickWand) -> Result<()> {
138        self.result_from_boolean(unsafe { bindings::MagickAddImage(self.wand, other_wand.wand) })
139    }
140
141    /// Append all images in the wand into a single new wand, stacking them
142    /// vertically when `stack` is `true` or horizontally when `false`.
143    pub fn append_all(&mut self, stack: bool) -> Result<MagickWand> {
144        unsafe { bindings::MagickResetIterator(self.wand) };
145        let wand_ptr = unsafe { bindings::MagickAppendImages(self.wand, stack.into()) };
146        Self::result_from_ptr_with_error_message(
147            wand_ptr,
148            MagickWand::from_ptr,
149            "failed to append image",
150        )
151    }
152
153    /// Set the image label property to the given string.
154    pub fn label_image(&self, label: &str) -> Result<()> {
155        let c_label = CString::new(label).map_err(|_| "label string contains null byte")?;
156        self.result_from_boolean(unsafe { bindings::MagickLabelImage(self.wand, c_label.as_ptr()) })
157    }
158
159    /// Write all images in the wand to the named file. When `adjoin` is `true`
160    /// and the format supports it, all images are written into a single file;
161    /// otherwise each image is written to a separately numbered file.
162    pub fn write_images(&self, path: &str, adjoin: bool) -> Result<()> {
163        let c_name = CString::new(path).map_err(|_| "path string contains null byte")?;
164        self.result_from_boolean(unsafe {
165            bindings::MagickWriteImages(self.wand, c_name.as_ptr(), adjoin.into())
166        })
167    }
168
169    /// Read the image data from the named file.
170    pub fn read_image(&self, path: &str) -> Result<()> {
171        let c_name = CString::new(path).map_err(|_| "path string contains null byte")?;
172        self.result_from_boolean(unsafe { bindings::MagickReadImage(self.wand, c_name.as_ptr()) })
173    }
174
175    /// Read the image data from the vector of bytes.
176    pub fn read_image_blob<T: AsRef<[u8]>>(&self, data: T) -> Result<()> {
177        let int_slice = data.as_ref();
178        let size = int_slice.len();
179        self.result_from_boolean(unsafe {
180            bindings::MagickReadImageBlob(self.wand, int_slice.as_ptr() as *const c_void, size)
181        })
182    }
183
184    /// Same as read_image, but reads only the width, height, size and format of an image,
185    /// without reading data.
186    pub fn ping_image(&self, path: &str) -> Result<()> {
187        let c_name = CString::new(path).map_err(|_| "path string contains null byte")?;
188        self.result_from_boolean(unsafe { bindings::MagickPingImage(self.wand, c_name.as_ptr()) })
189    }
190
191    /// Same as read_image, but reads only the width, height, size and format of an image,
192    /// without reading data.
193    pub fn ping_image_blob<T: AsRef<[u8]>>(&self, data: T) -> Result<()> {
194        let int_slice = data.as_ref();
195        let size = int_slice.len();
196        self.result_from_boolean(unsafe {
197            bindings::MagickPingImageBlob(self.wand, int_slice.as_ptr() as *const c_void, size)
198        })
199    }
200
201    /// Composes all the image layers from the current given image onward to produce a single image
202    /// of the merged layers.
203    ///
204    /// The inital canvas's size depends on the given LayerMethod, and is initialized using the
205    /// first images background color. The images are then composited onto that image in sequence
206    /// using the given composition that has been assigned to each individual image.
207    ///
208    /// * `method`: the method of selecting the size of the initial canvas.
209    ///   MergeLayer: Merge all layers onto a canvas just large enough to hold all the actual
210    ///   images. The virtual canvas of the first image is preserved but otherwise ignored.
211    ///
212    ///     FlattenLayer: Use the virtual canvas size of first image. Images which fall outside
213    ///   this canvas is clipped. This can be used to 'fill out' a given virtual canvas.
214    ///
215    ///     MosaicLayer: Start with the virtual canvas of the first image, enlarging left and right
216    ///   edges to contain all images. Images with negative offsets will be clipped.
217    pub fn merge_image_layers(&self, method: LayerMethod) -> Result<MagickWand> {
218        let wand_ptr = unsafe { bindings::MagickMergeImageLayers(self.wand, method) };
219        Self::result_from_ptr_with_error_message(
220            wand_ptr,
221            MagickWand::from_ptr,
222            "failed to merge image layers",
223        )
224    }
225
226    /// Returns the number of images associated with a magick wand.
227    pub fn get_number_images(&self) -> usize {
228        unsafe { bindings::MagickGetNumberImages(self.wand) }
229    }
230
231    /// Compare two images and return tuple `(distortion, diffImage)`
232    /// `diffImage` is `None` if `distortion == 0`
233    pub fn compare_images(
234        &self,
235        reference: &MagickWand,
236        metric: MetricType,
237    ) -> (f64, Option<MagickWand>) {
238        let mut distortion: f64 = 0.0;
239        let wand_ptr = unsafe {
240            bindings::MagickCompareImages(self.wand, reference.wand, metric, &mut distortion)
241        };
242
243        let wand =
244            Self::result_from_ptr_with_error_message(wand_ptr, MagickWand::from_ptr, "").ok();
245        (distortion, wand)
246    }
247
248    /// Compose another image onto self at (x, y) using composition_operator
249    pub fn compose_images(
250        &self,
251        reference: &MagickWand,
252        composition_operator: CompositeOperator,
253        clip_to_self: bool,
254        x: isize,
255        y: isize,
256    ) -> Result<()> {
257        self.result_from_boolean(unsafe {
258            bindings::MagickCompositeImage(
259                self.wand,
260                reference.wand,
261                composition_operator,
262                MagickBooleanType::from(clip_to_self),
263                x,
264                y,
265            )
266        })
267    }
268
269    /// Compose another image onto self with gravity using composition_operator
270    pub fn compose_images_gravity(
271        &self,
272        reference: &MagickWand,
273        composition_operator: CompositeOperator,
274        gravity_type: GravityType,
275    ) -> Result<()> {
276        self.result_from_boolean(unsafe {
277            bindings::MagickCompositeImageGravity(
278                self.wand,
279                reference.wand,
280                composition_operator,
281                gravity_type,
282            )
283        })
284    }
285
286    /// Rebuilds image sequence with each frame size the same as first frame, and composites each frame atop of previous.
287    /// Only affects GIF, and other formats with multiple pages/layers.
288    pub fn coalesce(&mut self) -> Result<MagickWand> {
289        let wand_ptr = unsafe { bindings::MagickCoalesceImages(self.wand) };
290        Self::result_from_ptr_with_error_message(
291            wand_ptr,
292            MagickWand::from_ptr,
293            "failed to coalesce images",
294        )
295    }
296
297    /// Replaces colors in the image from a color lookup table.
298    pub fn clut_image(&self, clut_wand: &MagickWand, method: PixelInterpolateMethod) -> Result<()> {
299        self.result_from_boolean(unsafe {
300            bindings::MagickClutImage(self.wand, clut_wand.wand, method)
301        })
302    }
303
304    /// Replaces colors in the image using a Hald color lookup table (a Hald CLUT image).
305    pub fn hald_clut_image(&self, clut_wand: &MagickWand) -> Result<()> {
306        self.result_from_boolean(unsafe {
307            bindings::MagickHaldClutImage(self.wand, clut_wand.wand)
308        })
309    }
310
311    /// Evaluate the given expression for each pixel, returning a new wand with
312    /// the result. Wraps ImageMagick's `MagickFxImage`.
313    pub fn fx(&mut self, expression: &str) -> Result<MagickWand> {
314        let c_expression =
315            CString::new(expression).map_err(|_| "expression string contains null byte")?;
316        let wand_ptr = unsafe { bindings::MagickFxImage(self.wand, c_expression.as_ptr()) };
317        Self::result_from_ptr_with_error_message(
318            wand_ptr,
319            MagickWand::from_ptr,
320            "failed to fx the image",
321        )
322    }
323
324    /// Sets the size of the wand, used to read images larger than the canvas or
325    /// to size formats (e.g. PostScript) that have no inherent dimensions.
326    pub fn set_size(&self, columns: usize, rows: usize) -> Result<()> {
327        self.result_from_boolean(unsafe { bindings::MagickSetSize(self.wand, columns, rows) })
328    }
329
330    /// Define two 'quantum_range' functions because the bindings::QuantumRange symbol
331    /// is not available if hdri is disabled in the compiled ImageMagick libs
332    #[cfg(not(feature = "disable-hdri"))]
333    fn quantum_range(&self) -> Result<f64> {
334        Ok(bindings::QuantumRange)
335    }
336
337    /// with disable-hdri enabled we define our own quantum_range
338    /// values lifted directly from magick-type.h
339    #[cfg(feature = "disable-hdri")]
340    fn quantum_range(&self) -> Result<f64> {
341        match bindings::MAGICKCORE_QUANTUM_DEPTH {
342            8 => Ok(255.0f64),
343            16 => Ok(65535.0f64),
344            32 => Ok(4294967295.0f64),
345            64 => Ok(18446744073709551615.0f64),
346            _ => Err(MagickError(
347                "Quantum depth must be one of 8, 16, 32 or 64".to_string(),
348            )),
349        }
350    }
351
352    /// Level an image. Black and white points are multiplied with QuantumRange to
353    /// decrease dependencies on the end user.
354    pub fn level_image(&self, black_point: f64, gamma: f64, white_point: f64) -> Result<()> {
355        let quantum_range = self.quantum_range()?;
356
357        self.result_from_boolean(unsafe {
358            bindings::MagickLevelImage(
359                self.wand,
360                black_point * quantum_range,
361                gamma,
362                white_point * quantum_range,
363            )
364        })
365    }
366
367    /// Applies the reversed [level_image](Self::level_image). It compresses the full range of color values, so
368    /// that they lie between the given black and white points. Gamma is applied before the values
369    /// are mapped. It can be used to de-contrast a greyscale image to the exact levels specified.
370    pub fn levelize_image(&self, black_point: f64, gamma: f64, white_point: f64) -> Result<()> {
371        let quantum_range = self.quantum_range()?;
372
373        self.result_from_boolean(unsafe {
374            bindings::MagickLevelizeImage(
375                self.wand,
376                black_point * quantum_range,
377                gamma,
378                white_point * quantum_range,
379            )
380        })
381    }
382
383    /// MagickNormalizeImage enhances the contrast of a color image by adjusting the pixels color
384    /// to span the entire range of colors available
385    pub fn normalize_image(&self) -> Result<()> {
386        self.result_from_boolean(unsafe { bindings::MagickNormalizeImage(self.wand) })
387    }
388
389    /// MagickOrderedDitherImage performs an ordered dither based on a number of pre-defined
390    /// dithering threshold maps, but over multiple intensity levels, which can be different for
391    /// different channels, according to the input arguments.
392    pub fn ordered_dither_image(&self, threshold_map: &str) -> Result<()> {
393        let c_threshold_map =
394            CString::new(threshold_map).map_err(|_| "threshold_map string contains null byte")?;
395
396        self.result_from_boolean(unsafe {
397            bindings::MagickOrderedDitherImage(self.wand, c_threshold_map.as_ptr())
398        })
399    }
400
401    /// Apply sigmoidal contrast to the image
402    ///
403    /// Adjusts the contrast of an image with a non-linear sigmoidal contrast algorithm. Increase
404    /// the contrast of the image using a sigmoidal transfer function without saturating highlights
405    /// or shadows. Contrast indicates how much to increase the contrast (0 is none; 3 is typical;
406    /// 20 is pushing it); mid-point indicates where midtones fall in the resultant image (0.0 is
407    /// white; 0.5 is middle-gray; 1.0 is black). Set sharpen to `true` to increase the image
408    /// contrast otherwise the contrast is reduced.
409    ///
410    /// * `sharpen`: increase or decrease image contrast
411    /// * `strength`: strength of the contrast, the larger the number the more 'threshold-like' it becomes.
412    /// * `midpoint`: midpoint of the function as a number in range [0, 1]
413    pub fn sigmoidal_contrast_image(
414        &self,
415        sharpen: bool,
416        strength: f64,
417        midpoint: f64,
418    ) -> Result<()> {
419        let quantum_range = self.quantum_range()?;
420
421        self.result_from_boolean(unsafe {
422            bindings::MagickSigmoidalContrastImage(
423                self.wand,
424                sharpen.into(),
425                strength,
426                midpoint * quantum_range,
427            )
428        })
429    }
430
431    /// Extend the image as defined by the geometry, gravity, and wand background color. Set the
432    /// (x,y) offset of the geometry to move the original wand relative to the extended wand.
433    pub fn extend_image(&self, width: usize, height: usize, x: isize, y: isize) -> Result<()> {
434        self.result_from_boolean(unsafe {
435            bindings::MagickExtentImage(self.wand, width, height, x, y)
436        })
437    }
438
439    /// Add or remove a named ICC, IPTC, or generic profile from the image.
440    /// Passing `None` (or an empty profile) for `profile` removes the named
441    /// profile.
442    pub fn profile_image<'a, T: Into<Option<&'a [u8]>>>(
443        &self,
444        name: &str,
445        profile: T,
446    ) -> Result<()> {
447        let c_name = CString::new(name).map_err(|_| "name string contains null byte")?;
448        let result = unsafe {
449            let profile = profile.into();
450            let profile_ptr = match profile {
451                Some(data) => data.as_ptr(),
452                None => ptr::null(),
453            } as *const c_void;
454            let profile_len = match profile {
455                Some(data) => data.len(),
456                None => 0,
457            };
458            bindings::MagickProfileImage(self.wand, c_name.as_ptr(), profile_ptr, profile_len)
459        };
460        self.result_from_boolean(result)
461    }
462
463    /// Strip the image of all profiles and comments.
464    pub fn strip_image(&self) -> Result<()> {
465        self.result_from_boolean(unsafe { bindings::MagickStripImage(self.wand) })
466    }
467
468    /// Flip the image vertically (mirror about the horizontal axis).
469    pub fn flip_image(&self) -> Result<()> {
470        self.result_from_boolean(unsafe { bindings::MagickFlipImage(self.wand) })
471    }
472
473    /// Negate the colors in the image, producing its photographic negative.
474    pub fn negate_image(&self) -> Result<()> {
475        self.result_from_boolean(unsafe { bindings::MagickNegateImage(self.wand, MagickTrue) })
476    }
477
478    /// Flop the image horizontally (mirror about the vertical axis).
479    pub fn flop_image(&self) -> Result<()> {
480        self.result_from_boolean(unsafe { bindings::MagickFlopImage(self.wand) })
481    }
482
483    /// Blur the image by convolving it with a Gaussian operator of the given
484    /// `radius` and standard deviation (`sigma`), both in pixels. For reasonable
485    /// results the radius should be larger than sigma; use a radius of 0 to let
486    /// ImageMagick select a suitable radius.
487    pub fn blur_image(&self, radius: f64, sigma: f64) -> Result<()> {
488        self.result_from_boolean(unsafe { bindings::MagickBlurImage(self.wand, radius, sigma) })
489    }
490
491    /// Blur the image with a Gaussian operator of the given `radius` and
492    /// standard deviation (`sigma`), both in pixels. Use a radius of 0 to let
493    /// ImageMagick select a suitable radius.
494    pub fn gaussian_blur_image(&self, radius: f64, sigma: f64) -> Result<()> {
495        self.result_from_boolean(unsafe {
496            bindings::MagickGaussianBlurImage(self.wand, radius, sigma)
497        })
498    }
499
500    /// Replace each pixel with corresponding statistic from the neighborhood of the specified width and height.
501    ///
502    /// * `statistic_type`: the statistic type (e.g. `StatisticType::Median`, `StatisticType::Mode`, etc.).
503    /// * `width`: the width of the pixel neighborhood.
504    /// * `height`: the height of the pixel neighborhood.
505    pub fn statistic_image(
506        &self,
507        statistic_type: StatisticType,
508        width: usize,
509        height: usize,
510    ) -> Result<()> {
511        self.result_from_boolean(unsafe {
512            bindings::MagickStatisticImage(self.wand, statistic_type, width, height)
513        })
514    }
515
516    /// Calculate median for each pixel's neighborhood.
517    ///
518    /// See [statistic_image](Self::statistic_image)
519    pub fn median_blur_image(&self, width: usize, height: usize) -> Result<()> {
520        self.statistic_image(StatisticType::Median, width, height)
521    }
522
523    /// Adaptively resize the currently selected image.
524    pub fn adaptive_resize_image(&self, width: usize, height: usize) -> Result<()> {
525        self.result_from_boolean(unsafe {
526            bindings::MagickAdaptiveResizeImage(self.wand, width, height)
527        })
528    }
529
530    /// Rotate the currently selected image by the given number of degrees,
531    /// filling any empty space with the background color of a given PixelWand
532    pub fn rotate_image(&self, background: &PixelWand, degrees: f64) -> Result<()> {
533        self.result_from_boolean(unsafe {
534            bindings::MagickRotateImage(self.wand, background.as_ptr(), degrees)
535        })
536    }
537
538    /// Trim the image removing the background color from the edges.
539    ///
540    /// `fuzz` is the color-matching tolerance in raw quantum units
541    /// (`0..=QuantumRange`), *not* a fraction or percentage. To express the
542    /// ImageMagick command line's `-fuzz 15%`, multiply: e.g. on a Q16 build use
543    /// `0.15 * 65535.0`. Passing a small value such as `0.15` is effectively a
544    /// zero-tolerance trim and will not remove a noisy border.
545    pub fn trim_image(&self, fuzz: f64) -> Result<()> {
546        self.result_from_boolean(unsafe { bindings::MagickTrimImage(self.wand, fuzz) })
547    }
548
549    /// Returns the virtual pixel method used when accessing pixels outside the
550    /// image (for example by `blur_image` near the edges).
551    pub fn get_image_virtual_pixel_method(&self) -> VirtualPixelMethod {
552        unsafe { bindings::MagickGetImageVirtualPixelMethod(self.wand) }
553    }
554
555    /// Sets the virtual pixel method used when accessing pixels outside the
556    /// image, returning the previous method. This is the equivalent of the
557    /// command line `-virtual-pixel` setting, e.g. `VirtualPixelMethod::Edge`
558    /// for `-virtual-pixel edge`.
559    pub fn set_image_virtual_pixel_method(
560        &mut self,
561        method: VirtualPixelMethod,
562    ) -> VirtualPixelMethod {
563        unsafe { bindings::MagickSetImageVirtualPixelMethod(self.wand, method) }
564    }
565
566    /// Retrieve the width of the image.
567    pub fn get_image_width(&self) -> usize {
568        unsafe { bindings::MagickGetImageWidth(self.wand) }
569    }
570
571    /// Retrieve the height of the image.
572    pub fn get_image_height(&self) -> usize {
573        unsafe { bindings::MagickGetImageHeight(self.wand) }
574    }
575
576    /// Retrieve the page geometry (width, height, x offset, y offset) of the image.
577    pub fn get_image_page(&self) -> (usize, usize, isize, isize) {
578        let (mut width, mut height, mut x, mut y) = (0usize, 0usize, 0isize, 0isize);
579        unsafe {
580            // Note: The C MagickGetImagePage function always returns true
581            // and exits on error, so we don't check the return value here.
582            bindings::MagickGetImagePage(self.wand, &mut width, &mut height, &mut x, &mut y);
583        }
584        (width, height, x, y)
585    }
586
587    /// Reset the Wand page canvas and position.
588    pub fn reset_image_page(&self, page_geometry: &str) -> Result<()> {
589        let c_page_geometry =
590            CString::new(page_geometry).map_err(|_| "page_geometry contains null byte")?;
591        self.result_from_boolean(unsafe {
592            bindings::MagickResetImagePage(self.wand, c_page_geometry.as_ptr())
593        })
594    }
595
596    /// Returns a value associated with the specified artifact.
597    ///
598    /// * `artifact`: the artifact.
599    pub fn get_image_artifact(&self, artifact: &str) -> Result<String> {
600        let c_artifact =
601            CString::new(artifact).map_err(|_| "artifact string contains null byte")?;
602
603        let c_value = unsafe { bindings::MagickGetImageArtifact(self.wand, c_artifact.as_ptr()) };
604        Self::result_from_ptr_with_error_message(
605            c_value,
606            Self::c_char_into_string,
607            format!("missing artifact: {artifact}"),
608        )
609    }
610
611    /// Returns the values of all image artifacts whose names match the given pattern.
612    pub fn get_image_artifacts(&self, pattern: &str) -> Result<Vec<String>> {
613        let c_pattern = CString::new(pattern)
614            .map_err(|_| MagickError("artifact string contains null byte".to_string()))?;
615        let mut num_of_artifacts: size_t = 0;
616
617        let c_values = unsafe {
618            bindings::MagickGetImageArtifacts(self.wand, c_pattern.as_ptr(), &mut num_of_artifacts)
619        };
620
621        Self::result_from_ptr_with_error_message(
622            c_values,
623            |c_values| Self::c_char_to_string_vec(c_values, num_of_artifacts),
624            "image has no artifacts",
625        )
626    }
627
628    /// Sets a key-value pair in the image artifact namespace. Artifacts differ from properties.
629    /// Properties are public and are generally exported to an external image format if the format
630    /// supports it. Artifacts are private and are utilized by the internal ImageMagick API to
631    /// modify the behavior of certain algorithms.
632    ///
633    /// * `artifact`: the artifact.
634    /// * `value`: the value.
635    ///
636    /// # Example
637    ///
638    /// This example shows how you can blend an image with its blurred copy with 50% opacity by
639    /// setting "compose:args" to "50". This is equivalent to having `-define compose:args=50` when
640    /// using imagemagick cli.
641    ///
642    /// ```
643    /// use magick_rust::{MagickWand, PixelWand, CompositeOperator};
644    ///
645    /// fn main() -> Result<(), magick_rust::MagickError> {
646    ///     let mut wand1 = MagickWand::new();
647    ///     wand1.new_image(4, 4, &PixelWand::new())?; // Replace with `read_image` to open your image file
648    ///     let wand2 = wand1.clone();
649    ///
650    ///     wand1.median_blur_image(10, 10)?;
651    ///
652    ///     wand1.set_image_artifact("compose:args", "50")?;
653    ///     wand1.compose_images(&wand2, CompositeOperator::Blend, false, 0, 0)?;
654    ///
655    ///     Ok(())
656    /// }
657    /// ```
658    pub fn set_image_artifact(&mut self, artifact: &str, value: &str) -> Result<()> {
659        let c_artifact =
660            CString::new(artifact).map_err(|_| "artifact string contains null byte")?;
661        let c_value = CString::new(value).map_err(|_| "value string contains null byte")?;
662
663        self.result_from_boolean(unsafe {
664            bindings::MagickSetImageArtifact(self.wand, c_artifact.as_ptr(), c_value.as_ptr())
665        })
666    }
667
668    /// Deletes a wand artifact.
669    ///
670    /// * `artifact`: the artifact.
671    pub fn delete_image_artifact(&mut self, artifact: &str) -> Result<()> {
672        let c_artifact =
673            CString::new(artifact).map_err(|_| "artifact string contains null byte")?;
674
675        Self::result_from_boolean_with_error_message(
676            unsafe { bindings::MagickDeleteImageArtifact(self.wand, c_artifact.as_ptr()) },
677            format!("missing artifact: {artifact}"),
678        )
679    }
680
681    /// Retrieve the named image property value.
682    pub fn get_image_property(&self, name: &str) -> Result<String> {
683        let c_name = CString::new(name).map_err(|_| "name string contains null byte")?;
684        let c_value = unsafe { bindings::MagickGetImageProperty(self.wand, c_name.as_ptr()) };
685
686        Self::result_from_ptr_with_error_message(
687            c_value,
688            Self::c_char_into_string,
689            format!("missing property: {name}"),
690        )
691    }
692
693    /// Returns the values of all image properties whose names match the given pattern.
694    pub fn get_image_properties(&self, pattern: &str) -> Result<Vec<String>> {
695        let c_pattern = CString::new(pattern)
696            .map_err(|_| MagickError("artifact string contains null byte".to_string()))?;
697        let mut num_of_artifacts: size_t = 0;
698
699        let c_values = unsafe {
700            bindings::MagickGetImageProperties(self.wand, c_pattern.as_ptr(), &mut num_of_artifacts)
701        };
702
703        self.result_from_ptr(c_values, |c_values| {
704            Self::c_char_to_string_vec(c_values, num_of_artifacts)
705        })
706    }
707
708    /// Set the named image property.
709    pub fn set_image_property(&self, name: &str, value: &str) -> Result<()> {
710        let c_name = CString::new(name).map_err(|_| "name string contains null byte")?;
711        let c_value = CString::new(value).map_err(|_| "value string contains null byte")?;
712        self.result_from_boolean(unsafe {
713            bindings::MagickSetImageProperty(self.wand, c_name.as_ptr(), c_value.as_ptr())
714        })
715    }
716
717    /// Returns a `PixelWand` instance for the pixel specified by x and y offests.
718    pub fn get_image_pixel_color(&self, x: isize, y: isize) -> Option<PixelWand> {
719        let pw = PixelWand::new();
720
721        let result = unsafe { bindings::MagickGetImagePixelColor(self.wand, x, y, pw.as_ptr()) };
722        self.result_from_boolean(result).map(|_| pw).ok()
723    }
724
725    /// Sets the image sampling factors.
726    ///
727    /// samplingFactors: An array of floats representing the sampling factor for each color component (in RGB order).
728    pub fn set_sampling_factors(&self, samplingFactors: &[f64]) -> Result<()> {
729        self.result_from_boolean(unsafe {
730            bindings::MagickSetSamplingFactors(
731                self.wand,
732                samplingFactors.len(),
733                &samplingFactors[0],
734            )
735        })
736    }
737
738    /// Returns the image histogram as a vector of `PixelWand` instances for every unique color.
739    pub fn get_image_histogram(&self) -> Option<Vec<PixelWand>> {
740        let mut color_count: size_t = 0;
741
742        unsafe {
743            bindings::MagickGetImageHistogram(self.wand, &mut color_count)
744                .as_mut()
745                .map(|ptrs| {
746                    slice::from_raw_parts(ptrs, color_count)
747                        .iter()
748                        .map(|wand_ptr| PixelWand::from_ptr(*wand_ptr))
749                        .collect()
750                })
751        }
752    }
753
754    /// Sharpens an image. We convolve the image with a Gaussian operator of the
755    /// given radius and standard deviation (sigma). For reasonable results, the
756    /// radius should be larger than sigma. Use a radius of 0 and SharpenImage()
757    /// selects a suitable radius for you.
758    ///
759    /// radius: the radius of the Gaussian, in pixels, not counting the center pixel.
760    ///
761    /// sigma: the standard deviation of the Gaussian, in pixels.
762    ///
763    pub fn sharpen_image(&self, radius: f64, sigma: f64) -> Result<()> {
764        self.result_from_boolean(unsafe { bindings::MagickSharpenImage(self.wand, radius, sigma) })
765    }
766
767    /// Set the background color.
768    pub fn set_background_color(&self, pixel_wand: &PixelWand) -> Result<()> {
769        self.result_from_boolean(unsafe {
770            bindings::MagickSetBackgroundColor(self.wand, pixel_wand.as_ptr())
771        })
772    }
773
774    /// Set the image background color.
775    pub fn set_image_background_color(&self, pixel_wand: &PixelWand) -> Result<()> {
776        self.result_from_boolean(unsafe {
777            bindings::MagickSetImageBackgroundColor(self.wand, pixel_wand.as_ptr())
778        })
779    }
780
781    /// Returns the image resolution as a pair (horizontal resolution, vertical resolution)
782    pub fn get_image_resolution(&self) -> Result<(f64, f64)> {
783        let mut x_resolution = 0f64;
784        let mut y_resolution = 0f64;
785        self.result_from_boolean(unsafe {
786            bindings::MagickGetImageResolution(self.wand, &mut x_resolution, &mut y_resolution)
787        })
788        .map(|_| (x_resolution, y_resolution))
789    }
790
791    /// Returns the range of the image as a pair `(minima, maxima)`, in raw
792    /// quantum values (i.e. `0..=QuantumRange`). The range is computed over the
793    /// channels currently enabled by the image's channel mask; by default that
794    /// is every channel. To restrict the range to a single channel, see
795    /// [`MagickWand::get_image_channel_range`].
796    pub fn get_image_range(&self) -> Result<(f64, f64)> {
797        let mut minima = 0f64;
798        let mut maxima = 0f64;
799        self.result_from_boolean(unsafe {
800            bindings::MagickGetImageRange(self.wand, &mut minima, &mut maxima)
801        })
802        .map(|_| (minima, maxima))
803    }
804
805    /// Returns the range of a single channel as a pair `(minima, maxima)`, in
806    /// raw quantum values (i.e. `0..=QuantumRange`). This is the equivalent of
807    /// PHP Imagick's `getImageChannelRange`, which was removed from the C API in
808    /// ImageMagick 7: it is implemented by temporarily setting the image channel
809    /// mask, reading the range, then restoring the previous mask.
810    pub fn get_image_channel_range(&mut self, channel: ChannelType) -> Result<(f64, f64)> {
811        let previous = self.set_image_channel_mask(channel);
812        let range = self.get_image_range();
813        self.set_image_channel_mask(previous);
814        range
815    }
816
817    /// Sets the image resolution
818    pub fn set_image_resolution(&self, x_resolution: f64, y_resolution: f64) -> Result<()> {
819        self.result_from_boolean(unsafe {
820            bindings::MagickSetImageResolution(self.wand, x_resolution, y_resolution)
821        })
822    }
823
824    /// Sets the wand resolution
825    pub fn set_resolution(&self, x_resolution: f64, y_resolution: f64) -> Result<()> {
826        self.result_from_boolean(unsafe {
827            bindings::MagickSetResolution(self.wand, x_resolution, y_resolution)
828        })
829    }
830
831    /// Applies a special effect to the image, similar to the effect achieved in
832    /// a photo darkroom by sepia toning. The `threshold` controls the extent of
833    /// the tone darkening and is given as a fraction of the quantum range
834    /// (a value around 0.8, i.e. 80%, is typical).
835    pub fn sepia_tone_image(&self, threshold: f64) -> Result<()> {
836        self.result_from_boolean(unsafe {
837            bindings::MagickSepiaToneImage(self.wand, threshold * self.quantum_range()?)
838        })
839    }
840
841    /// Extracts pixel data from the image as a vector of 0..255 values defined by `map`.
842    /// See <https://imagemagick.org/api/magick-image.php#MagickExportImagePixels> for more information.
843    pub fn export_image_pixels(
844        &self,
845        x: isize,
846        y: isize,
847        width: usize,
848        height: usize,
849        map: &str,
850    ) -> Option<Vec<u8>> {
851        let c_map = CString::new(map).ok()?;
852        let capacity = width * height * map.len();
853        let mut pixels = vec![0; capacity];
854
855        unsafe {
856            if bindings::MagickExportImagePixels(
857                self.wand,
858                x,
859                y,
860                width,
861                height,
862                c_map.as_ptr(),
863                bindings::StorageType::CharPixel,
864                pixels.as_mut_ptr() as *mut c_void,
865            ) == MagickTrue
866            {
867                Some(pixels)
868            } else {
869                None
870            }
871        }
872    }
873
874    /// Extracts pixel data from the image as a vector of `f64` values defined by
875    /// `map`. Like [`export_image_pixels`](Self::export_image_pixels) but with
876    /// floating-point (`DoublePixel`) storage.
877    pub fn export_image_pixels_double(
878        &self,
879        x: isize,
880        y: isize,
881        width: usize,
882        height: usize,
883        map: &str,
884    ) -> Option<Vec<f64>> {
885        let c_map = CString::new(map).expect("map contains null byte");
886        let capacity = width * height * map.len();
887        let mut pixels = Vec::with_capacity(capacity);
888        pixels.resize(capacity, 0.0);
889
890        unsafe {
891            if bindings::MagickExportImagePixels(
892                self.wand,
893                x,
894                y,
895                width,
896                height,
897                c_map.as_ptr(),
898                bindings::StorageType::DoublePixel,
899                pixels.as_mut_ptr() as *mut c_void,
900            ) == MagickTrue
901            {
902                Some(pixels)
903            } else {
904                None
905            }
906        }
907    }
908
909    /// Resize the image to the specified width and height, using the
910    /// specified filter type.
911    pub fn resize_image(&self, width: usize, height: usize, filter: FilterType) -> Result<()> {
912        self.result_from_boolean(unsafe {
913            bindings::MagickResizeImage(self.wand, width, height, filter)
914        })
915    }
916
917    /// Resize image by specifying the new size in percent of last size.
918    ///
919    /// Effectively resizes image to (current width * `width_scale`, current height *
920    /// `height_scale`)
921    pub fn scale_image(
922        &self,
923        width_scale: f64,
924        height_scale: f64,
925        filter: FilterType,
926    ) -> Result<()> {
927        if width_scale < 0.0 {
928            return Err(MagickError("negative width scale given".to_string()));
929        }
930        if height_scale < 0.0 {
931            return Err(MagickError("negative height scale given".to_string()));
932        }
933
934        let width = self.get_image_width();
935        let height = self.get_image_height();
936
937        let width = ((width as f64) * width_scale) as usize;
938        let height = ((height as f64) * height_scale) as usize;
939
940        self.resize_image(width, height, filter)
941    }
942
943    /// Resize the image to the specified width and height, using the
944    /// 'thumbnail' optimizations which remove a lot of image meta-data with the goal
945    /// of producing small low cost images suited for display on the web.
946    pub fn thumbnail_image(&self, width: usize, height: usize) -> Result<()> {
947        self.result_from_boolean(unsafe {
948            bindings::MagickThumbnailImage(self.wand, width, height)
949        })
950    }
951
952    /// Extract a region of the image. The width and height is used as the size
953    /// of the region. X and Y is the offset.
954    pub fn crop_image(&self, width: usize, height: usize, x: isize, y: isize) -> Result<()> {
955        self.result_from_boolean(unsafe {
956            bindings::MagickCropImage(self.wand, width, height, x, y)
957        })
958    }
959
960    /// Sample the image to the target resolution
961    ///
962    /// This is incredibly fast, as it does 1-1 pixel mapping for downscales, and box filtering for
963    /// upscales
964    pub fn sample_image(&self, width: usize, height: usize) -> Result<()> {
965        self.result_from_boolean(unsafe { bindings::MagickSampleImage(self.wand, width, height) })
966    }
967
968    /// Resample the image to the specified horizontal and vertical resolution, using the
969    /// specified filter type.
970    pub fn resample_image(
971        &self,
972        x_resolution: f64,
973        y_resolution: f64,
974        filter: FilterType,
975    ) -> Result<()> {
976        self.result_from_boolean(unsafe {
977            bindings::MagickResampleImage(self.wand, x_resolution, y_resolution, filter)
978        })
979    }
980
981    /// Rescale the image using seam carving algorithm
982    pub fn liquid_rescale_image(
983        &self,
984        width: usize,
985        height: usize,
986        delta_x: f64,
987        rigidity: f64,
988    ) -> Result<()> {
989        self.result_from_boolean(unsafe {
990            bindings::MagickLiquidRescaleImage(self.wand, width, height, delta_x, rigidity)
991        })
992    }
993
994    /// Implodes the image towards the center by the specified percentage
995    pub fn implode(&self, amount: f64, method: PixelInterpolateMethod) -> Result<()> {
996        self.result_from_boolean(unsafe { bindings::MagickImplodeImage(self.wand, amount, method) })
997    }
998
999    /// Resize the image to fit within the given dimensions, maintaining
1000    /// the current aspect ratio.
1001    pub fn fit(&self, width: usize, height: usize) {
1002        let mut width_ratio = width as f64;
1003        width_ratio /= self.get_image_width() as f64;
1004        let mut height_ratio = height as f64;
1005        height_ratio /= self.get_image_height() as f64;
1006        let (new_width, new_height) = if width_ratio < height_ratio {
1007            (
1008                width,
1009                (self.get_image_height() as f64 * width_ratio) as usize,
1010            )
1011        } else {
1012            (
1013                (self.get_image_width() as f64 * height_ratio) as usize,
1014                height,
1015            )
1016        };
1017        unsafe {
1018            bindings::MagickResetIterator(self.wand);
1019            while bindings::MagickNextImage(self.wand) != MagickFalse {
1020                bindings::MagickResizeImage(self.wand, new_width, new_height, FilterType::Lanczos);
1021            }
1022        }
1023    }
1024
1025    /// Detect if the loaded image is not in top-left orientation, and
1026    /// hence should be "auto" oriented so it is suitable for viewing.
1027    pub fn requires_orientation(&self) -> bool {
1028        self.get_image_orientation() != OrientationType::TopLeft
1029    }
1030
1031    /// Automatically adjusts the loaded image so that its orientation is
1032    /// suitable for viewing (i.e. top-left orientation).
1033    ///
1034    /// Returns `true` if successful or `false` if an error occurred.
1035    pub fn auto_orient(&self) -> bool {
1036        unsafe { bindings::MagickAutoOrientImage(self.wand) == MagickTrue }
1037    }
1038
1039    /// Write the current image to the provided path.
1040    pub fn write_image(&self, path: &str) -> Result<()> {
1041        let c_name = CString::new(path).map_err(|_| "name string contains null byte")?;
1042        self.result_from_boolean(unsafe { bindings::MagickWriteImage(self.wand, c_name.as_ptr()) })
1043    }
1044
1045    /// Write the image in the desired format to a new blob.
1046    ///
1047    /// The `format` argument may be any ImageMagick supported image
1048    /// format (e.g. GIF, JPEG, PNG, etc).
1049    pub fn write_image_blob(&self, format: &str) -> Result<Vec<u8>> {
1050        let c_format = CString::new(format).map_err(|_| "format string contains null byte")?;
1051        let mut length: size_t = 0;
1052        let blob = unsafe {
1053            bindings::MagickResetIterator(self.wand);
1054            bindings::MagickSetImageFormat(self.wand, c_format.as_ptr());
1055            bindings::MagickGetImageBlob(self.wand, &mut length)
1056        };
1057
1058        self.result_from_ptr(blob, |blob| Self::c_array_into_vec(blob, length))
1059    }
1060
1061    /// Write the images in the desired format to a new blob.
1062    ///
1063    /// The `format` argument may be any ImageMagick supported image
1064    /// format (e.g. GIF, JPEG, PNG, etc).
1065    pub fn write_images_blob(&self, format: &str) -> Result<Vec<u8>> {
1066        let c_format = CString::new(format).map_err(|_| "format string contains null byte")?;
1067        let mut length: size_t = 0;
1068        let blob = unsafe {
1069            bindings::MagickSetIteratorIndex(self.wand, 0);
1070            bindings::MagickSetImageFormat(self.wand, c_format.as_ptr());
1071            bindings::MagickGetImagesBlob(self.wand, &mut length)
1072        };
1073
1074        Ok(Self::c_array_into_vec(blob, length))
1075    }
1076
1077    /// Return false if the image alpha channel is not activated.
1078    /// That is, the image is RGB rather than RGBA or CMYK rather than CMYKA
1079    pub fn get_image_alpha_channel(&self) -> bool {
1080        let res = unsafe { bindings::MagickGetImageAlphaChannel(self.wand) };
1081        res == MagickTrue
1082    }
1083
1084    /// Renders the drawing wand on the current image
1085    pub fn draw_image(&mut self, drawing_wand: &DrawingWand) -> Result<()> {
1086        self.result_from_boolean(unsafe {
1087            bindings::MagickDrawImage(self.wand, drawing_wand.as_ptr())
1088        })
1089    }
1090
1091    /// Removes skew from the image. Skew is an artifact that
1092    /// occurs in scanned images because of the camera being misaligned,
1093    /// imperfections in the scanning or surface, or simply because the paper was
1094    /// not placed completely flat when scanned
1095    pub fn deskew_image(&mut self, threshold: f64) -> Result<()> {
1096        self.result_from_boolean(unsafe { bindings::MagickDeskewImage(self.wand, threshold) })
1097    }
1098
1099    /// Sets image clip mask.
1100    ///
1101    /// * `pixel_mask`: type of mask, Read or Write.
1102    /// * `clip_mask`: the clip_mask wand.
1103    pub fn set_image_mask(&mut self, pixel_mask: PixelMask, clip_mask: &MagickWand) -> Result<()> {
1104        self.result_from_boolean(unsafe {
1105            bindings::MagickSetImageMask(self.wand, pixel_mask, clip_mask.wand)
1106        })
1107    }
1108
1109    /// Set image channel mask
1110    pub fn set_image_channel_mask(&mut self, option: ChannelType) -> ChannelType {
1111        unsafe { bindings::MagickSetImageChannelMask(self.wand, option) }
1112    }
1113
1114    /// Apply an arithmetic, relational, or logical
1115    /// expression to an image.  Use these operators to lighten or darken an image,
1116    /// to increase or decrease contrast in an image, or to produce the "negative"
1117    /// of an image.
1118    pub fn evaluate_image(&mut self, op: MagickEvaluateOperator, val: f64) -> Result<()> {
1119        self.result_from_boolean(unsafe { bindings::MagickEvaluateImage(self.wand, op, val) })
1120    }
1121
1122    /// Surround the image with a border of the color defined
1123    /// by the `pixel_wand`.
1124    pub fn border_image(
1125        &self,
1126        pixel_wand: &PixelWand,
1127        width: usize,
1128        height: usize,
1129        compose: CompositeOperator,
1130    ) -> Result<()> {
1131        self.result_from_boolean(unsafe {
1132            bindings::MagickBorderImage(self.wand, pixel_wand.as_ptr(), width, height, compose)
1133        })
1134    }
1135
1136    /// Flood-fill the image starting at the pixel `(x, y)`, replacing every
1137    /// connected neighbouring pixel that matches the target with `fill`.
1138    ///
1139    /// This is the building block for "remove the background" operations such as
1140    /// the ImageMagick command line
1141    /// `-fill none -fuzz 75% -draw "alpha 0,0 floodfill"`: read the image, give
1142    /// it an alpha channel, then flood-fill from a corner with a transparent
1143    /// `fill` color.
1144    ///
1145    /// * `fill`: the color painted into the matched region (e.g. `"none"` for
1146    ///   transparency).
1147    /// * `fuzz`: how far a pixel's color may differ from the target and still be
1148    ///   considered a match, in raw quantum units (`0..=QuantumRange`). For a
1149    ///   percentage, multiply: e.g. 75% on a Q16 build is `0.75 * 65535.0`.
1150    /// * `border_color`: the target color to flood. With `invert == false`, the
1151    ///   region of connected pixels matching `border_color` (within `fuzz`),
1152    ///   starting from `(x, y)`, is painted with `fill`. With `invert == true`,
1153    ///   the connected region of pixels that do *not* match `border_color` is
1154    ///   painted instead.
1155    /// * `x`, `y`: the seed pixel where the fill begins.
1156    /// * `invert`: invert the sense of the match, as described above.
1157    pub fn floodfill_paint_image(
1158        &self,
1159        fill: &PixelWand,
1160        fuzz: f64,
1161        border_color: &PixelWand,
1162        x: isize,
1163        y: isize,
1164        invert: bool,
1165    ) -> Result<()> {
1166        self.result_from_boolean(unsafe {
1167            bindings::MagickFloodfillPaintImage(
1168                self.wand,
1169                fill.as_ptr(),
1170                fuzz,
1171                border_color.as_ptr(),
1172                x,
1173                y,
1174                if invert { MagickTrue } else { MagickFalse },
1175            )
1176        })
1177    }
1178
1179    /// Change any pixel that matches `target` (within `fuzz`) to the given
1180    /// `alpha` transparency. This is the wand-level equivalent of the command
1181    /// line `-transparent <color>` and avoids dropping down to the core
1182    /// `TransparentPaintImage` API.
1183    ///
1184    /// To make a color disappear, give the image an alpha channel (e.g.
1185    /// `set_image_alpha_channel(AlphaChannelOption::OpaqueAlphaChannel)`), then
1186    /// paint that color with `alpha == 0.0`.
1187    ///
1188    /// * `target`: the color to match.
1189    /// * `alpha`: the transparency to apply to matched pixels, where `1.0` is
1190    ///   fully opaque and `0.0` is fully transparent.
1191    /// * `fuzz`: how far a pixel's color may differ from `target` and still be
1192    ///   considered a match, in raw quantum units (`0..=QuantumRange`). For a
1193    ///   percentage, multiply: e.g. 10% on a Q16 build is `0.10 * 65535.0`.
1194    /// * `invert`: when `true`, paint the pixels that do *not* match `target`
1195    ///   instead.
1196    pub fn transparent_paint_image(
1197        &self,
1198        target: &PixelWand,
1199        alpha: f64,
1200        fuzz: f64,
1201        invert: bool,
1202    ) -> Result<()> {
1203        self.result_from_boolean(unsafe {
1204            bindings::MagickTransparentPaintImage(
1205                self.wand,
1206                target.as_ptr(),
1207                alpha,
1208                fuzz,
1209                if invert { MagickTrue } else { MagickFalse },
1210            )
1211        })
1212    }
1213
1214    /// Simulate an image shadow
1215    pub fn shadow_image(&self, alpha: f64, sigma: f64, x: isize, y: isize) -> Result<()> {
1216        self.result_from_boolean(unsafe {
1217            bindings::MagickShadowImage(self.wand, alpha, sigma, x, y)
1218        })
1219    }
1220
1221    /// Accepts pixel data and stores it in the image at the location you specify.
1222    /// See <https://imagemagick.org/api/magick-image.php#MagickImportImagePixels> for more information.
1223    pub fn import_image_pixels(
1224        &mut self,
1225        x: isize,
1226        y: isize,
1227        columns: usize,
1228        rows: usize,
1229        pixels: &[u8],
1230        map: &str,
1231    ) -> Result<()> {
1232        let pixel_map = CString::new(map).map_err(|_| "map string contains null byte")?;
1233        self.result_from_boolean(unsafe {
1234            bindings::MagickImportImagePixels(
1235                self.wand,
1236                x,
1237                y,
1238                columns,
1239                rows,
1240                pixel_map.as_ptr(),
1241                bindings::StorageType::CharPixel,
1242                pixels.as_ptr() as *const libc::c_void,
1243            )
1244        })
1245    }
1246
1247    /// Accepts `f64` pixel data and stores it in the image at the given
1248    /// location. Like [`import_image_pixels`](Self::import_image_pixels) but with
1249    /// floating-point (`DoublePixel`) storage.
1250    pub fn import_image_pixels_double(
1251        &mut self,
1252        x: isize,
1253        y: isize,
1254        columns: usize,
1255        rows: usize,
1256        pixels: &[f64],
1257        map: &str,
1258    ) -> Result<()> {
1259        let pixel_map = CString::new(map).expect("map string contains null byte");
1260        Self::result_from_boolean_with_error_message(
1261            unsafe {
1262                bindings::MagickImportImagePixels(
1263                    self.wand,
1264                    x,
1265                    y,
1266                    columns,
1267                    rows,
1268                    pixel_map.as_ptr(),
1269                    bindings::StorageType::DoublePixel,
1270                    pixels.as_ptr() as *const c_void,
1271                )
1272            },
1273            "unable to import pixels",
1274        )
1275    }
1276
1277    /// Borrow the wand's image list for read-only frame access.
1278    ///
1279    /// This is the ergonomic way to work with multi-image wands (such as the
1280    /// frames of an animated GIF): the returned [`Images`] view resets the
1281    /// iterator and hands out frame borrows that automatically position the
1282    /// iterator before each access. See [`Self::images_mut`] for mutable access.
1283    pub fn images(&self) -> Images<'_> {
1284        Images::new(self)
1285    }
1286
1287    /// Borrow the wand's image list for mutable frame access.
1288    ///
1289    /// Because ImageMagick exposes a single internal iterator, only one frame
1290    /// may be borrowed mutably at a time; this is enforced at compile time.
1291    pub fn images_mut(&mut self) -> ImagesMut<'_> {
1292        ImagesMut::new(self)
1293    }
1294
1295    /// Reset the wand iterator so that the next call to [`Self::next_image`]
1296    /// returns the first image. This is most useful before iterating an image
1297    /// list with `next_image`; it leaves the wand positioned *before* the first
1298    /// image rather than *on* it (see [`Self::set_first_iterator`] for that).
1299    /// See <https://imagemagick.org/api/magick-wand.php#MagickResetIterator> for more information.
1300    pub fn reset_iterator(&self) {
1301        unsafe {
1302            bindings::MagickResetIterator(self.wand);
1303        }
1304    }
1305
1306    /// Set the wand iterator to the first image.
1307    /// See <https://imagemagick.org/api/magick-wand.php#MagickSetFirstIterator> for more information.
1308    pub fn set_first_iterator(&self) {
1309        unsafe {
1310            bindings::MagickSetFirstIterator(self.wand);
1311        }
1312    }
1313
1314    /// Set the wand iterator to the last image.
1315    /// See <https://imagemagick.org/api/magick-wand.php#MagickSetLastIterator> for more information.
1316    pub fn set_last_iterator(&self) {
1317        unsafe {
1318            bindings::MagickSetLastIterator(self.wand);
1319        }
1320    }
1321
1322    /// Set the next image in the wand as the current image.
1323    ///
1324    /// Returns `true` while the iterator advanced onto a valid image, and
1325    /// `false` once it has moved past the last image.
1326    /// See <https://imagemagick.org/api/magick-image.php#MagickNextImage> for more information.
1327    pub fn next_image(&self) -> bool {
1328        let res = unsafe { bindings::MagickNextImage(self.wand) };
1329        res == MagickTrue
1330    }
1331
1332    /// Set the previous image in the wand as the current image.
1333    ///
1334    /// Returns `true` while the iterator stepped back onto a valid image, and
1335    /// `false` once it has moved before the first image.
1336    /// See <https://imagemagick.org/api/magick-image.php#MagickPreviousImage> for more information.
1337    pub fn previous_image(&self) -> bool {
1338        let res = unsafe { bindings::MagickPreviousImage(self.wand) };
1339        res == MagickTrue
1340    }
1341
1342    /// Returns `true` if the wand has more images when traversing the list in
1343    /// the forward direction.
1344    /// See <https://imagemagick.org/api/magick-image.php#MagickHasNextImage> for more information.
1345    pub fn has_next_image(&self) -> bool {
1346        let res = unsafe { bindings::MagickHasNextImage(self.wand) };
1347        res == MagickTrue
1348    }
1349
1350    /// Returns `true` if the wand has more images when traversing the list in
1351    /// the reverse direction.
1352    /// See <https://imagemagick.org/api/magick-image.php#MagickHasPreviousImage> for more information.
1353    pub fn has_previous_image(&self) -> bool {
1354        let res = unsafe { bindings::MagickHasPreviousImage(self.wand) };
1355        res == MagickTrue
1356    }
1357
1358    /// Remove the current image from the image list.
1359    /// See <https://imagemagick.org/api/magick-image.php#MagickRemoveImage> for more information.
1360    pub fn remove_image(&mut self) -> Result<()> {
1361        self.result_from_boolean(unsafe { bindings::MagickRemoveImage(self.wand) })
1362    }
1363
1364    /// Automatically performs threshold method to reduce grayscale data
1365    /// down to a binary black & white image. Included algorithms are
1366    /// Kapur, Otsu, and Triangle methods.
1367    /// See <https://imagemagick.org/api/magick-image.php#MagickAutoThresholdImage> for more information.
1368    pub fn auto_threshold(&self, method: AutoThresholdMethod) -> Result<()> {
1369        self.result_from_boolean(unsafe { bindings::MagickAutoThresholdImage(self.wand, method) })
1370    }
1371
1372    /// Set the image colorspace, transforming (unlike `set_image_colorspace`) image data in
1373    /// the process.
1374    pub fn transform_image_colorspace(&self, colorspace: ColorspaceType) -> Result<()> {
1375        self.result_from_boolean(unsafe {
1376            bindings::MagickTransformImageColorspace(self.wand, colorspace)
1377        })
1378    }
1379
1380    /// Reduce the number of colors in the image.
1381    pub fn quantize_image(
1382        &self,
1383        number_of_colors: usize,
1384        colorspace: ColorspaceType,
1385        tree_depth: usize,
1386        dither_method: DitherMethod,
1387        measure_error: bool,
1388    ) -> Result<()> {
1389        self.result_from_boolean(unsafe {
1390            bindings::MagickQuantizeImage(
1391                self.wand,
1392                number_of_colors,
1393                colorspace,
1394                tree_depth,
1395                dither_method,
1396                measure_error.into(),
1397            )
1398        })
1399    }
1400
1401    /// Reduce the number of colors in the images.
1402    pub fn quantize_images(
1403        &self,
1404        number_of_colors: usize,
1405        colorspace: ColorspaceType,
1406        tree_depth: usize,
1407        dither_method: DitherMethod,
1408        measure_error: bool,
1409    ) -> Result<()> {
1410        self.result_from_boolean(unsafe {
1411            bindings::MagickQuantizeImages(
1412                self.wand,
1413                number_of_colors,
1414                colorspace,
1415                tree_depth,
1416                dither_method,
1417                measure_error.into(),
1418            )
1419        })
1420    }
1421
1422    /// Applies an arithmetic, relational, or logical expression to an image. Use these operators
1423    /// to lighten or darken an image, to increase or decrease contrast in an image, or to produce
1424    /// the "negative" of an image.
1425    ///
1426    /// * `function`: the image function.
1427    /// * `args`: the function arguments.
1428    ///
1429    /// # Example
1430    ///
1431    /// This example show how you can apply smoothstep function (a polynomial `-2x^3 + 3x^2`) to
1432    /// every image pixel.
1433    ///
1434    /// ```
1435    /// use magick_rust::{MagickWand, PixelWand, MagickFunction};
1436    ///
1437    /// fn main() -> Result<(), magick_rust::MagickError> {
1438    ///     let mut wand1 = MagickWand::new();
1439    ///     wand1.new_image(4, 4, &PixelWand::new())?; // Replace with `read_image` to open your image file
1440    ///
1441    ///     // Apply smoothstep polynomial
1442    ///     wand1.function_image(MagickFunction::Polynomial, &[-2.0, 3.0, 0.0, 0.0])?;
1443    ///
1444    ///     Ok(())
1445    /// }
1446    /// ```
1447    pub fn function_image(&self, function: MagickFunction, args: &[f64]) -> Result<()> {
1448        let num_of_args: size_t = args.len();
1449        self.result_from_boolean(unsafe {
1450            bindings::MagickFunctionImage(self.wand, function, num_of_args, args.as_ptr())
1451        })
1452    }
1453
1454    /// Returns an image where each pixel is the sum of the pixels in the image sequence after
1455    /// applying its corresponding terms (coefficient and degree pairs).
1456    ///
1457    /// * `terms`: the list of polynomial coefficients and degree pairs and a constant.
1458    pub fn polynomial_image(&self, terms: &[f64]) -> Result<()> {
1459        if terms.len() & 1 != 1 {
1460            return Err(MagickError("no constant coefficient given".to_string()));
1461        }
1462
1463        let num_of_terms: size_t = terms.len() >> 1;
1464
1465        self.result_from_boolean(unsafe {
1466            bindings::MagickPolynomialImage(self.wand, num_of_terms, terms.as_ptr())
1467        })
1468    }
1469
1470    /// Applies a custom convolution kernel to the image.
1471    ///
1472    /// * `kernel_info`: An array of doubles representing the convolution kernel.
1473    pub fn convolve_image(&self, kernel_info: &KernelInfo) -> Result<()> {
1474        self.result_from_boolean(unsafe {
1475            bindings::MagickConvolveImage(self.wand, kernel_info.get_ptr())
1476        })
1477    }
1478
1479    /// Applies a user supplied kernel to the image according to the given morphology method.
1480    ///
1481    /// * `morphology_method`: the morphology method to be applied.
1482    /// * `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.
1483    /// * `kernel_info`: An array of doubles representing the morphology kernel.
1484    pub fn morphology_image(
1485        &self,
1486        morphology_method: MorphologyMethod,
1487        iterations: isize,
1488        kernel_info: &KernelInfo,
1489    ) -> Result<()> {
1490        self.result_from_boolean(unsafe {
1491            bindings::MagickMorphologyImage(
1492                self.wand,
1493                morphology_method,
1494                iterations,
1495                kernel_info.get_ptr(),
1496            )
1497        })
1498    }
1499
1500    /// Apply color transformation to an image. The method permits saturation changes, hue rotation,
1501    /// luminance to alpha, and various other effects. Although variable-sized transformation
1502    /// matrices can be used, typically one uses a 5x5 matrix for an RGBA image and a 6x6 for CMYKA
1503    /// (or RGBA with offsets). The matrix is similar to those used by Adobe Flash except offsets
1504    /// are in column 6 rather than 5 (in support of CMYKA images) and offsets are normalized
1505    /// (divide Flash offset by 255).
1506    ///
1507    /// * `color_matrix`: the color matrix.
1508    pub fn color_matrix_image(&self, color_matrix: &KernelInfo) -> Result<()> {
1509        self.result_from_boolean(unsafe {
1510            bindings::MagickColorMatrixImage(self.wand, color_matrix.get_ptr())
1511        })
1512    }
1513
1514    /// Applies a channel expression to the specified image. The expression
1515    /// consists of one or more channels, either mnemonic or numeric (e.g. red, 1), separated by
1516    /// actions as follows:
1517    ///
1518    /// <=> exchange two channels (e.g. red<=>blue) => transfer a channel to another (e.g.
1519    /// red=>green) , separate channel operations (e.g. red, green) | read channels from next input
1520    /// image (e.g. red | green) ; write channels to next output image (e.g. red; green; blue) A
1521    /// channel without a operation symbol implies extract. For example, to create 3 grayscale
1522    /// images from the red, green, and blue channels of an image, use:
1523    ///
1524    /// * `expression`: the expression.
1525    pub fn channel_fx_image(&self, expression: &str) -> Result<MagickWand> {
1526        let c_expression =
1527            CString::new(expression).map_err(|_| "artifact string contains null byte")?;
1528
1529        let wand_ptr = unsafe { bindings::MagickChannelFxImage(self.wand, c_expression.as_ptr()) };
1530        self.result_from_ptr(wand_ptr, MagickWand::from_ptr)
1531    }
1532
1533    /// Combines one or more images into a single image. The grayscale value of the pixels of each
1534    /// image in the sequence is assigned in order to the specified channels of the combined image.
1535    /// The typical ordering would be image 1 => Red, 2 => Green, 3 => Blue, etc.
1536    ///
1537    /// * `colorspace`: the colorspace.
1538    pub fn combine_images(&self, colorspace: ColorspaceType) -> Result<MagickWand> {
1539        let wand_ptr = unsafe { bindings::MagickCombineImages(self.wand, colorspace) };
1540        self.result_from_ptr(wand_ptr, MagickWand::from_ptr)
1541    }
1542
1543    /// Returns the current image from the magick wand.
1544    pub fn get_image(&self) -> Result<Image<'_>> {
1545        self.result_from_ptr(
1546            unsafe { bindings::GetImageFromMagickWand(self.wand) },
1547            Image::new,
1548        )
1549    }
1550
1551    /// Enhances contrast of an image by stretching the range of intensity values.
1552    pub fn contrast_stretch_image(&self, black_point: f64, white_point: f64) -> Result<()> {
1553        self.result_from_boolean(unsafe {
1554            bindings::MagickContrastStretchImage(self.wand, black_point, white_point)
1555        })
1556    }
1557
1558    mutations!(
1559        /// Sets the image to the specified alpha level.
1560        MagickSetImageAlpha => set_image_alpha(alpha: f64)
1561
1562        /// Control the brightness, saturation, and hue of an image
1563        MagickModulateImage => modulate_image(brightness: f64, saturation: f64, hue: f64)
1564
1565        /// Control the brightness and contrast
1566        MagickBrightnessContrastImage => brightness_contrast_image(brightness: f64, contrast: f64)
1567
1568        /// Set the image alpha channel mode.
1569        MagickSetImageAlphaChannel => set_image_alpha_channel(alpha_channel: AlphaChannelOption)
1570
1571        /// Discard all but one of any pixel color.
1572        MagickUniqueImageColors => unique_image_colors()
1573
1574        /// Applies k-means color reduction to the image.
1575        MagickKmeansImage => kmeans(number_colors: usize, max_iterations: usize, tolerance: f64)
1576
1577        /// Extracts the 'mean' from the image and adjust the image to try make set its gamma appropriately.
1578        MagickAutoGammaImage => auto_gamma()
1579
1580        /// Adjusts the levels of a particular image channel by scaling the minimum and maximum values to the full quantum range.
1581        MagickAutoLevelImage => auto_level()
1582    );
1583
1584    get!(get_image_colors, MagickGetImageColors, usize);
1585
1586    string_set_get!(
1587        get_filename,                    set_filename,                    MagickGetFilename,                 MagickSetFilename
1588        get_font,                        set_font,                        MagickGetFont,                     MagickSetFont
1589        get_format,                      set_format,                      MagickGetFormat,                   MagickSetFormat
1590        get_image_filename,              set_image_filename,              MagickGetImageFilename,            MagickSetImageFilename
1591        get_image_format,                set_image_format,                MagickGetImageFormat,              MagickSetImageFormat
1592    );
1593
1594    set_get!(
1595        get_colorspace,                  set_colorspace,                  MagickGetColorspace,               MagickSetColorspace,              ColorspaceType
1596        get_image_compose,               set_image_compose,               MagickGetImageCompose,             MagickSetImageCompose,            CompositeOperator
1597        get_compression,                 set_compression,                 MagickGetCompression,              MagickSetCompression,             CompressionType
1598        get_compression_quality,         set_compression_quality,         MagickGetCompressionQuality,       MagickSetCompressionQuality,      usize
1599        get_gravity,                     set_gravity,                     MagickGetGravity,                  MagickSetGravity,                 GravityType
1600        get_image_colorspace,            set_image_colorspace,            MagickGetImageColorspace,          MagickSetImageColorspace,         ColorspaceType
1601        get_image_compression,           set_image_compression,           MagickGetImageCompression,         MagickSetImageCompression,        CompressionType
1602        get_image_compression_quality,   set_image_compression_quality,   MagickGetImageCompressionQuality,  MagickSetImageCompressionQuality, usize
1603        get_image_delay,                 set_image_delay,                 MagickGetImageDelay,               MagickSetImageDelay,              usize
1604        get_image_depth,                 set_image_depth,                 MagickGetImageDepth,               MagickSetImageDepth,              usize
1605        get_image_dispose,               set_image_dispose,               MagickGetImageDispose,             MagickSetImageDispose,            DisposeType
1606        get_image_endian,                set_image_endian,                MagickGetImageEndian,              MagickSetImageEndian,             EndianType
1607        get_image_fuzz,                  set_image_fuzz,                  MagickGetImageFuzz,                MagickSetImageFuzz,               f64
1608        get_image_gamma,                 set_image_gamma,                 MagickGetImageGamma,               MagickSetImageGamma,              f64
1609        get_image_gravity,               set_image_gravity,               MagickGetImageGravity,             MagickSetImageGravity,            GravityType
1610        get_image_interlace_scheme,      set_image_interlace_scheme,      MagickGetImageInterlaceScheme,     MagickSetImageInterlaceScheme,    InterlaceType
1611        get_image_interpolate_method,    set_image_interpolate_method,    MagickGetImageInterpolateMethod,   MagickSetImageInterpolateMethod,  PixelInterpolateMethod
1612        get_image_iterations,            set_image_iterations,            MagickGetImageIterations,          MagickSetImageIterations,         usize
1613        get_image_orientation,           set_image_orientation,           MagickGetImageOrientation,         MagickSetImageOrientation,        OrientationType
1614        get_image_rendering_intent,      set_image_rendering_intent,      MagickGetImageRenderingIntent,     MagickSetImageRenderingIntent,    RenderingIntent
1615        get_image_scene,                 set_image_scene,                 MagickGetImageScene,               MagickSetImageScene,              usize
1616        get_image_type,                  set_image_type,                  MagickGetImageType,                MagickSetImageType,               ImageType
1617        get_image_units,                 set_image_units,                 MagickGetImageUnits,               MagickSetImageUnits,              ResolutionType
1618        get_interlace_scheme,            set_interlace_scheme,            MagickGetInterlaceScheme,          MagickSetInterlaceScheme,         InterlaceType
1619        get_interpolate_method,          set_interpolate_method,          MagickGetInterpolateMethod,        MagickSetInterpolateMethod,       PixelInterpolateMethod
1620        get_iterator_index,              set_iterator_index,              MagickGetIteratorIndex,            MagickSetIteratorIndex,           isize
1621        get_orientation,                 set_orientation,                 MagickGetOrientation,              MagickSetOrientation,             OrientationType
1622        get_pointsize,                   set_pointsize,                   MagickGetPointsize,                MagickSetPointsize,               f64
1623        get_type,                        set_type,                        MagickGetType,                     MagickSetType,                    ImageType
1624    );
1625
1626    fn result_from_boolean(&self, no_error: MagickBooleanType) -> Result<()> {
1627        if no_error == MagickTrue {
1628            Ok(())
1629        } else {
1630            Err(MagickError(self.get_exception()?.0))
1631        }
1632    }
1633
1634    fn result_from_boolean_with_error_message(
1635        no_error: MagickBooleanType,
1636        message: impl Into<String>,
1637    ) -> Result<()> {
1638        if no_error == MagickTrue {
1639            Ok(())
1640        } else {
1641            Err(MagickError(message.into()))
1642        }
1643    }
1644
1645    fn result_from_ptr<P, T>(&self, ptr: *mut P, new: impl FnOnce(*mut P) -> T) -> Result<T> {
1646        if ptr.is_null() {
1647            Err(MagickError(self.get_exception()?.0))
1648        } else {
1649            Ok(new(ptr))
1650        }
1651    }
1652
1653    fn result_from_ptr_with_error_message<P, T>(
1654        ptr: *mut P,
1655        new: impl FnOnce(*mut P) -> T,
1656        message: impl Into<String>,
1657    ) -> Result<T> {
1658        if ptr.is_null() {
1659            Err(MagickError(message.into()))
1660        } else {
1661            Ok(new(ptr))
1662        }
1663    }
1664
1665    fn c_char_to_string_vec(c_values: *mut *mut c_char, num_of_artifacts: usize) -> Vec<String> {
1666        let mut values: Vec<String> = Vec::with_capacity(num_of_artifacts);
1667        for i in 0..num_of_artifacts {
1668            // convert (and copy) the C string to a Rust string
1669            let cstr = unsafe { CStr::from_ptr(*c_values.add(i)) };
1670            values.push(cstr.to_string_lossy().into_owned());
1671        }
1672
1673        unsafe {
1674            bindings::MagickRelinquishMemory(c_values as *mut c_void);
1675        }
1676
1677        values
1678    }
1679
1680    fn c_char_into_string(c_value: *mut c_char) -> String {
1681        let value = unsafe { CStr::from_ptr(c_value) }
1682            .to_string_lossy()
1683            .into_owned();
1684
1685        unsafe {
1686            bindings::MagickRelinquishMemory(c_value as *mut c_void);
1687        }
1688
1689        value
1690    }
1691
1692    fn c_array_into_vec(blob: *mut c_uchar, length: usize) -> Vec<u8> {
1693        let mut bytes = vec![0; length];
1694
1695        unsafe {
1696            let ptr = bytes.as_mut_ptr();
1697            ptr::copy_nonoverlapping(blob, ptr, length);
1698            bindings::MagickRelinquishMemory(blob as *mut c_void);
1699        }
1700
1701        bytes
1702    }
1703}
1704
1705impl fmt::Debug for MagickWand {
1706    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1707        writeln!(f, "MagickWand {{")?;
1708        writeln!(f, "    Exception: {:?}", self.get_exception())?;
1709        writeln!(f, "    IsWand: {:?}", self.is_wand())?;
1710        self.fmt_string_settings(f, "    ")?;
1711        self.fmt_checked_settings(f, "    ")?;
1712        writeln!(f, "}}")
1713    }
1714}