Skip to main content

aseprite/
tiny_skia_conv.rs

1use tiny_skia::Pixmap;
2
3use crate::error::AsepriteError;
4use crate::types::Pixels;
5
6/// Convert premultiplied-alpha RGBA to straight-alpha RGBA (in place).
7fn premultiplied_to_straight(data: &mut [u8]) {
8    for chunk in data.chunks_exact_mut(4) {
9        let a = chunk[3] as u16;
10        if a == 0 {
11            chunk[0] = 0;
12            chunk[1] = 0;
13            chunk[2] = 0;
14        } else if a < 255 {
15            chunk[0] = ((chunk[0] as u16 * 255 + a / 2) / a).min(255) as u8;
16            chunk[1] = ((chunk[1] as u16 * 255 + a / 2) / a).min(255) as u8;
17            chunk[2] = ((chunk[2] as u16 * 255 + a / 2) / a).min(255) as u8;
18        }
19    }
20}
21
22/// Convert straight-alpha RGBA to premultiplied-alpha RGBA (in place).
23fn straight_to_premultiplied(data: &mut [u8]) {
24    for chunk in data.chunks_exact_mut(4) {
25        let a = chunk[3] as u16;
26        if a == 0 {
27            chunk[0] = 0;
28            chunk[1] = 0;
29            chunk[2] = 0;
30        } else if a < 255 {
31            chunk[0] = ((chunk[0] as u16 * a + 127) / 255) as u8;
32            chunk[1] = ((chunk[1] as u16 * a + 127) / 255) as u8;
33            chunk[2] = ((chunk[2] as u16 * a + 127) / 255) as u8;
34        }
35    }
36}
37
38/// Converts a [`tiny_skia::Pixmap`] (premultiplied alpha) into [`Pixels`] (straight alpha).
39impl From<Pixmap> for Pixels {
40    fn from(pixmap: Pixmap) -> Self {
41        let width = pixmap.width() as u16;
42        let height = pixmap.height() as u16;
43        let mut data = pixmap.take();
44        premultiplied_to_straight(&mut data);
45        Self {
46            data,
47            width,
48            height,
49        }
50    }
51}
52
53/// Converts a [`tiny_skia::Pixmap`] reference into [`Pixels`] (straight alpha), copying the data.
54impl From<&Pixmap> for Pixels {
55    fn from(pixmap: &Pixmap) -> Self {
56        let width = pixmap.width() as u16;
57        let height = pixmap.height() as u16;
58        let mut data = pixmap.data().to_vec();
59        premultiplied_to_straight(&mut data);
60        Self {
61            data,
62            width,
63            height,
64        }
65    }
66}
67
68/// Converts [`Pixels`] (straight alpha) into a [`tiny_skia::Pixmap`] (premultiplied alpha).
69///
70/// Returns [`AsepriteError::PixelSizeMismatch`] if the dimensions are invalid.
71impl TryFrom<Pixels> for Pixmap {
72    type Error = AsepriteError;
73
74    fn try_from(pixels: Pixels) -> Result<Self, Self::Error> {
75        let mut data = pixels.data;
76        straight_to_premultiplied(&mut data);
77        Pixmap::from_vec(
78            data,
79            tiny_skia::IntSize::from_wh(pixels.width as u32, pixels.height as u32).ok_or(
80                AsepriteError::PixelSizeMismatch {
81                    expected: pixels.width as usize * pixels.height as usize * 4,
82                    actual: 0,
83                },
84            )?,
85        )
86        .ok_or(AsepriteError::PixelSizeMismatch {
87            expected: pixels.width as usize * pixels.height as usize * 4,
88            actual: 0,
89        })
90    }
91}