use crate::error::{Error, Result};
use crate::image::{Dimensions, Image, Metadata, PixelLayout, pack_pixels, unpack_pixels};
impl TryFrom<&image::DynamicImage> for Image {
type Error = Error;
fn try_from(src: &image::DynamicImage) -> Result<Self> {
let dims = Dimensions::new(src.width(), src.height())?;
let rgba = src.to_rgba8().into_raw();
let has_alpha = rgba.chunks_exact(4).any(|px| px[3] != 0xff);
Ok(Self::from_parts(
dims,
PixelLayout::Rgba8,
rgba,
has_alpha,
Metadata::none(),
))
}
}
impl TryFrom<&Image> for image::RgbaImage {
type Error = Error;
fn try_from(src: &Image) -> Result<Self> {
let (w, h) = (src.width(), src.height());
let rgba = if src.layout() == PixelLayout::Rgba8 {
src.as_bytes().to_vec()
} else {
pack_pixels(
PixelLayout::Rgba8,
&unpack_pixels(src.layout(), src.as_bytes()),
)
};
Self::from_raw(w, h, rgba).ok_or(Error::PixelBufferMismatch)
}
}
#[cfg(test)]
mod tests {
use crate::image::{Dimensions, Image, Metadata, PixelLayout};
#[test]
fn dynamic_image_round_trips_through_webpkit_image() {
let mut src = image::RgbaImage::new(3, 2);
for (i, px) in src.pixels_mut().enumerate() {
let v = u8::try_from(i * 10 % 256).unwrap();
*px = image::Rgba([v, v.wrapping_add(1), v.wrapping_add(2), 0x80]);
}
let dynamic = image::DynamicImage::ImageRgba8(src.clone());
let img = Image::try_from(&dynamic).unwrap();
assert_eq!((img.width(), img.height()), (3, 2));
assert!(img.has_alpha(), "alpha 0x80 must be detected as non-opaque");
let back = image::RgbaImage::try_from(&img).unwrap();
assert_eq!(back.dimensions(), (3, 2));
assert_eq!(back.into_raw(), src.into_raw());
}
#[test]
fn rejects_dimensions_past_the_webp_maximum() {
let oversized = image::DynamicImage::new_rgba8(crate::MAX_DIMENSION + 1, 1);
assert!(matches!(
Image::try_from(&oversized),
Err(crate::Error::InvalidDimensions)
));
}
#[test]
fn non_rgba_layout_is_repacked_to_rgba_on_export() {
let dims = Dimensions::new(1, 1).unwrap();
let img = Image::from_parts(
dims,
PixelLayout::Bgra8,
vec![0, 0, 255, 255],
false,
Metadata::none(),
);
let rgba = image::RgbaImage::try_from(&img).unwrap();
assert_eq!(rgba.into_raw(), vec![255, 0, 0, 255], "must emit RGBA red");
}
}