zune_image/core_filters/
depth.rs

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//! Depth conversion routines
9//!
10//! This helps to convert from one bit depth to another
11//!
12//! zune-image associates bit depths with native representations
13//!, the following mapping indicates the types and range
14//!
15//!|BitDepth         |native type    |range      |
16//!|-----------------|---------------|-----------|
17//!|BitDepth::Eight  | [`u8`]        |0   - 255  |
18//!|BitDepth::Sixteen| [`u16`]       |0   - 65535|
19//!|BitDepth::F32    | [`f32`]       |0.0 - 1.0  |
20//!  
21//!
22//! Conversions are supported from any depth to another, both
23//! from and to a depth.
24//!
25//! The library automatically rescales the pixels during conversion, i.e
26//! when moving from `BitDepth::Eight` to `BitDepth::F32`, the library will automatically
27//! divide all pixels by `255.0` after converting them to f32's
28//!
29use zune_core::bit_depth::{BitDepth, BitType};
30use zune_core::log::trace;
31
32use crate::channel::Channel;
33use crate::errors::ImageErrors;
34use crate::image::Image;
35use crate::traits::OperationsTrait;
36
37/// Convert an image depth from u16 to u8
38///
39/// This is a simple division depth rescaling, we simply rescale the image pixels
40/// mapping the brightest image pixel (e.g 65535 for 16 bit images) to 255 and darkest to
41/// zero, squeezing everything else in between.
42///
43/// # Arguments
44///  - `from`: A reference to pixels in 16 bit format
45///  - `to`: A mutable reference to pixels in 8 bit format where we will
46/// write our pixels
47/// - `max_value`: Maximum value we expect this pixel to store.
48#[allow(clippy::cast_sign_loss, clippy::cast_possible_truncation)]
49pub(crate) fn depth_u16_to_u8(from: &[u16], to: &mut [u8], max_value: u16) {
50    if max_value == u16::MAX {
51        // divide by 257, this clamps it to 0..255
52        for (old, new) in from.iter().zip(to.iter_mut()) {
53            let new_val = (old / 257) as u8;
54            *new = new_val;
55        }
56    } else {
57        //okay do scaling
58        let max = 1.0 / f32::from(max_value);
59        let scale = 255.0;
60
61        for (old, new) in from.iter().zip(to.iter_mut()) {
62            let new_val = ((f32::from(*old) * max) * scale) as u8;
63            *new = new_val;
64        }
65    }
66}
67
68/// Convert an image depth from u8 to u16
69///
70/// This is a simple multiplication depth rescaling, we simply rescale the image pixels
71/// mapping the brightest image pixel (e.g 255 for 16 bit images) to 65535(16 bit) and darkest to
72/// zero, stretching everything else in between.
73///
74/// # Arguments
75///  - `from`: A reference to pixels in 16 bit format
76///  - `to`: A mutable reference to pixels in 8 bit format where we will
77/// write our pixels
78/// - `max_value`: Maximum value we expect this pixel to store.
79#[allow(clippy::cast_sign_loss, clippy::cast_possible_truncation)]
80pub(crate) fn depth_u8_to_u16(from: &[u8], to: &mut [u16], max_value: u16) {
81    // okay do scaling
82    let max = 1.0 / 255.0;
83    let scale = f32::from(max_value);
84
85    for (old, new) in from.iter().zip(to.iter_mut()) {
86        let new_val = ((f32::from(*old) * max) * scale) as u16;
87        *new = new_val;
88    }
89}
90
91/// Change the image's bit depth from it's initial
92/// value to the one specified by this operation.
93#[derive(Copy, Clone)]
94pub struct Depth {
95    depth: BitDepth
96}
97
98impl Depth {
99    pub fn new(depth: BitDepth) -> Depth {
100        Depth { depth }
101    }
102}
103
104impl OperationsTrait for Depth {
105    fn name(&self) -> &'static str {
106        "Depth"
107    }
108
109    fn execute_impl(&self, image: &mut Image) -> Result<(), ImageErrors> {
110        let image_depth = image.depth();
111
112        if image_depth == self.depth {
113            trace!("Image depth already matches requested, no-op");
114            return Ok(());
115        }
116
117        for channel in image.channels_mut(false) {
118            match (image_depth, self.depth) {
119                (BitDepth::Eight, BitDepth::Sixteen) => {
120                    let old_data = channel.reinterpret_as().unwrap();
121                    let mut new_channel = Channel::new_with_length::<u16>(old_data.len() * 2);
122
123                    let new_channel_raw = new_channel.reinterpret_as_mut().unwrap();
124
125                    depth_u8_to_u16(old_data, new_channel_raw, self.depth.max_value());
126
127                    *channel = new_channel;
128                }
129
130                (BitDepth::Sixteen, BitDepth::Eight) => {
131                    let old_data = channel.reinterpret_as::<u16>().unwrap();
132                    let mut new_channel = Channel::new_with_length::<u8>(channel.len() / 2);
133
134                    let new_channel_raw = new_channel.reinterpret_as_mut().unwrap();
135
136                    depth_u16_to_u8(old_data, new_channel_raw, image_depth.max_value());
137
138                    *channel = new_channel;
139                }
140                (BitDepth::Float32, BitDepth::Eight) => {
141                    let old_data = channel.reinterpret_as::<f32>().unwrap();
142                    let mut new_channel = Channel::new_with_length::<u8>(channel.len() / 4);
143
144                    let new_channel_raw = new_channel.reinterpret_as_mut::<u8>().unwrap();
145
146                    // scale by multiplying with 255
147                    for (old_chan, new_chan) in old_data.iter().zip(new_channel_raw.iter_mut()) {
148                        *new_chan = (255.0 * old_chan).clamp(0.0, 255.0) as u8;
149                    }
150
151                    *channel = new_channel;
152                }
153                (BitDepth::Float32, BitDepth::Sixteen) => {
154                    let old_data = channel.reinterpret_as::<f32>().unwrap();
155                    let mut new_channel = Channel::new_with_length::<u16>(channel.len() / 2);
156
157                    let new_channel_raw = new_channel.reinterpret_as_mut::<u16>().unwrap();
158
159                    // scale by multiplying with 65535
160                    for (old_chan, new_chan) in old_data.iter().zip(new_channel_raw.iter_mut()) {
161                        *new_chan = (65535.0 * old_chan).clamp(0.0, 65535.0) as u16;
162                    }
163
164                    *channel = new_channel;
165                }
166                (BitDepth::Eight, BitDepth::Float32) => {
167                    let old_data = channel.reinterpret_as::<u8>().unwrap();
168                    let mut new_channel = Channel::new_with_length::<f32>(old_data.len() * 4);
169
170                    let new_channel_raw = new_channel.reinterpret_as_mut::<f32>().unwrap();
171
172                    // scale by dividing with 255
173                    let recip = 1.0 / 255.0;
174                    for (old_chan, new_chan) in old_data.iter().zip(new_channel_raw.iter_mut()) {
175                        *new_chan = f32::from(*old_chan) * recip;
176                    }
177
178                    *channel = new_channel;
179                }
180                (BitDepth::Sixteen, BitDepth::Float32) => {
181                    let old_data = channel.reinterpret_as::<u16>().unwrap();
182                    let mut new_channel = Channel::new_with_length::<f32>(old_data.len() * 4);
183
184                    let new_channel_raw = new_channel.reinterpret_as_mut::<f32>().unwrap();
185
186                    // scale by dividing with 65535
187                    let recip = 1.0 / 65535.0;
188
189                    for (old_chan, new_chan) in old_data.iter().zip(new_channel_raw.iter_mut()) {
190                        *new_chan = f32::from(*old_chan) * recip;
191                    }
192
193                    *channel = new_channel;
194                }
195
196                (_, _) => {
197                    let msg = format!(
198                        "Unknown depth conversion from {:?} to {:?}",
199                        image_depth, self.depth
200                    );
201
202                    return Err(ImageErrors::GenericString(msg));
203                }
204            }
205        }
206        trace!("Image depth changed to {:?}", self.depth);
207
208        image.set_depth(self.depth);
209
210        Ok(())
211    }
212    fn supported_types(&self) -> &'static [BitType] {
213        &[BitType::U8, BitType::U16, BitType::F32]
214    }
215}