blob: fbc633813c9b6e54c7ac4ce468522bb5fdff2034 [file] [log] [blame]
// Copyright 2018 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#![deny(warnings)]
use font_rs::font::{parse, Font, FontError, GlyphBitmap};
use shared_buffer::SharedBuffer;
use std::collections::HashMap;
#[derive(Hash, Eq, PartialEq, Debug, Clone, Copy)]
pub struct Color {
pub r: u8,
pub g: u8,
pub b: u8,
pub a: u8,
}
#[derive(Hash, Eq, PartialEq, Debug, Clone, Copy)]
pub struct Point {
pub x: u32,
pub y: u32,
}
#[derive(Hash, Eq, PartialEq, Debug, Clone, Copy)]
pub struct Size {
pub width: u32,
pub height: u32,
}
#[derive(Hash, Eq, PartialEq, Debug, Clone, Copy)]
pub struct Rect {
pub left: u32,
pub top: u32,
pub right: u32,
pub bottom: u32,
}
#[derive(Hash, Eq, PartialEq, Debug, Clone, Copy)]
pub struct Paint {
pub fg: Color,
pub bg: Color,
}
#[derive(Hash, Eq, PartialEq, Debug, Clone, Copy)]
struct Glyph(u16);
#[derive(Hash, Eq, PartialEq, Debug)]
struct GlyphDescriptor {
size: u32,
glyph: Glyph,
}
pub struct FontFace<'a> {
font: Font<'a>,
glyph_cache: HashMap<GlyphDescriptor, GlyphBitmap>,
}
pub struct FontDescription<'a, 'b: 'a> {
pub face: &'a mut FontFace<'b>,
pub size: u32,
pub baseline: i32,
}
impl<'a> FontFace<'a> {
pub fn new(data: &'a [u8]) -> Result<FontFace<'a>, FontError> {
Ok(FontFace {
font: parse(data)?,
glyph_cache: HashMap::new(),
})
}
fn get_glyph(&mut self, glyph: Glyph, size: u32) -> &GlyphBitmap {
let font = &self.font;
let Glyph(glyph_id) = glyph;
self.glyph_cache
.entry(GlyphDescriptor { size, glyph })
.or_insert_with(|| font.render_glyph(glyph_id, size).unwrap())
}
fn lookup_glyph(&self, scalar: char) -> Option<Glyph> {
self.font
.lookup_glyph_id(scalar as u32)
.map(|id| Glyph(id))
}
}
pub struct Canvas {
// Assumes a pixel format of BGRA8 and a color space of sRGB.
buffer: SharedBuffer,
stride: u32,
}
const BYTES_PER_PIXEL: u32 = 4;
impl Canvas {
pub fn new(buffer: SharedBuffer, stride: u32) -> Canvas {
Canvas { buffer, stride }
}
#[inline]
fn write_color_at_offset(&mut self, offset: usize, color: Color) {
let pixel = [color.b, color.g, color.r, color.a];
self.buffer.write_at(offset, &pixel);
}
#[inline]
fn set_pixel_at_offset(&mut self, offset: usize, value: u8, paint: &Paint) {
match value {
0 => (),
255 => self.write_color_at_offset(offset, paint.fg),
_ => {
let fg = &paint.fg;
let bg = &paint.bg;
let a = ((value as u32) * (fg.a as u32)) >> 8;
let blend_factor = ((bg.a as u32) * (255 - a)) >> 8;
let pixel = [
(((bg.b as u32) * blend_factor + (fg.b as u32) * a) >> 8) as u8,
(((bg.g as u32) * blend_factor + (fg.g as u32) * a) >> 8) as u8,
(((bg.r as u32) * blend_factor + (fg.r as u32) * a) >> 8) as u8,
(blend_factor + (fg.a as u32)) as u8,
];
self.buffer.write_at(offset, &pixel);
}
}
}
fn draw_glyph_at(&mut self, glyph: &GlyphBitmap, x: i32, y: i32, paint: &Paint) {
let glyph_data = &glyph.data.as_slice();
let col_stride = BYTES_PER_PIXEL as i32;
let row_stride = self.stride as i32;
let mut row_offset = y * row_stride + x * col_stride;
for glyph_row in glyph_data.chunks(glyph.width) {
let mut offset = row_offset;
if offset > 0 {
for pixel in glyph_row {
let value = *pixel;
self.set_pixel_at_offset(offset as usize, value, paint);
offset += col_stride;
}
}
row_offset += row_stride;
}
}
pub fn fill_rect(&mut self, rect: &Rect, color: Color) {
let col_stride = BYTES_PER_PIXEL;
let row_stride = self.stride;
for y in rect.top..rect.bottom {
for x in rect.left..rect.right {
let offset = y * row_stride + x * col_stride;
self.write_color_at_offset(offset as usize, color);
}
}
}
pub fn fill_text(
&mut self, text: &str, point: Point, size: Size, font: &mut FontDescription, paint: &Paint,
) {
let mut x = point.x;
let advance = size.width;
for scalar in text.chars() {
let cell = Rect {
left: x,
top: point.y,
right: x + advance,
bottom: point.y + size.height,
};
self.fill_rect(&cell, paint.bg);
if scalar != ' ' {
if let Some(glyph_id) = font.face.lookup_glyph(scalar) {
let glyph = font.face.get_glyph(glyph_id, font.size);
let x = cell.left as i32 + glyph.left;
let y = cell.top as i32 + font.baseline + glyph.top;
self.draw_glyph_at(glyph, x, y, paint);
}
}
x += advance;
}
}
}