1use crate::pixmap::Pixmap;
7use alloc::sync::Arc;
8use peniko::{
9 Gradient, ImageQuality,
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
80#[derive(Debug, Clone)]
82pub struct Image {
83 pub source: ImageSource,
85 pub x_extend: peniko::Extend,
87 pub y_extend: peniko::Extend,
89 pub quality: ImageQuality,
91}
92
93impl Image {
94 pub fn from_peniko_image(image: &peniko::Image) -> Self {
103 if image.format != peniko::ImageFormat::Rgba8 {
106 unimplemented!("Unsupported image format: {:?}", image.format);
107 }
108
109 assert!(
110 image.width <= u16::MAX as u32 && image.height <= u16::MAX as u32,
111 "The image is too big. Its width and height can be no larger than {} pixels.",
112 u16::MAX,
113 );
114 let width = image.width.try_into().unwrap();
115 let height = image.height.try_into().unwrap();
116
117 #[expect(clippy::cast_possible_truncation, reason = "deliberate quantization")]
118 let global_alpha = u16::from((image.alpha * 255. + 0.5) as u8);
119
120 #[expect(clippy::cast_possible_truncation, reason = "This cannot overflow.")]
121 let pixels = image
122 .data
123 .data()
124 .chunks_exact(4)
125 .map(|rgba| {
126 let alpha = ((u16::from(rgba[3]) * global_alpha) / 255) as u8;
127 let multiply = |component| ((u16::from(alpha) * u16::from(component)) / 255) as u8;
128 PremulRgba8 {
129 r: multiply(rgba[0]),
130 g: multiply(rgba[1]),
131 b: multiply(rgba[2]),
132 a: alpha,
133 }
134 })
135 .collect();
136 let pixmap = Pixmap::from_parts(pixels, width, height);
137
138 Self {
139 source: ImageSource::Pixmap(Arc::new(pixmap)),
140 x_extend: image.x_extend,
141 y_extend: image.y_extend,
142 quality: image.quality,
143 }
144 }
145}
146
147#[derive(Debug, Clone, PartialEq, Copy)]
149pub struct PremulColor {
150 premul_u8: PremulRgba8,
151 premul_f32: peniko::color::PremulColor<Srgb>,
152}
153
154impl PremulColor {
155 pub fn from_alpha_color(color: AlphaColor<Srgb>) -> Self {
157 Self::from_premul_color(color.premultiply())
158 }
159
160 pub fn from_premul_color(color: peniko::color::PremulColor<Srgb>) -> Self {
162 Self {
163 premul_u8: color.to_rgba8(),
164 premul_f32: color,
165 }
166 }
167
168 pub fn as_premul_rgba8(&self) -> PremulRgba8 {
170 self.premul_u8
171 }
172
173 pub fn as_premul_f32(&self) -> peniko::color::PremulColor<Srgb> {
175 self.premul_f32
176 }
177
178 pub fn is_opaque(&self) -> bool {
180 self.premul_f32.components[3] == 1.0
181 }
182}
183
184#[derive(Debug, Clone)]
186pub enum PaintType {
187 Solid(AlphaColor<Srgb>),
189 Gradient(Gradient),
191 Image(Image),
193}
194
195impl From<AlphaColor<Srgb>> for PaintType {
196 fn from(value: AlphaColor<Srgb>) -> Self {
197 Self::Solid(value)
198 }
199}
200
201impl From<Gradient> for PaintType {
202 fn from(value: Gradient) -> Self {
203 Self::Gradient(value)
204 }
205}
206
207impl From<Image> for PaintType {
208 fn from(value: Image) -> Self {
209 Self::Image(value)
210 }
211}