Fix outline rendering mode to draw shapes as black or white based on contrast with their artboard's color (#3724)
* contrast * Update * error corrected * changes * Fix * fix-2 * cleanup-2 * Formatting --------- Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
parent
20e12edd45
commit
ed20f4ac9b
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::renderer::{RenderParams, format_transform_matrix};
|
use crate::renderer::{RenderParams, black_or_white_for_best_contrast, format_transform_matrix};
|
||||||
use core_types::consts::{LAYER_OUTLINE_STROKE_COLOR, LAYER_OUTLINE_STROKE_WEIGHT};
|
use core_types::consts::LAYER_OUTLINE_STROKE_WEIGHT;
|
||||||
use core_types::uuid::generate_uuid;
|
use core_types::uuid::generate_uuid;
|
||||||
use glam::DAffine2;
|
use glam::DAffine2;
|
||||||
use graphic_types::vector_types::gradient::{Gradient, GradientType};
|
use graphic_types::vector_types::gradient::{Gradient, GradientType};
|
||||||
|
|
@ -167,9 +167,12 @@ impl RenderExt for PathStyle {
|
||||||
match render_mode {
|
match render_mode {
|
||||||
RenderMode::Outline => {
|
RenderMode::Outline => {
|
||||||
let fill_attribute = Fill::None.render(svg_defs, element_transform, stroke_transform, bounds, transformed_bounds, render_params);
|
let fill_attribute = Fill::None.render(svg_defs, element_transform, stroke_transform, bounds, transformed_bounds, render_params);
|
||||||
let mut outline_stroke = Stroke::new(Some(LAYER_OUTLINE_STROKE_COLOR), LAYER_OUTLINE_STROKE_WEIGHT);
|
|
||||||
|
let outline_color = black_or_white_for_best_contrast(render_params.artboard_background);
|
||||||
|
let mut outline_stroke = Stroke::new(Some(outline_color), LAYER_OUTLINE_STROKE_WEIGHT);
|
||||||
// Outline strokes should be non-scaling by default
|
// Outline strokes should be non-scaling by default
|
||||||
outline_stroke.non_scaling = true;
|
outline_stroke.non_scaling = true;
|
||||||
|
|
||||||
let stroke_attribute = outline_stroke.render(svg_defs, element_transform, stroke_transform, bounds, transformed_bounds, render_params);
|
let stroke_attribute = outline_stroke.render(svg_defs, element_transform, stroke_transform, bounds, transformed_bounds, render_params);
|
||||||
format!("{fill_attribute}{stroke_attribute}")
|
format!("{fill_attribute}{stroke_attribute}")
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -182,6 +182,7 @@ pub struct RenderParams {
|
||||||
pub alignment_parent_transform: Option<DAffine2>,
|
pub alignment_parent_transform: Option<DAffine2>,
|
||||||
pub aligned_strokes: bool,
|
pub aligned_strokes: bool,
|
||||||
pub override_paint_order: bool,
|
pub override_paint_order: bool,
|
||||||
|
pub artboard_background: Option<Color>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Hash for RenderParams {
|
impl Hash for RenderParams {
|
||||||
|
|
@ -198,6 +199,7 @@ impl Hash for RenderParams {
|
||||||
}
|
}
|
||||||
self.aligned_strokes.hash(state);
|
self.aligned_strokes.hash(state);
|
||||||
self.override_paint_order.hash(state);
|
self.override_paint_order.hash(state);
|
||||||
|
self.artboard_background.hash(state);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -235,6 +237,26 @@ fn max_scale(transform: DAffine2) -> f64 {
|
||||||
(sx + sy).sqrt()
|
(sx + sy).sqrt()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn black_or_white_for_best_contrast(background: Option<Color>) -> Color {
|
||||||
|
let Some(bg) = background else { return core_types::consts::LAYER_OUTLINE_STROKE_COLOR };
|
||||||
|
|
||||||
|
let alpha = bg.a();
|
||||||
|
|
||||||
|
// Un-premultiply, then convert to gamma sRGB
|
||||||
|
let srgb = if alpha > f32::EPSILON {
|
||||||
|
Color::from_rgbaf32_unchecked(bg.r() / alpha, bg.g() / alpha, bg.b() / alpha, alpha).to_gamma_srgb()
|
||||||
|
} else {
|
||||||
|
Color::TRANSPARENT
|
||||||
|
};
|
||||||
|
|
||||||
|
// Composite over black in sRGB space, then convert back to linear for luminance
|
||||||
|
let composited = Color::from_rgbaf32_unchecked(srgb.r() * alpha, srgb.g() * alpha, srgb.b() * alpha, 1.).to_linear_srgb();
|
||||||
|
|
||||||
|
let threshold = (1.05 * 0.05f32).sqrt() - 0.05;
|
||||||
|
|
||||||
|
if composited.luminance_srgb() > threshold { Color::BLACK } else { Color::WHITE }
|
||||||
|
}
|
||||||
|
|
||||||
pub fn to_transform(transform: DAffine2) -> usvg::Transform {
|
pub fn to_transform(transform: DAffine2) -> usvg::Transform {
|
||||||
let cols = transform.to_cols_array();
|
let cols = transform.to_cols_array();
|
||||||
usvg::Transform::from_row(cols[0] as f32, cols[1] as f32, cols[2] as f32, cols[3] as f32, cols[4] as f32, cols[5] as f32)
|
usvg::Transform::from_row(cols[0] as f32, cols[1] as f32, cols[2] as f32, cols[3] as f32, cols[4] as f32, cols[5] as f32)
|
||||||
|
|
@ -441,7 +463,9 @@ impl Render for Artboard {
|
||||||
},
|
},
|
||||||
// Artwork content
|
// Artwork content
|
||||||
|render| {
|
|render| {
|
||||||
self.content.render_svg(render, render_params);
|
let mut render_params = render_params.clone();
|
||||||
|
render_params.artboard_background = Some(self.background);
|
||||||
|
self.content.render_svg(render, &render_params);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -463,7 +487,9 @@ impl Render for Artboard {
|
||||||
}
|
}
|
||||||
// Since the content's transform is right multiplied in when rendering the content, we just need to right multiply by the artboard offset here.
|
// Since the content's transform is right multiplied in when rendering the content, we just need to right multiply by the artboard offset here.
|
||||||
let child_transform = transform * DAffine2::from_translation(self.location.as_dvec2());
|
let child_transform = transform * DAffine2::from_translation(self.location.as_dvec2());
|
||||||
self.content.render_to_vello(scene, child_transform, context, render_params);
|
let mut render_params = render_params.clone();
|
||||||
|
render_params.artboard_background = Some(self.background);
|
||||||
|
self.content.render_to_vello(scene, child_transform, context, &render_params);
|
||||||
if self.clip {
|
if self.clip {
|
||||||
scene.pop_layer();
|
scene.pop_layer();
|
||||||
}
|
}
|
||||||
|
|
@ -882,7 +908,7 @@ impl Render for Table<Vector> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_to_vello(&self, scene: &mut Scene, parent_transform: DAffine2, _context: &mut RenderContext, render_params: &RenderParams) {
|
fn render_to_vello(&self, scene: &mut Scene, parent_transform: DAffine2, _context: &mut RenderContext, render_params: &RenderParams) {
|
||||||
use core_types::consts::{LAYER_OUTLINE_STROKE_COLOR, LAYER_OUTLINE_STROKE_WEIGHT};
|
use core_types::consts::LAYER_OUTLINE_STROKE_WEIGHT;
|
||||||
use graphic_types::vector_types::vector::style::{GradientType, StrokeCap, StrokeJoin};
|
use graphic_types::vector_types::vector::style::{GradientType, StrokeCap, StrokeJoin};
|
||||||
use vello::kurbo::{Cap, Join};
|
use vello::kurbo::{Cap, Join};
|
||||||
use vello::peniko;
|
use vello::peniko;
|
||||||
|
|
@ -1066,12 +1092,9 @@ impl Render for Table<Vector> {
|
||||||
dash_pattern: Default::default(),
|
dash_pattern: Default::default(),
|
||||||
dash_offset: 0.,
|
dash_offset: 0.,
|
||||||
};
|
};
|
||||||
let outline_color = peniko::Color::new([
|
|
||||||
LAYER_OUTLINE_STROKE_COLOR.r(),
|
let outline_color = black_or_white_for_best_contrast(render_params.artboard_background);
|
||||||
LAYER_OUTLINE_STROKE_COLOR.g(),
|
let outline_color = peniko::Color::new([outline_color.r(), outline_color.g(), outline_color.b(), outline_color.a()]);
|
||||||
LAYER_OUTLINE_STROKE_COLOR.b(),
|
|
||||||
LAYER_OUTLINE_STROKE_COLOR.a(),
|
|
||||||
]);
|
|
||||||
|
|
||||||
scene.stroke(&outline_stroke, kurbo::Affine::new(element_transform.to_cols_array()), outline_color, None, &path);
|
scene.stroke(&outline_stroke, kurbo::Affine::new(element_transform.to_cols_array()), outline_color, None, &path);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue