From e35922f081877755bcc13df06d7a7d4872ab46f5 Mon Sep 17 00:00:00 2001 From: Adoo Date: Tue, 19 Nov 2024 19:32:05 +0800 Subject: [PATCH] =?UTF-8?q?feat(painter):=20=F0=9F=8E=B8=20SVG=20support?= =?UTF-8?q?=20switch=20default=20color?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 1 + core/src/builtin_widgets/svg.rs | 8 +- core/src/builtin_widgets/theme/icon_theme.rs | 4 +- gpu/src/gpu_backend.rs | 11 +- macros/src/lib.rs | 40 ++++- painter/src/color.rs | 4 +- painter/src/painter.rs | 22 ++- painter/src/svg.rs | 163 ++++++++++++++++-- painter/src/text/svg_glyph_cache.rs | 2 +- .../fix_draw_svg_not_apply_alpha_wgpu.png | Bin 636 -> 848 bytes .../svg/tests/svgs_smoke_wgpu.png | Bin 1107 -> 1733 bytes .../tests/checked_with_default_by_wgpu.png | Bin 791 -> 646 bytes .../tests/checked_with_material_by_wgpu.png | Bin 791 -> 646 bytes .../indeterminate_with_default_by_wgpu.png | Bin 593 -> 454 bytes .../indeterminate_with_material_by_wgpu.png | Bin 593 -> 454 bytes .../tests/unchecked_with_default_by_wgpu.png | Bin 551 -> 414 bytes .../tests/unchecked_with_material_by_wgpu.png | Bin 551 -> 414 bytes .../icon/tests/icons_with_default_by_wgpu.png | Bin 3093 -> 1349 bytes .../tests/icons_with_material_by_wgpu.png | Bin 3568 -> 1882 bytes tests/include_svg_test.rs | 6 +- widgets/src/icon.rs | 12 +- widgets/src/path.rs | 2 +- 22 files changed, 228 insertions(+), 47 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 71991e5d0..91a18ecae 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -32,6 +32,7 @@ Please only add new entries below the [Unreleased](#unreleased---releasedate) he - **core**: Added `QueryId` as a replacement for `TypeId` to facilitate querying types by Provider across different binaries. (#656 @M-Adoo) - **core**: Added `OverrideClass` to override a single class within a subtree. (#657 @M-Adoo) - **widgets**: Added `LinearProgress` and `SpinnerProgress` widgets along with their respective material themes. (#630 @wjian23 @M-Adoo) +- **painter**: SVG now supports switching the default color, allowing for icon color changes. (#661 @M-Adoo) ### Changed diff --git a/core/src/builtin_widgets/svg.rs b/core/src/builtin_widgets/svg.rs index de3afa681..06074c449 100644 --- a/core/src/builtin_widgets/svg.rs +++ b/core/src/builtin_widgets/svg.rs @@ -2,7 +2,7 @@ use crate::prelude::*; impl Render for Svg { #[inline] - fn perform_layout(&self, clamp: BoxClamp, _: &mut LayoutCtx) -> Size { clamp.clamp(self.size) } + fn perform_layout(&self, clamp: BoxClamp, _: &mut LayoutCtx) -> Size { clamp.clamp(self.size()) } fn paint(&self, ctx: &mut PaintingCtx) { let painter = ctx.painter(); @@ -17,7 +17,7 @@ pub mod named_svgs { const DEFAULT_SVG_KEY: &str = "__RIRBIR_DEFAULT_SVG__"; static SVGS: LazyLock>> = LazyLock::new(|| { - let svg = include_crate_svg!("src/builtin_widgets/default_named.svg"); + let svg = include_crate_svg!("src/builtin_widgets/default_named.svg", true, false); let mut set = ahash::AHashMap::new(); set.insert(DEFAULT_SVG_KEY, svg); Mutex::new(set) @@ -51,8 +51,8 @@ mod tests { fn svgs_smoke() -> Painter { named_svgs::register( "test::add", - Svg::parse_from_bytes( - r#""#.as_bytes(), + Svg::parse_from_bytes(r#""#.as_bytes(), + true, false ).unwrap(), ); let mut painter = Painter::new(Rect::from_size(Size::new(128., 64.))); diff --git a/core/src/builtin_widgets/theme/icon_theme.rs b/core/src/builtin_widgets/theme/icon_theme.rs index 1de01aba9..1326b5988 100644 --- a/core/src/builtin_widgets/theme/icon_theme.rs +++ b/core/src/builtin_widgets/theme/icon_theme.rs @@ -46,7 +46,7 @@ macro_rules! define_named_svg { macro_rules! fill_svgs { ($theme: expr, $($name: path: $path: literal),+) => { $( - let icon = Resource::new(include_crate_svg!($path)); + let icon = Resource::new(include_crate_svg!($path, true, false)); $theme.set_svg($name, icon); )+ }; @@ -70,7 +70,7 @@ impl Compose for NamedSvg { impl IconTheme { pub fn new(icon_size: IconSize) -> Self { - let svg = include_crate_svg!("src/builtin_widgets/default_named.svg"); + let svg = include_crate_svg!("src/builtin_widgets/default_named.svg", true, false); let miss_icon = Resource::new(svg); let mut icons = HashMap::<_, _, ahash::RandomState>::default(); icons.insert(MISS_ICON, miss_icon); diff --git a/gpu/src/gpu_backend.rs b/gpu/src/gpu_backend.rs index 447a8b08c..4c893e0e5 100644 --- a/gpu/src/gpu_backend.rs +++ b/gpu/src/gpu_backend.rs @@ -759,8 +759,12 @@ mod tests { painter_backend_eq_image_test!(draw_svg_gradient, comparison = 0.0025); fn draw_svg_gradient() -> Painter { let mut painter = painter(Size::new(64., 64.)); - let svg = - Svg::parse_from_bytes(include_bytes!("../../tests/assets/fill_with_gradient.svg")).unwrap(); + let svg = Svg::parse_from_bytes( + include_bytes!("../../tests/assets/fill_with_gradient.svg"), + true, + false, + ) + .unwrap(); painter.draw_svg(&svg); painter @@ -805,8 +809,7 @@ mod tests { }) .collect(); - let svg = Svg { size: Size::new(512., 512.), commands: Resource::new(commands) }; - painter.draw_svg(&svg); + painter.draw_bundle_commands(Rect::from_size(Size::new(512., 512.)), Resource::new(commands)); painter } painter_backend_eq_image_test!(draw_bundle_svg, comparison = 0.001); diff --git a/macros/src/lib.rs b/macros/src/lib.rs index f8045892f..fd5b2bdbb 100644 --- a/macros/src/lib.rs +++ b/macros/src/lib.rs @@ -248,10 +248,11 @@ pub fn part_writer(input: TokenStream) -> TokenStream { /// This macro returns an expression of type `Svg`. #[proc_macro] pub fn include_crate_svg(input: TokenStream) -> TokenStream { - let file = parse_macro_input! { input as syn::LitStr }; + let IncludeSvgArgs { path, inherit_fill, inherit_stroke } = + parse_macro_input! { input as IncludeSvgArgs }; let dir = std::env::var("CARGO_MANIFEST_DIR").unwrap(); - let path = std::path::Path::new(&dir).join(file.value()); - include_svg_from_path(path) + let path = std::path::Path::new(&dir).join(path); + include_svg_from_path(path, inherit_fill, inherit_stroke) } /// Includes an SVG file as an `Svg`. @@ -265,7 +266,8 @@ pub fn include_crate_svg(input: TokenStream) -> TokenStream { #[cfg(feature = "nightly")] #[proc_macro] pub fn include_svg(input: TokenStream) -> TokenStream { - let rf = parse_macro_input! { input as syn::LitStr }; + let IncludeSvgArgs { path, inherit_fill, inherit_stroke } = + parse_macro_input! { input as IncludeSvgArgs }; let mut span = proc_macro::Span::call_site(); while let Some(p) = span.parent() { @@ -273,14 +275,16 @@ pub fn include_svg(input: TokenStream) -> TokenStream { } let mut file = span.source_file().path(); file.pop(); - file.push(rf.value()); + file.push(path); - include_svg_from_path(file) + include_svg_from_path(file, inherit_fill, inherit_stroke) } -fn include_svg_from_path(path: std::path::PathBuf) -> TokenStream { - let encoded_bytes = - ribir_painter::Svg::open(path.as_path()).and_then(|reader| reader.serialize()); +fn include_svg_from_path( + path: std::path::PathBuf, inherit_fill: bool, inherit_stroke: bool, +) -> TokenStream { + let encoded_bytes = ribir_painter::Svg::open(path.as_path(), inherit_fill, inherit_stroke) + .and_then(|reader| reader.serialize()); match encoded_bytes { Ok(data) => quote! { Svg::deserialize(#data).unwrap() @@ -292,3 +296,21 @@ fn include_svg_from_path(path: std::path::PathBuf) -> TokenStream { } } } + +struct IncludeSvgArgs { + path: String, + inherit_fill: bool, + inherit_stroke: bool, +} + +impl syn::parse::Parse for IncludeSvgArgs { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + let path = input.parse::()?.value(); + input.parse::()?; + let inherit_fill = input.parse::()?.value; + input.parse::()?; + let inherit_stroke = input.parse::()?.value; + + Ok(IncludeSvgArgs { path, inherit_fill, inherit_stroke }) + } +} diff --git a/painter/src/color.rs b/painter/src/color.rs index 8714bd460..96f8ca64c 100644 --- a/painter/src/color.rs +++ b/painter/src/color.rs @@ -71,13 +71,13 @@ impl Color { } #[inline] - pub fn from_u32(rgba: u32) -> Self { + pub const fn from_u32(rgba: u32) -> Self { let bytes = rgba.to_be_bytes(); Self { red: bytes[0], green: bytes[1], blue: bytes[2], alpha: bytes[3] } } #[inline] - pub fn into_u32(self) -> u32 { + pub const fn into_u32(self) -> u32 { let Self { red, green, blue, alpha } = self; u32::from_be_bytes([red, green, blue, alpha]) } diff --git a/painter/src/painter.rs b/painter/src/painter.rs index ffef16a8f..0a27f28a1 100644 --- a/painter/src/painter.rs +++ b/painter/src/painter.rs @@ -569,6 +569,7 @@ impl Painter { pub fn draw_svg(&mut self, svg: &Svg) -> &mut Self { invisible_return!(self); + let commands = svg.commands(self.fill_brush(), self.stroke_brush()); // For a large number of path commands (more than 16), bundle them // together as a single resource. This allows the backend to cache @@ -576,11 +577,11 @@ impl Painter { // For a small number of path commands (less than 16), store them // individually as multiple resources. This means the backend doesn't // need to perform a single draw operation for an SVG. - if svg.commands.len() <= 16 { + if commands.len() <= 16 { let transform = *self.transform(); let alpha = self.alpha(); - for cmd in svg.commands.iter() { + for cmd in commands.iter() { let cmd = match cmd.clone() { PaintCommand::Path(mut path) => { path.transform(&transform); @@ -600,8 +601,8 @@ impl Painter { self.commands.push(cmd); } } else { - let rect = Rect::from_size(svg.size); - self.draw_bundle_commands(rect, svg.commands.clone()); + let rect = Rect::from_size(svg.size()); + self.draw_bundle_commands(rect, commands.clone()); } self @@ -662,7 +663,7 @@ impl Painter { .map(|h| h as f32 / face.units_per_em() as f32) .unwrap_or(1.) .max(1.); - let size = svg.size; + let size = svg.size(); let bound_size = bounds.size; let scale = (bound_size.width / size.width).min(bound_size.height / size.height) / grid_scale; self @@ -984,7 +985,8 @@ mod test { fn filter_invalid_commands() { let mut painter = painter(); - let svg = Svg::parse_from_bytes(include_bytes!("../../tests/assets/test1.svg")).unwrap(); + let svg = + Svg::parse_from_bytes(include_bytes!("../../tests/assets/test1.svg"), true, false).unwrap(); painter .save() .set_transform(Transform::translation(f32::NAN, f32::INFINITY)) @@ -995,8 +997,12 @@ mod test { #[test] fn draw_svg_gradient() { let mut painter = Painter::new(Rect::from_size(Size::new(64., 64.))); - let svg = - Svg::parse_from_bytes(include_bytes!("../../tests/assets/fill_with_gradient.svg")).unwrap(); + let svg = Svg::parse_from_bytes( + include_bytes!("../../tests/assets/fill_with_gradient.svg"), + true, + false, + ) + .unwrap(); painter.draw_svg(&svg); } diff --git a/painter/src/svg.rs b/painter/src/svg.rs index e8fa9f693..e5c86f5ca 100644 --- a/painter/src/svg.rs +++ b/painter/src/svg.rs @@ -1,4 +1,4 @@ -use std::{error::Error, io::Read, vec}; +use std::{cell::RefCell, error::Error, io::Read, vec}; use ribir_algo::Resource; use ribir_geom::{Point, Rect, Size, Transform}; @@ -6,7 +6,8 @@ use serde::{Deserialize, Serialize}; use usvg::{Options, Stop, Tree}; use crate::{ - Brush, Color, GradientStop, LineCap, LineJoin, PaintCommand, Path, StrokeOptions, + Brush, Color, CommandBrush, GradientStop, LineCap, LineJoin, PaintCommand, PaintPathAction, Path, + StrokeOptions, color::{LinearGradient, RadialGradient}, }; @@ -14,17 +15,49 @@ use crate::{ /// currently quite simple and primarily used for Ribir icons. More features /// will be added as needed. -#[derive(Serialize, Deserialize, Clone)] +#[derive(Serialize, Deserialize)] pub struct Svg { - pub size: Size, - pub commands: Resource>, + size: Size, + commands: Resource>, + + inherited_fill: bool, + inherited_stroke: bool, + #[serde(skip)] + last: RefCell>, +} + +#[derive(Clone)] +struct StaticSvg { + inherited_fill: Brush, + inherited_stroke: Brush, + commands: Resource>, } -// todo: we need to support currentColor to change svg color. // todo: share fontdb impl Svg { - pub fn parse_from_bytes(svg_data: &[u8]) -> Result> { - let opt = Options { ..<_>::default() }; + // FIXME: This is a temporary workaround. Utilize the magic color for the SVG, + // and replace it with the actual color when rendering. + const DYNAMIC_COLOR: Color = Color::from_u32(0x191B1901); + const DYNAMIC_COLOR_STR: &'static str = "#191B1901"; + + /// Parse SVG from bytes. + /// + /// - **inherit_fill**: Indicates whether this SVG will inherit the fill color + /// from the environment. + /// - **inherit_stroke**: Indicates whether this SVG will inherit the stroke + /// color from the environment. + pub fn parse_from_bytes( + svg_data: &[u8], inherit_fill: bool, inherit_stroke: bool, + ) -> Result> { + let magic = Self::DYNAMIC_COLOR_STR; + let style_sheet = match (inherit_fill, inherit_stroke) { + (true, true) => Some(format!("svg {{ fill: {magic}; stroke: {magic} }}")), + (true, false) => Some(format!("svg {{ fill: {magic} }}")), + (false, true) => Some(format!("svg {{ stroke: {magic} }}")), + _ => None, + }; + + let opt = Options { style_sheet, ..<_>::default() }; let tree = Tree::from_data(svg_data, &opt).unwrap(); let size = tree.size(); @@ -34,20 +67,61 @@ impl Svg { paint_group(tree.root(), &mut painter); let paint_commands = painter.finish().to_owned().into_boxed_slice(); + let (used_fill_fallback, used_stroke_fallback) = fallback_color_check(&paint_commands); Ok(Svg { size: Size::new(size.width(), size.height()), commands: Resource::new(paint_commands), + inherited_fill: used_fill_fallback, + inherited_stroke: used_stroke_fallback, + last: RefCell::new(None), }) } - pub fn open>(path: P) -> Result> { + /// Parse SVG from a file. + /// + /// - **inherit_fill**: Indicates whether this SVG will inherit the fill color + /// from the environment. + /// - **inherit_stroke**: Indicates whether this SVG will inherit the stroke + /// color from the environment. + pub fn open>( + path: P, fill_inject: bool, stroke_inject: bool, + ) -> Result> { let mut file = std::fs::File::open(path)?; let mut bytes = vec![]; file.read_to_end(&mut bytes)?; - Self::parse_from_bytes(&bytes) + Self::parse_from_bytes(&bytes, fill_inject, stroke_inject) + } + + pub fn size(&self) -> Size { self.size } + + pub fn commands( + &self, fill_brush: &Brush, stroke_brush: &Brush, + ) -> Resource> { + if !self.inherited_fill && !self.inherited_stroke { + self.commands.clone() + } else { + let mut last = self.last.borrow_mut(); + if let Some(last) = last + .as_ref() + .filter(|last| &last.inherited_fill == fill_brush && &last.inherited_stroke == stroke_brush) + { + last.commands.clone() + } else { + let commands = brush_replace(&self.commands, fill_brush, stroke_brush); + let commands = Resource::new(commands); + *last = Some(StaticSvg { + inherited_fill: fill_brush.clone(), + inherited_stroke: stroke_brush.clone(), + commands: commands.clone(), + }); + commands + } + } } + pub fn command_size(&self) -> usize { self.commands.len() } + pub fn serialize(&self) -> Result> { // use json replace bincode, because https://github.com/Ogeon/palette/issues/130 Ok(serde_json::to_string(self)?) @@ -181,6 +255,63 @@ fn brush_from_usvg_paint(paint: &usvg::Paint, opacity: usvg::Opacity) -> (Brush, } } +fn fallback_color_check(cmds: &[PaintCommand]) -> (bool, bool) { + let mut fill_fallback = false; + let mut stroke_fallback = false; + for c in cmds { + if fill_fallback && stroke_fallback { + break; + } + + match c { + PaintCommand::Path(p) => { + if let PaintPathAction::Paint { painting_style, brush, .. } = &p.action { + if matches!(brush, CommandBrush::Color(c) if c == &Svg::DYNAMIC_COLOR) { + match painting_style { + crate::PaintingStyle::Fill => fill_fallback = true, + crate::PaintingStyle::Stroke(_) => stroke_fallback = true, + } + } + } + } + PaintCommand::PopClip => {} + PaintCommand::Bundle { cmds, .. } => { + let (f, s) = fallback_color_check(cmds); + fill_fallback = f; + stroke_fallback |= s; + } + } + } + (fill_fallback, stroke_fallback) +} + +fn brush_replace(cmds: &[PaintCommand], fill: &Brush, stroke: &Brush) -> Box<[PaintCommand]> { + cmds + .iter() + .map(|c| match c { + PaintCommand::Path(p) => { + let mut p = p.clone(); + if let PaintPathAction::Paint { painting_style, brush, .. } = &mut p.action { + if matches!(brush, CommandBrush::Color(c) if c == &Svg::DYNAMIC_COLOR) { + match painting_style { + crate::PaintingStyle::Fill => *brush = fill.clone().into(), + crate::PaintingStyle::Stroke(_) => *brush = stroke.clone().into(), + } + } + } + PaintCommand::Path(p) + } + PaintCommand::PopClip => PaintCommand::PopClip, + PaintCommand::Bundle { transform, opacity, bounds, cmds } => { + let cmds = brush_replace(cmds, fill, stroke); + let cmds = Resource::new(cmds); + + PaintCommand::Bundle { transform: *transform, opacity: *opacity, bounds: *bounds, cmds } + } + }) + .collect() +} + fn convert_to_gradient_stops(stops: &[Stop]) -> Vec { assert!(!stops.is_empty()); @@ -230,3 +361,15 @@ impl From for LineJoin { } } } + +impl Clone for Svg { + fn clone(&self) -> Self { + Svg { + size: self.size, + commands: self.commands.clone(), + inherited_fill: self.inherited_fill, + inherited_stroke: self.inherited_stroke, + last: RefCell::new(self.last.borrow().clone()), + } + } +} diff --git a/painter/src/text/svg_glyph_cache.rs b/painter/src/text/svg_glyph_cache.rs index 6f82f52de..87176d392 100644 --- a/painter/src/text/svg_glyph_cache.rs +++ b/painter/src/text/svg_glyph_cache.rs @@ -107,7 +107,7 @@ impl SvgDocument { std::str::from_utf8(&writer.into_inner()) .ok() - .and_then(|str| Svg::parse_from_bytes(str.as_bytes()).ok()) + .and_then(|str| Svg::parse_from_bytes(str.as_bytes(), true, false).ok()) } fn parse(data: &[u8]) -> Option> { diff --git a/test_cases/include_svg/fix_draw_svg_not_apply_alpha_wgpu.png b/test_cases/include_svg/fix_draw_svg_not_apply_alpha_wgpu.png index 65dedc1b80de91e51e895fc67737d9a28ae56d80..a06bff75ffe40ae0c0dbae159b4d8fab50acc1fc 100644 GIT binary patch delta 711 zcmeyva)E7vay_%Sr;B4q1>@TT%nMW^rycwFzx-YAi8ITKypz}E-rfd9nLYESd99yk zWUTb=XW90ws%7og*;m&Wi84%G&9EYd)#0fYgVy=f;^NO|7R&#AvHjk?d;1#yE6kkP z-roNGPTB3;`hst7ZXW+=&p-9~rx)(_zt(QwzCAxeZ@PHBaq%Pjh9x&{+_3n3q22Dw z*Yopie_Q-}zVK7bHmDN$|38lZKT@^#-gA40``>T(SDs0}mzJFTSmeKPV5OZ=an$i@5>ZSwz}FxP}L3jATeedmtN=dAVLvgQY5$9~w)ez1N)Z*T8q{r{iz|0~8` z&)svWUVHDQ_Rss-r~X%vmX=-~|4}sl<6(xJ-~9g`@bBySZ@c0TJIHN+yVn2cl0Rnr z_uTtG=bnGs&wa4_#niZ$Q|*e(X6Js6F*7$mzvqzloz_zHFapMme?54-pO-aY^P|2P)?rl&=f5BvZB=-*!^ zb9`}-*MF|{AGqW{!Ii!O`(n!fyBu*YYqx%#|L5fVnzt5xr-QuMK?bqc15te5?vMMW z5AHu?;``eE=Vbezw-P1zmSsl$|M&j?Kk=J+f8M)4jnAC5?CSh~FXz`CHu2T|_kO{r ze4zfW|Ix6(KCfyV9Q37j|JnKpH*>bh3Qh&agU$5AhYweqnVFrld1lyODSi87eM85J z?xT;66z$w|;=fPGywkGfcVs^v=db(F9G?#KXGg~a`xF2E{fqUlc_dul_kX6qWPc{H h$@)xYKx{1b!`n6mK&Cfv2mV%Q~loCICcwi9G-S literal 636 zcmeAS@N?(olHy`uVBq!ia0vp^P9V&|1|%PcFuY)3U~=|!aSW+oeEWcLf{N!P6;Gk= zRWom;0XwS?QDX zREt3?oH1Z0SHr1LhLCyJlN+}`x4#$p;CSQaKj)o4?727Zzf5|=^+)w;TKYGhf0BP~ z+<5$xz3|l43@c(-9U!^|{v5YvuHh(X`)|wfFL^Gb9g79?9^3!b0YCeT*?tJjdprBb z{pM5kJ;n#xKjh3m{a=qoe-}UFxet5JF~}d&+9p?but#*b6EFv{+C|y=k;6=_w;|+kbk-7KGkz0dExwPpe;Xq{ym=i zv|f$t{A&&i=A2LR@258a!=QNwBxu$#EhrLYK+=Q=HJVBtuxGN}s#2{eT>l1`su(<7 L{an^LB{Ts5swFCO diff --git a/test_cases/ribir_core/builtin_widgets/svg/tests/svgs_smoke_wgpu.png b/test_cases/ribir_core/builtin_widgets/svg/tests/svgs_smoke_wgpu.png index 4613360916436a0a55d72a26f743552b1ab69538..776a07acf1b06d0e4df3e95f9b687e1691708c6d 100644 GIT binary patch literal 1733 zcmah~eM}Q)94?ZHqS9m=)4^>0Sajk99h*0bs}^Ov_OH{mQp%sdu^|Gdt1oj63vqP_~ZS3KhN)Z z&+pkCyUahxpGKoCi~C~dUhqEy-tCJPf^WGoAdW^`crkA0=lgSgOm}>*y?d~v!$R?= z#J?+RvJ+D4G6x&ypQ9HmOX6jfAB3-&a`4b&c|R9NX7tIj`wo0Ieyg^y>p-H2I-oOtrfK1sg3R4AuM0so?u63Nr_oE>$(8&e zOD9XvwNK=j9H^JHoYX%y5vtQo293$X(syxeb#KIqeHv@;>O51NR)eaHuvR1ck zkSe6!elN*t&Td~8(K=L)Im!|3T88DD2yM)s_^TDs1cMpe5puNV0V8CY>D-PHnp%VEEnYqQ8>R_ zo!`weH)mO9Yan-o0~+&|DOTr-S%Ui@b6QmCj7|mVRpg{fLB_H29;>|7O5R~Go}qHN zpqsNbvcs1HavggAt(vShYriH*rWO+R>)CzSA&q^IUhsSeOg(^!2b!<_wr$(iWRy_2 z1hdh}VY=c6KfD%m)|w42k!y=Ypk!lHM#YrQJUKa}4EMBqIgA_)BSVpCKV-U^u&-`1 zz?nj!%gN4?kN4#k78Mn>kT?jKK@Ed|34-WzDSsk_mPz%3*L8#cZ14g$$0(m3DMbB*xmqySYSOhw zZorf_o6YKFjD-d=!XUv`Qurk}x$ilV&Bn`SFUxGlWVX1dJHMjOoRJuQ0mDZK`;F{A zq}gOL8DagEWc^y$)R%1fes17dEv80|4r7kS%;w4LdGHZPQ9 zBN-UKVdW~?;J&nVl)sd6NFf~}e|ar0g71)?9)`}FI>O^Z2&mWYwt1@(yec0O5E6C+ za#lf3e=;zu)zXoeHw^$R`>so#qpQTv0$^%zWb7_oG@a3=FIVW*GoBejq(wsMLB$Zh zmrTF@_Zp{IgNs>Oc*L4)wkn28*ja*8tEAIs*Aq{;LpOFMy)2(JrFc0%&25#q-_;rm~hOnoB-Mu<|wib zFr(P$5@n>P>(WU3HDGyj&XTu;{x9*6P>)&AxfJRsOZbM;{Nx@nqDB}HDRasUgU zA;*kPIy;ED2c_*4V@~Q$1p?-TfRG+7q>k6@uE-m!RTyeTLmpu{M&ko1fr$w&IZ@-` z6OT;a4&-r;hcD{eRnIEkd!urCeichY4>wm(f<<*sx(A#cOy?stGEZEvAqq=JFs%1Z zZ?5(81DwW&`ud!3D%?}55-g-AgjkmfR%Oy;dfG$ndJ_agZ{I#)D9fl5EM^Q}ik!l$ z02~_pLlERRn96vzfK<4qT&_b#PEO8D8Bh!%;wVKNYY{CVXD=>Ya^jV$|DRF3>{|FA c13Wf8aB{)75B~TFTu-#PnB6<;wjVk353|4s0ssI2 literal 1107 zcmeAS@N?(olHy`uVBq!ia0vp^O+f6#!3HE}zvlB~U|^B*ba4!+V0`<4ae|8HBo$Ag z?o~5)ySuogrKN?10zo+ld_O}APy#YKJRD?8TAG`iOUv%rGr7+Dq=lE4mY4gV2U+Qp z^mNtghEt*8j8j*yW6}zbWer)ki)%$psc68?SN4~b8~It}xc5jD7;oUe!}o@{p68Fz z2a5+~|DHR z-e~);I&jCU`;VVCtY?awTfaB>z~YAG%=PE%^|}66?qm4j`rvXS>xbEY?tk)l5Zqw? zxYCyMN7aMpjq;4|nAb7(eVpURxV+&$%eNozou96%@6LVjwXyu6way& z!TyhN$6W1ijq};|O%czN{~`R~d!^s)1G;~{D+1m7DDMGxqd5CLu^)a9+AA);Vc!06 z&bG#Ah%5SQA1rU2|ID?V;m*0@TTFS+_VZ6&{ZHM3A@4buwK|r6kJSIoz+l{eOBv)` zpq~`~{RZ(beLb-H^HUHRuUYIVu2icAKPeH1-KZ;+@d_+`u!k^b? ze%^my^+37N{}D6<{+xICFn{^SoYw#Kz|dl}>wI1!{$VRHtnAsRir)?Yu>P~xfovw$ z3Z)OhfBt`pI&l3n(1D;}`5(Dr&kMWW`nmcHak3r{mN)o6{@)J@+RDFtA0GZW4~&uj zJmw7Rj}=!5eQ^GB-UYVu`ta`lh)Ab%U%aQ=|__kBUp)ldiIblC)w68a(b;CO?!*&XU z|8)n(cI1KGA97%E``?BID0+)6PQF5X-et27UI$ixwigDPyE=!_9Kuq53yj1+vJBgS z+*Y8ebC}|qo_}#au>2#Kzj!YDKe-u<*BQPu0i#6ff3YGWH)vrv3oW~lsNg?`{edcv U*VUDCfrSNwr>mdKI;Vst0C&nsJ^%m! diff --git a/test_cases/ribir_widgets/checkbox/tests/checked_with_default_by_wgpu.png b/test_cases/ribir_widgets/checkbox/tests/checked_with_default_by_wgpu.png index 40643243c9d2a35c85603e095c50c81eb6804ce4..71575bb8318a19e64a1d787b0a2404ce7b79eec4 100644 GIT binary patch delta 549 zcmV+=0^0qT28IQYB!5jwL_t(|0qwv8pd?wA#_|7m?v2RIr*cbUu5H`4ZQHhO+qP|K zjGmTCubwh9BJTNiw`zK(hdXQ4uTHDc{O?EQ{{|}mH&A(iU;WkJ;OoEsoB6joc5H{& zfBo0xz258H_%DX0)o7mSE#LATICJIzyE5723skQPy2*C?+{dL#z8K3dVTyxDCKKYYBlYfUk^dWr7mwZ0|15x=GoIZVu z&;R_-;)j0dM|tn}eh+G`{0~IsMR5A`DL&#OK7{|lsQlkR<^KjM4{+?*4o`maQ}~YW z_#XZ({_qd~g#RW~9^f@zm;0XwS?QBB zH*E3G`~9c>^M?G(SEy)s|L5KNCDC6>y65FxysTTb%hiATiczQ0cbVLf4}n;$Bci-H$1riQ2+lc{mLJUXj({dsi$Z`ZQ2GLTiRFLs@}z31=kPuuH% zZ)f=PPyP?%`p>`CA3nYR&*{(X`+xtQV^cZlzamFmgS*Y2hc)lZ>*dYw6fm#HRkKU#jap?tlHm@ASdR=FLL>r_1aAEU&oGEO+Gfo`0`DT+;u0 z>Cd_Re`f!g1^;m8?Ru$J_f@>U^?U96?+?T49!`I`d*1)X6?-^9GW(v+-q-s7;EF%@ z9iIMY0|zT8nEPvvC|9;luV?+ITlZ>t{iO6eWzvvvWv^IXUg2K%zyE5723skQPy2*C?+{dL#z8K3dVTyxDCKKYYBlYfUk^dWr7mwZ0|15x=GoIZVu z&;R_-;)j0dM|tn}eh+G`{0~IsMR5A`DL&#OK7{|lsQlkR<^KjM4{+?*4o`maQ}~YW z_#XZ({_qd~g#RW~9^f@zm;0XwS?QBB zH*E3G`~9c>^M?G(SEy)s|L5KNCDC6>y65FxysTTb%hiATiczQ0cbVLf4}n;$Bci-H$1riQ2+lc{mLJUXj({dsi$Z`ZQ2GLTiRFLs@}z31=kPuuH% zZ)f=PPyP?%`p>`CA3nYR&*{(X`+xtQV^cZlzamFmgS*Y2hc)lZ>*dYw6fm#HRkKU#jap?tlHm@ASdR=FLL>r_1aAEU&oGEO+Gfo`0`DT+;u0 z>Cd_Re`f!g1^;m8?Ru$J_f@>U^?U96?+?T49!`I`d*1)X6?-^9GW(v+-q-s7;EF%@ z9iIMY0|zT8nEPvvC|9;luV?+ITlZ>t{iO6eWzvvvWv^IXUg2K%@TT%nMW^rycwFzx-YAi8ITKypz}E-rfd9nLYESd99yk zWUTb=XW90ws%7og*;m*5t-ko@z5CPuY+C=#11cNh|6aZS;Pw80vwv>4D$8lVUjO&{ z)4lgz-~FNAw(M%gyqCK7Kgn7YKjT^dYt{NA`;ShZ|3^fw{`(H~51Z|OC;t&&{3#~x zYiNAu_v-!MAKiasz3*{##hu6V4%9P)jQ_d6@zj6bkbn6LKGic%6@PGg-<#DtU$5I; z5qPKIFmKHv^%|4^y({)GsQbw*-+HY3(U+IOf6NzuV%qcn_kQ#L-Dvv%ORumwurb-% z#_;D!|A!!}er*47*#3|3zu7>KT(9U2fBe1fJwMp~{a4v*4*dRbpTQUG-N_#ry(TL# ciHQAC&gi#%+HU<`g8>LUUHx3vIVCg!0CS+kfdBvi literal 593 zcmeAS@N?(olHy`uVBq!ia0vp^Mj*_=1|;R|J2o;fFiCj2IEGX(zJ0(rLB(^DilaP0T~@04zeXJ&CSiFW%ulvT<3k#!plp`%l*%Ttn^8m z8@Bl8{r*$`c|-o?D^xVR|MTwslISlb-ShG;Ue>MJNO>@OJBULqB2tuGx9JI{&AI#5Jt{u}S}Vy`a{A^MIfG8xdyB3R_&cOgeAp zM0dMK6ZafCy`Sm7nEltw_9u<=cJ~%qeu(cm#k}XT^&VyWPs#SDz)s2k8XEsG^pCm2 sgE{R#8-`8fN&V;Oy=cGL$$McKFsU$jy85}Sb4q9e03cRNyZ`_I diff --git a/test_cases/ribir_widgets/checkbox/tests/indeterminate_with_material_by_wgpu.png b/test_cases/ribir_widgets/checkbox/tests/indeterminate_with_material_by_wgpu.png index 927a1ebac0dba61bbb349b1169b9dd8a7ac88211..754b83f87b534ddc784122ccefd5ce2b1ce721c7 100644 GIT binary patch delta 343 zcmcb}a*TO`ay?_Or;B4q1>@TT%nMW^rycwFzx-YAi8ITKypz}E-rfd9nLYESd99yk zWUTb=XW90ws%7og*;m*5t-ko@z5CPuY+C=#11cNh|6aZS;Pw80vwv>4D$8lVUjO&{ z)4lgz-~FNAw(M%gyqCK7Kgn7YKjT^dYt{NA`;ShZ|3^fw{`(H~51Z|OC;t&&{3#~x zYiNAu_v-!MAKiasz3*{##hu6V4%9P)jQ_d6@zj6bkbn6LKGic%6@PGg-<#DtU$5I; z5qPKIFmKHv^%|4^y({)GsQbw*-+HY3(U+IOf6NzuV%qcn_kQ#L-Dvv%ORumwurb-% z#_;D!|A!!}er*47*#3|3zu7>KT(9U2fBe1fJwMp~{a4v*4*dRbpTQUG-N_#ry(TL# ciHQAC&gi#%+HU<`g8>LUUHx3vIVCg!0CS+kfdBvi literal 593 zcmeAS@N?(olHy`uVBq!ia0vp^Mj*_=1|;R|J2o;fFiCj2IEGX(zJ0(rLB(^DilaP0T~@04zeXJ&CSiFW%ulvT<3k#!plp`%l*%Ttn^8m z8@Bl8{r*$`c|-o?D^xVR|MTwslISlb-ShG;Ue>MJNO>@OJBULqB2tuGx9JI{&AI#5Jt{u}S}Vy`a{A^MIfG8xdyB3R_&cOgeAp zM0dMK6ZafCy`Sm7nEltw_9u<=cJ~%qeu(cm#k}XT^&VyWPs#SDz)s2k8XEsG^pCm2 sgE{R#8-`8fN&V;Oy=cGL$$McKFsU$jy85}Sb4q9e03cRNyZ`_I diff --git a/test_cases/ribir_widgets/checkbox/tests/unchecked_with_default_by_wgpu.png b/test_cases/ribir_widgets/checkbox/tests/unchecked_with_default_by_wgpu.png index a1ec879425fc198a1428d6ae59b8171f791350d1..e6d7008d84a7bbc8fb445f25b72e201ac166c8f3 100644 GIT binary patch delta 260 zcmV+f0sH=^1fBzsB!6W|L_t(|0qwv8pd?wA#_|7m?v2RIr*cbUu5H`4ZQHhO+qP|K zjGmTCubwh9BJTNiw`zK(hdXQ4uTHDc{O?EQ{{|}mH&A(iU;WkJ;OoEsoB6joc5H{& zfBo0xz258H_%DX0)o7mSE#LATICJId{u`iaHJbnZle_^`liC3b7Jmd>o-bK5%o+0l0000< KMNUMnLSTa9!i>cL literal 551 zcmeAS@N?(olHy`uVBq!ia0vp^Mj*_=1|;R|J2o;fFuwD2aSW+oeEWcLf{N!P6;Gk= zRWom;0XwS?QBB zH*E3G`~9c>^M?G(SEy)s|L5KNCDC6>y65FxysTTb%hiATiczQ0b=!nPxZ`G|4XmScE<^ezVH=;{5EW4U9?`2zb z-jj*_6)NjLd|H2C|55+HU;IDy+|Emm;rk=L_!GnXFMRJ$^#8fh|B1c+2m7Ds+NY>_(qzYge0qHMzB2s}~;?3wPgnKW5l-TNDu02n-7{an^LB{Ts5;B`4$ diff --git a/test_cases/ribir_widgets/checkbox/tests/unchecked_with_material_by_wgpu.png b/test_cases/ribir_widgets/checkbox/tests/unchecked_with_material_by_wgpu.png index a1ec879425fc198a1428d6ae59b8171f791350d1..e6d7008d84a7bbc8fb445f25b72e201ac166c8f3 100644 GIT binary patch delta 260 zcmV+f0sH=^1fBzsB!6W|L_t(|0qwv8pd?wA#_|7m?v2RIr*cbUu5H`4ZQHhO+qP|K zjGmTCubwh9BJTNiw`zK(hdXQ4uTHDc{O?EQ{{|}mH&A(iU;WkJ;OoEsoB6joc5H{& zfBo0xz258H_%DX0)o7mSE#LATICJId{u`iaHJbnZle_^`liC3b7Jmd>o-bK5%o+0l0000< KMNUMnLSTa9!i>cL literal 551 zcmeAS@N?(olHy`uVBq!ia0vp^Mj*_=1|;R|J2o;fFuwD2aSW+oeEWcLf{N!P6;Gk= zRWom;0XwS?QBB zH*E3G`~9c>^M?G(SEy)s|L5KNCDC6>y65FxysTTb%hiATiczQ0b=!nPxZ`G|4XmScE<^ezVH=;{5EW4U9?`2zb z-jj*_6)NjLd|H2C|55+HU;IDy+|Emm;rk=L_!GnXFMRJ$^#8fh|B1c+2m7Ds+NY>_(qzYge0qHMzB2s}~;?3wPgnKW5l-TNDu02n-7{an^LB{Ts5;B`4$ diff --git a/test_cases/ribir_widgets/icon/tests/icons_with_default_by_wgpu.png b/test_cases/ribir_widgets/icon/tests/icons_with_default_by_wgpu.png index f4d7ac8e481af388daadb90a55fd45d607d846a5..5d14c348671c3d0a63884c5c723f37e8dc85223d 100644 GIT binary patch literal 1349 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 N$VkghRi_-U{0~d>FJS-x literal 3568 zcmeHKZBP?u9uMjjK?JKa(#lCr#leHAIV&$#UNSm>rBtb{kgG~!r3NWIf>KO!30dkW zDMM|%-q938NU2ls;7YE=12GA~B18d+4o8KOu#u2N-m-ZiXHxUlu_GHpw}p~Oq>b6HXS_`!1?|N?53YX!dqz!vTZrv!|l6@{ceRQo2A#;#zq zbG3NOH_uA*Z+fW<=fT?{%}*8@!mNrS5#*Vp&iEv3&IGNee!etyz?HmMHkL>8%MY2* zTQ@7qZ?hUF)7bPi207xngv^Op!G?2mFLP>Xk=`)R`srC~Tf2`OakT9TO#YsJ!(VF_ zL`|+aQ+P@TSyPtpSVnSS6B!)QcR?4lQt=(@K%sIXMfnoN`9^X%-bPg2Ja#_X&?Q*u z;(WzGAAyD(^+FC;(BqBjsS&0xHR&ijIb$?#-raU1!yyDb8hB2QypYTLX>>xby8rig zV!1V9&OW*`ks%cWUa<*0t4dgj+}Qi!j?UaqfQNeag1we%cf(_3FuYgNqpx)~v(U4r zE5`EB!lvTH8^O=FrlI*A6Q8zFy~Pc$l~+f;uZs3mvph`fNPwtBuBw)>dTN5Ho?~J58zKDK|j2aMqAIOJbO=4Cx){93S6`Zc}@VXfFl153aQd$o^L4aJ+sv zk5=HKZmzR9ZpWt83DX_#@t2MW5@hyS*+}g)KCibO@|HH*+@hkAlG@0a{oVlR-HrxjF;p$FD!&+&LsRTKXxD&h&Hi`y@Cvd1T;-uoY4m>n;FK0Y;-wVCn&Z{|Fu zb}9Jd!pcpMe+n9LI=wRVU8QbF6-M>`l{!?55ixT~VAr4@=ZKdlu;KGrzGcclMRK&U zF@-|FPT;)jMdkV)I9}aA@qSM+2|%X`>}vtcMUPJ!8Ac0$h!2#T!&pmMa#0NZdW7#Q zx5;L^E{6x@M#e?=b{=0tLwBe>TL3MlO&jFGVLi<@?!(1s!9_OL+JEt23;K$uuO8h}|8PUOTPh|IZbA1@RtA&TmSG!# zBQ!Y}&u^5Blv#DJA-n4zjy5iTl$4b8$$I*iYM*g3h*OVQ4Bhio&k3tius|c>9HBw| zxJ12NVsis12ieV@;znAu3_}{|EZ`n5z&J#Yx2ubTt9VxBeL_az_kbpvz(ji=0AwQM zdG5?ruY-bqa7>oopQ9emv44Gc<+C*8rmj*GP0;_xb?DV-#3r!b{u25w{tO7{1X8Q=O4K2pUMqwMrsh@Y z*SExe=6oznztTeWzr0|zUNjFE(_MLtdMeyUHDmKcd2JEsnVpv-@od3UKv!rrW`Djx48x}mk>isau7%P}= z@is!2<_OV-mj2a*`AZMMF2TjsDEUXr%GRXqHU725@L} zJV=!~YtNmb4vF9gBIjDFZI^POL>VWug^>HoSK6rEQv5DhXE+L4`?;fCz&<3A@HU<* z!3KZ2FOQ3l7iblyHF@ktzOO}a5VRM7_h`T(&H1&@Bk~_W_NzuyRB0I4^X-Z+ECe&e z7=d^KrlIbYksRghr0T#^@OGbiCK7)tdYF_+bv(qL@ Ycox6Wn^=hdw Painter { let mut painter = Painter::new(Rect::from_size(Size::new(64., 64.))); - let svg: Svg = include_crate_svg!("./assets/test1.svg"); + let svg: Svg = include_crate_svg!("./assets/test1.svg", true, false); painter.apply_alpha(0.5).draw_svg(&svg); painter } diff --git a/widgets/src/icon.rs b/widgets/src/icon.rs index d3cb379b4..626ee8a4e 100644 --- a/widgets/src/icon.rs +++ b/widgets/src/icon.rs @@ -113,11 +113,17 @@ mod tests { widget_image_tests!( icons, WidgetTester::new(row! { - @Icon { @ { svgs::DELETE }} - @Icon { @ { "search" } } + @Icon { + foreground: Color::BLUE, + @ { svgs::DELETE } + } + @Icon { + foreground: Color::RED, + @ { "search" } + } @Icon { @SpinnerProgress { value: Some(0.8) }} }) - .with_wnd_size(Size::new(300., 200.)) + .with_wnd_size(Size::new(128., 64.)) .with_env_init(|| { let mut theme = AppCtx::app_theme().write(); // Specify the icon font. diff --git a/widgets/src/path.rs b/widgets/src/path.rs index 8629cb29b..6137b121f 100644 --- a/widgets/src/path.rs +++ b/widgets/src/path.rs @@ -80,7 +80,7 @@ mod tests { .foreground(Color::BLACK) }) .with_wnd_size(WND_SIZE) - .with_comparison(0.000025), + .with_comparison(0.00003), LayoutCase::default().with_size(Size::splat(40.5)) ); }