| 1 | /* |
| 2 | * Copyright (c) 2023. |
| 3 | * |
| 4 | * This software is free software; |
| 5 | * |
| 6 | * You can redistribute it or modify it under terms of the MIT, Apache License or Zlib license |
| 7 | */ |
| 8 | |
| 9 | #![allow ( |
| 10 | clippy::many_single_char_names, |
| 11 | clippy::similar_names, |
| 12 | clippy::cast_possible_truncation, |
| 13 | clippy::cast_sign_loss, |
| 14 | clippy::cast_possible_wrap, |
| 15 | clippy::too_many_arguments, |
| 16 | clippy::doc_markdown |
| 17 | )] |
| 18 | |
| 19 | //! Color space conversion routines |
| 20 | //! |
| 21 | //! This files exposes functions to convert one colorspace to another in a jpeg |
| 22 | //! image |
| 23 | //! |
| 24 | //! Currently supported conversions are |
| 25 | //! |
| 26 | //! - `YCbCr` to `RGB,RGBA,GRAYSCALE,RGBX`. |
| 27 | //! |
| 28 | //! |
| 29 | //! Hey there, if your reading this it means you probably need something, so let me help you. |
| 30 | //! |
| 31 | //! There are 3 supported cpu extensions here. |
| 32 | //! 1. Scalar |
| 33 | //! 2. SSE |
| 34 | //! 3. AVX |
| 35 | //! |
| 36 | //! There are two types of the color convert functions |
| 37 | //! |
| 38 | //! 1. Acts on 16 pixels. |
| 39 | //! 2. Acts on 8 pixels. |
| 40 | //! |
| 41 | //! The reason for this is because when implementing the AVX part it occurred to me that we can actually |
| 42 | //! do better and process 2 MCU's if we change IDCT return type to be `i16's`, since a lot of |
| 43 | //! CPU's these days support AVX extensions, it becomes nice if we optimize for that path , |
| 44 | //! therefore AVX routines can process 16 pixels directly and SSE and Scalar just compensate. |
| 45 | //! |
| 46 | //! By compensating, I mean I wrote the 16 pixels version operating on the 8 pixel version twice. |
| 47 | //! |
| 48 | //! Therefore if your looking to optimize some routines, probably start there. |
| 49 | |
| 50 | pub use scalar::ycbcr_to_grayscale; |
| 51 | use zune_core::colorspace::ColorSpace; |
| 52 | use zune_core::options::DecoderOptions; |
| 53 | |
| 54 | #[cfg (any(target_arch = "x86" , target_arch = "x86_64" ))] |
| 55 | #[cfg (feature = "x86" )] |
| 56 | pub use crate::color_convert::avx::{ycbcr_to_rgb_avx2, ycbcr_to_rgba_avx2}; |
| 57 | use crate::decoder::ColorConvert16Ptr; |
| 58 | |
| 59 | mod avx; |
| 60 | mod scalar; |
| 61 | #[allow (unused_variables)] |
| 62 | pub fn choose_ycbcr_to_rgb_convert_func( |
| 63 | type_need: ColorSpace, options: &DecoderOptions |
| 64 | ) -> Option<ColorConvert16Ptr> { |
| 65 | #[cfg (any(target_arch = "x86" , target_arch = "x86_64" ))] |
| 66 | #[cfg (feature = "x86" )] |
| 67 | { |
| 68 | use zune_core::log::debug; |
| 69 | if options.use_avx2() { |
| 70 | debug!("Using AVX optimised color conversion functions" ); |
| 71 | |
| 72 | // I believe avx2 means sse4 is also available |
| 73 | // match colorspace |
| 74 | match type_need { |
| 75 | ColorSpace::RGB => return Some(ycbcr_to_rgb_avx2), |
| 76 | ColorSpace::RGBA => return Some(ycbcr_to_rgba_avx2), |
| 77 | _ => () // fall through to scalar, which has more types |
| 78 | }; |
| 79 | } |
| 80 | } |
| 81 | // when there is no x86 or we haven't returned by here, resort to scalar |
| 82 | return match type_need { |
| 83 | ColorSpace::RGB => Some(scalar::ycbcr_to_rgb_inner_16_scalar::<false>), |
| 84 | ColorSpace::RGBA => Some(scalar::ycbcr_to_rgba_inner_16_scalar::<false>), |
| 85 | ColorSpace::BGRA => Some(scalar::ycbcr_to_rgba_inner_16_scalar::<true>), |
| 86 | ColorSpace::BGR => Some(scalar::ycbcr_to_rgb_inner_16_scalar::<true>), |
| 87 | _ => None |
| 88 | }; |
| 89 | } |
| 90 | |