mirror of
https://github.com/Cykooz/fast_image_resize.git
synced 2026-10-08 01:11:09 +00:00
Fixed dividing image by alpha channel.
This commit is contained in:
@@ -32,6 +32,10 @@ A lot of breaking changes have been done in this release:
|
||||
type from the `image` crate. It allows you to use `DynamicImage` instances
|
||||
as arguments for `Resize::resize()` method.
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fixed dividing image by alpha channel.
|
||||
|
||||
## [3.0.4] - 2024-02-15
|
||||
|
||||
### Fixed
|
||||
|
||||
+8
-9
@@ -35,16 +35,18 @@ const fn recip_alpha16_array(precision: u64) -> [u64; 65536] {
|
||||
}
|
||||
|
||||
const PRECISION: u32 = 8;
|
||||
const ROUND_CORRECTION: u32 = 1 << (PRECISION - 1);
|
||||
const PRECISION16: u64 = 33;
|
||||
const ROUND_CORRECTION16: u64 = 1 << (PRECISION16 - 1);
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn div_and_clip(v: u8, recip_alpha: u32) -> u8 {
|
||||
((v as u32 * recip_alpha) >> PRECISION).min(255) as u8
|
||||
((v as u32 * recip_alpha + ROUND_CORRECTION) >> PRECISION).min(0xff) as u8
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn div_and_clip16(v: u16, recip_alpha: u64) -> u16 {
|
||||
((v as u64 * recip_alpha) >> PRECISION16).min(65535) as u16
|
||||
((v as u64 * recip_alpha + ROUND_CORRECTION16) >> PRECISION16).min(0xffff) as u16
|
||||
}
|
||||
|
||||
pub(crate) const RECIP_ALPHA: [u32; 256] = recip_alpha_array(PRECISION);
|
||||
@@ -74,23 +76,20 @@ mod tests {
|
||||
let mut err_sum: i32 = 0;
|
||||
for alpha in 0..=255u8 {
|
||||
for color in 0..=255u8 {
|
||||
let multiplied_color = (color as f64 * alpha as f64 / 255.).round().min(255.) as u8;
|
||||
|
||||
let expected_color = if alpha == 0 {
|
||||
0
|
||||
} else {
|
||||
let recip_alpha = 255. / alpha as f64;
|
||||
let res = multiplied_color as f64 * recip_alpha;
|
||||
res.min(255.) as u8
|
||||
let res = color as f64 / (alpha as f64 / 255.);
|
||||
res.round().min(255.) as u8
|
||||
};
|
||||
|
||||
let recip_alpha = RECIP_ALPHA[alpha as usize];
|
||||
let result_color = div_and_clip(multiplied_color, recip_alpha);
|
||||
let result_color = div_and_clip(color, recip_alpha);
|
||||
let delta = result_color as i32 - expected_color as i32;
|
||||
err_sum += delta.abs();
|
||||
}
|
||||
}
|
||||
assert_eq!(err_sum, 3468);
|
||||
assert_eq!(err_sum, 2512);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -181,9 +181,6 @@ unsafe fn divide_alpha_row_inplace(row: &mut [U16x2]) {
|
||||
#[inline]
|
||||
#[target_feature(enable = "simd128")]
|
||||
unsafe fn divide_alpha_4_pixels(pixels: v128) -> v128 {
|
||||
const ALPHA_MASK: v128 = u32x4(0xffff0000, 0xffff0000, 0xffff0000, 0xffff0000);
|
||||
const LUMA_MASK: v128 = u32x4(0xffff, 0xffff, 0xffff, 0xffff);
|
||||
const ALPHA_MAX: v128 = f32x4(65535.0, 65535.0, 65535.0, 65535.0);
|
||||
/*
|
||||
|L0 A0 | |L1 A1 | |L2 A2 | |L3 A3 |
|
||||
|0001 0203| |0405 0607| |0809 1011| |1213 1415|
|
||||
@@ -191,14 +188,20 @@ unsafe fn divide_alpha_4_pixels(pixels: v128) -> v128 {
|
||||
const ALPHA32_SH: v128 = i8x16(2, 3, -1, -1, 6, 7, -1, -1, 10, 11, -1, -1, 14, 15, -1, -1);
|
||||
|
||||
let alpha_f32x4 = f32x4_convert_i32x4(u8x16_swizzle(pixels, ALPHA32_SH));
|
||||
let luma_f32x4 = f32x4_convert_i32x4(v128_and(pixels, LUMA_MASK));
|
||||
let scaled_luma_f32x4 = f32x4_mul(luma_f32x4, ALPHA_MAX);
|
||||
let luma_mask = u32x4_splat(0xffff);
|
||||
let luma_f32x4 = f32x4_convert_i32x4(v128_and(pixels, luma_mask));
|
||||
let alpha_max = f32x4_splat(65535.0);
|
||||
let scaled_luma_f32x4 = f32x4_mul(luma_f32x4, alpha_max);
|
||||
// In case of zero division the result will be u32::MAX or 0.
|
||||
let divided_luma_u32x4 = u32x4_trunc_sat_f32x4(f32x4_div(scaled_luma_f32x4, alpha_f32x4));
|
||||
let divided_luma_u32x4 = u32x4_trunc_sat_f32x4(f32x4_add(
|
||||
f32x4_div(scaled_luma_f32x4, alpha_f32x4),
|
||||
f32x4_splat(0.5),
|
||||
));
|
||||
// All u32::MAX values in arguments will interpreted as -1i32.
|
||||
// u16x8_narrow_i32x4() converts all negative values into 0.
|
||||
let divided_luma_u16 = u16x8_narrow_i32x4(divided_luma_u32x4, divided_luma_u32x4);
|
||||
|
||||
let alpha = v128_and(pixels, ALPHA_MASK);
|
||||
let alpha_mask = u32x4_splat(0xffff0000);
|
||||
let alpha = v128_and(pixels, alpha_mask);
|
||||
v128_or(u32x4_extend_low_u16x8(divided_luma_u16), alpha)
|
||||
}
|
||||
|
||||
@@ -190,8 +190,7 @@ unsafe fn divide_alpha_row_inplace(row: &mut [U16x4]) {
|
||||
#[target_feature(enable = "simd128")]
|
||||
unsafe fn divide_alpha_2_pixels(pixels: v128) -> v128 {
|
||||
let zero = u64x2_splat(0);
|
||||
let alpha_mask = u64x2_splat(0xffff000000000000);
|
||||
let alpha_max = f32x4_splat(65535.0);
|
||||
|
||||
/*
|
||||
|R0 G0 B0 A0 | |R1 G1 B1 A1 |
|
||||
|0001 0203 0405 0607| |0809 1011 1213 1415|
|
||||
@@ -208,18 +207,24 @@ unsafe fn divide_alpha_2_pixels(pixels: v128) -> v128 {
|
||||
let pix_hi_f32x4 =
|
||||
f32x4_convert_i32x4(i16x8_shuffle::<4, 12, 5, 13, 6, 14, 7, 15>(pixels, zero));
|
||||
|
||||
let alpha_max = f32x4_splat(65535.0);
|
||||
let scaled_pix_lo_f32x4 = f32x4_mul(pix_lo_f32x4, alpha_max);
|
||||
let scaled_pix_hi_f32x4 = f32x4_mul(pix_hi_f32x4, alpha_max);
|
||||
|
||||
// In case of zero division the result will be u32::MAX or 0.
|
||||
let divided_pix_lo_u32x4 =
|
||||
u32x4_trunc_sat_f32x4(f32x4_div(scaled_pix_lo_f32x4, alpha_lo_f32x4));
|
||||
let divided_pix_hi_u32x4 =
|
||||
u32x4_trunc_sat_f32x4(f32x4_div(scaled_pix_hi_f32x4, alpha_hi_f32x4));
|
||||
let divided_pix_lo_u32x4 = u32x4_trunc_sat_f32x4(f32x4_add(
|
||||
f32x4_div(scaled_pix_lo_f32x4, alpha_lo_f32x4),
|
||||
f32x4_splat(0.5),
|
||||
));
|
||||
let divided_pix_hi_u32x4 = u32x4_trunc_sat_f32x4(f32x4_add(
|
||||
f32x4_div(scaled_pix_hi_f32x4, alpha_hi_f32x4),
|
||||
f32x4_splat(0.5),
|
||||
));
|
||||
|
||||
// All u32::MAX values in arguments will interpreted as -1i32.
|
||||
// u16x8_narrow_i32x4() converts all negative values into 0.
|
||||
let two_pixels_i16x8 = u16x8_narrow_i32x4(divided_pix_lo_u32x4, divided_pix_hi_u32x4);
|
||||
let alpha_mask = u64x2_splat(0xffff000000000000);
|
||||
let alpha = v128_and(pixels, alpha_mask);
|
||||
v128_or(two_pixels_i16x8, alpha)
|
||||
}
|
||||
|
||||
@@ -199,8 +199,9 @@ unsafe fn divide_alpha_16_pixels(pixels: __m256i) -> __m256i {
|
||||
let scaled_alpha_i16 = _mm256_packus_epi32(scaled_alpha_lo_i32, scaled_alpha_hi_i32);
|
||||
|
||||
let luma_i16 = _mm256_and_si256(pixels, luma_mask);
|
||||
let scaled_luma_i16 = _mm256_mullo_epi16(luma_i16, scaled_alpha_i16);
|
||||
let scaled_luma_i16 = _mm256_srli_epi16::<8>(scaled_luma_i16);
|
||||
let luma_i16 = _mm256_slli_epi16::<7>(luma_i16);
|
||||
let scaled_luma_i16 = _mm256_mulhrs_epi16(luma_i16, scaled_alpha_i16);
|
||||
let scaled_luma_i16 = _mm256_min_epu16(scaled_luma_i16, luma_mask);
|
||||
|
||||
let alpha = _mm256_and_si256(pixels, alpha_mask);
|
||||
_mm256_blendv_epi8(scaled_luma_i16, alpha, alpha_mask)
|
||||
|
||||
@@ -216,8 +216,9 @@ unsafe fn divide_alpha_8_pixels(pixels: __m128i) -> __m128i {
|
||||
let scaled_alpha_i16 = _mm_packus_epi32(scaled_alpha_lo_i32, scaled_alpha_hi_i32);
|
||||
|
||||
let luma_i16 = _mm_and_si128(pixels, luma_mask);
|
||||
let scaled_luma_i16 = _mm_mullo_epi16(luma_i16, scaled_alpha_i16);
|
||||
let scaled_luma_i16 = _mm_srli_epi16::<8>(scaled_luma_i16);
|
||||
let luma_i16 = _mm_slli_epi16::<7>(luma_i16);
|
||||
let scaled_luma_i16 = _mm_mulhrs_epi16(luma_i16, scaled_alpha_i16);
|
||||
let scaled_luma_i16 = _mm_min_epu16(scaled_luma_i16, luma_mask);
|
||||
|
||||
let alpha = _mm_and_si128(pixels, alpha_mask);
|
||||
_mm_blendv_epi8(scaled_luma_i16, alpha, alpha_mask)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use std::arch::wasm32::*;
|
||||
|
||||
use crate::pixels::U8x2;
|
||||
use crate::wasm32_utils::{u16x8_mul_add_shr16, u16x8_mul_shr16};
|
||||
use crate::{ImageView, ImageViewMut};
|
||||
|
||||
use super::native;
|
||||
@@ -174,8 +175,6 @@ unsafe fn divide_alpha_row_inplace(row: &mut [U8x2]) {
|
||||
#[inline]
|
||||
#[target_feature(enable = "simd128")]
|
||||
unsafe fn divide_alpha_8_pixels(pixels: v128) -> v128 {
|
||||
let alpha_mask = i16x8_splat(0xff00u16 as i16);
|
||||
let luma_mask = i16x8_splat(0xff);
|
||||
const ALPHA32_SH_LO: v128 = i8x16(1, -1, -1, -1, 3, -1, -1, -1, 5, -1, -1, -1, 7, -1, -1, -1);
|
||||
const ALPHA32_SH_HI: v128 = i8x16(
|
||||
9, -1, -1, -1, 11, -1, -1, -1, 13, -1, -1, -1, 15, -1, -1, -1,
|
||||
@@ -184,20 +183,28 @@ unsafe fn divide_alpha_8_pixels(pixels: v128) -> v128 {
|
||||
|
||||
let alpha_lo_f32 = f32x4_convert_u32x4(u8x16_swizzle(pixels, ALPHA32_SH_LO));
|
||||
// In case of zero division the result will be u32::MAX or 0.
|
||||
let scaled_alpha_lo_u32 = u32x4_trunc_sat_f32x4(f32x4_div(alpha_scale, alpha_lo_f32));
|
||||
let scaled_alpha_lo_u32 = u32x4_trunc_sat_f32x4(f32x4_add(
|
||||
f32x4_div(alpha_scale, alpha_lo_f32),
|
||||
f32x4_splat(0.5),
|
||||
));
|
||||
|
||||
let alpha_hi_f32 = f32x4_convert_u32x4(i8x16_swizzle(pixels, ALPHA32_SH_HI));
|
||||
let scaled_alpha_hi_u32 = u32x4_trunc_sat_f32x4(f32x4_div(alpha_scale, alpha_hi_f32));
|
||||
let alpha_hi_f32 = f32x4_convert_u32x4(u8x16_swizzle(pixels, ALPHA32_SH_HI));
|
||||
let scaled_alpha_hi_u32 = u32x4_trunc_sat_f32x4(f32x4_add(
|
||||
f32x4_div(alpha_scale, alpha_hi_f32),
|
||||
f32x4_splat(0.5),
|
||||
));
|
||||
|
||||
// All u32::MAX values in arguments will interpreted as -1i32.
|
||||
// u16x8_narrow_i32x4() converts all negative values into 0.
|
||||
let scaled_alpha_u16 = u16x8_narrow_i32x4(scaled_alpha_lo_u32, scaled_alpha_hi_u32);
|
||||
|
||||
let luma_u16 = v128_and(pixels, luma_mask);
|
||||
let scaled_luma_u16 = u16x8_mul(luma_u16, scaled_alpha_u16);
|
||||
let scaled_luma_u16 = u16x8_shr(scaled_luma_u16, 8);
|
||||
let luma_u16 = u16x8_shl(pixels, 8);
|
||||
let scaled_luma_u16 = u16x8_mul_add_shr16(luma_u16, scaled_alpha_u16, u32x4_splat(0x8000));
|
||||
let luma_max = u16x8_splat(0xff);
|
||||
let scaled_luma_u16 = u16x8_min(scaled_luma_u16, luma_max);
|
||||
|
||||
// Blend scaled luma with original alpha channel.
|
||||
let alpha_mask = u16x8_splat(0xff00);
|
||||
let alpha = v128_and(pixels, alpha_mask);
|
||||
v128_or(scaled_luma_u16, alpha)
|
||||
}
|
||||
|
||||
+24
-9
@@ -189,20 +189,35 @@ unsafe fn divide_alpha_8_pixels(pixels: __m256i) -> __m256i {
|
||||
13, 12, 13, 12, 13, 12, 13, 12, 9, 8, 9, 8, 9, 8, 9, 8,
|
||||
);
|
||||
let alpha_scale = _mm256_set1_ps(255.0 * 256.0);
|
||||
let max_value = _mm256_set1_epi16(0xff);
|
||||
|
||||
let alpha_f32 = _mm256_cvtepi32_ps(_mm256_srli_epi32::<24>(pixels));
|
||||
let scaled_alpha_f32 = _mm256_div_ps(alpha_scale, alpha_f32);
|
||||
let scaled_alpha_i32 = _mm256_cvtps_epi32(scaled_alpha_f32);
|
||||
let mma0 = _mm256_shuffle_epi8(scaled_alpha_i32, shuffle1);
|
||||
let mma1 = _mm256_shuffle_epi8(scaled_alpha_i32, shuffle2);
|
||||
let recip_alpha_i32 = _mm256_cvtps_epi32(_mm256_div_ps(alpha_scale, alpha_f32));
|
||||
|
||||
let pix0 = _mm256_unpacklo_epi8(zero, pixels);
|
||||
let pix1 = _mm256_unpackhi_epi8(zero, pixels);
|
||||
// Recip alpha in Q8.8 format
|
||||
let recip_alpha_lo_q8_8 = _mm256_shuffle_epi8(recip_alpha_i32, shuffle1);
|
||||
let recip_alpha_hi_q8_8 = _mm256_shuffle_epi8(recip_alpha_i32, shuffle2);
|
||||
|
||||
let pix0 = _mm256_mulhi_epu16(pix0, mma0);
|
||||
let pix1 = _mm256_mulhi_epu16(pix1, mma1);
|
||||
// Pixels components in format Q9.7
|
||||
let components_lo_q9_7 = _mm256_slli_epi16::<7>(_mm256_unpacklo_epi8(pixels, zero));
|
||||
let components_hi_q9_7 = _mm256_slli_epi16::<7>(_mm256_unpackhi_epi8(pixels, zero));
|
||||
|
||||
// Multiplied pixels components as i16.
|
||||
//
|
||||
// fn _mm256_mulhrs_epi16(a: i16, b: i16) -> i16 {
|
||||
// let tmp: i32 = ((a as i32 * b as i32) >> 14) + 1;
|
||||
// (tmp >> 1) as i16
|
||||
// }
|
||||
let res_components_lo_i16 = _mm256_min_epu16(
|
||||
_mm256_mulhrs_epi16(components_lo_q9_7, recip_alpha_lo_q8_8),
|
||||
max_value,
|
||||
);
|
||||
let res_components_hi_i16 = _mm256_min_epu16(
|
||||
_mm256_mulhrs_epi16(components_hi_q9_7, recip_alpha_hi_q8_8),
|
||||
max_value,
|
||||
);
|
||||
|
||||
let alpha = _mm256_and_si256(pixels, alpha_mask);
|
||||
let rgb = _mm256_packus_epi16(pix0, pix1);
|
||||
let rgb = _mm256_packus_epi16(res_components_lo_i16, res_components_hi_i16);
|
||||
_mm256_blendv_epi8(rgb, alpha, alpha_mask)
|
||||
}
|
||||
|
||||
@@ -25,9 +25,9 @@ impl AlphaMulDiv for U8x4 {
|
||||
#[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_image, dst_image) },
|
||||
CpuExtensions::Neon => unsafe { neon::multiply_alpha(src_view, dst_view) },
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
CpuExtensions::Simd128 => unsafe { wasm32::multiply_alpha(src_image, dst_image) },
|
||||
CpuExtensions::Simd128 => unsafe { wasm32::multiply_alpha(src_view, dst_view) },
|
||||
_ => native::multiply_alpha(src_view, dst_view),
|
||||
}
|
||||
Ok(())
|
||||
@@ -43,9 +43,9 @@ impl AlphaMulDiv for U8x4 {
|
||||
#[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) },
|
||||
CpuExtensions::Neon => unsafe { neon::multiply_alpha_inplace(image_view) },
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
CpuExtensions::Simd128 => unsafe { wasm32::multiply_alpha_inplace(image) },
|
||||
CpuExtensions::Simd128 => unsafe { wasm32::multiply_alpha_inplace(image_view) },
|
||||
_ => native::multiply_alpha_inplace(image_view),
|
||||
}
|
||||
Ok(())
|
||||
|
||||
+24
-10
@@ -202,19 +202,33 @@ unsafe fn divide_alpha_4_pixels(src_pixels: __m128i) -> __m128i {
|
||||
let alpha_scale = _mm_set1_ps(255.0 * 256.0);
|
||||
|
||||
let alpha_f32 = _mm_cvtepi32_ps(_mm_srli_epi32::<24>(src_pixels));
|
||||
let scaled_alpha_f32 = _mm_div_ps(alpha_scale, alpha_f32);
|
||||
let scaled_alpha_i32 = _mm_cvtps_epi32(scaled_alpha_f32);
|
||||
let mma0 = _mm_shuffle_epi8(scaled_alpha_i32, shuffle1);
|
||||
let mma1 = _mm_shuffle_epi8(scaled_alpha_i32, shuffle2);
|
||||
let recip_alpha_i32 = _mm_cvtps_epi32(_mm_div_ps(alpha_scale, alpha_f32));
|
||||
// Recip alpha in Q8.8 format
|
||||
let recip_alpha_lo_q8_8 = _mm_shuffle_epi8(recip_alpha_i32, shuffle1);
|
||||
let recip_alpha_hi_q8_8 = _mm_shuffle_epi8(recip_alpha_i32, shuffle2);
|
||||
|
||||
let pix0 = _mm_unpacklo_epi8(zero, src_pixels);
|
||||
let pix1 = _mm_unpackhi_epi8(zero, src_pixels);
|
||||
// Pixels components in format Q9.7
|
||||
let components_lo_q9_7 = _mm_slli_epi16::<7>(_mm_unpacklo_epi8(src_pixels, zero));
|
||||
let components_hi_q9_7 = _mm_slli_epi16::<7>(_mm_unpackhi_epi8(src_pixels, zero));
|
||||
|
||||
let pix0 = _mm_mulhi_epu16(pix0, mma0);
|
||||
let pix1 = _mm_mulhi_epu16(pix1, mma1);
|
||||
// Multiplied pixels components as i16.
|
||||
//
|
||||
// fn _mm_mulhrs_epi16(a: i16, b: i16) -> i16 {
|
||||
// let tmp: i32 = ((a as i32 * b as i32) >> 14) + 1;
|
||||
// (tmp >> 1) as i16
|
||||
// }
|
||||
let max_value = _mm_set1_epi16(0xff);
|
||||
let res_components_lo_i16 = _mm_min_epu16(
|
||||
_mm_mulhrs_epi16(components_lo_q9_7, recip_alpha_lo_q8_8),
|
||||
max_value,
|
||||
);
|
||||
let res_components_hi_i16 = _mm_min_epu16(
|
||||
_mm_mulhrs_epi16(components_hi_q9_7, recip_alpha_hi_q8_8),
|
||||
max_value,
|
||||
);
|
||||
|
||||
let alpha = _mm_and_si128(src_pixels, alpha_mask);
|
||||
let rgb = _mm_packus_epi16(pix0, pix1);
|
||||
let rgba = _mm_packus_epi16(res_components_lo_i16, res_components_hi_i16);
|
||||
|
||||
_mm_blendv_epi8(rgb, alpha, alpha_mask)
|
||||
_mm_blendv_epi8(rgba, alpha, alpha_mask)
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use std::arch::wasm32::*;
|
||||
|
||||
use crate::pixels::U8x4;
|
||||
use crate::wasm32_utils;
|
||||
use crate::wasm32_utils::u16x8_mul_add_shr16;
|
||||
use crate::{ImageView, ImageViewMut};
|
||||
|
||||
use super::native;
|
||||
@@ -171,28 +171,34 @@ unsafe fn divide_alpha_row_inplace(row: &mut [U8x4]) {
|
||||
unsafe fn divide_alpha_4_pixels(pixels: v128) -> v128 {
|
||||
const FACTOR_LO_SHUFFLE: v128 = i8x16(0, 1, 0, 1, 0, 1, -1, -1, 2, 3, 2, 3, 2, 3, -1, -1);
|
||||
const FACTOR_HI_SHUFFLE: v128 = i8x16(4, 5, 4, 5, 4, 5, -1, -1, 6, 7, 6, 7, 6, 7, -1, -1);
|
||||
let alpha_mask = u32x4_splat(0xff000000);
|
||||
|
||||
let alpha_scale = f32x4_splat(255.0 * 256.0);
|
||||
|
||||
let alpha_f32 = f32x4_convert_i32x4(u32x4_shr(pixels, 24));
|
||||
// In case of zero division the result will be u32::MAX or 0.
|
||||
let scaled_alpha_u32 = u32x4_trunc_sat_f32x4(f32x4_div(alpha_scale, alpha_f32));
|
||||
let scaled_alpha_u32 = u32x4_trunc_sat_f32x4(f32x4_add(
|
||||
f32x4_div(alpha_scale, alpha_f32),
|
||||
f32x4_splat(0.5),
|
||||
));
|
||||
// All u32::MAX values in arguments will interpreted as -1i32.
|
||||
// u16x8_narrow_i32x4() converts all negative values into 0.
|
||||
let scaled_alpha_u16 = u16x8_narrow_i32x4(scaled_alpha_u32, scaled_alpha_u32);
|
||||
let factor_lo_u16x8 = u8x16_swizzle(scaled_alpha_u16, FACTOR_LO_SHUFFLE);
|
||||
let factor_hi_u16x8 = u8x16_swizzle(scaled_alpha_u16, FACTOR_HI_SHUFFLE);
|
||||
|
||||
// alpha_mask's first byte is 0
|
||||
let zero = u32x4_splat(0);
|
||||
let src_u16_lo =
|
||||
u8x16_shuffle::<0, 16, 0, 17, 0, 18, 0, 19, 0, 20, 0, 21, 0, 22, 0, 23>(alpha_mask, pixels);
|
||||
u8x16_shuffle::<0, 16, 0, 17, 0, 18, 0, 19, 0, 20, 0, 21, 0, 22, 0, 23>(zero, pixels);
|
||||
let src_u16_hi =
|
||||
u8x16_shuffle::<0, 24, 0, 25, 0, 26, 0, 27, 0, 28, 0, 29, 0, 30, 0, 31>(alpha_mask, pixels);
|
||||
u8x16_shuffle::<0, 24, 0, 25, 0, 26, 0, 27, 0, 28, 0, 29, 0, 30, 0, 31>(zero, pixels);
|
||||
|
||||
let dst_lo = wasm32_utils::u16x8_mul_shr16(src_u16_lo, factor_lo_u16x8);
|
||||
let dst_hi = wasm32_utils::u16x8_mul_shr16(src_u16_hi, factor_hi_u16x8);
|
||||
let color_max = u16x8_splat(0xff);
|
||||
let dst_lo = u16x8_mul_add_shr16(src_u16_lo, factor_lo_u16x8, u32x4_splat(0x8000));
|
||||
let dst_lo = u16x8_min(dst_lo, color_max);
|
||||
let dst_hi = u16x8_mul_add_shr16(src_u16_hi, factor_hi_u16x8, u32x4_splat(0x8000));
|
||||
let dst_hi = u16x8_min(dst_hi, color_max);
|
||||
|
||||
let alpha = v128_and(pixels, alpha_mask);
|
||||
let alpha = v128_and(pixels, u32x4_splat(0xff000000));
|
||||
let rgb = u8x16_narrow_i16x8(dst_lo, dst_hi);
|
||||
v128_or(rgb, alpha)
|
||||
}
|
||||
|
||||
@@ -79,6 +79,14 @@ pub(crate) unsafe fn u16x8_mul_shr16(a_u16x8: v128, b_u16x8: v128) -> v128 {
|
||||
i16x8_shuffle::<1, 3, 5, 7, 9, 11, 13, 15>(lo_u32x4, hi_u32x4)
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn u16x8_mul_add_shr16(a_u16x8: v128, b_u16x8: v128, c: v128) -> v128 {
|
||||
let lo_u32x4 = u32x4_extmul_low_u16x8(a_u16x8, b_u16x8);
|
||||
let hi_u32x4 = u32x4_extmul_high_u16x8(a_u16x8, b_u16x8);
|
||||
let lo_u32x4 = u32x4_add(lo_u32x4, c);
|
||||
let hi_u32x4 = u32x4_add(hi_u32x4, c);
|
||||
i16x8_shuffle::<1, 3, 5, 7, 9, 11, 13, 15>(lo_u32x4, hi_u32x4)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[target_feature(enable = "simd128")]
|
||||
pub(crate) unsafe fn i64x2_mul_lo(a: v128, b: v128) -> v128 {
|
||||
|
||||
+275
-361
@@ -1,22 +1,69 @@
|
||||
use fast_image_resize::images::{TypedImage, TypedImageMut};
|
||||
use fast_image_resize::{CpuExtensions, MulDiv, PixelTrait, PixelType};
|
||||
use testing::cpu_ext_into_str;
|
||||
use fast_image_resize::images::{Image, TypedImage, TypedImageMut};
|
||||
use fast_image_resize::{CpuExtensions, MulDiv, PixelTrait};
|
||||
use testing::{cpu_ext_into_str, PixelTestingExt};
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
enum Oper {
|
||||
Mul,
|
||||
Div,
|
||||
}
|
||||
|
||||
// Multiplies by alpha
|
||||
#[derive(Clone, Copy)]
|
||||
struct TestCaseU16 {
|
||||
pub color: u16,
|
||||
pub alpha: u16,
|
||||
pub expected_color: u16,
|
||||
}
|
||||
|
||||
fn mul_div_alpha_test<P, const N: usize>(
|
||||
oper: Oper,
|
||||
src_pixels_tpl: [P; N],
|
||||
expected_pixels_tpl: [P; N],
|
||||
const fn new_case_16(c: u16, a: u16, e: u16) -> TestCaseU16 {
|
||||
TestCaseU16 {
|
||||
color: c,
|
||||
alpha: a,
|
||||
expected_color: e,
|
||||
}
|
||||
}
|
||||
|
||||
fn full_mul_div_alpha_test_u8<P: PixelTrait<Component = u8>>(
|
||||
create_pixel: fn(u8, u8) -> P,
|
||||
cpu_extensions: CpuExtensions,
|
||||
) where
|
||||
P: PixelTrait,
|
||||
{
|
||||
) {
|
||||
const PRECISION: u32 = 8;
|
||||
const ALPHA_SCALE: u32 = 255u32 * (1 << (PRECISION + 1));
|
||||
const ROUND_CORRECTION: u32 = 1 << (PRECISION - 1);
|
||||
|
||||
for oper in [Oper::Mul, Oper::Div] {
|
||||
for color in 0u8..=255u8 {
|
||||
for alpha in 0u8..=255u8 {
|
||||
let result_color = if alpha == 0 {
|
||||
0
|
||||
} else {
|
||||
match oper {
|
||||
Oper::Mul => {
|
||||
let tmp = color as u32 * alpha as u32 + 128;
|
||||
(((tmp >> 8) + tmp) >> 8) as u8
|
||||
}
|
||||
Oper::Div => {
|
||||
let recip_alpha = ((ALPHA_SCALE / alpha as u32) + 1) >> 1;
|
||||
let tmp = (color as u32 * recip_alpha + ROUND_CORRECTION) >> PRECISION;
|
||||
tmp.min(255) as u8
|
||||
}
|
||||
}
|
||||
};
|
||||
let src = [create_pixel(color, alpha)];
|
||||
let res = [create_pixel(result_color, alpha)];
|
||||
mul_div_alpha_test(oper, &src, &res, cpu_extensions);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn mul_div_alpha_test<P: PixelTrait>(
|
||||
oper: Oper,
|
||||
src_pixels_tpl: &[P],
|
||||
expected_pixels_tpl: &[P],
|
||||
cpu_extensions: CpuExtensions,
|
||||
) {
|
||||
assert_eq!(src_pixels_tpl.len(), expected_pixels_tpl.len());
|
||||
if !cpu_extensions.is_supported() {
|
||||
println!(
|
||||
"Cpu Extensions '{}' not supported by your CPU",
|
||||
@@ -24,6 +71,7 @@ fn mul_div_alpha_test<P, const N: usize>(
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
let width: u32 = 8 + 8 + 7;
|
||||
let height: u32 = 3;
|
||||
|
||||
@@ -37,7 +85,6 @@ fn mul_div_alpha_test<P, const N: usize>(
|
||||
let mut dst_pixels = src_pixels.clone();
|
||||
|
||||
let src_image = TypedImage::from_pixels(width, height, &src_pixels).unwrap();
|
||||
|
||||
let mut dst_image = TypedImageMut::from_pixels(width, height, &mut dst_pixels).unwrap();
|
||||
|
||||
let mut alpha_mul_div: MulDiv = Default::default();
|
||||
@@ -54,6 +101,12 @@ fn mul_div_alpha_test<P, const N: usize>(
|
||||
.unwrap(),
|
||||
}
|
||||
|
||||
let oper_str = if oper == Oper::Mul {
|
||||
"multiple"
|
||||
} else {
|
||||
"divide"
|
||||
};
|
||||
|
||||
let expected_pixels: Vec<P> = expected_pixels_tpl
|
||||
.iter()
|
||||
.copied()
|
||||
@@ -67,7 +120,7 @@ fn mul_div_alpha_test<P, const N: usize>(
|
||||
{
|
||||
assert_eq!(
|
||||
r, *e,
|
||||
"failed test: src={s:?}, result={r:?}, expected_result={e:?}",
|
||||
"failed test for {oper_str} alpha: src={s:?}, result={r:?}, expected_result={e:?}",
|
||||
);
|
||||
}
|
||||
|
||||
@@ -87,202 +140,140 @@ fn mul_div_alpha_test<P, const N: usize>(
|
||||
for ((s, r), e) in src_pixels.iter().zip(src_pixels_clone).zip(expected_pixels) {
|
||||
assert_eq!(
|
||||
r, e,
|
||||
"failed inplace test: src={s:?}, result={r:?}, expected_result={e:?}",
|
||||
"failed inplace test for {oper_str} alpha: src={s:?}, result={r:?}, expected_result={e:?}",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn run_tests_with_real_image_u8<P, const N: usize>(oper: Oper, expected_checksum: [u64; N])
|
||||
where
|
||||
P: PixelTrait<Component = u8> + PixelTestingExt,
|
||||
{
|
||||
let mut pixels = vec![0u8; 256 * 256 * N];
|
||||
let mut i: usize = 0;
|
||||
for alpha in 0..=255u8 {
|
||||
for color in 0..=255u8 {
|
||||
let pixel = pixels.get_mut(i..i + N).unwrap();
|
||||
for comp in pixel.iter_mut().take(N - 1) {
|
||||
*comp = color;
|
||||
}
|
||||
if let Some(c) = pixel.iter_mut().last() {
|
||||
*c = alpha;
|
||||
}
|
||||
i += N;
|
||||
}
|
||||
}
|
||||
let size = 256;
|
||||
let src_image = Image::from_vec_u8(size, size, pixels, P::pixel_type()).unwrap();
|
||||
let mut dst_image = Image::new(size, size, P::pixel_type());
|
||||
|
||||
let mut alpha_mul_div: MulDiv = Default::default();
|
||||
|
||||
let mut cpu_extensions_vec = vec![CpuExtensions::None];
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
{
|
||||
cpu_extensions_vec.push(CpuExtensions::Sse4_1);
|
||||
cpu_extensions_vec.push(CpuExtensions::Avx2);
|
||||
}
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
{
|
||||
cpu_extensions_vec.push(CpuExtensions::Neon);
|
||||
}
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
{
|
||||
cpu_extensions_vec.push(CpuExtensions::Simd128);
|
||||
}
|
||||
for cpu_extensions in cpu_extensions_vec {
|
||||
if !cpu_extensions.is_supported() {
|
||||
println!(
|
||||
"Cpu Extensions '{}' not supported by your CPU",
|
||||
cpu_ext_into_str(cpu_extensions)
|
||||
);
|
||||
continue;
|
||||
}
|
||||
unsafe {
|
||||
alpha_mul_div.set_cpu_extensions(cpu_extensions);
|
||||
}
|
||||
|
||||
match oper {
|
||||
Oper::Mul => {
|
||||
alpha_mul_div
|
||||
.multiply_alpha(&src_image, &mut dst_image)
|
||||
.unwrap();
|
||||
}
|
||||
Oper::Div => {
|
||||
alpha_mul_div
|
||||
.divide_alpha(&src_image, &mut dst_image)
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
let oper_str = if oper == Oper::Mul {
|
||||
"multiple"
|
||||
} else {
|
||||
"divide"
|
||||
};
|
||||
|
||||
let pixel_type_str = P::pixel_type_str();
|
||||
let cpu_ext_str = cpu_ext_into_str(cpu_extensions);
|
||||
let name = format!("{oper_str}_alpha_{pixel_type_str}-{cpu_ext_str}");
|
||||
testing::save_result(&dst_image, &name);
|
||||
|
||||
let checksum = testing::image_checksum::<P, N>(&dst_image);
|
||||
assert_eq!(
|
||||
checksum, expected_checksum,
|
||||
"failed test for {oper_str} alpha real image: \
|
||||
pixel_type={pixel_type_str}, cpu_extensions={cpu_ext_str}",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
mod u8x4 {
|
||||
use fast_image_resize::images::Image;
|
||||
use fast_image_resize::pixels::U8x4;
|
||||
|
||||
use super::*;
|
||||
|
||||
const fn p4(r: u8, g: u8, b: u8, a: u8) -> U8x4 {
|
||||
U8x4::new([r, g, b, a])
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod multiply_alpha_u8x4 {
|
||||
use super::*;
|
||||
|
||||
const SRC_PIXELS: [U8x4; 3] = [
|
||||
p4(255, 128, 0, 128),
|
||||
p4(255, 128, 0, 255),
|
||||
p4(255, 128, 0, 0),
|
||||
];
|
||||
const RES_PIXELS: [U8x4; 3] = [p4(128, 64, 0, 128), p4(255, 128, 0, 255), p4(0, 0, 0, 0)];
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
#[test]
|
||||
fn avx2_test() {
|
||||
mul_div_alpha_test(Oper::Mul, SRC_PIXELS, RES_PIXELS, CpuExtensions::Avx2);
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
#[test]
|
||||
fn sse4_test() {
|
||||
mul_div_alpha_test(Oper::Mul, SRC_PIXELS, RES_PIXELS, CpuExtensions::Sse4_1);
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
#[test]
|
||||
fn neon_test() {
|
||||
mul_div_alpha_test(Oper::Mul, SRC_PIXELS, RES_PIXELS, CpuExtensions::Neon);
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
#[test]
|
||||
fn wasm32_test() {
|
||||
mul_div_alpha_test(Oper::Mul, SRC_PIXELS, RES_PIXELS, CpuExtensions::Simd128);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn native_test() {
|
||||
mul_div_alpha_test(Oper::Mul, SRC_PIXELS, RES_PIXELS, CpuExtensions::None);
|
||||
}
|
||||
}
|
||||
|
||||
// Divides by alpha
|
||||
|
||||
#[cfg(test)]
|
||||
mod divide_alpha_u8x4 {
|
||||
use super::*;
|
||||
|
||||
const OPER: Oper = Oper::Div;
|
||||
const SRC_PIXELS: [U8x4; 3] = [
|
||||
p4(128, 64, 0, 128),
|
||||
p4(255, 128, 0, 255),
|
||||
p4(255, 128, 0, 0),
|
||||
];
|
||||
const RES_PIXELS: [U8x4; 3] = [p4(255, 127, 0, 128), p4(255, 128, 0, 255), p4(0, 0, 0, 0)];
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
#[test]
|
||||
fn avx2_test() {
|
||||
mul_div_alpha_test(OPER, SRC_PIXELS, RES_PIXELS, CpuExtensions::Avx2);
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
#[test]
|
||||
fn sse4_test() {
|
||||
mul_div_alpha_test(OPER, SRC_PIXELS, RES_PIXELS, CpuExtensions::Sse4_1);
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
#[test]
|
||||
fn neon_test() {
|
||||
mul_div_alpha_test(OPER, SRC_PIXELS, RES_PIXELS, CpuExtensions::Neon);
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
#[test]
|
||||
fn wasm32_test() {
|
||||
mul_div_alpha_test(OPER, SRC_PIXELS, RES_PIXELS, CpuExtensions::Simd128);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn native_test() {
|
||||
mul_div_alpha_test(OPER, SRC_PIXELS, RES_PIXELS, CpuExtensions::None);
|
||||
}
|
||||
const fn new_u8x4(c: u8, a: u8) -> U8x4 {
|
||||
U8x4::new([c, c, c, a])
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn multiply_alpha_real_image_test() {
|
||||
let mut pixels = vec![0u8; 256 * 256 * 4];
|
||||
let mut i: usize = 0;
|
||||
for alpha in 0..=255u8 {
|
||||
for color in 0..=255u8 {
|
||||
let pixel = pixels.get_mut(i..i + 4).unwrap();
|
||||
pixel.copy_from_slice(&[color, color, color, alpha]);
|
||||
i += 4;
|
||||
}
|
||||
}
|
||||
let size = 256;
|
||||
let src_image = Image::from_vec_u8(size, size, pixels, PixelType::U8x4).unwrap();
|
||||
let mut dst_image = Image::new(size, size, PixelType::U8x4);
|
||||
fn native_test() {
|
||||
full_mul_div_alpha_test_u8(new_u8x4, CpuExtensions::None);
|
||||
}
|
||||
|
||||
let mut alpha_mul_div: MulDiv = Default::default();
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
#[test]
|
||||
fn sse4_test() {
|
||||
full_mul_div_alpha_test_u8(new_u8x4, CpuExtensions::Sse4_1);
|
||||
}
|
||||
|
||||
let mut cpu_extensions_vec = vec![CpuExtensions::None];
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
{
|
||||
cpu_extensions_vec.push(CpuExtensions::Avx2);
|
||||
}
|
||||
for cpu_extensions in cpu_extensions_vec {
|
||||
if !cpu_extensions.is_supported() {
|
||||
println!(
|
||||
"Cpu Extensions '{}' not supported by your CPU",
|
||||
cpu_ext_into_str(cpu_extensions)
|
||||
);
|
||||
continue;
|
||||
}
|
||||
unsafe {
|
||||
alpha_mul_div.set_cpu_extensions(cpu_extensions);
|
||||
}
|
||||
alpha_mul_div
|
||||
.multiply_alpha(&src_image, &mut dst_image)
|
||||
.unwrap();
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
#[test]
|
||||
fn avx2_test() {
|
||||
full_mul_div_alpha_test_u8(new_u8x4, CpuExtensions::Avx2);
|
||||
}
|
||||
|
||||
let name = format!("multiple_alpha-{}", cpu_ext_into_str(cpu_extensions));
|
||||
testing::save_result(&dst_image, &name);
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
#[test]
|
||||
fn neon_test() {
|
||||
full_mul_div_alpha_test_u8(new_u8x4, CpuExtensions::Neon);
|
||||
}
|
||||
|
||||
let checksum = testing::image_checksum::<U8x4, 4>(&dst_image);
|
||||
assert_eq!(checksum, [4177920, 4177920, 4177920, 8355840]);
|
||||
}
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
#[test]
|
||||
fn wasm32_test() {
|
||||
full_mul_div_alpha_test_u8(new_u8x4, CpuExtensions::Simd128);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn divide_alpha_real_image_test() {
|
||||
let mut pixels = vec![0u8; 256 * 256 * 4];
|
||||
let mut i: usize = 0;
|
||||
for alpha in 0..=255u8 {
|
||||
for color in 0..=255u8 {
|
||||
let multiplied_color =
|
||||
(color as f64 * (alpha as f64 / 255.)).round().min(255.) as u8;
|
||||
let pixel = pixels.get_mut(i..i + 4).unwrap();
|
||||
pixel.copy_from_slice(&[
|
||||
multiplied_color,
|
||||
multiplied_color,
|
||||
multiplied_color,
|
||||
alpha,
|
||||
]);
|
||||
i += 4;
|
||||
}
|
||||
}
|
||||
let size = 256;
|
||||
let src_image = Image::from_vec_u8(size, size, pixels, PixelType::U8x4).unwrap();
|
||||
let mut dst_image = Image::new(size, size, PixelType::U8x4);
|
||||
fn multiply_real_image() {
|
||||
run_tests_with_real_image_u8::<U8x4, 4>(Oper::Mul, [4177920, 4177920, 4177920, 8355840]);
|
||||
}
|
||||
|
||||
let mut alpha_mul_div: MulDiv = Default::default();
|
||||
|
||||
let mut cpu_extensions_vec = vec![CpuExtensions::None];
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
{
|
||||
cpu_extensions_vec.push(CpuExtensions::Sse4_1);
|
||||
cpu_extensions_vec.push(CpuExtensions::Avx2);
|
||||
}
|
||||
for cpu_extensions in cpu_extensions_vec {
|
||||
if !cpu_extensions.is_supported() {
|
||||
println!(
|
||||
"Cpu Extensions '{}' not supported by your CPU",
|
||||
cpu_ext_into_str(cpu_extensions)
|
||||
);
|
||||
continue;
|
||||
}
|
||||
unsafe {
|
||||
alpha_mul_div.set_cpu_extensions(cpu_extensions);
|
||||
}
|
||||
alpha_mul_div
|
||||
.divide_alpha(&src_image, &mut dst_image)
|
||||
.unwrap();
|
||||
|
||||
let name = format!("divide_alpha-{}", cpu_ext_into_str(cpu_extensions));
|
||||
testing::save_result(&dst_image, &name);
|
||||
|
||||
let checksum = testing::image_checksum::<U8x4, 4>(&dst_image);
|
||||
assert_eq!(checksum, [8292504, 8292504, 8292504, 8355840]);
|
||||
}
|
||||
#[test]
|
||||
fn divide_real_image() {
|
||||
run_tests_with_real_image_u8::<U8x4, 4>(Oper::Div, [12452343, 12452343, 12452343, 8355840]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -292,65 +283,84 @@ mod not_u8x4 {
|
||||
|
||||
use super::*;
|
||||
|
||||
const fn p2(l: u8, a: u8) -> U8x2 {
|
||||
U8x2::new([l, a])
|
||||
const fn new_u16x2(l: u16, a: u16) -> U16x2 {
|
||||
U16x2::new([l, a])
|
||||
}
|
||||
|
||||
const fn new_u16x4(c: u16, a: u16) -> U16x4 {
|
||||
U16x4::new([c, c, c, a])
|
||||
}
|
||||
|
||||
fn get_div_test_cases_u16<P>(create_pixel: fn(u16, u16) -> P) -> (Vec<P>, Vec<P>)
|
||||
where
|
||||
P: PixelTrait<Component = u16>,
|
||||
{
|
||||
let test_cases = [
|
||||
new_case_16(0x8000, 0x8000, 0xffff),
|
||||
new_case_16(0x4000, 0x8000, 0x8000),
|
||||
new_case_16(0, 0x8000, 0),
|
||||
new_case_16(0xffff, 0xffff, 0xffff),
|
||||
new_case_16(0x8000, 0xffff, 0x8000),
|
||||
new_case_16(1, 2, 32768),
|
||||
new_case_16(0, 0xffff, 0),
|
||||
new_case_16(0xffff, 0, 0),
|
||||
new_case_16(0x8000, 0, 0),
|
||||
new_case_16(0, 0, 0),
|
||||
];
|
||||
let mut scr_pixels = vec![];
|
||||
let mut expected_pixels = vec![];
|
||||
for case in test_cases {
|
||||
scr_pixels.push(create_pixel(case.color, case.alpha));
|
||||
expected_pixels.push(create_pixel(case.expected_color, case.alpha));
|
||||
}
|
||||
(scr_pixels, expected_pixels)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod multiply_alpha_u8x2 {
|
||||
mod u8x2 {
|
||||
use super::*;
|
||||
|
||||
const OPER: Oper = Oper::Mul;
|
||||
const SRC_PIXELS: [U8x2; 9] = [
|
||||
p2(255, 128),
|
||||
p2(128, 128),
|
||||
p2(0, 128),
|
||||
p2(255, 255),
|
||||
p2(128, 255),
|
||||
p2(0, 255),
|
||||
p2(255, 0),
|
||||
p2(128, 0),
|
||||
p2(0, 0),
|
||||
];
|
||||
const RES_PIXELS: [U8x2; 9] = [
|
||||
p2(128, 128),
|
||||
p2(64, 128),
|
||||
p2(0, 128),
|
||||
p2(255, 255),
|
||||
p2(128, 255),
|
||||
p2(0, 255),
|
||||
p2(0, 0),
|
||||
p2(0, 0),
|
||||
p2(0, 0),
|
||||
];
|
||||
const fn new_u8x2(l: u8, a: u8) -> U8x2 {
|
||||
U8x2::new([l, a])
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
#[test]
|
||||
fn avx2_test() {
|
||||
mul_div_alpha_test(OPER, SRC_PIXELS, RES_PIXELS, CpuExtensions::Avx2);
|
||||
fn native_test() {
|
||||
full_mul_div_alpha_test_u8(new_u8x2, CpuExtensions::None);
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
#[test]
|
||||
fn sse4_test() {
|
||||
mul_div_alpha_test(OPER, SRC_PIXELS, RES_PIXELS, CpuExtensions::Sse4_1);
|
||||
full_mul_div_alpha_test_u8(new_u8x2, CpuExtensions::Sse4_1);
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
#[test]
|
||||
fn avx2_test() {
|
||||
full_mul_div_alpha_test_u8(new_u8x2, CpuExtensions::Avx2);
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
#[test]
|
||||
fn neon_test() {
|
||||
mul_div_alpha_test(OPER, SRC_PIXELS, RES_PIXELS, CpuExtensions::Neon);
|
||||
full_mul_div_alpha_test_u8(new_u8x2, CpuExtensions::Neon);
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
#[test]
|
||||
fn wasm32_test() {
|
||||
mul_div_alpha_test(OPER, SRC_PIXELS, RES_PIXELS, CpuExtensions::Simd128);
|
||||
full_mul_div_alpha_test_u8(new_u8x2, CpuExtensions::Simd128);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn native_test() {
|
||||
mul_div_alpha_test(OPER, SRC_PIXELS, RES_PIXELS, CpuExtensions::None);
|
||||
fn multiply_real_image() {
|
||||
run_tests_with_real_image_u8::<U8x2, 2>(Oper::Mul, [4177920, 8355840]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn divide_real_image() {
|
||||
run_tests_with_real_image_u8::<U8x2, 2>(Oper::Div, [12452343, 8355840]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -386,30 +396,30 @@ mod not_u8x4 {
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
#[test]
|
||||
fn avx2_test() {
|
||||
mul_div_alpha_test(Oper::Mul, SRC_PIXELS, RES_PIXELS, CpuExtensions::Avx2);
|
||||
mul_div_alpha_test(Oper::Mul, &SRC_PIXELS, &RES_PIXELS, CpuExtensions::Avx2);
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
#[test]
|
||||
fn sse4_test() {
|
||||
mul_div_alpha_test(Oper::Mul, SRC_PIXELS, RES_PIXELS, CpuExtensions::Sse4_1);
|
||||
mul_div_alpha_test(Oper::Mul, &SRC_PIXELS, &RES_PIXELS, CpuExtensions::Sse4_1);
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
#[test]
|
||||
fn neon_test() {
|
||||
mul_div_alpha_test(Oper::Mul, SRC_PIXELS, RES_PIXELS, CpuExtensions::Neon);
|
||||
mul_div_alpha_test(Oper::Mul, &SRC_PIXELS, &RES_PIXELS, CpuExtensions::Neon);
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
#[test]
|
||||
fn wasm32_test() {
|
||||
mul_div_alpha_test(Oper::Mul, SRC_PIXELS, RES_PIXELS, CpuExtensions::Simd128);
|
||||
mul_div_alpha_test(Oper::Mul, &SRC_PIXELS, &RES_PIXELS, CpuExtensions::Simd128);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn native_test() {
|
||||
mul_div_alpha_test(Oper::Mul, SRC_PIXELS, RES_PIXELS, CpuExtensions::None);
|
||||
mul_div_alpha_test(Oper::Mul, &SRC_PIXELS, &RES_PIXELS, CpuExtensions::None);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -433,88 +443,30 @@ mod not_u8x4 {
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
#[test]
|
||||
fn avx2_test() {
|
||||
mul_div_alpha_test(Oper::Mul, SRC_PIXELS, RES_PIXELS, CpuExtensions::Avx2);
|
||||
mul_div_alpha_test(Oper::Mul, &SRC_PIXELS, &RES_PIXELS, CpuExtensions::Avx2);
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
#[test]
|
||||
fn sse4_test() {
|
||||
mul_div_alpha_test(Oper::Mul, SRC_PIXELS, RES_PIXELS, CpuExtensions::Sse4_1);
|
||||
mul_div_alpha_test(Oper::Mul, &SRC_PIXELS, &RES_PIXELS, CpuExtensions::Sse4_1);
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
#[test]
|
||||
fn neon_test() {
|
||||
mul_div_alpha_test(Oper::Mul, SRC_PIXELS, RES_PIXELS, CpuExtensions::Neon);
|
||||
mul_div_alpha_test(Oper::Mul, &SRC_PIXELS, &RES_PIXELS, CpuExtensions::Neon);
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
#[test]
|
||||
fn wasm32_test() {
|
||||
mul_div_alpha_test(Oper::Mul, SRC_PIXELS, RES_PIXELS, CpuExtensions::Simd128);
|
||||
mul_div_alpha_test(Oper::Mul, &SRC_PIXELS, &RES_PIXELS, CpuExtensions::Simd128);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn native_test() {
|
||||
mul_div_alpha_test(Oper::Mul, SRC_PIXELS, RES_PIXELS, CpuExtensions::None);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod divide_alpha_u8x2 {
|
||||
use super::*;
|
||||
|
||||
const OPER: Oper = Oper::Div;
|
||||
const SRC_PIXELS: [U8x2; 9] = [
|
||||
p2(128, 128),
|
||||
p2(64, 128),
|
||||
p2(0, 128),
|
||||
p2(255, 255),
|
||||
p2(128, 255),
|
||||
p2(0, 255),
|
||||
p2(255, 0),
|
||||
p2(128, 0),
|
||||
p2(0, 0),
|
||||
];
|
||||
const RES_PIXELS: [U8x2; 9] = [
|
||||
p2(255, 128),
|
||||
p2(127, 128),
|
||||
p2(0, 128),
|
||||
p2(255, 255),
|
||||
p2(128, 255),
|
||||
p2(0, 255),
|
||||
p2(0, 0),
|
||||
p2(0, 0),
|
||||
p2(0, 0),
|
||||
];
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
#[test]
|
||||
fn avx2_test() {
|
||||
mul_div_alpha_test(OPER, SRC_PIXELS, RES_PIXELS, CpuExtensions::Avx2);
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
#[test]
|
||||
fn sse4_test() {
|
||||
mul_div_alpha_test(OPER, SRC_PIXELS, RES_PIXELS, CpuExtensions::Sse4_1);
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
#[test]
|
||||
fn neon_test() {
|
||||
mul_div_alpha_test(OPER, SRC_PIXELS, RES_PIXELS, CpuExtensions::Neon);
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
#[test]
|
||||
fn wasm32_test() {
|
||||
mul_div_alpha_test(OPER, SRC_PIXELS, RES_PIXELS, CpuExtensions::Simd128);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn native_test() {
|
||||
mul_div_alpha_test(OPER, SRC_PIXELS, RES_PIXELS, CpuExtensions::None);
|
||||
mul_div_alpha_test(Oper::Mul, &SRC_PIXELS, &RES_PIXELS, CpuExtensions::None);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -523,67 +475,39 @@ mod not_u8x4 {
|
||||
use super::*;
|
||||
|
||||
const OPER: Oper = Oper::Div;
|
||||
const SRC_PIXELS: [U16x2; 9] = [
|
||||
U16x2::new([0x8000, 0x8000]),
|
||||
U16x2::new([0x4000, 0x8000]),
|
||||
U16x2::new([0, 0x8000]),
|
||||
U16x2::new([0xffff, 0xffff]),
|
||||
U16x2::new([0x8000, 0xffff]),
|
||||
U16x2::new([0, 0xffff]),
|
||||
U16x2::new([0xffff, 0]),
|
||||
U16x2::new([0x8000, 0]),
|
||||
U16x2::new([0, 0]),
|
||||
];
|
||||
const RES_PIXELS: [U16x2; 9] = [
|
||||
U16x2::new([0xffff, 0x8000]),
|
||||
U16x2::new([0x7fff, 0x8000]),
|
||||
U16x2::new([0, 0x8000]),
|
||||
U16x2::new([0xffff, 0xffff]),
|
||||
U16x2::new([0x8000, 0xffff]),
|
||||
U16x2::new([0, 0xffff]),
|
||||
U16x2::new([0, 0]),
|
||||
U16x2::new([0, 0]),
|
||||
U16x2::new([0, 0]),
|
||||
];
|
||||
const SIMD_RES_PIXELS: [U16x2; 9] = [
|
||||
U16x2::new([0xffff, 0x8000]),
|
||||
U16x2::new([0x8000, 0x8000]),
|
||||
U16x2::new([0, 0x8000]),
|
||||
U16x2::new([0xffff, 0xffff]),
|
||||
U16x2::new([0x8000, 0xffff]),
|
||||
U16x2::new([0, 0xffff]),
|
||||
U16x2::new([0, 0]),
|
||||
U16x2::new([0, 0]),
|
||||
U16x2::new([0, 0]),
|
||||
];
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
#[test]
|
||||
fn avx2_test() {
|
||||
mul_div_alpha_test(OPER, SRC_PIXELS, SIMD_RES_PIXELS, CpuExtensions::Avx2);
|
||||
fn native_test() {
|
||||
let (scr_pixels, expected_pixels) = get_div_test_cases_u16(new_u16x2);
|
||||
mul_div_alpha_test(OPER, &scr_pixels, &expected_pixels, CpuExtensions::None);
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
#[test]
|
||||
fn sse4_test() {
|
||||
mul_div_alpha_test(OPER, SRC_PIXELS, SIMD_RES_PIXELS, CpuExtensions::Sse4_1);
|
||||
let (scr_pixels, expected_pixels) = get_div_test_cases_u16(new_u16x2);
|
||||
mul_div_alpha_test(OPER, &scr_pixels, &expected_pixels, CpuExtensions::Sse4_1);
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
#[test]
|
||||
fn avx2_test() {
|
||||
let (scr_pixels, expected_pixels) = get_div_test_cases_u16(new_u16x2);
|
||||
mul_div_alpha_test(OPER, &scr_pixels, &expected_pixels, CpuExtensions::Avx2);
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
#[test]
|
||||
fn neon_test() {
|
||||
mul_div_alpha_test(OPER, SRC_PIXELS, SIMD_RES_PIXELS, CpuExtensions::Neon);
|
||||
let (scr_pixels, expected_pixels) = get_div_test_cases_u16(new_u16x2);
|
||||
mul_div_alpha_test(OPER, &scr_pixels, &expected_pixels, CpuExtensions::Neon);
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
#[test]
|
||||
fn wasm32_test() {
|
||||
mul_div_alpha_test(OPER, SRC_PIXELS, RES_PIXELS, CpuExtensions::Simd128);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn native_test() {
|
||||
mul_div_alpha_test(OPER, SRC_PIXELS, RES_PIXELS, CpuExtensions::None);
|
||||
let (scr_pixels, expected_pixels) = get_div_test_cases_u16(new_u16x2);
|
||||
mul_div_alpha_test(OPER, &scr_pixels, &expected_pixels, CpuExtensions::Simd128);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -592,49 +516,39 @@ mod not_u8x4 {
|
||||
use super::*;
|
||||
|
||||
const OPER: Oper = Oper::Div;
|
||||
const SRC_PIXELS: [U16x4; 3] = [
|
||||
U16x4::new([0x8000, 0x4000, 0, 0x8000]),
|
||||
U16x4::new([0xffff, 0x8000, 0, 0xffff]),
|
||||
U16x4::new([0xffff, 0x8000, 0, 0]),
|
||||
];
|
||||
const RES_PIXELS: [U16x4; 3] = [
|
||||
U16x4::new([0xffff, 0x7fff, 0, 0x8000]),
|
||||
U16x4::new([0xffff, 0x8000, 0, 0xffff]),
|
||||
U16x4::new([0, 0, 0, 0]),
|
||||
];
|
||||
const SIMD_RES_PIXELS: [U16x4; 3] = [
|
||||
U16x4::new([0xffff, 0x8000, 0, 0x8000]),
|
||||
U16x4::new([0xffff, 0x8000, 0, 0xffff]),
|
||||
U16x4::new([0, 0, 0, 0]),
|
||||
];
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
#[test]
|
||||
fn avx2_test() {
|
||||
mul_div_alpha_test(OPER, SRC_PIXELS, SIMD_RES_PIXELS, CpuExtensions::Avx2);
|
||||
fn native_test() {
|
||||
let (scr_pixels, expected_pixels) = get_div_test_cases_u16(new_u16x4);
|
||||
mul_div_alpha_test(OPER, &scr_pixels, &expected_pixels, CpuExtensions::None);
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
#[test]
|
||||
fn sse4_test() {
|
||||
mul_div_alpha_test(OPER, SRC_PIXELS, SIMD_RES_PIXELS, CpuExtensions::Sse4_1);
|
||||
let (scr_pixels, expected_pixels) = get_div_test_cases_u16(new_u16x4);
|
||||
mul_div_alpha_test(OPER, &scr_pixels, &expected_pixels, CpuExtensions::Sse4_1);
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
#[test]
|
||||
fn avx2_test() {
|
||||
let (scr_pixels, expected_pixels) = get_div_test_cases_u16(new_u16x4);
|
||||
mul_div_alpha_test(OPER, &scr_pixels, &expected_pixels, CpuExtensions::Avx2);
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
#[test]
|
||||
fn neon_test() {
|
||||
mul_div_alpha_test(OPER, SRC_PIXELS, SIMD_RES_PIXELS, CpuExtensions::Neon);
|
||||
let (scr_pixels, expected_pixels) = get_div_test_cases_u16(new_u16x4);
|
||||
mul_div_alpha_test(OPER, &scr_pixels, &expected_pixels, CpuExtensions::Neon);
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
#[test]
|
||||
fn wasm32_test() {
|
||||
mul_div_alpha_test(OPER, SRC_PIXELS, RES_PIXELS, CpuExtensions::Simd128);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn native_test() {
|
||||
mul_div_alpha_test(OPER, SRC_PIXELS, RES_PIXELS, CpuExtensions::None);
|
||||
let (scr_pixels, expected_pixels) = get_div_test_cases_u16(new_u16x4);
|
||||
mul_div_alpha_test(OPER, &scr_pixels, &expected_pixels, CpuExtensions::Simd128);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user