use bytemuck::{Pod, Zeroable};
use peniko::{
BlendMode,
color::{AlphaColor, ColorSpace, DynamicColor, OpaqueColor, PremulColor, Srgb},
};
use super::Monoid;
#[derive(Copy, Clone, PartialEq, Eq, Pod, Zeroable)]
#[repr(C)]
pub struct DrawTag(pub u32);
impl DrawTag {
pub const NOP: Self = Self(0);
pub const COLOR: Self = Self(0x44);
pub const LINEAR_GRADIENT: Self = Self(0x114);
pub const RADIAL_GRADIENT: Self = Self(0x29c);
pub const SWEEP_GRADIENT: Self = Self(0x254);
pub const IMAGE: Self = Self(0x28C);
pub const BLUR_RECT: Self = Self(0x2d4);
pub const BEGIN_CLIP: Self = Self(0x49);
pub const END_CLIP: Self = Self(0x21);
}
impl DrawTag {
pub const fn info_size(self) -> u32 {
(self.0 >> 6) & 0xf
}
}
pub const DRAW_INFO_FLAGS_FILL_RULE_BIT: u32 = 1;
#[derive(Copy, Clone, Pod, Zeroable, Debug, Default)]
#[repr(C)]
pub struct DrawBbox {
pub bbox: [f32; 4],
}
#[derive(Clone, Copy, Debug, Default, Zeroable, Pod)]
#[repr(C)]
pub struct DrawColor {
pub rgba: u32,
}
impl<CS: ColorSpace> From<AlphaColor<CS>> for DrawColor {
fn from(color: AlphaColor<CS>) -> Self {
Self {
rgba: color.convert::<Srgb>().premultiply().to_rgba8().to_u32(),
}
}
}
impl From<DynamicColor> for DrawColor {
fn from(color: DynamicColor) -> Self {
Self {
rgba: color
.to_alpha_color::<Srgb>()
.premultiply()
.to_rgba8()
.to_u32(),
}
}
}
impl<CS: ColorSpace> From<OpaqueColor<CS>> for DrawColor {
fn from(color: OpaqueColor<CS>) -> Self {
Self {
rgba: color
.convert::<Srgb>()
.with_alpha(1.)
.premultiply()
.to_rgba8()
.to_u32(),
}
}
}
impl<CS: ColorSpace> From<PremulColor<CS>> for DrawColor {
fn from(color: PremulColor<CS>) -> Self {
Self {
rgba: color.convert::<Srgb>().to_rgba8().to_u32(),
}
}
}
#[derive(Clone, Copy, Debug, Default, Zeroable, Pod)]
#[repr(C)]
pub struct DrawLinearGradient {
pub index: u32,
pub p0: [f32; 2],
pub p1: [f32; 2],
}
#[derive(Clone, Copy, Debug, Default, Zeroable, Pod)]
#[repr(C)]
pub struct DrawRadialGradient {
pub index: u32,
pub p0: [f32; 2],
pub p1: [f32; 2],
pub r0: f32,
pub r1: f32,
}
#[derive(Clone, Copy, Debug, Default, Zeroable, Pod)]
#[repr(C)]
pub struct DrawSweepGradient {
pub index: u32,
pub p0: [f32; 2],
pub t0: f32,
pub t1: f32,
}
#[derive(Clone, Copy, Debug, Default, Zeroable, Pod)]
#[repr(C)]
pub struct DrawImage {
pub xy: u32,
pub width_height: u32,
pub sample_alpha: u32,
}
#[derive(Clone, Copy, Debug, Default, Zeroable, Pod)]
#[repr(C)]
pub struct DrawBlurRoundedRect {
pub color: DrawColor,
pub width: f32,
pub height: f32,
pub radius: f32,
pub std_dev: f32,
}
#[derive(Clone, Copy, Debug, Default, Zeroable, Pod)]
#[repr(C)]
pub struct DrawBeginClip {
pub blend_mode: u32,
pub alpha: f32,
}
impl DrawBeginClip {
pub const LUMINANCE_MASK_BLEND_MODE: u32 = 0x10000;
pub const CLIP_BLEND_MODE: u32 = 0x8003;
pub fn new(blend_mode: BlendMode, alpha: f32) -> Self {
Self {
blend_mode: ((blend_mode.mix as u32) << 8) | blend_mode.compose as u32,
alpha,
}
}
pub fn luminance_mask(alpha: f32) -> Self {
Self {
blend_mode: Self::LUMINANCE_MASK_BLEND_MODE,
alpha,
}
}
pub fn clip() -> Self {
Self {
blend_mode: Self::CLIP_BLEND_MODE,
alpha: 1.0,
}
}
}
#[derive(Copy, Clone, PartialEq, Eq, Pod, Zeroable, Default, Debug)]
#[repr(C)]
pub struct DrawMonoid {
pub path_ix: u32,
pub clip_ix: u32,
pub scene_offset: u32,
pub info_offset: u32,
}
impl Monoid for DrawMonoid {
type SourceValue = DrawTag;
fn new(tag: DrawTag) -> Self {
Self {
path_ix: (tag != DrawTag::NOP) as u32,
clip_ix: tag.0 & 1,
scene_offset: (tag.0 >> 2) & 0x7,
info_offset: (tag.0 >> 6) & 0xf,
}
}
fn combine(&self, other: &Self) -> Self {
Self {
path_ix: self.path_ix + other.path_ix,
clip_ix: self.clip_ix + other.clip_ix,
scene_offset: self.scene_offset + other.scene_offset,
info_offset: self.info_offset + other.info_offset,
}
}
}
#[cfg(test)]
mod tests {
use peniko::Color;
use super::DrawColor;
#[test]
fn draw_color_endianness() {
let c = Color::from_rgba8(0x00, 0xca, 0xfe, 0xff);
assert_eq!(
bytemuck::bytes_of(&DrawColor::from(c)),
[0x00, 0xca, 0xfe, 0xff]
);
}
#[test]
fn draw_color_premultiplied() {
let c = Color::from_rgba8(0x00, 0xca, 0xfe, 0x00);
assert_eq!(DrawColor::from(c).rgba, 0);
}
}