mirx 0.45.0

MIRX binary image format — flat & chunk-based container for ahead-of-time UI assets
Documentation
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 }
    }

    /// Appends an operation and returns the scene for continued authoring.
    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]);
    }
}