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(&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}