pic_scale/colors/
luv_scaler.rs

1/*
2 * Copyright (c) Radzivon Bartoshyk. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without modification,
5 * are permitted provided that the following conditions are met:
6 *
7 * 1.  Redistributions of source code must retain the above copyright notice, this
8 * list of conditions and the following disclaimer.
9 *
10 * 2.  Redistributions in binary form must reproduce the above copyright notice,
11 * this list of conditions and the following disclaimer in the documentation
12 * and/or other materials provided with the distribution.
13 *
14 * 3.  Neither the name of the copyright holder nor the names of its
15 * contributors may be used to endorse or promote products derived from
16 * this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
25 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
26 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30use colorutils_rs::{
31    SRGB_TO_XYZ_D65, TransferFunction, XYZ_TO_SRGB_D65, luv_to_rgb, luv_with_alpha_to_rgba,
32    rgb_to_luv, rgba_to_luv_with_alpha,
33};
34
35use crate::pic_scale_error::{PicScaleError, try_vec};
36use crate::scaler::ScalingF32;
37use crate::support::check_image_size_overflow;
38use crate::{ImageStore, ImageStoreMut, ResamplingFunction, Scaler, Scaling, ThreadingPolicy};
39
40#[derive(Debug, Copy, Clone)]
41/// Converts image to *CIE LUV* components scales it and convert back
42pub struct LuvScaler {
43    pub(crate) scaler: Scaler,
44}
45
46impl LuvScaler {
47    pub fn new(filter: ResamplingFunction) -> Self {
48        LuvScaler {
49            scaler: Scaler::new(filter),
50        }
51    }
52}
53
54impl Scaling for LuvScaler {
55    fn set_threading_policy(&mut self, threading_policy: ThreadingPolicy) {
56        self.scaler.set_threading_policy(threading_policy)
57    }
58
59    fn resize_plane<'a>(
60        &'a self,
61        _: &ImageStore<'a, u8, 1>,
62        _: &mut ImageStoreMut<'a, u8, 1>,
63    ) -> Result<(), PicScaleError> {
64        unimplemented!()
65    }
66
67    fn resize_cbcr8<'a>(
68        &'a self,
69        _: &ImageStore<'a, u8, 2>,
70        _: &mut ImageStoreMut<'a, u8, 2>,
71    ) -> Result<(), PicScaleError> {
72        unimplemented!()
73    }
74
75    fn resize_gray_alpha<'a>(
76        &'a self,
77        _: &ImageStore<'a, u8, 2>,
78        _: &mut ImageStoreMut<'a, u8, 2>,
79        _: bool,
80    ) -> Result<(), PicScaleError> {
81        unimplemented!()
82    }
83
84    fn resize_rgb<'a>(
85        &self,
86        store: &ImageStore<'a, u8, 3>,
87        into: &mut ImageStoreMut<'a, u8, 3>,
88    ) -> Result<(), PicScaleError> {
89        let new_size = into.get_size();
90        into.validate()?;
91        store.validate()?;
92        if store.width == 0 || store.height == 0 || new_size.width == 0 || new_size.height == 0 {
93            return Err(PicScaleError::ZeroImageDimensions);
94        }
95
96        if check_image_size_overflow(store.width, store.height, store.channels) {
97            return Err(PicScaleError::SourceImageIsTooLarge);
98        }
99
100        if check_image_size_overflow(new_size.width, new_size.height, store.channels) {
101            return Err(PicScaleError::DestinationImageIsTooLarge);
102        }
103
104        if store.width == new_size.width && store.height == new_size.height {
105            store.copied_to_mut(into);
106            return Ok(());
107        }
108
109        const CN: usize = 3;
110
111        let mut target_vertical = try_vec![f32::default(); store.width * store.height * CN];
112
113        let mut lab_store =
114            ImageStoreMut::<f32, CN>::from_slice(&mut target_vertical, store.width, store.height)?;
115        lab_store.bit_depth = into.bit_depth;
116
117        let lab_stride = lab_store.width as u32 * CN as u32 * size_of::<f32>() as u32;
118        rgb_to_luv(
119            store.buffer.as_ref(),
120            store.width as u32 * CN as u32,
121            lab_store.buffer.borrow_mut(),
122            lab_stride,
123            lab_store.width as u32,
124            lab_store.height as u32,
125            &SRGB_TO_XYZ_D65,
126            TransferFunction::Srgb,
127        );
128
129        let new_immutable_store = ImageStore::<f32, CN> {
130            buffer: std::borrow::Cow::Owned(target_vertical),
131            channels: CN,
132            width: store.width,
133            height: store.height,
134            stride: store.width * CN,
135            bit_depth: into.bit_depth,
136        };
137
138        let mut new_store = ImageStoreMut::<f32, CN>::try_alloc(into.width, into.height)?;
139        self.scaler
140            .resize_rgb_f32(&new_immutable_store, &mut new_store)?;
141
142        let new_lab_stride = new_store.width as u32 * CN as u32 * size_of::<f32>() as u32;
143
144        luv_to_rgb(
145            new_store.buffer.borrow(),
146            new_lab_stride,
147            into.buffer.borrow_mut(),
148            into.width as u32 * CN as u32,
149            new_store.width as u32,
150            new_store.height as u32,
151            &XYZ_TO_SRGB_D65,
152            TransferFunction::Srgb,
153        );
154        Ok(())
155    }
156
157    fn resize_rgba<'a>(
158        &'a self,
159        store: &ImageStore<'a, u8, 4>,
160        into: &mut ImageStoreMut<'a, u8, 4>,
161        premultiply_alpha: bool,
162    ) -> Result<(), PicScaleError> {
163        let new_size = into.get_size();
164        into.validate()?;
165        store.validate()?;
166        if store.width == 0 || store.height == 0 || new_size.width == 0 || new_size.height == 0 {
167            return Err(PicScaleError::ZeroImageDimensions);
168        }
169
170        if check_image_size_overflow(store.width, store.height, store.channels) {
171            return Err(PicScaleError::SourceImageIsTooLarge);
172        }
173
174        if check_image_size_overflow(new_size.width, new_size.height, store.channels) {
175            return Err(PicScaleError::DestinationImageIsTooLarge);
176        }
177
178        if store.width == new_size.width && store.height == new_size.height {
179            store.copied_to_mut(into);
180            return Ok(());
181        }
182
183        const CN: usize = 4;
184
185        let mut target_vertical = try_vec![f32::default(); store.width * store.height * CN];
186
187        let mut lab_store =
188            ImageStoreMut::<f32, CN>::from_slice(&mut target_vertical, store.width, store.height)?;
189        lab_store.bit_depth = into.bit_depth;
190
191        let lab_stride = lab_store.width as u32 * CN as u32 * size_of::<f32>() as u32;
192        rgba_to_luv_with_alpha(
193            store.buffer.as_ref(),
194            store.width as u32 * CN as u32,
195            lab_store.buffer.borrow_mut(),
196            lab_stride,
197            lab_store.width as u32,
198            lab_store.height as u32,
199            &SRGB_TO_XYZ_D65,
200            TransferFunction::Srgb,
201        );
202
203        let new_immutable_store = ImageStore::<f32, CN> {
204            buffer: std::borrow::Cow::Owned(target_vertical),
205            channels: CN,
206            width: store.width,
207            height: store.height,
208            stride: store.width * CN,
209            bit_depth: into.bit_depth,
210        };
211
212        let mut new_store = ImageStoreMut::<f32, CN>::try_alloc(into.width, into.height)?;
213        self.scaler
214            .resize_rgba_f32(&new_immutable_store, &mut new_store, premultiply_alpha)?;
215
216        let new_lab_stride = new_store.width as u32 * CN as u32 * size_of::<f32>() as u32;
217
218        luv_with_alpha_to_rgba(
219            new_store.buffer.borrow(),
220            new_lab_stride,
221            into.buffer.borrow_mut(),
222            into.width as u32 * CN as u32,
223            new_store.width as u32,
224            new_store.height as u32,
225            &XYZ_TO_SRGB_D65,
226            TransferFunction::Srgb,
227        );
228        Ok(())
229    }
230}