blob: ccfe40701d92bc5a2d5b587c9ad3c2ecffaec681 [file] [log] [blame]
// Copyright 2019 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.
use crate::{
canvas::{measure_text, Canvas, FontDescription, FontFace, MappingPixelSink, Paint},
geometry::{Coord, IntSize, Point, Rect, Size},
};
use failure::{Error, ResultExt};
use fidl_fuchsia_images as images;
use fuchsia_scenic::{EntityNode, HostImageCycler, SessionPtr};
use lazy_static::lazy_static;
// This font creation method isn't ideal. The correct method would be to ask the Fuchsia
// font service for the font data.
static FONT_DATA: &'static [u8] =
include_bytes!("../../../../bin/fonts/third_party/robotoslab/RobotoSlab-Regular.ttf");
lazy_static! {
pub static ref FONT_FACE: FontFace<'static> =
FontFace::new(&FONT_DATA).expect("Failed to create font");
}
const BASELINE: i32 = 0;
fn make_font_description<'a, 'b>(size: u32, baseline: i32) -> FontDescription<'a, 'b> {
FontDescription { face: &FONT_FACE, size: size, baseline: baseline }
}
/// Struct to render a static string as as Scenic node
pub struct Label {
text: String,
image_cycler: HostImageCycler,
}
impl Label {
/// Create a new label for a string in a Scenic session
pub fn new(session: &SessionPtr, text: &str) -> Result<Label, Error> {
Ok(Label { text: text.to_string(), image_cycler: HostImageCycler::new(session.clone()) })
}
/// Call to update the contents of the Scenic node
pub fn update(&mut self, font_size: u32, paint: &Paint) -> Result<(), Error> {
// Figure out the pixel dimension of the label
let size = self.dimensions(font_size).ceil().to_i32();
let w = size.width;
let h = size.height;
if w > 0 && h > 0 {
// Fuchsia is uniformly 4 bytes per pixel but ideally this would
// come from the graphics environment.
let stride = (w * 4) as u32;
// Create a description of this pixel buffer that
// Scenic can understand.
let info = images::ImageInfo {
transform: images::Transform::Normal,
width: w as u32,
height: h as u32,
stride: stride,
pixel_format: images::PixelFormat::Bgra8,
color_space: images::ColorSpace::Srgb,
tiling: images::Tiling::Linear,
alpha_format: images::AlphaFormat::Opaque,
};
// Grab an image buffer from the cycler
let guard = self.image_cycler.acquire(info).context("failed to allocate buffer")?;
// Create a canvas to render into the buffer
let mut canvas = Canvas::<MappingPixelSink>::new(
IntSize::new(w, h),
guard.image().mapping().clone(),
stride,
4,
);
// since the label buffer is sized to fit the text, always draw at 0,0
let location = Point::zero();
// fill the buffer with the bg color, since fill_text ignore pixels that
// aren't covered by glyphs
let bounds = Rect::from_size(Size::new(w as Coord, h as Coord));
canvas.fill_rect(&bounds, paint.bg);
// Fill the text
canvas.fill_text(
&self.text,
location,
&mut make_font_description(font_size, BASELINE),
&paint,
);
}
Ok(())
}
/// Use canvas's measure_text() to find the pixel size of the label
pub fn dimensions(&self, font_size: u32) -> Size {
let mut font_description = make_font_description(font_size, BASELINE);
measure_text(&self.text, &mut font_description)
}
/// Expose the image cycler's node so that it can be added to the
/// button's container
pub fn node(&mut self) -> &EntityNode {
self.image_cycler.node()
}
}