blob: 20e4792f01fe302d7422cd9ec29200765b2cf9e8 [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.
#![allow(dead_code)]
#![allow(unused_imports)]
use anyhow::Error;
use carnelian::{
make_app_assistant, make_font_description, AnimationMode, App, AppAssistant, AppAssistantPtr,
AppContext, Canvas, LocalBoxFuture, MappingPixelSink, PixelSink, Point, Size, ViewAssistant,
ViewAssistantContext, ViewAssistantPtr, ViewKey, ViewMode,
};
use fuchsia_zircon::{ClockId, Time};
use mold::{
tile::{Map, Op},
ColorBuffer, Layer, Path, PixelFormat, Raster,
};
use rusttype::{Contour, Font, Scale, Segment};
use std::{
collections::BTreeMap,
env, f32,
io::{self, Read},
slice, thread,
};
static FONT_DATA: &'static [u8] =
include_bytes!("../../../../../prebuilt/third_party/fonts/robotoslab/RobotoSlab-Regular.ttf");
/// Convenience function that can be called from main and causes the Fuchsia process being
/// run over ssh to be terminated when the user hits control-C.
pub fn wait_for_close() {
if let Some(argument) = env::args().next() {
if !argument.starts_with("/tmp") {
return;
}
}
thread::spawn(move || loop {
let mut input = [0; 1];
match io::stdin().read_exact(&mut input) {
Ok(()) => {}
Err(_) => {
std::process::exit(0);
}
}
});
}
struct RoundedRect {
path: Path,
}
impl RoundedRect {
fn new(pos: Point, size: Point, radius: f32) -> Self {
let dist = 4.0 / 3.0 * (f32::consts::PI / 8.0).tan();
let control_dist = dist * radius;
let tl = pos.to_vector();
let tr = pos.to_vector() + Point::new(size.x, 0.0).to_vector();
let br = pos.to_vector() + size.to_vector();
let bl = pos.to_vector() + Point::new(0.0, size.y).to_vector();
let rt = Point::new(0.0, radius).to_vector();
let rr = Point::new(-radius, 0.0).to_vector();
let rb = Point::new(0.0, -radius).to_vector();
let rl = Point::new(radius, 0.0).to_vector();
let ct = Point::new(0.0, -control_dist).to_vector();
let cr = Point::new(control_dist, 0.0).to_vector();
let cb = Point::new(0.0, control_dist).to_vector();
let cl = Point::new(-control_dist, 0.0).to_vector();
macro_rules! c {
( $v:expr ) => {
mold::Point::new($v.x, $v.y)
};
}
let mut path = Path::new();
path.line(c!(tl + rl), c!(tr + rr));
path.cubic(c!(tr + rr), c!(tr + rr + cr), c!(tr + rt + ct), c!(tr + rt));
path.line(c!(tr + rt), c!(br + rb));
path.cubic(c!(br + rb), c!(br + rb + cb), c!(br + rr + cr), c!(br + rr));
path.line(c!(br + rr), c!(bl + rl));
path.cubic(c!(bl + rl), c!(bl + rl + cl), c!(bl + rb + cb), c!(bl + rb));
path.line(c!(bl + rb), c!(tl + rt));
path.cubic(c!(tl + rt), c!(tl + rt + ct), c!(tl + rl + cl), c!(tl + rl));
Self { path }
}
}
struct Glyph {
path: Path,
}
impl Glyph {
fn new(contours: &[Contour], scale: f32) -> Self {
let mut path = Path::new();
let flip_scale = |x: f32, y: f32| {
let x = x * scale;
let y = (-y + 1.0) * scale;
mold::Point::new(x, y)
};
for contour in contours {
for segment in &contour.segments {
match segment {
Segment::Line(line) => {
path.line(
flip_scale(line.p[1].x, line.p[1].y),
flip_scale(line.p[0].x, line.p[0].y),
);
}
Segment::Curve(curve) => {
path.quad(
flip_scale(curve.p[2].x, curve.p[2].y),
flip_scale(curve.p[1].x, curve.p[1].y),
flip_scale(curve.p[0].x, curve.p[0].y),
);
}
}
}
}
Self { path }
}
}
struct Contents {
rect: Raster,
glyph: Vec<Contour>,
map: Map,
size: Size,
}
impl Contents {
fn make_background(size: &Size) -> Map {
let mut map = Map::new(size.width.floor() as usize, size.height.floor() as usize);
map.global(0, vec![Op::ColorAccZero]);
map.global(3, vec![Op::ColorAccBackground(0xEBD5_B3FF)]);
map
}
fn new(size: Size) -> Contents {
let map = Self::make_background(&size);
let rounded_rect = RoundedRect::new(Point::new(200.0, 100.0), Point::new(90.0, 50.0), 20.0);
let rect = Raster::new(&rounded_rect.path);
let font_description = make_font_description(0, 1);
let glyph =
font_description.face.font.glyph('a').scaled(Scale::uniform(1.0)).shape().unwrap();
Self { rect, glyph, map: map, size: Size::zero() }
}
fn update(&mut self, size: &Size, start: &Time, canvas: &Canvas<MappingPixelSink>) {
if self.size != *size {
self.size = *size;
self.map = Self::make_background(size);
}
const SPEED: f32 = 1.2;
const SECONDS_PER_NANOSECOND: f32 = 1e-9;
const DELTA: f32 = 100.0;
const GLYPH_SIZE: f32 = 200.0;
let time_now = Time::get(ClockId::Monotonic);
let t =
(time_now.into_nanos() - start.into_nanos()) as f32 * SECONDS_PER_NANOSECOND * SPEED;
self.rect.set_translation(mold::Point::new((DELTA * t.sin()).round() as i32, 0));
let mut raster = Raster::new(&Glyph::new(&self.glyph, GLYPH_SIZE * t.sin().abs()).path);
raster.translate(mold::Point::new(100, 100));
self.map.print(
1,
Layer::new(
self.rect.clone(),
vec![
Op::CoverWipZero,
Op::CoverWipNonZero,
Op::ColorWipZero,
Op::ColorWipFillSolid(0x7B68_EEFF),
Op::ColorAccBlendOver,
],
),
);
self.map.print(
2,
Layer::new(
raster,
vec![
Op::CoverWipZero,
Op::CoverWipNonZero,
Op::ColorWipZero,
Op::ColorWipFillSolid(0x0000_00FF),
Op::ColorAccBlendOver,
],
),
);
self.map.render(PixelSinkWrapper::new(
canvas.pixel_sink.clone(),
canvas.col_stride,
canvas.row_stride,
));
}
}
#[derive(Default)]
struct DrawingAppAssistant;
impl AppAssistant for DrawingAppAssistant {
fn setup(&mut self) -> Result<(), Error> {
Ok(())
}
fn create_view_assistant_canvas(&mut self, _: ViewKey) -> Result<ViewAssistantPtr, Error> {
Ok(Box::new(DrawingViewAssistant::new()))
}
fn get_mode(&self) -> ViewMode {
ViewMode::Canvas
}
}
struct DrawingViewAssistant {
contents: BTreeMap<u64, Contents>,
start: Time,
}
impl DrawingViewAssistant {
pub fn new() -> Self {
Self { contents: BTreeMap::new(), start: Time::get(ClockId::Monotonic) }
}
}
#[derive(Clone)]
struct PixelSinkWrapper<P: PixelSink> {
pixel_sink: P,
format: PixelFormat,
stride: usize,
}
impl<P: PixelSink> PixelSinkWrapper<P> {
pub fn new(pixel_sink: P, col_stride: u32, row_stride: u32) -> Self {
Self {
pixel_sink,
format: if col_stride == 2 { PixelFormat::RGB565 } else { PixelFormat::BGRA8888 },
stride: (row_stride / col_stride) as usize,
}
}
}
impl<P: PixelSink> ColorBuffer for PixelSinkWrapper<P> {
fn pixel_format(&self) -> PixelFormat {
self.format
}
fn stride(&self) -> usize {
self.stride
}
unsafe fn write_at(&mut self, offset: usize, src: *const u8, len: usize) {
self.pixel_sink.write_pixel_at_offset(offset, slice::from_raw_parts(src, len));
}
}
impl ViewAssistant for DrawingViewAssistant {
fn setup(&mut self, _: &ViewAssistantContext<'_>) -> Result<(), Error> {
Ok(())
}
fn update(&mut self, context: &ViewAssistantContext<'_>) -> Result<(), Error> {
let canvas = context.canvas.as_ref().unwrap().borrow();
// Temporary hack to deal with the fact that carnelian
// allocates a new buffer for each frame with the same
// image ID of zero.
let mut temp_content;
let content;
if canvas.id == 0 {
temp_content = Contents::new(context.size);
content = &mut temp_content;
} else {
content = self.contents.entry(canvas.id).or_insert_with(|| Contents::new(context.size));
}
content.update(&context.size, &self.start, &canvas);
Ok(())
}
fn initial_animation_mode(&mut self) -> AnimationMode {
return AnimationMode::EveryFrame;
}
}
fn main() -> Result<(), Error> {
println!("drawing: started");
wait_for_close();
App::run(make_app_assistant::<DrawingAppAssistant>())
}