Skip to main content

pic_scale/colors/
linear_precise_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 */
29use crate::colors::common_splitter::{SplitPlanInterceptor, Splitter};
30use crate::mixed_storage::CpuRound;
31use crate::plan::Resampling;
32use crate::validation::PicScaleError;
33use crate::{ImageSize, ImageStore, ImageStoreMut, ResamplingFunction, Scaler, ThreadingPolicy};
34use colorutils_rs::TransferFunction;
35use std::sync::Arc;
36
37#[derive(Debug, Copy, Clone)]
38/// Converts image to linear f32 components scales it and convert back.
39///
40/// This is more precise and slower than scaling in linear fixed point.
41///
42/// This is an expensive method, and its precision might not be required,
43/// consider to use [LinearApproxScaler] instead
44pub struct LinearScaler {
45    pub(crate) scaler: Scaler,
46    pub(crate) transfer_function: TransferFunction,
47}
48
49impl LinearScaler {
50    /// Creates new instance with sRGB transfer function
51    pub fn new(filter: ResamplingFunction) -> Self {
52        LinearScaler {
53            scaler: Scaler::new(filter),
54            transfer_function: TransferFunction::Srgb,
55        }
56    }
57
58    /// Creates new instance with requested transfer function
59    pub fn new_with_transfer(
60        filter: ResamplingFunction,
61        transfer_function: TransferFunction,
62    ) -> Self {
63        LinearScaler {
64            scaler: Scaler::new(filter),
65            transfer_function,
66        }
67    }
68}
69
70struct Common8BitSplitter<const N: usize> {
71    linearization: Box<[f32; 256]>,
72    gamma: Box<[u8; 65536]>,
73    has_alpha: bool,
74}
75
76impl<const N: usize> Splitter<u8, f32, N> for Common8BitSplitter<N> {
77    fn split(&self, from: &ImageStore<'_, u8, N>, into: &mut ImageStoreMut<'_, f32, N>) {
78        const S: f32 = 1. / 255.;
79        if N == 4 {
80            for (src, dst) in from
81                .as_bytes()
82                .chunks_exact(4)
83                .zip(into.buffer.borrow_mut().chunks_exact_mut(4))
84            {
85                dst[0] = self.linearization[src[0] as usize];
86                dst[1] = self.linearization[src[1] as usize];
87                dst[2] = self.linearization[src[2] as usize];
88                dst[3] = src[3] as f32 * S;
89            }
90        } else if N == 2 && self.has_alpha {
91            for (src, dst) in from
92                .as_bytes()
93                .chunks_exact(2)
94                .zip(into.buffer.borrow_mut().chunks_exact_mut(2))
95            {
96                dst[0] = self.linearization[src[0] as usize];
97                dst[1] = src[1] as f32 * S;
98            }
99        } else {
100            for (&src, dst) in from.as_bytes().iter().zip(into.buffer.borrow_mut()) {
101                *dst = self.linearization[src as usize];
102            }
103        }
104    }
105
106    fn merge(&self, from: &ImageStore<'_, f32, N>, into: &mut ImageStoreMut<'_, u8, N>) {
107        if N == 4 {
108            for (src, dst) in from
109                .as_bytes()
110                .chunks_exact(4)
111                .zip(into.buffer.borrow_mut().chunks_exact_mut(4))
112            {
113                let v0 = (src[0] * 65535.).cpu_round() as u16;
114                let v1 = (src[1] * 65535.).cpu_round() as u16;
115                let v2 = (src[2] * 65535.).cpu_round() as u16;
116
117                dst[0] = self.gamma[v0 as usize];
118                dst[1] = self.gamma[v1 as usize];
119                dst[2] = self.gamma[v2 as usize];
120                dst[3] = (src[3] * 255.).cpu_round() as u8;
121            }
122        } else if N == 2 && self.has_alpha {
123            for (src, dst) in from
124                .as_bytes()
125                .chunks_exact(2)
126                .zip(into.buffer.borrow_mut().chunks_exact_mut(2))
127            {
128                let v0 = (src[0] * 65535.).cpu_round() as u16;
129
130                dst[0] = self.gamma[v0 as usize];
131                dst[1] = (src[1] * 255.).cpu_round() as u8;
132            }
133        } else {
134            for (&src, dst) in from.as_bytes().iter().zip(into.buffer.borrow_mut()) {
135                let v = (src * 65535.).cpu_round() as u16;
136                *dst = self.gamma[v as usize];
137            }
138        }
139    }
140
141    fn bit_depth(&self) -> usize {
142        8
143    }
144}
145
146struct Common16BitSplitter<const N: usize> {
147    linearization: Box<[f32; 65536]>,
148    gamma: Box<[u16; 262144]>,
149    has_alpha: bool,
150    bit_depth: usize,
151}
152
153impl<const N: usize> Splitter<u16, f32, N> for Common16BitSplitter<N> {
154    fn split(&self, from: &ImageStore<'_, u16, N>, into: &mut ImageStoreMut<'_, f32, N>) {
155        if N == 4 {
156            let max_bit_depth_value = ((1u32 << self.bit_depth) - 1) as f32;
157
158            let v_recip = 1. / max_bit_depth_value;
159
160            for (src, dst) in from
161                .as_bytes()
162                .chunks_exact(4)
163                .zip(into.buffer.borrow_mut().chunks_exact_mut(4))
164            {
165                dst[0] = self.linearization[src[0] as usize];
166                dst[1] = self.linearization[src[1] as usize];
167                dst[2] = self.linearization[src[2] as usize];
168                dst[3] = src[3] as f32 * v_recip;
169            }
170        } else if N == 2 && self.has_alpha {
171            let max_bit_depth_value = ((1u32 << self.bit_depth) - 1) as f32;
172            let v_recip = 1. / max_bit_depth_value;
173
174            for (src, dst) in from
175                .as_bytes()
176                .chunks_exact(2)
177                .zip(into.buffer.borrow_mut().chunks_exact_mut(2))
178            {
179                dst[0] = self.linearization[src[0] as usize];
180                dst[1] = src[1] as f32 * v_recip;
181            }
182        } else {
183            for (&src, dst) in from.as_bytes().iter().zip(into.buffer.borrow_mut()) {
184                *dst = self.linearization[src as usize];
185            }
186        }
187    }
188
189    fn merge(&self, from: &ImageStore<'_, f32, N>, into: &mut ImageStoreMut<'_, u16, N>) {
190        if N == 4 {
191            let max_bit_depth_value = ((1u32 << self.bit_depth) - 1) as f32;
192            for (src, dst) in from
193                .as_bytes()
194                .chunks_exact(4)
195                .zip(into.buffer.borrow_mut().chunks_exact_mut(4))
196            {
197                let v0 = ((src[0] * 262143.).cpu_round().max(0.) as u32).min(262143);
198                let v1 = ((src[1] * 262143.).cpu_round().max(0.) as u32).min(262143);
199                let v2 = ((src[2] * 262143.).cpu_round().max(0.) as u32).min(262143);
200
201                dst[0] = self.gamma[v0 as usize];
202                dst[1] = self.gamma[v1 as usize];
203                dst[2] = self.gamma[v2 as usize];
204                dst[3] = (src[3] * max_bit_depth_value)
205                    .cpu_round()
206                    .min(max_bit_depth_value)
207                    .max(0.) as u16;
208            }
209        } else if N == 2 && self.has_alpha {
210            let max_bit_depth_value = ((1u32 << self.bit_depth) - 1) as f32;
211            for (src, dst) in from
212                .as_bytes()
213                .chunks_exact(2)
214                .zip(into.buffer.borrow_mut().chunks_exact_mut(2))
215            {
216                let v0 = ((src[0] * 262143.).cpu_round().max(0.) as u32).min(262143);
217
218                dst[0] = self.gamma[v0 as usize];
219                dst[1] = (src[1] * max_bit_depth_value)
220                    .cpu_round()
221                    .min(max_bit_depth_value)
222                    .max(0.) as u16;
223            }
224        } else {
225            for (&src, dst) in from.as_bytes().iter().zip(into.buffer.borrow_mut()) {
226                let v = ((src * 262143.).cpu_round().max(0.) as u32).min(262143);
227                *dst = self.gamma[v as usize];
228            }
229        }
230    }
231
232    fn bit_depth(&self) -> usize {
233        8
234    }
235}
236
237struct Linearization {
238    linearization: Box<[f32; 256]>,
239    gamma: Box<[u8; 65536]>,
240}
241
242struct Linearization16 {
243    linearization: Box<[f32; 65536]>,
244    gamma: Box<[u16; 262144]>,
245}
246
247fn make_linearization16(
248    transfer_function: TransferFunction,
249    bit_depth: usize,
250) -> Result<Linearization16, PicScaleError> {
251    if bit_depth < 8 {
252        return Err(PicScaleError::UnsupportedBitDepth(bit_depth));
253    }
254    let mut linearizing = Box::new([0f32; 65536]);
255    let max_lin_depth = (1u32 << bit_depth) - 1;
256    let keep_max = 1u32 << bit_depth;
257    let mut gamma = Box::new([0u16; 262144]);
258
259    let rcp_bit_depth = 1. / max_lin_depth as f32;
260
261    for (i, dst) in linearizing.iter_mut().take(keep_max as usize).enumerate() {
262        *dst = transfer_function.linearize(i as f32 * rcp_bit_depth);
263    }
264
265    const RCP_LUT_CAP: f32 = 1. / 262143.;
266
267    for (i, dst) in gamma.iter_mut().enumerate() {
268        *dst = (transfer_function.gamma(i as f32 * RCP_LUT_CAP) * max_lin_depth as f32)
269            .cpu_round()
270            .min(max_lin_depth as f32) as u16;
271    }
272
273    Ok(Linearization16 {
274        linearization: linearizing,
275        gamma,
276    })
277}
278
279fn make_linearization(transfer_function: TransferFunction) -> Linearization {
280    let mut linearizing = Box::new([0f32; 256]);
281    let mut gamma = Box::new([0u8; 65536]);
282
283    const S: f32 = 1. / 255.;
284
285    for (i, dst) in linearizing.iter_mut().enumerate() {
286        *dst = transfer_function.linearize(i as f32 * S);
287    }
288
289    const RCP_LUT_CAP: f32 = 1. / 65535.;
290
291    for (i, dst) in gamma.iter_mut().enumerate() {
292        *dst = (transfer_function.gamma(i as f32 * RCP_LUT_CAP) * 255.)
293            .cpu_round()
294            .min(255.) as u8;
295    }
296
297    Linearization {
298        linearization: linearizing,
299        gamma,
300    }
301}
302
303impl LinearScaler {
304    pub fn set_threading_policy(&mut self, threading_policy: ThreadingPolicy) -> Self {
305        self.scaler.threading_policy = threading_policy;
306        *self
307    }
308
309    pub fn plan_planar_resampling(
310        &self,
311        source_size: ImageSize,
312        target_size: ImageSize,
313    ) -> Result<Arc<Resampling<u8, 1>>, PicScaleError> {
314        let intercept = self
315            .scaler
316            .plan_planar_resampling_f32(source_size, target_size)?;
317        let scratch_size = intercept.scratch_size();
318        let lin = make_linearization(self.transfer_function);
319        Ok(Arc::new(SplitPlanInterceptor {
320            intercept,
321            splitter: Arc::new(Common8BitSplitter {
322                linearization: lin.linearization,
323                gamma: lin.gamma,
324                has_alpha: false,
325            }),
326            inner_scratch: scratch_size,
327        }))
328    }
329
330    pub fn plan_cbcr_resampling(
331        &self,
332        source_size: ImageSize,
333        target_size: ImageSize,
334    ) -> Result<Arc<Resampling<u8, 2>>, PicScaleError> {
335        let intercept = self
336            .scaler
337            .plan_cbcr_resampling_f32(source_size, target_size)?;
338        let scratch_size = intercept.scratch_size();
339        let lin = make_linearization(self.transfer_function);
340        Ok(Arc::new(SplitPlanInterceptor {
341            intercept,
342            splitter: Arc::new(Common8BitSplitter {
343                linearization: lin.linearization,
344                gamma: lin.gamma,
345                has_alpha: false,
346            }),
347            inner_scratch: scratch_size,
348        }))
349    }
350
351    pub fn plan_gray_alpha_resampling(
352        &self,
353        source_size: ImageSize,
354        target_size: ImageSize,
355        premultiply_alpha: bool,
356    ) -> Result<Arc<Resampling<u8, 2>>, PicScaleError> {
357        let intercept = self.scaler.plan_gray_alpha_resampling_f32(
358            source_size,
359            target_size,
360            premultiply_alpha,
361        )?;
362        let scratch_size = intercept.scratch_size();
363        let lin = make_linearization(self.transfer_function);
364        Ok(Arc::new(SplitPlanInterceptor {
365            intercept,
366            splitter: Arc::new(Common8BitSplitter {
367                linearization: lin.linearization,
368                gamma: lin.gamma,
369                has_alpha: true,
370            }),
371            inner_scratch: scratch_size,
372        }))
373    }
374
375    pub fn plan_rgb_resampling(
376        &self,
377        source_size: ImageSize,
378        target_size: ImageSize,
379    ) -> Result<Arc<Resampling<u8, 3>>, PicScaleError> {
380        let intercept = self
381            .scaler
382            .plan_rgb_resampling_f32(source_size, target_size)?;
383        let scratch_size = intercept.scratch_size();
384        let lin = make_linearization(self.transfer_function);
385        Ok(Arc::new(SplitPlanInterceptor {
386            intercept,
387            splitter: Arc::new(Common8BitSplitter {
388                linearization: lin.linearization,
389                gamma: lin.gamma,
390                has_alpha: false,
391            }),
392            inner_scratch: scratch_size,
393        }))
394    }
395
396    pub fn plan_rgba_resampling(
397        &self,
398        source_size: ImageSize,
399        target_size: ImageSize,
400        premultiply_alpha: bool,
401    ) -> Result<Arc<Resampling<u8, 4>>, PicScaleError> {
402        let intercept =
403            self.scaler
404                .plan_rgba_resampling_f32(source_size, target_size, premultiply_alpha)?;
405        let scratch_size = intercept.scratch_size();
406        let lin = make_linearization(self.transfer_function);
407        Ok(Arc::new(SplitPlanInterceptor {
408            intercept,
409            splitter: Arc::new(Common8BitSplitter {
410                linearization: lin.linearization,
411                gamma: lin.gamma,
412                has_alpha: true,
413            }),
414            inner_scratch: scratch_size,
415        }))
416    }
417
418    pub fn plan_planar_resampling16(
419        &self,
420        source_size: ImageSize,
421        target_size: ImageSize,
422        bit_depth: usize,
423    ) -> Result<Arc<Resampling<u16, 1>>, PicScaleError> {
424        let intercept = self
425            .scaler
426            .plan_planar_resampling_f32(source_size, target_size)?;
427        let scratch_size = intercept.scratch_size();
428        let lin = make_linearization16(self.transfer_function, bit_depth)?;
429        Ok(Arc::new(SplitPlanInterceptor {
430            intercept,
431            splitter: Arc::new(Common16BitSplitter {
432                linearization: lin.linearization,
433                gamma: lin.gamma,
434                has_alpha: false,
435                bit_depth,
436            }),
437            inner_scratch: scratch_size,
438        }))
439    }
440
441    pub fn plan_gray_alpha_resampling16(
442        &self,
443        source_size: ImageSize,
444        target_size: ImageSize,
445        premultiply_alpha: bool,
446        bit_depth: usize,
447    ) -> Result<Arc<Resampling<u16, 2>>, PicScaleError> {
448        let intercept = self.scaler.plan_gray_alpha_resampling_f32(
449            source_size,
450            target_size,
451            premultiply_alpha,
452        )?;
453        let scratch_size = intercept.scratch_size();
454        let lin = make_linearization16(self.transfer_function, bit_depth)?;
455        Ok(Arc::new(SplitPlanInterceptor {
456            intercept,
457            splitter: Arc::new(Common16BitSplitter {
458                linearization: lin.linearization,
459                gamma: lin.gamma,
460                has_alpha: true,
461                bit_depth,
462            }),
463            inner_scratch: scratch_size,
464        }))
465    }
466
467    pub fn plan_cbcr_resampling16(
468        &self,
469        source_size: ImageSize,
470        target_size: ImageSize,
471        bit_depth: usize,
472    ) -> Result<Arc<Resampling<u16, 2>>, PicScaleError> {
473        let intercept = self
474            .scaler
475            .plan_cbcr_resampling_f32(source_size, target_size)?;
476        let scratch_size = intercept.scratch_size();
477        let lin = make_linearization16(self.transfer_function, bit_depth)?;
478        Ok(Arc::new(SplitPlanInterceptor {
479            intercept,
480            splitter: Arc::new(Common16BitSplitter {
481                linearization: lin.linearization,
482                gamma: lin.gamma,
483                has_alpha: false,
484                bit_depth,
485            }),
486            inner_scratch: scratch_size,
487        }))
488    }
489
490    pub fn plan_rgb_resampling16(
491        &self,
492        source_size: ImageSize,
493        target_size: ImageSize,
494        bit_depth: usize,
495    ) -> Result<Arc<Resampling<u16, 3>>, PicScaleError> {
496        let intercept = self
497            .scaler
498            .plan_rgb_resampling_f32(source_size, target_size)?;
499        let scratch_size = intercept.scratch_size();
500        let lin = make_linearization16(self.transfer_function, bit_depth)?;
501        Ok(Arc::new(SplitPlanInterceptor {
502            intercept,
503            splitter: Arc::new(Common16BitSplitter {
504                linearization: lin.linearization,
505                gamma: lin.gamma,
506                has_alpha: false,
507                bit_depth,
508            }),
509            inner_scratch: scratch_size,
510        }))
511    }
512
513    pub fn plan_rgba_resampling16(
514        &self,
515        source_size: ImageSize,
516        target_size: ImageSize,
517        premultiply_alpha: bool,
518        bit_depth: usize,
519    ) -> Result<Arc<Resampling<u16, 4>>, PicScaleError> {
520        let intercept =
521            self.scaler
522                .plan_rgba_resampling_f32(source_size, target_size, premultiply_alpha)?;
523        let scratch_size = intercept.scratch_size();
524        let lin = make_linearization16(self.transfer_function, bit_depth)?;
525        Ok(Arc::new(SplitPlanInterceptor {
526            intercept,
527            splitter: Arc::new(Common16BitSplitter {
528                linearization: lin.linearization,
529                gamma: lin.gamma,
530                has_alpha: true,
531                bit_depth,
532            }),
533            inner_scratch: scratch_size,
534        }))
535    }
536}