diff --git a/graphics/src/damage.rs b/graphics/src/damage.rs index 60bcaa85f3..0160fbceb9 100644 --- a/graphics/src/damage.rs +++ b/graphics/src/damage.rs @@ -46,16 +46,18 @@ impl Damage for Primitive { shadow_softness, .. } => { - let shadow_bounds = Rectangle { + let bounds_with_shadow = Rectangle { x: bounds.x + shadow_offset.x.min(0.0) - shadow_softness, y: bounds.y + shadow_offset.y.min(0.0) - shadow_softness, width: bounds.width - + (shadow_offset.x.max(0.0) + shadow_softness) * 2.0, + + shadow_offset.x.max(0.0) + + shadow_softness * 2.0, height: bounds.height - + (shadow_offset.y.max(0.0) + shadow_softness) * 2.0, + + shadow_offset.y.max(0.0) + + shadow_softness * 2.0, }; - shadow_bounds.expand(1.0) + bounds_with_shadow.expand(1.0) } Self::Image { bounds, .. } | Self::Svg { bounds, .. } => { bounds.expand(1.0) diff --git a/tiny_skia/src/backend.rs b/tiny_skia/src/backend.rs index 05337b3cec..52d608096a 100644 --- a/tiny_skia/src/backend.rs +++ b/tiny_skia/src/backend.rs @@ -193,124 +193,108 @@ impl Backend { if shadow_color.a > 0.0 { fn smoothstep(a: f32, b: f32, x: f32) -> f32 { - let x = x.clamp(a, b); - let x = x.clamp(0.0, (x - a) / (b - a)); + let x = ((x - a) / (b - a)).clamp(0.0, 1.0); x * x * (3.0 - 2.0 * x) } fn rounded_box_sdf( - center_distance: Vector, + to_center: Vector, size: Size, - radius: [f32; 4], + radii: &[f32], ) -> f32 { - let radius = match ( - center_distance.x > 0.0, - center_distance.y > 0.0, - ) { - (false, false) => radius[0], - (true, false) => radius[1], - (true, true) => radius[2], - (false, true) => radius[3], - }; - - let x = (center_distance.x.abs() - size.width + radius) - .max(0.0); - let y = (center_distance.y.abs() - size.height - + radius) - .max(0.0); - - (x * x + y * y).sqrt() - radius + let radius = + match (to_center.x > 0.0, to_center.y > 0.0) { + (true, true) => radii[2], + (true, false) => radii[1], + (false, true) => radii[3], + (false, false) => radii[0], + }; + + let x = + (to_center.x.abs() - size.width + radius).max(0.0); + let y = + (to_center.y.abs() - size.height + radius).max(0.0); + + (x.powf(2.0) + y.powf(2.0)).sqrt() - radius } - let shadow_bounds = Rectangle { - x: bounds.x + shadow_offset.x.min(0.0) - - shadow_softness, - y: bounds.y + shadow_offset.y.min(0.0) - - shadow_softness, - width: bounds.width - + (shadow_offset.x.max(0.0) + shadow_softness) - * 2.0, - height: bounds.height - + (shadow_offset.y.max(0.0) + shadow_softness) - * 2.0, - } * scale_factor; - - if let Some(shadow_color) = tiny_skia::Color::from_rgba( - shadow_color.r, - shadow_color.g, - shadow_color.b, - shadow_color.a, - ) { - let mut bytes = vec![]; - let radius = [ - fill_border_radius[0] * scale_factor, - fill_border_radius[1] * scale_factor, - fill_border_radius[2] * scale_factor, - fill_border_radius[3] * scale_factor, - ]; - for y in shadow_bounds.y as u32 - ..(shadow_bounds.y as u32 - + shadow_bounds.height as u32) - { - for x in shadow_bounds.x as u32 - ..(shadow_bounds.x as u32 - + shadow_bounds.width as u32) - { - let shadow_distance = rounded_box_sdf( - Vector { - x: x as f32 - - physical_bounds.position().x - - shadow_offset.x * scale_factor - - (physical_bounds.width / 2.0), - y: y as f32 - - physical_bounds.position().y - - shadow_offset.y * scale_factor - - (physical_bounds.height / 2.0), - }, - Size::new( - physical_bounds.width / 2.0, - physical_bounds.height / 2.0, - ), - radius, + let shadow_bounds = (Rectangle { + x: bounds.x + shadow_offset.x - shadow_softness, + y: bounds.y + shadow_offset.y - shadow_softness, + width: bounds.width + shadow_softness * 2.0, + height: bounds.height + shadow_softness * 2.0, + } + translation) + * scale_factor; + + let radii = fill_border_radius + .into_iter() + .map(|radius| radius * scale_factor) + .collect::>(); + let (x, y, width, height) = ( + shadow_bounds.x as u32, + shadow_bounds.y as u32, + shadow_bounds.width as u32, + shadow_bounds.height as u32, + ); + let half_width = physical_bounds.width / 2.0; + let half_height = physical_bounds.height / 2.0; + + let colors = (y..y + height) + .flat_map(|y| { + (x..x + width).map(move |x| (x as f32, y as f32)) + }) + .map(|(x, y)| { + let shadow_distance = rounded_box_sdf( + Vector::new( + x - physical_bounds.position().x + - (shadow_offset.x * scale_factor) + - half_width, + y - physical_bounds.position().y + - (shadow_offset.y * scale_factor) + - half_height, + ), + Size::new(half_width, half_height), + &radii, + ); + let shadow_alpha = 1.0 + - smoothstep( + -shadow_softness * scale_factor * 2.0, + *shadow_softness * scale_factor, + shadow_distance, ); - let shadow_alpha = 1.0 - - smoothstep( - -shadow_softness * scale_factor, - *shadow_softness * scale_factor, - shadow_distance, - ); - - let mut color = shadow_color; - color.apply_opacity(shadow_alpha); - - bytes.extend(bytemuck::bytes_of( - &color.to_color_u8().premultiply(), - )); - } - } - if let Some(p) = tiny_skia::Pixmap::from_vec( - bytes, - tiny_skia::IntSize::from_wh( - shadow_bounds.width as u32, - shadow_bounds.height as u32, + tiny_skia::Color::from_rgba( + // This configuration gives the correct color, but how are + // the blue and red channels getting mixed up? + shadow_color.b, + shadow_color.g, + shadow_color.r, + shadow_color.a * shadow_alpha, ) - .unwrap(), - ) { - pixels.draw_pixmap( - shadow_bounds.x as i32, - shadow_bounds.y as i32, - p.as_ref(), - &tiny_skia::PixmapPaint { - blend_mode: - tiny_skia::BlendMode::SourceOver, - ..Default::default() - }, - Default::default(), - None, + .map(|color| color.to_color_u8().premultiply()) + .unwrap_or_else(|| { + tiny_skia::PremultipliedColorU8::TRANSPARENT + }) + }) + .collect(); + + if let Some(p) = tiny_skia::IntSize::from_wh(width, height) + .and_then(|size| { + tiny_skia::Pixmap::from_vec( + bytemuck::cast_vec(colors), + size, ) - } + }) + { + pixels.draw_pixmap( + x as i32, + y as i32, + p.as_ref(), + &Default::default(), + Default::default(), + None, + ) } } diff --git a/wgpu/src/shader/quad.wgsl b/wgpu/src/shader/quad.wgsl index 30e4cc777c..c8bc59b091 100644 --- a/wgpu/src/shader/quad.wgsl +++ b/wgpu/src/shader/quad.wgsl @@ -77,7 +77,7 @@ fn solid_vs_main(input: SolidVertexInput) -> SolidVertexOutput { var pos: vec2 = (input.pos + min(input.shadow_offset, vec2(0.0, 0.0)) - input.shadow_softness) * globals.scale; var quad_pos: vec2 = input.pos * globals.scale; - var scale: vec2 = (input.scale + (vec2(max(0.0, input.shadow_offset.x), max(0.0, input.shadow_offset.y)) + input.shadow_softness) * 2.0) * globals.scale; + var scale: vec2 = (input.scale + vec2(max(0.0, input.shadow_offset.x), max(0.0, input.shadow_offset.y)) + input.shadow_softness * 2.0) * globals.scale; var quad_scale: vec2 = input.scale * globals.scale; var min_border_radius = min(input.scale.x, input.scale.y) * 0.5; @@ -159,8 +159,13 @@ fn solid_fs_main( let shadow_distance = rounded_box_sdf(input.position.xy - input.pos - input.shadow_offset - (input.scale / 2.0), input.scale / 2.0, border_radius); let shadow_alpha = 1.0 - smoothstep(-input.shadow_softness, input.shadow_softness, shadow_distance); let shadow_color = input.shadow_color; + let base_color = select( + vec4(shadow_color.x, shadow_color.y, shadow_color.z, 0.0), + quad_color, + radius_alpha > 0.0 + ); - return mix(quad_color, shadow_color, (1.0 - radius_alpha) * shadow_alpha); + return mix(base_color, shadow_color, (1.0 - radius_alpha) * shadow_alpha); } else { return quad_color; } @@ -366,6 +371,6 @@ fn gradient_fs_main(input: GradientVertexOutput) -> @location(0) vec4 { return vec4(mixed_color.x, mixed_color.y, mixed_color.z, mixed_color.w * radius_alpha); } -fn rounded_box_sdf(center_position: vec2, size: vec2, radius: f32) -> f32 { - return length(max(abs(center_position) - size + vec2(radius, radius), vec2(0.0, 0.0))) - radius; +fn rounded_box_sdf(to_center: vec2, size: vec2, radius: f32) -> f32 { + return length(max(abs(to_center) - size + vec2(radius, radius), vec2(0.0, 0.0))) - radius; }