Example 02 realized
This commit is contained in:
@@ -43,10 +43,20 @@ struct TextVertex {
|
||||
|
||||
#[derive(Clone, Copy, Debug, Default)]
|
||||
struct ActiveClip {
|
||||
rect_active: bool,
|
||||
rect: Option<Rect>,
|
||||
rounded: Option<(Rect, f32)>,
|
||||
}
|
||||
|
||||
impl ActiveClip {
|
||||
fn resolved_rect(self) -> Option<Rect> {
|
||||
if !self.rect_active {
|
||||
return None;
|
||||
}
|
||||
Some(self.rect.unwrap_or_else(empty_clip_rect))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
struct PixelRect {
|
||||
left: i32,
|
||||
@@ -995,8 +1005,20 @@ fn fs_main(input: VertexOut) -> @location(0) vec4<f32> {
|
||||
let text_bounds_clip = text.bounds.map(|bounds| {
|
||||
Rect::new(text.origin.x, text.origin.y, bounds.width, bounds.height)
|
||||
});
|
||||
let clip_rect =
|
||||
intersect_rects(active_clip.rect, text_bounds_clip).map(rect_to_pixel_rect);
|
||||
let logical_clip_rect =
|
||||
match (active_clip.rect_active, active_clip.rect, text_bounds_clip) {
|
||||
(true, Some(active_clip_rect), Some(text_bounds_rect)) => {
|
||||
intersect_rects(Some(active_clip_rect), Some(text_bounds_rect))
|
||||
}
|
||||
(true, Some(active_clip_rect), None) => Some(active_clip_rect),
|
||||
(true, None, _) => None,
|
||||
(false, _, Some(text_bounds_rect)) => Some(text_bounds_rect),
|
||||
(false, _, None) => None,
|
||||
};
|
||||
if active_clip.rect_active && logical_clip_rect.is_none() {
|
||||
continue;
|
||||
}
|
||||
let clip_rect = logical_clip_rect.map(rect_to_pixel_rect);
|
||||
|
||||
for glyph in &text.glyphs {
|
||||
let Some(cache_key) = glyph.cache_key else {
|
||||
@@ -1173,7 +1195,7 @@ fn fs_main(input: VertexOut) -> @location(0) vec4<f32> {
|
||||
.image_cache
|
||||
.get(&key)
|
||||
.expect("image cache entry should exist after insertion");
|
||||
let vertices = build_image_vertices(image.rect, image.uv_rect, logical_size, clip);
|
||||
let vertices = build_image_vertices(image.rect, image.uv_rect, logical_size, clip)?;
|
||||
let vertex_buffer = self
|
||||
.device
|
||||
.create_buffer_init(&wgpu::util::BufferInitDescriptor {
|
||||
@@ -1210,7 +1232,7 @@ fn fs_main(input: VertexOut) -> @location(0) vec4<f32> {
|
||||
text.origin.x + cached.origin_offset.x,
|
||||
text.origin.y + cached.origin_offset.y,
|
||||
);
|
||||
let vertices = build_text_vertices(origin, cached.size, logical_size, clip);
|
||||
let vertices = build_text_vertices(origin, cached.size, logical_size, clip)?;
|
||||
let vertex_buffer = self
|
||||
.device
|
||||
.create_buffer_init(&wgpu::util::BufferInitDescriptor {
|
||||
@@ -1635,7 +1657,7 @@ fn build_text_vertices(
|
||||
size: UiSize,
|
||||
logical_size: UiSize,
|
||||
clip: ActiveClip,
|
||||
) -> [TextVertex; 6] {
|
||||
) -> Option<[TextVertex; 6]> {
|
||||
let rect = Rect::new(origin.x, origin.y, size.width, size.height);
|
||||
build_textured_vertices(
|
||||
rect,
|
||||
@@ -1651,7 +1673,7 @@ fn build_image_vertices(
|
||||
uv_rect: (f32, f32, f32, f32),
|
||||
logical_size: UiSize,
|
||||
clip: ActiveClip,
|
||||
) -> [TextVertex; 6] {
|
||||
) -> Option<[TextVertex; 6]> {
|
||||
build_textured_vertices(rect, uv_rect, [1.0, 1.0, 1.0, 1.0], logical_size, clip)
|
||||
}
|
||||
|
||||
@@ -1661,7 +1683,8 @@ fn build_textured_vertices(
|
||||
color: [f32; 4],
|
||||
logical_size: UiSize,
|
||||
clip: ActiveClip,
|
||||
) -> [TextVertex; 6] {
|
||||
) -> Option<[TextVertex; 6]> {
|
||||
let (rect, (u0, v0, u1, v1)) = clip_textured_rect(rect, uv_rect, clip.resolved_rect())?;
|
||||
let left = to_ndc_x(rect.origin.x, logical_size.width.max(1.0));
|
||||
let right = to_ndc_x(rect.origin.x + rect.size.width, logical_size.width.max(1.0));
|
||||
let top = to_ndc_y(rect.origin.y, logical_size.height.max(1.0));
|
||||
@@ -1669,11 +1692,10 @@ fn build_textured_vertices(
|
||||
rect.origin.y + rect.size.height,
|
||||
logical_size.height.max(1.0),
|
||||
);
|
||||
let (u0, v0, u1, v1) = uv_rect;
|
||||
let clip_rect = clip_rect_array(clip.rect);
|
||||
let clip_rect = clip_rect_array(clip);
|
||||
let (rounded_clip_rect, clip_params) = rounded_clip_arrays(clip);
|
||||
|
||||
[
|
||||
Some([
|
||||
TextVertex {
|
||||
position: [left, top],
|
||||
world_position: [rect.origin.x, rect.origin.y],
|
||||
@@ -1731,7 +1753,7 @@ fn build_textured_vertices(
|
||||
rounded_clip_rect,
|
||||
clip_params,
|
||||
},
|
||||
]
|
||||
])
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
@@ -1754,7 +1776,7 @@ fn push_glyph_vertices(
|
||||
let bottom = to_ndc_y(dest_rect.bottom as f32, logical_size.height.max(1.0));
|
||||
|
||||
let color = color_to_f32(color);
|
||||
let clip_rect = clip_rect_array(clip.rect);
|
||||
let clip_rect = clip_rect_array(clip);
|
||||
let (rounded_clip_rect, clip_params) = rounded_clip_arrays(clip);
|
||||
vertices.extend_from_slice(&[
|
||||
TextVertex {
|
||||
@@ -1959,7 +1981,7 @@ fn push_shape_vertices(
|
||||
let rect_data = rect_to_array(shader_rect);
|
||||
let fill_color = fill_color.map_or([0.0, 0.0, 0.0, 0.0], color_to_f32);
|
||||
let border_color = border_color.map_or([0.0, 0.0, 0.0, 0.0], color_to_f32);
|
||||
let clip_rect = clip_rect_array(clip.rect);
|
||||
let clip_rect = clip_rect_array(clip);
|
||||
let (rounded_clip_rect, clip_params) = rounded_clip_arrays(clip);
|
||||
let shadow_base_rect = shadow_base_rect.map_or([0.0, 0.0, 0.0, 0.0], rect_to_array);
|
||||
|
||||
@@ -2091,8 +2113,9 @@ fn shadow_blur_extent(blur: f32) -> f32 {
|
||||
blur.max(0.0) * 2.0
|
||||
}
|
||||
|
||||
fn clip_rect_array(rect: Option<Rect>) -> [f32; 4] {
|
||||
rect.map_or([0.0, 0.0, 0.0, 0.0], rect_to_array)
|
||||
fn clip_rect_array(clip: ActiveClip) -> [f32; 4] {
|
||||
clip.resolved_rect()
|
||||
.map_or([0.0, 0.0, 0.0, 0.0], rect_to_array)
|
||||
}
|
||||
|
||||
fn rounded_clip_arrays(clip: ActiveClip) -> ([f32; 4], [f32; 2]) {
|
||||
@@ -2100,7 +2123,7 @@ fn rounded_clip_arrays(clip: ActiveClip) -> ([f32; 4], [f32; 2]) {
|
||||
.rounded
|
||||
.map_or([0.0, 0.0, 0.0, 0.0], |(rect, _)| rect_to_array(rect));
|
||||
let clip_params = [
|
||||
if clip.rect.is_some() { 1.0 } else { 0.0 },
|
||||
if clip.rect_active { 1.0 } else { 0.0 },
|
||||
clip.rounded.map_or(0.0, |(_, radius)| radius),
|
||||
];
|
||||
(rounded_clip_rect, clip_params)
|
||||
@@ -2111,7 +2134,12 @@ fn push_clip_state(stack: &mut Vec<ActiveClip>, active: &mut ActiveClip, region:
|
||||
if region.radius > 0.0 {
|
||||
active.rounded = Some((region.rect, region.radius));
|
||||
}
|
||||
active.rect = Some(intersect_rects(active.rect, Some(region.rect)).unwrap_or(region.rect));
|
||||
active.rect = if active.rect_active {
|
||||
intersect_rects(active.rect, Some(region.rect))
|
||||
} else {
|
||||
Some(region.rect)
|
||||
};
|
||||
active.rect_active = true;
|
||||
}
|
||||
|
||||
fn pop_clip_state(stack: &mut Vec<ActiveClip>, active: &mut ActiveClip) {
|
||||
@@ -2136,6 +2164,10 @@ fn intersect_rects(first: Option<Rect>, second: Option<Rect>) -> Option<Rect> {
|
||||
}
|
||||
}
|
||||
|
||||
fn empty_clip_rect() -> Rect {
|
||||
Rect::new(1.0, 1.0, -1.0, -1.0)
|
||||
}
|
||||
|
||||
fn to_ndc_x(x: f32, width: f32) -> f32 {
|
||||
(x / width) * 2.0 - 1.0
|
||||
}
|
||||
@@ -2183,6 +2215,43 @@ fn clipped_glyph_quad(
|
||||
Some((clipped, (u0, v0, u1, v1)))
|
||||
}
|
||||
|
||||
fn clip_textured_rect(
|
||||
rect: Rect,
|
||||
uv_rect: (f32, f32, f32, f32),
|
||||
clip_rect: Option<Rect>,
|
||||
) -> Option<(Rect, (f32, f32, f32, f32))> {
|
||||
let clipped = if let Some(clip) = clip_rect {
|
||||
let left = rect.origin.x.max(clip.origin.x);
|
||||
let top = rect.origin.y.max(clip.origin.y);
|
||||
let right = (rect.origin.x + rect.size.width).min(clip.origin.x + clip.size.width);
|
||||
let bottom = (rect.origin.y + rect.size.height).min(clip.origin.y + clip.size.height);
|
||||
if right <= left || bottom <= top {
|
||||
return None;
|
||||
}
|
||||
Rect::new(left, top, right - left, bottom - top)
|
||||
} else {
|
||||
rect
|
||||
};
|
||||
|
||||
if rect.size.width <= 0.0 || rect.size.height <= 0.0 {
|
||||
return None;
|
||||
}
|
||||
|
||||
let (u0, v0, u1, v1) = uv_rect;
|
||||
let clipped_u0 = u0 + (u1 - u0) * ((clipped.origin.x - rect.origin.x) / rect.size.width);
|
||||
let clipped_v0 = v0 + (v1 - v0) * ((clipped.origin.y - rect.origin.y) / rect.size.height);
|
||||
let clipped_u1 = u1
|
||||
- (u1 - u0)
|
||||
* (((rect.origin.x + rect.size.width) - (clipped.origin.x + clipped.size.width))
|
||||
/ rect.size.width);
|
||||
let clipped_v1 = v1
|
||||
- (v1 - v0)
|
||||
* (((rect.origin.y + rect.size.height) - (clipped.origin.y + clipped.size.height))
|
||||
/ rect.size.height);
|
||||
|
||||
Some((clipped, (clipped_u0, clipped_v0, clipped_u1, clipped_v1)))
|
||||
}
|
||||
|
||||
fn text_texture_key(text: &PreparedText) -> TextTextureKey {
|
||||
TextTextureKey {
|
||||
text: text.text.clone(),
|
||||
@@ -2208,9 +2277,13 @@ fn text_texture_key(text: &PreparedText) -> TextTextureKey {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{blend_rgba, build_vertices, text_texture_key};
|
||||
use super::{
|
||||
ActiveClip, blend_rgba, build_text_vertices, build_vertices, clip_rect_array,
|
||||
push_clip_state, rounded_clip_arrays, text_texture_key,
|
||||
};
|
||||
use ruin_ui::{
|
||||
Color, GlyphInstance, Point, PreparedText, Rect, SceneSnapshot, TextSelectionStyle, UiSize,
|
||||
ClipRegion, Color, GlyphInstance, Point, PreparedText, Rect, SceneSnapshot,
|
||||
TextSelectionStyle, UiSize,
|
||||
};
|
||||
|
||||
#[test]
|
||||
@@ -2284,4 +2357,44 @@ mod tests {
|
||||
|
||||
assert_eq!(text_texture_key(&first), text_texture_key(&second));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn nested_clip_with_empty_intersection_stays_clipped() {
|
||||
let mut clip_stack = Vec::new();
|
||||
let mut active_clip = ActiveClip::default();
|
||||
|
||||
push_clip_state(
|
||||
&mut clip_stack,
|
||||
&mut active_clip,
|
||||
ClipRegion {
|
||||
rect: Rect::new(0.0, 0.0, 100.0, 100.0),
|
||||
radius: 0.0,
|
||||
},
|
||||
);
|
||||
push_clip_state(
|
||||
&mut clip_stack,
|
||||
&mut active_clip,
|
||||
ClipRegion {
|
||||
rect: Rect::new(150.0, 150.0, 50.0, 50.0),
|
||||
radius: 8.0,
|
||||
},
|
||||
);
|
||||
|
||||
assert!(active_clip.rect_active);
|
||||
assert_eq!(active_clip.rect, None);
|
||||
let clip_rect = clip_rect_array(active_clip);
|
||||
assert!(clip_rect[0] > clip_rect[2]);
|
||||
assert!(clip_rect[1] > clip_rect[3]);
|
||||
let (_, clip_params) = rounded_clip_arrays(active_clip);
|
||||
assert_eq!(clip_params[0], 1.0);
|
||||
assert!(
|
||||
build_text_vertices(
|
||||
Point::new(160.0, 160.0),
|
||||
UiSize::new(24.0, 12.0),
|
||||
UiSize::new(400.0, 400.0),
|
||||
active_clip,
|
||||
)
|
||||
.is_none()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user