pic_scale/colors/
linear_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    linear_u8_to_rgb, linear_u8_to_rgba, rgb_to_linear_u8, rgba_to_linear_u8, TransferFunction,
32};
33
34use crate::pic_scale_error::PicScaleError;
35use crate::scaler::Scaling;
36use crate::support::check_image_size_overflow;
37use crate::{ImageStore, ImageStoreMut, ResamplingFunction, Scaler, ThreadingPolicy};
38
39#[derive(Debug, Copy, Clone)]
40/// Linearize image into u8, scale and then convert it back. It's much faster than scale in f32, however involves some precision loss
41pub struct LinearApproxScaler {
42    pub(crate) scaler: Scaler,
43    pub(crate) transfer_function: TransferFunction,
44}
45
46impl LinearApproxScaler {
47    /// Creates new instance with sRGB transfer function
48    pub fn new(filter: ResamplingFunction) -> Self {
49        LinearApproxScaler {
50            scaler: Scaler::new(filter),
51            transfer_function: TransferFunction::Srgb,
52        }
53    }
54
55    /// Creates new instance with provided transfer function
56    pub fn new_with_transfer(
57        filter: ResamplingFunction,
58        transfer_function: TransferFunction,
59    ) -> Self {
60        LinearApproxScaler {
61            scaler: Scaler::new(filter),
62            transfer_function,
63        }
64    }
65}
66
67impl Scaling for LinearApproxScaler {
68    fn set_threading_policy(&mut self, threading_policy: ThreadingPolicy) {
69        self.scaler.threading_policy = threading_policy;
70    }
71
72    fn resize_cbcr8<'a>(
73        &'a self,
74        _: &ImageStore<'a, u8, 2>,
75        _: &mut ImageStoreMut<'a, u8, 2>,
76    ) -> Result<(), PicScaleError> {
77        unimplemented!()
78    }
79
80    fn resize_rgb<'a>(
81        &self,
82        store: &ImageStore<'a, u8, 3>,
83        into: &mut ImageStoreMut<'a, u8, 3>,
84    ) -> Result<(), PicScaleError> {
85        let new_size = into.get_size();
86        into.validate()?;
87        store.validate()?;
88        if store.width == 0 || store.height == 0 || new_size.width == 0 || new_size.height == 0 {
89            return Err(PicScaleError::ZeroImageDimensions);
90        }
91
92        if check_image_size_overflow(store.width, store.height, store.channels) {
93            return Err(PicScaleError::SourceImageIsTooLarge);
94        }
95
96        if check_image_size_overflow(new_size.width, new_size.height, store.channels) {
97            return Err(PicScaleError::DestinationImageIsTooLarge);
98        }
99
100        if store.width == new_size.width && store.height == new_size.height {
101            store.copied_to_mut(into);
102            return Ok(());
103        }
104
105        const COMPONENTS: usize = 3;
106
107        let mut target_vertical = vec![u8::default(); store.width * store.height * COMPONENTS];
108
109        let mut lab_store = ImageStoreMut::<u8, COMPONENTS>::from_slice(
110            &mut target_vertical,
111            store.width,
112            store.height,
113        )?;
114        lab_store.bit_depth = into.bit_depth;
115
116        let lab_stride =
117            lab_store.width as u32 * COMPONENTS as u32 * std::mem::size_of::<u8>() as u32;
118
119        rgb_to_linear_u8(
120            store.buffer.as_ref(),
121            store.width as u32 * COMPONENTS as u32,
122            lab_store.buffer.borrow_mut(),
123            lab_stride,
124            lab_store.width as u32,
125            lab_store.height as u32,
126            self.transfer_function,
127        );
128
129        let new_immutable_store = ImageStore::<u8, COMPONENTS> {
130            buffer: std::borrow::Cow::Owned(target_vertical),
131            channels: COMPONENTS,
132            width: store.width,
133            height: store.height,
134            stride: store.width * COMPONENTS,
135            bit_depth: into.bit_depth,
136        };
137
138        let mut new_store = ImageStoreMut::<u8, COMPONENTS>::alloc(into.width, into.height);
139
140        self.scaler
141            .resize_rgb(&new_immutable_store, &mut new_store)?;
142        let new_lab_stride =
143            new_store.width as u32 * COMPONENTS as u32 * std::mem::size_of::<u8>() as u32;
144        linear_u8_to_rgb(
145            new_store.buffer.borrow(),
146            new_lab_stride,
147            into.buffer.borrow_mut(),
148            into.width as u32 * COMPONENTS as u32,
149            new_store.width as u32,
150            new_store.height as u32,
151            self.transfer_function,
152        );
153        Ok(())
154    }
155
156    fn resize_rgba<'a>(
157        &self,
158        store: &ImageStore<'a, u8, 4>,
159        into: &mut ImageStoreMut<'a, u8, 4>,
160        premultiply_alpha: bool,
161    ) -> Result<(), PicScaleError> {
162        let new_size = into.get_size();
163        into.validate()?;
164        store.validate()?;
165        if store.width == 0 || store.height == 0 || new_size.width == 0 || new_size.height == 0 {
166            return Err(PicScaleError::ZeroImageDimensions);
167        }
168
169        if check_image_size_overflow(store.width, store.height, store.channels) {
170            return Err(PicScaleError::SourceImageIsTooLarge);
171        }
172
173        if check_image_size_overflow(new_size.width, new_size.height, store.channels) {
174            return Err(PicScaleError::DestinationImageIsTooLarge);
175        }
176
177        if store.width == new_size.width && store.height == new_size.height {
178            store.copied_to_mut(into);
179            return Ok(());
180        }
181
182        const COMPONENTS: usize = 4;
183
184        let mut target_vertical = vec![u8::default(); store.width * store.height * COMPONENTS];
185
186        let mut lab_store = ImageStoreMut::<u8, COMPONENTS>::from_slice(
187            &mut target_vertical,
188            store.width,
189            store.height,
190        )?;
191        lab_store.bit_depth = into.bit_depth;
192
193        let lab_stride =
194            lab_store.width as u32 * COMPONENTS as u32 * std::mem::size_of::<u8>() as u32;
195
196        rgba_to_linear_u8(
197            store.buffer.as_ref(),
198            store.width as u32 * COMPONENTS as u32,
199            lab_store.buffer.borrow_mut(),
200            lab_stride,
201            lab_store.width as u32,
202            lab_store.height as u32,
203            self.transfer_function,
204        );
205
206        let new_immutable_store = ImageStore::<u8, COMPONENTS> {
207            buffer: std::borrow::Cow::Owned(target_vertical),
208            channels: COMPONENTS,
209            width: store.width,
210            height: store.height,
211            stride: store.width * COMPONENTS,
212            bit_depth: into.bit_depth,
213        };
214
215        let mut new_store = ImageStoreMut::<u8, COMPONENTS>::alloc(into.width, into.height);
216
217        self.scaler
218            .resize_rgba(&new_immutable_store, &mut new_store, premultiply_alpha)?;
219        let new_lab_stride =
220            new_store.width as u32 * COMPONENTS as u32 * std::mem::size_of::<u8>() as u32;
221        linear_u8_to_rgba(
222            new_store.buffer.borrow(),
223            new_lab_stride,
224            into.buffer.borrow_mut(),
225            into.width as u32 * COMPONENTS as u32,
226            new_store.width as u32,
227            new_store.height as u32,
228            self.transfer_function,
229        );
230        Ok(())
231    }
232}