Skip to main content

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