denise/paint.rs
1//! A colour prepared for drawing.
2//!
3//! Premultiplication is arithmetic on a word, not rasterisation, and every
4//! backend needs the same answer from it — so it lives here rather than in any
5//! one renderer. Integer throughout, like everything else this crate owns: no
6//! `libm` on a `no_std` target, and the same bytes on x86 and ARM.
7
8use crate::color::Color;
9
10/// Mask selecting the two 8-bit lanes at bits 0..8 and 16..24.
11pub const LANES: u32 = 0x00FF_00FF;
12
13/// Multiplies two packed 8-bit lanes by `a` (`0..=255`) and divides by 255 with
14/// correct rounding.
15///
16/// The `+ 0x80` bias and the fold-back of the high bits make this exact, not the
17/// usual `>> 8` approximation: `mul_lanes(x, 255) == x` and `mul_lanes(x, 0) == 0`.
18/// Cheap approximations get those endpoints wrong, and an opaque fill that lands on
19/// 254 instead of 255 is visible as banding the moment anything is drawn twice.
20#[inline(always)]
21pub const fn mul_lanes(x: u32, a: u32) -> u32 {
22 let t = x * a + 0x0080_0080;
23 ((t + ((t >> 8) & LANES)) >> 8) & LANES
24}
25
26/// A colour prepared for drawing.
27///
28/// Premultiplication happens once here rather than once per pixel. Constructing a
29/// `Paint` is the only division-by-255 in a fill.
30#[derive(Clone, Copy, Debug, PartialEq, Eq)]
31pub struct Paint {
32 /// `0xAARRGGBB`, colour channels premultiplied by alpha.
33 premul: u32,
34 /// Straight alpha, `0..=255`.
35 alpha: u32,
36}
37
38impl Paint {
39 /// Prepares a colour for drawing.
40 #[inline]
41 pub const fn new(color: Color) -> Self {
42 let a = color.a as u32;
43 let rb = mul_lanes(((color.r as u32) << 16) | color.b as u32, a);
44 let g = mul_lanes(color.g as u32, a);
45 Self {
46 premul: (a << 24) | rb | (g << 8),
47 alpha: a,
48 }
49 }
50
51 /// The premultiplied `0xAARRGGBB` word.
52 #[inline]
53 pub const fn premultiplied(self) -> u32 {
54 self.premul
55 }
56
57 /// Straight alpha, `0..=255`.
58 #[inline]
59 pub const fn alpha(self) -> u32 {
60 self.alpha
61 }
62
63 /// Returns `true` if drawing can skip the read-modify-write and just store.
64 #[inline]
65 pub const fn is_opaque(self) -> bool {
66 self.alpha == 255
67 }
68
69 /// Returns `true` if drawing would change nothing.
70 #[inline]
71 pub const fn is_invisible(self) -> bool {
72 self.alpha == 0
73 }
74
75 /// This paint scaled by an anti-aliasing coverage of `0..=255`.
76 #[inline]
77 pub const fn scaled(self, coverage: u32) -> Self {
78 let premul = scale_premul(self.premul, coverage);
79 Self {
80 premul,
81 alpha: premul >> 24,
82 }
83 }
84}
85
86impl From<Color> for Paint {
87 #[inline]
88 fn from(color: Color) -> Self {
89 Paint::new(color)
90 }
91}
92
93/// Scales a premultiplied `0xAARRGGBB` word — alpha lane included — by a
94/// coverage of `0..=255`.
95#[inline(always)]
96pub const fn scale_premul(px: u32, coverage: u32) -> u32 {
97 let rb = mul_lanes(px & LANES, coverage);
98 let ag = mul_lanes((px >> 8) & LANES, coverage);
99 rb | (ag << 8)
100}