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}