From d92c8cab23e0bbcd287b6cece5d41ca5d37f620f Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 4 Feb 2021 09:06:33 +0100 Subject: [PATCH] Fix path fitting Include the stroke width in the boundaries when fitting a path into the size of a `Path` element. --- sixtyfps_runtime/corelib/items.rs | 17 +++++++++++++++++ sixtyfps_runtime/rendering_backends/gl/lib.rs | 7 +++++-- .../rendering_backends/qt/qt_window.rs | 8 ++++++-- 3 files changed, 28 insertions(+), 4 deletions(-) diff --git a/sixtyfps_runtime/corelib/items.rs b/sixtyfps_runtime/corelib/items.rs index 816ce05f6..bbb51081e 100644 --- a/sixtyfps_runtime/corelib/items.rs +++ b/sixtyfps_runtime/corelib/items.rs @@ -25,6 +25,7 @@ When adding an item or a property, it needs to be kept in sync with different pl #![allow(missing_docs)] // because documenting each property of items is redundent use crate::component::ComponentVTable; +use crate::graphics::PathDataIterator; use crate::graphics::{Brush, Color, PathData, Point, Rect, Size}; use crate::input::{ FocusEvent, InputEventResult, KeyEvent, KeyEventResult, KeyEventType, MouseEvent, @@ -600,6 +601,22 @@ impl Item for Path { } } +impl Path { + /// Returns an iterator of the events of the path and an offset, so that the + /// shape fits into the width/height of the path while respecting the stroke + /// width. + pub fn fitted_path_events( + self: Pin<&Self>, + ) -> (euclid::default::Vector2D, PathDataIterator) { + let stroke_width = self.stroke_width(); + let bounds_width = (self.width() - stroke_width).max(0.); + let bounds_height = (self.height() - stroke_width).max(0.); + let offset = euclid::default::Vector2D::new(stroke_width / 2., stroke_width / 2.); + let event_iterator = self.elements().iter_fitted(bounds_width, bounds_height); + (offset, event_iterator) + } +} + impl ItemConsts for Path { const cached_rendering_data_offset: const_field_offset::FieldOffset = Path::FIELD_OFFSETS.cached_rendering_data.as_unpinned_projection(); diff --git a/sixtyfps_runtime/rendering_backends/gl/lib.rs b/sixtyfps_runtime/rendering_backends/gl/lib.rs index 19434fdb9..a6b023076 100644 --- a/sixtyfps_runtime/rendering_backends/gl/lib.rs +++ b/sixtyfps_runtime/rendering_backends/gl/lib.rs @@ -954,8 +954,11 @@ impl ItemRenderer for GLItemRenderer { if matches!(elements, sixtyfps_corelib::PathData::None) { return; } + + let (offset, path_events) = path.fitted_path_events(); + let mut fpath = femtovg::Path::new(); - for x in elements.iter_fitted(path.width(), path.height()).iter() { + for x in path_events.iter() { match x { lyon_path::Event::Begin { at } => { fpath.move_to(at.x, at.y); @@ -983,7 +986,7 @@ impl ItemRenderer for GLItemRenderer { border_paint.set_line_width(path.stroke_width()); self.shared_data.canvas.borrow_mut().save_with(|canvas| { - canvas.translate(pos.x + path.x(), pos.y + path.y()); + canvas.translate(pos.x + path.x() + offset.x, pos.y + path.y() + offset.y); canvas.fill_path(&mut fpath, fill_paint); canvas.stroke_path(&mut fpath, border_paint); }) diff --git a/sixtyfps_runtime/rendering_backends/qt/qt_window.rs b/sixtyfps_runtime/rendering_backends/qt/qt_window.rs index b233cb3b2..6dde5399c 100644 --- a/sixtyfps_runtime/rendering_backends/qt/qt_window.rs +++ b/sixtyfps_runtime/rendering_backends/qt/qt_window.rs @@ -372,12 +372,16 @@ impl ItemRenderer for QtItemRenderer<'_> { } // FIXME: handle width/height //let rect: qttypes::QRectF = get_geometry!(pos, items::Path, path); - let pos = qttypes::QPoint { x: (pos.x + path.x()) as _, y: (pos.y + path.y()) as _ }; let fill_brush: qttypes::QBrush = path.fill().into(); let stroke_brush: qttypes::QBrush = path.stroke().into(); let stroke_width: f32 = path.stroke_width(); + let (offset, path_events) = path.fitted_path_events(); + let pos = qttypes::QPoint { + x: (pos.x + path.x() + offset.x) as _, + y: (pos.y + path.y() + offset.y) as _, + }; let mut painter_path = QPainterPath::default(); - for x in elements.iter_fitted(path.width(), path.height()).iter() { + for x in path_events.iter() { impl From for qttypes::QPointF { fn from(p: Point) -> Self { qttypes::QPointF { x: p.x as _, y: p.y as _ }