@@ -1168,10 +1168,16 @@ fn draw_bg_gradient_shift(
11681168 return ;
11691169 }
11701170
1171- let colors : Vec < skia_safe:: Color4f > = bg. colors . iter ( ) . map ( |c| color4f_from_hex ( c) ) . collect ( ) ;
1171+ let base_colors : Vec < skia_safe:: Color4f > = bg. colors . iter ( ) . map ( |c| color4f_from_hex ( c) ) . collect ( ) ;
11721172 let angle = ( bg. speed * time) % 360.0 ;
11731173 let rad = angle. to_radians ( ) ;
11741174
1175+ // Interpolate in linear color space to reduce banding on dark gradients
1176+ let linear_cs = skia_safe:: ColorSpace :: new_srgb_linear ( ) ;
1177+
1178+ // Subdivide color stops (16 intermediate steps between each pair) for smoother gradients
1179+ let ( colors, positions) = subdivide_gradient_stops ( & base_colors, 16 ) ;
1180+
11751181 let shader = match bg. gradient_type {
11761182 GradientType :: Linear => {
11771183 let cx = width / 2.0 ;
@@ -1181,8 +1187,8 @@ fn draw_bg_gradient_shift(
11811187 let end = Point :: new ( cx + rad. cos ( ) * half_diag, cy + rad. sin ( ) * half_diag) ;
11821188 skia_safe:: shader:: Shader :: linear_gradient (
11831189 ( start, end) ,
1184- GradientShaderColors :: ColorsInSpace ( & colors, Some ( skia_safe :: ColorSpace :: new_srgb ( ) ) ) ,
1185- None ,
1190+ GradientShaderColors :: ColorsInSpace ( & colors, Some ( linear_cs ) ) ,
1191+ Some ( & positions [ .. ] ) ,
11861192 skia_safe:: TileMode :: Clamp ,
11871193 None ,
11881194 None ,
@@ -1194,8 +1200,8 @@ fn draw_bg_gradient_shift(
11941200 skia_safe:: shader:: Shader :: radial_gradient (
11951201 center,
11961202 radius,
1197- GradientShaderColors :: ColorsInSpace ( & colors, Some ( skia_safe :: ColorSpace :: new_srgb ( ) ) ) ,
1198- None ,
1203+ GradientShaderColors :: ColorsInSpace ( & colors, Some ( linear_cs ) ) ,
1204+ Some ( & positions [ .. ] ) ,
11991205 skia_safe:: TileMode :: Clamp ,
12001206 None ,
12011207 None ,
@@ -1206,10 +1212,49 @@ fn draw_bg_gradient_shift(
12061212 if let Some ( shader) = shader {
12071213 let mut paint = Paint :: default ( ) ;
12081214 paint. set_shader ( shader) ;
1215+ paint. set_dither ( true ) ;
12091216 canvas. draw_rect ( skia_safe:: Rect :: from_wh ( width, height) , & paint) ;
12101217 }
12111218}
12121219
1220+ /// Subdivide gradient color stops by inserting intermediate interpolated colors.
1221+ /// Returns (colors, positions) with `subdivisions` extra stops between each original pair.
1222+ fn subdivide_gradient_stops (
1223+ colors : & [ skia_safe:: Color4f ] ,
1224+ subdivisions : u32 ,
1225+ ) -> ( Vec < skia_safe:: Color4f > , Vec < f32 > ) {
1226+ let n = colors. len ( ) ;
1227+ if n < 2 {
1228+ return ( colors. to_vec ( ) , vec ! [ 0.0 ] ) ;
1229+ }
1230+ let total = ( n - 1 ) * subdivisions as usize + n;
1231+ let mut out_colors = Vec :: with_capacity ( total) ;
1232+ let mut out_pos = Vec :: with_capacity ( total) ;
1233+ let seg = ( n - 1 ) as f32 ;
1234+
1235+ for i in 0 ..n - 1 {
1236+ let c0 = & colors[ i] ;
1237+ let c1 = & colors[ i + 1 ] ;
1238+ let steps = subdivisions + 1 ;
1239+ for s in 0 ..steps {
1240+ let t = s as f32 / steps as f32 ;
1241+ let global_t = ( i as f32 + t) / seg;
1242+ out_colors. push ( skia_safe:: Color4f {
1243+ r : c0. r + ( c1. r - c0. r ) * t,
1244+ g : c0. g + ( c1. g - c0. g ) * t,
1245+ b : c0. b + ( c1. b - c0. b ) * t,
1246+ a : c0. a + ( c1. a - c0. a ) * t,
1247+ } ) ;
1248+ out_pos. push ( global_t) ;
1249+ }
1250+ }
1251+ // Last color
1252+ out_colors. push ( colors[ n - 1 ] ) ;
1253+ out_pos. push ( 1.0 ) ;
1254+
1255+ ( out_colors, out_pos)
1256+ }
1257+
12131258/// Soft colored glow zones (halo preset).
12141259fn draw_bg_halo (
12151260 canvas : & Canvas ,
0 commit comments