Skip to main content

magick_rust/types/
image.rs

1/*
2 * Copyright 2024 5ohue
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::marker::PhantomData;
17use std::ops::{Deref, DerefMut};
18
19use crate::bindings;
20use crate::result::{MagickError, Result};
21use crate::wand::MagickWand;
22
23/// A borrowed handle to a single image owned by a [`MagickWand`].
24///
25/// The handle borrows the wand's underlying image and is only valid for the
26/// wand's lifetime; it does not own or free the image.
27pub struct Image<'a> {
28    image: *mut bindings::Image,
29    phantom_data: PhantomData<&'a bindings::Image>,
30}
31
32impl Image<'_> {
33    pub(crate) fn new(img: *mut bindings::Image) -> Self {
34        // SAFETY: This is safe and also does not require an Image::drop() call as:
35        //         The lifetime of Image is the same as the lifetime of wrapped bindings::Image.
36        //         The bindings::Image is borrowed by the caller from MagickWand.
37        //         Magickwand::drop() destroys the bindings::MagickWand, which
38        //         destroys both the associated bindings::Image and itself.
39        Image {
40            image: img,
41            phantom_data: PhantomData,
42        }
43    }
44
45    pub(crate) unsafe fn get_ptr(&self) -> *mut bindings::Image {
46        self.image
47    }
48}
49
50/// Move the wand's internal iterator to `index`.
51///
52/// ImageMagick's `MagickWand` has a single internal iterator that every
53/// per-image operation reads from. The image-list views below re-pin that
54/// iterator on every frame access so that an unrelated call cannot leave it
55/// pointing at the wrong frame.
56///
57/// This is a `&self` counterpart to [`MagickWand::set_iterator_index`] (which
58/// takes `&mut self`); the shared borrow is sound because it mutates only the
59/// iterator cursor in C-allocated memory behind the wand pointer. `index` is
60/// always validated against the image count before this is called, so the
61/// boolean return is ignored.
62fn pin(wand: &MagickWand, index: isize) {
63    debug_assert!(
64        index >= 0 && (index as usize) < wand.get_number_images(),
65        "frame index {index} out of bounds (count {})",
66        wand.get_number_images()
67    );
68    unsafe {
69        bindings::MagickSetIteratorIndex(wand.as_ptr(), index);
70    }
71}
72
73/// A read-only view over the images (frames) held by a [`MagickWand`].
74///
75/// Obtained from [`MagickWand::images`]. Creating the view resets the wand's
76/// iterator, and each frame handed out by [`Images::get`], [`Images::first`],
77/// [`Images::last`], or the iteration helpers re-pins the iterator to the
78/// correct frame before delegating, so frame access stays consistent regardless
79/// of call order.
80pub struct Images<'w> {
81    wand: &'w MagickWand,
82}
83
84impl<'w> Images<'w> {
85    pub(crate) fn new(wand: &'w MagickWand) -> Self {
86        wand.reset_iterator();
87        Images { wand }
88    }
89
90    /// The number of images (frames) in the list.
91    pub fn count(&self) -> usize {
92        self.wand.get_number_images()
93    }
94
95    /// Returns `true` if the list contains no images.
96    pub fn is_empty(&self) -> bool {
97        self.count() == 0
98    }
99
100    /// Borrow the frame at `index`, or `None` if out of bounds.
101    pub fn get(&self, index: usize) -> Option<ImageRef<'_>> {
102        if index < self.count() {
103            Some(ImageRef {
104                wand: self.wand,
105                index: index as isize,
106            })
107        } else {
108            None
109        }
110    }
111
112    /// Borrow the first frame, or `None` if the list is empty.
113    pub fn first(&self) -> Option<ImageRef<'_>> {
114        self.get(0)
115    }
116
117    /// Borrow the last frame, or `None` if the list is empty.
118    pub fn last(&self) -> Option<ImageRef<'_>> {
119        self.count().checked_sub(1).and_then(|i| self.get(i))
120    }
121
122    /// Visit every frame in order, passing its index and a borrow to `f`.
123    pub fn for_each(&self, mut f: impl FnMut(usize, ImageRef<'_>)) {
124        for index in 0..self.count() {
125            f(
126                index,
127                ImageRef {
128                    wand: self.wand,
129                    index: index as isize,
130                },
131            );
132        }
133    }
134
135    /// Like [`Images::for_each`], but `f` may fail; the first error stops
136    /// iteration and is returned.
137    pub fn try_for_each(&self, mut f: impl FnMut(usize, ImageRef<'_>) -> Result<()>) -> Result<()> {
138        for index in 0..self.count() {
139            f(
140                index,
141                ImageRef {
142                    wand: self.wand,
143                    index: index as isize,
144                },
145            )?;
146        }
147        Ok(())
148    }
149}
150
151/// A mutable view over the images (frames) held by a [`MagickWand`].
152///
153/// Obtained from [`MagickWand::images_mut`]. Because ImageMagick exposes a
154/// single internal iterator, only one [`ImageMut`] may be borrowed at a time;
155/// this is enforced by the borrow checker, as each accessor borrows the view
156/// mutably.
157pub struct ImagesMut<'w> {
158    wand: &'w mut MagickWand,
159}
160
161impl<'w> ImagesMut<'w> {
162    pub(crate) fn new(wand: &'w mut MagickWand) -> Self {
163        wand.reset_iterator();
164        ImagesMut { wand }
165    }
166
167    /// The number of images (frames) in the list.
168    pub fn count(&self) -> usize {
169        self.wand.get_number_images()
170    }
171
172    /// Returns `true` if the list contains no images.
173    pub fn is_empty(&self) -> bool {
174        self.count() == 0
175    }
176
177    /// Mutably borrow the frame at `index`, or `None` if out of bounds.
178    pub fn get(&mut self, index: usize) -> Option<ImageMut<'_>> {
179        if index < self.count() {
180            Some(ImageMut {
181                wand: &mut *self.wand,
182                index: index as isize,
183            })
184        } else {
185            None
186        }
187    }
188
189    /// Mutably borrow the first frame, or `None` if the list is empty.
190    pub fn first(&mut self) -> Option<ImageMut<'_>> {
191        self.get(0)
192    }
193
194    /// Mutably borrow the last frame, or `None` if the list is empty.
195    pub fn last(&mut self) -> Option<ImageMut<'_>> {
196        self.count().checked_sub(1).and_then(|i| self.get(i))
197    }
198
199    /// Remove the frame at `index` from the list.
200    ///
201    /// Note that removing a frame shifts the indices of all later frames down
202    /// by one, so any indices computed before the removal are stale afterwards.
203    pub fn remove(&mut self, index: usize) -> Result<()> {
204        let count = self.count();
205        if index >= count {
206            return Err(MagickError(format!(
207                "image index {index} out of bounds (count {count})"
208            )));
209        }
210        pin(self.wand, index as isize);
211        self.wand.remove_image()
212    }
213
214    /// Append all frames from `other` to the end of this list.
215    pub fn append(&mut self, other: &MagickWand) -> Result<()> {
216        self.wand.set_last_iterator();
217        self.wand.add_image(other)
218    }
219
220    /// Visit every frame in order, passing its index and a mutable borrow to
221    /// `f`.
222    ///
223    /// `f` must not add or remove frames (e.g. via [`ImageMut`]'s deref to
224    /// [`MagickWand::remove_image`] / [`MagickWand::add_image`]): the set of
225    /// indices to visit is fixed when iteration begins, so changing the frame
226    /// count mid-iteration visits the wrong frames. Use [`ImagesMut::remove`]
227    /// or [`ImagesMut::append`] before or after iterating instead.
228    pub fn for_each(&mut self, mut f: impl FnMut(usize, ImageMut<'_>)) {
229        for index in 0..self.count() {
230            f(
231                index,
232                ImageMut {
233                    wand: &mut *self.wand,
234                    index: index as isize,
235                },
236            );
237        }
238    }
239
240    /// Like [`ImagesMut::for_each`], but `f` may fail; the first error stops
241    /// iteration and is returned. The same "do not add or remove frames"
242    /// caveat as [`ImagesMut::for_each`] applies.
243    pub fn try_for_each(
244        &mut self,
245        mut f: impl FnMut(usize, ImageMut<'_>) -> Result<()>,
246    ) -> Result<()> {
247        for index in 0..self.count() {
248            f(
249                index,
250                ImageMut {
251                    wand: &mut *self.wand,
252                    index: index as isize,
253                },
254            )?;
255        }
256        Ok(())
257    }
258}
259
260/// A handle to a single frame for read-only access.
261///
262/// Each accessor pins the wand's iterator to this frame and then reads, so the
263/// returned values always correspond to this frame regardless of what other
264/// frames are touched in between. Unlike [`ImageMut`], this handle deliberately
265/// does *not* deref to [`MagickWand`]: doing so would both expose the wand's
266/// image-mutating methods (defeating the read-only contract) and hand out a
267/// `&MagickWand` that silently goes stale once another frame is accessed.
268pub struct ImageRef<'a> {
269    wand: &'a MagickWand,
270    index: isize,
271}
272
273/// Generate read-only forwarding accessors on [`ImageRef`]. Each pins the
274/// iterator to the frame, then delegates to the eponymous [`MagickWand`] getter.
275macro_rules! frame_getters {
276    ($($name:ident($($arg:ident: $ty:ty),*) -> $ret:ty;)*) => {
277        impl ImageRef<'_> {
278            $(
279                #[doc = concat!(
280                    "Read this frame's value; see [`MagickWand::",
281                    stringify!($name), "`]."
282                )]
283                pub fn $name(&self, $($arg: $ty),*) -> $ret {
284                    pin(self.wand, self.index);
285                    self.wand.$name($($arg),*)
286                }
287            )*
288        }
289    };
290}
291
292frame_getters! {
293    get_image_width() -> usize;
294    get_image_height() -> usize;
295    get_image_page() -> (usize, usize, isize, isize);
296    get_image_resolution() -> Result<(f64, f64)>;
297    get_image_range() -> Result<(f64, f64)>;
298    get_image_colors() -> usize;
299    get_image_alpha_channel() -> bool;
300    get_image_virtual_pixel_method() -> crate::VirtualPixelMethod;
301    get_image_pixel_color(x: isize, y: isize) -> Option<crate::PixelWand>;
302    get_image_histogram() -> Option<Vec<crate::PixelWand>>;
303    get_image_artifact(artifact: &str) -> Result<String>;
304    get_image_artifacts(pattern: &str) -> Result<Vec<String>>;
305    get_image_property(name: &str) -> Result<String>;
306    get_image_properties(pattern: &str) -> Result<Vec<String>>;
307    get_image_format() -> Result<String>;
308    get_image_filename() -> Result<String>;
309    get_image_compose() -> crate::CompositeOperator;
310    get_image_colorspace() -> crate::ColorspaceType;
311    get_image_compression() -> crate::CompressionType;
312    get_image_compression_quality() -> usize;
313    get_image_delay() -> usize;
314    get_image_depth() -> usize;
315    get_image_dispose() -> crate::DisposeType;
316    get_image_endian() -> crate::EndianType;
317    get_image_fuzz() -> f64;
318    get_image_gamma() -> f64;
319    get_image_gravity() -> crate::GravityType;
320    get_image_interlace_scheme() -> crate::InterlaceType;
321    get_image_interpolate_method() -> crate::PixelInterpolateMethod;
322    get_image_iterations() -> usize;
323    get_image_orientation() -> crate::OrientationType;
324    get_image_rendering_intent() -> crate::RenderingIntent;
325    get_image_scene() -> usize;
326    get_image_type() -> crate::ImageType;
327    get_image_units() -> crate::ResolutionType;
328}
329
330impl ImageRef<'_> {
331    /// Borrow this frame as an [`Image`], e.g. for [`MagickWand::new_from_image`].
332    ///
333    /// The returned [`Image`] captures this frame's underlying image pointer, so
334    /// it remains valid even if the wand's iterator is later moved to another
335    /// frame.
336    pub fn get_image(&self) -> Result<Image<'_>> {
337        pin(self.wand, self.index);
338        self.wand.get_image()
339    }
340}
341
342/// A handle to a single frame for mutable access.
343///
344/// Derefs to [`MagickWand`], pinning the wand's iterator to this frame first,
345/// so every existing per-image `MagickWand` operation works directly on the
346/// frame.
347pub struct ImageMut<'a> {
348    wand: &'a mut MagickWand,
349    index: isize,
350}
351
352impl Deref for ImageMut<'_> {
353    type Target = MagickWand;
354
355    fn deref(&self) -> &MagickWand {
356        pin(self.wand, self.index);
357        &*self.wand
358    }
359}
360
361impl DerefMut for ImageMut<'_> {
362    fn deref_mut(&mut self) -> &mut MagickWand {
363        pin(self.wand, self.index);
364        &mut *self.wand
365    }
366}