|
6 | 6 | use crate::pixmap::Pixmap; |
7 | 7 | use alloc::sync::Arc; |
8 | 8 | use peniko::{ |
9 | | - Gradient, ImageQuality, ImageSampler, |
| 9 | + Gradient, |
10 | 10 | color::{AlphaColor, PremulRgba8, Srgb}, |
11 | 11 | }; |
12 | 12 |
|
@@ -77,84 +77,68 @@ pub enum ImageSource { |
77 | 77 | OpaqueId(ImageId), |
78 | 78 | } |
79 | 79 |
|
80 | | -/// An image. |
81 | | -#[derive(Debug, Clone)] |
82 | | -pub struct Image { |
83 | | - /// The underlying pixmap of the image. |
84 | | - pub source: ImageSource, |
85 | | - /// Extend mode in the horizontal direction. |
86 | | - pub x_extend: peniko::Extend, |
87 | | - /// Extend mode in the vertical direction. |
88 | | - pub y_extend: peniko::Extend, |
89 | | - /// Hint for desired rendering quality. |
90 | | - pub quality: ImageQuality, |
91 | | -} |
92 | | - |
93 | | -impl Image { |
94 | | - /// Convert a [`peniko::ImageBrush`] to an [`Image`]. |
| 80 | +impl ImageSource { |
| 81 | + /// Convert a [`peniko::ImageData`] to an [`ImageSource`]. |
95 | 82 | /// |
96 | 83 | /// This is a somewhat lossy conversion, as the image data data is transformed to |
97 | 84 | /// [premultiplied RGBA8](`PremulRgba8`). |
98 | 85 | /// |
99 | 86 | /// # Panics |
100 | 87 | /// |
101 | 88 | /// This panics if `image` has a `width` or `height` greater than `u16::MAX`. |
102 | | - pub fn from_peniko_image(brush: &peniko::ImageBrush) -> Self { |
| 89 | + pub fn from_peniko_image_data(image: &peniko::ImageData) -> Self { |
103 | 90 | // TODO: how do we deal with `peniko::ImageFormat` growing? See also |
104 | 91 | // <https://github.com/linebender/vello/pull/996#discussion_r2080510863>. |
105 | | - if brush.image.format != peniko::ImageFormat::Rgba8 { |
106 | | - unimplemented!("Unsupported image format: {:?}", brush.image.format); |
107 | | - } |
108 | | - if brush.image.alpha_type != peniko::ImageAlphaType::Alpha { |
109 | | - unimplemented!("Unsupported image alpha type: {:?}", brush.image.alpha_type); |
110 | | - } |
| 92 | + let do_alpha_multiply = image.alpha_type != peniko::ImageAlphaType::AlphaPremultiplied; |
111 | 93 |
|
112 | 94 | assert!( |
113 | | - brush.image.width <= u16::MAX as u32 && brush.image.height <= u16::MAX as u32, |
| 95 | + image.width <= u16::MAX as u32 && image.height <= u16::MAX as u32, |
114 | 96 | "The image is too big. Its width and height can be no larger than {} pixels.", |
115 | 97 | u16::MAX, |
116 | 98 | ); |
117 | | - let width = brush.image.width.try_into().unwrap(); |
118 | | - let height = brush.image.height.try_into().unwrap(); |
119 | | - let ImageSampler { |
120 | | - x_extend, |
121 | | - y_extend, |
122 | | - quality, |
123 | | - alpha: global_alpha, |
124 | | - } = brush.sampler; |
125 | | - |
126 | | - #[expect(clippy::cast_possible_truncation, reason = "deliberate quantization")] |
127 | | - let global_alpha = u16::from((global_alpha * 255. + 0.5) as u8); |
| 99 | + let width = image.width.try_into().unwrap(); |
| 100 | + let height = image.height.try_into().unwrap(); |
128 | 101 |
|
129 | 102 | // TODO: SIMD |
130 | 103 | #[expect(clippy::cast_possible_truncation, reason = "This cannot overflow.")] |
131 | | - let pixels = brush |
132 | | - .image |
| 104 | + let pixels = image |
133 | 105 | .data |
134 | 106 | .data() |
135 | 107 | .chunks_exact(4) |
136 | | - .map(|rgba| { |
137 | | - let alpha = ((u16::from(rgba[3]) * global_alpha) / 255) as u8; |
138 | | - let multiply = |component| ((u16::from(alpha) * u16::from(component)) / 255) as u8; |
139 | | - PremulRgba8 { |
140 | | - r: multiply(rgba[0]), |
141 | | - g: multiply(rgba[1]), |
142 | | - b: multiply(rgba[2]), |
143 | | - a: alpha, |
| 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 | + } |
144 | 130 | } |
145 | 131 | }) |
146 | 132 | .collect(); |
147 | 133 | let pixmap = Pixmap::from_parts(pixels, width, height); |
148 | 134 |
|
149 | | - Self { |
150 | | - source: ImageSource::Pixmap(Arc::new(pixmap)), |
151 | | - x_extend, |
152 | | - y_extend, |
153 | | - quality, |
154 | | - } |
| 135 | + Self::Pixmap(Arc::new(pixmap)) |
155 | 136 | } |
156 | 137 | } |
157 | 138 |
|
| 139 | +/// An image. |
| 140 | +pub type Image = peniko::ImageBrush<ImageSource>; |
| 141 | + |
158 | 142 | /// A premultiplied color. |
159 | 143 | #[derive(Debug, Clone, PartialEq, Copy)] |
160 | 144 | pub struct PremulColor { |
@@ -193,30 +177,4 @@ impl PremulColor { |
193 | 177 | } |
194 | 178 |
|
195 | 179 | /// A kind of paint that can be used for filling and stroking shapes. |
196 | | -#[derive(Debug, Clone)] |
197 | | -pub enum PaintType { |
198 | | - /// A solid color. |
199 | | - Solid(AlphaColor<Srgb>), |
200 | | - /// A gradient. |
201 | | - Gradient(Gradient), |
202 | | - /// An image. |
203 | | - Image(Image), |
204 | | -} |
205 | | - |
206 | | -impl From<AlphaColor<Srgb>> for PaintType { |
207 | | - fn from(value: AlphaColor<Srgb>) -> Self { |
208 | | - Self::Solid(value) |
209 | | - } |
210 | | -} |
211 | | - |
212 | | -impl From<Gradient> for PaintType { |
213 | | - fn from(value: Gradient) -> Self { |
214 | | - Self::Gradient(value) |
215 | | - } |
216 | | -} |
217 | | - |
218 | | -impl From<Image> for PaintType { |
219 | | - fn from(value: Image) -> Self { |
220 | | - Self::Image(value) |
221 | | - } |
222 | | -} |
| 180 | +pub type PaintType = peniko::Brush<Image, Gradient>; |
0 commit comments