Add support for stroking paths

This will make it easier to visualize the path for layouts.
This commit is contained in:
Simon Hausmann 2020-07-09 09:36:16 +02:00
parent dfe95a2f6d
commit f45ff6ce79
6 changed files with 101 additions and 8 deletions

View file

@ -75,6 +75,8 @@ fn main() {
y: 300., y: 300.,
elements: PathElements::StaticElements(TRIANGLE_PATH.into()), elements: PathElements::StaticElements(TRIANGLE_PATH.into()),
fill_color: Color::from_rgb(0, 128, 255), fill_color: Color::from_rgb(0, 128, 255),
stroke_color: Color::BLACK,
stroke_width: 2.0,
}); });
render_cache.allocate_entry(path_primitive) render_cache.allocate_entry(path_primitive)
}; };

View file

@ -279,6 +279,8 @@ impl TypeRegister {
path.properties.insert("x".to_owned(), Type::Float32); path.properties.insert("x".to_owned(), Type::Float32);
path.properties.insert("y".to_owned(), Type::Float32); path.properties.insert("y".to_owned(), Type::Float32);
path.properties.insert("fill_color".to_owned(), Type::Color); path.properties.insert("fill_color".to_owned(), Type::Color);
path.properties.insert("stroke_color".to_owned(), Type::Color);
path.properties.insert("stroke_width".to_owned(), Type::Float32);
path.disallow_global_types_as_child_elements = true; path.disallow_global_types_as_child_elements = true;
let path_elements = { let path_elements = {

View file

@ -207,6 +207,8 @@ impl Color {
pub const BLACK: Color = Color::from_rgb(0, 0, 0); pub const BLACK: Color = Color::from_rgb(0, 0, 0);
/// A constant for the white color /// A constant for the white color
pub const WHITE: Color = Color::from_rgb(255, 255, 255); pub const WHITE: Color = Color::from_rgb(255, 255, 255);
/// A constant for the transparent color
pub const TRANSPARENT: Color = Color::from_rgba(0, 0, 0, 0);
} }
impl From<u32> for Color { impl From<u32> for Color {
@ -428,6 +430,8 @@ pub enum RenderingPrimitive {
y: f32, y: f32,
elements: crate::PathElements, elements: crate::PathElements,
fill_color: Color, fill_color: Color,
stroke_color: Color,
stroke_width: f32,
}, },
} }

View file

@ -270,6 +270,8 @@ pub struct Path {
pub y: Property<f32>, pub y: Property<f32>,
pub elements: Property<PathElements>, pub elements: Property<PathElements>,
pub fill_color: Property<Color>, pub fill_color: Property<Color>,
pub stroke_color: Property<Color>,
pub stroke_width: Property<f32>,
pub cached_rendering_data: CachedRenderingData, pub cached_rendering_data: CachedRenderingData,
} }
@ -291,6 +293,8 @@ impl Item for Path {
y: Self::field_offsets().y.apply_pin(self).get(context), y: Self::field_offsets().y.apply_pin(self).get(context),
elements: Self::field_offsets().elements.apply_pin(self).get(context), elements: Self::field_offsets().elements.apply_pin(self).get(context),
fill_color: Self::field_offsets().fill_color.apply_pin(self).get(context), fill_color: Self::field_offsets().fill_color.apply_pin(self).get(context),
stroke_color: Self::field_offsets().stroke_color.apply_pin(self).get(context),
stroke_width: Self::field_offsets().stroke_width.apply_pin(self).get(context),
} }
} }

View file

