Move opaque check function to Graphic impl

This commit is contained in:
YohYamasaki
2026-05-19 16:33:18 +09:00
parent 3091a6f2df
commit 3fd61169ae
2 changed files with 85 additions and 91 deletions

View File

@@ -356,6 +356,14 @@ impl Graphic {
_ => false,
}
}
pub fn is_opaque(&self) -> bool {
match self {
Graphic::Color(list) => list.element(0).is_some_and(|color| color.is_opaque()),
Graphic::Gradient(list) => list.element(0).is_some_and(|stops| stops.iter().all(|stop| stop.color.a() >= 1. - f32::EPSILON)),
_ => false,
}
}
}
impl BoundingBox for Graphic {
@@ -481,3 +489,79 @@ impl<T: Clone> OmitIndex for List<T> {
self.omit_index(self.len() - index)
}
}
#[cfg(test)]
mod graphic_is_opaque_tests {
use vector_types::{GradientSpreadMethod, GradientStop};
use super::*;
fn color_graphic(alpha: f64) -> Graphic {
let color = Color::from_rgbaf32(1.0, 0.0, 0.0, alpha as f32).unwrap();
Graphic::Color(List::new_from_element(color))
}
fn gradient_graphic(gradient: GradientStops) -> Graphic {
let mut gradient_list = List::new_from_element(gradient);
gradient_list.set_attribute(ATTR_SPREAD_METHOD, 0, GradientSpreadMethod::Pad);
Graphic::Gradient(gradient_list)
}
#[test]
fn opaque_color_is_opaque() {
let g = color_graphic(1.0);
assert!(g.is_opaque());
}
#[test]
fn transparent_color_is_not_opaque() {
let g = color_graphic(0.5);
assert!(!g.is_opaque());
}
#[test]
fn vector_is_not_opaque() {
let g = Graphic::Vector(List::default());
assert!(!g.is_opaque());
}
#[test]
fn gradient_with_all_opaque_stops_is_opaque() {
let color_1 = Color::from_rgbaf32(1.0, 0.0, 0.0, 1.).unwrap();
let color_2 = Color::from_rgbaf32(1.0, 0.0, 0.0, 1.).unwrap();
let gradient = GradientStops::new(vec![
GradientStop {
position: 0.,
midpoint: 0.5,
color: color_1,
},
GradientStop {
position: 1.,
midpoint: 0.5,
color: color_2,
},
]);
let g = gradient_graphic(gradient);
assert!(g.is_opaque());
}
#[test]
fn gradient_with_transparent_stop_is_not_opaque() {
let color_1 = Color::from_rgbaf32(1.0, 0.0, 0.0, 0.5).unwrap();
let color_2 = Color::from_rgbaf32(1.0, 0.0, 0.0, 1.).unwrap();
let gradient = GradientStops::new(vec![
GradientStop {
position: 0.,
midpoint: 0.5,
color: color_1,
},
GradientStop {
position: 1.,
midpoint: 0.5,
color: color_2,
},
]);
let g = gradient_graphic(gradient);
assert!(!g.is_opaque());
}
}

View File

@@ -337,15 +337,6 @@ fn draw_raster_outline(scene: &mut Scene, outline_transform: &DAffine2, render_p
scene.stroke(&outline_stroke, Affine::IDENTITY, outline_color_peniko, None, &outline_path);
}
/// Returns true if the resolved fill graphic fully and opaquely covers the path interior.
fn fill_covers_opaquely(fill_graphic: Option<&Graphic>) -> bool {
match fill_graphic {
Some(Graphic::Color(list)) => list.element(0).is_some_and(|c| c.a() >= 1.0),
Some(Graphic::Gradient(list)) => list.element(0).is_some_and(|stops| stops.iter().all(|stop| stop.color.a() >= 1.0)),
_ => false,
}
}
/// Emits an SVG `<path>` element with the resolved fill attribute corresponding to the given fill_graphic.
#[allow(clippy::too_many_arguments)]
fn emit_svg_fill_path(
@@ -1015,7 +1006,7 @@ impl Render for List<Vector> {
let path_is_closed = vector.stroke_bezier_paths().all(|path| path.closed());
let can_draw_aligned_stroke = path_is_closed && vector.style.stroke().is_some_and(|stroke| stroke.has_renderable_stroke() && stroke.align.is_not_centered());
let can_use_paint_order = !(fill_graphic.is_none() || !fill_covers_opaquely(fill_graphic) || mask_type == MaskType::Clip);
let can_use_paint_order = !(fill_graphic.is_none_or(|graphic| !graphic.is_opaque()) || mask_type == MaskType::Clip);
let needs_separate_alignment_fill = can_draw_aligned_stroke && !can_use_paint_order;
let wants_stroke_below = vector.style.stroke().map(|s| s.paint_order) == Some(PaintOrder::StrokeBelow);
@@ -2187,84 +2178,3 @@ impl SvgRenderAttrs<'_> {
self.0.svg.push(value.into());
}
}
#[cfg(test)]
mod svg_fill_helper_tests {
use vector_types::GradientStop;
use super::*;
fn color_graphic(alpha: f64) -> Graphic {
let color = Color::from_rgbaf32(1.0, 0.0, 0.0, alpha as f32).unwrap();
Graphic::Color(List::new_from_element(color))
}
fn gradient_graphic(gradient: GradientStops) -> Graphic {
let mut gradient_list = List::new_from_element(gradient);
gradient_list.set_attribute(ATTR_SPREAD_METHOD, 0, GradientSpreadMethod::Pad);
Graphic::Gradient(gradient_list)
}
#[test]
fn opaquely_none_is_false() {
assert!(!fill_covers_opaquely(None));
}
#[test]
fn opaquely_opaque_color_is_true() {
let g = color_graphic(1.0);
assert!(fill_covers_opaquely(Some(&g)));
}
#[test]
fn opaquely_transparent_color_is_false() {
let g = color_graphic(0.5);
assert!(!fill_covers_opaquely(Some(&g)));
}
#[test]
fn opaquely_vector_is_false() {
let g = Graphic::Vector(List::default());
assert!(!fill_covers_opaquely(Some(&g)));
}
#[test]
fn opaquely_gradient_all_opaque_is_true() {
let color_1 = Color::from_rgbaf32(1.0, 0.0, 0.0, 1.).unwrap();
let color_2 = Color::from_rgbaf32(1.0, 0.0, 0.0, 1.).unwrap();
let gradient = GradientStops::new(vec![
GradientStop {
position: 0.,
midpoint: 0.5,
color: color_1,
},
GradientStop {
position: 1.,
midpoint: 0.5,
color: color_2,
},
]);
let g = gradient_graphic(gradient);
assert!(fill_covers_opaquely(Some(&g)));
}
#[test]
fn opaquely_transparent_gradient_is_false() {
let color_1 = Color::from_rgbaf32(1.0, 0.0, 0.0, 0.5).unwrap();
let color_2 = Color::from_rgbaf32(1.0, 0.0, 0.0, 1.).unwrap();
let gradient = GradientStops::new(vec![
GradientStop {
position: 0.,
midpoint: 0.5,
color: color_1,
},
GradientStop {
position: 1.,
midpoint: 0.5,
color: color_2,
},
]);
let g = gradient_graphic(gradient);
assert!(!fill_covers_opaquely(Some(&g)));
}
}