hayro/
pixmap.rs

1// Copyright 2025 the Vello Authors
2// SPDX-License-Identifier: Apache-2.0 OR MIT
3
4use bytemuck::{Pod, Zeroable};
5use hayro_interpret::color::AlphaColor;
6use image::codecs::png::PngEncoder;
7use image::{ExtendedColorType, ImageEncoder};
8use std::io::Cursor;
9use std::vec;
10use std::vec::Vec;
11
12#[derive(Clone, Copy, PartialEq, Eq, Debug, Pod, Zeroable)]
13#[repr(C)]
14pub(crate) struct PremulRgba8 {
15    /// Red component.
16    pub r: u8,
17    /// Green component.
18    pub g: u8,
19    /// Blue component.
20    pub b: u8,
21    /// Alpha component.
22    pub a: u8,
23}
24
25impl PremulRgba8 {
26    #[must_use]
27    pub(crate) const fn from_u8_array([r, g, b, a]: [u8; 4]) -> Self {
28        Self { r, g, b, a }
29    }
30
31    #[must_use]
32    pub(crate) const fn from_u32(packed_bytes: u32) -> Self {
33        Self::from_u8_array(u32::to_ne_bytes(packed_bytes))
34    }
35}
36
37#[derive(Clone, Copy, PartialEq, Eq, Debug, Pod, Zeroable)]
38#[repr(C)]
39pub(crate) struct Rgba8 {
40    pub r: u8,
41    pub g: u8,
42    pub b: u8,
43    pub a: u8,
44}
45
46impl From<Rgba8> for AlphaColor {
47    fn from(value: Rgba8) -> Self {
48        Self::from_rgba8(value.r, value.g, value.b, value.a)
49    }
50}
51
52/// A pixmap of premultiplied RGBA8 values backed by [`u8`][core::u8].
53#[derive(Debug, Clone)]
54pub struct Pixmap {
55    /// Width of the pixmap in pixels.  
56    width: u16,
57    /// Height of the pixmap in pixels.
58    height: u16,
59    /// Buffer of the pixmap in RGBA8 format.
60    buf: Vec<PremulRgba8>,
61}
62
63impl Pixmap {
64    /// Create a new pixmap with the given width and height in pixels.
65    pub(crate) fn new(width: u16, height: u16) -> Self {
66        let buf = vec![PremulRgba8::from_u32(0); width as usize * height as usize];
67        Self { width, height, buf }
68    }
69
70    /// Return the width of the pixmap.
71    pub fn width(&self) -> u16 {
72        self.width
73    }
74
75    /// Return the height of the pixmap.
76    pub fn height(&self) -> u16 {
77        self.height
78    }
79
80    /// Returns a reference to the underlying data as premultiplied RGBA8.
81    ///
82    /// The pixels are in row-major order.
83    pub(crate) fn data(&self) -> &[PremulRgba8] {
84        &self.buf
85    }
86
87    /// Returns a reference to the underlying data as premultiplied RGBA8.
88    ///
89    /// The pixels are in row-major order. Each pixel consists of four bytes in the order
90    /// `[r, g, b, a]`.
91    pub fn data_as_u8_slice(&self) -> &[u8] {
92        bytemuck::cast_slice(&self.buf)
93    }
94
95    /// Returns a mutable reference to the underlying data as premultiplied RGBA8.
96    ///
97    /// The pixels are in row-major order. Each pixel consists of four bytes in the order
98    /// `[r, g, b, a]`.
99    pub fn data_as_u8_slice_mut(&mut self) -> &mut [u8] {
100        bytemuck::cast_slice_mut(&mut self.buf)
101    }
102
103    /// Consume the pixmap, returning the data as the underlying [`Vec`] of premultiplied RGBA8.
104    ///
105    /// The pixels are in row-major order.
106    pub fn take_u8(self) -> Vec<u8> {
107        bytemuck::cast_vec(self.buf)
108    }
109
110    /// Encode the pixmap into a PNG file.
111    pub fn take_png(self) -> Vec<u8> {
112        let mut png_data = Vec::new();
113        let cursor = Cursor::new(&mut png_data);
114        let encoder = PngEncoder::new(cursor);
115        encoder
116            .write_image(
117                self.data_as_u8_slice(),
118                self.width() as u32,
119                self.height() as u32,
120                ExtendedColorType::Rgba8,
121            )
122            .expect("Failed to encode image");
123
124        png_data
125    }
126}