use crate::path::Path;
use crate::scene::paint::Paint;
use crate::types::{Color, Fixed, Point, Rect, Transform, Transform3D};
use alloc::borrow::Cow;
use alloc::string::String;
use alloc::vec::Vec;
#[derive(Clone, Debug, PartialEq)]
pub enum ResourceRef {
Token(String),
Index(u32),
Inline(Path),
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum FillRule {
EvenOdd,
NonZero,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum CompositeMode {
SourceOver,
Add,
Screen,
Multiply,
Darken,
Lighten,
Difference,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum LineCap {
Butt,
Round,
Square,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum LineJoin {
Miter,
Round,
Bevel,
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct GlyphPlacement {
glyph_id: u16,
origin: Point,
offset: Point,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct GlyphPose {
glyph_id: u16,
origin: Point,
tangent: Point,
}
impl GlyphPose {
pub const WIRE_SIZE: usize = 18;
pub const fn new(glyph_id: u16, origin: Point, tangent: Point) -> Self {
Self {
glyph_id,
origin,
tangent,
}
}
pub const fn glyph_id(self) -> u16 {
self.glyph_id
}
pub const fn origin(self) -> Point {
self.origin
}
pub const fn tangent(self) -> Point {
self.tangent
}
pub(crate) const fn has_unit_tangent(self) -> bool {
let x = i32::from_le_bytes(self.tangent.x.to_le_bytes());
let y = i32::from_le_bytes(self.tangent.y.to_le_bytes());
let magnitude = x as i64 * x as i64 + y as i64 * y as i64;
magnitude >= 254_i64 * 254_i64 && magnitude <= 258_i64 * 258_i64
}
}
impl GlyphPlacement {
pub const WIRE_SIZE: usize = 18;
pub const fn new(glyph_id: u16, origin: Point) -> Self {
Self {
glyph_id,
origin,
offset: Point::ZERO,
}
}
pub const fn glyph_id(self) -> u16 {
self.glyph_id
}
pub const fn origin(self) -> Point {
self.origin
}
pub const fn offset(self) -> Point {
self.offset
}
pub const fn with_offset(mut self, offset: Point) -> Self {
self.offset = offset;
self
}
}
#[derive(Clone, Debug, PartialEq)]
pub enum SceneOp {
GroupBegin {
transform: Option<Transform>,
projective: Option<Transform3D>,
opacity: Option<u8>,
clip: Option<ResourceRef>,
mask: Option<ResourceRef>,
filter: Option<ResourceRef>,
disjoint_hint: bool,
},
GroupEnd,
PushClip {
path: Path,
transform: Transform,
fill_rule: FillRule,
},
PopClip,
FillPath {
path: Path,
transform: Transform,
paint: Paint,
opa: u8,
fill_rule: FillRule,
},
StrokePath {
path: Path,
transform: Transform,
paint: Paint,
width: Fixed,
opa: u8,
line_cap: LineCap,
line_join: LineJoin,
miter_limit: Fixed,
dash: Cow<'static, [Fixed]>,
},
FillRect {
area: Rect,
transform: Transform,
quad: Option<[Point; 4]>,
color: Color,
radius: Fixed,
opa: u8,
},
Border {
area: Rect,
transform: Transform,
quad: Option<[Point; 4]>,
color: Color,
width: Fixed,
radius: Fixed,
opa: u8,
},
GlyphRun {
font: ResourceRef,
ppem: u16,
pos: Point,
transform: Transform,
color: Color,
opa: u8,
glyphs: Vec<GlyphPlacement>,
},
PosedGlyphRun {
font: ResourceRef,
ppem: u16,
pos: Point,
transform: Transform,
color: Color,
opa: u8,
glyphs: Vec<GlyphPose>,
},
Line {
p1: Point,
p2: Point,
transform: Transform,
color: Color,
width: Fixed,
opa: u8,
},
Arc {
center: Point,
transform: Transform,
radius: Fixed,
start_angle: Fixed,
end_angle: Fixed,
color: Color,
width: Fixed,
opa: u8,
},
Blit {
texture: ResourceRef,
pos: Point,
size: Point,
transform: Transform,
quad: Option<[Point; 4]>,
opa: u8,
radius: Fixed,
composite: CompositeMode,
},
}
#[derive(Clone, Debug, Default, PartialEq)]
pub struct Scene {
pub ops: Vec<SceneOp>,
}
impl Scene {
pub fn new() -> Self {
Self::default()
}
pub fn from_ops(ops: Vec<SceneOp>) -> Self {
Self { ops }
}
pub fn push(&mut self, op: SceneOp) -> &mut Self {
self.ops.push(op);
self
}
}
#[cfg(test)]
mod tests {
use super::{Scene, SceneOp};
#[test]
fn push_appends_and_chains() {
let mut scene = Scene::new();
scene.push(SceneOp::GroupEnd).push(SceneOp::PopClip);
assert_eq!(scene.ops, [SceneOp::GroupEnd, SceneOp::PopClip]);
}
}