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, 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
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<()> {
76 self.result_from_boolean(unsafe {
77 bindings::MagickNewImage(self.wand, columns, rows, background.as_ptr())
78 })
79 }
80
81 #[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 pub fn get_resource_limit(resource: ResourceType) -> u64 {
96 unsafe { bindings::MagickGetResourceLimit(resource) as u64 }
97 }
98
99 pub fn get_resource(resource: ResourceType) -> u64 {
101 unsafe { bindings::MagickGetResource(resource) as u64 }
102 }
103
104 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 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 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 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 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 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 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 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 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 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 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 pub fn get_number_images(&self) -> usize {
228 unsafe { bindings::MagickGetNumberImages(self.wand) }
229 }
230
231 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 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 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 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 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 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 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 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 #[cfg(not(feature = "disable-hdri"))]
333 fn quantum_range(&self) -> Result<f64> {
334 Ok(bindings::QuantumRange)
335 }
336
337 #[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 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 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 pub fn normalize_image(&self) -> Result<()> {
386 self.result_from_boolean(unsafe { bindings::MagickNormalizeImage(self.wand) })
387 }
388
389 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 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 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 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 pub fn strip_image(&self) -> Result<()> {
465 self.result_from_boolean(unsafe { bindings::MagickStripImage(self.wand) })
466 }
467
468 pub fn flip_image(&self) -> Result<()> {
470 self.result_from_boolean(unsafe { bindings::MagickFlipImage(self.wand) })
471 }
472
473 pub fn negate_image(&self) -> Result<()> {
475 self.result_from_boolean(unsafe { bindings::MagickNegateImage(self.wand, MagickTrue) })
476 }
477
478 pub fn flop_image(&self) -> Result<()> {
480 self.result_from_boolean(unsafe { bindings::MagickFlopImage(self.wand) })
481 }
482
483 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 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 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 pub fn median_blur_image(&self, width: usize, height: usize) -> Result<()> {
520 self.statistic_image(StatisticType::Median, width, height)
521 }
522
523 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 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 pub fn trim_image(&self, fuzz: f64) -> Result<()> {
546 self.result_from_boolean(unsafe { bindings::MagickTrimImage(self.wand, fuzz) })
547 }
548
549 pub fn get_image_virtual_pixel_method(&self) -> VirtualPixelMethod {
552 unsafe { bindings::MagickGetImageVirtualPixelMethod(self.wand) }
553 }
554
555 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 pub fn get_image_width(&self) -> usize {
568 unsafe { bindings::MagickGetImageWidth(self.wand) }
569 }
570
571 pub fn get_image_height(&self) -> usize {
573 unsafe { bindings::MagickGetImageHeight(self.wand) }
574 }
575
576 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 bindings::MagickGetImagePage(self.wand, &mut width, &mut height, &mut x, &mut y);
583 }
584 (width, height, x, y)
585 }
586
587 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 pub fn implode(&self, amount: f64, method: PixelInterpolateMethod) -> Result<()> {
996 self.result_from_boolean(unsafe { bindings::MagickImplodeImage(self.wand, amount, method) })
997 }
998
999 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 pub fn requires_orientation(&self) -> bool {
1028 self.get_image_orientation() != OrientationType::TopLeft
1029 }
1030
1031 pub fn auto_orient(&self) -> bool {
1036 unsafe { bindings::MagickAutoOrientImage(self.wand) == MagickTrue }
1037 }
1038
1039 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 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 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 pub fn get_image_alpha_channel(&self) -> bool {
1080 let res = unsafe { bindings::MagickGetImageAlphaChannel(self.wand) };
1081 res == MagickTrue
1082 }
1083
1084 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 pub fn deskew_image(&mut self, threshold: f64) -> Result<()> {
1096 self.result_from_boolean(unsafe { bindings::MagickDeskewImage(self.wand, threshold) })
1097 }
1098
1099 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 pub fn set_image_channel_mask(&mut self, option: ChannelType) -> ChannelType {
1111 unsafe { bindings::MagickSetImageChannelMask(self.wand, option) }
1112 }
1113
1114 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 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 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 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 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 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 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 pub fn images(&self) -> Images<'_> {
1284 Images::new(self)
1285 }
1286
1287 pub fn images_mut(&mut self) -> ImagesMut<'_> {
1292 ImagesMut::new(self)
1293 }
1294
1295 pub fn reset_iterator(&self) {
1301 unsafe {
1302 bindings::MagickResetIterator(self.wand);
1303 }
1304 }
1305
1306 pub fn set_first_iterator(&self) {
1309 unsafe {
1310 bindings::MagickSetFirstIterator(self.wand);
1311 }
1312 }
1313
1314 pub fn set_last_iterator(&self) {
1317 unsafe {
1318 bindings::MagickSetLastIterator(self.wand);
1319 }
1320 }
1321
1322 pub fn next_image(&self) -> bool {
1328 let res = unsafe { bindings::MagickNextImage(self.wand) };
1329 res == MagickTrue
1330 }
1331
1332 pub fn previous_image(&self) -> bool {
1338 let res = unsafe { bindings::MagickPreviousImage(self.wand) };
1339 res == MagickTrue
1340 }
1341
1342 pub fn has_next_image(&self) -> bool {
1346 let res = unsafe { bindings::MagickHasNextImage(self.wand) };
1347 res == MagickTrue
1348 }
1349
1350 pub fn has_previous_image(&self) -> bool {
1354 let res = unsafe { bindings::MagickHasPreviousImage(self.wand) };
1355 res == MagickTrue
1356 }
1357
1358 pub fn remove_image(&mut self) -> Result<()> {
1361 self.result_from_boolean(unsafe { bindings::MagickRemoveImage(self.wand) })
1362 }
1363
1364 pub fn auto_threshold(&self, method: AutoThresholdMethod) -> Result<()> {
1369 self.result_from_boolean(unsafe { bindings::MagickAutoThresholdImage(self.wand, method) })
1370 }
1371
1372 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 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 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 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 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 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 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 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 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 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 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 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 MagickSetImageAlpha => set_image_alpha(alpha: f64)
1561
1562 MagickModulateImage => modulate_image(brightness: f64, saturation: f64, hue: f64)
1564
1565 MagickBrightnessContrastImage => brightness_contrast_image(brightness: f64, contrast: f64)
1567
1568 MagickSetImageAlphaChannel => set_image_alpha_channel(alpha_channel: AlphaChannelOption)
1570
1571 MagickUniqueImageColors => unique_image_colors()
1573
1574 MagickKmeansImage => kmeans(number_colors: usize, max_iterations: usize, tolerance: f64)
1576
1577 MagickAutoGammaImage => auto_gamma()
1579
1580 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 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}