@ -3,7 +3,10 @@ use glow::{Context as GLContext, HasContext};
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
use itertools::Itertools; use itertools::Itertools;
use lyon::tessellation::geometry_builder::{BuffersBuilder, VertexBuffers}; use lyon::tessellation::geometry_builder::{BuffersBuilder, VertexBuffers};
use lyon::tessellation::{FillAttributes, FillOptions, FillTessellator}; use lyon::tessellation::{
FillAttributes, FillOptions, FillTessellator, StrokeAttributes, StrokeOptions,
StrokeTessellator,
};
use sixtyfps_corelib::abi::datastructures::{ use sixtyfps_corelib::abi::datastructures::{
Color, ComponentWindow, ComponentWindowOpaque, Point, Rect, RenderingPrimitive, Resource, Size, Color, ComponentWindow, ComponentWindowOpaque, Point, Rect, RenderingPrimitive, Resource, Size,
}; };
@ -111,6 +114,7 @@ pub struct GLRenderer {
pub struct GLRenderingPrimitivesBuilder { pub struct GLRenderingPrimitivesBuilder {
context: Rc<glow::Context>, context: Rc<glow::Context>,
fill_tesselator: FillTessellator, fill_tesselator: FillTessellator,
stroke_tesselator: StrokeTessellator,
texture_atlas: Rc<RefCell<TextureAtlas>>, texture_atlas: Rc<RefCell<TextureAtlas>>,
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
platform_data: Rc<RefCell<PlatformData>>, platform_data: Rc<RefCell<PlatformData>>,
@ -226,6 +230,7 @@ impl GraphicsBackend for GLRenderer {
GLRenderingPrimitivesBuilder { GLRenderingPrimitivesBuilder {
context: self.context.clone(), context: self.context.clone(),
fill_tesselator: FillTessellator::new(), fill_tesselator: FillTessellator::new(),
stroke_tesselator: StrokeTessellator::new(),
texture_atlas: self.texture_atlas.clone(), texture_atlas: self.texture_atlas.clone(),
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
platform_data: self.platform_data.clone(), platform_data: self.platform_data.clone(),
@ -306,7 +311,9 @@ impl RenderingPrimitivesBuilder for GLRenderingPrimitivesBuilder {
rect_path.line_to(Point::new(*width, *height)); rect_path.line_to(Point::new(*width, *height));
rect_path.line_to(Point::new(0.0, *height)); rect_path.line_to(Point::new(0.0, *height));
rect_path.close(); rect_path.close();
self.create_path(&rect_path.build(), FillStyle::SolidColor(*color)) self.fill_path(&rect_path.build(), FillStyle::SolidColor(*color))
.into_iter()
.collect()
} }
RenderingPrimitive::Image { x: _, y: _, source } => { RenderingPrimitive::Image { x: _, y: _, source } => {
match source { match source {
@ -337,8 +344,39 @@ impl RenderingPrimitivesBuilder for GLRenderingPrimitivesBuilder {
if *font_pixel_size != 0. { *font_pixel_size } else { 48.0 * 72. / 96. }; if *font_pixel_size != 0. { *font_pixel_size } else { 48.0 * 72. / 96. };
smallvec![self.create_glyph_runs(text, font_family, pixel_size, *color)] smallvec![self.create_glyph_runs(text, font_family, pixel_size, *color)]
} }
RenderingPrimitive::Path { x: _, y: _, elements, fill_color } => self RenderingPrimitive::Path {
.create_path(elements.build_path().iter(), FillStyle::SolidColor(*fill_color)), x: _,
y: _,
elements,
fill_color,
stroke_color,
stroke_width,
} => {
let mut primitives = SmallVec::new();
if *fill_color != Color::TRANSPARENT {
primitives.extend(
self.fill_path(
elements.build_path().iter(),
FillStyle::SolidColor(*fill_color),
)
.into_iter(),
);
}
if *stroke_color != Color::TRANSPARENT {
primitives.extend(
self.stroke_path(
elements.build_path().iter(),
*stroke_color,
*stroke_width,
)
.into_iter(),
);
}
primitives
}
}, },
rendering_primitive: primitive, rendering_primitive: primitive,
} }
@ -346,11 +384,11 @@ impl RenderingPrimitivesBuilder for GLRenderingPrimitivesBuilder {
} }
impl GLRenderingPrimitivesBuilder { impl GLRenderingPrimitivesBuilder {
fn create_path( fn fill_path(
&mut self, &mut self,
path: impl IntoIterator<Item = lyon::path::PathEvent>, path: impl IntoIterator<Item = lyon::path::PathEvent>,
style: FillStyle, style: FillStyle,
) -> GLRenderingPrimitives { ) -> Option<GLRenderingPrimitive> {
let mut geometry: VertexBuffers<Vertex, u16> = VertexBuffers::new(); let mut geometry: VertexBuffers<Vertex, u16> = VertexBuffers::new();
let fill_opts = FillOptions::default(); let fill_opts = FillOptions::default();
@ -368,13 +406,53 @@ impl GLRenderingPrimitivesBuilder {
.unwrap(); .unwrap();
if geometry.vertices.len() == 0 || geometry.indices.len() == 0 { if geometry.vertices.len() == 0 || geometry.indices.len() == 0 {
return SmallVec::new(); return None;
} }
let vertices = GLArrayBuffer::new(&self.context, &geometry.vertices); let vertices = GLArrayBuffer::new(&self.context, &geometry.vertices);
let indices = GLIndexBuffer::new(&self.context, &geometry.indices); let indices = GLIndexBuffer::new(&self.context, &geometry.indices);
smallvec![GLRenderingPrimitive::FillPath { vertices, indices, style }.into()] Some(GLRenderingPrimitive::FillPath { vertices, indices, style }.into())
}
fn stroke_path(
&mut self,
path: impl IntoIterator<Item = lyon::path::PathEvent>,
stroke_color: Color,
stroke_width: f32,
) -> Option<GLRenderingPrimitive> {
let mut geometry: VertexBuffers<Vertex, u16> = VertexBuffers::new();
let stroke_opts = StrokeOptions::DEFAULT.with_line_width(stroke_width);
self.stroke_tesselator
.tessellate(
path,
&stroke_opts,
&mut BuffersBuilder::new(
&mut geometry,
|pos: lyon::math::Point, _: StrokeAttributes| Vertex {
_pos: [pos.x as f32, pos.y as f32],
},
),
)
.unwrap();
if geometry.vertices.len() == 0 || geometry.indices.len() == 0 {
return None;
}
let vertices = GLArrayBuffer::new(&self.context, &geometry.vertices);
let indices = GLIndexBuffer::new(&self.context, &geometry.indices);
Some(
GLRenderingPrimitive::FillPath {
vertices,
indices,
style: FillStyle::SolidColor(stroke_color),
}
.into(),
)
} }
fn create_image( fn create_image(

View file

@ -106,6 +106,9 @@ Hello := Rectangle {
x: 100; x: 100;
y: 300; y: 300;
fill_color: green; fill_color: green;
stroke_color: black;
stroke_width: 2.0;
LineTo { LineTo {
x: 100; x: 100;
y: 50; y: 50;