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