Add an incomplete port of the NanoVG example app

This commit is contained in:
Patrick Walton 2020-02-18 14:35:00 -08:00
parent fd070d7a24
commit 25f9346160
5 changed files with 631 additions and 0 deletions

17
Cargo.lock generated
View File

@ -267,6 +267,23 @@ dependencies = [
"sdl2-sys 0.33.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "canvas_nanovg"
version = "0.1.0"
dependencies = [
"arrayvec 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
"gl 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)",
"pathfinder_canvas 0.1.0",
"pathfinder_color 0.1.0",
"pathfinder_content 0.1.0",
"pathfinder_geometry 0.4.0",
"pathfinder_gl 0.1.0",
"pathfinder_gpu 0.1.0",
"pathfinder_renderer 0.1.0",
"sdl2 0.33.0 (registry+https://github.com/rust-lang/crates.io-index)",
"sdl2-sys 0.33.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "canvas_text"
version = "0.1.0"

View File

@ -12,6 +12,7 @@ members = [
"examples/canvas_metal_minimal",
"examples/canvas_minimal",
"examples/canvas_moire",
"examples/canvas_nanovg",
"examples/canvas_text",
"examples/lottie_basic",
"examples/swf_basic",

View File

@ -0,0 +1,33 @@
[package]
name = "canvas_nanovg"
version = "0.1.0"
authors = ["Patrick Walton <pcwalton@mimiga.net>"]
edition = "2018"
[dependencies]
arrayvec = "0.5"
gl = "0.14"
sdl2 = "0.33"
sdl2-sys = "0.33"
[dependencies.pathfinder_canvas]
path = "../../canvas"
features = ["pf-text"]
[dependencies.pathfinder_color]
path = "../../color"
[dependencies.pathfinder_content]
path = "../../content"
[dependencies.pathfinder_geometry]
path = "../../geometry"
[dependencies.pathfinder_gl]
path = "../../gl"
[dependencies.pathfinder_gpu]
path = "../../gpu"
[dependencies.pathfinder_renderer]
path = "../../renderer"

View File

@ -0,0 +1,18 @@
Copyright (c) 2013 Mikko Mononen memon@inside.org
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.

View File

@ -0,0 +1,562 @@
// pathfinder/examples/canvas_nanovg/src/main.rs
//
// Copyright © 2020 The Pathfinder Project Developers.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use arrayvec::ArrayVec;
use pathfinder_canvas::{CanvasFontContext, CanvasRenderingContext2D, FillStyle, LineJoin, Path2D};
use pathfinder_color::{ColorF, ColorU};
use pathfinder_content::fill::FillRule;
use pathfinder_content::gradient::{ColorStop, Gradient};
use pathfinder_content::outline::ArcDirection;
use pathfinder_content::stroke::LineCap;
use pathfinder_geometry::angle;
use pathfinder_geometry::line_segment::LineSegment2F;
use pathfinder_geometry::rect::RectF;
use pathfinder_geometry::transform2d::Transform2F;
use pathfinder_geometry::util;
use pathfinder_geometry::vector::{Vector2F, Vector2I};
use pathfinder_gl::{GLDevice, GLVersion};
use pathfinder_gpu::resources::FilesystemResourceLoader;
use pathfinder_renderer::concurrent::rayon::RayonExecutor;
use pathfinder_renderer::concurrent::scene_proxy::SceneProxy;
use pathfinder_renderer::gpu::options::{DestFramebuffer, RendererOptions};
use pathfinder_renderer::gpu::renderer::Renderer;
use pathfinder_renderer::options::BuildOptions;
use sdl2::event::Event;
use sdl2::keyboard::Keycode;
use sdl2::video::GLProfile;
use std::f32::consts::PI;
use std::time::Instant;
// TODO(pcwalton): See if we can reduce the amount of code by using the canvas shadow feature.
const PI_2: f32 = PI * 2.0;
static PARAGRAPH_TEXT: &'static str = "This is a longer chunk of text.
I would have used lorem ipsum, but she was busy jumping over the lazy dog with the fox and all \
the men who came to the aid of the party.";
fn render_demo(canvas: &mut CanvasRenderingContext2D,
mouse_position: Vector2F,
window_size: Vector2F,
time: f32) {
draw_eyes(canvas,
RectF::new(Vector2F::new(window_size.x() - 250.0, 50.0),
Vector2F::new(150.0, 100.0)),
mouse_position,
time);
/*
FIXME(pcwalton): Too slow. See https://github.com/linebender/skribo/issues/30
draw_paragraph(canvas,
RectF::new(Vector2F::new(window_size.x() - 450.0, 50.0),
Vector2F::new(150.0, 100.0)));
*/
draw_graph(canvas,
RectF::new(window_size.scale_xy(Vector2F::new(0.0, 0.5)),
window_size.scale_xy(Vector2F::new(1.0, 0.5))),
time);
draw_color_wheel(canvas,
RectF::new(window_size - Vector2F::splat(300.0), Vector2F::splat(250.0)),
time);
draw_lines(canvas,
RectF::new(Vector2F::new(120.0, window_size.y() - 50.0),
Vector2F::new(600.0, 50.0)),
time);
draw_caps(canvas, RectF::new(Vector2F::new(10.0, 300.0), Vector2F::new(30.0, 40.0)));
draw_clip(canvas, Vector2F::new(50.0, window_size.y() - 80.0), time);
}
fn draw_eyes(canvas: &mut CanvasRenderingContext2D,
rect: RectF,
mouse_position: Vector2F,
time: f32) {
let eyes_radii = rect.size().scale_xy(Vector2F::new(0.23, 0.5));
let eyes_left_position = rect.origin() + eyes_radii;
let eyes_right_position = rect.origin() + Vector2F::new(rect.width() - eyes_radii.x(),
eyes_radii.y());
let eyes_center = f32::min(eyes_radii.x(), eyes_radii.y()) * 0.5;
let blink = 1.0 - f32::powf(f32::sin(time * 0.5), 200.0) * 0.8;
let mut gradient =
Gradient::linear(LineSegment2F::new(Vector2F::new(0.0, rect.height() * 0.5),
rect.size().scale_xy(Vector2F::new(0.1, 1.0))) +
rect.origin());
gradient.add_color_stop(ColorStop::new(ColorU::new(0, 0, 0, 32), 0.0));
gradient.add_color_stop(ColorStop::new(ColorU::new(0, 0, 0, 16), 1.0));
let mut path = Path2D::new();
path.ellipse(eyes_left_position + Vector2F::new(3.0, 16.0), eyes_radii, 0.0, 0.0, PI_2);
path.ellipse(eyes_right_position + Vector2F::new(3.0, 16.0), eyes_radii, 0.0, 0.0, PI_2);
canvas.set_fill_style(FillStyle::Gradient(gradient));
canvas.fill_path(path, FillRule::Winding);
let mut gradient =
Gradient::linear(LineSegment2F::new(Vector2F::new(0.0, rect.height() * 0.25),
rect.size().scale_xy(Vector2F::new(0.1, 1.0))) +
rect.origin());
gradient.add_color_stop(ColorStop::new(ColorU::new(220, 220, 220, 255), 0.0));
gradient.add_color_stop(ColorStop::new(ColorU::new(128, 128, 128, 255), 1.0));
let mut path = Path2D::new();
path.ellipse(eyes_left_position, eyes_radii, 0.0, 0.0, PI_2);
path.ellipse(eyes_right_position, eyes_radii, 0.0, 0.0, PI_2);
canvas.set_fill_style(FillStyle::Gradient(gradient));
canvas.fill_path(path, FillRule::Winding);
let mut delta = (mouse_position - eyes_right_position) / eyes_radii.scale(10.0);
let distance = delta.length();
if distance > 1.0 {
delta = delta.scale(1.0 / distance);
}
delta = delta.scale_xy(eyes_radii).scale_xy(Vector2F::new(0.4, 0.5));
let mut path = Path2D::new();
path.ellipse(eyes_left_position +
delta +
Vector2F::new(0.0, eyes_radii.y() * 0.25 * (1.0 - blink)),
Vector2F::new(eyes_center, eyes_center * blink),
0.0,
0.0,
PI_2);
path.ellipse(eyes_right_position +
delta +
Vector2F::new(0.0, eyes_radii.y() * 0.25 * (1.0 - blink)),
Vector2F::new(eyes_center, eyes_center * blink),
0.0,
0.0,
PI_2);
canvas.set_fill_style(FillStyle::Color(ColorU::new(32, 32, 32, 255)));
canvas.fill_path(path, FillRule::Winding);
let gloss_position = eyes_left_position - eyes_radii.scale_xy(Vector2F::new(0.25, 0.5));
let mut gloss = Gradient::radial(LineSegment2F::new(gloss_position, gloss_position),
eyes_radii.x() * 0.1,
eyes_radii.x() * 0.75);
gloss.add_color_stop(ColorStop::new(ColorU::new(255, 255, 255, 128), 0.0));
gloss.add_color_stop(ColorStop::new(ColorU::new(255, 255, 255, 0), 1.0));
canvas.set_fill_style(FillStyle::Gradient(gloss));
let mut path = Path2D::new();
path.ellipse(eyes_left_position, eyes_radii, 0.0, 0.0, PI_2);
canvas.fill_path(path, FillRule::Winding);
let gloss_position = eyes_right_position - eyes_radii.scale_xy(Vector2F::new(0.25, 0.5));
let mut gloss = Gradient::radial(LineSegment2F::new(gloss_position, gloss_position),
eyes_radii.x() * 0.1,
eyes_radii.x() * 0.75);
gloss.add_color_stop(ColorStop::new(ColorU::new(255, 255, 255, 128), 0.0));
gloss.add_color_stop(ColorStop::new(ColorU::new(255, 255, 255, 0), 1.0));
canvas.set_fill_style(FillStyle::Gradient(gloss));
let mut path = Path2D::new();
path.ellipse(eyes_right_position, eyes_radii, 0.0, 0.0, PI_2);
canvas.fill_path(path, FillRule::Winding);
}
// This is nowhere near correct line layout, but it suffices to more or less match what NanoVG
// does.
fn draw_paragraph(canvas: &mut CanvasRenderingContext2D, rect: RectF) {
const LINE_HEIGHT: f32 = 24.0;
canvas.save();
canvas.set_font_size(18.0);
let mut cursor = rect.origin();
next_line(canvas, &mut cursor, rect);
let space_width = canvas.measure_text("A B").width - canvas.measure_text("AB").width;
for space_separated in PARAGRAPH_TEXT.split(' ') {
let mut first = true;
for word in space_separated.split('\n') {
if !first {
next_line(canvas, &mut cursor, rect);
}
first = false;
let word_width = canvas.measure_text(word).width;
if cursor.x() + space_width + word_width > rect.max_x() {
next_line(canvas, &mut cursor, rect);
} else if cursor.x() > rect.min_x() {
cursor = cursor + Vector2F::new(space_width, 0.0);
}
canvas.set_fill_style(FillStyle::Color(ColorU::white()));
canvas.fill_text(word, cursor);
cursor = cursor + Vector2F::new(word_width, 0.0);
}
}
canvas.restore();
fn next_line(canvas: &mut CanvasRenderingContext2D, cursor: &mut Vector2F, rect: RectF) {
cursor.set_x(rect.min_x());
canvas.set_fill_style(FillStyle::Color(ColorU::new(255, 255, 255, 16)));
canvas.fill_rect(RectF::new(*cursor, Vector2F::new(rect.width(), LINE_HEIGHT)));
*cursor = *cursor + Vector2F::new(0.0, LINE_HEIGHT);
}
}
fn draw_graph(canvas: &mut CanvasRenderingContext2D, rect: RectF, time: f32) {
let sample_spread = rect.width() / 5.0;
let samples = [
(1.0 + f32::sin(time * 1.2345 + f32::cos(time * 0.33457) * 0.44)) * 0.5,
(1.0 + f32::sin(time * 0.68363 + f32::cos(time * 1.30) * 1.55)) * 0.5,
(1.0 + f32::sin(time * 1.1642 + f32::cos(time * 0.33457) * 1.24)) * 0.5,
(1.0 + f32::sin(time * 0.56345 + f32::cos(time * 1.63) * 0.14)) * 0.5,
(1.0 + f32::sin(time * 1.6245 + f32::cos(time * 0.254) * 0.3)) * 0.5,
(1.0 + f32::sin(time * 0.345 + f32::cos(time * 0.03) * 0.6)) * 0.5,
];
let sample_scale = Vector2F::new(sample_spread, rect.height() * 0.8);
let sample_points: ArrayVec<[Vector2F; 6]> = samples.iter()
.enumerate()
.map(|(index, &sample)| {
rect.origin() + Vector2F::new(index as f32, sample).scale_xy(sample_scale)
}).collect();
// Draw graph background.
let mut background = Gradient::linear(LineSegment2F::new(Vector2F::default(),
Vector2F::new(0.0, rect.height())) +
rect.origin());
background.add_color_stop(ColorStop::new(ColorU::new(0, 160, 192, 0), 0.0));
background.add_color_stop(ColorStop::new(ColorU::new(0, 160, 192, 64), 1.0));
canvas.set_fill_style(FillStyle::Gradient(background));
let mut path = create_graph_path(&sample_points, sample_spread, Vector2F::default());
path.line_to(rect.lower_right());
path.line_to(rect.lower_left());
canvas.fill_path(path, FillRule::Winding);
// Draw graph line shadow.
canvas.set_stroke_style(FillStyle::Color(ColorU::new(0, 0, 0, 32)));
canvas.set_line_width(3.0);
let path = create_graph_path(&sample_points, sample_spread, Vector2F::new(0.0, 2.0));
canvas.stroke_path(path);
// Draw graph line.
canvas.set_stroke_style(FillStyle::Color(ColorU::new(0, 160, 192, 255)));
canvas.set_line_width(3.0);
let path = create_graph_path(&sample_points, sample_spread, Vector2F::default());
canvas.stroke_path(path);
// Draw sample position highlights.
for &sample_point in &sample_points {
let gradient_center = sample_point + Vector2F::new(0.0, 2.0);
let mut background = Gradient::radial(LineSegment2F::new(gradient_center, gradient_center),
3.0,
8.0);
background.add_color_stop(ColorStop::new(ColorU::new(0, 0, 0, 32), 0.0));
background.add_color_stop(ColorStop::new(ColorU::transparent_black(), 1.0));
canvas.set_fill_style(FillStyle::Gradient(background));
canvas.fill_rect(RectF::new(sample_point + Vector2F::new(-10.0, -10.0 + 2.0),
Vector2F::splat(20.0)));
}
// Draw sample positions.
canvas.set_fill_style(FillStyle::Color(ColorU::new(0, 160, 192, 255)));
let mut path = Path2D::new();
for &sample_point in &sample_points {
path.ellipse(sample_point, Vector2F::splat(4.0), 0.0, 0.0, PI_2);
}
canvas.fill_path(path, FillRule::Winding);
canvas.set_fill_style(FillStyle::Color(ColorU::new(220, 220, 220, 255)));
let mut path = Path2D::new();
for &sample_point in &sample_points {
path.ellipse(sample_point, Vector2F::splat(2.0), 0.0, 0.0, PI_2);
}
canvas.fill_path(path, FillRule::Winding);
// Reset state.
canvas.set_line_width(1.0);
}
fn draw_color_wheel(canvas: &mut CanvasRenderingContext2D, rect: RectF, time: f32) {
let hue = time * 0.12;
canvas.save();
let center = rect.center();
let outer_radius = f32::min(rect.width(), rect.height()) * 0.5 - 5.0;
let inner_radius = outer_radius - 20.0;
// Half a pixel arc length in radians.
let half_arc_len = 0.5 / outer_radius;
// Draw outer circle.
for segment in 0..6 {
let start_angle = segment as f32 / 6.0 * PI_2 - half_arc_len;
let end_angle = (segment + 1) as f32 / 6.0 * PI_2 + half_arc_len;
let line = LineSegment2F::new(Vector2F::new(f32::cos(start_angle), f32::sin(start_angle)),
Vector2F::new(f32::cos(end_angle), f32::sin(end_angle)));
let scale = util::lerp(inner_radius, outer_radius, 0.5);
let mut gradient = Gradient::linear(line.scale(scale) + center);
let start_color = ColorF::from_hsl(start_angle, 1.0, 0.55).to_u8();
let end_color = ColorF::from_hsl(end_angle, 1.0, 0.55).to_u8();
gradient.add_color_stop(ColorStop::new(start_color, 0.0));
gradient.add_color_stop(ColorStop::new(end_color, 1.0));
canvas.set_fill_style(FillStyle::Gradient(gradient));
let mut path = Path2D::new();
path.arc(center, inner_radius, start_angle, end_angle, ArcDirection::CW);
path.arc(center, outer_radius, end_angle, start_angle, ArcDirection::CCW);
path.close_path();
canvas.fill_path(path, FillRule::Winding);
}
// Stroke outer circle.
canvas.set_stroke_style(FillStyle::Color(ColorU::new(0, 0, 0, 64)));
canvas.set_line_width(1.0);
let mut path = Path2D::new();
path.ellipse(center, Vector2F::splat(inner_radius - 0.5), 0.0, 0.0, PI_2);
path.ellipse(center, Vector2F::splat(outer_radius + 0.5), 0.0, 0.0, PI_2);
canvas.stroke_path(path);
// Prepare to draw the selector.
canvas.save();
canvas.set_current_transform(&(Transform2F::from_translation(center) *
Transform2F::from_rotation(hue)));
canvas.set_stroke_style(FillStyle::Color(ColorU::new(255, 255, 255, 192)));
canvas.set_line_width(2.0);
canvas.stroke_rect(RectF::new(Vector2F::new(inner_radius - 1.0, -3.0),
Vector2F::new(outer_radius - inner_radius + 2.0, 6.0)));
// TODO(pcwalton): Marker fill with box gradient
// Draw center triangle.
let triangle_radius = inner_radius - 6.0;
let triangle_vertex_a = Vector2F::new(triangle_radius, 0.0);
let triangle_vertex_b = Vector2F::new(f32::cos(PI * 2.0 / 3.0),
f32::sin(PI * 2.0 / 3.0)).scale(triangle_radius);
let triangle_vertex_c = Vector2F::new(f32::cos(PI * -2.0 / 3.0),
f32::sin(PI * -2.0 / 3.0)).scale(triangle_radius);
let mut gradient_0 = Gradient::linear(LineSegment2F::new(triangle_vertex_a,
triangle_vertex_b));
gradient_0.add_color_stop(ColorStop::new(ColorF::from_hsl(hue, 1.0, 0.5).to_u8(), 0.0));
gradient_0.add_color_stop(ColorStop::new(ColorU::white(), 1.0));
let mut gradient_1 =
Gradient::linear(LineSegment2F::new(triangle_vertex_a.lerp(triangle_vertex_b, 0.5),
triangle_vertex_c));
gradient_1.add_color_stop(ColorStop::new(ColorU::transparent_black(), 0.0));
gradient_1.add_color_stop(ColorStop::new(ColorU::black(), 1.0));
let mut path = Path2D::new();
path.move_to(triangle_vertex_a);
path.line_to(triangle_vertex_b);
path.line_to(triangle_vertex_c);
path.close_path();
canvas.set_fill_style(FillStyle::Gradient(gradient_0));
canvas.fill_path(path.clone(), FillRule::Winding);
canvas.set_fill_style(FillStyle::Gradient(gradient_1));
canvas.fill_path(path.clone(), FillRule::Winding);
canvas.set_stroke_style(FillStyle::Color(ColorU::new(0, 0, 0, 64)));
canvas.stroke_path(path);
// Stroke the selection circle on the triangle.
let selection_circle_center =
Vector2F::new(f32::cos(PI_2 / 3.0),
f32::sin(PI_2 / 3.0)).scale(triangle_radius)
.scale_xy(Vector2F::new(0.3, 0.4));
canvas.set_stroke_style(FillStyle::Color(ColorU::new(255, 255, 255, 192)));
canvas.set_line_width(2.0);
let mut path = Path2D::new();
path.ellipse(selection_circle_center, Vector2F::splat(5.0), 0.0, 0.0, PI_2);
canvas.stroke_path(path);
// Fill the selection circle.
let mut gradient = Gradient::radial(LineSegment2F::new(selection_circle_center,
selection_circle_center),
7.0,
9.0);
gradient.add_color_stop(ColorStop::new(ColorU::new(0, 0, 0, 64), 0.0));
gradient.add_color_stop(ColorStop::new(ColorU::transparent_black(), 1.0));
canvas.set_fill_style(FillStyle::Gradient(gradient));
let mut path = Path2D::new();
path.rect(RectF::new(selection_circle_center - Vector2F::splat(20.0), Vector2F::splat(40.0)));
path.ellipse(selection_circle_center, Vector2F::splat(7.0), 0.0, 0.0, PI_2);
canvas.fill_path(path, FillRule::EvenOdd);
canvas.restore();
canvas.restore();
}
fn draw_lines(canvas: &mut CanvasRenderingContext2D, rect: RectF, time: f32) {
const PADDING: f32 = 5.0;
let spacing = rect.width() / 9.0 - PADDING * 2.0;
canvas.save();
let points = [
Vector2F::new(-spacing * 0.25 + f32::cos(time * 0.3) * spacing * 0.5,
f32::sin(time * 0.3) * spacing * 0.5),
Vector2F::new(-spacing * 0.25, 0.0),
Vector2F::new( spacing * 0.25, 0.0),
Vector2F::new( spacing * 0.25 + f32::cos(time * -0.3) * spacing * 0.5,
f32::sin(time * -0.3) * spacing * 0.5),
];
for (cap_index, &cap) in [LineCap::Butt, LineCap::Round, LineCap::Square].iter().enumerate() {
for (join_index, &join) in [
LineJoin::Miter, LineJoin::Miter /* FIXME(pcwalton): Round crashes */, LineJoin::Bevel
].iter().enumerate() {
let origin = rect.origin() +
Vector2F::new(spacing, -spacing).scale(0.5) +
Vector2F::new((cap_index * 3 + join_index) as f32 / 9.0 * rect.width(), 0.0) +
Vector2F::splat(PADDING);
canvas.set_line_cap(cap);
canvas.set_line_join(join);
canvas.set_line_width(spacing * 0.3);
canvas.set_stroke_style(FillStyle::Color(ColorU::new(0, 0, 0, 160)));
let mut path = Path2D::new();
path.move_to(points[0] + origin);
path.line_to(points[1] + origin);
path.line_to(points[2] + origin);
path.line_to(points[3] + origin);
canvas.stroke_path(path.clone());
canvas.set_line_cap(LineCap::Butt);
canvas.set_line_join(LineJoin::Bevel);
canvas.set_line_width(1.0);
canvas.set_stroke_style(FillStyle::Color(ColorU::new(0, 192, 255, 255)));
canvas.stroke_path(path);
}
}
canvas.restore();
}
fn draw_caps(canvas: &mut CanvasRenderingContext2D, rect: RectF) {
const LINE_WIDTH: f32 = 8.0;
canvas.save();
canvas.set_fill_style(FillStyle::Color(ColorU::new(255, 255, 255, 32)));
canvas.fill_rect(rect.dilate(Vector2F::new(LINE_WIDTH / 2.0, 0.0)));
canvas.fill_rect(rect);
canvas.set_line_width(LINE_WIDTH);
for (cap_index, &cap) in [LineCap::Butt, LineCap::Round, LineCap::Square].iter().enumerate() {
canvas.set_line_cap(cap);
canvas.set_stroke_style(FillStyle::Color(ColorU::black()));
let offset = cap_index as f32 * 10.0 + 5.0;
let mut path = Path2D::new();
path.move_to(rect.origin() + Vector2F::new(0.0, offset));
path.line_to(rect.upper_right() + Vector2F::new(0.0, offset));
canvas.stroke_path(path);
}
canvas.restore();
}
fn draw_clip(canvas: &mut CanvasRenderingContext2D, origin: Vector2F, time: f32) {
canvas.save();
// Draw first rect.
let transform_a = Transform2F::from_translation(origin) *
Transform2F::from_rotation(angle::angle_from_degrees(5.0));
canvas.set_current_transform(&transform_a);
canvas.set_fill_style(FillStyle::Color(ColorU::new(255, 0, 0, 255)));
let mut clip_path = Path2D::new();
clip_path.rect(RectF::new(Vector2F::splat(-20.0), Vector2F::new(60.0, 40.0)));
canvas.fill_path(clip_path.clone(), FillRule::Winding);
// Draw second rectangle with no clip.
let transform_b = transform_a * Transform2F::from_translation(Vector2F::new(40.0, 0.0)) *
Transform2F::from_rotation(time);
canvas.set_current_transform(&transform_b);
canvas.set_fill_style(FillStyle::Color(ColorU::new(255, 128, 0, 64)));
let fill_rect = RectF::new(Vector2F::new(-20.0, -10.0), Vector2F::new(60.0, 30.0));
canvas.fill_rect(fill_rect);
// Draw second rectangle with clip.
canvas.set_current_transform(&transform_a);
canvas.clip_path(clip_path, FillRule::Winding);
canvas.set_current_transform(&transform_b);
canvas.set_fill_style(FillStyle::Color(ColorU::new(255, 128, 0, 255)));
canvas.fill_rect(fill_rect);
canvas.restore();
}
fn create_graph_path(sample_points: &[Vector2F], sample_spread: f32, offset: Vector2F) -> Path2D {
let mut path = Path2D::new();
path.move_to(sample_points[0] + Vector2F::new(0.0, 2.0));
for pair in sample_points.windows(2) {
path.bezier_curve_to(pair[0] + offset + Vector2F::new(sample_spread * 0.5, 0.0),
pair[1] + offset - Vector2F::new(sample_spread * 0.5, 0.0),
pair[1] + offset);
}
path
}
fn main() {
// Set up SDL2.
let sdl_context = sdl2::init().unwrap();
let video = sdl_context.video().unwrap();
// Make sure we have at least a GL 3.0 context. Pathfinder requires this.
let gl_attributes = video.gl_attr();
gl_attributes.set_context_profile(GLProfile::Core);
gl_attributes.set_context_version(3, 3);
// Open a window.
let window_size = Vector2I::new(800, 600);
let window =
video.window("NanoVG example port", window_size.x() as u32, window_size.y() as u32)
.opengl()
.build()
.unwrap();
// Create the GL context, and make it current.
let gl_context = window.gl_create_context().unwrap();
gl::load_with(|name| video.gl_get_proc_address(name) as *const _);
window.gl_make_current(&gl_context).unwrap();
// Create a Pathfinder renderer.
let mut renderer = Renderer::new(GLDevice::new(GLVersion::GL3, 0),
&FilesystemResourceLoader::locate(),
DestFramebuffer::full_window(window_size),
RendererOptions {
background_color: Some(ColorF::new(0.3, 0.3, 0.32, 1.0)),
});
// Initialize state.
let mut event_pump = sdl_context.event_pump().unwrap();
let mut mouse_position = Vector2F::default();
let start_time = Instant::now();
// Enter the main loop.
loop {
// Make a canvas.
let mut canvas = CanvasRenderingContext2D::new(CanvasFontContext::from_system_source(),
window_size.to_f32());
// Render the demo.
let time = (Instant::now() - start_time).as_secs_f32();
render_demo(&mut canvas, mouse_position, window_size.to_f32(), time);
// Render the canvas to screen.
let scene = SceneProxy::from_scene(canvas.into_scene(), RayonExecutor);
scene.build_and_render(&mut renderer, BuildOptions::default());
window.gl_swap_window();
for event in event_pump.poll_iter() {
match event {
Event::Quit {..} | Event::KeyDown { keycode: Some(Keycode::Escape), .. } => return,
Event::MouseMotion { x, y, .. } => {
mouse_position = Vector2I::new(x, y).to_f32();
}
_ => {}
}
}
}
}