use bevy::prelude::*;
pub(crate) fn is_pixel_transparent(image: &Image, webview_size: Vec2, pos_dip: Vec2) -> bool {
let img_size = UVec2::new(image.width(), image.height());
if img_size.x == 0 || img_size.y == 0 || webview_size.x <= 0.0 || webview_size.y <= 0.0 {
return false;
}
let px = dip_to_pixel(pos_dip, img_size, webview_size);
let offset = ((px.y * img_size.x + px.x) * 4 + 3) as usize;
let Some(data) = image.data.as_ref() else {
return false;
};
data.len() > offset && data[offset] == 0
}
fn dip_to_pixel(pos: Vec2, img_size: UVec2, dip_size: Vec2) -> UVec2 {
if dip_size.x <= 0.0 || dip_size.y <= 0.0 || img_size.x == 0 || img_size.y == 0 {
return UVec2::ZERO;
}
let sx = img_size.x as f32 / dip_size.x;
let sy = img_size.y as f32 / dip_size.y;
let x = ((pos.x * sx).floor() as u32).min(img_size.x - 1);
let y = ((pos.y * sy).floor() as u32).min(img_size.y - 1);
UVec2::new(x, y)
}
#[cfg(test)]
mod tests {
use super::*;
use bevy::render::render_resource::Extent3d;
fn image_with_alpha(width: u32, height: u32, alpha_per_pixel: &[u8]) -> Image {
let mut data = vec![0u8; (width * height * 4) as usize];
for (i, &a) in alpha_per_pixel.iter().enumerate() {
data[i * 4 + 3] = a;
}
let mut image = Image::default();
image.texture_descriptor.size = Extent3d {
width,
height,
depth_or_array_layers: 1,
};
image.data = Some(data);
image
}
#[test]
fn dip_to_pixel_identity_at_dpr_1() {
let result = dip_to_pixel(
Vec2::new(100.0, 200.0),
UVec2::new(800, 800),
Vec2::new(800.0, 800.0),
);
assert_eq!(result, UVec2::new(100, 200));
}
#[test]
fn dip_to_pixel_scales_by_dpr_2() {
let result = dip_to_pixel(
Vec2::new(100.0, 200.0),
UVec2::new(1600, 1600),
Vec2::new(800.0, 800.0),
);
assert_eq!(result, UVec2::new(200, 400));
}
#[test]
fn dip_to_pixel_scales_by_dpr_1_5() {
let result = dip_to_pixel(
Vec2::new(100.0, 100.0),
UVec2::new(1200, 900),
Vec2::new(800.0, 600.0),
);
assert_eq!(result, UVec2::new(150, 150));
}
#[test]
fn dip_to_pixel_clamps_to_image_bounds() {
let result = dip_to_pixel(
Vec2::new(1000.0, 1000.0),
UVec2::new(800, 800),
Vec2::new(800.0, 800.0),
);
assert_eq!(result, UVec2::new(799, 799));
}
#[test]
fn dip_to_pixel_zero_position_is_origin() {
let result = dip_to_pixel(Vec2::ZERO, UVec2::new(1600, 1600), Vec2::new(800.0, 800.0));
assert_eq!(result, UVec2::ZERO);
}
#[test]
fn transparent_pixel_is_transparent() {
let image = image_with_alpha(2, 1, &[255, 0]);
let size = Vec2::new(2.0, 1.0);
assert!(!is_pixel_transparent(&image, size, Vec2::new(0.0, 0.0)));
assert!(is_pixel_transparent(&image, size, Vec2::new(1.0, 0.0)));
}
#[test]
fn zero_area_or_missing_data_is_opaque() {
let image = image_with_alpha(2, 1, &[0, 0]);
assert!(!is_pixel_transparent(
&image,
Vec2::ZERO,
Vec2::new(1.0, 0.0)
));
let mut empty = Image::default();
empty.texture_descriptor.size = Extent3d {
width: 2,
height: 1,
depth_or_array_layers: 1,
};
empty.data = None;
assert!(!is_pixel_transparent(
&empty,
Vec2::new(2.0, 1.0),
Vec2::new(1.0, 0.0)
));
}
}