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    luv_to_rgb, luv_with_alpha_to_rgba, rgb_to_luv, rgba_to_luv_with_alpha, TransferFunction,
32    SRGB_TO_XYZ_D65, XYZ_TO_SRGB_D65,
33};
34
35use crate::pic_scale_error::PicScaleError;
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_cbcr8<'a>(
60        &'a self,
61        _: &ImageStore<'a, u8, 2>,
62        _: &mut ImageStoreMut<'a, u8, 2>,
63    ) -> Result<(), PicScaleError> {
64        unimplemented!()
65    }
66
67    fn resize_rgb<'a>(
68        &self,
69        store: &ImageStore<'a, u8, 3>,
70        into: &mut ImageStoreMut<'a, u8, 3>,
71    ) -> Result<(), PicScaleError> {
72        let new_size = into.get_size();
73        into.validate()?;
74        store.validate()?;
75        if store.width == 0 || store.height == 0 || new_size.width == 0 || new_size.height == 0 {
76            return Err(PicScaleError::ZeroImageDimensions);
77        }
78
79        if check_image_size_overflow(store.width, store.height, store.channels) {
80            return Err(PicScaleError::SourceImageIsTooLarge);
81        }
82
83        if check_image_size_overflow(new_size.width, new_size.height, store.channels) {
84            return Err(PicScaleError::DestinationImageIsTooLarge);
85        }
86
87        if store.width == new_size.width && store.height == new_size.height {
88            store.copied_to_mut(into);
89            return Ok(());
90        }
91
92        const COMPONENTS: usize = 3;
93
94        let mut target_vertical = vec![f32::default(); store.width * store.height * COMPONENTS];
95
96        let mut lab_store = ImageStoreMut::<f32, COMPONENTS>::from_slice(
97            &mut target_vertical,
98            store.width,
99            store.height,
100        )?;
101        lab_store.bit_depth = into.bit_depth;
102
103        let lab_stride =
104            lab_store.width as u32 * COMPONENTS as u32 * std::mem::size_of::<f32>() as u32;
105        rgb_to_luv(
106            store.buffer.as_ref(),
107            store.width as u32 * COMPONENTS as u32,
108            lab_store.buffer.borrow_mut(),
109            lab_stride,
110            lab_store.width as u32,
111            lab_store.height as u32,
112            &SRGB_TO_XYZ_D65,
113            TransferFunction::Srgb,
114        );
115
116        let new_immutable_store = ImageStore::<f32, COMPONENTS> {
117            buffer: std::borrow::Cow::Owned(target_vertical),
118            channels: COMPONENTS,
119            width: store.width,
120            height: store.height,
121            stride: store.width * COMPONENTS,
122            bit_depth: into.bit_depth,
123        };
124
125        let mut new_store = ImageStoreMut::<f32, COMPONENTS>::alloc(into.width, into.height);
126        self.scaler
127            .resize_rgb_f32(&new_immutable_store, &mut new_store)?;
128
129        let new_lab_stride =
130            new_store.width as u32 * COMPONENTS as u32 * std::mem::size_of::<f32>() as u32;
131
132        luv_to_rgb(
133            new_store.buffer.borrow(),
134            new_lab_stride,
135            into.buffer.borrow_mut(),
136            into.width as u32 * COMPONENTS as u32,
137            new_store.width as u32,
138            new_store.height as u32,
139            &XYZ_TO_SRGB_D65,
140            TransferFunction::Srgb,
141        );
142        Ok(())
143    }
144
145    fn resize_rgba<'a>(
146        &'a self,
147        store: &ImageStore<'a, u8, 4>,
148        into: &mut ImageStoreMut<'a, u8, 4>,
149        premultiply_alpha: bool,
150    ) -> Result<(), PicScaleError> {
151        let new_size = into.get_size();
152        into.validate()?;
153        store.validate()?;
154        if store.width == 0 || store.height == 0 || new_size.width == 0 || new_size.height == 0 {
155            return Err(PicScaleError::ZeroImageDimensions);
156        }
157
158        if check_image_size_overflow(store.width, store.height, store.channels) {
159            return Err(PicScaleError::SourceImageIsTooLarge);
160        }
161
162        if check_image_size_overflow(new_size.width, new_size.height, store.channels) {
163            return Err(PicScaleError::DestinationImageIsTooLarge);
164        }
165
166        if store.width == new_size.width && store.height == new_size.height {
167            store.copied_to_mut(into);
168            return Ok(());
169        }
170
171        const COMPONENTS: usize = 4;
172
173        let mut target_vertical = vec![f32::default(); store.width * store.height * COMPONENTS];
174
175        let mut lab_store = ImageStoreMut::<f32, COMPONENTS>::from_slice(
176            &mut target_vertical,
177            store.width,
178            store.height,
179        )?;
180        lab_store.bit_depth = into.bit_depth;
181
182        let lab_stride =
183            lab_store.width as u32 * COMPONENTS as u32 * std::mem::size_of::<f32>() as u32;
184        rgba_to_luv_with_alpha(
185            store.buffer.as_ref(),
186            store.width as u32 * COMPONENTS as u32,
187            lab_store.buffer.borrow_mut(),
188            lab_stride,
189            lab_store.width as u32,
190            lab_store.height as u32,
191            &SRGB_TO_XYZ_D65,
192            TransferFunction::Srgb,
193        );
194
195        let new_immutable_store = ImageStore::<f32, COMPONENTS> {
196            buffer: std::borrow::Cow::Owned(target_vertical),
197            channels: COMPONENTS,
198            width: store.width,
199            height: store.height,
200            stride: store.width * COMPONENTS,
201            bit_depth: into.bit_depth,
202        };
203
204        let mut new_store = ImageStoreMut::<f32, COMPONENTS>::alloc(into.width, into.height);
205        self.scaler
206            .resize_rgba_f32(&new_immutable_store, &mut new_store, premultiply_alpha)?;
207
208        let new_lab_stride =
209            new_store.width as u32 * COMPONENTS as u32 * std::mem::size_of::<f32>() as u32;
210
211        luv_with_alpha_to_rgba(
212            new_store.buffer.borrow(),
213            new_lab_stride,
214            into.buffer.borrow_mut(),
215            into.width as u32 * COMPONENTS as u32,
216            new_store.width as u32,
217            new_store.height as u32,
218            &XYZ_TO_SRGB_D65,
219            TransferFunction::Srgb,
220        );
221        Ok(())
222    }
223}