Skip to content

Commit

Permalink
fix: maths
Browse files Browse the repository at this point in the history
  • Loading branch information
nicksenger committed Jul 17, 2023
1 parent 5cd8e33 commit 3fa3570
Show file tree
Hide file tree
Showing 3 changed files with 104 additions and 113 deletions.
10 changes: 6 additions & 4 deletions graphics/src/damage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,16 +46,18 @@ impl<T: Damage> Damage for Primitive<T> {
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)
Expand Down
194 changes: 89 additions & 105 deletions tiny_skia/src/backend.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::<Vec<_>>();
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,
)
}
}

Expand Down
13 changes: 9 additions & 4 deletions wgpu/src/shader/quad.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ fn solid_vs_main(input: SolidVertexInput) -> SolidVertexOutput {

var pos: vec2<f32> = (input.pos + min(input.shadow_offset, vec2<f32>(0.0, 0.0)) - input.shadow_softness) * globals.scale;
var quad_pos: vec2<f32> = input.pos * globals.scale;
var scale: vec2<f32> = (input.scale + (vec2<f32>(max(0.0, input.shadow_offset.x), max(0.0, input.shadow_offset.y)) + input.shadow_softness) * 2.0) * globals.scale;
var scale: vec2<f32> = (input.scale + vec2<f32>(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<f32> = input.scale * globals.scale;

var min_border_radius = min(input.scale.x, input.scale.y) * 0.5;
Expand Down Expand Up @@ -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<f32>(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;
}
Expand Down Expand Up @@ -366,6 +371,6 @@ fn gradient_fs_main(input: GradientVertexOutput) -> @location(0) vec4<f32> {
return vec4<f32>(mixed_color.x, mixed_color.y, mixed_color.z, mixed_color.w * radius_alpha);
}

fn rounded_box_sdf(center_position: vec2<f32>, size: vec2<f32>, radius: f32) -> f32 {
return length(max(abs(center_position) - size + vec2<f32>(radius, radius), vec2<f32>(0.0, 0.0))) - radius;
fn rounded_box_sdf(to_center: vec2<f32>, size: vec2<f32>, radius: f32) -> f32 {
return length(max(abs(to_center) - size + vec2<f32>(radius, radius), vec2<f32>(0.0, 0.0))) - radius;
}

0 comments on commit 3fa3570

Please sign in to comment.