parley: Simplify stroke and brush interface towards the renderer

The renderer needs to provide a PlatformBrush type that encodes if it's a fill or a stroke. In exchange, the stroke style interpretation can be handled in renderer-independent code.
This commit is contained in:
Simon Hausmann 2025-10-01 10:49:00 +02:00 committed by Simon Hausmann
parent 993358dee4
commit d1ac15ac7e
2 changed files with 101 additions and 98 deletions

View file

@ -10,11 +10,9 @@ use std::cell::RefCell;
use crate::{
graphics::FontRequest,
items::TextStrokeStyle,
lengths::{
LogicalLength, LogicalPoint, LogicalRect, LogicalSize, PhysicalPx, ScaleFactor, SizeLengths,
},
lengths::{LogicalLength, LogicalPoint, LogicalRect, LogicalSize, ScaleFactor, SizeLengths},
textlayout::{TextHorizontalAlignment, TextOverflow, TextVerticalAlignment, TextWrap},
Coord, SharedString,
SharedString,
};
use i_slint_common::sharedfontique;
@ -48,14 +46,12 @@ pub trait GlyphRenderer: crate::item_rendering::ItemRenderer {
&mut self,
font: &parley::Font,
font_size: f32,
fill_brush: Self::PlatformBrush,
stroke_brush: Option<Self::PlatformBrush>,
stroke_style: &Option<TextStrokeStyle>,
brush: Self::PlatformBrush,
y_offset: f32,
glyphs_it: &mut dyn Iterator<Item = parley::layout::Glyph>,
);
/// Fills the given rectangle with the specified brush. This is used for drawing selection
/// Fills the given rectangle with the specified color. This is used for drawing selection
/// rectangles as well as the text cursor.
fn fill_rectangle(
&mut self,
@ -63,7 +59,7 @@ pub trait GlyphRenderer: crate::item_rendering::ItemRenderer {
physical_y: f32,
physical_width: f32,
physical_height: f32,
brush: Self::PlatformBrush,
color: crate::Color,
);
}
@ -296,8 +292,6 @@ impl Layout {
&parley::Font,
f32,
<R as GlyphRenderer>::PlatformBrush,
Option<<R as GlyphRenderer>::PlatformBrush>,
&Option<TextStrokeStyle>,
&mut dyn Iterator<Item = parley::layout::Glyph>,
),
) {
@ -335,15 +329,59 @@ impl Layout {
None => (default_fill_brush.clone(), &brush.stroke),
};
draw_glyphs(
item_renderer,
run.font(),
run.font_size(),
fill_brush,
default_stroke_brush.clone(),
stroke_style,
glyphs_it,
);
match stroke_style {
Some(TextStrokeStyle::Outside) => {
let glyphs = glyphs_it.collect::<alloc::vec::Vec<_>>();
if let Some(stroke_brush) = default_stroke_brush.clone() {
draw_glyphs(
item_renderer,
run.font(),
run.font_size(),
stroke_brush,
&mut glyphs.iter().cloned(),
);
}
draw_glyphs(
item_renderer,
run.font(),
run.font_size(),
fill_brush,
&mut glyphs.into_iter(),
);
}
Some(TextStrokeStyle::Center) => {
let glyphs = glyphs_it.collect::<alloc::vec::Vec<_>>();
draw_glyphs(
item_renderer,
run.font(),
run.font_size(),
fill_brush,
&mut glyphs.iter().cloned(),
);
if let Some(stroke_brush) = default_stroke_brush.clone() {
draw_glyphs(
item_renderer,
run.font(),
run.font_size(),
stroke_brush,
&mut glyphs.into_iter(),
);
}
}
None => {
draw_glyphs(
item_renderer,
run.font(),
run.font_size(),
fill_brush,
glyphs_it,
);
}
}
}
parley::PositionedLayoutItem::InlineBox(_inline_box) => {}
};
@ -409,16 +447,8 @@ pub fn draw_text(
item_renderer,
platform_fill_brush,
platform_stroke_brush,
&mut |item_renderer, font, font_size, fill_brush, stroke_brush, stroke_style, glyphs_it| {
item_renderer.draw_glyph_run(
font,
font_size,
fill_brush,
stroke_brush,
stroke_style,
layout.y_offset,
glyphs_it,
);
&mut |item_renderer, font, font_size, brush, glyphs_it| {
item_renderer.draw_glyph_run(font, font_size, brush, layout.y_offset, glyphs_it);
},
);
}
@ -487,35 +517,21 @@ pub fn draw_text_input(
),
);
selection.geometry_with(&layout.inner, |rect, _| {
if let Some(selection_brush) = item_renderer.platform_text_fill_brush(
text_input.selection_background_color().into(),
euclid::size2::<Coord, PhysicalPx>(rect.width() as _, rect.height() as _)
/ scale_factor,
) {
item_renderer.fill_rectangle(
rect.min_x() as _,
rect.min_y() as f32 + layout.y_offset,
rect.width() as _,
rect.height() as _,
selection_brush,
);
}
item_renderer.fill_rectangle(
rect.min_x() as _,
rect.min_y() as f32 + layout.y_offset,
rect.width() as _,
rect.height() as _,
text_input.selection_background_color(),
);
});
layout.draw(
item_renderer,
platform_fill_brush,
None,
&mut |item_renderer, font, font_size, fill_brush, stroke_brush, stroke_style, glyphs_it| {
item_renderer.draw_glyph_run(
font,
font_size,
fill_brush,
stroke_brush,
stroke_style,
layout.y_offset,
glyphs_it,
);
&mut |item_renderer, font, font_size, brush, glyphs_it| {
item_renderer.draw_glyph_run(font, font_size, brush, layout.y_offset, glyphs_it);
},
);
@ -528,19 +544,13 @@ pub fn draw_text_input(
let rect =
cursor.geometry(&layout.inner, (text_input.text_cursor_width() * scale_factor).get());
if let Some(cursor_brush) = item_renderer.platform_text_fill_brush(
visual_representation.cursor_color.into(),
euclid::size2::<Coord, PhysicalPx>(rect.width() as _, rect.height() as _)
/ scale_factor,
) {
item_renderer.fill_rectangle(
rect.min_x() as _,
rect.min_y() as f32 + layout.y_offset,
rect.width() as _,
rect.height() as _,
cursor_brush,
);
}
item_renderer.fill_rectangle(
rect.min_x() as _,
rect.min_y() as f32 + layout.y_offset,
rect.width() as _,
rect.height() as _,
visual_representation.cursor_color,
);
}
}

