mirror of
https://github.com/penpot/penpot.git
synced 2026-03-21 18:03:47 +00:00
🐛 Fix text shadows and blur and refactor text rendering
This commit is contained in:
@@ -1,9 +1,10 @@
|
||||
use super::{RenderState, SurfaceId};
|
||||
use crate::render::strokes;
|
||||
use crate::render::text::{self};
|
||||
use crate::shapes::{Shadow, Shape, Stroke, Type};
|
||||
use skia_safe::textlayout::ParagraphBuilder;
|
||||
use skia_safe::{Paint, Path};
|
||||
use skia_safe::{canvas::SaveLayerRec, Paint, Path};
|
||||
|
||||
use crate::render::text;
|
||||
use crate::textlayout::ParagraphBuilderGroup;
|
||||
|
||||
// Fill Shadows
|
||||
pub fn render_fill_inner_shadows(
|
||||
@@ -46,7 +47,6 @@ pub fn render_stroke_inner_shadows(
|
||||
stroke,
|
||||
Some(surface_id),
|
||||
filter.as_ref(),
|
||||
None,
|
||||
antialias,
|
||||
)
|
||||
}
|
||||
@@ -76,15 +76,6 @@ pub fn render_text_path_stroke_drop_shadows(
|
||||
}
|
||||
}
|
||||
|
||||
pub fn render_text_inner_shadows(
|
||||
render_state: &mut RenderState,
|
||||
shape: &Shape,
|
||||
paragraphs: &mut [Vec<ParagraphBuilder>],
|
||||
surface_id: SurfaceId,
|
||||
) {
|
||||
text::render(render_state, shape, paragraphs, Some(surface_id));
|
||||
}
|
||||
|
||||
// Render text paths (unused)
|
||||
#[allow(dead_code)]
|
||||
pub fn render_text_path_stroke_inner_shadows(
|
||||
@@ -129,3 +120,50 @@ fn render_shadow_paint(
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn render_text_shadows(
|
||||
render_state: &mut RenderState,
|
||||
shape: &Shape,
|
||||
paragraphs: &mut [ParagraphBuilderGroup],
|
||||
stroke_paragraphs_group: &mut [Vec<ParagraphBuilderGroup>],
|
||||
surface_id: Option<SurfaceId>,
|
||||
shadows: &[Paint],
|
||||
blur_filter: &Option<skia_safe::ImageFilter>,
|
||||
) {
|
||||
if stroke_paragraphs_group.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
let canvas = render_state
|
||||
.surfaces
|
||||
.canvas(surface_id.unwrap_or(SurfaceId::TextDropShadows));
|
||||
|
||||
for shadow in shadows {
|
||||
let shadow_layer = SaveLayerRec::default().paint(shadow);
|
||||
canvas.save_layer(&shadow_layer);
|
||||
|
||||
text::render(
|
||||
None,
|
||||
Some(canvas),
|
||||
shape,
|
||||
paragraphs,
|
||||
surface_id,
|
||||
None,
|
||||
blur_filter.as_ref(),
|
||||
);
|
||||
|
||||
for stroke_paragraphs in stroke_paragraphs_group.iter_mut() {
|
||||
text::render(
|
||||
None,
|
||||
Some(canvas),
|
||||
shape,
|
||||
stroke_paragraphs,
|
||||
surface_id,
|
||||
None,
|
||||
blur_filter.as_ref(),
|
||||
);
|
||||
}
|
||||
|
||||
canvas.restore();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,11 +3,10 @@ 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::ParagraphBuilder, ImageFilter, RRect};
|
||||
use skia_safe::{self as skia, ImageFilter, RRect};
|
||||
|
||||
use super::{RenderState, SurfaceId};
|
||||
use crate::render::filters::compose_filters;
|
||||
use crate::render::text::{self};
|
||||
use crate::render::{get_dest_rect, get_source_rect};
|
||||
|
||||
// FIXME: See if we can simplify these arguments
|
||||
@@ -519,7 +518,6 @@ pub fn render(
|
||||
stroke: &Stroke,
|
||||
surface_id: Option<SurfaceId>,
|
||||
shadow: Option<&ImageFilter>,
|
||||
paragraphs: Option<&mut Vec<Vec<ParagraphBuilder>>>,
|
||||
antialias: bool,
|
||||
) {
|
||||
let scale = render_state.get_scale();
|
||||
@@ -564,14 +562,7 @@ pub fn render(
|
||||
shape.image_filter(1.).as_ref(),
|
||||
antialias,
|
||||
),
|
||||
Type::Text(_) => {
|
||||
text::render(
|
||||
render_state,
|
||||
shape,
|
||||
paragraphs.expect("Text shapes should have paragraphs"),
|
||||
surface_id,
|
||||
);
|
||||
}
|
||||
Type::Text(_) => {}
|
||||
shape_type @ (Type::Path(_) | Type::Bool(_)) => {
|
||||
if let Some(path) = shape_type.path() {
|
||||
draw_stroke_on_path(
|
||||
|
||||
@@ -17,15 +17,16 @@ const TILE_SIZE_MULTIPLIER: i32 = 2;
|
||||
#[repr(u32)]
|
||||
#[derive(Debug, PartialEq, Clone, Copy)]
|
||||
pub enum SurfaceId {
|
||||
Target = 0b0_0000_0001,
|
||||
Cache = 0b0_0000_0010,
|
||||
Current = 0b0_0000_0100,
|
||||
Fills = 0b0_0000_1000,
|
||||
Strokes = 0b0_0001_0000,
|
||||
DropShadows = 0b0_0010_0000,
|
||||
InnerShadows = 0b0_0100_0000,
|
||||
UI = 0b0_1000_0000,
|
||||
Debug = 0b1_0000_0000,
|
||||
Target = 0b00_0000_0001,
|
||||
Cache = 0b00_0000_0010,
|
||||
Current = 0b00_0000_0100,
|
||||
Fills = 0b00_0000_1000,
|
||||
Strokes = 0b00_0001_0000,
|
||||
DropShadows = 0b00_0010_0000,
|
||||
InnerShadows = 0b00_0100_0000,
|
||||
TextDropShadows = 0b00_1000_0000,
|
||||
UI = 0b01_0000_0000,
|
||||
Debug = 0b10_0000_0001,
|
||||
}
|
||||
|
||||
pub struct Surfaces {
|
||||
@@ -42,6 +43,8 @@ pub struct Surfaces {
|
||||
drop_shadows: skia::Surface,
|
||||
// used for rendering over shadows.
|
||||
inner_shadows: skia::Surface,
|
||||
// used for rendering text drop shadows
|
||||
text_drop_shadows: skia::Surface,
|
||||
// used for displaying auxiliary workspace elements
|
||||
ui: skia::Surface,
|
||||
// for drawing debug info.
|
||||
@@ -73,6 +76,8 @@ impl Surfaces {
|
||||
gpu_state.create_surface_with_isize("drop_shadows".to_string(), extra_tile_dims);
|
||||
let inner_shadows =
|
||||
gpu_state.create_surface_with_isize("inner_shadows".to_string(), extra_tile_dims);
|
||||
let text_drop_shadows =
|
||||
gpu_state.create_surface_with_isize("text_drop_shadows".to_string(), extra_tile_dims);
|
||||
let shape_fills =
|
||||
gpu_state.create_surface_with_isize("shape_fills".to_string(), extra_tile_dims);
|
||||
let shape_strokes =
|
||||
@@ -88,6 +93,7 @@ impl Surfaces {
|
||||
current,
|
||||
drop_shadows,
|
||||
inner_shadows,
|
||||
text_drop_shadows,
|
||||
shape_fills,
|
||||
shape_strokes,
|
||||
ui,
|
||||
@@ -166,6 +172,9 @@ impl Surfaces {
|
||||
if ids & SurfaceId::InnerShadows as u32 != 0 {
|
||||
f(self.get_mut(SurfaceId::InnerShadows));
|
||||
}
|
||||
if ids & SurfaceId::TextDropShadows as u32 != 0 {
|
||||
f(self.get_mut(SurfaceId::TextDropShadows));
|
||||
}
|
||||
if ids & SurfaceId::DropShadows as u32 != 0 {
|
||||
f(self.get_mut(SurfaceId::DropShadows));
|
||||
}
|
||||
@@ -189,11 +198,15 @@ impl Surfaces {
|
||||
pub fn update_render_context(&mut self, render_area: skia::Rect, scale: f32) {
|
||||
let translation = self.get_render_context_translation(render_area, scale);
|
||||
self.apply_mut(
|
||||
SurfaceId::Fills as u32 | SurfaceId::Strokes as u32 | SurfaceId::InnerShadows as u32,
|
||||
SurfaceId::Fills as u32
|
||||
| SurfaceId::Strokes as u32
|
||||
| SurfaceId::InnerShadows as u32
|
||||
| SurfaceId::TextDropShadows as u32,
|
||||
|s| {
|
||||
s.canvas().restore();
|
||||
s.canvas().save();
|
||||
s.canvas().translate(translation);
|
||||
let canvas = s.canvas();
|
||||
canvas.reset_matrix();
|
||||
canvas.scale((scale, scale));
|
||||
canvas.translate(translation);
|
||||
},
|
||||
);
|
||||
}
|
||||
@@ -206,6 +219,7 @@ impl Surfaces {
|
||||
SurfaceId::Current => &mut self.current,
|
||||
SurfaceId::DropShadows => &mut self.drop_shadows,
|
||||
SurfaceId::InnerShadows => &mut self.inner_shadows,
|
||||
SurfaceId::TextDropShadows => &mut self.text_drop_shadows,
|
||||
SurfaceId::Fills => &mut self.shape_fills,
|
||||
SurfaceId::Strokes => &mut self.shape_strokes,
|
||||
SurfaceId::Debug => &mut self.debug,
|
||||
@@ -257,13 +271,15 @@ impl Surfaces {
|
||||
pub fn reset(&mut self, color: skia::Color) {
|
||||
self.canvas(SurfaceId::Fills).restore_to_count(1);
|
||||
self.canvas(SurfaceId::InnerShadows).restore_to_count(1);
|
||||
self.canvas(SurfaceId::TextDropShadows).restore_to_count(1);
|
||||
self.canvas(SurfaceId::Strokes).restore_to_count(1);
|
||||
self.canvas(SurfaceId::Current).restore_to_count(1);
|
||||
self.apply_mut(
|
||||
SurfaceId::Fills as u32
|
||||
| SurfaceId::Strokes as u32
|
||||
| SurfaceId::Current as u32
|
||||
| SurfaceId::InnerShadows as u32,
|
||||
| SurfaceId::InnerShadows as u32
|
||||
| SurfaceId::TextDropShadows as u32,
|
||||
|s| {
|
||||
s.canvas().clear(color).reset_matrix();
|
||||
},
|
||||
|
||||
@@ -1,22 +1,55 @@
|
||||
use super::{RenderState, Shape, SurfaceId};
|
||||
use crate::shapes::VerticalAlign;
|
||||
use skia_safe::{
|
||||
canvas::SaveLayerRec, textlayout::LineMetrics, textlayout::Paragraph,
|
||||
textlayout::ParagraphBuilder, textlayout::RectHeightStyle, textlayout::RectWidthStyle,
|
||||
textlayout::StyleMetrics, textlayout::TextDecoration, textlayout::TextStyle, Canvas, Paint,
|
||||
Path,
|
||||
canvas::SaveLayerRec,
|
||||
textlayout::{
|
||||
LineMetrics, Paragraph, ParagraphBuilder, RectHeightStyle, RectWidthStyle, StyleMetrics,
|
||||
TextDecoration, TextStyle,
|
||||
},
|
||||
Canvas, ImageFilter, Paint, Path,
|
||||
};
|
||||
|
||||
pub fn render(
|
||||
render_state: &mut RenderState,
|
||||
render_state: Option<&mut RenderState>,
|
||||
canvas: Option<&Canvas>,
|
||||
shape: &Shape,
|
||||
paragraphs: &mut [Vec<ParagraphBuilder>],
|
||||
surface_id: Option<SurfaceId>,
|
||||
shadow: Option<&Paint>,
|
||||
blur: Option<&ImageFilter>,
|
||||
) {
|
||||
let canvas = render_state
|
||||
.surfaces
|
||||
.canvas(surface_id.unwrap_or(SurfaceId::Fills));
|
||||
let render_canvas = if let Some(rs) = render_state {
|
||||
rs.surfaces.canvas(surface_id.unwrap_or(SurfaceId::Fills))
|
||||
} else if let Some(c) = canvas {
|
||||
c
|
||||
} else {
|
||||
return;
|
||||
};
|
||||
|
||||
if let Some(blur_filter) = blur {
|
||||
let mut blur_paint = Paint::default();
|
||||
blur_paint.set_image_filter(blur_filter.clone());
|
||||
let blur_layer = SaveLayerRec::default().paint(&blur_paint);
|
||||
render_canvas.save_layer(&blur_layer);
|
||||
}
|
||||
|
||||
if let Some(shadow_paint) = shadow {
|
||||
let layer_rec = SaveLayerRec::default().paint(shadow_paint);
|
||||
render_canvas.save_layer(&layer_rec);
|
||||
draw_text(render_canvas, shape, paragraphs);
|
||||
render_canvas.restore();
|
||||
} else {
|
||||
draw_text(render_canvas, shape, paragraphs);
|
||||
}
|
||||
|
||||
if blur.is_some() {
|
||||
render_canvas.restore();
|
||||
}
|
||||
|
||||
render_canvas.restore();
|
||||
}
|
||||
|
||||
fn draw_text(canvas: &Canvas, shape: &Shape, paragraphs: &mut [Vec<ParagraphBuilder>]) {
|
||||
// Width
|
||||
let paragraph_width = if let crate::shapes::Type::Text(text_content) = &shape.shape_type {
|
||||
text_content.width()
|
||||
@@ -63,8 +96,6 @@ pub fn render(
|
||||
global_offset_y = group_offset_y;
|
||||
}
|
||||
}
|
||||
|
||||
canvas.restore();
|
||||
}
|
||||
|
||||
fn draw_text_decorations(
|
||||
@@ -286,11 +317,11 @@ pub fn render_as_path(
|
||||
// let text_content = text_content.new_bounds(shape.selrect());
|
||||
// let paths = text_content.get_paths(antialias);
|
||||
|
||||
// shadows::render_text_drop_shadows(self, &shape, &paths, antialias);
|
||||
// shadows::render_text_shadows(self, &shape, &paths, antialias);
|
||||
// text::render(self, &paths, None, None);
|
||||
|
||||
// for stroke in shape.visible_strokes().rev() {
|
||||
// shadows::render_text_path_stroke_drop_shadows(
|
||||
// shadows::render_text_path_stroke_shadows(
|
||||
// self, &shape, &paths, stroke, antialias,
|
||||
// );
|
||||
// strokes::render_text_paths(self, &shape, stroke, &paths, None, None, antialias);
|
||||
|
||||
Reference in New Issue
Block a user