summaryrefslogblamecommitdiffstats
path: root/graphics/src/layer.rs
blob: ae9c6ce06e9c3705080a16edab90056e157d30e0 (plain) (tree)









































































































































































































































































                                                                               
use crate::image;
use crate::svg;
use crate::triangle;
use crate::{
    Background, Font, HorizontalAlignment, Point, Primitive, Rectangle, Size,
    Vector, VerticalAlignment, Viewport,
};

pub struct Layer<'a> {
    pub bounds: Rectangle<u32>,
    pub quads: Vec<Quad>,
    pub meshes: Vec<Mesh<'a>>,
    pub text: Vec<Text<'a>>,
    pub images: Vec<Image>,
}

impl<'a> Layer<'a> {
    pub fn new(bounds: Rectangle<u32>) -> Self {
        Self {
            bounds,
            quads: Vec::new(),
            meshes: Vec::new(),
            text: Vec::new(),
            images: Vec::new(),
        }
    }

    pub fn overlay(lines: &'a [impl AsRef<str>], viewport: &Viewport) -> Self {
        let (width, height) = viewport.dimensions();

        let mut overlay = Layer::new(Rectangle {
            x: 0,
            y: 0,
            width,
            height,
        });

        for (i, line) in lines.iter().enumerate() {
            let text = Text {
                content: line.as_ref(),
                bounds: Rectangle::new(
                    Point::new(11.0, 11.0 + 25.0 * i as f32),
                    Size::INFINITY,
                ),
                color: [0.9, 0.9, 0.9, 1.0],
                size: 20.0,
                font: Font::Default,
                horizontal_alignment: HorizontalAlignment::Left,
                vertical_alignment: VerticalAlignment::Top,
            };

            overlay.text.push(text);

            overlay.text.push(Text {
                bounds: text.bounds + Vector::new(-1.0, -1.0),
                color: [0.0, 0.0, 0.0, 1.0],
                ..text
            });
        }

        overlay
    }

    pub(crate) fn intersection(
        &self,
        rectangle: Rectangle,
    ) -> Option<Rectangle<u32>> {
        let layer_bounds: Rectangle<f32> = self.bounds.into();

        layer_bounds.intersection(&rectangle).map(Into::into)
    }

    pub fn generate(
        primitive: &'a Primitive,
        viewport: &Viewport,
    ) -> Vec<Self> {
        let mut layers = Vec::new();
        let (width, height) = viewport.dimensions();

        layers.push(Layer::new(Rectangle {
            x: 0,
            y: 0,
            width,
            height,
        }));

        Self::process_primitive(&mut layers, Vector::new(0.0, 0.0), primitive);

        layers
    }

    fn process_primitive(
        layers: &mut Vec<Self>,
        translation: Vector,
        primitive: &'a Primitive,
    ) {
        match primitive {
            Primitive::None => {}
            Primitive::Group { primitives } => {
                // TODO: Inspect a bit and regroup (?)
                for primitive in primitives {
                    Self::process_primitive(layers, translation, primitive)
                }
            }
            Primitive::Text {
                content,
                bounds,
                size,
                color,
                font,
                horizontal_alignment,
                vertical_alignment,
            } => {
                let layer = layers.last_mut().unwrap();

                layer.text.push(Text {
                    content,
                    bounds: *bounds + translation,
                    size: *size,
                    color: color.into_linear(),
                    font: *font,
                    horizontal_alignment: *horizontal_alignment,
                    vertical_alignment: *vertical_alignment,
                });
            }
            Primitive::Quad {
                bounds,
                background,
                border_radius,
                border_width,
                border_color,
            } => {
                let layer = layers.last_mut().unwrap();

                // TODO: Move some of these computations to the GPU (?)
                layer.quads.push(Quad {
                    position: [
                        bounds.x + translation.x,
                        bounds.y + translation.y,
                    ],
                    scale: [bounds.width, bounds.height],
                    color: match background {
                        Background::Color(color) => color.into_linear(),
                    },
                    border_radius: *border_radius as f32,
                    border_width: *border_width as f32,
                    border_color: border_color.into_linear(),
                });
            }
            Primitive::Mesh2D { buffers, size } => {
                let layer = layers.last_mut().unwrap();

                let bounds = Rectangle::new(
                    Point::new(translation.x, translation.y),
                    *size,
                );

                // Only draw visible content
                if let Some(clip_bounds) = layer.intersection(bounds) {
                    layer.meshes.push(Mesh {
                        origin: Point::new(translation.x, translation.y),
                        buffers,
                        clip_bounds: clip_bounds.into(),
                    });
                }
            }
            Primitive::Clip {
                bounds,
                offset,
                content,
            } => {
                let layer = layers.last_mut().unwrap();

                // Only draw visible content
                if let Some(clip_bounds) =
                    layer.intersection(*bounds + translation)
                {
                    let clip_layer = Layer::new(clip_bounds.into());
                    let new_layer = Layer::new(layer.bounds);

                    layers.push(clip_layer);
                    Self::process_primitive(
                        layers,
                        translation
                            - Vector::new(offset.x as f32, offset.y as f32),
                        content,
                    );
                    layers.push(new_layer);
                }
            }
            Primitive::Translate {
                translation: new_translation,
                content,
            } => {
                Self::process_primitive(
                    layers,
                    translation + *new_translation,
                    &content,
                );
            }
            Primitive::Cached { cache } => {
                Self::process_primitive(layers, translation, &cache);
            }
            Primitive::Image { handle, bounds } => {
                let layer = layers.last_mut().unwrap();

                layer.images.push(Image::Raster {
                    handle: handle.clone(),
                    bounds: *bounds + translation,
                });
            }
            Primitive::Svg { handle, bounds } => {
                let layer = layers.last_mut().unwrap();

                layer.images.push(Image::Vector {
                    handle: handle.clone(),
                    bounds: *bounds + translation,
                });
            }
        }
    }
}

#[derive(Debug, Clone, Copy)]
#[repr(C)]
pub struct Quad {
    pub position: [f32; 2],
    pub scale: [f32; 2],
    pub color: [f32; 4],
    pub border_color: [f32; 4],
    pub border_radius: f32,
    pub border_width: f32,
}

#[derive(Debug, Clone, Copy)]
pub struct Mesh<'a> {
    pub origin: Point,
    pub buffers: &'a triangle::Mesh2D,
    pub clip_bounds: Rectangle<u32>,
}

#[derive(Debug, Clone, Copy)]
pub struct Text<'a> {
    pub content: &'a str,
    pub bounds: Rectangle,
    pub color: [f32; 4],
    pub size: f32,
    pub font: Font,
    pub horizontal_alignment: HorizontalAlignment,
    pub vertical_alignment: VerticalAlignment,
}

#[derive(Debug, Clone)]
pub enum Image {
    Raster {
        handle: image::Handle,
        bounds: Rectangle,
    },
    Vector {
        handle: svg::Handle,
        bounds: Rectangle,
    },
}

unsafe impl bytemuck::Zeroable for Quad {}
unsafe impl bytemuck::Pod for Quad {}