View file

@ -17,7 +17,6 @@ use i_slint_core::item_rendering::{
};
use i_slint_core::items::{
self, Clip, FillRule, ImageRendering, ImageTiling, ItemRc, Layer, Opacity, RenderingResult,
TextStrokeStyle,
};
use i_slint_core::lengths::{
LogicalBorderRadius, LogicalLength, LogicalPoint, LogicalRect, LogicalSize, LogicalVector,
@ -909,8 +908,14 @@ impl<'a, R: femtovg::Renderer + TextureImporter> ItemRenderer for GLItemRenderer
}
}
#[derive(Clone)]
pub enum GlyphBrush {
Fill(femtovg::Paint),
Stroke(femtovg::Paint),
}
impl<'a, R: femtovg::Renderer + TextureImporter> GlyphRenderer for GLItemRenderer<'a, R> {
type PlatformBrush = femtovg::Paint;
type PlatformBrush = GlyphBrush;
fn platform_text_fill_brush(
&mut self,
@ -918,7 +923,7 @@ impl<'a, R: femtovg::Renderer + TextureImporter> GlyphRenderer for GLItemRendere
size: LogicalSize,
) -> Option<Self::PlatformBrush> {
let text_path = rect_to_path((size * self.scale_factor).into());
self.brush_to_paint(brush, &text_path)
self.brush_to_paint(brush, &text_path).map(GlyphBrush::Fill)
}
fn platform_brush_for_color(
@ -928,7 +933,7 @@ impl<'a, R: femtovg::Renderer + TextureImporter> GlyphRenderer for GLItemRendere
if color.alpha() == 0 {
None
} else {
Some(femtovg::Paint::color(to_femtovg_color(&color)))
Some(GlyphBrush::Fill(femtovg::Paint::color(to_femtovg_color(&color))))
}
}
@ -945,7 +950,7 @@ impl<'a, R: femtovg::Renderer + TextureImporter> GlyphRenderer for GLItemRendere
None
} else {
paint.set_line_width(physical_stroke_width);
Some(paint)
Some(GlyphBrush::Stroke(paint))
}
}
None => None,
@ -956,9 +961,7 @@ impl<'a, R: femtovg::Renderer + TextureImporter> GlyphRenderer for GLItemRendere
&mut self,
font: &parley::Font,
font_size: f32,
mut fill_brush: Self::PlatformBrush,
mut stroke_brush: Option<Self::PlatformBrush>,
stroke_style: &Option<TextStrokeStyle>,
mut brush: Self::PlatformBrush,
y_offset: f32,
glyphs_it: &mut dyn Iterator<Item = parley::layout::Glyph>,
) {
@ -970,32 +973,16 @@ impl<'a, R: femtovg::Renderer + TextureImporter> GlyphRenderer for GLItemRendere
glyph_id: glyph.id,
});
fill_brush.set_font_size(font_size);
if let Some(stroke_brush) = stroke_brush.as_mut() {
stroke_brush.set_font_size(font_size);
}
let mut canvas = self.canvas.borrow_mut();
match stroke_style {
Some(i_slint_core::items::TextStrokeStyle::Outside) => {
let glyphs = glyphs_it.collect::<Vec<_>>();
if let Some(stroke_brush) = stroke_brush.as_ref() {
canvas.stroke_glyph_run(font_id, glyphs.clone(), &stroke_brush).unwrap();
}
canvas.fill_glyph_run(font_id, glyphs, &fill_brush).unwrap();
match &mut brush {
GlyphBrush::Fill(paint) => {
paint.set_font_size(font_size);
canvas.fill_glyph_run(font_id, glyphs_it, &paint).unwrap();
}
Some(i_slint_core::items::TextStrokeStyle::Center) => {
let glyphs = glyphs_it.collect::<Vec<_>>();
canvas.fill_glyph_run(font_id, glyphs.clone(), &fill_brush).unwrap();
if let Some(stroke_brush) = stroke_brush.as_ref() {
canvas.stroke_glyph_run(font_id, glyphs.clone(), &stroke_brush).unwrap();
}
}
None => {
canvas.fill_glyph_run(font_id, glyphs_it, &fill_brush).unwrap();
GlyphBrush::Stroke(paint) => {
paint.set_font_size(font_size);
canvas.stroke_glyph_run(font_id, glyphs_it, &paint).unwrap();
}
}
}
@ -1006,8 +993,14 @@ impl<'a, R: femtovg::Renderer + TextureImporter> GlyphRenderer for GLItemRendere
physical_y: f32,
physical_width: f32,
physical_height: f32,
paint: Self::PlatformBrush,
color: Color,
) {
if color.alpha() == 0 {
return;
}
let paint = femtovg::Paint::color(to_femtovg_color(&color));
let mut path = femtovg::Path::new();
path.rect(physical_x, physical_y, physical_width, physical_height);