Merge pull request #6137 from penpot/superalex-rendering-wasm-performance

🎉 Avoid rendering too small shapes
This commit is contained in:
Aitor Moreno 2025-03-27 10:22:02 +01:00 committed by GitHub
commit 3cf823ffb3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 128 additions and 65 deletions

View file

@ -306,6 +306,8 @@ impl RenderState {
s.canvas().save(); s.canvas().save();
}); });
let antialias = shape.should_use_antialias(self.get_scale());
// set clipping // set clipping
if let Some((bounds, corners, transform)) = clip_bounds { if let Some((bounds, corners, transform)) = clip_bounds {
self.surfaces.apply_mut(surface_ids, |s| { self.surfaces.apply_mut(surface_ids, |s| {
@ -315,11 +317,13 @@ impl RenderState {
if let Some(corners) = corners { if let Some(corners) = corners {
let rrect = RRect::new_rect_radii(bounds, &corners); let rrect = RRect::new_rect_radii(bounds, &corners);
self.surfaces.apply_mut(surface_ids, |s| { self.surfaces.apply_mut(surface_ids, |s| {
s.canvas().clip_rrect(rrect, skia::ClipOp::Intersect, true); s.canvas()
.clip_rrect(rrect, skia::ClipOp::Intersect, antialias);
}); });
} else { } else {
self.surfaces.apply_mut(surface_ids, |s| { self.surfaces.apply_mut(surface_ids, |s| {
s.canvas().clip_rect(bounds, skia::ClipOp::Intersect, true); s.canvas()
.clip_rect(bounds, skia::ClipOp::Intersect, antialias);
}); });
} }
@ -392,15 +396,15 @@ impl RenderState {
); );
for fill in shape.fills().rev() { for fill in shape.fills().rev() {
fills::render(self, &shape, fill); fills::render(self, &shape, fill, antialias);
} }
for stroke in shape.strokes().rev() { for stroke in shape.strokes().rev() {
strokes::render(self, &shape, stroke); strokes::render(self, &shape, stroke, antialias);
} }
shadows::render_inner_shadows(self, &shape); shadows::render_inner_shadows(self, &shape, antialias);
shadows::render_drop_shadows(self, &shape); shadows::render_drop_shadows(self, &shape, antialias);
} }
}; };
@ -588,7 +592,7 @@ impl RenderState {
if !self.render_in_progress { if !self.render_in_progress {
return Ok(()); return Ok(());
} }
let scale = self.get_scale();
let mut should_stop = false; let mut should_stop = false;
while !should_stop { while !should_stop {
if let Some(current_tile) = self.current_tile { if let Some(current_tile) = self.current_tile {
@ -665,6 +669,7 @@ impl RenderState {
} }
if !transformed_element.extrect().intersects(self.render_area) if !transformed_element.extrect().intersects(self.render_area)
|| transformed_element.hidden() || transformed_element.hidden()
|| transformed_element.visually_insignificant(scale)
{ {
debug::render_debug_shape(self, &transformed_element, false); debug::render_debug_shape(self, &transformed_element, false);
continue; continue;

View file

@ -9,6 +9,7 @@ fn draw_image_fill(
shape: &Shape, shape: &Shape,
image_fill: &ImageFill, image_fill: &ImageFill,
paint: &Paint, paint: &Paint,
antialias: bool,
) { ) {
let image = render_state.images.get(&image_fill.id()); let image = render_state.images.get(&image_fill.id());
if image.is_none() { if image.is_none() {
@ -62,15 +63,15 @@ fn draw_image_fill(
.. ..
}) => { }) => {
let rrect: RRect = RRect::new_rect_radii(container, &corners); let rrect: RRect = RRect::new_rect_radii(container, &corners);
canvas.clip_rrect(rrect, skia::ClipOp::Intersect, true); canvas.clip_rrect(rrect, skia::ClipOp::Intersect, antialias);
} }
Type::Rect(_) | Type::Frame(_) => { Type::Rect(_) | Type::Frame(_) => {
canvas.clip_rect(container, skia::ClipOp::Intersect, true); canvas.clip_rect(container, skia::ClipOp::Intersect, antialias);
} }
Type::Circle => { Type::Circle => {
let mut oval_path = skia::Path::new(); let mut oval_path = skia::Path::new();
oval_path.add_oval(container, None); oval_path.add_oval(container, None);
canvas.clip_path(&oval_path, skia::ClipOp::Intersect, true); canvas.clip_path(&oval_path, skia::ClipOp::Intersect, antialias);
} }
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() {
@ -78,13 +79,13 @@ fn draw_image_fill(
canvas.clip_path( canvas.clip_path(
&path.to_skia_path().transform(&path_transform), &path.to_skia_path().transform(&path_transform),
skia::ClipOp::Intersect, skia::ClipOp::Intersect,
true, antialias,
); );
} }
} }
} }
Type::SVGRaw(_) => { Type::SVGRaw(_) => {
canvas.clip_rect(container, skia::ClipOp::Intersect, true); canvas.clip_rect(container, skia::ClipOp::Intersect, antialias);
} }
Type::Group(_) => unreachable!("A group should not have fills"), Type::Group(_) => unreachable!("A group should not have fills"),
Type::Text(_) => unimplemented!("TODO"), Type::Text(_) => unimplemented!("TODO"),
@ -108,12 +109,12 @@ fn draw_image_fill(
/** /**
* This SHOULD be the only public function in this module. * This SHOULD be the only public function in this module.
*/ */
pub fn render(render_state: &mut RenderState, shape: &Shape, fill: &Fill) { pub fn render(render_state: &mut RenderState, shape: &Shape, fill: &Fill, antialias: bool) {
let paint = &fill.to_paint(&shape.selrect); let paint = &fill.to_paint(&shape.selrect, antialias);
match (fill, &shape.shape_type) { match (fill, &shape.shape_type) {
(Fill::Image(image_fill), _) => { (Fill::Image(image_fill), _) => {
draw_image_fill(render_state, shape, image_fill, paint); draw_image_fill(render_state, shape, image_fill, paint, antialias);
} }
(_, Type::Rect(_) | Type::Frame(_)) => { (_, Type::Rect(_) | Type::Frame(_)) => {
render_state render_state

View file

@ -3,27 +3,37 @@ use crate::shapes::{Shadow, Shape, Type};
use skia_safe::{self as skia, Paint}; use skia_safe::{self as skia, Paint};
// Drop Shadows // Drop Shadows
pub fn render_drop_shadows(render_state: &mut RenderState, shape: &Shape) { pub fn render_drop_shadows(render_state: &mut RenderState, shape: &Shape, antialias: bool) {
if shape.has_fills() { if shape.has_fills() {
for shadow in shape.drop_shadows().rev().filter(|s| !s.hidden()) { for shadow in shape.drop_shadows().rev().filter(|s| !s.hidden()) {
render_fill_drop_shadow(render_state, &shape, &shadow); render_fill_drop_shadow(render_state, &shape, &shadow, antialias);
} }
} else { } else {
let scale = render_state.get_scale(); let scale = render_state.get_scale();
for shadow in shape.drop_shadows().rev().filter(|s| !s.hidden()) { for shadow in shape.drop_shadows().rev().filter(|s| !s.hidden()) {
render_stroke_drop_shadow(render_state, &shadow, scale); render_stroke_drop_shadow(render_state, &shadow, scale, antialias);
} }
} }
} }
fn render_fill_drop_shadow(render_state: &mut RenderState, shape: &Shape, shadow: &Shadow) { fn render_fill_drop_shadow(
let paint = &shadow.get_drop_shadow_paint(); render_state: &mut RenderState,
shape: &Shape,
shadow: &Shadow,
antialias: bool,
) {
let paint = &shadow.get_drop_shadow_paint(antialias);
render_shadow_paint(render_state, shape, paint, SurfaceId::DropShadows); render_shadow_paint(render_state, shape, paint, SurfaceId::DropShadows);
} }
// TODO: Stroke shadows // TODO: Stroke shadows
fn render_stroke_drop_shadow(render_state: &mut RenderState, shadow: &Shadow, scale: f32) { fn render_stroke_drop_shadow(
let shadow_paint = &shadow.to_paint(scale); render_state: &mut RenderState,
shadow: &Shadow,
scale: f32,
antialias: bool,
) {
let shadow_paint = &shadow.to_paint(scale, antialias);
render_state render_state
.surfaces .surfaces
@ -42,27 +52,37 @@ fn render_stroke_drop_shadow(render_state: &mut RenderState, shadow: &Shadow, sc
} }
// Inner Shadows // Inner Shadows
pub fn render_inner_shadows(render_state: &mut RenderState, shape: &Shape) { pub fn render_inner_shadows(render_state: &mut RenderState, shape: &Shape, antialias: bool) {
if shape.has_fills() { if shape.has_fills() {
for shadow in shape.inner_shadows().rev().filter(|s| !s.hidden()) { for shadow in shape.inner_shadows().rev().filter(|s| !s.hidden()) {
render_fill_inner_shadow(render_state, &shape, &shadow); render_fill_inner_shadow(render_state, &shape, &shadow, antialias);
} }
} else { } else {
let scale = render_state.get_scale(); let scale = render_state.get_scale();
for shadow in shape.inner_shadows().rev().filter(|s| !s.hidden()) { for shadow in shape.inner_shadows().rev().filter(|s| !s.hidden()) {
render_stroke_inner_shadow(render_state, &shadow, scale); render_stroke_inner_shadow(render_state, &shadow, scale, antialias);
} }
} }
} }
fn render_fill_inner_shadow(render_state: &mut RenderState, shape: &Shape, shadow: &Shadow) { fn render_fill_inner_shadow(
let paint = &shadow.get_inner_shadow_paint(); render_state: &mut RenderState,
shape: &Shape,
shadow: &Shadow,
antialias: bool,
) {
let paint = &shadow.get_inner_shadow_paint(antialias);
render_shadow_paint(render_state, shape, paint, SurfaceId::InnerShadows); render_shadow_paint(render_state, shape, paint, SurfaceId::InnerShadows);
} }
// TODO: Stroke shadows // TODO: Stroke shadows
fn render_stroke_inner_shadow(render_state: &mut RenderState, shadow: &Shadow, scale: f32) { fn render_stroke_inner_shadow(
let shadow_paint = &shadow.to_paint(scale); render_state: &mut RenderState,
shadow: &Shadow,
scale: f32,
antialias: bool,
) {
let shadow_paint = &shadow.to_paint(scale, antialias);
render_state render_state
.surfaces .surfaces

View file

@ -15,13 +15,14 @@ fn draw_stroke_on_rect(
corners: &Option<Corners>, corners: &Option<Corners>,
svg_attrs: &HashMap<String, String>, svg_attrs: &HashMap<String, String>,
scale: f32, scale: f32,
antialias: bool,
) { ) {
// Draw the different kind of strokes for a rect is straightforward, we just need apply a stroke to: // Draw the different kind of strokes for a rect is straightforward, we just need apply a stroke to:
// - The same rect if it's a center stroke // - The same rect if it's a center stroke
// - A bigger rect if it's an outer stroke // - A bigger rect if it's an outer stroke
// - A smaller rect if it's an outer stroke // - A smaller rect if it's an outer stroke
let stroke_rect = stroke.outer_rect(rect); let stroke_rect = stroke.outer_rect(rect);
let paint = stroke.to_paint(selrect, svg_attrs, scale); let paint = stroke.to_paint(selrect, svg_attrs, scale, antialias);
match corners { match corners {
Some(radii) => { Some(radii) => {
@ -42,13 +43,17 @@ fn draw_stroke_on_circle(
selrect: &Rect, selrect: &Rect,
svg_attrs: &HashMap<String, String>, svg_attrs: &HashMap<String, String>,
scale: f32, scale: f32,
antialias: bool,
) { ) {
// Draw the different kind of strokes for an oval is straightforward, we just need apply a stroke to: // Draw the different kind of strokes for an oval is straightforward, we just need apply a stroke to:
// - The same oval if it's a center stroke // - The same oval if it's a center stroke
// - A bigger oval if it's an outer stroke // - A bigger oval if it's an outer stroke
// - A smaller oval if it's an outer stroke // - A smaller oval if it's an outer stroke
let stroke_rect = stroke.outer_rect(rect); let stroke_rect = stroke.outer_rect(rect);
canvas.draw_oval(&stroke_rect, &stroke.to_paint(selrect, svg_attrs, scale)); canvas.draw_oval(
&stroke_rect,
&stroke.to_paint(selrect, svg_attrs, scale, antialias),
);
} }
fn draw_stroke_on_path( fn draw_stroke_on_path(
@ -59,18 +64,20 @@ fn draw_stroke_on_path(
path_transform: Option<&Matrix>, path_transform: Option<&Matrix>,
svg_attrs: &HashMap<String, String>, svg_attrs: &HashMap<String, String>,
scale: f32, scale: f32,
antialias: bool,
) { ) {
let mut skia_path = path.to_skia_path(); let mut skia_path = path.to_skia_path();
skia_path.transform(path_transform.unwrap()); skia_path.transform(path_transform.unwrap());
let is_open = path.is_open(); let is_open = path.is_open();
let paint_stroke = stroke.to_stroked_paint(is_open, selrect, svg_attrs, scale); let paint_stroke = stroke.to_stroked_paint(is_open, selrect, svg_attrs, scale, antialias);
// Draw the different kind of strokes for a path requires different strategies: // Draw the different kind of strokes for a path requires different strategies:
match stroke.render_kind(is_open) { match stroke.render_kind(is_open) {
// For inner stroke we draw a center stroke (with double width) and clip to the original path (that way the extra outer stroke is removed) // For inner stroke we draw a center stroke (with double width) and clip to the original path (that way the extra outer stroke is removed)
StrokeKind::InnerStroke => { StrokeKind::InnerStroke => {
canvas.save(); // As we are using clear for surfaces we use save and restore here to still be able to clean the full surface canvas.save(); // As we are using clear for surfaces we use save and restore here to still be able to clean the full surface
canvas.clip_path(&skia_path, skia::ClipOp::Intersect, true); canvas.clip_path(&skia_path, skia::ClipOp::Intersect, antialias);
canvas.draw_path(&skia_path, &paint_stroke); canvas.draw_path(&skia_path, &paint_stroke);
canvas.restore(); canvas.restore();
} }
@ -82,7 +89,7 @@ fn draw_stroke_on_path(
StrokeKind::OuterStroke => { StrokeKind::OuterStroke => {
let mut paint = skia::Paint::default(); let mut paint = skia::Paint::default();
paint.set_blend_mode(skia::BlendMode::SrcOver); paint.set_blend_mode(skia::BlendMode::SrcOver);
paint.set_anti_alias(true); paint.set_anti_alias(antialias);
let layer_rec = skia::canvas::SaveLayerRec::default().paint(&paint); let layer_rec = skia::canvas::SaveLayerRec::default().paint(&paint);
canvas.save_layer(&layer_rec); canvas.save_layer(&layer_rec);
@ -90,7 +97,7 @@ fn draw_stroke_on_path(
let mut clear_paint = skia::Paint::default(); let mut clear_paint = skia::Paint::default();
clear_paint.set_blend_mode(skia::BlendMode::Clear); clear_paint.set_blend_mode(skia::BlendMode::Clear);
clear_paint.set_anti_alias(true); clear_paint.set_anti_alias(antialias);
canvas.draw_path(&skia_path, &clear_paint); canvas.draw_path(&skia_path, &clear_paint);
canvas.restore(); canvas.restore();
@ -105,6 +112,7 @@ fn draw_stroke_on_path(
is_open, is_open,
svg_attrs, svg_attrs,
scale, scale,
antialias,
); );
} }
@ -155,6 +163,7 @@ fn handle_stroke_caps(
is_open: bool, is_open: bool,
svg_attrs: &HashMap<String, String>, svg_attrs: &HashMap<String, String>,
scale: f32, scale: f32,
antialias: bool,
) { ) {
let points_count = path.count_points(); let points_count = path.count_points();
let mut points = vec![Point::default(); points_count]; let mut points = vec![Point::default(); points_count];
@ -165,7 +174,8 @@ fn handle_stroke_caps(
let first_point = points.first().unwrap(); let first_point = points.first().unwrap();
let last_point = points.last().unwrap(); let last_point = points.last().unwrap();
let mut paint_stroke = stroke.to_stroked_paint(is_open, selrect, svg_attrs, scale); let mut paint_stroke =
stroke.to_stroked_paint(is_open, selrect, svg_attrs, scale, antialias);
handle_stroke_cap( handle_stroke_cap(
canvas, canvas,
@ -325,6 +335,7 @@ fn draw_image_stroke_in_container(
shape: &Shape, shape: &Shape,
stroke: &Stroke, stroke: &Stroke,
image_fill: &ImageFill, image_fill: &ImageFill,
antialias: bool,
) { ) {
let image = render_state.images.get(&image_fill.id()); let image = render_state.images.get(&image_fill.id());
if image.is_none() { if image.is_none() {
@ -341,7 +352,7 @@ fn draw_image_stroke_in_container(
// Save canvas and layer state // Save canvas and layer state
let mut pb = skia::Paint::default(); let mut pb = skia::Paint::default();
pb.set_blend_mode(skia::BlendMode::SrcOver); pb.set_blend_mode(skia::BlendMode::SrcOver);
pb.set_anti_alias(true); pb.set_anti_alias(antialias);
let layer_rec = skia::canvas::SaveLayerRec::default().paint(&pb); let layer_rec = skia::canvas::SaveLayerRec::default().paint(&pb);
canvas.save_layer(&layer_rec); canvas.save_layer(&layer_rec);
@ -359,11 +370,18 @@ fn draw_image_stroke_in_container(
&shape_type.corners(), &shape_type.corners(),
svg_attrs, svg_attrs,
scale, scale,
antialias,
); );
} }
Type::Circle => { Type::Circle => draw_stroke_on_circle(
draw_stroke_on_circle(canvas, stroke, container, &outer_rect, svg_attrs, scale) canvas,
} stroke,
container,
&outer_rect,
svg_attrs,
scale,
antialias,
),
shape_type @ (Type::Path(_) | Type::Bool(_)) => { shape_type @ (Type::Path(_) | Type::Bool(_)) => {
if let Some(p) = shape_type.path() { if let Some(p) = shape_type.path() {
@ -373,15 +391,16 @@ fn draw_image_stroke_in_container(
let stroke_kind = stroke.render_kind(p.is_open()); let stroke_kind = stroke.render_kind(p.is_open());
match stroke_kind { match stroke_kind {
StrokeKind::InnerStroke => { StrokeKind::InnerStroke => {
canvas.clip_path(&path, skia::ClipOp::Intersect, true); canvas.clip_path(&path, skia::ClipOp::Intersect, antialias);
} }
StrokeKind::CenterStroke => {} StrokeKind::CenterStroke => {}
StrokeKind::OuterStroke => { StrokeKind::OuterStroke => {
canvas.clip_path(&path, skia::ClipOp::Difference, true); canvas.clip_path(&path, skia::ClipOp::Difference, antialias);
} }
} }
let is_open = p.is_open(); let is_open = p.is_open();
let mut paint = stroke.to_stroked_paint(is_open, &outer_rect, svg_attrs, scale); let mut paint =
stroke.to_stroked_paint(is_open, &outer_rect, svg_attrs, scale, antialias);
canvas.draw_path(&path, &paint); canvas.draw_path(&path, &paint);
if stroke.render_kind(is_open) == StrokeKind::OuterStroke { if stroke.render_kind(is_open) == StrokeKind::OuterStroke {
// Small extra inner stroke to overlap with the fill // Small extra inner stroke to overlap with the fill
@ -397,6 +416,7 @@ fn draw_image_stroke_in_container(
is_open, is_open,
svg_attrs, svg_attrs,
scale, scale,
antialias,
); );
canvas.restore(); canvas.restore();
} }
@ -410,11 +430,11 @@ fn draw_image_stroke_in_container(
// stroke over the image. // stroke over the image.
let mut image_paint = skia::Paint::default(); let mut image_paint = skia::Paint::default();
image_paint.set_blend_mode(skia::BlendMode::SrcIn); image_paint.set_blend_mode(skia::BlendMode::SrcIn);
image_paint.set_anti_alias(true); image_paint.set_anti_alias(antialias);
// Compute scaled rect and clip to it // Compute scaled rect and clip to it
let dest_rect = calculate_scaled_rect(size, container, stroke.delta()); let dest_rect = calculate_scaled_rect(size, container, stroke.delta());
canvas.clip_rect(dest_rect, skia::ClipOp::Intersect, true); canvas.clip_rect(dest_rect, skia::ClipOp::Intersect, antialias);
canvas.draw_image_rect(image.unwrap(), None, dest_rect, &image_paint); canvas.draw_image_rect(image.unwrap(), None, dest_rect, &image_paint);
// Clear outer stroke for paths if necessary. When adding an outer stroke we need to empty the stroke added too in the inner area. // Clear outer stroke for paths if necessary. When adding an outer stroke we need to empty the stroke added too in the inner area.
@ -424,7 +444,7 @@ fn draw_image_stroke_in_container(
path.transform(&path_transform.unwrap()); path.transform(&path_transform.unwrap());
let mut clear_paint = skia::Paint::default(); let mut clear_paint = skia::Paint::default();
clear_paint.set_blend_mode(skia::BlendMode::Clear); clear_paint.set_blend_mode(skia::BlendMode::Clear);
clear_paint.set_anti_alias(true); clear_paint.set_anti_alias(antialias);
canvas.draw_path(&path, &clear_paint); canvas.draw_path(&path, &clear_paint);
} }
} }
@ -436,7 +456,7 @@ fn draw_image_stroke_in_container(
/** /**
* This SHOULD be the only public function in this module. * This SHOULD be the only public function in this module.
*/ */
pub fn render(render_state: &mut RenderState, shape: &Shape, stroke: &Stroke) { pub fn render(render_state: &mut RenderState, shape: &Shape, stroke: &Stroke, antialias: bool) {
let scale = render_state.get_scale(); let scale = render_state.get_scale();
let canvas = render_state.surfaces.canvas(SurfaceId::Strokes); let canvas = render_state.surfaces.canvas(SurfaceId::Strokes);
let selrect = shape.selrect; let selrect = shape.selrect;
@ -444,7 +464,7 @@ pub fn render(render_state: &mut RenderState, shape: &Shape, stroke: &Stroke) {
let svg_attrs = &shape.svg_attrs; let svg_attrs = &shape.svg_attrs;
if let Fill::Image(image_fill) = &stroke.fill { if let Fill::Image(image_fill) = &stroke.fill {
draw_image_stroke_in_container(render_state, shape, stroke, image_fill); draw_image_stroke_in_container(render_state, shape, stroke, image_fill, antialias);
} else { } else {
match &shape.shape_type { match &shape.shape_type {
shape_type @ (Type::Rect(_) | Type::Frame(_)) => { shape_type @ (Type::Rect(_) | Type::Frame(_)) => {
@ -456,11 +476,12 @@ pub fn render(render_state: &mut RenderState, shape: &Shape, stroke: &Stroke) {
&shape_type.corners(), &shape_type.corners(),
svg_attrs, svg_attrs,
scale, scale,
antialias,
); );
} }
Type::Circle => { Type::Circle => draw_stroke_on_circle(
draw_stroke_on_circle(canvas, stroke, &selrect, &selrect, svg_attrs, scale) canvas, stroke, &selrect, &selrect, svg_attrs, scale, antialias,
} ),
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() {
draw_stroke_on_path( draw_stroke_on_path(
@ -471,6 +492,7 @@ pub fn render(render_state: &mut RenderState, shape: &Shape, stroke: &Stroke) {
path_transform.as_ref(), path_transform.as_ref(),
svg_attrs, svg_attrs,
scale, scale,
antialias,
); );
} }
} }

View file

@ -42,6 +42,9 @@ pub use transform::*;
use crate::math; use crate::math;
use crate::math::{Bounds, Matrix, Point}; use crate::math::{Bounds, Matrix, Point};
const MIN_VISIBLE_SIZE: f32 = 2.0;
const ANTIALIAS_THRESHOLD: f32 = 15.0;
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub enum Type { pub enum Type {
Frame(Frame), Frame(Frame),
@ -572,6 +575,16 @@ impl Shape {
self.hidden self.hidden
} }
pub fn visually_insignificant(&self, scale: f32) -> bool {
self.selrect.width() * scale < MIN_VISIBLE_SIZE
|| self.selrect.height() * scale < MIN_VISIBLE_SIZE
}
pub fn should_use_antialias(&self, scale: f32) -> bool {
self.selrect.width() * scale > ANTIALIAS_THRESHOLD
|| self.selrect.height() * scale > ANTIALIAS_THRESHOLD
}
// TODO: Maybe store this inside the shape // TODO: Maybe store this inside the shape
pub fn bounds(&self) -> Bounds { pub fn bounds(&self) -> Bounds {
let mut bounds = Bounds::new( let mut bounds = Bounds::new(

View file

@ -158,13 +158,13 @@ impl Fill {
}) })
} }
pub fn to_paint(&self, rect: &Rect) -> skia::Paint { pub fn to_paint(&self, rect: &Rect, anti_alias: bool) -> skia::Paint {
match self { match self {
Self::Solid(color) => { Self::Solid(color) => {
let mut p = skia::Paint::default(); let mut p = skia::Paint::default();
p.set_color(*color); p.set_color(*color);
p.set_style(skia::PaintStyle::Fill); p.set_style(skia::PaintStyle::Fill);
p.set_anti_alias(true); p.set_anti_alias(anti_alias);
p.set_blend_mode(skia::BlendMode::SrcOver); p.set_blend_mode(skia::BlendMode::SrcOver);
p p
} }
@ -173,7 +173,7 @@ impl Fill {
p.set_shader(gradient.to_linear_shader(&rect)); p.set_shader(gradient.to_linear_shader(&rect));
p.set_alpha((gradient.opacity * 255.) as u8); p.set_alpha((gradient.opacity * 255.) as u8);
p.set_style(skia::PaintStyle::Fill); p.set_style(skia::PaintStyle::Fill);
p.set_anti_alias(true); p.set_anti_alias(anti_alias);
p.set_blend_mode(skia::BlendMode::SrcOver); p.set_blend_mode(skia::BlendMode::SrcOver);
p p
} }
@ -182,14 +182,14 @@ impl Fill {
p.set_shader(gradient.to_radial_shader(&rect)); p.set_shader(gradient.to_radial_shader(&rect));
p.set_alpha((gradient.opacity * 255.) as u8); p.set_alpha((gradient.opacity * 255.) as u8);
p.set_style(skia::PaintStyle::Fill); p.set_style(skia::PaintStyle::Fill);
p.set_anti_alias(true); p.set_anti_alias(anti_alias);
p.set_blend_mode(skia::BlendMode::SrcOver); p.set_blend_mode(skia::BlendMode::SrcOver);
p p
} }
Self::Image(image_fill) => { Self::Image(image_fill) => {
let mut p = skia::Paint::default(); let mut p = skia::Paint::default();
p.set_style(skia::PaintStyle::Fill); p.set_style(skia::PaintStyle::Fill);
p.set_anti_alias(true); p.set_anti_alias(anti_alias);
p.set_blend_mode(skia::BlendMode::SrcOver); p.set_blend_mode(skia::BlendMode::SrcOver);
p.set_alpha(image_fill.opacity); p.set_alpha(image_fill.opacity);
p p

View file

@ -62,7 +62,7 @@ impl Shadow {
self.hidden self.hidden
} }
pub fn to_paint(&self, scale: f32) -> skia::Paint { pub fn to_paint(&self, scale: f32, antialias: bool) -> skia::Paint {
let mut paint = skia::Paint::default(); let mut paint = skia::Paint::default();
let image_filter = match self.style { let image_filter = match self.style {
@ -71,7 +71,7 @@ impl Shadow {
}; };
paint.set_image_filter(image_filter); paint.set_image_filter(image_filter);
paint.set_anti_alias(true); paint.set_anti_alias(antialias);
paint paint
} }
@ -128,13 +128,13 @@ impl Shadow {
// New methods for Drop Shadows // New methods for Drop Shadows
pub fn get_drop_shadow_paint(&self) -> skia::Paint { pub fn get_drop_shadow_paint(&self, antialias: bool) -> skia::Paint {
let mut paint = skia::Paint::default(); let mut paint = skia::Paint::default();
let image_filter = self.get_drop_shadow_filter(); let image_filter = self.get_drop_shadow_filter();
paint.set_image_filter(image_filter); paint.set_image_filter(image_filter);
paint.set_anti_alias(true); paint.set_anti_alias(antialias);
paint paint
} }
@ -158,13 +158,13 @@ impl Shadow {
// New methods for Inner Shadows // New methods for Inner Shadows
pub fn get_inner_shadow_paint(&self) -> skia::Paint { pub fn get_inner_shadow_paint(&self, antialias: bool) -> skia::Paint {
let mut paint = skia::Paint::default(); let mut paint = skia::Paint::default();
let image_filter = self.get_inner_shadow_filter(); let image_filter = self.get_inner_shadow_filter();
paint.set_image_filter(image_filter); paint.set_image_filter(image_filter);
paint.set_anti_alias(true); paint.set_anti_alias(antialias);
paint paint
} }

View file

@ -160,8 +160,9 @@ impl Stroke {
rect: &Rect, rect: &Rect,
svg_attrs: &HashMap<String, String>, svg_attrs: &HashMap<String, String>,
scale: f32, scale: f32,
antialias: bool,
) -> skia::Paint { ) -> skia::Paint {
let mut paint = self.fill.to_paint(rect); let mut paint = self.fill.to_paint(rect, antialias);
paint.set_style(skia::PaintStyle::Stroke); paint.set_style(skia::PaintStyle::Stroke);
let width = match self.kind { let width = match self.kind {
@ -171,7 +172,7 @@ impl Stroke {
}; };
paint.set_stroke_width(width); paint.set_stroke_width(width);
paint.set_anti_alias(true); paint.set_anti_alias(antialias);
if let Some("round") = svg_attrs.get("stroke-linecap").map(String::as_str) { if let Some("round") = svg_attrs.get("stroke-linecap").map(String::as_str) {
paint.set_stroke_cap(skia::paint::Cap::Round); paint.set_stroke_cap(skia::paint::Cap::Round);
@ -225,8 +226,9 @@ impl Stroke {
rect: &Rect, rect: &Rect,
svg_attrs: &HashMap<String, String>, svg_attrs: &HashMap<String, String>,
scale: f32, scale: f32,
antialias: bool,
) -> skia::Paint { ) -> skia::Paint {
let mut paint = self.to_paint(rect, svg_attrs, scale); let mut paint = self.to_paint(rect, svg_attrs, scale, antialias);
match self.render_kind(is_open) { match self.render_kind(is_open) {
StrokeKind::InnerStroke => { StrokeKind::InnerStroke => {
paint.set_stroke_width(2. * paint.stroke_width()); paint.set_stroke_width(2. * paint.stroke_width());