1use 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)]
38pub struct LinearApproxScaler {
41 pub(crate) scaler: Scaler,
42 pub(crate) transfer_function: TransferFunction,
43}
44
45impl LinearApproxScaler {
46 pub fn new(filter: ResamplingFunction) -> Self {
48 LinearApproxScaler {
49 scaler: Scaler::new(filter),
50 transfer_function: TransferFunction::Srgb,
51 }
52 }
53
54 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}