makepad-draw 1.0.0

Makepad 2d drawing API
Documentation
use {
    super::{
        geom::{Point, Rect, Size},
        image::{Bgra, SubimageMut},
    },
    makepad_rustybuzz as rustybuzz,
    rustybuzz::ttf_parser,
};

#[derive(Clone, Debug)]
pub struct GlyphRasterImage<'a> {
    origin_in_dpxs: Point<f32>,
    dpxs_per_em: f32,
    format: Format,
    data: &'a [u8],
}

impl<'a> GlyphRasterImage<'a> {
    pub fn from_raster_glyph_image(image: ttf_parser::RasterGlyphImage<'a>) -> Option<Self> {
        Some(Self {
            origin_in_dpxs: Point::new(image.x as f32, image.y as f32),
            dpxs_per_em: image.pixels_per_em as f32,
            format: Format::from_raster_image_format(image.format)?,
            data: image.data,
        })
    }

    pub fn origin_in_dpxs(&self) -> Point<f32> {
        self.origin_in_dpxs
    }

    pub fn size_in_dpxs(&self) -> Size<f32> {
        let size = self.decode_size();
        Size::new(size.width as f32, size.height as f32)
    }

    pub fn bounds_in_dpxs(&self) -> Rect<f32> {
        Rect::new(self.origin_in_dpxs(), self.size_in_dpxs())
    }

    pub fn dpxs_per_em(&self) -> f32 {
        self.dpxs_per_em
    }

    pub fn decode_size(&self) -> Size<usize> {
        match self.format {
            Format::Png => self.decode_size_png(),
        }
    }

    fn decode_size_png(&self) -> Size<usize> {
        let decoder = png::Decoder::new(self.data);
        let reader = decoder.read_info().unwrap();
        let info = reader.info();
        Size {
            width: info.width as usize,
            height: info.height as usize,
        }
    }

    pub fn decode(&self, image: &mut SubimageMut<Bgra>) {
        match self.format {
            Format::Png => self.decode_png(image),
        }
    }

    fn decode_png(&self, image: &mut SubimageMut<Bgra>) {
        let decoder = png::Decoder::new(self.data);
        let mut reader = decoder.read_info().unwrap();
        let mut buffer = vec![0; reader.output_buffer_size()];
        let output_info = reader.next_frame(&mut buffer).unwrap();
        let info = reader.info();
        let height = info.height as usize;
        let width = info.width as usize;
        match output_info.color_type {
            png::ColorType::Indexed => {
                let palette = info.palette.as_ref().unwrap();
                let trns = info.trns.as_ref();
                let mut set_pixel = |x, y, index| {
                    let base = index * 3;
                    let r = palette[base + 0];
                    let g = palette[base + 1];
                    let b = palette[base + 2];
                    let a = trns.map_or(255, |trns| trns.get(index).copied().unwrap_or(255));
                    image[Point::new(x, y)] = Bgra::new(b, g, r, a);
                };
                match output_info.bit_depth {
                    png::BitDepth::Four => {
                        let bytes_per_row = (width + 1) / 2;
                        for y in 0..height {
                            for x in 0..width {
                                let byte = buffer[y * bytes_per_row + x / 2];
                                set_pixel(
                                    x,
                                    y,
                                    if x % 2 == 0 { byte >> 4 } else { byte & 0x0F } as usize,
                                );
                            }
                        }
                    }
                    png::BitDepth::Eight => {
                        for y in 0..height as usize {
                            for x in 0..width as usize {
                                set_pixel(x, y, buffer[y * width + x] as usize);
                            }
                        }
                    }
                    _ => println!("WARNING: encountered rasterized glyph with unsupported bit depth"),
                }
            }
            _ => println!("WARNING: encountered rasterized glyph with unsupported color type"),
        }
    }
}

#[derive(Clone, Copy, Debug)]
pub enum Format {
    Png,
}

impl Format {
    pub fn from_raster_image_format(format: ttf_parser::RasterImageFormat) -> Option<Self> {
        match format {
            ttf_parser::RasterImageFormat::PNG => Some(Self::Png),
            _ => None,
        }
    }
}