@@ -762,3 +762,73 @@ fn filter_varying_depths_clips_and_compositions(ctx: &mut impl Renderer) {
762762 ctx. pop_layer ( ) ;
763763 }
764764}
765+
766+ /// Test that blur filters correctly expand bounds when the layer is rotated.
767+ ///
768+ /// This verifies that the expansion calculation uses `transform_rect_bbox` to account for
769+ /// the full transformation matrix (including rotation and shear), rather than just extracting
770+ /// x/y scales separately. A 45-degree rotation should produce a diamond-shaped blur.
771+ #[ vello_test( skip_hybrid, skip_multithreaded) ]
772+ fn filter_rotated_blur ( ctx : & mut impl Renderer ) {
773+ let filter_gaussian_blur = Filter :: from_primitive ( FilterPrimitive :: GaussianBlur {
774+ std_deviation : 4.0 ,
775+ edge_mode : EdgeMode :: None ,
776+ } ) ;
777+ let center = Point :: new ( 50.0 , 50.0 ) ;
778+ ctx. set_transform ( Affine :: rotate_about ( std:: f64:: consts:: PI / 4.0 , center) ) ;
779+
780+ let width = 24. ;
781+ let overlap = 6. ;
782+ let between = 12. ;
783+
784+ let x = 21. ;
785+ let y = 21. ;
786+ let mut left = x;
787+ let mut top = y;
788+
789+ let circle_path = Circle :: new ( center, 25. ) . to_path ( 0.1 ) ;
790+ {
791+ ctx. push_layer (
792+ Some ( & circle_path) ,
793+ None ,
794+ None ,
795+ None ,
796+ Some ( filter_gaussian_blur. clone ( ) ) ,
797+ ) ;
798+ ctx. set_paint ( ROYAL_BLUE ) ;
799+ ctx. fill_rect ( & Rect :: from_points ( ( left, top) , ( left + width, top + width) ) ) ;
800+ {
801+ ctx. push_layer ( None , None , None , None , None ) ;
802+ ctx. set_paint ( PURPLE ) ;
803+ left = x + width + between;
804+ top = y;
805+ ctx. fill_rect ( & Rect :: from_points ( ( left, top) , ( left + width, top + width) ) ) ;
806+ {
807+ ctx. push_layer ( None , None , None , None , None ) ;
808+ ctx. set_paint ( TOMATO ) ;
809+ left = x + width - overlap;
810+ top = y + width - overlap;
811+ ctx. fill_rect ( & Rect :: from_points ( ( left, top) , ( left + width, top + width) ) ) ;
812+ {
813+ ctx. push_layer ( None , None , None , None , None ) ;
814+ ctx. set_paint ( VIOLET ) ;
815+ left = x;
816+ top = y + width + between;
817+ ctx. fill_rect ( & Rect :: from_points ( ( left, top) , ( left + width, top + width) ) ) ;
818+ {
819+ ctx. push_layer ( None , None , None , None , None ) ;
820+ ctx. set_paint ( SEA_GREEN ) ;
821+ left = x + width + between;
822+ top = y + width + between;
823+ ctx. fill_rect ( & Rect :: from_points ( ( left, top) , ( left + width, top + width) ) ) ;
824+ ctx. pop_layer ( ) ;
825+ }
826+ ctx. pop_layer ( ) ;
827+ }
828+ ctx. pop_layer ( ) ;
829+ }
830+ ctx. pop_layer ( ) ;
831+ }
832+ ctx. pop_layer ( ) ;
833+ }
834+ }
0 commit comments