drawing_gl 0.8.0

OpenGL backend for 2D graphics library
Documentation
use crate::generic::device::{Device, RenderTarget};
use crate::generic::texture_font::Font;
use crate::units::{PixelToUvTransform, PixelTransform};
use crate::Fonts;
use crate::{
    display_list::StrokeStyle,
    generic::{clipping::Scissor, path::FlattenedPath, texture_font::FontParams},
    BasicCompositeOperation, CompositeOperation, LineJoin, PathElement, Primitive,
};
use drawing_api::*;

use std::convert::Into;

pub struct Renderer;

impl Default for Renderer {
    fn default() -> Self {
        Self::new()
    }
}

impl Renderer {
    pub fn new() -> Self {
        Renderer {}
    }

    pub fn draw<D: Device>(
        &mut self,
        render_target: &D::RenderTarget,
        primitives: &[Primitive<D::Texture, Fonts<D>>],
        antialiasing: bool,
    ) -> Result<(), &'static str> {
        let mut device = render_target.get_device();
        self.draw_internal::<D>(
            &mut device,
            render_target,
            primitives,
            antialiasing,
            PixelTransform::identity(),
            Scissor::empty(),
        )
    }

    fn draw_internal<D: Device>(
        &mut self,
        device: &mut D,
        render_target: &D::RenderTarget,
        primitives: &[Primitive<D::Texture, Fonts<D>>],
        antialiasing: bool,
        pixel_transform: PixelTransform,
        scissor: Scissor,
    ) -> Result<(), &'static str> {
        let pixel_to_device_transform = pixel_transform.then(&render_target.get_device_transform());

        for primitive in primitives {
            match primitive {
                Primitive::Clear { color } => {
                    device.clear(render_target, color);
                }

                Primitive::Line {
                    color,
                    thickness,
                    start_point,
                    end_point,
                } => {
                    let thickness = pixel_to_device_transform
                        .transform_point(PixelPoint::new(*thickness, *thickness))
                        .x;

                    device.line(
                        render_target,
                        color,
                        thickness,
                        *start_point,
                        *end_point,
                        pixel_to_device_transform,
                    );
                }

                Primitive::Rectangle { color, rect } => {
                    device.rect_colored(render_target, color, *rect, pixel_to_device_transform)
                }

                Primitive::Image { texture, rect, src } => {
                    let pixel_to_uv_transform = Self::get_uv_transform(texture.get_descriptor());
                    device.rect_textured(
                        render_target,
                        &texture,
                        false,
                        &[1.0f32, 1.0f32, 1.0f32, 1.0f32],
                        *rect,
                        *src,
                        pixel_to_device_transform,
                        pixel_to_uv_transform,
                    );
                }

                Primitive::Text {
                    fonts,
                    family_name,
                    color,
                    position,
                    clipping_rect,
                    size,
                    text,
                } => {
                    if let Some(font) = fonts.data.lock().unwrap().fonts.get_mut(family_name) {
                        font.draw(
                            device,
                            render_target,
                            color,
                            text,
                            *position,
                            *clipping_rect,
                            FontParams { size: *size as u8 },
                            pixel_to_device_transform,
                        )?;
                    }
                }

                Primitive::Stroke {
                    path,
                    thickness,
                    brush,
                } => {
                    let scale = 1.0f32; // TODO: take from transform? xform.average_scale()?
                    let stroke_width = *thickness * scale; //.clamped(0.0, 200.0);
                    let aspect_ratio = render_target.get_aspect_ratio();
                    let flattened_path = Self::get_stroke_path(
                        path,
                        stroke_width,
                        &Default::default(),
                        aspect_ratio,
                        antialiasing,
                    );

                    let (paint, texture) = crate::generic::device::Paint::from_brush(brush);

                    device.stroke(
                        render_target,
                        &paint,
                        texture.as_ref(),
                        true,
                        &flattened_path.paths,
                        stroke_width,
                        1.0f32 / aspect_ratio,
                        antialiasing,
                        scissor,
                        CompositeOperation::Basic(BasicCompositeOperation::SrcOver).into(),
                        pixel_to_device_transform,
                    );
                }

                Primitive::StrokeStyled {
                    path,
                    thickness,
                    brush,
                    style,
                } => {
                    let scale = 1.0f32; // TODO: take from transform? xform.average_scale()?
                    let stroke_width = *thickness * scale; //.clamped(0.0, 200.0);
                    let aspect_ratio = render_target.get_aspect_ratio();
                    let flattened_path = Self::get_stroke_path(
                        path,
                        stroke_width,
                        style,
                        aspect_ratio,
                        antialiasing,
                    );

                    let (paint, texture) = crate::generic::device::Paint::from_brush(brush);

                    device.stroke(
                        render_target,
                        &paint,
                        texture.as_ref(),
                        true,
                        &flattened_path.paths,
                        stroke_width,
                        1.0f32 / aspect_ratio,
                        antialiasing,
                        scissor,
                        CompositeOperation::Basic(BasicCompositeOperation::SrcOver).into(),
                        pixel_to_device_transform,
                    );
                }

                Primitive::Fill { path, brush } => {
                    let aspect_ratio = render_target.get_aspect_ratio();
                    let flattened_path = Self::get_fill_path(path, aspect_ratio, antialiasing);

                    let (paint, texture) = crate::generic::device::Paint::from_brush(brush);

                    device.fill(
                        render_target,
                        &paint,
                        texture.as_ref(),
                        true,
                        &flattened_path.paths,
                        flattened_path.bounds,
                        1.0f32 / aspect_ratio,
                        antialiasing,
                        scissor,
                        CompositeOperation::Basic(BasicCompositeOperation::SrcOver).into(),
                        pixel_to_device_transform,
                    );
                }

                Primitive::ClipRect { rect, primitives } => {
                    self.draw_internal(
                        device,
                        render_target,
                        primitives,
                        antialiasing,
                        pixel_transform,
                        scissor.intersect_with_rect(*rect, &pixel_transform),
                    )?;
                }

                Primitive::ClipPath { .. } => {
                    /*device.save_state();

                    device.set_clip_path(path);
                    self.draw(device, render_target, primitives, resources, antialiasing)?;

                    device.restore_state();*/
                }

                Primitive::Transform {
                    transform,
                    primitives,
                } => {
                    self.draw_internal(
                        device,
                        render_target,
                        primitives,
                        antialiasing,
                        transform.then(&pixel_transform),
                        scissor.apply_transform(transform),
                    )?;
                }

                Primitive::Composite { color, primitives } => {
                    let size = render_target.get_size();
                    let (texture2, texture2_view) =
                        device.create_render_target(size.0 as u16, size.1 as u16)?;

                    device.clear(&texture2_view, &[0.0f32, 0.0f32, 0.0f32, 0.0f32]);

                    self.draw(&texture2_view, primitives, antialiasing)?;

                    let pixel_to_uv_transform = Self::get_uv_transform(texture2.get_descriptor());
                    device.rect_textured(
                        render_target,
                        &texture2,
                        false,
                        color,
                        PixelRect::new(
                            PixelPoint::new(0.0f32, 0.0f32),
                            PixelSize::new(size.0 as f32, size.1 as f32),
                        ),
                        PixelRect::new(
                            PixelPoint::new(0.0f32, 0.0f32),
                            PixelSize::new(size.0 as f32, size.1 as f32),
                        ),
                        pixel_to_device_transform,
                        pixel_to_uv_transform,
                    );
                }
            }
        }

        Ok(())
    }

    fn get_stroke_path(
        path: &[PathElement],
        stroke_width: f32,
        stroke_style: &StrokeStyle,
        aspect_ratio: f32,
        antialiasing: bool,
    ) -> FlattenedPath {
        let mut flattened_path =
            FlattenedPath::new(path, 0.01f32 / aspect_ratio, 0.25f32 / aspect_ratio);
        let fringe_width = 1.0f32 / aspect_ratio;
        if antialiasing {
            flattened_path.expand_stroke(
                stroke_width * 0.5f32,
                fringe_width,
                stroke_style.line_cap,
                stroke_style.line_join,
                stroke_style.miter_limit,
                0.25f32 / aspect_ratio,
            );
        } else {
            flattened_path.expand_stroke(
                stroke_width * 0.5f32,
                0.0,
                stroke_style.line_cap,
                stroke_style.line_join,
                stroke_style.miter_limit,
                0.25f32 / aspect_ratio,
            );
        }
        flattened_path
    }

    fn get_fill_path(path: &[PathElement], aspect_ratio: f32, antialiasing: bool) -> FlattenedPath {
        let mut flattened_path =
            FlattenedPath::new(path, 0.01f32 / aspect_ratio, 0.25f32 / aspect_ratio);

        let fringe_width = 1.0f32 / aspect_ratio;
        if antialiasing {
            flattened_path.expand_fill(fringe_width, LineJoin::Miter, 2.4f32, fringe_width);
        } else {
            flattened_path.expand_fill(0.0f32, LineJoin::Miter, 2.4f32, fringe_width);
        }
        flattened_path
    }

    fn get_uv_transform(descriptor: drawing_api::TextureDescriptor) -> PixelToUvTransform {
        PixelToUvTransform::identity().then_scale(
            1.0f32 / descriptor.width as f32,
            1.0f32 / descriptor.height as f32,
        )
    }
}