mirror of
https://github.com/penpot/penpot.git
synced 2025-08-07 14:38:33 +02:00
🔧 Refactor ParagraphBuilder and fix auto height
This commit is contained in:
parent
4c21468850
commit
b40b1fa2e4
9 changed files with 161 additions and 166 deletions
|
@ -460,37 +460,44 @@ impl RenderState {
|
|||
});
|
||||
|
||||
let text_content = text_content.new_bounds(shape.selrect());
|
||||
let paragraphs = text_content.get_skia_paragraphs(self.fonts.font_collection());
|
||||
let mut paragraphs = text_content.get_skia_paragraphs();
|
||||
|
||||
shadows::render_text_drop_shadows(self, &shape, ¶graphs, antialias);
|
||||
text::render(self, &shape, ¶graphs, None);
|
||||
shadows::render_text_drop_shadows(self, &shape, &mut paragraphs, antialias);
|
||||
text::render(self, &shape, &mut paragraphs, None);
|
||||
|
||||
if shape.has_inner_strokes() {
|
||||
// Inner strokes paints need the text fill to apply correctly their blend modes
|
||||
// (e.g., SrcATop, DstOver)
|
||||
text::render(self, &shape, ¶graphs, Some(SurfaceId::Strokes));
|
||||
text::render(self, &shape, &mut paragraphs, Some(SurfaceId::Strokes));
|
||||
}
|
||||
|
||||
for stroke in shape.strokes().rev() {
|
||||
let stroke_paragraphs = text_content.get_skia_stroke_paragraphs(
|
||||
stroke,
|
||||
&shape.selrect(),
|
||||
self.fonts.font_collection(),
|
||||
let mut stroke_paragraphs =
|
||||
text_content.get_skia_stroke_paragraphs(stroke, &shape.selrect());
|
||||
shadows::render_text_drop_shadows(
|
||||
self,
|
||||
&shape,
|
||||
&mut stroke_paragraphs,
|
||||
antialias,
|
||||
);
|
||||
shadows::render_text_drop_shadows(self, &shape, &stroke_paragraphs, antialias);
|
||||
strokes::render(
|
||||
self,
|
||||
&shape,
|
||||
stroke,
|
||||
None,
|
||||
None,
|
||||
Some(&stroke_paragraphs),
|
||||
Some(&mut stroke_paragraphs),
|
||||
antialias,
|
||||
);
|
||||
shadows::render_text_inner_shadows(
|
||||
self,
|
||||
&shape,
|
||||
&mut stroke_paragraphs,
|
||||
antialias,
|
||||
);
|
||||
shadows::render_text_inner_shadows(self, &shape, &stroke_paragraphs, antialias);
|
||||
}
|
||||
|
||||
shadows::render_text_inner_shadows(self, &shape, ¶graphs, antialias);
|
||||
shadows::render_text_inner_shadows(self, &shape, &mut paragraphs, antialias);
|
||||
}
|
||||
_ => {
|
||||
let surface_ids = SurfaceId::Strokes as u32
|
||||
|
|
|
@ -2,7 +2,8 @@ use super::{RenderState, SurfaceId};
|
|||
use crate::render::strokes;
|
||||
use crate::render::text::{self};
|
||||
use crate::shapes::{Shadow, Shape, Stroke, Type};
|
||||
use skia_safe::{canvas::SaveLayerRec, textlayout::Paragraph, Paint, Path};
|
||||
use skia_safe::textlayout::ParagraphBuilder;
|
||||
use skia_safe::{canvas::SaveLayerRec, Paint, Path};
|
||||
|
||||
// Fill Shadows
|
||||
pub fn render_fill_drop_shadows(render_state: &mut RenderState, shape: &Shape, antialias: bool) {
|
||||
|
@ -88,7 +89,7 @@ pub fn render_stroke_inner_shadows(
|
|||
pub fn render_text_drop_shadows(
|
||||
render_state: &mut RenderState,
|
||||
shape: &Shape,
|
||||
paragraphs: &[Vec<Paragraph>],
|
||||
paragraphs: &mut [ParagraphBuilder],
|
||||
antialias: bool,
|
||||
) {
|
||||
for shadow in shape.drop_shadows().rev().filter(|s| !s.hidden()) {
|
||||
|
@ -123,7 +124,7 @@ pub fn render_text_drop_shadow(
|
|||
render_state: &mut RenderState,
|
||||
shape: &Shape,
|
||||
shadow: &Shadow,
|
||||
paragraphs: &[Vec<Paragraph>],
|
||||
paragraphs: &mut [ParagraphBuilder],
|
||||
antialias: bool,
|
||||
) {
|
||||
let paint = shadow.get_drop_shadow_paint(antialias);
|
||||
|
@ -145,7 +146,7 @@ pub fn render_text_drop_shadow(
|
|||
pub fn render_text_inner_shadows(
|
||||
render_state: &mut RenderState,
|
||||
shape: &Shape,
|
||||
paragraphs: &[Vec<Paragraph>],
|
||||
paragraphs: &mut [ParagraphBuilder],
|
||||
antialias: bool,
|
||||
) {
|
||||
for shadow in shape.inner_shadows().rev().filter(|s| !s.hidden()) {
|
||||
|
@ -157,7 +158,7 @@ pub fn render_text_inner_shadow(
|
|||
render_state: &mut RenderState,
|
||||
shape: &Shape,
|
||||
shadow: &Shadow,
|
||||
paragraphs: &[Vec<Paragraph>],
|
||||
paragraphs: &mut [ParagraphBuilder],
|
||||
antialias: bool,
|
||||
) {
|
||||
let paint = shadow.get_inner_shadow_paint(antialias);
|
||||
|
|
|
@ -3,7 +3,7 @@ use std::collections::HashMap;
|
|||
use crate::math::{Matrix, Point, Rect};
|
||||
|
||||
use crate::shapes::{Corners, Fill, ImageFill, Path, Shape, Stroke, StrokeCap, StrokeKind, Type};
|
||||
use skia_safe::{self as skia, textlayout::Paragraph, ImageFilter, RRect};
|
||||
use skia_safe::{self as skia, textlayout::ParagraphBuilder, ImageFilter, RRect};
|
||||
|
||||
use super::{RenderState, SurfaceId};
|
||||
use crate::render::text::{self};
|
||||
|
@ -485,7 +485,7 @@ pub fn render(
|
|||
stroke: &Stroke,
|
||||
surface_id: Option<SurfaceId>,
|
||||
shadow: Option<&ImageFilter>,
|
||||
paragraphs: Option<&[Vec<Paragraph>]>,
|
||||
paragraphs: Option<&mut [ParagraphBuilder]>,
|
||||
antialias: bool,
|
||||
) {
|
||||
let scale = render_state.get_scale();
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
use super::{RenderState, Shape, SurfaceId};
|
||||
use crate::shapes::VerticalAlign;
|
||||
use skia_safe::{textlayout::Paragraph, FontMetrics, Paint, Path};
|
||||
use skia_safe::{textlayout::ParagraphBuilder, FontMetrics, Paint, Path};
|
||||
|
||||
pub fn render(
|
||||
render_state: &mut RenderState,
|
||||
shape: &Shape,
|
||||
paragraphs: &[Vec<Paragraph>],
|
||||
paragraphs: &mut [ParagraphBuilder],
|
||||
surface_id: Option<SurfaceId>,
|
||||
) {
|
||||
let canvas = render_state
|
||||
|
@ -14,25 +14,19 @@ pub fn render(
|
|||
|
||||
let container_height = shape.selrect().height();
|
||||
|
||||
for group in paragraphs {
|
||||
let total_paragraphs_height: f32 = group.iter().map(|p| p.height()).sum();
|
||||
for builder in paragraphs {
|
||||
let mut skia_paragraph = builder.build();
|
||||
skia_paragraph.layout(shape.bounds().width());
|
||||
let paragraph_height: f32 = skia_paragraph.height();
|
||||
|
||||
let mut offset_y = match shape.vertical_align() {
|
||||
VerticalAlign::Center => (container_height - total_paragraphs_height) / 2.0,
|
||||
VerticalAlign::Bottom => container_height - total_paragraphs_height,
|
||||
let offset_y = match shape.vertical_align() {
|
||||
VerticalAlign::Center => (container_height - paragraph_height) / 2.0,
|
||||
VerticalAlign::Bottom => container_height - paragraph_height,
|
||||
_ => 0.0,
|
||||
};
|
||||
|
||||
let mut offset_lines_y = offset_y;
|
||||
|
||||
for skia_paragraph in group {
|
||||
let xy = (shape.selrect().x(), shape.selrect().y() + offset_y);
|
||||
skia_paragraph.paint(canvas, xy);
|
||||
offset_y += skia_paragraph.height();
|
||||
}
|
||||
|
||||
for skia_paragraph in group {
|
||||
let xy = (shape.selrect().x(), shape.selrect().y() + offset_lines_y);
|
||||
|
||||
for line_metrics in skia_paragraph.get_line_metrics().iter() {
|
||||
let style_metrics: Vec<_> = line_metrics
|
||||
|
@ -63,8 +57,7 @@ pub fn render(
|
|||
{
|
||||
let decoration_type = text_style.decoration().ty;
|
||||
let text_left = xy.0 + current_x_offset;
|
||||
let text_top =
|
||||
xy.1 + line_metrics.baseline as f32 - line_metrics.ascent as f32;
|
||||
let text_top = xy.1 + line_metrics.baseline as f32 - line_metrics.ascent as f32;
|
||||
let text_width = segment_width;
|
||||
let line_height = line_metrics.height as f32;
|
||||
|
||||
|
@ -76,8 +69,9 @@ pub fn render(
|
|||
text_width,
|
||||
line_height,
|
||||
);
|
||||
|
||||
if let Some(decoration_rect) = r {
|
||||
let decoration_paint = text_style.foreground().clone();
|
||||
let decoration_paint = text_style.foreground();
|
||||
canvas.draw_rect(decoration_rect, &decoration_paint);
|
||||
}
|
||||
}
|
||||
|
@ -85,8 +79,6 @@ pub fn render(
|
|||
current_x_offset += segment_width;
|
||||
}
|
||||
}
|
||||
offset_lines_y += skia_paragraph.height();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -164,8 +164,6 @@ fn propagate_transform(
|
|||
};
|
||||
|
||||
let shapes = &state.shapes;
|
||||
let font_col = state.render_state.fonts.font_collection();
|
||||
|
||||
let shape_bounds_before = bounds.find(shape);
|
||||
let mut shape_bounds_after = shape_bounds_before.transform(&entry.transform);
|
||||
|
||||
|
@ -173,9 +171,9 @@ fn propagate_transform(
|
|||
|
||||
if let Type::Text(content) = &shape.shape_type {
|
||||
if content.grow_type() == GrowType::AutoHeight {
|
||||
let mut paragraphs = content.get_skia_paragraphs(font_col);
|
||||
let mut paragraphs = content.get_skia_paragraphs();
|
||||
set_paragraphs_width(shape_bounds_after.width(), &mut paragraphs);
|
||||
let height = auto_height(¶graphs);
|
||||
let height = auto_height(&mut paragraphs, shape_bounds_after.width());
|
||||
let resize_transform = math::resize_matrix(
|
||||
&shape_bounds_after,
|
||||
&shape_bounds_after,
|
||||
|
|
|
@ -5,13 +5,13 @@ use crate::{
|
|||
use skia_safe::{
|
||||
self as skia,
|
||||
paint::Paint,
|
||||
textlayout::{FontCollection, ParagraphBuilder, ParagraphStyle},
|
||||
textlayout::{ParagraphBuilder, ParagraphStyle},
|
||||
};
|
||||
use std::collections::HashSet;
|
||||
|
||||
use super::FontFamily;
|
||||
use crate::shapes::{self, merge_fills, set_paint_fill, Stroke, StrokeKind};
|
||||
use crate::utils::{get_fallback_fonts, uuid_from_u32};
|
||||
use crate::utils::{get_fallback_fonts, get_font_collection, uuid_from_u32};
|
||||
use crate::wasm::fills::parse_fills_from_bytes;
|
||||
use crate::Uuid;
|
||||
|
||||
|
@ -40,16 +40,15 @@ pub struct TextContent {
|
|||
pub grow_type: GrowType,
|
||||
}
|
||||
|
||||
pub fn set_paragraphs_width(width: f32, paragraphs: &mut Vec<Vec<skia::textlayout::Paragraph>>) {
|
||||
for group in paragraphs {
|
||||
for paragraph in group {
|
||||
pub fn set_paragraphs_width(width: f32, paragraphs: &mut [ParagraphBuilder]) {
|
||||
for p in paragraphs {
|
||||
// We first set max so we can get the min_intrinsic_width (this is the min word size)
|
||||
// then after we set either the real with or the min.
|
||||
// This is done this way so the words are not break into lines.
|
||||
let mut paragraph = p.build();
|
||||
paragraph.layout(f32::MAX);
|
||||
paragraph.layout(f32::max(width, paragraph.min_intrinsic_width().ceil()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TextContent {
|
||||
|
@ -94,42 +93,37 @@ impl TextContent {
|
|||
self.paragraphs.push(paragraph);
|
||||
}
|
||||
|
||||
pub fn to_paragraphs(&self, fonts: &FontCollection) -> Vec<Vec<skia::textlayout::Paragraph>> {
|
||||
pub fn to_paragraphs(&self) -> Vec<ParagraphBuilder> {
|
||||
let fonts = get_font_collection();
|
||||
let fallback_fonts = get_fallback_fonts();
|
||||
let mut paragraph_group = Vec::new();
|
||||
let paragraphs = self
|
||||
.paragraphs
|
||||
self.paragraphs
|
||||
.iter()
|
||||
.map(|p| {
|
||||
let paragraph_style = p.paragraph_to_style();
|
||||
let mut builder = ParagraphBuilder::new(¶graph_style, fonts);
|
||||
for leaf in &p.children {
|
||||
let text_style = leaf.to_style(p, &self.bounds, fallback_fonts); // FIXME
|
||||
let text_style = leaf.to_style(p, &self.bounds, fallback_fonts);
|
||||
let text = leaf.apply_text_transform();
|
||||
builder.push_style(&text_style);
|
||||
builder.add_text(&text);
|
||||
builder.pop();
|
||||
}
|
||||
builder.build()
|
||||
builder
|
||||
})
|
||||
.collect();
|
||||
paragraph_group.push(paragraphs);
|
||||
paragraph_group
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn to_stroke_paragraphs(
|
||||
&self,
|
||||
stroke: &Stroke,
|
||||
bounds: &Rect,
|
||||
fonts: &FontCollection,
|
||||
) -> Vec<Vec<skia::textlayout::Paragraph>> {
|
||||
pub fn to_stroke_paragraphs(&self, stroke: &Stroke, bounds: &Rect) -> Vec<ParagraphBuilder> {
|
||||
let fallback_fonts = get_fallback_fonts();
|
||||
let mut paragraph_group = Vec::new();
|
||||
let stroke_paints = get_text_stroke_paints(stroke, bounds);
|
||||
let fonts = get_font_collection();
|
||||
|
||||
for stroke_paint in stroke_paints {
|
||||
let mut stroke_paragraphs = Vec::new();
|
||||
for paragraph in &self.paragraphs {
|
||||
stroke_paints
|
||||
.into_iter()
|
||||
.flat_map(|stroke_paint| {
|
||||
self.paragraphs
|
||||
.iter()
|
||||
.map(|paragraph| {
|
||||
let paragraph_style = paragraph.paragraph_to_style();
|
||||
let mut builder = ParagraphBuilder::new(¶graph_style, fonts);
|
||||
for leaf in ¶graph.children {
|
||||
|
@ -140,21 +134,20 @@ impl TextContent {
|
|||
builder.add_text(&text);
|
||||
builder.pop();
|
||||
}
|
||||
let p = builder.build();
|
||||
stroke_paragraphs.push(p);
|
||||
}
|
||||
paragraph_group.push(stroke_paragraphs);
|
||||
}
|
||||
paragraph_group
|
||||
builder
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn collect_paragraphs(
|
||||
&self,
|
||||
mut paragraphs: Vec<Vec<skia::textlayout::Paragraph>>,
|
||||
) -> Vec<Vec<skia::textlayout::Paragraph>> {
|
||||
mut paragraphs: Vec<ParagraphBuilder>,
|
||||
) -> Vec<ParagraphBuilder> {
|
||||
if self.grow_type() == GrowType::AutoWidth {
|
||||
set_paragraphs_width(f32::MAX, &mut paragraphs);
|
||||
let max_width = auto_width(¶graphs).ceil();
|
||||
let max_width = auto_width(&mut paragraphs).ceil();
|
||||
set_paragraphs_width(max_width, &mut paragraphs);
|
||||
} else {
|
||||
set_paragraphs_width(self.width(), &mut paragraphs);
|
||||
|
@ -162,20 +155,16 @@ impl TextContent {
|
|||
paragraphs
|
||||
}
|
||||
|
||||
pub fn get_skia_paragraphs(
|
||||
&self,
|
||||
fonts: &FontCollection,
|
||||
) -> Vec<Vec<skia::textlayout::Paragraph>> {
|
||||
self.collect_paragraphs(self.to_paragraphs(fonts))
|
||||
pub fn get_skia_paragraphs(&self) -> Vec<ParagraphBuilder> {
|
||||
self.collect_paragraphs(self.to_paragraphs())
|
||||
}
|
||||
|
||||
pub fn get_skia_stroke_paragraphs(
|
||||
&self,
|
||||
stroke: &Stroke,
|
||||
bounds: &Rect,
|
||||
fonts: &FontCollection,
|
||||
) -> Vec<Vec<skia::textlayout::Paragraph>> {
|
||||
self.collect_paragraphs(self.to_stroke_paragraphs(stroke, bounds, fonts))
|
||||
) -> Vec<ParagraphBuilder> {
|
||||
self.collect_paragraphs(self.to_stroke_paragraphs(stroke, bounds))
|
||||
}
|
||||
|
||||
pub fn grow_type(&self) -> GrowType {
|
||||
|
@ -627,24 +616,28 @@ impl From<&Vec<u8>> for RawTextData {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn auto_width(paragraphs: &[Vec<skia::textlayout::Paragraph>]) -> f32 {
|
||||
paragraphs.iter().flatten().fold(0.0, |auto_width, p| {
|
||||
f32::max(p.max_intrinsic_width(), auto_width)
|
||||
pub fn auto_width(paragraphs: &mut [ParagraphBuilder]) -> f32 {
|
||||
paragraphs.iter_mut().fold(0.0, |auto_width, p| {
|
||||
let mut paragraph = p.build();
|
||||
paragraph.layout(f32::MAX);
|
||||
f32::max(paragraph.max_intrinsic_width(), auto_width)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn max_width(paragraphs: &[Vec<skia::textlayout::Paragraph>]) -> f32 {
|
||||
paragraphs
|
||||
.iter()
|
||||
.flatten()
|
||||
.fold(0.0, |max_width, p| f32::max(p.max_width(), max_width))
|
||||
pub fn max_width(paragraphs: &mut [ParagraphBuilder]) -> f32 {
|
||||
paragraphs.iter_mut().fold(0.0, |max_width, p| {
|
||||
let mut paragraph = p.build();
|
||||
paragraph.layout(f32::MAX);
|
||||
f32::max(paragraph.max_width(), max_width)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn auto_height(paragraphs: &[Vec<skia::textlayout::Paragraph>]) -> f32 {
|
||||
paragraphs
|
||||
.iter()
|
||||
.flatten()
|
||||
.fold(0.0, |auto_height, p| auto_height + p.height())
|
||||
pub fn auto_height(paragraphs: &mut [ParagraphBuilder], width: f32) -> f32 {
|
||||
paragraphs.iter_mut().fold(0.0, |auto_height, p| {
|
||||
let mut paragraph = p.build();
|
||||
paragraph.layout(width);
|
||||
auto_height + paragraph.height()
|
||||
})
|
||||
}
|
||||
|
||||
fn get_text_stroke_paints(stroke: &Stroke, bounds: &Rect) -> Vec<Paint> {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use skia_safe::{self as skia, Path, Point};
|
||||
use skia_safe::{self as skia, textlayout::FontCollection, Path, Point};
|
||||
use std::collections::HashMap;
|
||||
|
||||
mod shapes_pool;
|
||||
|
@ -167,6 +167,10 @@ impl State {
|
|||
.rebuild_modifier_tiles(&self.shapes, &self.modifiers);
|
||||
}
|
||||
|
||||
pub fn font_collection(&self) -> &FontCollection {
|
||||
self.render_state.fonts().font_collection()
|
||||
}
|
||||
|
||||
pub fn get_grid_coords(&self, pos_x: f32, pos_y: f32) -> Option<(i32, i32)> {
|
||||
let shape = self.current_shape()?;
|
||||
let bounds = shape.bounds();
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
use crate::skia::textlayout::FontCollection;
|
||||
use crate::skia::Image;
|
||||
use crate::uuid::Uuid;
|
||||
use crate::with_state_mut;
|
||||
|
@ -31,3 +32,7 @@ pub fn get_image(image_id: &Uuid) -> Option<&Image> {
|
|||
pub fn get_fallback_fonts() -> &'static HashSet<String> {
|
||||
with_state_mut!(state, { state.render_state().fonts().get_fallback() })
|
||||
}
|
||||
|
||||
pub fn get_font_collection() -> &'static FontCollection {
|
||||
with_state_mut!(state, { state.font_collection() })
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@ use crate::mem;
|
|||
use crate::shapes::{auto_height, auto_width, max_width, GrowType, RawTextData, Type};
|
||||
|
||||
use crate::STATE;
|
||||
use crate::{with_current_shape, with_current_shape_mut, with_state_mut};
|
||||
use crate::{with_current_shape, with_current_shape_mut};
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn clear_shape_text() {
|
||||
|
@ -35,11 +35,6 @@ pub extern "C" fn set_shape_grow_type(grow_type: u8) {
|
|||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn get_text_dimensions() -> *mut u8 {
|
||||
let font_col;
|
||||
with_state_mut!(state, {
|
||||
font_col = state.render_state.fonts.font_collection();
|
||||
});
|
||||
|
||||
let mut width = 0.01;
|
||||
let mut height = 0.01;
|
||||
let mut m_width = 0.01;
|
||||
|
@ -49,16 +44,16 @@ pub extern "C" fn get_text_dimensions() -> *mut u8 {
|
|||
height = shape.selrect.height();
|
||||
|
||||
if let Type::Text(content) = &shape.shape_type {
|
||||
let paragraphs = content.get_skia_paragraphs(font_col);
|
||||
m_width = max_width(¶graphs);
|
||||
let mut paragraphs = content.get_skia_paragraphs();
|
||||
m_width = max_width(&mut paragraphs);
|
||||
|
||||
match content.grow_type() {
|
||||
GrowType::AutoHeight => {
|
||||
height = auto_height(¶graphs).ceil();
|
||||
height = auto_height(&mut paragraphs, width).ceil();
|
||||
}
|
||||
GrowType::AutoWidth => {
|
||||
width = auto_width(¶graphs).ceil();
|
||||
height = auto_height(¶graphs).ceil();
|
||||
width = auto_width(&mut paragraphs).ceil();
|
||||
height = auto_height(&mut paragraphs, width).ceil();
|
||||
}
|
||||
GrowType::Fixed => {}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue