1#![forbid(unsafe_code)]
30use crate::image_store::ImageStoreMut;
31use crate::plan::{AlphaPlanner, DefaultPlanner, Resampling};
32use crate::scaler::ScalingOptions;
33use crate::validation::PicScaleError;
34use crate::{
35 CbCrF16ImageStore, ImageSize, ImageStoreScaling, PlanarF16ImageStore, RgbF16ImageStore,
36 RgbaF16ImageStore, Scaler,
37};
38use core::f16;
39use std::sync::Arc;
40
41#[cfg_attr(docsrs, doc(cfg(feature = "nightly_f16")))]
43impl Scaler {
44 pub fn plan_planar_resampling_f16(
62 &self,
63 source_size: ImageSize,
64 target_size: ImageSize,
65 ) -> Result<Arc<Resampling<f16, 1>>, PicScaleError> {
66 DefaultPlanner::plan_generic_resize::<f16, f32, 1>(self, source_size, target_size, 8)
67 }
68
69 pub fn plan_cbcr_resampling_f16(
88 &self,
89 source_size: ImageSize,
90 target_size: ImageSize,
91 ) -> Result<Arc<Resampling<f16, 2>>, PicScaleError> {
92 DefaultPlanner::plan_generic_resize::<f16, f32, 2>(self, source_size, target_size, 8)
93 }
94
95 pub fn plan_rgb_resampling_f16(
114 &self,
115 source_size: ImageSize,
116 target_size: ImageSize,
117 ) -> Result<Arc<Resampling<f16, 3>>, PicScaleError> {
118 DefaultPlanner::plan_generic_resize::<f16, f32, 3>(self, source_size, target_size, 8)
119 }
120
121 pub fn plan_rgba_resampling_f16(
140 &self,
141 source_size: ImageSize,
142 target_size: ImageSize,
143 premultiply_alpha: bool,
144 ) -> Result<Arc<Resampling<f16, 4>>, PicScaleError> {
145 AlphaPlanner::plan_generic_resize_with_alpha::<f16, f32, 4>(
146 self,
147 source_size,
148 target_size,
149 8,
150 premultiply_alpha,
151 )
152 }
153}
154
155#[cfg_attr(docsrs, doc(cfg(feature = "nightly_f16")))]
156impl<'b> ImageStoreScaling<'b, f16, 1> for PlanarF16ImageStore<'b> {
157 fn scale(
158 &self,
159 store: &mut ImageStoreMut<'b, f16, 1>,
160 options: ScalingOptions,
161 ) -> Result<(), PicScaleError> {
162 let scaler =
163 Scaler::new(options.resampling_function).set_threading_policy(options.threading_policy);
164 let plan = DefaultPlanner::plan_generic_resize(
165 &scaler,
166 self.size(),
167 store.size(),
168 store.bit_depth,
169 )?;
170 plan.resample(self, store)
171 }
172}
173
174#[cfg_attr(docsrs, doc(cfg(feature = "nightly_f16")))]
175impl<'b> ImageStoreScaling<'b, f16, 2> for CbCrF16ImageStore<'b> {
176 fn scale(
177 &self,
178 store: &mut ImageStoreMut<'b, f16, 2>,
179 options: ScalingOptions,
180 ) -> Result<(), PicScaleError> {
181 let scaler =
182 Scaler::new(options.resampling_function).set_threading_policy(options.threading_policy);
183 let plan = DefaultPlanner::plan_generic_resize(
184 &scaler,
185 self.size(),
186 store.size(),
187 store.bit_depth,
188 )?;
189 plan.resample(self, store)
190 }
191}
192
193#[cfg_attr(docsrs, doc(cfg(feature = "nightly_f16")))]
194impl<'b> ImageStoreScaling<'b, f16, 3> for RgbF16ImageStore<'b> {
195 fn scale(
196 &self,
197 store: &mut ImageStoreMut<'b, f16, 3>,
198 options: ScalingOptions,
199 ) -> Result<(), PicScaleError> {
200 let scaler =
201 Scaler::new(options.resampling_function).set_threading_policy(options.threading_policy);
202 let plan = DefaultPlanner::plan_generic_resize(
203 &scaler,
204 self.size(),
205 store.size(),
206 store.bit_depth,
207 )?;
208 plan.resample(self, store)
209 }
210}
211
212#[cfg_attr(docsrs, doc(cfg(feature = "nightly_f16")))]
213impl<'b> ImageStoreScaling<'b, f16, 4> for RgbaF16ImageStore<'b> {
214 fn scale(
215 &self,
216 store: &mut ImageStoreMut<'b, f16, 4>,
217 options: ScalingOptions,
218 ) -> Result<(), PicScaleError> {
219 let scaler =
220 Scaler::new(options.resampling_function).set_threading_policy(options.threading_policy);
221 let plan = AlphaPlanner::plan_generic_resize_with_alpha(
222 &scaler,
223 self.size(),
224 store.size(),
225 store.bit_depth,
226 options.premultiply_alpha,
227 )?;
228 plan.resample(self, store)
229 }
230}
231
232#[cfg(test)]
233mod tests {
234 use super::*;
235 use crate::{ImageStore, ResamplingFunction, ThreadingPolicy};
236 use core::f16;
237
238 #[test]
239 fn check_rgba_f16_resizing_vertical() {
240 let image_width = 8;
241 let image_height = 8;
242 const CN: usize = 4;
243 let mut image = vec![0_f16; image_height * image_width * CN];
244 for dst in image.as_chunks_mut::<CN>().0.iter_mut() {
245 dst[0] = (124f32 / 255f32) as f16;
246 dst[1] = (41f32 / 255f32) as f16;
247 dst[2] = (99f32 / 255f32) as f16;
248 dst[3] = 1f32 as f16;
249 }
250 let mut scaler = Scaler::new(ResamplingFunction::Lanczos3);
251 scaler.set_threading_policy(ThreadingPolicy::Single);
252 let src_store = ImageStore::from_slice(&image, image_width, image_height).unwrap();
253 let mut target_store = ImageStoreMut::<f16, 4>::alloc(image_width, image_height / 2);
254 let planned = scaler
255 .plan_rgba_resampling_f16(src_store.size(), target_store.size(), false)
256 .unwrap();
257 planned.resample(&src_store, &mut target_store).unwrap();
258 let target_data = target_store.buffer.borrow();
259
260 for dst in target_data.chunks_exact(CN) {
261 assert!(
262 (dst[0] as f32 * 255f32 - 124f32).abs() < 3f32,
263 "R channel mismatch: {}",
264 dst[0] as f32 * 255f32
265 );
266 assert!(
267 (dst[1] as f32 * 255f32 - 41f32).abs() < 3f32,
268 "G channel mismatch: {}",
269 dst[1] as f32 * 255f32
270 );
271 assert!(
272 (dst[2] as f32 * 255f32 - 99f32).abs() < 3f32,
273 "B channel mismatch: {}",
274 dst[2] as f32 * 255f32
275 );
276 assert!(
277 (dst[3] as f32 - 1f32).abs() < 0.01f32,
278 "A channel mismatch: {}",
279 dst[3] as f32
280 );
281 }
282 }
283
284 #[test]
285 fn check_rgba_f16_resizing_vertical_threading() {
286 let image_width = 8;
287 let image_height = 8;
288 const CN: usize = 4;
289 let mut image = vec![0_f16; image_height * image_width * CN];
290 for dst in image.as_chunks_mut::<CN>().0.iter_mut() {
291 dst[0] = (124f32 / 255f32) as f16;
292 dst[1] = (41f32 / 255f32) as f16;
293 dst[2] = (99f32 / 255f32) as f16;
294 dst[3] = 1f32 as f16;
295 }
296 let scaler = Scaler::new(ResamplingFunction::Lanczos3)
297 .set_threading_policy(ThreadingPolicy::Adaptive);
298 let src_store = ImageStore::from_slice(&image, image_width, image_height).unwrap();
299 let mut target_store = ImageStoreMut::<f16, 4>::alloc(image_width, image_height / 2);
300 let planned = scaler
301 .plan_rgba_resampling_f16(src_store.size(), target_store.size(), false)
302 .unwrap();
303 planned.resample(&src_store, &mut target_store).unwrap();
304 let target_data = target_store.buffer.borrow();
305
306 for dst in target_data.chunks_exact(CN) {
307 assert!(
308 (dst[0] as f32 * 255f32 - 124f32).abs() < 3f32,
309 "R channel mismatch: {}",
310 dst[0] as f32 * 255f32
311 );
312 assert!(
313 (dst[1] as f32 * 255f32 - 41f32).abs() < 3f32,
314 "G channel mismatch: {}",
315 dst[1] as f32 * 255f32
316 );
317 assert!(
318 (dst[2] as f32 * 255f32 - 99f32).abs() < 3f32,
319 "B channel mismatch: {}",
320 dst[2] as f32 * 255f32
321 );
322 assert!(
323 (dst[3] as f32 - 1f32).abs() < 0.01f32,
324 "A channel mismatch: {}",
325 dst[3] as f32
326 );
327 }
328 }
329
330 #[test]
331 fn check_rgba_f16_nearest_vertical() {
332 let image_width = 255;
333 let image_height = 512;
334 const CN: usize = 4;
335 let mut image = vec![0_f16; image_height * image_width * CN];
336 for dst in image.as_chunks_mut::<CN>().0.iter_mut() {
337 dst[0] = (124f32 / 255f32) as f16;
338 dst[1] = (41f32 / 255f32) as f16;
339 dst[2] = (99f32 / 255f32) as f16;
340 dst[3] = (77f32 / 255f32) as f16;
341 }
342 let mut scaler = Scaler::new(ResamplingFunction::Nearest);
343 scaler.set_threading_policy(ThreadingPolicy::Single);
344 let src_store = ImageStore::from_slice(&image, image_width, image_height).unwrap();
345 let mut target_store = ImageStoreMut::<f16, 4>::alloc(image_width, image_height / 2);
346 let planned = scaler
347 .plan_rgba_resampling_f16(src_store.size(), target_store.size(), false)
348 .unwrap();
349 planned.resample(&src_store, &mut target_store).unwrap();
350 let target_data = target_store.buffer.borrow();
351
352 for dst in target_data.chunks_exact(CN) {
353 assert!(
354 (dst[0] as f32 * 255f32 - 124f32).abs() < 3f32,
355 "R channel mismatch: {}",
356 dst[0] as f32 * 255f32
357 );
358 assert!(
359 (dst[1] as f32 * 255f32 - 41f32).abs() < 3f32,
360 "G channel mismatch: {}",
361 dst[1] as f32 * 255f32
362 );
363 assert!(
364 (dst[2] as f32 * 255f32 - 99f32).abs() < 3f32,
365 "B channel mismatch: {}",
366 dst[2] as f32 * 255f32
367 );
368 assert!(
369 (dst[3] as f32 * 255f32 - 77f32).abs() < 3f32,
370 "A channel mismatch: {}",
371 dst[3] as f32 * 255f32
372 );
373 }
374 }
375
376 #[test]
377 fn check_rgba_f16_nearest_vertical_threading() {
378 let image_width = 255;
379 let image_height = 512;
380 const CN: usize = 4;
381 let mut image = vec![0_f16; image_height * image_width * CN];
382 for dst in image.as_chunks_mut::<CN>().0.iter_mut() {
383 dst[0] = (124f32 / 255f32) as f16;
384 dst[1] = (41f32 / 255f32) as f16;
385 dst[2] = (99f32 / 255f32) as f16;
386 dst[3] = (77f32 / 255f32) as f16;
387 }
388 let scaler = Scaler::new(ResamplingFunction::Nearest)
389 .set_threading_policy(ThreadingPolicy::Adaptive);
390 let src_store = ImageStore::from_slice(&image, image_width, image_height).unwrap();
391 let mut target_store = ImageStoreMut::<f16, 4>::alloc(image_width, image_height / 2);
392 let planned = scaler
393 .plan_rgba_resampling_f16(src_store.size(), target_store.size(), false)
394 .unwrap();
395 planned.resample(&src_store, &mut target_store).unwrap();
396 let target_data = target_store.buffer.borrow();
397
398 for dst in target_data.chunks_exact(CN) {
399 assert!(
400 (dst[0] as f32 * 255f32 - 124f32).abs() < 3f32,
401 "R channel mismatch: {}",
402 dst[0] as f32 * 255f32
403 );
404 assert!(
405 (dst[1] as f32 * 255f32 - 41f32).abs() < 3f32,
406 "G channel mismatch: {}",
407 dst[1] as f32 * 255f32
408 );
409 assert!(
410 (dst[2] as f32 * 255f32 - 99f32).abs() < 3f32,
411 "B channel mismatch: {}",
412 dst[2] as f32 * 255f32
413 );
414 assert!(
415 (dst[3] as f32 * 255f32 - 77f32).abs() < 3f32,
416 "A channel mismatch: {}",
417 dst[3] as f32 * 255f32
418 );
419 }
420 }
421
422 #[test]
423 fn check_rgb_f16_resizing_vertical() {
424 let image_width = 8;
425 let image_height = 8;
426 const CN: usize = 3;
427 let mut image = vec![0_f16; image_height * image_width * CN];
428 for dst in image.as_chunks_mut::<CN>().0.iter_mut() {
429 dst[0] = (124f32 / 255f32) as f16;
430 dst[1] = (41f32 / 255f32) as f16;
431 dst[2] = (99f32 / 255f32) as f16;
432 }
433 let mut scaler = Scaler::new(ResamplingFunction::Lanczos3);
434 scaler.set_threading_policy(ThreadingPolicy::Single);
435 let src_store = ImageStore::from_slice(&image, image_width, image_height).unwrap();
436 let mut target_store = ImageStoreMut::<f16, 3>::alloc(image_width, image_height / 2);
437 let planned = scaler
438 .plan_rgb_resampling_f16(src_store.size(), target_store.size())
439 .unwrap();
440 planned.resample(&src_store, &mut target_store).unwrap();
441 let target_data = target_store.buffer.borrow();
442
443 for dst in target_data.as_chunks::<CN>().0.iter() {
444 assert!(
445 (dst[0] as f32 * 255f32 - 124f32).abs() < 3f32,
446 "R channel mismatch: {}",
447 dst[0] as f32 * 255f32
448 );
449 assert!(
450 (dst[1] as f32 * 255f32 - 41f32).abs() < 3f32,
451 "G channel mismatch: {}",
452 dst[1] as f32 * 255f32
453 );
454 assert!(
455 (dst[2] as f32 * 255f32 - 99f32).abs() < 3f32,
456 "B channel mismatch: {}",
457 dst[2] as f32 * 255f32
458 );
459 }
460 }
461
462 #[test]
463 fn check_cbcr_f16_resizing_vertical() {
464 let image_width = 8;
465 let image_height = 8;
466 const CN: usize = 2;
467 let mut image = vec![0_f16; image_height * image_width * CN];
468 for dst in image.as_chunks_mut::<CN>().0.iter_mut() {
469 dst[0] = (124f32 / 255f32) as f16;
470 dst[1] = (41f32 / 255f32) as f16;
471 }
472 let mut scaler = Scaler::new(ResamplingFunction::Lanczos3);
473 scaler.set_threading_policy(ThreadingPolicy::Single);
474 let src_store = ImageStore::from_slice(&image, image_width, image_height).unwrap();
475 let mut target_store = ImageStoreMut::<f16, 2>::alloc(image_width, image_height / 2);
476 let planned = scaler
477 .plan_cbcr_resampling_f16(src_store.size(), target_store.size())
478 .unwrap();
479 planned.resample(&src_store, &mut target_store).unwrap();
480 let target_data = target_store.buffer.borrow();
481
482 for dst in target_data.as_chunks::<CN>().0.iter() {
483 assert!(
484 (dst[0] as f32 * 255f32 - 124f32).abs() < 3f32,
485 "R channel mismatch: {}",
486 dst[0] as f32 * 255f32
487 );
488 assert!(
489 (dst[1] as f32 * 255f32 - 41f32).abs() < 3f32,
490 "G channel mismatch: {}",
491 dst[1] as f32 * 255f32
492 );
493 }
494 }
495
496 #[test]
497 fn check_planar_f16_resizing_vertical() {
498 let image_width = 8;
499 let image_height = 8;
500 const CN: usize = 1;
501 let mut image = vec![0_f16; image_height * image_width * CN];
502 for dst in image.as_chunks_mut::<CN>().0.iter_mut() {
503 dst[0] = (124f32 / 255f32) as f16;
504 }
505 let mut scaler = Scaler::new(ResamplingFunction::Lanczos3);
506 scaler.set_threading_policy(ThreadingPolicy::Single);
507 let src_store = ImageStore::from_slice(&image, image_width, image_height).unwrap();
508 let mut target_store = ImageStoreMut::<f16, 1>::alloc(image_width, image_height / 2);
509 let planned = scaler
510 .plan_planar_resampling_f16(src_store.size(), target_store.size())
511 .unwrap();
512 planned.resample(&src_store, &mut target_store).unwrap();
513 let target_data = target_store.buffer.borrow();
514
515 for dst in target_data.as_chunks::<CN>().0.iter() {
516 assert!(
517 (dst[0] as f32 * 255f32 - 124f32).abs() < 3f32,
518 "R channel mismatch: {}",
519 dst[0] as f32 * 255f32
520 );
521 }
522 }
523}