1use 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, InterlaceType, KernelInfo, LayerMethod, MagickEvaluateOperator, MagickFunction,
39 MetricType, MorphologyMethod, OrientationType, PixelInterpolateMethod, PixelMask,
40 RenderingIntent, ResolutionType, StatisticType,
41};
42
43wand_common!(
44 MagickWand,
45 NewMagickWand,
46 ClearMagickWand,
47 IsMagickWand,
48 CloneMagickWand,
49 DestroyMagickWand,
50 MagickClearException,
51 MagickGetExceptionType,
52 MagickGetException
53);
54
55impl MagickWand {
62 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 pub fn new_image(&self, columns: usize, rows: usize, background: &PixelWand) -> Result<()> {
75 self.result_from_boolean(unsafe {
76 bindings::MagickNewImage(self.wand, columns, rows, background.as_ptr())
77 })
78 }
79
80 #[cfg(any(target_os = "linux", target_os = "macos"))]
82 pub fn set_resource_limit(resource: ResourceType, limit: u64) -> Result<()> {
83 Self::result_from_boolean_with_error_message(
84 unsafe {
85 bindings::MagickSetResourceLimit(resource, limit as bindings::MagickSizeType)
86 },
87 "failed to set resource limit",
88 )
89 }
90
91 pub fn set_option(&mut self, key: &str, value: &str) -> Result<()> {
92 let c_key = CString::new(key).map_err(|_| "key string contains null byte")?;
93 let c_value = CString::new(value).map_err(|_| "value string contains null byte")?;
94 self.result_from_boolean(unsafe {
95 bindings::MagickSetOption(self.wand, c_key.as_ptr(), c_value.as_ptr())
96 })
97 }
98
99 pub fn annotate_image(
100 &mut self,
101 drawing_wand: &DrawingWand,
102 x: f64,
103 y: f64,
104 angle: f64,
105 text: &str,
106 ) -> Result<()> {
107 let c_string = CString::new(text).map_err(|_| "could not convert to cstring")?;
108 self.result_from_boolean(unsafe {
109 bindings::MagickAnnotateImage(
110 self.wand,
111 drawing_wand.as_ptr(),
112 x,
113 y,
114 angle,
115 c_string.as_ptr() as *const _,
116 )
117 })
118 }
119
120 pub fn add_image(&mut self, other_wand: &MagickWand) -> Result<()> {
122 self.result_from_boolean(unsafe { bindings::MagickAddImage(self.wand, other_wand.wand) })
123 }
124
125 pub fn append_all(&mut self, stack: bool) -> Result<MagickWand> {
126 unsafe { bindings::MagickResetIterator(self.wand) };
127 let wand_ptr = unsafe { bindings::MagickAppendImages(self.wand, stack.into()) };
128 Self::result_from_ptr_with_error_message(
129 wand_ptr,
130 MagickWand::from_ptr,
131 "failed to append image",
132 )
133 }
134
135 pub fn label_image(&self, label: &str) -> Result<()> {
136 let c_label = CString::new(label).map_err(|_| "label string contains null byte")?;
137 self.result_from_boolean(unsafe { bindings::MagickLabelImage(self.wand, c_label.as_ptr()) })
138 }
139
140 pub fn write_images(&self, path: &str, adjoin: bool) -> Result<()> {
141 let c_name = CString::new(path).map_err(|_| "path string contains null byte")?;
142 self.result_from_boolean(unsafe {
143 bindings::MagickWriteImages(self.wand, c_name.as_ptr(), adjoin.into())
144 })
145 }
146
147 pub fn read_image(&self, path: &str) -> Result<()> {
149 let c_name = CString::new(path).map_err(|_| "path string contains null byte")?;
150 self.result_from_boolean(unsafe { bindings::MagickReadImage(self.wand, c_name.as_ptr()) })
151 }
152
153 pub fn read_image_blob<T: AsRef<[u8]>>(&self, data: T) -> Result<()> {
155 let int_slice = data.as_ref();
156 let size = int_slice.len();
157 self.result_from_boolean(unsafe {
158 bindings::MagickReadImageBlob(self.wand, int_slice.as_ptr() as *const c_void, size)
159 })
160 }
161
162 pub fn ping_image(&self, path: &str) -> Result<()> {
165 let c_name = CString::new(path).map_err(|_| "path string contains null byte")?;
166 self.result_from_boolean(unsafe { bindings::MagickPingImage(self.wand, c_name.as_ptr()) })
167 }
168
169 pub fn ping_image_blob<T: AsRef<[u8]>>(&self, data: T) -> Result<()> {
172 let int_slice = data.as_ref();
173 let size = int_slice.len();
174 self.result_from_boolean(unsafe {
175 bindings::MagickPingImageBlob(self.wand, int_slice.as_ptr() as *const c_void, size)
176 })
177 }
178
179 pub fn merge_image_layers(&self, method: LayerMethod) -> Result<MagickWand> {
196 let wand_ptr = unsafe { bindings::MagickMergeImageLayers(self.wand, method) };
197 Self::result_from_ptr_with_error_message(
198 wand_ptr,
199 MagickWand::from_ptr,
200 "failed to merge image layers",
201 )
202 }
203
204 pub fn get_number_images(&self) -> usize {
206 unsafe { bindings::MagickGetNumberImages(self.wand) }
207 }
208
209 pub fn compare_images(
212 &self,
213 reference: &MagickWand,
214 metric: MetricType,
215 ) -> (f64, Option<MagickWand>) {
216 let mut distortion: f64 = 0.0;
217 let wand_ptr = unsafe {
218 bindings::MagickCompareImages(self.wand, reference.wand, metric, &mut distortion)
219 };
220
221 let wand = Self::result_from_ptr_with_error_message(wand_ptr, MagickWand::from_ptr, "").ok();
222 (distortion, wand)
223 }
224
225 pub fn compose_images(
227 &self,
228 reference: &MagickWand,
229 composition_operator: CompositeOperator,
230 clip_to_self: bool,
231 x: isize,
232 y: isize,
233 ) -> Result<()> {
234 self.result_from_boolean(unsafe {
235 bindings::MagickCompositeImage(
236 self.wand,
237 reference.wand,
238 composition_operator,
239 MagickBooleanType::from(clip_to_self),
240 x,
241 y,
242 )
243 })
244 }
245
246 pub fn compose_images_gravity(
248 &self,
249 reference: &MagickWand,
250 composition_operator: CompositeOperator,
251 gravity_type: GravityType,
252 ) -> Result<()> {
253 self.result_from_boolean(unsafe {
254 bindings::MagickCompositeImageGravity(
255 self.wand,
256 reference.wand,
257 composition_operator,
258 gravity_type,
259 )
260 })
261 }
262
263 pub fn coalesce(&mut self) -> Result<MagickWand> {
266 let wand_ptr = unsafe { bindings::MagickCoalesceImages(self.wand) };
267 Self::result_from_ptr_with_error_message(
268 wand_ptr,
269 MagickWand::from_ptr,
270 "failed to coalesce images",
271 )
272 }
273
274 pub fn clut_image(&self, clut_wand: &MagickWand, method: PixelInterpolateMethod) -> Result<()> {
276 self.result_from_boolean(unsafe {
277 bindings::MagickClutImage(self.wand, clut_wand.wand, method)
278 })
279 }
280
281 pub fn hald_clut_image(&self, clut_wand: &MagickWand) -> Result<()> {
282 self.result_from_boolean(unsafe {
283 bindings::MagickHaldClutImage(self.wand, clut_wand.wand)
284 })
285 }
286
287 pub fn fx(&mut self, expression: &str) -> Result<MagickWand> {
288 let c_expression =
289 CString::new(expression).map_err(|_| "expression string contains null byte")?;
290 let wand_ptr = unsafe { bindings::MagickFxImage(self.wand, c_expression.as_ptr()) };
291 Self::result_from_ptr_with_error_message(
292 wand_ptr,
293 MagickWand::from_ptr,
294 "failed to fx the image",
295 )
296 }
297
298 pub fn set_size(&self, columns: usize, rows: usize) -> Result<()> {
299 self.result_from_boolean(unsafe { bindings::MagickSetSize(self.wand, columns, rows) })
300 }
301
302 #[cfg(not(feature = "disable-hdri"))]
305 fn quantum_range(&self) -> Result<f64> {
306 Ok(bindings::QuantumRange)
307 }
308
309 #[cfg(feature = "disable-hdri")]
312 fn quantum_range(&self) -> Result<f64> {
313 match bindings::MAGICKCORE_QUANTUM_DEPTH {
314 8 => Ok(255.0f64),
315 16 => Ok(65535.0f64),
316 32 => Ok(4294967295.0f64),
317 64 => Ok(18446744073709551615.0f64),
318 _ => Err(MagickError(
319 "Quantum depth must be one of 8, 16, 32 or 64".to_string(),
320 )),
321 }
322 }
323
324 pub fn level_image(&self, black_point: f64, gamma: f64, white_point: f64) -> Result<()> {
327 let quantum_range = self.quantum_range()?;
328
329 self.result_from_boolean(unsafe {
330 bindings::MagickLevelImage(
331 self.wand,
332 black_point * quantum_range,
333 gamma,
334 white_point * quantum_range,
335 )
336 })
337 }
338
339 pub fn levelize_image(&self, black_point: f64, gamma: f64, white_point: f64) -> Result<()> {
343 let quantum_range = self.quantum_range()?;
344
345 self.result_from_boolean(unsafe {
346 bindings::MagickLevelizeImage(
347 self.wand,
348 black_point * quantum_range,
349 gamma,
350 white_point * quantum_range,
351 )
352 })
353 }
354
355 pub fn normalize_image(&self) -> Result<()> {
358 self.result_from_boolean(unsafe { bindings::MagickNormalizeImage(self.wand) })
359 }
360
361 pub fn ordered_dither_image(&self, threshold_map: &str) -> Result<()> {
365 let c_threshold_map =
366 CString::new(threshold_map).map_err(|_| "threshold_map string contains null byte")?;
367
368 self.result_from_boolean(unsafe {
369 bindings::MagickOrderedDitherImage(self.wand, c_threshold_map.as_ptr())
370 })
371 }
372
373 pub fn sigmoidal_contrast_image(
386 &self,
387 sharpen: bool,
388 strength: f64,
389 midpoint: f64,
390 ) -> Result<()> {
391 let quantum_range = self.quantum_range()?;
392
393 self.result_from_boolean(unsafe {
394 bindings::MagickSigmoidalContrastImage(
395 self.wand,
396 sharpen.into(),
397 strength,
398 midpoint * quantum_range,
399 )
400 })
401 }
402
403 pub fn extend_image(&self, width: usize, height: usize, x: isize, y: isize) -> Result<()> {
406 self.result_from_boolean(unsafe {
407 bindings::MagickExtentImage(self.wand, width, height, x, y)
408 })
409 }
410
411 pub fn profile_image<'a, T: Into<Option<&'a [u8]>>>(
412 &self,
413 name: &str,
414 profile: T,
415 ) -> Result<()> {
416 let c_name = CString::new(name).map_err(|_| "name string contains null byte")?;
417 let result = unsafe {
418 let profile = profile.into();
419 let profile_ptr = match profile {
420 Some(data) => data.as_ptr(),
421 None => ptr::null(),
422 } as *const c_void;
423 let profile_len = match profile {
424 Some(data) => data.len(),
425 None => 0,
426 };
427 bindings::MagickProfileImage(self.wand, c_name.as_ptr(), profile_ptr, profile_len)
428 };
429 self.result_from_boolean(result)
430 }
431
432 pub fn strip_image(&self) -> Result<()> {
433 self.result_from_boolean(unsafe { bindings::MagickStripImage(self.wand) })
434 }
435
436 pub fn flip_image(&self) -> Result<()> {
437 self.result_from_boolean(unsafe { bindings::MagickFlipImage(self.wand) })
438 }
439
440 pub fn negate_image(&self) -> Result<()> {
441 self.result_from_boolean(unsafe { bindings::MagickNegateImage(self.wand, MagickTrue) })
442 }
443
444 pub fn flop_image(&self) -> Result<()> {
445 self.result_from_boolean(unsafe { bindings::MagickFlopImage(self.wand) })
446 }
447
448 pub fn blur_image(&self, radius: f64, sigma: f64) -> Result<()> {
449 self.result_from_boolean(unsafe { bindings::MagickBlurImage(self.wand, radius, sigma) })
450 }
451
452 pub fn gaussian_blur_image(&self, radius: f64, sigma: f64) -> Result<()> {
453 self.result_from_boolean(unsafe {
454 bindings::MagickGaussianBlurImage(self.wand, radius, sigma)
455 })
456 }
457
458 pub fn statistic_image(
464 &self,
465 statistic_type: StatisticType,
466 width: usize,
467 height: usize,
468 ) -> Result<()> {
469 self.result_from_boolean(unsafe {
470 bindings::MagickStatisticImage(self.wand, statistic_type, width, height)
471 })
472 }
473
474 pub fn median_blur_image(&self, width: usize, height: usize) -> Result<()> {
478 self.statistic_image(StatisticType::Median, width, height)
479 }
480
481 pub fn adaptive_resize_image(&self, width: usize, height: usize) -> Result<()> {
483 self.result_from_boolean(unsafe {
484 bindings::MagickAdaptiveResizeImage(self.wand, width, height)
485 })
486 }
487
488 pub fn rotate_image(&self, background: &PixelWand, degrees: f64) -> Result<()> {
491 self.result_from_boolean(unsafe {
492 bindings::MagickRotateImage(self.wand, background.as_ptr(), degrees)
493 })
494 }
495
496 pub fn trim_image(&self, fuzz: f64) -> Result<()> {
498 self.result_from_boolean(unsafe { bindings::MagickTrimImage(self.wand, fuzz) })
499 }
500
501 pub fn get_image_width(&self) -> usize {
503 unsafe { bindings::MagickGetImageWidth(self.wand) }
504 }
505
506 pub fn get_image_height(&self) -> usize {
508 unsafe { bindings::MagickGetImageHeight(self.wand) }
509 }
510
511 pub fn get_image_page(&self) -> (usize, usize, isize, isize) {
513 let (mut width, mut height, mut x, mut y) = (0usize, 0usize, 0isize, 0isize);
514 unsafe {
515 bindings::MagickGetImagePage(self.wand, &mut width, &mut height, &mut x, &mut y);
518 }
519 (width, height, x, y)
520 }
521
522 pub fn reset_image_page(&self, page_geometry: &str) -> Result<()> {
524 let c_page_geometry =
525 CString::new(page_geometry).map_err(|_| "page_geometry contains null byte")?;
526 self.result_from_boolean(unsafe {
527 bindings::MagickResetImagePage(self.wand, c_page_geometry.as_ptr())
528 })
529 }
530
531 pub fn get_image_artifact(&self, artifact: &str) -> Result<String> {
535 let c_artifact =
536 CString::new(artifact).map_err(|_| "artifact string contains null byte")?;
537
538 let c_value = unsafe { bindings::MagickGetImageArtifact(self.wand, c_artifact.as_ptr()) };
539 Self::result_from_ptr_with_error_message(
540 c_value,
541 Self::c_char_into_string,
542 format!("missing artifact: {artifact}"),
543 )
544 }
545
546 pub fn get_image_artifacts(&self, pattern: &str) -> Result<Vec<String>> {
547 let c_pattern = CString::new(pattern)
548 .map_err(|_| MagickError("artifact string contains null byte".to_string()))?;
549 let mut num_of_artifacts: size_t = 0;
550
551 let c_values = unsafe {
552 bindings::MagickGetImageArtifacts(self.wand, c_pattern.as_ptr(), &mut num_of_artifacts)
553 };
554
555 Self::result_from_ptr_with_error_message(
556 c_values,
557 |c_values| Self::c_char_to_string_vec(c_values, num_of_artifacts),
558 "image has no artifacts",
559 )
560 }
561
562 pub fn set_image_artifact(&mut self, artifact: &str, value: &str) -> Result<()> {
593 let c_artifact =
594 CString::new(artifact).map_err(|_| "artifact string contains null byte")?;
595 let c_value = CString::new(value).map_err(|_| "value string contains null byte")?;
596
597 self.result_from_boolean(unsafe {
598 bindings::MagickSetImageArtifact(self.wand, c_artifact.as_ptr(), c_value.as_ptr())
599 })
600 }
601
602 pub fn delete_image_artifact(&mut self, artifact: &str) -> Result<()> {
606 let c_artifact =
607 CString::new(artifact).map_err(|_| "artifact string contains null byte")?;
608
609 Self::result_from_boolean_with_error_message(
610 unsafe { bindings::MagickDeleteImageArtifact(self.wand, c_artifact.as_ptr()) },
611 format!("missing artifact: {artifact}"),
612 )
613 }
614
615 pub fn get_image_property(&self, name: &str) -> Result<String> {
617 let c_name = CString::new(name).map_err(|_| "name string contains null byte")?;
618 let c_value = unsafe { bindings::MagickGetImageProperty(self.wand, c_name.as_ptr()) };
619
620 Self::result_from_ptr_with_error_message(
621 c_value,
622 Self::c_char_into_string,
623 format!("missing property: {name}"),
624 )
625 }
626
627 pub fn get_image_properties(&self, pattern: &str) -> Result<Vec<String>> {
628 let c_pattern = CString::new(pattern)
629 .map_err(|_| MagickError("artifact string contains null byte".to_string()))?;
630 let mut num_of_artifacts: size_t = 0;
631
632 let c_values = unsafe {
633 bindings::MagickGetImageProperties(self.wand, c_pattern.as_ptr(), &mut num_of_artifacts)
634 };
635
636 self.result_from_ptr(c_values, |c_values| Self::c_char_to_string_vec(c_values, num_of_artifacts))
637 }
638
639 pub fn set_image_property(&self, name: &str, value: &str) -> Result<()> {
641 let c_name = CString::new(name).map_err(|_| "name string contains null byte")?;
642 let c_value = CString::new(value).map_err(|_| "value string contains null byte")?;
643 self.result_from_boolean(unsafe {
644 bindings::MagickSetImageProperty(self.wand, c_name.as_ptr(), c_value.as_ptr())
645 })
646 }
647
648 pub fn get_image_pixel_color(&self, x: isize, y: isize) -> Option<PixelWand> {
650 let pw = PixelWand::new();
651
652 let result = unsafe { bindings::MagickGetImagePixelColor(self.wand, x, y, pw.as_ptr()) };
653 self.result_from_boolean(result).map(|_| pw).ok()
654 }
655
656 pub fn set_sampling_factors(&self, samplingFactors: &[f64]) -> Result<()> {
660 self.result_from_boolean( unsafe {
661 bindings::MagickSetSamplingFactors(
662 self.wand,
663 samplingFactors.len(),
664 &samplingFactors[0],
665 )
666 })
667 }
668
669 pub fn get_image_histogram(&self) -> Option<Vec<PixelWand>> {
671 let mut color_count: size_t = 0;
672
673 unsafe {
674 bindings::MagickGetImageHistogram(self.wand, &mut color_count)
675 .as_mut()
676 .map(|ptrs| {
677 slice::from_raw_parts(ptrs, color_count)
678 .iter()
679 .map(|wand_ptr| PixelWand::from_ptr(*wand_ptr))
680 .collect()
681 })
682 }
683 }
684
685 pub fn sharpen_image(&self, radius: f64, sigma: f64) -> Result<()> {
695 self.result_from_boolean(unsafe { bindings::MagickSharpenImage(self.wand, radius, sigma) })
696 }
697
698 pub fn set_background_color(&self, pixel_wand: &PixelWand) -> Result<()> {
700 self.result_from_boolean(unsafe {
701 bindings::MagickSetBackgroundColor(self.wand, pixel_wand.as_ptr())
702 })
703 }
704
705 pub fn set_image_background_color(&self, pixel_wand: &PixelWand) -> Result<()> {
707 self.result_from_boolean(unsafe {
708 bindings::MagickSetImageBackgroundColor(self.wand, pixel_wand.as_ptr())
709 })
710 }
711
712 pub fn get_image_resolution(&self) -> Result<(f64, f64)> {
714 let mut x_resolution = 0f64;
715 let mut y_resolution = 0f64;
716 self.result_from_boolean(unsafe {
717 bindings::MagickGetImageResolution(self.wand, &mut x_resolution, &mut y_resolution)
718 })
719 .map(|_| (x_resolution, y_resolution))
720 }
721
722 pub fn set_image_resolution(&self, x_resolution: f64, y_resolution: f64) -> Result<()> {
724 self.result_from_boolean(unsafe {
725 bindings::MagickSetImageResolution(self.wand, x_resolution, y_resolution)
726 })
727 }
728
729 pub fn set_resolution(&self, x_resolution: f64, y_resolution: f64) -> Result<()> {
731 self.result_from_boolean(unsafe {
732 bindings::MagickSetResolution(self.wand, x_resolution, y_resolution)
733 })
734 }
735
736 pub fn sepia_tone_image(&self, threshold: f64) -> Result<()> {
738 self.result_from_boolean(unsafe {
739 bindings::MagickSepiaToneImage(self.wand, threshold * self.quantum_range()?)
740 })
741 }
742
743 pub fn export_image_pixels(
746 &self,
747 x: isize,
748 y: isize,
749 width: usize,
750 height: usize,
751 map: &str,
752 ) -> Option<Vec<u8>> {
753 let c_map = CString::new(map).ok()?;
754 let capacity = width * height * map.len();
755 let mut pixels = vec![0; capacity];
756
757 unsafe {
758 if bindings::MagickExportImagePixels(
759 self.wand,
760 x,
761 y,
762 width,
763 height,
764 c_map.as_ptr(),
765 bindings::StorageType::CharPixel,
766 pixels.as_mut_ptr() as *mut c_void,
767 ) == MagickTrue
768 {
769 Some(pixels)
770 } else {
771 None
772 }
773 }
774 }
775
776 pub fn export_image_pixels_double(
777 &self,
778 x: isize,
779 y: isize,
780 width: usize,
781 height: usize,
782 map: &str,
783 ) -> Option<Vec<f64>> {
784 let c_map = CString::new(map).expect("map contains null byte");
785 let capacity = width * height * map.len();
786 let mut pixels = Vec::with_capacity(capacity);
787 pixels.resize(capacity, 0.0);
788
789 unsafe {
790 if bindings::MagickExportImagePixels(
791 self.wand,
792 x,
793 y,
794 width,
795 height,
796 c_map.as_ptr(),
797 bindings::StorageType::DoublePixel,
798 pixels.as_mut_ptr() as *mut c_void,
799 ) == MagickTrue
800 {
801 Some(pixels)
802 } else {
803 None
804 }
805 }
806 }
807
808 pub fn resize_image(&self, width: usize, height: usize, filter: FilterType) -> Result<()> {
811 self.result_from_boolean(unsafe {
812 bindings::MagickResizeImage(self.wand, width, height, filter)
813 })
814 }
815
816 pub fn scale_image(
821 &self,
822 width_scale: f64,
823 height_scale: f64,
824 filter: FilterType,
825 ) -> Result<()> {
826 if width_scale < 0.0 {
827 return Err(MagickError("negative width scale given".to_string()));
828 }
829 if height_scale < 0.0 {
830 return Err(MagickError("negative height scale given".to_string()));
831 }
832
833 let width = self.get_image_width();
834 let height = self.get_image_height();
835
836 let width = ((width as f64) * width_scale) as usize;
837 let height = ((height as f64) * height_scale) as usize;
838
839 self.resize_image(width, height, filter)
840 }
841
842 pub fn thumbnail_image(&self, width: usize, height: usize) -> Result<()> {
846 self.result_from_boolean(unsafe {
847 bindings::MagickThumbnailImage(self.wand, width, height)
848 })
849 }
850
851 pub fn crop_image(&self, width: usize, height: usize, x: isize, y: isize) -> Result<()> {
854 self.result_from_boolean(unsafe {
855 bindings::MagickCropImage(self.wand, width, height, x, y)
856 })
857 }
858
859 pub fn sample_image(&self, width: usize, height: usize) -> Result<()> {
864 self.result_from_boolean(unsafe { bindings::MagickSampleImage(self.wand, width, height) })
865 }
866
867 pub fn resample_image(
870 &self,
871 x_resolution: f64,
872 y_resolution: f64,
873 filter: FilterType,
874 ) -> Result<()> {
875 self.result_from_boolean(unsafe {
876 bindings::MagickResampleImage(self.wand, x_resolution, y_resolution, filter)
877 })
878 }
879
880 pub fn liquid_rescale_image(
882 &self,
883 width: usize,
884 height: usize,
885 delta_x: f64,
886 rigidity: f64,
887 ) -> Result<()> {
888 self.result_from_boolean(unsafe {
889 bindings::MagickLiquidRescaleImage(self.wand, width, height, delta_x, rigidity)
890 })
891 }
892
893 pub fn implode(&self, amount: f64, method: PixelInterpolateMethod) -> Result<()> {
895 self.result_from_boolean(unsafe { bindings::MagickImplodeImage(self.wand, amount, method) })
896 }
897
898 pub fn fit(&self, width: usize, height: usize) {
901 let mut width_ratio = width as f64;
902 width_ratio /= self.get_image_width() as f64;
903 let mut height_ratio = height as f64;
904 height_ratio /= self.get_image_height() as f64;
905 let (new_width, new_height) = if width_ratio < height_ratio {
906 (
907 width,
908 (self.get_image_height() as f64 * width_ratio) as usize,
909 )
910 } else {
911 (
912 (self.get_image_width() as f64 * height_ratio) as usize,
913 height,
914 )
915 };
916 unsafe {
917 bindings::MagickResetIterator(self.wand);
918 while bindings::MagickNextImage(self.wand) != MagickFalse {
919 bindings::MagickResizeImage(self.wand, new_width, new_height, FilterType::Lanczos);
920 }
921 }
922 }
923
924 pub fn requires_orientation(&self) -> bool {
927 self.get_image_orientation() != OrientationType::TopLeft
928 }
929
930 pub fn auto_orient(&self) -> bool {
935 unsafe { bindings::MagickAutoOrientImage(self.wand) == MagickTrue }
936 }
937
938 pub fn write_image(&self, path: &str) -> Result<()> {
940 let c_name = CString::new(path).map_err(|_| "name string contains null byte")?;
941 self.result_from_boolean(unsafe { bindings::MagickWriteImage(self.wand, c_name.as_ptr()) })
942 }
943
944 pub fn write_image_blob(&self, format: &str) -> Result<Vec<u8>> {
949 let c_format = CString::new(format).map_err(|_| "format string contains null byte")?;
950 let mut length: size_t = 0;
951 let blob = unsafe {
952 bindings::MagickResetIterator(self.wand);
953 bindings::MagickSetImageFormat(self.wand, c_format.as_ptr());
954 bindings::MagickGetImageBlob(self.wand, &mut length)
955 };
956
957 self.result_from_ptr(blob, |blob| Self::c_array_into_vec(blob, length))
958 }
959
960 pub fn write_images_blob(&self, format: &str) -> Result<Vec<u8>> {
965 let c_format = CString::new(format).map_err(|_| "format string contains null byte")?;
966 let mut length: size_t = 0;
967 let blob = unsafe {
968 bindings::MagickSetIteratorIndex(self.wand, 0);
969 bindings::MagickSetImageFormat(self.wand, c_format.as_ptr());
970 bindings::MagickGetImagesBlob(self.wand, &mut length)
971 };
972
973 Ok(Self::c_array_into_vec(blob, length))
974 }
975
976 pub fn get_image_alpha_channel(&self) -> bool {
979 let res = unsafe { bindings::MagickGetImageAlphaChannel(self.wand) };
980 res == MagickTrue
981 }
982
983 pub fn draw_image(&mut self, drawing_wand: &DrawingWand) -> Result<()> {
985 self.result_from_boolean(unsafe { bindings::MagickDrawImage(self.wand, drawing_wand.as_ptr()) })
986 }
987
988 pub fn deskew_image(&mut self, threshold: f64) -> Result<()> {
993 self.result_from_boolean(unsafe { bindings::MagickDeskewImage(self.wand, threshold) })
994 }
995
996 pub fn set_image_mask(&mut self, pixel_mask: PixelMask, clip_mask: &MagickWand) -> Result<()> {
1001 self.result_from_boolean(unsafe {
1002 bindings::MagickSetImageMask(self.wand, pixel_mask, clip_mask.wand)
1003 })
1004 }
1005
1006 pub fn set_image_channel_mask(&mut self, option: ChannelType) -> ChannelType {
1008 unsafe { bindings::MagickSetImageChannelMask(self.wand, option) }
1009 }
1010
1011 pub fn evaluate_image(&mut self, op: MagickEvaluateOperator, val: f64) -> Result<()> {
1016 self.result_from_boolean(unsafe { bindings::MagickEvaluateImage(self.wand, op, val) })
1017 }
1018
1019 pub fn border_image(
1022 &self,
1023 pixel_wand: &PixelWand,
1024 width: usize,
1025 height: usize,
1026 compose: CompositeOperator,
1027 ) -> Result<()> {
1028 self.result_from_boolean(unsafe {
1029 bindings::MagickBorderImage(self.wand, pixel_wand.as_ptr(), width, height, compose)
1030 })
1031 }
1032
1033 pub fn shadow_image(&self, alpha: f64, sigma: f64, x: isize, y: isize) -> Result<()> {
1035 self.result_from_boolean(unsafe {
1036 bindings::MagickShadowImage(self.wand, alpha, sigma, x, y)
1037 })
1038 }
1039
1040 pub fn import_image_pixels(
1043 &mut self,
1044 x: isize,
1045 y: isize,
1046 columns: usize,
1047 rows: usize,
1048 pixels: &[u8],
1049 map: &str,
1050 ) -> Result<()> {
1051 let pixel_map = CString::new(map).map_err(|_| "map string contains null byte")?;
1052 self.result_from_boolean(unsafe {
1053 bindings::MagickImportImagePixels(
1054 self.wand,
1055 x,
1056 y,
1057 columns,
1058 rows,
1059 pixel_map.as_ptr(),
1060 bindings::StorageType::CharPixel,
1061 pixels.as_ptr() as *const libc::c_void,
1062 )
1063 })
1064 }
1065
1066 pub fn import_image_pixels_double(
1067 &mut self,
1068 x: isize,
1069 y: isize,
1070 columns: usize,
1071 rows: usize,
1072 pixels: &[f64],
1073 map: &str,
1074 ) -> Result<()> {
1075 let pixel_map = CString::new(map).expect("map string contains null byte");
1076 Self::result_from_boolean_with_error_message( unsafe {
1077 bindings::MagickImportImagePixels(
1078 self.wand,
1079 x,
1080 y,
1081 columns,
1082 rows,
1083 pixel_map.as_ptr(),
1084 bindings::StorageType::DoublePixel,
1085 pixels.as_ptr() as *const c_void,
1086 )
1087 }, "unable to import pixels")
1088 }
1089
1090 pub fn set_first_iterator(&self) {
1093 unsafe {
1094 bindings::MagickSetFirstIterator(self.wand);
1095 }
1096 }
1097
1098 pub fn next_image(&self) -> bool {
1101 let res = unsafe { bindings::MagickNextImage(self.wand) };
1102 res == MagickTrue
1103 }
1104
1105 pub fn auto_threshold(&self, method: AutoThresholdMethod) -> Result<()> {
1110 self.result_from_boolean(unsafe { bindings::MagickAutoThresholdImage(self.wand, method) })
1111 }
1112
1113 pub fn transform_image_colorspace(&self, colorspace: ColorspaceType) -> Result<()> {
1116 self.result_from_boolean(unsafe {
1117 bindings::MagickTransformImageColorspace(self.wand, colorspace)
1118 })
1119 }
1120
1121 pub fn quantize_image(
1123 &self,
1124 number_of_colors: usize,
1125 colorspace: ColorspaceType,
1126 tree_depth: usize,
1127 dither_method: DitherMethod,
1128 measure_error: bool,
1129 ) -> Result<()> {
1130 self.result_from_boolean(unsafe {
1131 bindings::MagickQuantizeImage(
1132 self.wand,
1133 number_of_colors,
1134 colorspace,
1135 tree_depth,
1136 dither_method,
1137 measure_error.into(),
1138 )
1139 })
1140 }
1141
1142 pub fn quantize_images(
1144 &self,
1145 number_of_colors: usize,
1146 colorspace: ColorspaceType,
1147 tree_depth: usize,
1148 dither_method: DitherMethod,
1149 measure_error: bool,
1150 ) -> Result<()> {
1151 self.result_from_boolean(unsafe {
1152 bindings::MagickQuantizeImages(
1153 self.wand,
1154 number_of_colors,
1155 colorspace,
1156 tree_depth,
1157 dither_method,
1158 measure_error.into(),
1159 )
1160 })
1161 }
1162
1163 pub fn function_image(&self, function: MagickFunction, args: &[f64]) -> Result<()> {
1189 let num_of_args: size_t = args.len();
1190 self.result_from_boolean(unsafe {
1191 bindings::MagickFunctionImage(self.wand, function, num_of_args, args.as_ptr())
1192 })
1193 }
1194
1195 pub fn polynomial_image(&self, terms: &[f64]) -> Result<()> {
1200 if terms.len() & 1 != 1 {
1201 return Err(MagickError("no constant coefficient given".to_string()));
1202 }
1203
1204 let num_of_terms: size_t = terms.len() >> 1;
1205
1206 self.result_from_boolean(unsafe {
1207 bindings::MagickPolynomialImage(self.wand, num_of_terms, terms.as_ptr())
1208 })
1209 }
1210
1211 pub fn convolve_image(&self, kernel_info: &KernelInfo) -> Result<()> {
1215 self.result_from_boolean(unsafe {
1216 bindings::MagickConvolveImage(self.wand, kernel_info.get_ptr())
1217 })
1218 }
1219
1220 pub fn morphology_image(
1226 &self,
1227 morphology_method: MorphologyMethod,
1228 iterations: isize,
1229 kernel_info: &KernelInfo,
1230 ) -> Result<()> {
1231 self.result_from_boolean(unsafe {
1232 bindings::MagickMorphologyImage(
1233 self.wand,
1234 morphology_method,
1235 iterations,
1236 kernel_info.get_ptr(),
1237 )
1238 })
1239 }
1240
1241 pub fn color_matrix_image(&self, color_matrix: &KernelInfo) -> Result<()> {
1250 self.result_from_boolean(unsafe {
1251 bindings::MagickColorMatrixImage(self.wand, color_matrix.get_ptr())
1252 })
1253 }
1254
1255 pub fn channel_fx_image(&self, expression: &str) -> Result<MagickWand> {
1267 let c_expression =
1268 CString::new(expression).map_err(|_| "artifact string contains null byte")?;
1269
1270 let wand_ptr = unsafe { bindings::MagickChannelFxImage(self.wand, c_expression.as_ptr()) };
1271 self.result_from_ptr(wand_ptr, MagickWand::from_ptr)
1272 }
1273
1274 pub fn combine_images(&self, colorspace: ColorspaceType) -> Result<MagickWand> {
1280 let wand_ptr = unsafe { bindings::MagickCombineImages(self.wand, colorspace) };
1281 self.result_from_ptr(wand_ptr, MagickWand::from_ptr)
1282 }
1283
1284 pub fn get_image(&self) -> Result<Image<'_>> {
1286 self.result_from_ptr(
1287 unsafe { bindings::GetImageFromMagickWand(self.wand) },
1288 Image::new,
1289 )
1290 }
1291
1292 pub fn contrast_stretch_image(&self, black_point: f64, white_point: f64) -> Result<()> {
1294 self.result_from_boolean(unsafe {
1295 bindings::MagickContrastStretchImage(self.wand, black_point, white_point)
1296 })
1297 }
1298
1299 mutations!(
1300 MagickSetImageAlpha => set_image_alpha(alpha: f64)
1302
1303 MagickModulateImage => modulate_image(brightness: f64, saturation: f64, hue: f64)
1305
1306 MagickBrightnessContrastImage => brightness_contrast_image(brightness: f64, contrast: f64)
1308
1309 MagickSetImageAlphaChannel => set_image_alpha_channel(alpha_channel: AlphaChannelOption)
1311
1312 MagickUniqueImageColors => unique_image_colors()
1314
1315 MagickKmeansImage => kmeans(number_colors: usize, max_iterations: usize, tolerance: f64)
1317
1318 MagickAutoGammaImage => auto_gamma()
1320
1321 MagickAutoLevelImage => auto_level()
1323 );
1324
1325 get!(get_image_colors, MagickGetImageColors, usize);
1326
1327 string_set_get!(
1328 get_filename, set_filename, MagickGetFilename, MagickSetFilename
1329 get_font, set_font, MagickGetFont, MagickSetFont
1330 get_format, set_format, MagickGetFormat, MagickSetFormat
1331 get_image_filename, set_image_filename, MagickGetImageFilename, MagickSetImageFilename
1332 get_image_format, set_image_format, MagickGetImageFormat, MagickSetImageFormat
1333 );
1334
1335 set_get!(
1336 get_colorspace, set_colorspace, MagickGetColorspace, MagickSetColorspace, ColorspaceType
1337 get_image_compose, set_image_compose, MagickGetImageCompose, MagickSetImageCompose, CompositeOperator
1338 get_compression, set_compression, MagickGetCompression, MagickSetCompression, CompressionType
1339 get_compression_quality, set_compression_quality, MagickGetCompressionQuality, MagickSetCompressionQuality, usize
1340 get_gravity, set_gravity, MagickGetGravity, MagickSetGravity, GravityType
1341 get_image_colorspace, set_image_colorspace, MagickGetImageColorspace, MagickSetImageColorspace, ColorspaceType
1342 get_image_compression, set_image_compression, MagickGetImageCompression, MagickSetImageCompression, CompressionType
1343 get_image_compression_quality, set_image_compression_quality, MagickGetImageCompressionQuality, MagickSetImageCompressionQuality, usize
1344 get_image_delay, set_image_delay, MagickGetImageDelay, MagickSetImageDelay, usize
1345 get_image_depth, set_image_depth, MagickGetImageDepth, MagickSetImageDepth, usize
1346 get_image_dispose, set_image_dispose, MagickGetImageDispose, MagickSetImageDispose, DisposeType
1347 get_image_endian, set_image_endian, MagickGetImageEndian, MagickSetImageEndian, EndianType
1348 get_image_fuzz, set_image_fuzz, MagickGetImageFuzz, MagickSetImageFuzz, f64
1349 get_image_gamma, set_image_gamma, MagickGetImageGamma, MagickSetImageGamma, f64
1350 get_image_gravity, set_image_gravity, MagickGetImageGravity, MagickSetImageGravity, GravityType
1351 get_image_interlace_scheme, set_image_interlace_scheme, MagickGetImageInterlaceScheme, MagickSetImageInterlaceScheme, InterlaceType
1352 get_image_interpolate_method, set_image_interpolate_method, MagickGetImageInterpolateMethod, MagickSetImageInterpolateMethod, PixelInterpolateMethod
1353 get_image_iterations, set_image_iterations, MagickGetImageIterations, MagickSetImageIterations, usize
1354 get_image_orientation, set_image_orientation, MagickGetImageOrientation, MagickSetImageOrientation, OrientationType
1355 get_image_rendering_intent, set_image_rendering_intent, MagickGetImageRenderingIntent, MagickSetImageRenderingIntent, RenderingIntent
1356 get_image_scene, set_image_scene, MagickGetImageScene, MagickSetImageScene, usize
1357 get_image_type, set_image_type, MagickGetImageType, MagickSetImageType, ImageType
1358 get_image_units, set_image_units, MagickGetImageUnits, MagickSetImageUnits, ResolutionType
1359 get_interlace_scheme, set_interlace_scheme, MagickGetInterlaceScheme, MagickSetInterlaceScheme, InterlaceType
1360 get_interpolate_method, set_interpolate_method, MagickGetInterpolateMethod, MagickSetInterpolateMethod, PixelInterpolateMethod
1361 get_iterator_index, set_iterator_index, MagickGetIteratorIndex, MagickSetIteratorIndex, isize
1362 get_orientation, set_orientation, MagickGetOrientation, MagickSetOrientation, OrientationType
1363 get_pointsize, set_pointsize, MagickGetPointsize, MagickSetPointsize, f64
1364 get_type, set_type, MagickGetType, MagickSetType, ImageType
1365 );
1366
1367 fn result_from_boolean(&self, no_error: MagickBooleanType) -> Result<()> {
1368 if no_error == MagickTrue {
1369 Ok(())
1370 } else {
1371 Err(MagickError(self.get_exception()?.0))
1372 }
1373 }
1374
1375 fn result_from_boolean_with_error_message(
1376 no_error: MagickBooleanType,
1377 message: impl Into<String>,
1378 ) -> Result<()> {
1379 if no_error == MagickTrue {
1380 Ok(())
1381 } else {
1382 Err(MagickError(message.into()))
1383 }
1384 }
1385
1386 fn result_from_ptr<P, T>(&self, ptr: *mut P, new: impl FnOnce(*mut P) -> T) -> Result<T> {
1387 if ptr.is_null() {
1388 Err(MagickError(self.get_exception()?.0))
1389 } else {
1390 Ok(new(ptr))
1391 }
1392 }
1393
1394 fn result_from_ptr_with_error_message<P, T>(
1395 ptr: *mut P,
1396 new: impl FnOnce(*mut P) -> T,
1397 message: impl Into<String>,
1398 ) -> Result<T> {
1399 if ptr.is_null() {
1400 Err(MagickError(message.into()))
1401 } else {
1402 Ok(new(ptr))
1403 }
1404 }
1405
1406 fn c_char_to_string_vec(c_values: *mut *mut c_char, num_of_artifacts: usize) -> Vec<String> {
1407 let mut values: Vec<String> = Vec::with_capacity(num_of_artifacts);
1408 for i in 0..num_of_artifacts {
1409 let cstr = unsafe { CStr::from_ptr(*c_values.add(i)) };
1411 values.push(cstr.to_string_lossy().into_owned());
1412 }
1413
1414 unsafe {
1415 bindings::MagickRelinquishMemory(c_values as *mut c_void);
1416 }
1417
1418 values
1419 }
1420
1421 fn c_char_into_string(c_value: *mut c_char) -> String {
1422 let value = unsafe { CStr::from_ptr(c_value) }
1423 .to_string_lossy()
1424 .into_owned();
1425
1426 unsafe {
1427 bindings::MagickRelinquishMemory(c_value as *mut c_void);
1428 }
1429
1430 value
1431 }
1432
1433 fn c_array_into_vec(blob: *mut c_uchar, length: usize) -> Vec<u8> {
1434 let mut bytes = vec![0; length];
1435
1436 unsafe {
1437 let ptr = bytes.as_mut_ptr();
1438 ptr::copy_nonoverlapping(blob, ptr, length);
1439 bindings::MagickRelinquishMemory(blob as *mut c_void);
1440 }
1441
1442 bytes
1443 }
1444}
1445
1446impl fmt::Debug for MagickWand {
1447 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1448 writeln!(f, "MagickWand {{")?;
1449 writeln!(f, " Exception: {:?}", self.get_exception())?;
1450 writeln!(f, " IsWand: {:?}", self.is_wand())?;
1451 self.fmt_string_settings(f, " ")?;
1452 self.fmt_checked_settings(f, " ")?;
1453 writeln!(f, "}}")
1454 }
1455}