zune_image/
traits.rs

1/*
2 * Copyright (c) 2023.
3 *
4 * This software is free software; You can redistribute it or modify it under terms of the MIT, Apache License or Zlib license
5 */
6//! Various encapsulations of common image operations
7//!
8//! This contains traits that allow homogenous implementation of various items in different stages
9//! of the image processing timeline and the use of this via encapsulation without worrying of implementation
10//!
11//! The main traits are divided into the following
12//! - decoding: `DecoderTrait`: Implementing this for a format means the library can decode such formats
13//! - image processing `OperationsTrait`: Implementing this means one can modify the image or extract information from it
14//! - encoding: `EncoderTrait`: Implementing this means the image can be saved to a certain format
15//!
16#![allow(unused_variables)]
17use zune_core::bit_depth::{BitDepth, BitType};
18use zune_core::bytestream::ZByteWriterTrait;
19use zune_core::colorspace::{ColorSpace, ALL_COLORSPACES};
20use zune_core::log::{trace, warn};
21use zune_core::options::EncoderOptions;
22
23use crate::codecs::ImageFormat;
24use crate::core_filters::colorspace::ColorspaceConv;
25use crate::core_filters::depth::Depth;
26use crate::errors::{ImageErrors, ImageOperationsErrors};
27use crate::image::Image;
28use crate::metadata::AlphaState::NonPreMultiplied;
29use crate::metadata::{AlphaState, ImageMetadata};
30use crate::pipelines::EncodeResult;
31
32/// Encapsulates an image decoder.
33///
34/// All supported image decoders must implement this class
35pub trait DecoderTrait {
36    /// Decode a buffer already in memory
37    ///
38    /// The buffer to be decoded is the one passed
39    /// to the decoder when initializing the decoder
40    ///
41    /// # Returns
42    /// - Image -> Pixels decoded from the image as interleaved pixels.
43    ///
44    /// # Errors
45    ///  - Any image decoding errors will be propagated to the caller.
46    ///
47    /// # Example
48    /// ```no_run
49    /// use zune_core::bytestream::ZCursor;
50    /// #[cfg(feature = "jpeg")]
51    /// {
52    ///     use zune_image::traits::DecoderTrait;
53    ///     use zune_jpeg::JpegDecoder;
54    ///     let mut decoder = JpegDecoder::new(ZCursor::new([0xFF,0xD8]));
55    ///
56    ///     decoder.decode().unwrap();
57    /// }
58    /// #[cfg(not(feature="jpeg"))]
59    /// ()
60    /// ```
61    fn decode(&mut self) -> Result<Image, crate::errors::ImageErrors>;
62
63    /// Get width and height of the image
64    ///
65    /// # Returns
66    /// - Some(width,height)
67    /// - None -> If image hasn't been decoded and we can't extract
68    ///  the width and height.
69    fn dimensions(&self) -> Option<(usize, usize)>;
70
71    /// Get the colorspace that the decoded pixels
72    /// are stored in.
73    fn out_colorspace(&self) -> ColorSpace;
74
75    /// Get the name of the decoder
76    fn name(&self) -> &'static str;
77
78    /// Return true whether or not this codec is fully supported
79    /// and well tested to handle various formats.
80    ///
81    /// Currently set to true but a codec that is experimental should override it
82    /// to be false
83    fn is_experimental(&self) -> bool {
84        false
85    }
86    /// Read image metadata returning the values as
87    /// a struct
88    fn read_headers(&mut self) -> Result<Option<ImageMetadata>, crate::errors::ImageErrors> {
89        Ok(None)
90    }
91}
92
93/// This encapsulates an image operation.
94///
95/// All operations that can be stored in a workflow
96/// need to encapsulate this struct.
97pub trait OperationsTrait: Send + Sync {
98    /// Get the name of this operation
99    fn name(&self) -> &'static str;
100
101    /// Execute a simple operation on the image
102    /// manipulating the image struct
103    ///
104    /// An object should implement this function, but
105    /// a caller should call [`execute`], which does some error checking
106    /// before calling this method
107    ///
108    /// [`execute`]: Self::execute
109    fn execute_impl(&self, image: &mut Image) -> Result<(), ImageErrors>;
110
111    /// Return the supported colorspaces this operation supports
112    ///
113    /// Some operations cannot work on all colorspaces, e.g rgb to grayscale will
114    /// only work on RGB colorspace, not YUV or YCbCr colorspace, hence such an operation
115    /// must only declare support for such colorspace
116    ///
117    /// During execution, the image colorspace will be matched to this colorspace and
118    /// if it doesn't support it, an error will be raised during execution
119    fn supported_colorspaces(&self) -> &'static [ColorSpace] {
120        &ALL_COLORSPACES
121    }
122    /// Get supported bit types for this operation
123    ///
124    /// Not all operations are supported for all bit types and
125    /// o each support requires careful analysis to ensure it's doing
126    /// the right things
127    fn supported_types(&self) -> &'static [BitType];
128
129    /// Execute an operation
130    ///
131    /// This does come common error checking operations, e.g
132    /// it checks that image dimensions match array length and that this operation
133    /// supports the image colorspace, before calling [`execute_impl`]
134    ///
135    /// # Arguments
136    /// - image: A mutable reference to an image which
137    /// this operation will manipulate
138    ///
139    ///
140    /// # Errors
141    /// Any operations error will be propagated to the caller
142    ///
143    ///
144    /// [`execute_impl`]: Self::execute_impl
145    fn execute(&self, image: &mut Image) -> Result<(), ImageErrors> {
146        // Confirm colorspace
147        let colorspace = image.colorspace();
148
149        let supported = self
150            .supported_colorspaces()
151            .iter()
152            .any(|x| *x == colorspace);
153
154        if !supported {
155            match colorspace {
156                // for multi-band images, we want to ignore them
157                // since it is a lot of work to match them. so we assume they are images
158                // with color channels that lacks an alpha channel.
159                // This means it kinda behaves like an RGB image
160                ColorSpace::MultiBand(_) => {
161                    warn!("Multi-band image encountered");
162                    warn!("The image will be treated as a n-channel image with no alpha channel to allow operations to run on it");
163                }
164                _ => {
165                    return Err(ImageErrors::UnsupportedColorspace(
166                        colorspace,
167                        self.name(),
168                        self.supported_colorspaces()
169                    ));
170                }
171            }
172        }
173        // if image.metadata.alpha != self.alpha_state()
174        // {
175        //     PremultiplyAlpha::new(self.alpha_state());
176        // }
177        // check we support the bit depth
178        let bit_type = image.metadata.depth().bit_type();
179
180        let supported = self.supported_types().iter().any(|x| *x == bit_type);
181
182        if !supported {
183            return Err(ImageErrors::OperationsError(
184                ImageOperationsErrors::UnsupportedType(self.name(), bit_type)
185            ));
186        }
187
188        confirm_invariants(image)?;
189
190        self.execute_impl(image)
191            .map_err(<ImageErrors as Into<ImageErrors>>::into)?;
192
193        confirm_invariants(image)?;
194
195        Ok(())
196    }
197    /// Alpha state for which the image operation works in
198    ///
199    /// Most image expect a premultiplied alpha state to work correctly
200    /// this allows one to override the alpha state the image will
201    /// be converted into before carrying out an operation
202    fn alpha_state(&self) -> AlphaState {
203        AlphaState::PreMultiplied
204    }
205
206    /// Clone the image and execute the operation on it, returning
207    /// a new image instead of modifying the existing one
208    ///
209    /// This is provided as a convenience function for when one
210    /// doesn't want to modify the existing image
211    fn clone_and_execute(&self, image: &Image) -> Result<Image, ImageErrors> {
212        let mut c_img = image.clone();
213        self.execute(&mut c_img)?;
214        Ok(c_img)
215    }
216}
217
218/// Confirm that image invariants have been respected across image
219/// operations
220fn confirm_invariants(image: &Image) -> Result<(), ImageErrors> {
221    // Ensure dimensions are correct
222
223    for frame in image.frames_ref() {
224        if frame.channels.len() != image.colorspace().num_components() {
225            {
226                return Err(ImageErrors::GenericString(format!(
227                    "Components mismatch, expected {} channels since image format is {:?}, but found {}",
228                    image.colorspace().num_components(),
229                    image.colorspace(),
230                    frame.channels.len()
231                )));
232            }
233        }
234    }
235
236    let (width, height) = image.dimensions();
237    // check the number of channels match the length
238
239    let expected_length = image.depth().size_of() * width * height;
240
241    for channel in image.channels_ref(true) {
242        if channel.len() != expected_length {
243            return Err(ImageErrors::DimensionsMisMatch(
244                expected_length,
245                channel.len()
246            ));
247        }
248    }
249
250    Ok(())
251}
252
253/// The trait dealing with image encoding and saving
254pub trait EncoderTrait {
255    /// Get the name of the encoder
256    fn name(&self) -> &'static str;
257
258    /// Encode and write to a file
259    ///
260    /// The image pixels are stored internally by the decoder, e.g
261    /// by asking for it during initialization
262    ///
263    /// - Note: Callers should use the [encode] function and not this function
264    /// as that does some necessary conversions for an image to prepare it for encoding
265    ///
266    /// # Arguments
267    /// - image: An image which we are trying to encode.
268    ///
269    /// # Returns
270    /// - `Ok(usize)`: The number of bytes written into `sink`
271    /// in the format [ImageFormat]
272    ///
273    /// - Err : An unrecoverable error occurred
274    ///
275    /// [encode]: EncoderTrait::encode
276    fn encode_inner<T: ZByteWriterTrait>(
277        &mut self, image: &Image, sink: T
278    ) -> Result<usize, ImageErrors>;
279
280    /// Return all colorspaces supported by this encoder.
281    ///
282    /// An encoder should reject any other colorspace and should not try to write
283    /// an unknown colorspace
284    fn supported_colorspaces(&self) -> &'static [ColorSpace];
285
286    /// Encode the actual image into the specified format
287    ///
288    /// This also covers conversion where possible,
289    /// allowing things like bit-depth conversion where possible
290    ///
291    /// After doing some book keeping, this will eventually call `encode_inner` which
292    /// actually carries out the encoding
293    ///
294    /// # Arguments
295    /// - image: The image to encode
296    ///
297    /// # Returns
298    ///
299    /// - `Ok(usize)`: The number of bytes written into `sink`
300    /// in the format [ImageFormat]
301    ///
302    /// - Err : An unrecoverable error occurred
303    ///
304    /// # Note
305    /// The library may clone `image` depending on configurations of the encoder
306    /// e.g to do colorspace conversions or bit-depth conversions, hence it
307    /// is recommended to have the image in a format that can be encoded
308    /// directly to prevent such
309    fn encode<T: ZByteWriterTrait>(
310        &mut self, image: &Image, sink: T
311    ) -> Result<usize, ImageErrors> {
312        // confirm things hold themselves
313        confirm_invariants(image)?;
314
315        // check colorspace is correct.
316        let colorspace = image.colorspace();
317        let supported_colorspaces = self.supported_colorspaces();
318
319        // deal convert bit depths
320        let depth = image.depth();
321
322        if image.is_animated() && !self.supports_animated_images() {
323            warn!("The current image is animated but the encoder ({:?}) doesn't support animated images, this will only encode the first frame",self.name());
324        }
325        if !supported_colorspaces.contains(&colorspace)
326            || !self.supported_bit_depth().contains(&depth)
327            || image.metadata.alpha != NonPreMultiplied
328        {
329            let mut image_clone = image.clone();
330
331            if !supported_colorspaces.contains(&colorspace) {
332                // get default colorspace
333                let default_colorspace = self.default_colorspace(colorspace);
334                let image_format = self.format();
335
336                trace!("Image is in {colorspace:?} colorspace,converting it to {default_colorspace:?} which is the default configured colorspace of {image_format:?}");
337                // try converting  it to a supported colorspace
338                let converter = ColorspaceConv::new(default_colorspace);
339
340                converter.execute(&mut image_clone)?
341            }
342            let image_depth = image.depth();
343
344            if !self.supported_bit_depth().contains(&depth) {
345                trace!(
346                    "Image depth is in {:?}, but {} encoder supports {:?}",
347                    image.depth(),
348                    self.name(),
349                    self.supported_bit_depth()
350                );
351                trace!(
352                    "Converting image to a depth of {:?}",
353                    self.default_depth(image_depth)
354                );
355
356                let depth = Depth::new(self.default_depth(image_depth));
357
358                depth.execute(&mut image_clone)?;
359            }
360
361            // confirm again we didn't mess up
362            confirm_invariants(&image_clone)?;
363
364            self.encode_inner(&image_clone, sink)
365        } else {
366            self.encode_inner(image, sink)
367        }
368    }
369    /// Return the image format for which this
370    /// encoder will encode the format in
371    ///
372    /// # Example
373    ///  Get jpeg encoder format
374    ///-  Requires jpeg feature to work
375    /// ```
376    /// use zune_image::codecs::ImageFormat;
377    /// use zune_image::codecs::jpeg::JpegEncoder;
378    /// use zune_image::traits::EncoderTrait;
379    ///
380    /// let encoder = JpegEncoder::new();
381    /// assert_eq!(encoder.format(),ImageFormat::JPEG);
382    /// ```
383    fn format(&self) -> ImageFormat;
384
385    /// Call `encode` and then store the image
386    /// and format in `EncodeResult`
387    fn encode_to_result(&mut self, image: &Image) -> Result<EncodeResult, ImageErrors> {
388        let mut sink = vec![];
389        let data = self.encode(image, &mut sink)?;
390
391        Ok(EncodeResult {
392            data:   vec![],
393            format: self.format()
394        })
395    }
396    /// Get supported bit-depths for this image
397    ///
398    /// This should return all supported bit depth
399    /// for the encoder
400    fn supported_bit_depth(&self) -> &'static [BitDepth];
401
402    /// Returns the common/expected bit depth for this image
403    ///
404    /// This is used in conjunction with [`supported_bit_depth`]
405    /// for cases where we want to convert the image to a bit depth
406    /// since the image is not in one of the supported image formats
407    ///
408    /// [`supported_bit_depth`]:EncoderTrait::supported_bit_depth
409    fn default_depth(&self, depth: BitDepth) -> BitDepth;
410
411    /// Returns the default colorspace to use when the image
412    /// contains a different colorspace
413    ///
414    /// Default is RGB
415    ///
416    /// # Arguments
417    /// - colorspace: The colorspace the image is currently in
418    fn default_colorspace(&self, _: ColorSpace) -> ColorSpace {
419        ColorSpace::RGB
420    }
421
422    /// Set encoder options for this encoder
423    ///
424    /// This allows one to configure specific settings for an encoder where supported
425    fn set_options(&mut self, _: EncoderOptions) {}
426
427    /// Return true if the encoder can encode multiple image frames as one animated Image.
428    ///
429    /// This returns true if the format and encoder can encode animated images, false otherwise
430    ///
431    /// If false, the encoder will only encode one frame from the image otherwise it will
432    /// encode all frames as a single animated image
433    fn supports_animated_images(&self) -> bool {
434        false
435    }
436}
437
438/// Trait that encapsulates supported
439/// integers which work with the image crates
440pub trait ZuneInts<T> {
441    fn depth() -> BitDepth;
442
443    ///Maximum value for this type
444    ///
445    /// For integers its the maximum value they can hold
446    /// For float values it's 1.0
447    fn max_value() -> T;
448}
449
450impl ZuneInts<u8> for u8 {
451    #[inline(always)]
452    fn depth() -> BitDepth {
453        BitDepth::Eight
454    }
455    #[inline(always)]
456    fn max_value() -> u8 {
457        255
458    }
459}
460
461impl ZuneInts<u16> for u16 {
462    #[inline(always)]
463    fn depth() -> BitDepth {
464        BitDepth::Sixteen
465    }
466    #[inline(always)]
467    fn max_value() -> u16 {
468        u16::MAX
469    }
470}
471
472impl ZuneInts<f32> for f32 {
473    #[inline(always)]
474    fn depth() -> BitDepth {
475        BitDepth::Float32
476    }
477    #[inline(always)]
478    fn max_value() -> f32 {
479        1.0
480    }
481}
482
483/// Trait that encapsulates image decoders that
484/// can write data as raw native endian into
485/// a buffer of u8
486pub trait DecodeInto {
487    type BufferType: Sized;
488
489    /// Decode raw image bytes into a buffer that can
490    /// hold `BufferType` bytes
491    ///
492    /// The rationale is that u8 bytes can alias any type
493    /// and higher bytes offer ways to construct types from
494    /// u8's hence they can be used as a base type
495    fn decode_into(&mut self, buffer: &mut [Self::BufferType]) -> Result<(), ImageErrors>;
496
497    /// Minimum buffer length which is needed to decode this image
498    ///
499    /// This may call `decode_headers` for the image routine to fetch the
500    /// expected output size.
501    fn decode_output_buffer_size(&mut self) -> Result<usize, ImageErrors>;
502}
503/// Convert something into an image by consuming it
504pub trait IntoImage {
505    /// Consumes this and returns an image
506    fn into_image(&mut self) -> Result<Image, ImageErrors>;
507}