@@ -7,7 +7,7 @@ use core_types::bounds::RenderBoundingBox;
77use core_types:: color:: Color ;
88use core_types:: color:: SRGBA8 ;
99use core_types:: consts:: DEFAULT_FONT_SIZE ;
10- use core_types:: list:: { ATTR_FILL , ATTR_STROKE , Item , List } ;
10+ use core_types:: list:: { ATTR_FILL , ATTR_FILTER_EFFECTS , ATTR_STROKE , Item , List } ;
1111use core_types:: math:: quad:: Quad ;
1212use core_types:: render_complexity:: RenderComplexity ;
1313use core_types:: transform:: Footprint ;
@@ -21,6 +21,7 @@ use dyn_any::DynAny;
2121use glam:: { DAffine2 , DMat2 , DVec2 } ;
2222use graphene_hash:: CacheHashWrapper ;
2323use graphene_resource:: Resource ;
24+ use graphic_types:: SvgFilterEffect ;
2425use graphic_types:: graphic:: { graphic_list_at, has_paint_at, is_paint_present, set_paint_attribute} ;
2526use graphic_types:: raster_types:: { BitmapMut , CPU , GPU , Image , Raster } ;
2627use graphic_types:: vector_types:: gradient:: { GradientStops , GradientType } ;
@@ -268,6 +269,29 @@ pub fn format_transform_matrix(transform: DAffine2) -> String {
268269 } ) + ")"
269270}
270271
272+ fn write_svg_filter_def ( svg_defs : & mut String , effects : & [ SvgFilterEffect ] ) -> Option < String > {
273+ if effects. is_empty ( ) {
274+ return None ;
275+ }
276+
277+ let id = format ! ( "filter-{}" , generate_uuid( ) ) ;
278+ write ! ( svg_defs, r#"<filter id="{id}" x="-50%" y="-50%" width="200%" height="200%" color-interpolation-filters="sRGB">"# ) . unwrap ( ) ;
279+
280+ let mut input = "SourceGraphic" . to_string ( ) ;
281+ for ( index, effect) in effects. iter ( ) . enumerate ( ) {
282+ let result = format ! ( "effect{index}" ) ;
283+ match effect {
284+ SvgFilterEffect :: GaussianBlur { std_deviation_x, std_deviation_y } => {
285+ write ! ( svg_defs, r#"<feGaussianBlur in="{input}" stdDeviation="{std_deviation_x} {std_deviation_y}" result="{result}"/>"# ) . unwrap ( ) ;
286+ }
287+ }
288+ input = result;
289+ }
290+
291+ svg_defs. push_str ( "</filter>" ) ;
292+ Some ( id)
293+ }
294+
271295/// `(max, min)` factors by which a unit vector is stretched under `transform`'s linear part — the
272296/// principal and minor singular values, equal to the semi-axes of the ellipse a unit circle maps to.
273297/// Equivalent to `(max(sx, sy), min(sx, sy))` for axis-aligned scales, but accounts for shear.
@@ -835,6 +859,7 @@ impl Render for List<Graphic> {
835859 for index in 0 ..self . len ( ) {
836860 let transform: DAffine2 = self . attribute_cloned_or_default ( ATTR_TRANSFORM , index) ;
837861 let blend_mode: BlendMode = self . attribute_cloned_or_default ( ATTR_BLEND_MODE , index) ;
862+ let filter_effects: Vec < SvgFilterEffect > = self . attribute_cloned_or_default ( ATTR_FILTER_EFFECTS , index) ;
838863 let opacity_attr: f64 = self . attribute_cloned_or ( ATTR_OPACITY , index, 1. ) ;
839864 let opacity_fill_attr: f64 = self . attribute_cloned_or ( ATTR_OPACITY_FILL , index, 1. ) ;
840865 let element = self . element ( index) . unwrap ( ) ;
@@ -1061,9 +1086,9 @@ impl Render for List<Vector> {
10611086 let Some ( vector) = self . element ( index) else { continue } ;
10621087 let item_transform: DAffine2 = self . attribute_cloned_or_default ( ATTR_TRANSFORM , index) ;
10631088 let blend_mode_attr: BlendMode = self . attribute_cloned_or_default ( ATTR_BLEND_MODE , index) ;
1089+ let filter_effects: Vec < SvgFilterEffect > = self . attribute_cloned_or_default ( ATTR_FILTER_EFFECTS , index) ;
10641090 let opacity_attr: f64 = self . attribute_cloned_or ( ATTR_OPACITY , index, 1. ) ;
10651091 let opacity_fill_attr: f64 = self . attribute_cloned_or ( ATTR_OPACITY_FILL , index, 1. ) ;
1066-
10671092 // Only consider strokes with non-zero weight, since default strokes with zero weight would prevent assigning the correct stroke transform
10681093 let has_real_stroke = vector. stroke . as_ref ( ) . filter ( |stroke| stroke. weight ( ) > 0. ) ;
10691094 let set_stroke_transform = has_real_stroke. map ( |stroke| stroke. transform ) . filter ( |transform| transform_is_invertible ( * transform) ) ;
@@ -1161,6 +1186,10 @@ impl Render for List<Vector> {
11611186 attributes. push ( ATTR_TRANSFORM , matrix) ;
11621187 }
11631188
1189+ if let Some ( filter_id) = write_svg_filter_def ( & mut attributes. 0 . svg_defs , & filter_effects) {
1190+ attributes. push ( "filter" , format ! ( "url(#{filter_id})" ) ) ;
1191+ }
1192+
11641193 let defs = & mut attributes. 0 . svg_defs ;
11651194 if let Some ( ( ref id, mask_type, ref vector_item) ) = push_id {
11661195 let mut svg = SvgRender :: new ( ) ;
0 commit comments