blob: a80364364e1382e82e0b16e4bad857ab986b42ae [file] [log] [blame]
// Copyright 2022 the Vello Authors
// SPDX-License-Identifier: Apache-2.0 OR MIT
use bytemuck::{Pod, Zeroable};
use peniko::{
BlendMode,
color::{AlphaColor, ColorSpace, DynamicColor, OpaqueColor, PremulColor, Srgb},
};
use super::Monoid;
/// Draw tag representation.
#[derive(Copy, Clone, PartialEq, Eq, Pod, Zeroable)]
#[repr(C)]
pub struct DrawTag(pub u32);
impl DrawTag {
/// No operation.
pub const NOP: Self = Self(0);
/// Color fill.
pub const COLOR: Self = Self(0x44);
/// Linear gradient fill.
pub const LINEAR_GRADIENT: Self = Self(0x114);
/// Radial gradient fill.
pub const RADIAL_GRADIENT: Self = Self(0x29c);
/// Sweep gradient fill.
pub const SWEEP_GRADIENT: Self = Self(0x254);
/// Image fill.
pub const IMAGE: Self = Self(0x28C); // info: 10, scene: 3
/// Blurred rounded rectangle.
pub const BLUR_RECT: Self = Self(0x2d4); // info: 11, scene: 5 (DrawBlurRoundedRect)
/// Begin layer/clip.
pub const BEGIN_CLIP: Self = Self(0x9);
/// End layer/clip.
pub const END_CLIP: Self = Self(0x21);
}
impl DrawTag {
/// Returns the size of the info buffer (in u32s) used by this tag.
pub const fn info_size(self) -> u32 {
(self.0 >> 6) & 0xf
}
}
/// The first word of each draw info stream entry contains the flags.
///
/// This is not part of the draw object stream but gets used after the draw
/// objects get reduced on the GPU. `0` represents a non-zero fill.
/// `1` represents an even-odd fill.
pub const DRAW_INFO_FLAGS_FILL_RULE_BIT: u32 = 1;
/// Draw object bounding box.
#[derive(Copy, Clone, Pod, Zeroable, Debug, Default)]
#[repr(C)]
pub struct DrawBbox {
pub bbox: [f32; 4],
}
/// Draw data for a solid color.
#[derive(Clone, Copy, Debug, Default, Zeroable, Pod)]
#[repr(C)]
pub struct DrawColor {
/// Packed little-endian RGBA premultiplied color with the red component in the low byte, i.e.,
/// with `r` the least significant byte and `a` the most significant.
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(),
}
}
}
/// Draw data for a linear gradient.
#[derive(Clone, Copy, Debug, Default, Zeroable, Pod)]
#[repr(C)]
pub struct DrawLinearGradient {
/// Ramp index.
pub index: u32,
/// Start point.
pub p0: [f32; 2],
/// End point.
pub p1: [f32; 2],
}
/// Draw data for a radial gradient.
#[derive(Clone, Copy, Debug, Default, Zeroable, Pod)]
#[repr(C)]
pub struct DrawRadialGradient {
/// Ramp index.
pub index: u32,
/// Start point.
pub p0: [f32; 2],
/// End point.
pub p1: [f32; 2],
/// Start radius.
pub r0: f32,
/// End radius.
pub r1: f32,
}
/// Draw data for a sweep gradient.
#[derive(Clone, Copy, Debug, Default, Zeroable, Pod)]
#[repr(C)]
pub struct DrawSweepGradient {
/// Ramp index.
pub index: u32,
/// Center point.
pub p0: [f32; 2],
/// Normalized start angle.
pub t0: f32,
/// Normalized end angle.
pub t1: f32,
}
/// Draw data for an image.
#[derive(Clone, Copy, Debug, Default, Zeroable, Pod)]
#[repr(C)]
pub struct DrawImage {
/// Packed atlas coordinates.
pub xy: u32,
/// Packed image dimensions.
pub width_height: u32,
/// Packed quality, extend mode and 8-bit alpha (bits `qqxxyyaaaaaaaa`,
/// 18 unused prefix bits).
pub sample_alpha: u32,
}
/// Draw data for a blurred rounded rectangle.
#[derive(Clone, Copy, Debug, Default, Zeroable, Pod)]
#[repr(C)]
pub struct DrawBlurRoundedRect {
/// Solid color brush.
pub color: DrawColor,
/// Rectangle width.
pub width: f32,
/// Rectangle height.
pub height: f32,
/// Rectangle corner radius.
pub radius: f32,
/// Standard deviation of gaussian filter.
pub std_dev: f32,
}
/// Draw data for a clip or layer.
#[derive(Clone, Copy, Debug, Default, Zeroable, Pod)]
#[repr(C)]
pub struct DrawBeginClip {
/// Blend mode.
pub blend_mode: u32,
/// Group alpha.
pub alpha: f32,
}
impl DrawBeginClip {
/// The `blend_mode` used to indicate that a layer should be
/// treated as a luminance mask.
///
/// The least significant 16 bits are reserved for Mix + Compose
/// combinations.
pub const LUMINANCE_MASK_LAYER: u32 = 0x10000;
/// Creates new clip draw data for a Porter-Duff blend mode.
pub fn new(blend_mode: BlendMode, alpha: f32) -> Self {
Self {
blend_mode: ((blend_mode.mix as u32) << 8) | blend_mode.compose as u32,
alpha,
}
}
/// Creates a new clip draw data for a luminance mask.
pub fn luminance_mask(alpha: f32) -> Self {
Self {
blend_mode: Self::LUMINANCE_MASK_LAYER,
alpha,
}
}
}
/// Monoid for the draw tag stream.
#[derive(Copy, Clone, PartialEq, Eq, Pod, Zeroable, Default, Debug)]
#[repr(C)]
pub struct DrawMonoid {
// The number of paths preceding this draw object.
pub path_ix: u32,
// The number of clip operations preceding this draw object.
pub clip_ix: u32,
// The offset of the encoded draw object in the scene (u32s).
pub scene_offset: u32,
// The offset of the associated info.
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() {
// `DrawColor` should be packed little-endian with red the least significant byte.
//
// If this changes intentionally, the `DrawColor` docs also need updating.
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() {
// If this changes intentionally, the `DrawColor` docs also need updating.
let c = Color::from_rgba8(0x00, 0xca, 0xfe, 0x00);
assert_eq!(DrawColor::from(c).rgba, 0);
}
}