mirror of
https://github.com/penpot/penpot.git
synced 2026-02-12 14:42:56 +00:00
🐛 Fix stroke weight visually different with different levels of zoom
This commit is contained in:
committed by
Alonso Torres
parent
b881e36875
commit
6a84215911
@@ -27,8 +27,8 @@ fn draw_stroke_on_rect(
|
||||
// - The same rect if it's a center stroke
|
||||
// - A bigger rect if it's an outer stroke
|
||||
// - A smaller rect if it's an outer stroke
|
||||
let stroke_rect = stroke.outer_rect(rect);
|
||||
let mut paint = stroke.to_paint(selrect, svg_attrs, scale, antialias);
|
||||
let stroke_rect = stroke.aligned_rect(rect, scale);
|
||||
let mut paint = stroke.to_paint(selrect, svg_attrs, antialias);
|
||||
|
||||
// Apply both blur and shadow filters if present, composing them if necessary.
|
||||
let filter = compose_filters(blur, shadow);
|
||||
@@ -63,8 +63,8 @@ fn draw_stroke_on_circle(
|
||||
// - The same oval if it's a center stroke
|
||||
// - A bigger oval if it's an outer stroke
|
||||
// - A smaller oval if it's an outer stroke
|
||||
let stroke_rect = stroke.outer_rect(rect);
|
||||
let mut paint = stroke.to_paint(selrect, svg_attrs, scale, antialias);
|
||||
let stroke_rect = stroke.aligned_rect(rect, scale);
|
||||
let mut paint = stroke.to_paint(selrect, svg_attrs, antialias);
|
||||
|
||||
// Apply both blur and shadow filters if present, composing them if necessary.
|
||||
let filter = compose_filters(blur, shadow);
|
||||
@@ -131,7 +131,6 @@ pub fn draw_stroke_on_path(
|
||||
selrect: &Rect,
|
||||
path_transform: Option<&Matrix>,
|
||||
svg_attrs: Option<&SvgAttrs>,
|
||||
scale: f32,
|
||||
shadow: Option<&ImageFilter>,
|
||||
blur: Option<&ImageFilter>,
|
||||
antialias: bool,
|
||||
@@ -142,7 +141,7 @@ pub fn draw_stroke_on_path(
|
||||
let is_open = path.is_open();
|
||||
|
||||
let mut paint: skia_safe::Handle<_> =
|
||||
stroke.to_stroked_paint(is_open, selrect, svg_attrs, scale, antialias);
|
||||
stroke.to_stroked_paint(is_open, selrect, svg_attrs, antialias);
|
||||
|
||||
let filter = compose_filters(blur, shadow);
|
||||
paint.set_image_filter(filter);
|
||||
@@ -166,7 +165,6 @@ pub fn draw_stroke_on_path(
|
||||
canvas,
|
||||
is_open,
|
||||
svg_attrs,
|
||||
scale,
|
||||
blur,
|
||||
antialias,
|
||||
);
|
||||
@@ -218,7 +216,6 @@ fn handle_stroke_caps(
|
||||
canvas: &skia::Canvas,
|
||||
is_open: bool,
|
||||
svg_attrs: Option<&SvgAttrs>,
|
||||
scale: f32,
|
||||
blur: Option<&ImageFilter>,
|
||||
antialias: bool,
|
||||
) {
|
||||
@@ -233,8 +230,7 @@ fn handle_stroke_caps(
|
||||
let first_point = points.first().unwrap();
|
||||
let last_point = points.last().unwrap();
|
||||
|
||||
let mut paint_stroke =
|
||||
stroke.to_stroked_paint(is_open, selrect, svg_attrs, scale, antialias);
|
||||
let mut paint_stroke = stroke.to_stroked_paint(is_open, selrect, svg_attrs, antialias);
|
||||
|
||||
if let Some(filter) = blur {
|
||||
paint_stroke.set_image_filter(filter.clone());
|
||||
@@ -405,7 +401,7 @@ fn draw_image_stroke_in_container(
|
||||
|
||||
// Draw the stroke based on the shape type, we are using this stroke as
|
||||
// a "selector" of the area of the image we want to show.
|
||||
let outer_rect = stroke.outer_rect(container);
|
||||
let outer_rect = stroke.aligned_rect(container, scale);
|
||||
|
||||
match &shape.shape_type {
|
||||
shape_type @ (Type::Rect(_) | Type::Frame(_)) => {
|
||||
@@ -450,8 +446,7 @@ fn draw_image_stroke_in_container(
|
||||
}
|
||||
}
|
||||
let is_open = p.is_open();
|
||||
let mut paint =
|
||||
stroke.to_stroked_paint(is_open, &outer_rect, svg_attrs, scale, antialias);
|
||||
let mut paint = stroke.to_stroked_paint(is_open, &outer_rect, svg_attrs, antialias);
|
||||
canvas.draw_path(&path, &paint);
|
||||
if stroke.render_kind(is_open) == StrokeKind::Outer {
|
||||
// Small extra inner stroke to overlap with the fill
|
||||
@@ -466,7 +461,6 @@ fn draw_image_stroke_in_container(
|
||||
canvas,
|
||||
is_open,
|
||||
svg_attrs,
|
||||
scale,
|
||||
shape.image_filter(1.).as_ref(),
|
||||
antialias,
|
||||
);
|
||||
@@ -662,7 +656,6 @@ fn render_internal(
|
||||
&selrect,
|
||||
path_transform.as_ref(),
|
||||
svg_attrs,
|
||||
scale,
|
||||
shadow,
|
||||
shape.image_filter(1.).as_ref(),
|
||||
antialias,
|
||||
@@ -685,14 +678,13 @@ pub fn render_text_paths(
|
||||
shadow: Option<&ImageFilter>,
|
||||
antialias: bool,
|
||||
) {
|
||||
let scale = render_state.get_scale();
|
||||
let canvas = render_state
|
||||
.surfaces
|
||||
.canvas_and_mark_dirty(surface_id.unwrap_or(SurfaceId::Strokes));
|
||||
let selrect = &shape.selrect;
|
||||
let svg_attrs = shape.svg_attrs.as_ref();
|
||||
let mut paint: skia_safe::Handle<_> =
|
||||
stroke.to_text_stroked_paint(false, selrect, svg_attrs, scale, antialias);
|
||||
stroke.to_text_stroked_paint(false, selrect, svg_attrs, antialias);
|
||||
|
||||
if let Some(filter) = shadow {
|
||||
paint.set_image_filter(filter.clone());
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use crate::math::is_close_to;
|
||||
use crate::shapes::fills::{Fill, SolidColor};
|
||||
use skia_safe::{self as skia, Rect};
|
||||
|
||||
@@ -144,6 +145,15 @@ impl Stroke {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn aligned_rect(&self, rect: &Rect, scale: f32) -> Rect {
|
||||
let stroke_rect = self.outer_rect(rect);
|
||||
if self.kind != StrokeKind::Center {
|
||||
return stroke_rect;
|
||||
}
|
||||
|
||||
align_rect_to_half_pixel(&stroke_rect, self.width, scale)
|
||||
}
|
||||
|
||||
pub fn outer_corners(&self, corners: &Corners) -> Corners {
|
||||
let offset = match self.kind {
|
||||
StrokeKind::Center => 0.0,
|
||||
@@ -162,7 +172,6 @@ impl Stroke {
|
||||
&self,
|
||||
rect: &Rect,
|
||||
svg_attrs: Option<&SvgAttrs>,
|
||||
scale: f32,
|
||||
antialias: bool,
|
||||
) -> skia::Paint {
|
||||
let mut paint = self.fill.to_paint(rect, antialias);
|
||||
@@ -171,7 +180,7 @@ impl Stroke {
|
||||
let width = match self.kind {
|
||||
StrokeKind::Inner => self.width,
|
||||
StrokeKind::Center => self.width,
|
||||
StrokeKind::Outer => self.width + (1. / scale),
|
||||
StrokeKind::Outer => self.width,
|
||||
};
|
||||
|
||||
paint.set_stroke_width(width);
|
||||
@@ -230,10 +239,9 @@ impl Stroke {
|
||||
is_open: bool,
|
||||
rect: &Rect,
|
||||
svg_attrs: Option<&SvgAttrs>,
|
||||
scale: f32,
|
||||
antialias: bool,
|
||||
) -> skia::Paint {
|
||||
let mut paint = self.to_paint(rect, svg_attrs, scale, antialias);
|
||||
let mut paint = self.to_paint(rect, svg_attrs, antialias);
|
||||
match self.render_kind(is_open) {
|
||||
StrokeKind::Inner => {
|
||||
paint.set_stroke_width(2. * paint.stroke_width());
|
||||
@@ -254,10 +262,9 @@ impl Stroke {
|
||||
is_open: bool,
|
||||
rect: &Rect,
|
||||
svg_attrs: Option<&SvgAttrs>,
|
||||
scale: f32,
|
||||
antialias: bool,
|
||||
) -> skia::Paint {
|
||||
let mut paint = self.to_paint(rect, svg_attrs, scale, antialias);
|
||||
let mut paint = self.to_paint(rect, svg_attrs, antialias);
|
||||
match self.render_kind(is_open) {
|
||||
StrokeKind::Inner => {
|
||||
paint.set_stroke_width(2. * paint.stroke_width());
|
||||
@@ -284,6 +291,38 @@ impl Stroke {
|
||||
}
|
||||
}
|
||||
|
||||
fn align_rect_to_half_pixel(rect: &Rect, stroke_width: f32, scale: f32) -> Rect {
|
||||
if scale <= 0.0 {
|
||||
return *rect;
|
||||
}
|
||||
|
||||
let stroke_pixels = stroke_width * scale;
|
||||
let stroke_pixels_rounded = stroke_pixels.round();
|
||||
if !is_close_to(stroke_pixels, stroke_pixels_rounded) {
|
||||
return *rect;
|
||||
}
|
||||
|
||||
if (stroke_pixels_rounded as i32) % 2 == 0 {
|
||||
return *rect;
|
||||
}
|
||||
|
||||
let left_px = rect.left * scale;
|
||||
let top_px = rect.top * scale;
|
||||
let target_frac = 0.5;
|
||||
let dx_px = target_frac - (left_px - left_px.floor());
|
||||
let dy_px = target_frac - (top_px - top_px.floor());
|
||||
|
||||
if is_close_to(dx_px, 0.0) && is_close_to(dy_px, 0.0) {
|
||||
return *rect;
|
||||
}
|
||||
|
||||
Rect::from_xywh(
|
||||
rect.left + (dx_px / scale),
|
||||
rect.top + (dy_px / scale),
|
||||
rect.width(),
|
||||
rect.height(),
|
||||
)
|
||||
}
|
||||
fn cap_margin_for_cap(cap: Option<StrokeCap>, width: f32) -> f32 {
|
||||
match cap {
|
||||
Some(StrokeCap::LineArrow)
|
||||
|
||||
Reference in New Issue
Block a user