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