mirror of
https://github.com/Cykooz/fast_image_resize.git
synced 2026-10-08 01:11:09 +00:00
Fixed error in NEON implementation of MulDiv::multiply_alpha() and MulDiv::multiply_alpha_inplace() for U8x2 pixels (#49).
This commit is contained in:
@@ -1,3 +1,11 @@
|
||||
## [Unreleased] - ReleaseDate
|
||||
|
||||
## Fixed
|
||||
|
||||
- Fixed error in `NEON` implementation of `MulDiv::multiply_alpha()` and
|
||||
`MulDiv::multiply_alpha_inplace()` for `U8x2` pixels
|
||||
([#49](https://github.com/Cykooz/fast_image_resize/issues/49)).
|
||||
|
||||
## [5.1.2] - 2025-02-16
|
||||
|
||||
## Fixed
|
||||
|
||||
+13
-24
@@ -36,13 +36,13 @@ unsafe fn multiply_alpha_row(src_row: &[U8x2], dst_row: &mut [U8x2]) {
|
||||
foreach_with_pre_reading(
|
||||
src_dst,
|
||||
|(src, dst)| {
|
||||
let pixels = neon_utils::load_deintrel_u8x16x2(src, 0);
|
||||
let pixels = neon_utils::load_deintrel_u8x16x4(src, 0);
|
||||
let dst_ptr = dst.as_mut_ptr() as *mut u8;
|
||||
(pixels, dst_ptr)
|
||||
},
|
||||
|(mut pixels, dst_ptr)| {
|
||||
pixels = multiplies_alpha_32_pixels(pixels);
|
||||
vst2q_u8(dst_ptr, pixels);
|
||||
vst4q_u8(dst_ptr, pixels);
|
||||
},
|
||||
);
|
||||
|
||||
@@ -82,19 +82,19 @@ unsafe fn multiply_alpha_row_inplace(row: &mut [U8x2]) {
|
||||
foreach_with_pre_reading(
|
||||
&mut chunks,
|
||||
|chunk| {
|
||||
let pixels = neon_utils::load_deintrel_u8x16x2(chunk, 0);
|
||||
let pixels = neon_utils::load_deintrel_u8x16x4(chunk, 0);
|
||||
let dst_ptr = chunk.as_mut_ptr() as *mut u8;
|
||||
(pixels, dst_ptr)
|
||||
},
|
||||
|(mut pixels, dst_ptr)| {
|
||||
pixels = multiplies_alpha_32_pixels(pixels);
|
||||
vst2q_u8(dst_ptr, pixels);
|
||||
vst4q_u8(dst_ptr, pixels);
|
||||
},
|
||||
);
|
||||
|
||||
let reminder = chunks.into_remainder();
|
||||
let mut chunks = reminder.chunks_exact_mut(16);
|
||||
for chunk in &mut chunks {
|
||||
if let Some(chunk) = chunks.next() {
|
||||
let mut pixels = neon_utils::load_deintrel_u8x8x4(chunk, 0);
|
||||
pixels = multiplies_alpha_16_pixels(pixels);
|
||||
let chunk_ptr = chunk.as_mut_ptr() as *mut u8;
|
||||
@@ -103,7 +103,7 @@ unsafe fn multiply_alpha_row_inplace(row: &mut [U8x2]) {
|
||||
|
||||
let reminder = chunks.into_remainder();
|
||||
let mut chunks = reminder.chunks_exact_mut(8);
|
||||
for chunk in &mut chunks {
|
||||
if let Some(chunk) = chunks.next() {
|
||||
let mut pixels = neon_utils::load_deintrel_u8x8x2(chunk, 0);
|
||||
pixels = multiplies_alpha_8_pixels(pixels);
|
||||
let chunk_ptr = chunk.as_mut_ptr() as *mut u8;
|
||||
@@ -116,31 +116,20 @@ unsafe fn multiply_alpha_row_inplace(row: &mut [U8x2]) {
|
||||
}
|
||||
}
|
||||
|
||||
// #[inline(always)]
|
||||
// unsafe fn multiplies_alpha_64_pixels(mut pixels: uint8x16x4_t) -> uint8x16x4_t {
|
||||
// let zero_u8x16 = vdupq_n_u8(0);
|
||||
// let alpha_u16 = uint16x8x2_t(
|
||||
// vreinterpretq_u16_u8(vzip1q_u8(pixels.1, zero_u8x16)),
|
||||
// vreinterpretq_u16_u8(vzip2q_u8(pixels.1, zero_u8x16)),
|
||||
// );
|
||||
// pixels.0 = neon_utils::mul_color_to_alpha_u8x16(pixels.0, alpha_u16, zero_u8x16);
|
||||
|
||||
// let alpha_u16 = uint16x8x2_t(
|
||||
// vreinterpretq_u16_u8(vzip1q_u8(pixels.3, zero_u8x16)),
|
||||
// vreinterpretq_u16_u8(vzip2q_u8(pixels.3, zero_u8x16)),
|
||||
// );
|
||||
// pixels.2 = neon_utils::mul_color_to_alpha_u8x16(pixels.2, alpha_u16, zero_u8x16);
|
||||
// pixels
|
||||
// }
|
||||
|
||||
#[inline(always)]
|
||||
unsafe fn multiplies_alpha_32_pixels(mut pixels: uint8x16x2_t) -> uint8x16x2_t {
|
||||
unsafe fn multiplies_alpha_32_pixels(mut pixels: uint8x16x4_t) -> uint8x16x4_t {
|
||||
let zero_u8x16 = vdupq_n_u8(0);
|
||||
let alpha_u16 = uint16x8x2_t(
|
||||
vreinterpretq_u16_u8(vzip1q_u8(pixels.1, zero_u8x16)),
|
||||
vreinterpretq_u16_u8(vzip2q_u8(pixels.1, zero_u8x16)),
|
||||
);
|
||||
pixels.0 = neon_utils::mul_color_to_alpha_u8x16(pixels.0, alpha_u16, zero_u8x16);
|
||||
|
||||
let alpha_u16 = uint16x8x2_t(
|
||||
vreinterpretq_u16_u8(vzip1q_u8(pixels.3, zero_u8x16)),
|
||||
vreinterpretq_u16_u8(vzip2q_u8(pixels.3, zero_u8x16)),
|
||||
);
|
||||
pixels.2 = neon_utils::mul_color_to_alpha_u8x16(pixels.2, alpha_u16, zero_u8x16);
|
||||
pixels
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -149,7 +149,7 @@ impl Image<'static> {
|
||||
}
|
||||
|
||||
impl<'a> Image<'a> {
|
||||
/// Create an image with from slice with pixels data.
|
||||
/// Create an image from slice with pixels data.
|
||||
pub fn from_slice_u8(
|
||||
width: u32,
|
||||
height: u32,
|
||||
|
||||
+65
-63
@@ -23,78 +23,80 @@ fn mul_div_alpha_test<P: PixelTrait>(
|
||||
return;
|
||||
}
|
||||
|
||||
let width: u32 = 8 + 8 + 7;
|
||||
let height: u32 = 3;
|
||||
for width in [1, 9, 17, 25, 33, 41, 49, 57, 65] {
|
||||
let src_size = width as usize * height as usize;
|
||||
let src_pixels: Vec<P> = src_pixels_tpl
|
||||
.iter()
|
||||
.copied()
|
||||
.cycle()
|
||||
.take(src_size)
|
||||
.collect();
|
||||
let mut dst_pixels = src_pixels.clone();
|
||||
|
||||
let src_size = width as usize * height as usize;
|
||||
let src_pixels: Vec<P> = src_pixels_tpl
|
||||
.iter()
|
||||
.copied()
|
||||
.cycle()
|
||||
.take(src_size)
|
||||
.collect();
|
||||
let mut dst_pixels = src_pixels.clone();
|
||||
let src_image = TypedImageRef::new(width, height, &src_pixels).unwrap();
|
||||
let mut dst_image = TypedImage::from_pixels_slice(width, height, &mut dst_pixels).unwrap();
|
||||
|
||||
let src_image = TypedImageRef::new(width, height, &src_pixels).unwrap();
|
||||
let mut dst_image = TypedImage::from_pixels_slice(width, height, &mut dst_pixels).unwrap();
|
||||
let mut alpha_mul_div: MulDiv = Default::default();
|
||||
unsafe {
|
||||
alpha_mul_div.set_cpu_extensions(cpu_extensions);
|
||||
}
|
||||
|
||||
let mut alpha_mul_div: MulDiv = Default::default();
|
||||
unsafe {
|
||||
alpha_mul_div.set_cpu_extensions(cpu_extensions);
|
||||
}
|
||||
match oper {
|
||||
Oper::Mul => alpha_mul_div
|
||||
.multiply_alpha_typed(&src_image, &mut dst_image)
|
||||
.unwrap(),
|
||||
Oper::Div => alpha_mul_div
|
||||
.divide_alpha_typed(&src_image, &mut dst_image)
|
||||
.unwrap(),
|
||||
}
|
||||
|
||||
match oper {
|
||||
Oper::Mul => alpha_mul_div
|
||||
.multiply_alpha_typed(&src_image, &mut dst_image)
|
||||
.unwrap(),
|
||||
Oper::Div => alpha_mul_div
|
||||
.divide_alpha_typed(&src_image, &mut dst_image)
|
||||
.unwrap(),
|
||||
}
|
||||
let oper_str = if oper == Oper::Mul {
|
||||
"multiple"
|
||||
} else {
|
||||
"divide"
|
||||
};
|
||||
|
||||
let oper_str = if oper == Oper::Mul {
|
||||
"multiple"
|
||||
} else {
|
||||
"divide"
|
||||
};
|
||||
let cpu_ext_str = cpu_ext_into_str(cpu_extensions);
|
||||
let expected_pixels: Vec<P> = expected_pixels_tpl
|
||||
.iter()
|
||||
.copied()
|
||||
.cycle()
|
||||
.take(src_size)
|
||||
.collect();
|
||||
for ((s, r), e) in src_pixels
|
||||
.iter()
|
||||
.zip(dst_pixels)
|
||||
.zip(expected_pixels.iter())
|
||||
{
|
||||
assert_eq!(
|
||||
r, *e,
|
||||
"failed test for {oper_str} alpha with '{cpu_ext_str}' CPU extensions \
|
||||
and image width {width}: src={s:?}, result={r:?}, expected_result={e:?}",
|
||||
);
|
||||
}
|
||||
|
||||
let cpu_ext_str = cpu_ext_into_str(cpu_extensions);
|
||||
let expected_pixels: Vec<P> = expected_pixels_tpl
|
||||
.iter()
|
||||
.copied()
|
||||
.cycle()
|
||||
.take(src_size)
|
||||
.collect();
|
||||
for ((s, r), e) in src_pixels
|
||||
.iter()
|
||||
.zip(dst_pixels)
|
||||
.zip(expected_pixels.iter())
|
||||
{
|
||||
assert_eq!(
|
||||
r, *e,
|
||||
"failed test for {oper_str} alpha with '{cpu_ext_str}' CPU extensions: \
|
||||
src={s:?}, result={r:?}, expected_result={e:?}",
|
||||
);
|
||||
}
|
||||
// Inplace
|
||||
let mut src_pixels_clone = src_pixels.clone();
|
||||
let mut image =
|
||||
TypedImage::from_pixels_slice(width, height, &mut src_pixels_clone).unwrap();
|
||||
|
||||
// Inplace
|
||||
let mut src_pixels_clone = src_pixels.clone();
|
||||
let mut image = TypedImage::from_pixels_slice(width, height, &mut src_pixels_clone).unwrap();
|
||||
match oper {
|
||||
Oper::Mul => alpha_mul_div
|
||||
.multiply_alpha_inplace_typed(&mut image)
|
||||
.unwrap(),
|
||||
Oper::Div => alpha_mul_div
|
||||
.divide_alpha_inplace_typed(&mut image)
|
||||
.unwrap(),
|
||||
}
|
||||
|
||||
match oper {
|
||||
Oper::Mul => alpha_mul_div
|
||||
.multiply_alpha_inplace_typed(&mut image)
|
||||
.unwrap(),
|
||||
Oper::Div => alpha_mul_div
|
||||
.divide_alpha_inplace_typed(&mut image)
|
||||
.unwrap(),
|
||||
}
|
||||
|
||||
for ((s, r), e) in src_pixels.iter().zip(src_pixels_clone).zip(expected_pixels) {
|
||||
assert_eq!(
|
||||
r, e,
|
||||
"failed inplace test for {oper_str} alpha: src={s:?}, result={r:?}, expected_result={e:?}",
|
||||
);
|
||||
for ((s, r), e) in src_pixels.iter().zip(src_pixels_clone).zip(expected_pixels) {
|
||||
assert_eq!(
|
||||
r, e,
|
||||
"failed inplace test for {oper_str} alpha with '{cpu_ext_str}' CPU extensions \
|
||||
and image width {width}: src={s:?}, result={r:?}, expected_result={e:?}",
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user