1use crate::pixmap::Pixmap;
7use alloc::sync::Arc;
8use peniko::{
9 Gradient,
10 color::{AlphaColor, PremulRgba8, Srgb},
11};
12
13#[derive(Debug, Clone, PartialEq, Eq)]
18pub struct IndexedPaint(u32);
19
20impl IndexedPaint {
21 pub fn new(index: usize) -> Self {
23 Self(u32::try_from(index).expect("exceeded the maximum number of paints"))
24 }
25
26 pub fn index(&self) -> usize {
28 usize::try_from(self.0).unwrap()
29 }
30}
31
32#[derive(Debug, Clone, PartialEq)]
41pub enum Paint {
42 Solid(PremulColor),
44 Indexed(IndexedPaint),
46}
47
48impl From<AlphaColor<Srgb>> for Paint {
49 fn from(value: AlphaColor<Srgb>) -> Self {
50 Self::Solid(PremulColor::from_alpha_color(value))
51 }
52}
53
54#[derive(Clone, Copy, Hash, PartialEq, Eq, Debug)]
56pub struct ImageId(u32);
57
58impl ImageId {
59 pub fn new(value: u32) -> Self {
62 Self(value)
63 }
64
65 pub fn as_u32(&self) -> u32 {
67 self.0
68 }
69}
70
71#[derive(Debug, Clone)]
73pub enum ImageSource {
74 Pixmap(Arc<Pixmap>),
76 OpaqueId(ImageId),
78}
79
80impl ImageSource {
81 pub fn from_peniko_image_data(image: &peniko::ImageData) -> Self {
90 let do_alpha_multiply = image.alpha_type != peniko::ImageAlphaType::AlphaPremultiplied;
93
94 assert!(
95 image.width <= u16::MAX as u32 && image.height <= u16::MAX as u32,
96 "The image is too big. Its width and height can be no larger than {} pixels.",
97 u16::MAX,
98 );
99 let width = image.width.try_into().unwrap();
100 let height = image.height.try_into().unwrap();
101
102 #[expect(clippy::cast_possible_truncation, reason = "This cannot overflow.")]
104 let pixels = image
105 .data
106 .data()
107 .chunks_exact(4)
108 .map(|pixel| {
109 let rgba: [u8; 4] = match image.format {
110 peniko::ImageFormat::Rgba8 => pixel.try_into().unwrap(),
111 peniko::ImageFormat::Bgra8 => [pixel[2], pixel[1], pixel[0], pixel[3]],
112 format => unimplemented!("Unsupported image format: {format:?}"),
113 };
114 let alpha = u16::from(rgba[3]);
115 let multiply = |component| ((alpha * u16::from(component)) / 255) as u8;
116 if do_alpha_multiply {
117 PremulRgba8 {
118 r: multiply(rgba[0]),
119 g: multiply(rgba[1]),
120 b: multiply(rgba[2]),
121 a: rgba[3],
122 }
123 } else {
124 PremulRgba8 {
125 r: rgba[0],
126 g: rgba[1],
127 b: rgba[2],
128 a: rgba[3],
129 }
130 }
131 })
132 .collect();
133 let pixmap = Pixmap::from_parts(pixels, width, height);
134
135 Self::Pixmap(Arc::new(pixmap))
136 }
137}
138
139pub type Image = peniko::ImageBrush<ImageSource>;
141
142#[derive(Debug, Clone, PartialEq, Copy)]
144pub struct PremulColor {
145 premul_u8: PremulRgba8,
146 premul_f32: peniko::color::PremulColor<Srgb>,
147}
148
149impl PremulColor {
150 pub fn from_alpha_color(color: AlphaColor<Srgb>) -> Self {
152 Self::from_premul_color(color.premultiply())
153 }
154
155 pub fn from_premul_color(color: peniko::color::PremulColor<Srgb>) -> Self {
157 Self {
158 premul_u8: color.to_rgba8(),
159 premul_f32: color,
160 }
161 }
162
163 pub fn as_premul_rgba8(&self) -> PremulRgba8 {
165 self.premul_u8
166 }
167
168 pub fn as_premul_f32(&self) -> peniko::color::PremulColor<Srgb> {
170 self.premul_f32
171 }
172
173 pub fn is_opaque(&self) -> bool {
175 self.premul_f32.components[3] == 1.0
176 }
177}
178
179pub type PaintType = peniko::Brush<Image, Gradient>;