Added basic support for the new pixel type PixelType::F32x4 (#30).

This commit is contained in:
Kirill Kuzminykh
2024-06-18 23:20:13 +03:00
parent 91c28d4539
commit 5e450afe04
19 changed files with 486 additions and 9 deletions
+1
View File
@@ -5,6 +5,7 @@
- Added support for the new pixel type `PixelType::F32x2` with
optimizations for SSE4.1 and AVX2 (#30).
- Added basic support for the new pixel type `PixelType::F32x3` (#30).
- Added basic support for the new pixel type `PixelType::F32x4` (#30).
## [4.0.0] - 2024-05-13
+5
View File
@@ -95,6 +95,11 @@ name = "bench_compare_rgba16"
harness = false
[[bench]]
name = "bench_compare_rgba32f"
harness = false
[[bench]]
name = "bench_compare_l"
harness = false
+1 -1
View File
@@ -22,7 +22,7 @@ Supported pixel formats and available optimisations:
| U16x4 | Four `u16` components per pixel (e.g. RGBA16, RGBx16, CMYK16) | + | + | + | + |
| I32 | One `i32` component per pixel (e.g. L) | - | - | - | - |
| F32 | One `f32` component per pixel (e.g. L) | - | - | - | - |
| F32x2 | Two `f32` components per pixel (e.g. LA32f) | + | + | - | - |
| F32x2 | Two `f32` components per pixel (e.g. LA32F) | + | + | - | - |
| F32x3 | Three `f32` components per pixel (e.g. RGB32F) | - | - | - | - |
## Colorspace
+14
View File
@@ -0,0 +1,14 @@
use fast_image_resize::pixels::F32x4;
mod utils;
pub fn bench_downscale_rgba32f(bench_group: &mut utils::BenchGroup) {
type P = F32x4;
utils::libvips_resize::<P>(bench_group, true);
utils::fir_resize::<P>(bench_group, true);
}
fn main() {
let res = utils::run_bench(bench_downscale_rgba32f, "Compare resize of RGBA32F image");
utils::print_and_write_compare_result(&res);
}
@@ -0,0 +1,14 @@
### Resize RGBA32F image (F32x4) 4928x3279 => 852x567
Pipeline:
`src_image => multiply by alpha => resize => divide by alpha => dst_image`
- Source image
[nasa-4928x3279-rgba.png](https://github.com/Cykooz/fast_image_resize/blob/main/data/nasa-4928x3279-rgba.png)
- Numbers in the table mean a duration of image resizing in milliseconds.
- The `image` crate does not support multiplying and dividing by alpha channel.
- The `resize` crate does not support multiplying and dividing by alpha channel
for this pixel format.
{{ compare_results -}}
+23
View File
@@ -256,3 +256,26 @@ Pipeline:
| fir rust | 0.87 | 17.12 | 27.34 | 51.51 | 75.67 |
<!-- bench_compare_rgb32f end -->
<!-- bench_compare_rgba32f start -->
### Resize RGBA32F image (F32x4) 4928x3279 => 852x567
Pipeline:
`src_image => multiply by alpha => resize => divide by alpha => dst_image`
- Source image
[nasa-4928x3279-rgba.png](https://github.com/Cykooz/fast_image_resize/blob/main/data/nasa-4928x3279-rgba.png)
- Numbers in the table mean a duration of image resizing in milliseconds.
- The `image` crate does not support multiplying and dividing by alpha channel.
- The `resize` crate does not support multiplying and dividing by alpha channel
for this pixel format.
| | Nearest | Box | Bilinear | Bicubic | Lanczos3 |
|----------|:-------:|:------:|:--------:|:-------:|:--------:|
| libvips | 23.29 | 111.87 | 140.37 | 251.12 | 381.52 |
| fir rust | 0.98 | 35.65 | 45.42 | 70.24 | 92.90 |
<!-- bench_compare_rgba32f end -->
+87
View File
@@ -0,0 +1,87 @@
use crate::cpu_extensions::CpuExtensions;
use crate::pixels::F32x4;
use crate::{ImageError, ImageView, ImageViewMut};
use super::AlphaMulDiv;
// #[cfg(target_arch = "x86_64")]
// mod avx2;
mod native;
// #[cfg(target_arch = "x86_64")]
// mod sse4;
impl AlphaMulDiv for F32x4 {
fn multiply_alpha(
src_view: &impl ImageView<Pixel = Self>,
dst_view: &mut impl ImageViewMut<Pixel = Self>,
cpu_extensions: CpuExtensions,
) -> Result<(), ImageError> {
match cpu_extensions {
// #[cfg(target_arch = "x86_64")]
// CpuExtensions::Avx2 => unsafe { avx2::multiply_alpha(src_view, dst_view) },
// #[cfg(target_arch = "x86_64")]
// CpuExtensions::Sse4_1 => unsafe { sse4::multiply_alpha(src_view, dst_view) },
// #[cfg(target_arch = "aarch64")]
// CpuExtensions::Neon => unsafe { neon::multiply_alpha(src_view, dst_view) },
// #[cfg(target_arch = "wasm32")]
// CpuExtensions::Simd128 => unsafe { wasm32::multiply_alpha(src_view, dst_view) },
_ => native::multiply_alpha(src_view, dst_view),
}
Ok(())
}
fn multiply_alpha_inplace(
image_view: &mut impl ImageViewMut<Pixel = Self>,
cpu_extensions: CpuExtensions,
) -> Result<(), ImageError> {
match cpu_extensions {
// #[cfg(target_arch = "x86_64")]
// CpuExtensions::Avx2 => unsafe { avx2::multiply_alpha_inplace(image_view) },
// #[cfg(target_arch = "x86_64")]
// CpuExtensions::Sse4_1 => unsafe { sse4::multiply_alpha_inplace(image_view) },
// #[cfg(target_arch = "aarch64")]
// CpuExtensions::Neon => unsafe { neon::multiply_alpha_inplace(image_view) },
// #[cfg(target_arch = "wasm32")]
// CpuExtensions::Simd128 => unsafe { wasm32::multiply_alpha_inplace(image_view) },
_ => native::multiply_alpha_inplace(image_view),
}
Ok(())
}
fn divide_alpha(
src_view: &impl ImageView<Pixel = Self>,
dst_view: &mut impl ImageViewMut<Pixel = Self>,
cpu_extensions: CpuExtensions,
) -> Result<(), ImageError> {
match cpu_extensions {
// #[cfg(target_arch = "x86_64")]
// CpuExtensions::Avx2 => unsafe { avx2::divide_alpha(src_view, dst_view) },
// #[cfg(target_arch = "x86_64")]
// CpuExtensions::Sse4_1 => unsafe { sse4::divide_alpha(src_view, dst_view) },
// #[cfg(target_arch = "aarch64")]
// CpuExtensions::Neon => unsafe { crate::alpha::u16x2::neon::divide_alpha(src_view, dst_view) },
// #[cfg(target_arch = "wasm32")]
// CpuExtensions::Simd128 => unsafe { crate::alpha::u16x2::wasm32::divide_alpha(src_view, dst_view) },
_ => native::divide_alpha(src_view, dst_view),
}
Ok(())
}
fn divide_alpha_inplace(
image_view: &mut impl ImageViewMut<Pixel = Self>,
cpu_extensions: CpuExtensions,
) -> Result<(), ImageError> {
match cpu_extensions {
// #[cfg(target_arch = "x86_64")]
// CpuExtensions::Avx2 => unsafe { avx2::divide_alpha_inplace(image_view) },
// #[cfg(target_arch = "x86_64")]
// CpuExtensions::Sse4_1 => unsafe { sse4::divide_alpha_inplace(image_view) },
// #[cfg(target_arch = "aarch64")]
// CpuExtensions::Neon => unsafe { crate::alpha::u16x2::neon::divide_alpha_inplace(image_view) },
// #[cfg(target_arch = "wasm32")]
// CpuExtensions::Simd128 => unsafe { crate::alpha::u16x2::wasm32::divide_alpha_inplace(image_view) },
_ => native::divide_alpha_inplace(image_view),
}
Ok(())
}
}
+111
View File
@@ -0,0 +1,111 @@
use num_traits::Zero;
use crate::pixels::F32x4;
use crate::utils::foreach_with_pre_reading;
use crate::{ImageView, ImageViewMut};
pub(crate) fn multiply_alpha(
src_view: &impl ImageView<Pixel = F32x4>,
dst_view: &mut impl ImageViewMut<Pixel = F32x4>,
) {
let src_rows = src_view.iter_rows(0);
let dst_rows = dst_view.iter_rows_mut(0);
for (src_row, dst_row) in src_rows.zip(dst_rows) {
multiply_alpha_row(src_row, dst_row);
}
}
pub(crate) fn multiply_alpha_inplace(image_view: &mut impl ImageViewMut<Pixel = F32x4>) {
for row in image_view.iter_rows_mut(0) {
multiply_alpha_row_inplace(row);
}
}
#[inline(always)]
pub(crate) fn multiply_alpha_row(src_row: &[F32x4], dst_row: &mut [F32x4]) {
for (src_pixel, dst_pixel) in src_row.iter().zip(dst_row) {
let components = src_pixel.0;
let alpha = components[3];
dst_pixel.0 = [
components[0] * alpha,
components[1] * alpha,
components[2] * alpha,
alpha,
];
}
}
#[inline(always)]
pub(crate) fn multiply_alpha_row_inplace(row: &mut [F32x4]) {
for pixel in row {
let alpha = pixel.0[3];
pixel.0[0] *= alpha;
pixel.0[1] *= alpha;
pixel.0[2] *= alpha;
}
}
// Divide
#[inline]
pub(crate) fn divide_alpha(
src_view: &impl ImageView<Pixel = F32x4>,
dst_view: &mut impl ImageViewMut<Pixel = F32x4>,
) {
let src_rows = src_view.iter_rows(0);
let dst_rows = dst_view.iter_rows_mut(0);
for (src_row, dst_row) in src_rows.zip(dst_rows) {
divide_alpha_row(src_row, dst_row);
}
}
#[inline]
pub(crate) fn divide_alpha_inplace(image_view: &mut impl ImageViewMut<Pixel = F32x4>) {
for row in image_view.iter_rows_mut(0) {
divide_alpha_row_inplace(row);
}
}
#[inline(always)]
pub(crate) fn divide_alpha_row(src_row: &[F32x4], dst_row: &mut [F32x4]) {
foreach_with_pre_reading(
src_row.iter().zip(dst_row),
|(&src_pixel, dst_pixel)| (src_pixel, dst_pixel),
|(src_pixel, dst_pixel)| {
let components = src_pixel.0;
let alpha = components[3];
if alpha.is_zero() {
dst_pixel.0 = [0.; 4];
} else {
let recip_alpha = 1. / alpha;
dst_pixel.0 = [
components[0] * recip_alpha,
components[1] * recip_alpha,
components[2] * recip_alpha,
alpha,
];
}
},
);
}
#[inline(always)]
pub(crate) fn divide_alpha_row_inplace(row: &mut [F32x4]) {
for pixel in row {
let components = pixel.0;
let alpha = components[3];
if alpha.is_zero() {
pixel.0 = [0.; 4];
} else {
let recip_alpha = 1. / alpha;
pixel.0 = [
components[0] * recip_alpha,
components[1] * recip_alpha,
components[2] * recip_alpha,
alpha,
];
}
}
}
+1
View File
@@ -9,6 +9,7 @@ cfg_if::cfg_if! {
mod u16x4;
mod u8x2;
mod f32x2;
mod f32x4;
}
}
+23 -4
View File
@@ -1,6 +1,6 @@
use crate::pixels::{
F32x2, F32x3, InnerPixel, IntoPixelComponent, U16x2, U16x3, U16x4, U8x2, U8x3, U8x4, F32, I32,
U16, U8,
F32x2, F32x3, F32x4, InnerPixel, IntoPixelComponent, U16x2, U16x3, U16x4, U8x2, U8x3, U8x4,
F32, I32, U16, U8,
};
use crate::{
try_pixel_type, DifferentDimensionsError, ImageView, ImageViewMut, IntoImageView,
@@ -54,7 +54,13 @@ pub fn change_type_of_pixel_components(
(PT::U16x3, U16x3),
(PT::F32x3, F32x3)
),
PixelType::U8x4 => map_dst!(U8x4, dst_pixel_type, (PT::U8x4, U8x4), (PT::U16x4, U16x4)),
PixelType::U8x4 => map_dst!(
U8x4,
dst_pixel_type,
(PT::U8x4, U8x4),
(PT::U16x4, U16x4),
(PT::F32x4, F32x4)
),
PixelType::U16 => map_dst!(
U16,
dst_pixel_type,
@@ -77,7 +83,13 @@ pub fn change_type_of_pixel_components(
(PT::U16x3, U16x3),
(PT::F32x3, F32x3)
),
PixelType::U16x4 => map_dst!(U16x4, dst_pixel_type, (PT::U8x4, U8x4), (PT::U16x4, U16x4)),
PixelType::U16x4 => map_dst!(
U16x4,
dst_pixel_type,
(PT::U8x4, U8x4),
(PT::U16x4, U16x4),
(PT::F32x4, F32x4)
),
PixelType::I32 => map_dst!(
I32,
dst_pixel_type,
@@ -108,6 +120,13 @@ pub fn change_type_of_pixel_components(
(PT::U16x3, U16x3),
(PT::F32x3, F32x3)
),
PixelType::F32x4 => map_dst!(
F32x4,
dst_pixel_type,
(PT::U8x4, U8x4),
(PT::U16x4, U16x4),
(PT::F32x4, F32x4)
),
}
}
+48
View File
@@ -0,0 +1,48 @@
use crate::convolution::vertical_f32::vert_convolution_f32;
use crate::cpu_extensions::CpuExtensions;
use crate::pixels::F32x4;
use crate::{ImageView, ImageViewMut};
use super::{Coefficients, Convolution};
// #[cfg(target_arch = "x86_64")]
// mod avx2;
mod native;
// #[cfg(target_arch = "aarch64")]
// mod neon;
// #[cfg(target_arch = "x86_64")]
// mod sse4;
// #[cfg(target_arch = "wasm32")]
// mod wasm32;
impl Convolution for F32x4 {
fn horiz_convolution(
src_view: &impl ImageView<Pixel = Self>,
dst_view: &mut impl ImageViewMut<Pixel = Self>,
offset: u32,
coeffs: Coefficients,
cpu_extensions: CpuExtensions,
) {
match cpu_extensions {
// #[cfg(target_arch = "x86_64")]
// CpuExtensions::Avx2 => avx2::horiz_convolution(src_view, dst_view, offset, coeffs),
// #[cfg(target_arch = "x86_64")]
// CpuExtensions::Sse4_1 => sse4::horiz_convolution(src_view, dst_view, offset, coeffs),
#[cfg(target_arch = "aarch64")]
CpuExtensions::Neon => neon::horiz_convolution(src_view, dst_view, offset, coeffs),
#[cfg(target_arch = "wasm32")]
CpuExtensions::Simd128 => wasm32::horiz_convolution(src_view, dst_view, offset, coeffs),
_ => native::horiz_convolution(src_view, dst_view, offset, coeffs),
}
}
fn vert_convolution(
src_view: &impl ImageView<Pixel = Self>,
dst_view: &mut impl ImageViewMut<Pixel = Self>,
offset: u32,
coeffs: Coefficients,
cpu_extensions: CpuExtensions,
) {
vert_convolution_f32(src_view, dst_view, offset, coeffs, cpu_extensions);
}
}
+27
View File
@@ -0,0 +1,27 @@
use crate::convolution::Coefficients;
use crate::pixels::F32x4;
use crate::{ImageView, ImageViewMut};
pub(crate) fn horiz_convolution(
src_view: &impl ImageView<Pixel = F32x4>,
dst_view: &mut impl ImageViewMut<Pixel = F32x4>,
offset: u32,
coeffs: Coefficients,
) {
let coefficients_chunks = coeffs.get_chunks();
let src_rows = src_view.iter_rows(offset);
let dst_rows = dst_view.iter_rows_mut(0);
for (dst_row, src_row) in dst_rows.zip(src_rows) {
for (dst_pixel, coeffs_chunk) in dst_row.iter_mut().zip(&coefficients_chunks) {
let first_x_src = coeffs_chunk.start as usize;
let mut ss = [0.; 4];
let src_pixels = unsafe { src_row.get_unchecked(first_x_src..) };
for (&k, &src_pixel) in coeffs_chunk.values.iter().zip(src_pixels) {
for (s, c) in ss.iter_mut().zip(src_pixel.0) {
*s += c as f64 * k;
}
}
dst_pixel.0 = ss.map(|v| v as f32);
}
}
}
+1
View File
@@ -23,6 +23,7 @@ cfg_if::cfg_if! {
mod f32x1;
mod f32x2;
mod f32x3;
mod f32x4;
mod vertical_u16;
mod vertical_f32;
}
+6 -1
View File
@@ -1,6 +1,6 @@
use crate::cpu_extensions::CpuExtensions;
use crate::image_view::{try_pixel_type, ImageViewMut, IntoImageView, IntoImageViewMut};
use crate::pixels::{F32x2, U16x2, U16x4, U8x2, U8x4};
use crate::pixels::{F32x2, F32x4, U16x2, U16x4, U8x2, U8x4};
use crate::{ImageError, ImageView, MulDivImagesError, PixelTrait, PixelType};
/// Methods of this structure used to multiply or divide color-channels (RGB or Luma)
@@ -65,6 +65,7 @@ impl MulDiv {
PixelType::U16x2 => self.multiply::<U16x2>(src_image, dst_image),
PixelType::U16x4 => self.multiply::<U16x4>(src_image, dst_image),
PixelType::F32x2 => self.multiply::<F32x2>(src_image, dst_image),
PixelType::F32x4 => self.multiply::<F32x4>(src_image, dst_image),
_ => Err(MulDivImagesError::ImageError(
ImageError::UnsupportedPixelType,
)),
@@ -121,6 +122,7 @@ impl MulDiv {
PixelType::U16x2 => self.multiply_inplace::<U16x2>(image),
PixelType::U16x4 => self.multiply_inplace::<U16x4>(image),
PixelType::F32x2 => self.multiply_inplace::<F32x2>(image),
PixelType::F32x4 => self.multiply_inplace::<F32x4>(image),
_ => Err(ImageError::UnsupportedPixelType),
}
@@ -173,6 +175,7 @@ impl MulDiv {
PixelType::U16x2 => self.divide::<U16x2>(src_image, dst_image),
PixelType::U16x4 => self.divide::<U16x4>(src_image, dst_image),
PixelType::F32x2 => self.divide::<F32x2>(src_image, dst_image),
PixelType::F32x4 => self.divide::<F32x4>(src_image, dst_image),
_ => Err(MulDivImagesError::ImageError(
ImageError::UnsupportedPixelType,
)),
@@ -229,6 +232,7 @@ impl MulDiv {
PixelType::U16x2 => self.divide_inplace::<U16x2>(image),
PixelType::U16x4 => self.divide_inplace::<U16x4>(image),
PixelType::F32x2 => self.divide_inplace::<F32x2>(image),
PixelType::F32x4 => self.divide_inplace::<F32x4>(image),
_ => Err(ImageError::UnsupportedPixelType),
}
@@ -271,6 +275,7 @@ impl MulDiv {
| PixelType::U16x2
| PixelType::U16x4
| PixelType::F32x2
| PixelType::F32x4
)
}
#[cfg(feature = "only_u8x4")]
+13 -2
View File
@@ -19,6 +19,7 @@ pub enum PixelType {
F32,
F32x2,
F32x3,
F32x4,
}
impl PixelType {
@@ -34,6 +35,7 @@ impl PixelType {
Self::U16x4 => 8,
Self::F32x2 => 8,
Self::F32x3 => 12,
Self::F32x4 => 16,
_ => 4,
}
}
@@ -53,6 +55,7 @@ impl PixelType {
Self::F32 => unsafe { buffer.align_to::<F32>().0.is_empty() },
Self::F32x2 => unsafe { buffer.align_to::<F32x2>().0.is_empty() },
Self::F32x3 => unsafe { buffer.align_to::<F32x3>().0.is_empty() },
Self::F32x4 => unsafe { buffer.align_to::<F32x4>().0.is_empty() },
}
}
}
@@ -311,7 +314,7 @@ pixel_struct!(
f32,
2,
PixelType::F32x2,
"Two `f32` component per pixel (e.g. LA-F32)"
"Two `f32` component per pixel (e.g. LA32F)"
);
pixel_struct!(
F32x3,
@@ -319,7 +322,15 @@ pixel_struct!(
f32,
3,
PixelType::F32x3,
"Three `f32` components per pixel (e.g. RGB-F32)"
"Three `f32` components per pixel (e.g. RGB32F)"
);
pixel_struct!(
F32x4,
[f32; 4],
f32,
4,
PixelType::F32x4,
"Four `f32` components per pixel (e.g. RGBA32F)"
);
pub trait IntoPixelComponent<Out: PixelComponent>
+1
View File
@@ -179,6 +179,7 @@ impl Resizer {
(PT::F32, pixels::F32),
(PT::F32x2, pixels::F32x2),
(PT::F32x3, pixels::F32x3),
(PT::F32x4, pixels::F32x4),
);
#[cfg(feature = "only_u8x4")]
+24 -1
View File
@@ -61,6 +61,7 @@ pub trait PixelTestingExt: PixelTrait {
PixelType::F32 => "f32",
PixelType::F32x2 => "f32x2",
PixelType::F32x3 => "f32x3",
PixelType::F32x4 => "f32x4",
_ => unreachable!(),
}
}
@@ -100,7 +101,8 @@ pub trait PixelTestingExt: PixelTrait {
| PixelType::U8x4
| PixelType::U16x2
| PixelType::U16x4
| PixelType::F32x2 => (
| PixelType::F32x2
| PixelType::F32x4 => (
"./data/nasa-4928x3279-rgba.png",
"./data/nasa-4019x4019-rgba.png",
"./data/nasa-852x567-rgba.png",
@@ -367,6 +369,21 @@ impl PixelTestingExt for F32x3 {
}
}
impl PixelTestingExt for F32x4 {
type ImagePixel = image::Rgba<f32>;
type Container = Vec<f32>;
fn load_image_buffer(
img_reader: Reader<BufReader<File>>,
) -> ImageBuffer<Self::ImagePixel, Self::Container> {
img_reader.decode().unwrap().to_rgba32f()
}
fn img_into_bytes(img: ImageBuffer<Self::ImagePixel, Self::Container>) -> Vec<u8> {
img.as_raw().iter().flat_map(|&c| c.to_le_bytes()).collect()
}
}
pub fn save_result(image: &Image, name: &str) {
if std::env::var("SAVE_RESULT").unwrap_or_else(|_| "".to_owned()) == "" {
return;
@@ -401,6 +418,12 @@ pub fn save_result(image: &Image, name: &str) {
save_result(&image_u16, name);
return;
}
PixelType::F32x4 => {
let mut image_u16 = Image::new(image.width(), image.height(), PixelType::U16x4);
change_type_of_pixel_components(image, &mut image_u16).unwrap();
save_result(&image_u16, name);
return;
}
_ => panic!("Unsupported type of pixels"),
};
image::save_buffer(
+29
View File
@@ -489,4 +489,33 @@ mod f32_tests {
}
}
}
#[cfg(test)]
mod f32x4 {
use fast_image_resize::pixels::F32x4;
use super::*;
type P = F32x4;
const fn new_pixel(c: f32, a: f32) -> P {
P::new([c, c, c, a])
}
#[test]
fn multiple_alpha_test() {
let (scr_pixels, expected_pixels) = get_mul_test_cases_f32(new_pixel);
for cpu_extensions in P::cpu_extensions() {
mul_div_alpha_test(Oper::Mul, &scr_pixels, &expected_pixels, cpu_extensions);
}
}
#[test]
fn divide_alpha_test() {
let (scr_pixels, expected_pixels) = get_div_test_cases_f32(new_pixel);
for cpu_extensions in P::cpu_extensions() {
mul_div_alpha_test(Oper::Div, &scr_pixels, &expected_pixels, cpu_extensions);
}
}
}
}
+57
View File
@@ -754,6 +754,63 @@ mod not_u8x4 {
}
}
// F32x4
#[test]
fn downscale_f32x4() {
type P = F32x4;
P::downscale_test(
ResizeAlg::Nearest,
CpuExtensions::None,
[
28271357515050,
34102344731602,
34154875278897,
26023210300788,
],
);
for cpu_extensions in P::cpu_extensions() {
P::downscale_test(
ResizeAlg::Convolution(FilterType::Lanczos3),
cpu_extensions,
[
41676905227663,
40314876714373,
40146438250679,
29873206892121,
],
);
}
}
#[test]
fn upscale_f32x4() {
type P = F32x4;
P::upscale_test(
ResizeAlg::Nearest,
CpuExtensions::None,
[
9939217321658853,
9944847383085392,
9940281434784023,
10060767588318486,
],
);
for cpu_extensions in P::cpu_extensions() {
P::upscale_test(
ResizeAlg::Convolution(FilterType::Lanczos3),
cpu_extensions,
[
10431959951229359,
10423928285570087,
10420567450069105,
10465695788378423,
],
);
}
}
#[test]
fn fractional_cropping() {
let mut src_buf = [0, 0, 0, 0, 255, 0, 0, 0, 0];