use crate::cv::ComputerVision;
use crate::ImageBuffer;
#[cfg(target_arch = "wasm32")]
use core::arch::wasm32::*;
pub struct WasmCV;
impl ComputerVision for WasmCV {
fn grayscale(src: &ImageBuffer, dst: &mut [u8]) {
let src_data = src.data;
let len = src_data.len();
#[cfg(target_arch = "wasm32")]
{
let mut i = 0;
let mut j = 0;
while i + 15 < len {
let r = src_data[i] as f32;
let g = src_data[i + 1] as f32;
let b = src_data[i + 2] as f32;
dst[j] = ((r * 0.299 + g * 0.587 + b * 0.114 + 0.5) as u32 & 0xff) as u8;
j += 1;
i += 4;
}
while i < len {
let r = src_data[i] as f32;
let g = src_data[i + 1] as f32;
let b = src_data[i + 2] as f32;
dst[j] = ((r * 0.299 + g * 0.587 + b * 0.114 + 0.5) as u32 & 0xff) as u8;
j += 1;
i += 4;
}
}
#[cfg(not(target_arch = "wasm32"))]
{
crate::cv::scalar::ScalarCV::grayscale(src, dst);
}
}
fn threshold(src: &[u8], dst: &mut [u8], threshold: u8) {
let len = src.len();
let mut i = 0;
#[cfg(target_arch = "wasm32")]
{
let thresh_vec = u8x16_splat(threshold);
while i + 15 < len {
let data = v128_load(src[i..].as_ptr() as *const v128);
let gt = u8x16_gt(data, thresh_vec);
v128_store(dst[i..].as_mut_ptr() as *mut v128, gt);
i += 16;
}
}
while i < len {
dst[i] = if src[i] <= threshold { 0 } else { 255 };
i += 1;
}
}
fn otsu(src: &[u8]) -> u8 {
crate::cv::scalar::ScalarCV::otsu(src)
}
fn stack_box_blur(src: &ImageBuffer, dst: &mut [u8], kernel_size: usize) {
crate::cv::scalar::ScalarCV::stack_box_blur(src, dst, kernel_size)
}
fn adaptive_threshold(src: &ImageBuffer, dst: &mut [u8], kernel_size: usize, threshold: u8) {
crate::cv::scalar::ScalarCV::stack_box_blur(src, dst, kernel_size);
#[cfg(target_arch = "wasm32")]
{
let src_data = src.data;
let len = src_data.len();
let mut i = 0;
let thresh_vec = i16x8_splat(-(threshold as i16));
while i + 15 < len {
let s_ptr = src_data[i..].as_ptr() as *const v128;
let src_128 = v128_load(s_ptr);
let d_ptr = dst[i..].as_ptr() as *const v128;
let dst_128 = v128_load(d_ptr);
let src_low = u16x8_extend_low_u8x16(src_128);
let src_high = u16x8_extend_high_u8x16(src_128);
let dst_low = u16x8_extend_low_u8x16(dst_128);
let dst_high = u16x8_extend_high_u8x16(dst_128);
let diff_low = i16x8_sub(src_low, dst_low);
let diff_high = i16x8_sub(src_high, dst_high);
let mask_low = i16x8_le(diff_low, thresh_vec); let mask_high = i16x8_le(diff_high, thresh_vec);
let result = i8x16_narrow_i16x8(mask_low, mask_high);
v128_store(dst[i..].as_mut_ptr() as *mut v128, result);
i += 16;
}
while i < len {
let val = src_data[i] as i32 - dst[i] as i32;
dst[i] = if val <= -(threshold as i32) { 255 } else { 0 };
i += 1;
}
}
#[cfg(not(target_arch = "wasm32"))]
{
crate::cv::scalar::ScalarCV::adaptive_threshold(src, dst, kernel_size, threshold);
}
}
fn warp(src: &ImageBuffer, dst: &mut [u8], contour: &[crate::Point2f; 4], warp_size: usize) {
crate::cv::scalar::ScalarCV::warp(src, dst, contour, warp_size)
}
fn count_non_zero(src: &ImageBuffer, square: &crate::cv::Square) -> usize {
crate::cv::scalar::ScalarCV::count_non_zero(src, square)
}
fn gaussian_blur(src: &ImageBuffer, dst: &mut [u8], kernel_size: usize) {
crate::cv::scalar::ScalarCV::gaussian_blur(src, dst, kernel_size)
}
}
#[cfg(test)]
mod tests {
#![allow(clippy::needless_range_loop)]
use super::*;
use crate::ImageBuffer;
include!("../../tests/data/sample_8x8.rs");
#[test]
fn test_wasm_simd_grayscale_logic() {
let src = ImageBuffer {
data: &SAMPLE_8X8_RGBA,
width: 8,
height: 8,
};
let mut dst = [0u8; 64];
WasmCV::grayscale(&src, &mut dst);
assert_eq!(dst[0], 141);
assert_eq!(dst[1], 50);
for i in 0..64 {
if i % 2 == 0 {
assert_eq!(dst[i], 141);
} else {
assert_eq!(dst[i], 50);
}
}
}
#[test]
fn test_wasm_simd_threshold_bounds() {
let mut gray = [0u8; 64];
let mut dst = [0u8; 64];
for i in 0..64 {
gray[i] = if i % 2 == 0 { 141 } else { 50 };
}
WasmCV::threshold(&gray, &mut dst, 50);
for i in 0..64 {
if i % 2 == 0 {
assert_eq!(dst[i], 255);
} else {
assert_eq!(dst[i], 0);
}
}
}
#[test]
fn test_wasm_simd_adaptive_threshold() {
let mut gray = [0u8; 64];
for i in 0..64 {
gray[i] = if i % 4 == 0 { 200 } else { 50 };
}
let src = ImageBuffer {
data: &gray,
width: 8,
height: 8,
};
let mut dst = [0u8; 64];
WasmCV::adaptive_threshold(&src, &mut dst, 2, 10);
for i in 0..64 {
assert!(dst[i] == 0 || dst[i] == 255);
}
let mut scalar_dst = [0u8; 64];
crate::cv::scalar::ScalarCV::adaptive_threshold(&src, &mut scalar_dst, 2, 10);
assert_eq!(dst, scalar_dst);
}
#[test]
fn test_wasm_simd_stack_box_blur_parity() {
let mut gray = [0u8; 64];
for i in 0..64 {
gray[i] = if i % 2 == 0 { 200 } else { 50 };
}
let src = ImageBuffer {
data: &gray,
width: 8,
height: 8,
};
let mut dst_simd = [0u8; 64];
let mut dst_scalar = [0u8; 64];
WasmCV::stack_box_blur(&src, &mut dst_simd, 2);
crate::cv::scalar::ScalarCV::stack_box_blur(&src, &mut dst_scalar, 2);
assert_eq!(dst_simd, dst_scalar);
}
}