blob: 24bb7b02e7ccb71f712327b2df1919c2d9a8ec8e [file] [log] [blame]
// Copyright 2020 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 {
anyhow::Error,
argh::FromArgs,
carnelian::{
color::Color,
input, make_app_assistant,
render::{
BlendMode, Composition, Context, Fill, FillRule, Layer, PreClear, Raster, RenderExt,
Style,
},
AnimationMode, App, AppAssistant, RenderOptions, Size, ViewAssistant, ViewAssistantContext,
ViewAssistantPtr, ViewKey,
},
euclid::{
default::{Point2D, Transform2D, Vector2D},
Angle,
},
fuchsia_trace_provider,
fuchsia_zircon::{AsHandleRef, Event, Signals},
std::collections::BTreeMap,
};
#[path = "static/fuchsia.rs"]
mod fuchsia;
const BACKGROUND_COLOR: Color = Color { r: 255, g: 255, b: 255, a: 255 };
/// Svg.
#[derive(Debug, FromArgs)]
#[argh(name = "svg-rs")]
struct Args {
/// use spinel (GPU rendering back-end)
#[argh(switch, short = 's')]
use_spinel: bool,
}
#[derive(Default)]
struct SvgAppAssistant {
use_spinel: bool,
}
impl AppAssistant for SvgAppAssistant {
fn setup(&mut self) -> Result<(), Error> {
let args: Args = argh::from_env();
self.use_spinel = args.use_spinel;
Ok(())
}
fn create_view_assistant(&mut self, _: ViewKey) -> Result<ViewAssistantPtr, Error> {
Ok(Box::new(SvgViewAssistant::new()))
}
fn get_render_options(&self) -> RenderOptions {
RenderOptions { use_spinel: self.use_spinel }
}
}
struct Rendering {
size: Size,
composition: Composition,
last_position: Option<Point2D<f32>>,
}
impl Rendering {
fn new() -> Rendering {
let composition = Composition::new(BACKGROUND_COLOR);
Rendering { size: Size::zero(), composition, last_position: None }
}
}
struct SvgViewAssistant {
rasters: Option<Vec<(Raster, Style)>>,
renderings: BTreeMap<u64, Rendering>,
position: Point2D<f32>,
touch_locations: BTreeMap<input::pointer::PointerId, Point2D<f32>>,
}
impl SvgViewAssistant {
pub fn new() -> Self {
Self {
rasters: None,
renderings: BTreeMap::new(),
position: Point2D::zero(),
touch_locations: BTreeMap::new(),
}
}
}
impl ViewAssistant for SvgViewAssistant {
fn setup(&mut self, _context: &ViewAssistantContext) -> Result<(), Error> {
Ok(())
}
fn render(
&mut self,
render_context: &mut Context,
ready_event: Event,
context: &ViewAssistantContext,
) -> Result<(), Error> {
let image_id = context.image_id;
let rendering = self.renderings.entry(image_id).or_insert_with(|| Rendering::new());
let position = self.position;
let last_position = rendering.last_position;
let rasters = self.rasters.get_or_insert_with(|| {
let size = 50.0;
let min_size = context.size.width.min(context.size.height);
let transform = Transform2D::create_rotation(Angle::degrees(90.0))
.pre_translate(Vector2D::new(-size / 2.0, -size / 2.0))
.post_scale(min_size / size, min_size / size)
.post_translate(Vector2D::new(context.size.width / 2.0, context.size.height / 2.0));
fuchsia::fuchsia(render_context, Some(&transform))
});
let layers = rasters.iter().map(|(raster, style)| Layer {
raster: raster.clone().translate(position.to_vector().to_i32()),
style: *style,
});
if let Some(last_position) = last_position {
rendering.composition.replace(
..,
layers.chain(rasters.iter().map(|(raster, _)| Layer {
raster: raster.clone().translate(last_position.to_vector().to_i32()),
style: Style {
fill_rule: FillRule::WholeTile,
fill: Fill::Solid(BACKGROUND_COLOR),
blend_mode: BlendMode::Over,
},
})),
);
} else {
rendering.composition.replace(.., layers);
}
let ext = if rendering.size != context.size {
rendering.size = context.size;
RenderExt {
pre_clear: Some(PreClear { color: BACKGROUND_COLOR }),
..Default::default()
}
} else {
RenderExt::default()
};
rendering.last_position = Some(position);
let image = render_context.get_current_image(context);
render_context.render(&rendering.composition, None, image, &ext);
ready_event.as_handle_ref().signal(Signals::NONE, Signals::EVENT_SIGNALED)?;
Ok(())
}
fn initial_animation_mode(&mut self) -> AnimationMode {
return AnimationMode::EveryFrame;
}
fn handle_pointer_event(
&mut self,
_context: &mut ViewAssistantContext,
_event: &input::Event,
pointer_event: &input::pointer::Event,
) -> Result<(), Error> {
match &pointer_event.phase {
input::pointer::Phase::Down(touch_location) => {
self.touch_locations
.insert(pointer_event.pointer_id.clone(), touch_location.to_f32());
}
input::pointer::Phase::Moved(touch_location) => {
if let Some(location) = self.touch_locations.get_mut(&pointer_event.pointer_id) {
// Pan image using the change to touch location.
self.position += touch_location.to_f32() - *location;
*location = touch_location.to_f32();
}
}
input::pointer::Phase::Up => {
self.touch_locations.remove(&pointer_event.pointer_id.clone());
}
_ => (),
}
Ok(())
}
}
fn main() -> Result<(), Error> {
fuchsia_trace_provider::trace_provider_create_with_fdio();
println!("Svg Example");
App::run(make_app_assistant::<SvgAppAssistant>())
}