mirror of
https://github.com/penpot/penpot.git
synced 2026-09-11 22:49:30 +00:00
🐛 Fix stroke artifacts
This commit is contained in:
parent
c58054d19c
commit
bdcf448f3f
@ -642,6 +642,10 @@ impl RenderState {
|
|||||||
apply_to_current_surface: bool,
|
apply_to_current_surface: bool,
|
||||||
offset: Option<(f32, f32)>,
|
offset: Option<(f32, f32)>,
|
||||||
parent_shadows: Option<Vec<skia_safe::Paint>>,
|
parent_shadows: Option<Vec<skia_safe::Paint>>,
|
||||||
|
<<<<<<< Updated upstream
|
||||||
|
=======
|
||||||
|
outset: Option<f32>,
|
||||||
|
>>>>>>> Stashed changes
|
||||||
) {
|
) {
|
||||||
let surface_ids = fills_surface_id as u32
|
let surface_ids = fills_surface_id as u32
|
||||||
| strokes_surface_id as u32
|
| strokes_surface_id as u32
|
||||||
@ -710,6 +714,10 @@ impl RenderState {
|
|||||||
&visible_strokes,
|
&visible_strokes,
|
||||||
Some(SurfaceId::Current),
|
Some(SurfaceId::Current),
|
||||||
antialias,
|
antialias,
|
||||||
|
<<<<<<< Updated upstream
|
||||||
|
=======
|
||||||
|
outset,
|
||||||
|
>>>>>>> Stashed changes
|
||||||
);
|
);
|
||||||
|
|
||||||
self.surfaces.apply_mut(SurfaceId::Current as u32, |s| {
|
self.surfaces.apply_mut(SurfaceId::Current as u32, |s| {
|
||||||
@ -1055,10 +1063,31 @@ impl RenderState {
|
|||||||
{
|
{
|
||||||
if let Some(fills_to_render) = self.nested_fills.last() {
|
if let Some(fills_to_render) = self.nested_fills.last() {
|
||||||
let fills_to_render = fills_to_render.clone();
|
let fills_to_render = fills_to_render.clone();
|
||||||
|
<<<<<<< Updated upstream
|
||||||
fills::render(self, shape, &fills_to_render, antialias, fills_surface_id);
|
fills::render(self, shape, &fills_to_render, antialias, fills_surface_id);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
fills::render(self, shape, &shape.fills, antialias, fills_surface_id);
|
fills::render(self, shape, &shape.fills, antialias, fills_surface_id);
|
||||||
|
=======
|
||||||
|
fills::render(
|
||||||
|
self,
|
||||||
|
shape,
|
||||||
|
&fills_to_render,
|
||||||
|
antialias,
|
||||||
|
fills_surface_id,
|
||||||
|
outset,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fills::render(
|
||||||
|
self,
|
||||||
|
shape,
|
||||||
|
&shape.fills,
|
||||||
|
antialias,
|
||||||
|
fills_surface_id,
|
||||||
|
outset,
|
||||||
|
);
|
||||||
|
>>>>>>> Stashed changes
|
||||||
}
|
}
|
||||||
|
|
||||||
// Skip stroke rendering for clipped frames - they are drawn in render_shape_exit
|
// Skip stroke rendering for clipped frames - they are drawn in render_shape_exit
|
||||||
@ -1073,6 +1102,10 @@ impl RenderState {
|
|||||||
&visible_strokes,
|
&visible_strokes,
|
||||||
Some(strokes_surface_id),
|
Some(strokes_surface_id),
|
||||||
antialias,
|
antialias,
|
||||||
|
<<<<<<< Updated upstream
|
||||||
|
=======
|
||||||
|
outset,
|
||||||
|
>>>>>>> Stashed changes
|
||||||
);
|
);
|
||||||
if !fast_mode {
|
if !fast_mode {
|
||||||
for stroke in &visible_strokes {
|
for stroke in &visible_strokes {
|
||||||
|
|||||||
@ -2,7 +2,15 @@ use skia_safe::{self as skia, Paint, RRect};
|
|||||||
|
|
||||||
use super::{filters, RenderState, SurfaceId};
|
use super::{filters, RenderState, SurfaceId};
|
||||||
use crate::render::get_source_rect;
|
use crate::render::get_source_rect;
|
||||||
use crate::shapes::{merge_fills, Fill, Frame, ImageFill, Rect, Shape, Type};
|
use crate::shapes::{merge_fills, Fill, Frame, ImageFill, Rect, Shape, StrokeKind, Type};
|
||||||
|
|
||||||
|
/// True when the shape has at least one visible inner stroke.
|
||||||
|
fn has_inner_stroke(shape: &Shape) -> bool {
|
||||||
|
let is_open = shape.is_open();
|
||||||
|
shape
|
||||||
|
.visible_strokes()
|
||||||
|
.any(|s| s.render_kind(is_open) == StrokeKind::Inner)
|
||||||
|
}
|
||||||
|
|
||||||
fn draw_image_fill(
|
fn draw_image_fill(
|
||||||
render_state: &mut RenderState,
|
render_state: &mut RenderState,
|
||||||
@ -97,6 +105,10 @@ pub fn render(
|
|||||||
fills: &[Fill],
|
fills: &[Fill],
|
||||||
antialias: bool,
|
antialias: bool,
|
||||||
surface_id: SurfaceId,
|
surface_id: SurfaceId,
|
||||||
|
<<<<<<< Updated upstream
|
||||||
|
=======
|
||||||
|
outset: Option<f32>,
|
||||||
|
>>>>>>> Stashed changes
|
||||||
) {
|
) {
|
||||||
if fills.is_empty() {
|
if fills.is_empty() {
|
||||||
return;
|
return;
|
||||||
@ -106,8 +118,18 @@ pub fn render(
|
|||||||
// and sampling options that get_fill_shader (used by merge_fills) lacks.
|
// and sampling options that get_fill_shader (used by merge_fills) lacks.
|
||||||
let has_image_fills = fills.iter().any(|f| matches!(f, Fill::Image(_)));
|
let has_image_fills = fills.iter().any(|f| matches!(f, Fill::Image(_)));
|
||||||
if has_image_fills {
|
if has_image_fills {
|
||||||
|
let scale = render_state.get_scale().max(1e-6);
|
||||||
|
let inset = if has_inner_stroke(shape) {
|
||||||
|
Some(1.0 / scale)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
for fill in fills.iter().rev() {
|
for fill in fills.iter().rev() {
|
||||||
|
<<<<<<< Updated upstream
|
||||||
render_single_fill(render_state, shape, fill, antialias, surface_id);
|
render_single_fill(render_state, shape, fill, antialias, surface_id);
|
||||||
|
=======
|
||||||
|
render_single_fill(render_state, shape, fill, antialias, surface_id, outset, inset);
|
||||||
|
>>>>>>> Stashed changes
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -115,6 +137,13 @@ pub fn render(
|
|||||||
let mut paint = merge_fills(fills, shape.selrect);
|
let mut paint = merge_fills(fills, shape.selrect);
|
||||||
paint.set_anti_alias(antialias);
|
paint.set_anti_alias(antialias);
|
||||||
|
|
||||||
|
let scale = render_state.get_scale().max(1e-6);
|
||||||
|
let inset = if has_inner_stroke(shape) {
|
||||||
|
Some(1.0 / scale)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
if let Some(image_filter) = shape.image_filter(1.) {
|
if let Some(image_filter) = shape.image_filter(1.) {
|
||||||
let bounds = image_filter.compute_fast_bounds(shape.selrect);
|
let bounds = image_filter.compute_fast_bounds(shape.selrect);
|
||||||
if filters::render_with_filter_surface(
|
if filters::render_with_filter_surface(
|
||||||
@ -124,7 +153,11 @@ pub fn render(
|
|||||||
|state, temp_surface| {
|
|state, temp_surface| {
|
||||||
let mut filtered_paint = paint.clone();
|
let mut filtered_paint = paint.clone();
|
||||||
filtered_paint.set_image_filter(image_filter.clone());
|
filtered_paint.set_image_filter(image_filter.clone());
|
||||||
|
<<<<<<< Updated upstream
|
||||||
draw_fill_to_surface(state, shape, temp_surface, &filtered_paint);
|
draw_fill_to_surface(state, shape, temp_surface, &filtered_paint);
|
||||||
|
=======
|
||||||
|
draw_fill_to_surface(state, shape, temp_surface, &filtered_paint, outset, inset);
|
||||||
|
>>>>>>> Stashed changes
|
||||||
},
|
},
|
||||||
) {
|
) {
|
||||||
return;
|
return;
|
||||||
@ -133,28 +166,53 @@ pub fn render(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
<<<<<<< Updated upstream
|
||||||
draw_fill_to_surface(render_state, shape, surface_id, &paint);
|
draw_fill_to_surface(render_state, shape, surface_id, &paint);
|
||||||
|
=======
|
||||||
|
draw_fill_to_surface(render_state, shape, surface_id, &paint, outset, inset);
|
||||||
|
>>>>>>> Stashed changes
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Draws a single paint (with a merged shader) to the appropriate surface
|
/// Draws a single paint (with a merged shader) to the appropriate surface
|
||||||
/// based on the shape type.
|
/// based on the shape type.
|
||||||
|
/// When `inset` is Some(eps), the fill is inset by eps (e.g. to avoid seam with inner strokes).
|
||||||
fn draw_fill_to_surface(
|
fn draw_fill_to_surface(
|
||||||
render_state: &mut RenderState,
|
render_state: &mut RenderState,
|
||||||
shape: &Shape,
|
shape: &Shape,
|
||||||
surface_id: SurfaceId,
|
surface_id: SurfaceId,
|
||||||
paint: &Paint,
|
paint: &Paint,
|
||||||
|
<<<<<<< Updated upstream
|
||||||
) {
|
) {
|
||||||
match &shape.shape_type {
|
match &shape.shape_type {
|
||||||
Type::Rect(_) | Type::Frame(_) => {
|
Type::Rect(_) | Type::Frame(_) => {
|
||||||
render_state.surfaces.draw_rect_to(surface_id, shape, paint);
|
render_state.surfaces.draw_rect_to(surface_id, shape, paint);
|
||||||
|
=======
|
||||||
|
outset: Option<f32>,
|
||||||
|
inset: Option<f32>,
|
||||||
|
) {
|
||||||
|
match &shape.shape_type {
|
||||||
|
Type::Rect(_) | Type::Frame(_) => {
|
||||||
|
render_state
|
||||||
|
.surfaces
|
||||||
|
.draw_rect_to(surface_id, shape, paint, outset, inset);
|
||||||
|
>>>>>>> Stashed changes
|
||||||
}
|
}
|
||||||
Type::Circle => {
|
Type::Circle => {
|
||||||
render_state
|
render_state
|
||||||
.surfaces
|
.surfaces
|
||||||
|
<<<<<<< Updated upstream
|
||||||
.draw_circle_to(surface_id, shape, paint);
|
.draw_circle_to(surface_id, shape, paint);
|
||||||
}
|
}
|
||||||
Type::Path(_) | Type::Bool(_) => {
|
Type::Path(_) | Type::Bool(_) => {
|
||||||
render_state.surfaces.draw_path_to(surface_id, shape, paint);
|
render_state.surfaces.draw_path_to(surface_id, shape, paint);
|
||||||
|
=======
|
||||||
|
.draw_circle_to(surface_id, shape, paint, outset, inset);
|
||||||
|
}
|
||||||
|
Type::Path(_) | Type::Bool(_) => {
|
||||||
|
render_state
|
||||||
|
.surfaces
|
||||||
|
.draw_path_to(surface_id, shape, paint, outset);
|
||||||
|
>>>>>>> Stashed changes
|
||||||
}
|
}
|
||||||
Type::Group(_) => {}
|
Type::Group(_) => {}
|
||||||
_ => unreachable!("This shape should not have fills"),
|
_ => unreachable!("This shape should not have fills"),
|
||||||
@ -167,6 +225,11 @@ fn render_single_fill(
|
|||||||
fill: &Fill,
|
fill: &Fill,
|
||||||
antialias: bool,
|
antialias: bool,
|
||||||
surface_id: SurfaceId,
|
surface_id: SurfaceId,
|
||||||
|
<<<<<<< Updated upstream
|
||||||
|
=======
|
||||||
|
outset: Option<f32>,
|
||||||
|
inset: Option<f32>,
|
||||||
|
>>>>>>> Stashed changes
|
||||||
) {
|
) {
|
||||||
let mut paint = fill.to_paint(&shape.selrect, antialias);
|
let mut paint = fill.to_paint(&shape.selrect, antialias);
|
||||||
if let Some(image_filter) = shape.image_filter(1.) {
|
if let Some(image_filter) = shape.image_filter(1.) {
|
||||||
@ -185,6 +248,11 @@ fn render_single_fill(
|
|||||||
antialias,
|
antialias,
|
||||||
temp_surface,
|
temp_surface,
|
||||||
&filtered_paint,
|
&filtered_paint,
|
||||||
|
<<<<<<< Updated upstream
|
||||||
|
=======
|
||||||
|
outset,
|
||||||
|
inset,
|
||||||
|
>>>>>>> Stashed changes
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
) {
|
) {
|
||||||
@ -194,7 +262,20 @@ fn render_single_fill(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
<<<<<<< Updated upstream
|
||||||
draw_single_fill_to_surface(render_state, shape, fill, antialias, surface_id, &paint);
|
draw_single_fill_to_surface(render_state, shape, fill, antialias, surface_id, &paint);
|
||||||
|
=======
|
||||||
|
draw_single_fill_to_surface(
|
||||||
|
render_state,
|
||||||
|
shape,
|
||||||
|
fill,
|
||||||
|
antialias,
|
||||||
|
surface_id,
|
||||||
|
&paint,
|
||||||
|
outset,
|
||||||
|
inset,
|
||||||
|
);
|
||||||
|
>>>>>>> Stashed changes
|
||||||
}
|
}
|
||||||
|
|
||||||
fn draw_single_fill_to_surface(
|
fn draw_single_fill_to_surface(
|
||||||
@ -204,6 +285,11 @@ fn draw_single_fill_to_surface(
|
|||||||
antialias: bool,
|
antialias: bool,
|
||||||
surface_id: SurfaceId,
|
surface_id: SurfaceId,
|
||||||
paint: &Paint,
|
paint: &Paint,
|
||||||
|
<<<<<<< Updated upstream
|
||||||
|
=======
|
||||||
|
outset: Option<f32>,
|
||||||
|
inset: Option<f32>,
|
||||||
|
>>>>>>> Stashed changes
|
||||||
) {
|
) {
|
||||||
match (fill, &shape.shape_type) {
|
match (fill, &shape.shape_type) {
|
||||||
(Fill::Image(image_fill), _) => {
|
(Fill::Image(image_fill), _) => {
|
||||||
@ -217,15 +303,30 @@ fn draw_single_fill_to_surface(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
(_, Type::Rect(_) | Type::Frame(_)) => {
|
(_, Type::Rect(_) | Type::Frame(_)) => {
|
||||||
|
<<<<<<< Updated upstream
|
||||||
render_state.surfaces.draw_rect_to(surface_id, shape, paint);
|
render_state.surfaces.draw_rect_to(surface_id, shape, paint);
|
||||||
|
=======
|
||||||
|
render_state
|
||||||
|
.surfaces
|
||||||
|
.draw_rect_to(surface_id, shape, paint, outset, inset);
|
||||||
|
>>>>>>> Stashed changes
|
||||||
}
|
}
|
||||||
(_, Type::Circle) => {
|
(_, Type::Circle) => {
|
||||||
render_state
|
render_state
|
||||||
.surfaces
|
.surfaces
|
||||||
|
<<<<<<< Updated upstream
|
||||||
.draw_circle_to(surface_id, shape, paint);
|
.draw_circle_to(surface_id, shape, paint);
|
||||||
}
|
}
|
||||||
(_, Type::Path(_)) | (_, Type::Bool(_)) => {
|
(_, Type::Path(_)) | (_, Type::Bool(_)) => {
|
||||||
render_state.surfaces.draw_path_to(surface_id, shape, paint);
|
render_state.surfaces.draw_path_to(surface_id, shape, paint);
|
||||||
|
=======
|
||||||
|
.draw_circle_to(surface_id, shape, paint, outset, inset);
|
||||||
|
}
|
||||||
|
(_, Type::Path(_)) | (_, Type::Bool(_)) => {
|
||||||
|
render_state
|
||||||
|
.surfaces
|
||||||
|
.draw_path_to(surface_id, shape, paint, outset);
|
||||||
|
>>>>>>> Stashed changes
|
||||||
}
|
}
|
||||||
(_, Type::Group(_)) => {
|
(_, Type::Group(_)) => {
|
||||||
// Groups can have fills but they propagate them to their children
|
// Groups can have fills but they propagate them to their children
|
||||||
|
|||||||
@ -106,12 +106,22 @@ fn render_shadow_paint(
|
|||||||
) {
|
) {
|
||||||
match &shape.shape_type {
|
match &shape.shape_type {
|
||||||
Type::Rect(_) | Type::Frame(_) => {
|
Type::Rect(_) | Type::Frame(_) => {
|
||||||
|
<<<<<<< Updated upstream
|
||||||
render_state.surfaces.draw_rect_to(surface_id, shape, paint);
|
render_state.surfaces.draw_rect_to(surface_id, shape, paint);
|
||||||
|
=======
|
||||||
|
render_state
|
||||||
|
.surfaces
|
||||||
|
.draw_rect_to(surface_id, shape, paint, None, None);
|
||||||
|
>>>>>>> Stashed changes
|
||||||
}
|
}
|
||||||
Type::Circle => {
|
Type::Circle => {
|
||||||
render_state
|
render_state
|
||||||
.surfaces
|
.surfaces
|
||||||
|
<<<<<<< Updated upstream
|
||||||
.draw_circle_to(surface_id, shape, paint);
|
.draw_circle_to(surface_id, shape, paint);
|
||||||
|
=======
|
||||||
|
.draw_circle_to(surface_id, shape, paint, None, None);
|
||||||
|
>>>>>>> Stashed changes
|
||||||
}
|
}
|
||||||
Type::Path(_) | Type::Bool(_) => {
|
Type::Path(_) | Type::Bool(_) => {
|
||||||
render_state.surfaces.draw_path_to(surface_id, shape, paint);
|
render_state.surfaces.draw_path_to(surface_id, shape, paint);
|
||||||
|
|||||||
@ -526,6 +526,10 @@ pub fn render(
|
|||||||
strokes: &[&Stroke],
|
strokes: &[&Stroke],
|
||||||
surface_id: Option<SurfaceId>,
|
surface_id: Option<SurfaceId>,
|
||||||
antialias: bool,
|
antialias: bool,
|
||||||
|
<<<<<<< Updated upstream
|
||||||
|
=======
|
||||||
|
outset: Option<f32>,
|
||||||
|
>>>>>>> Stashed changes
|
||||||
) {
|
) {
|
||||||
if strokes.is_empty() {
|
if strokes.is_empty() {
|
||||||
return;
|
return;
|
||||||
@ -540,6 +544,13 @@ pub fn render(
|
|||||||
// edges semi-transparent and revealing strokes underneath.
|
// edges semi-transparent and revealing strokes underneath.
|
||||||
if let Some(image_filter) = shape.image_filter(1.) {
|
if let Some(image_filter) = shape.image_filter(1.) {
|
||||||
let mut content_bounds = shape.selrect;
|
let mut content_bounds = shape.selrect;
|
||||||
|
<<<<<<< Updated upstream
|
||||||
|
=======
|
||||||
|
// Expand for outset if provided
|
||||||
|
if let Some(s) = outset.filter(|&s| s > 0.0) {
|
||||||
|
content_bounds.outset((s, s));
|
||||||
|
}
|
||||||
|
>>>>>>> Stashed changes
|
||||||
let max_margin = strokes
|
let max_margin = strokes
|
||||||
.iter()
|
.iter()
|
||||||
.map(|s| s.bounds_width(shape.is_open()))
|
.map(|s| s.bounds_width(shape.is_open()))
|
||||||
@ -583,6 +594,10 @@ pub fn render(
|
|||||||
antialias,
|
antialias,
|
||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
|
<<<<<<< Updated upstream
|
||||||
|
=======
|
||||||
|
outset,
|
||||||
|
>>>>>>> Stashed changes
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -595,12 +610,36 @@ pub fn render(
|
|||||||
|
|
||||||
// No blur or filter surface unavailable — draw strokes individually.
|
// No blur or filter surface unavailable — draw strokes individually.
|
||||||
for stroke in strokes.iter().rev() {
|
for stroke in strokes.iter().rev() {
|
||||||
|
<<<<<<< Updated upstream
|
||||||
render_single(render_state, shape, stroke, surface_id, None, antialias);
|
render_single(render_state, shape, stroke, surface_id, None, antialias);
|
||||||
|
=======
|
||||||
|
render_single(
|
||||||
|
render_state,
|
||||||
|
shape,
|
||||||
|
stroke,
|
||||||
|
surface_id,
|
||||||
|
None,
|
||||||
|
antialias,
|
||||||
|
outset,
|
||||||
|
);
|
||||||
|
>>>>>>> Stashed changes
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
<<<<<<< Updated upstream
|
||||||
render_merged(render_state, shape, strokes, surface_id, antialias, false);
|
render_merged(render_state, shape, strokes, surface_id, antialias, false);
|
||||||
|
=======
|
||||||
|
render_merged(
|
||||||
|
render_state,
|
||||||
|
shape,
|
||||||
|
strokes,
|
||||||
|
surface_id,
|
||||||
|
antialias,
|
||||||
|
false,
|
||||||
|
outset,
|
||||||
|
);
|
||||||
|
>>>>>>> Stashed changes
|
||||||
}
|
}
|
||||||
|
|
||||||
fn strokes_share_geometry(strokes: &[&Stroke]) -> bool {
|
fn strokes_share_geometry(strokes: &[&Stroke]) -> bool {
|
||||||
@ -620,6 +659,10 @@ fn render_merged(
|
|||||||
surface_id: Option<SurfaceId>,
|
surface_id: Option<SurfaceId>,
|
||||||
antialias: bool,
|
antialias: bool,
|
||||||
bypass_filter: bool,
|
bypass_filter: bool,
|
||||||
|
<<<<<<< Updated upstream
|
||||||
|
=======
|
||||||
|
outset: Option<f32>,
|
||||||
|
>>>>>>> Stashed changes
|
||||||
) {
|
) {
|
||||||
let representative = *strokes
|
let representative = *strokes
|
||||||
.last()
|
.last()
|
||||||
@ -635,6 +678,13 @@ fn render_merged(
|
|||||||
if !bypass_filter {
|
if !bypass_filter {
|
||||||
if let Some(image_filter) = blur_filter.clone() {
|
if let Some(image_filter) = blur_filter.clone() {
|
||||||
let mut content_bounds = shape.selrect;
|
let mut content_bounds = shape.selrect;
|
||||||
|
<<<<<<< Updated upstream
|
||||||
|
=======
|
||||||
|
// Expand for outset if provided
|
||||||
|
if let Some(s) = outset.filter(|&s| s > 0.0) {
|
||||||
|
content_bounds.outset((s, s));
|
||||||
|
}
|
||||||
|
>>>>>>> Stashed changes
|
||||||
let stroke_margin = representative.bounds_width(shape.is_open());
|
let stroke_margin = representative.bounds_width(shape.is_open());
|
||||||
if stroke_margin > 0.0 {
|
if stroke_margin > 0.0 {
|
||||||
content_bounds.inset((-stroke_margin, -stroke_margin));
|
content_bounds.inset((-stroke_margin, -stroke_margin));
|
||||||
@ -660,7 +710,19 @@ fn render_merged(
|
|||||||
canvas.save_layer(&layer_rec);
|
canvas.save_layer(&layer_rec);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
<<<<<<< Updated upstream
|
||||||
render_merged(state, shape, strokes, Some(temp_surface), antialias, true);
|
render_merged(state, shape, strokes, Some(temp_surface), antialias, true);
|
||||||
|
=======
|
||||||
|
render_merged(
|
||||||
|
state,
|
||||||
|
shape,
|
||||||
|
strokes,
|
||||||
|
Some(temp_surface),
|
||||||
|
antialias,
|
||||||
|
true,
|
||||||
|
outset,
|
||||||
|
);
|
||||||
|
>>>>>>> Stashed changes
|
||||||
|
|
||||||
state.surfaces.apply_mut(temp_surface as u32, |surface| {
|
state.surfaces.apply_mut(temp_surface as u32, |surface| {
|
||||||
surface.canvas().restore();
|
surface.canvas().restore();
|
||||||
@ -676,7 +738,20 @@ fn render_merged(
|
|||||||
// via SrcOver), matching the non-merged path where strokes[0] is drawn last (on top).
|
// via SrcOver), matching the non-merged path where strokes[0] is drawn last (on top).
|
||||||
let fills: Vec<Fill> = strokes.iter().map(|s| s.fill.clone()).collect();
|
let fills: Vec<Fill> = strokes.iter().map(|s| s.fill.clone()).collect();
|
||||||
|
|
||||||
|
<<<<<<< Updated upstream
|
||||||
let merged = merge_fills(&fills, shape.selrect);
|
let merged = merge_fills(&fills, shape.selrect);
|
||||||
|
=======
|
||||||
|
// Expand selrect if outset is provided
|
||||||
|
let selrect = if let Some(s) = outset.filter(|&s| s > 0.0) {
|
||||||
|
let mut r = shape.selrect;
|
||||||
|
r.outset((s, s));
|
||||||
|
r
|
||||||
|
} else {
|
||||||
|
shape.selrect
|
||||||
|
};
|
||||||
|
|
||||||
|
let merged = merge_fills(&fills, selrect);
|
||||||
|
>>>>>>> Stashed changes
|
||||||
let scale = render_state.get_scale();
|
let scale = render_state.get_scale();
|
||||||
let target_surface = surface_id.unwrap_or(SurfaceId::Strokes);
|
let target_surface = surface_id.unwrap_or(SurfaceId::Strokes);
|
||||||
let canvas = render_state.surfaces.canvas_and_mark_dirty(target_surface);
|
let canvas = render_state.surfaces.canvas_and_mark_dirty(target_surface);
|
||||||
@ -747,6 +822,10 @@ pub fn render_single(
|
|||||||
surface_id: Option<SurfaceId>,
|
surface_id: Option<SurfaceId>,
|
||||||
shadow: Option<&ImageFilter>,
|
shadow: Option<&ImageFilter>,
|
||||||
antialias: bool,
|
antialias: bool,
|
||||||
|
<<<<<<< Updated upstream
|
||||||
|
=======
|
||||||
|
outset: Option<f32>,
|
||||||
|
>>>>>>> Stashed changes
|
||||||
) {
|
) {
|
||||||
render_single_internal(
|
render_single_internal(
|
||||||
render_state,
|
render_state,
|
||||||
@ -757,6 +836,10 @@ pub fn render_single(
|
|||||||
antialias,
|
antialias,
|
||||||
false,
|
false,
|
||||||
false,
|
false,
|
||||||
|
<<<<<<< Updated upstream
|
||||||
|
=======
|
||||||
|
outset,
|
||||||
|
>>>>>>> Stashed changes
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -770,10 +853,21 @@ fn render_single_internal(
|
|||||||
antialias: bool,
|
antialias: bool,
|
||||||
bypass_filter: bool,
|
bypass_filter: bool,
|
||||||
skip_blur: bool,
|
skip_blur: bool,
|
||||||
|
<<<<<<< Updated upstream
|
||||||
|
=======
|
||||||
|
outset: Option<f32>,
|
||||||
|
>>>>>>> Stashed changes
|
||||||
) {
|
) {
|
||||||
if !bypass_filter {
|
if !bypass_filter {
|
||||||
if let Some(image_filter) = shape.image_filter(1.) {
|
if let Some(image_filter) = shape.image_filter(1.) {
|
||||||
let mut content_bounds = shape.selrect;
|
let mut content_bounds = shape.selrect;
|
||||||
|
<<<<<<< Updated upstream
|
||||||
|
=======
|
||||||
|
// Expand for outset if provided
|
||||||
|
if let Some(s) = outset.filter(|&s| s > 0.0) {
|
||||||
|
content_bounds.outset((s, s));
|
||||||
|
}
|
||||||
|
>>>>>>> Stashed changes
|
||||||
let stroke_margin = stroke.bounds_width(shape.is_open());
|
let stroke_margin = stroke.bounds_width(shape.is_open());
|
||||||
if stroke_margin > 0.0 {
|
if stroke_margin > 0.0 {
|
||||||
content_bounds.inset((-stroke_margin, -stroke_margin));
|
content_bounds.inset((-stroke_margin, -stroke_margin));
|
||||||
@ -799,6 +893,10 @@ fn render_single_internal(
|
|||||||
antialias,
|
antialias,
|
||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
|
<<<<<<< Updated upstream
|
||||||
|
=======
|
||||||
|
outset,
|
||||||
|
>>>>>>> Stashed changes
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
) {
|
) {
|
||||||
@ -867,7 +965,25 @@ fn render_single_internal(
|
|||||||
shape_type @ (Type::Path(_) | Type::Bool(_)) => {
|
shape_type @ (Type::Path(_) | Type::Bool(_)) => {
|
||||||
if let Some(path) = shape_type.path() {
|
if let Some(path) = shape_type.path() {
|
||||||
let is_open = path.is_open();
|
let is_open = path.is_open();
|
||||||
|
<<<<<<< Updated upstream
|
||||||
let paint = stroke.to_stroked_paint(is_open, &selrect, svg_attrs, antialias);
|
let paint = stroke.to_stroked_paint(is_open, &selrect, svg_attrs, antialias);
|
||||||
|
=======
|
||||||
|
let mut paint =
|
||||||
|
stroke.to_stroked_paint(is_open, &selrect, svg_attrs, antialias);
|
||||||
|
// Apply outset by increasing stroke width
|
||||||
|
if let Some(s) = outset.filter(|&s| s > 0.0) {
|
||||||
|
let current_width = paint.stroke_width();
|
||||||
|
// Path stroke kinds are built differently:
|
||||||
|
// - Center uses the stroke width directly.
|
||||||
|
// - Inner/Outer use a doubled width plus clipping/clearing logic.
|
||||||
|
// Compensate outset so visual growth is comparable across kinds.
|
||||||
|
let outset_growth = match stroke.render_kind(is_open) {
|
||||||
|
StrokeKind::Center => s * 2.0,
|
||||||
|
StrokeKind::Inner | StrokeKind::Outer => s * 4.0,
|
||||||
|
};
|
||||||
|
paint.set_stroke_width(current_width + outset_growth);
|
||||||
|
}
|
||||||
|
>>>>>>> Stashed changes
|
||||||
draw_stroke_on_path(
|
draw_stroke_on_path(
|
||||||
canvas,
|
canvas,
|
||||||
stroke,
|
stroke,
|
||||||
|
|||||||
@ -355,9 +355,42 @@ impl Surfaces {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
<<<<<<< Updated upstream
|
||||||
pub fn draw_rect_to(&mut self, id: SurfaceId, shape: &Shape, paint: &Paint) {
|
pub fn draw_rect_to(&mut self, id: SurfaceId, shape: &Shape, paint: &Paint) {
|
||||||
if let Some(corners) = shape.shape_type.corners() {
|
if let Some(corners) = shape.shape_type.corners() {
|
||||||
let rrect = RRect::new_rect_radii(shape.selrect, &corners);
|
let rrect = RRect::new_rect_radii(shape.selrect, &corners);
|
||||||
|
=======
|
||||||
|
pub fn draw_rect_to(
|
||||||
|
&mut self,
|
||||||
|
id: SurfaceId,
|
||||||
|
shape: &Shape,
|
||||||
|
paint: &Paint,
|
||||||
|
outset: Option<f32>,
|
||||||
|
inset: Option<f32>,
|
||||||
|
) {
|
||||||
|
let mut rect = if let Some(s) = outset.filter(|&s| s > 0.0) {
|
||||||
|
let mut r = shape.selrect;
|
||||||
|
r.outset((s, s));
|
||||||
|
r
|
||||||
|
} else {
|
||||||
|
shape.selrect
|
||||||
|
};
|
||||||
|
if let Some(eps) = inset.filter(|&e| e > 0.0) {
|
||||||
|
rect.inset((eps, eps));
|
||||||
|
}
|
||||||
|
if let Some(corners) = shape.shape_type.corners() {
|
||||||
|
let corners = if let Some(eps) = inset.filter(|&e| e > 0.0) {
|
||||||
|
let mut c = corners;
|
||||||
|
for r in c.iter_mut() {
|
||||||
|
r.x = (r.x - eps).max(0.0);
|
||||||
|
r.y = (r.y - eps).max(0.0);
|
||||||
|
}
|
||||||
|
c
|
||||||
|
} else {
|
||||||
|
corners
|
||||||
|
};
|
||||||
|
let rrect = RRect::new_rect_radii(rect, &corners);
|
||||||
|
>>>>>>> Stashed changes
|
||||||
self.canvas_and_mark_dirty(id).draw_rrect(rrect, paint);
|
self.canvas_and_mark_dirty(id).draw_rrect(rrect, paint);
|
||||||
} else {
|
} else {
|
||||||
self.canvas_and_mark_dirty(id)
|
self.canvas_and_mark_dirty(id)
|
||||||
@ -365,6 +398,7 @@ impl Surfaces {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
<<<<<<< Updated upstream
|
||||||
pub fn draw_circle_to(&mut self, id: SurfaceId, shape: &Shape, paint: &Paint) {
|
pub fn draw_circle_to(&mut self, id: SurfaceId, shape: &Shape, paint: &Paint) {
|
||||||
self.canvas_and_mark_dirty(id)
|
self.canvas_and_mark_dirty(id)
|
||||||
.draw_oval(shape.selrect, paint);
|
.draw_oval(shape.selrect, paint);
|
||||||
@ -373,6 +407,46 @@ impl Surfaces {
|
|||||||
pub fn draw_path_to(&mut self, id: SurfaceId, shape: &Shape, paint: &Paint) {
|
pub fn draw_path_to(&mut self, id: SurfaceId, shape: &Shape, paint: &Paint) {
|
||||||
if let Some(path) = shape.get_skia_path() {
|
if let Some(path) = shape.get_skia_path() {
|
||||||
self.canvas_and_mark_dirty(id).draw_path(&path, paint);
|
self.canvas_and_mark_dirty(id).draw_path(&path, paint);
|
||||||
|
=======
|
||||||
|
pub fn draw_circle_to(
|
||||||
|
&mut self,
|
||||||
|
id: SurfaceId,
|
||||||
|
shape: &Shape,
|
||||||
|
paint: &Paint,
|
||||||
|
outset: Option<f32>,
|
||||||
|
inset: Option<f32>,
|
||||||
|
) {
|
||||||
|
let mut rect = if let Some(s) = outset.filter(|&s| s > 0.0) {
|
||||||
|
let mut r = shape.selrect;
|
||||||
|
r.outset((s, s));
|
||||||
|
r
|
||||||
|
} else {
|
||||||
|
shape.selrect
|
||||||
|
};
|
||||||
|
if let Some(eps) = inset.filter(|&e| e > 0.0) {
|
||||||
|
rect.inset((eps, eps));
|
||||||
|
}
|
||||||
|
self.canvas_and_mark_dirty(id).draw_oval(rect, paint);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn draw_path_to(
|
||||||
|
&mut self,
|
||||||
|
id: SurfaceId,
|
||||||
|
shape: &Shape,
|
||||||
|
paint: &Paint,
|
||||||
|
outset: Option<f32>,
|
||||||
|
) {
|
||||||
|
if let Some(path) = shape.get_skia_path() {
|
||||||
|
let canvas = self.canvas_and_mark_dirty(id);
|
||||||
|
if let Some(s) = outset.filter(|&s| s > 0.0) {
|
||||||
|
// Draw path as a thick stroke to get outset (expanded) silhouette
|
||||||
|
let mut stroke_paint = paint.clone();
|
||||||
|
stroke_paint.set_stroke_width(s * 2.0);
|
||||||
|
canvas.draw_path(&path, &stroke_paint);
|
||||||
|
} else {
|
||||||
|
canvas.draw_path(&path, paint);
|
||||||
|
}
|
||||||
|
>>>>>>> Stashed changes
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user