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}