forma_render/
styling.rs

1// Copyright 2022 Google LLC
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     https://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use std::{
16    fmt, hash,
17    sync::{
18        atomic::{AtomicUsize, Ordering},
19        Arc,
20    },
21};
22
23use crate::{
24    math::{AffineTransform, Point},
25    utils::CanonBits,
26};
27#[derive(Clone, Copy, Debug)]
28pub struct Color {
29    pub r: f32,
30    pub g: f32,
31    pub b: f32,
32    pub a: f32,
33}
34
35impl Eq for Color {}
36
37impl PartialEq for Color {
38    fn eq(&self, other: &Self) -> bool {
39        self.r == other.r && self.g == other.g && self.b == other.b && self.a == other.a
40    }
41}
42
43impl hash::Hash for Color {
44    fn hash<H: hash::Hasher>(&self, state: &mut H) {
45        self.r.to_canon_bits().hash(state);
46        self.g.to_canon_bits().hash(state);
47        self.b.to_canon_bits().hash(state);
48        self.a.to_canon_bits().hash(state);
49    }
50}
51
52impl Default for Color {
53    fn default() -> Self {
54        Self {
55            r: 0.0,
56            g: 0.0,
57            b: 0.0,
58            a: 1.0,
59        }
60    }
61}
62
63#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
64pub enum FillRule {
65    NonZero,
66    EvenOdd,
67}
68
69impl Default for FillRule {
70    fn default() -> Self {
71        Self::NonZero
72    }
73}
74
75#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
76pub enum GradientType {
77    Linear,
78    Radial,
79}
80
81const NO_STOP: f32 = -1.0;
82
83#[derive(Clone, Debug)]
84pub struct GradientBuilder {
85    r#type: GradientType,
86    start: Point,
87    end: Point,
88    stops: Vec<(Color, f32)>,
89}
90
91impl GradientBuilder {
92    pub fn new(start: Point, end: Point) -> Self {
93        Self {
94            r#type: GradientType::Linear,
95            start,
96            end,
97            stops: Vec::new(),
98        }
99    }
100
101    pub fn r#type(&mut self, r#type: GradientType) -> &mut Self {
102        self.r#type = r#type;
103        self
104    }
105
106    pub fn color(&mut self, color: Color) -> &mut Self {
107        self.stops.push((color, NO_STOP));
108        self
109    }
110
111    pub fn color_with_stop(&mut self, color: Color, stop: f32) -> &mut Self {
112        if !(0.0..=1.0).contains(&stop) {
113            panic!("gradient stops must be between 0.0 and 1.0");
114        }
115
116        self.stops.push((color, stop));
117        self
118    }
119
120    pub fn build(mut self) -> Option<Gradient> {
121        if self.stops.len() < 2 {
122            return None;
123        }
124
125        let stop_increment = 1.0 / (self.stops.len() - 1) as f32;
126        for (i, (_, stop)) in self.stops.iter_mut().enumerate() {
127            if *stop == NO_STOP {
128                *stop = i as f32 * stop_increment;
129            }
130        }
131
132        Some(Gradient {
133            r#type: self.r#type,
134            start: self.start,
135            end: self.end,
136            stops: self.stops.into(),
137        })
138    }
139}
140
141#[derive(Clone, Debug)]
142pub struct Gradient {
143    pub(crate) r#type: GradientType,
144    pub(crate) start: Point,
145    pub(crate) end: Point,
146    pub(crate) stops: Arc<[(Color, f32)]>,
147}
148
149impl Eq for Gradient {}
150
151impl PartialEq for Gradient {
152    fn eq(&self, other: &Self) -> bool {
153        self.r#type == other.r#type
154            && self.start == other.start
155            && self.end == other.end
156            && self.stops == other.stops
157    }
158}
159
160impl hash::Hash for Gradient {
161    fn hash<H: hash::Hasher>(&self, state: &mut H) {
162        self.r#type.hash(state);
163        self.start.hash(state);
164        self.end.hash(state);
165
166        self.stops.len().hash(state);
167        for (color, stop) in self.stops.iter() {
168            (color, stop.to_canon_bits()).hash(state);
169        }
170    }
171}
172
173impl Gradient {
174    pub fn r#type(&self) -> GradientType {
175        self.r#type
176    }
177
178    pub fn start(&self) -> Point {
179        self.start
180    }
181
182    pub fn end(&self) -> Point {
183        self.end
184    }
185
186    #[inline]
187    pub fn colors_with_stops(&self) -> &[(Color, f32)] {
188        &self.stops
189    }
190}
191
192#[derive(Debug)]
193pub enum ImageError {
194    SizeMismatch {
195        len: usize,
196        width: usize,
197        height: usize,
198    },
199    TooLarge,
200}
201
202impl fmt::Display for ImageError {
203    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
204        match self {
205            Self::SizeMismatch { len, width, height } => {
206                write!(
207                    f,
208                    "buffer has {} pixels, which does not match \
209                     the specified width ({}) and height ({})",
210                    len, width, height
211                )
212            }
213            Self::TooLarge => {
214                write!(
215                    f,
216                    "image dimensions exceed what is addressable \
217                     with f32; try to reduce the image size."
218                )
219            }
220        }
221    }
222}
223
224/// f16 value without denormals and within 0 and one.
225#[allow(non_camel_case_types)]
226#[repr(C)]
227#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
228pub(crate) struct f16(u16);
229
230impl f16 {
231    #[inline]
232    pub fn to_f32(self) -> f32 {
233        if self.0 != 0 {
234            f32::from_bits(0x3800_0000 + (u32::from(self.0) << 13))
235        } else {
236            0.0
237        }
238    }
239}
240
241impl From<f32> for f16 {
242    fn from(val: f32) -> Self {
243        if val != 0.0 {
244            f16(((val.to_bits() - 0x3800_0000) >> 13) as u16)
245        } else {
246            f16(0)
247        }
248    }
249}
250
251/// Transforms `sRGB` component into linear space.
252fn to_linear(l: u8) -> f32 {
253    let l = f32::from(l) * 255.0f32.recip();
254    if l <= 0.04045 {
255        l * 12.92f32.recip()
256    } else {
257        ((l + 0.055) * 1.055f32.recip()).powf(2.4)
258    }
259}
260
261#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
262pub struct ImageId(usize);
263
264impl ImageId {
265    fn new() -> ImageId {
266        static GENERATOR: AtomicUsize = AtomicUsize::new(0);
267
268        ImageId(GENERATOR.fetch_add(1, Ordering::SeqCst))
269    }
270}
271
272#[derive(Clone, Debug)]
273pub struct Image {
274    /// Pixels RGBA.
275    pub(crate) data: Arc<[[f16; 4]]>,
276    /// Largest x coordinate within the Image.
277    pub(crate) max_x: f32,
278    /// Largest y coordinate within the Image.
279    pub(crate) max_y: f32,
280    /// Width of the image in pixels.
281    pub(crate) width: u32,
282    /// Unique identifier for this image.
283    pub(crate) id: ImageId,
284}
285
286impl Eq for Image {}
287
288impl PartialEq for Image {
289    fn eq(&self, other: &Self) -> bool {
290        self.data.as_ptr() == other.data.as_ptr()
291            && self.max_x == other.max_x
292            && self.max_y == other.max_y
293    }
294}
295
296impl hash::Hash for Image {
297    fn hash<H: hash::Hasher>(&self, state: &mut H) {
298        self.data.as_ptr().hash(state);
299        self.max_x.to_canon_bits().hash(state);
300        self.max_y.to_canon_bits().hash(state);
301    }
302}
303
304impl Image {
305    /// Creates an image from `sRGB` color channels and linear alpha.
306    /// The boxed array size must match the image dimensions.
307    pub fn from_srgba(data: &[[u8; 4]], width: usize, height: usize) -> Result<Self, ImageError> {
308        let to_alpha = |a| f32::from(a) * f32::from(u8::MAX).recip();
309        let data = data
310            .iter()
311            .map(|c| {
312                [
313                    to_linear(c[0]),
314                    to_linear(c[1]),
315                    to_linear(c[2]),
316                    to_alpha(c[3]),
317                ]
318                .map(f16::from)
319            })
320            .collect();
321        Self::new(data, width, height)
322    }
323
324    pub fn from_linear_rgba(
325        data: &[[f32; 4]],
326        width: usize,
327        height: usize,
328    ) -> Result<Self, ImageError> {
329        let data = data.iter().map(|c| c.map(f16::from)).collect();
330        Self::new(data, width, height)
331    }
332
333    fn new(data: Arc<[[f16; 4]]>, width: usize, height: usize) -> Result<Self, ImageError> {
334        match width * height {
335            len if len > u32::MAX as usize => Err(ImageError::TooLarge),
336            len if len != data.len() => Err(ImageError::SizeMismatch {
337                len: data.len(),
338                width,
339                height,
340            }),
341            _ => Ok(Image {
342                data,
343                max_x: width as f32 - 1.0,
344                max_y: height as f32 - 1.0,
345                width: width as u32,
346                id: ImageId::new(),
347            }),
348        }
349    }
350
351    pub fn id(&self) -> ImageId {
352        self.id
353    }
354
355    pub fn width(&self) -> u32 {
356        self.width
357    }
358
359    pub fn height(&self) -> u32 {
360        self.max_y as u32 + 1
361    }
362
363    pub(crate) fn data(&self) -> &[[f16; 4]] {
364        self.data.as_ref()
365    }
366}
367
368/// Describes how to shade a surface using a bitmap image.
369#[derive(Clone, Debug, Eq, Hash, PartialEq)]
370pub struct Texture {
371    /// Transformation from screen-space to texture-space.
372    pub transform: AffineTransform,
373    /// Image shared with zero or more textures.
374    pub image: Image,
375}
376
377#[derive(Clone, Debug, Eq, Hash, PartialEq)]
378pub enum Fill {
379    Solid(Color),
380    Gradient(Gradient),
381    Texture(Texture),
382}
383
384impl Default for Fill {
385    fn default() -> Self {
386        Self::Solid(Color::default())
387    }
388}
389
390#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
391pub enum BlendMode {
392    Over,
393    Multiply,
394    Screen,
395    Overlay,
396    Darken,
397    Lighten,
398    ColorDodge,
399    ColorBurn,
400    HardLight,
401    SoftLight,
402    Difference,
403    Exclusion,
404    Hue,
405    Saturation,
406    Color,
407    Luminosity,
408}
409
410impl Default for BlendMode {
411    fn default() -> Self {
412        Self::Over
413    }
414}
415
416#[derive(Clone, Debug, Default, Eq, Hash, PartialEq)]
417pub struct Style {
418    pub is_clipped: bool,
419    pub fill: Fill,
420    pub blend_mode: BlendMode,
421}
422
423#[derive(Clone, Debug, Eq, Hash, PartialEq)]
424pub enum Func {
425    Draw(Style),
426    // Clips the subsequent layer with this one.
427    // From this order up to to order + n included are affected, if
428    // their `is_clipped` property is `true`.
429    Clip(usize),
430}
431
432impl Default for Func {
433    fn default() -> Self {
434        Self::Draw(Style::default())
435    }
436}
437
438#[derive(Clone, Debug, Default, Eq, Hash, PartialEq)]
439pub struct Props {
440    pub fill_rule: FillRule,
441    pub func: Func,
442}
443
444#[cfg(test)]
445mod tests {
446    use std::collections::HashSet;
447
448    use super::*;
449
450    #[test]
451    fn f16_error() {
452        // Error for the 256 values of u8 alpha is low.
453        let alpha_mse = (0u8..=255u8)
454            .map(|u| f32::from(u) / 255.0)
455            .map(|v| (v - f16::from(v).to_f32()))
456            .map(|d| d * d)
457            .sum::<f32>()
458            / 256.0;
459        assert!(alpha_mse < 5e-8, "alpha_mse: {}", alpha_mse);
460
461        // Values for 256 values of u8 alpha are distinct.
462        let alpha_distinct = (0u8..=255u8)
463            .map(|a| f16::from(f32::from(a) / 255.0))
464            .collect::<HashSet<f16>>()
465            .len();
466        assert_eq!(alpha_distinct, 256);
467
468        // Error for the 256 value of u8 sRGB is low.
469        let component_mse = (0u8..=255u8)
470            .map(to_linear)
471            .map(|v| (v - f16::from(v).to_f32()))
472            .map(|d| d * d)
473            .sum::<f32>()
474            / 256.0;
475        assert!(component_mse < 3e-8, "component_mse: {}", component_mse);
476
477        // Values for 256 values of u8 sRGB are distinct.
478        let component_distinct = (0u8..=255u8)
479            .map(|c| f16::from(to_linear(c)))
480            .collect::<HashSet<f16>>()
481            .len();
482        assert_eq!(component_distinct, 256);
483
484        // Min and max values are intact.
485        assert_eq!(f16::from(0.0).to_f32(), 0.0);
486        assert_eq!(f16::from(1.0).to_f32(), 1.0);
487    }
488
489    #[test]
490    fn f16_conversion() {
491        for i in 0..255 {
492            let value = (i as f32) / 255.0;
493            let value_f16 = f16::from(value);
494            assert!(half::f16::from_f32(value).to_bits().abs_diff(value_f16.0) <= 1);
495            assert_eq!(
496                half::f16::from_bits(value_f16.0).to_f32(),
497                value_f16.to_f32()
498            );
499        }
500    }
501}