Detailed changes
@@ -3,8 +3,8 @@ use std::time::Duration;
use anyhow::Result;
use gpui::{
Animation, AnimationExt as _, App, Application, AssetSource, Bounds, Context, SharedString,
- Transformation, Window, WindowBounds, WindowOptions, black, bounce, div, ease_in_out,
- percentage, prelude::*, px, rgb, size, svg,
+ Transformation, Window, WindowBounds, WindowOptions, bounce, div, ease_in_out, percentage,
+ prelude::*, px, size, svg,
};
struct Assets {}
@@ -37,37 +37,66 @@ struct AnimationExample {}
impl Render for AnimationExample {
fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
- div().flex().flex_col().size_full().justify_around().child(
- div().flex().flex_row().w_full().justify_around().child(
+ div()
+ .flex()
+ .flex_col()
+ .size_full()
+ .bg(gpui::white())
+ .text_color(gpui::black())
+ .justify_around()
+ .child(
div()
.flex()
- .bg(rgb(0x2e7d32))
- .size(px(300.0))
- .justify_center()
- .items_center()
- .shadow_lg()
- .text_xl()
- .text_color(black())
- .child("hello")
+ .flex_col()
+ .size_full()
+ .justify_around()
.child(
- svg()
- .size_8()
- .path(ARROW_CIRCLE_SVG)
- .text_color(black())
- .with_animation(
- "image_circle",
- Animation::new(Duration::from_secs(2))
- .repeat()
- .with_easing(bounce(ease_in_out)),
- |svg, delta| {
- svg.with_transformation(Transformation::rotate(percentage(
- delta,
- )))
- },
+ div()
+ .id("content")
+ .flex()
+ .flex_col()
+ .h(px(150.))
+ .overflow_y_scroll()
+ .w_full()
+ .flex_1()
+ .justify_center()
+ .items_center()
+ .text_xl()
+ .gap_4()
+ .child("Hello Animation")
+ .child(
+ svg()
+ .size_20()
+ .overflow_hidden()
+ .path(ARROW_CIRCLE_SVG)
+ .text_color(gpui::black())
+ .with_animation(
+ "image_circle",
+ Animation::new(Duration::from_secs(2))
+ .repeat()
+ .with_easing(bounce(ease_in_out)),
+ |svg, delta| {
+ svg.with_transformation(Transformation::rotate(
+ percentage(delta),
+ ))
+ },
+ ),
),
+ )
+ .child(
+ div()
+ .flex()
+ .h(px(64.))
+ .w_full()
+ .p_2()
+ .justify_center()
+ .items_center()
+ .border_t_1()
+ .border_color(gpui::black().opacity(0.1))
+ .bg(gpui::black().opacity(0.05))
+ .child("Other Panel"),
),
- ),
- )
+ )
}
}
@@ -172,6 +172,12 @@ fn distance_from_clip_rect(unit_vertex: vec2<f32>, bounds: Bounds, clip_bounds:
return distance_from_clip_rect_impl(position, clip_bounds);
}
+fn distance_from_clip_rect_transformed(unit_vertex: vec2<f32>, bounds: Bounds, clip_bounds: Bounds, transform: TransformationMatrix) -> vec4<f32> {
+ let position = unit_vertex * vec2<f32>(bounds.size) + bounds.origin;
+ let transformed = transpose(transform.rotation_scale) * position + transform.translation;
+ return distance_from_clip_rect_impl(transformed, clip_bounds);
+}
+
// https://gamedev.stackexchange.com/questions/92015/optimized-linear-to-srgb-glsl
fn srgb_to_linear(srgb: vec3<f32>) -> vec3<f32> {
let cutoff = srgb < vec3<f32>(0.04045);
@@ -1150,7 +1156,7 @@ fn vs_mono_sprite(@builtin(vertex_index) vertex_id: u32, @builtin(instance_index
out.tile_position = to_tile_position(unit_vertex, sprite.tile);
out.color = hsla_to_rgba(sprite.color);
- out.clip_distances = distance_from_clip_rect(unit_vertex, sprite.bounds, sprite.content_mask);
+ out.clip_distances = distance_from_clip_rect_transformed(unit_vertex, sprite.bounds, sprite.content_mask, sprite.transformation);
return out;
}
@@ -18,6 +18,8 @@ float2 to_tile_position(float2 unit_vertex, AtlasTile tile,
constant Size_DevicePixels *atlas_size);
float4 distance_from_clip_rect(float2 unit_vertex, Bounds_ScaledPixels bounds,
Bounds_ScaledPixels clip_bounds);
+float4 distance_from_clip_rect_transformed(float2 unit_vertex, Bounds_ScaledPixels bounds,
+ Bounds_ScaledPixels clip_bounds, TransformationMatrix transformation);
float corner_dash_velocity(float dv1, float dv2);
float dash_alpha(float t, float period, float length, float dash_velocity,
float antialias_threshold);
@@ -599,13 +601,14 @@ struct MonochromeSpriteVertexOutput {
float4 position [[position]];
float2 tile_position;
float4 color [[flat]];
- float clip_distance [[clip_distance]][4];
+ float4 clip_distance;
};
struct MonochromeSpriteFragmentInput {
float4 position [[position]];
float2 tile_position;
float4 color [[flat]];
+ float4 clip_distance;
};
vertex MonochromeSpriteVertexOutput monochrome_sprite_vertex(
@@ -620,8 +623,8 @@ vertex MonochromeSpriteVertexOutput monochrome_sprite_vertex(
MonochromeSprite sprite = sprites[sprite_id];
float4 device_position =
to_device_position_transformed(unit_vertex, sprite.bounds, sprite.transformation, viewport_size);
- float4 clip_distance = distance_from_clip_rect(unit_vertex, sprite.bounds,
- sprite.content_mask.bounds);
+ float4 clip_distance = distance_from_clip_rect_transformed(unit_vertex, sprite.bounds,
+ sprite.content_mask.bounds, sprite.transformation);
float2 tile_position = to_tile_position(unit_vertex, sprite.tile, atlas_size);
float4 color = hsla_to_rgba(sprite.color);
return MonochromeSpriteVertexOutput{
@@ -635,6 +638,10 @@ fragment float4 monochrome_sprite_fragment(
MonochromeSpriteFragmentInput input [[stage_in]],
constant MonochromeSprite *sprites [[buffer(SpriteInputIndex_Sprites)]],
texture2d<float> atlas_texture [[texture(SpriteInputIndex_AtlasTexture)]]) {
+ if (any(input.clip_distance < float4(0.0))) {
+ return float4(0.0);
+ }
+
constexpr sampler atlas_texture_sampler(mag_filter::linear,
min_filter::linear);
float4 sample =
@@ -1096,6 +1103,23 @@ float4 distance_from_clip_rect(float2 unit_vertex, Bounds_ScaledPixels bounds,
clip_bounds.origin.y + clip_bounds.size.height - position.y);
}
+float4 distance_from_clip_rect_transformed(float2 unit_vertex, Bounds_ScaledPixels bounds,
+ Bounds_ScaledPixels clip_bounds, TransformationMatrix transformation) {
+ float2 position =
+ unit_vertex * float2(bounds.size.width, bounds.size.height) +
+ float2(bounds.origin.x, bounds.origin.y);
+ float2 transformed_position = float2(0, 0);
+ transformed_position[0] = position[0] * transformation.rotation_scale[0][0] + position[1] * transformation.rotation_scale[0][1];
+ transformed_position[1] = position[0] * transformation.rotation_scale[1][0] + position[1] * transformation.rotation_scale[1][1];
+ transformed_position[0] += transformation.translation[0];
+ transformed_position[1] += transformation.translation[1];
+
+ return float4(transformed_position.x - clip_bounds.origin.x,
+ clip_bounds.origin.x + clip_bounds.size.width - transformed_position.x,
+ transformed_position.y - clip_bounds.origin.y,
+ clip_bounds.origin.y + clip_bounds.size.height - transformed_position.y);
+}
+
float4 over(float4 below, float4 above) {
float4 result;
float alpha = above.a + below.a * (1.0 - above.a);
@@ -107,6 +107,12 @@ float4 distance_from_clip_rect(float2 unit_vertex, Bounds bounds, Bounds clip_bo
return distance_from_clip_rect_impl(position, clip_bounds);
}
+float4 distance_from_clip_rect_transformed(float2 unit_vertex, Bounds bounds, Bounds clip_bounds, TransformationMatrix transformation) {
+ float2 position = unit_vertex * bounds.size + bounds.origin;
+ float2 transformed = mul(position, transformation.rotation_scale) + transformation.translation;
+ return distance_from_clip_rect_impl(transformed, clip_bounds);
+}
+
// Convert linear RGB to sRGB
float3 linear_to_srgb(float3 color) {
return pow(color, float3(2.2, 2.2, 2.2));
@@ -1088,7 +1094,7 @@ MonochromeSpriteVertexOutput monochrome_sprite_vertex(uint vertex_id: SV_VertexI
MonochromeSprite sprite = mono_sprites[sprite_id];
float4 device_position =
to_device_position_transformed(unit_vertex, sprite.bounds, sprite.transformation);
- float4 clip_distance = distance_from_clip_rect(unit_vertex, sprite.bounds, sprite.content_mask);
+ float4 clip_distance = distance_from_clip_rect_transformed(unit_vertex, sprite.bounds, sprite.content_mask, sprite.transformation);
float2 tile_position = to_tile_position(unit_vertex, sprite.tile);
float4 color = hsla_to_rgba(sprite.color);
@@ -54,7 +54,10 @@ impl SvgRenderer {
}
}
- pub(crate) fn render(&self, params: &RenderSvgParams) -> Result<Option<Vec<u8>>> {
+ pub(crate) fn render(
+ &self,
+ params: &RenderSvgParams,
+ ) -> Result<Option<(Size<DevicePixels>, Vec<u8>)>> {
anyhow::ensure!(!params.size.is_zero(), "can't render at a zero size");
// Load the tree.
@@ -65,30 +68,33 @@ impl SvgRenderer {
let pixmap = self.render_pixmap(&bytes, SvgSize::Size(params.size))?;
// Convert the pixmap's pixels into an alpha mask.
+ let size = Size::new(
+ DevicePixels(pixmap.width() as i32),
+ DevicePixels(pixmap.height() as i32),
+ );
let alpha_mask = pixmap
.pixels()
.iter()
.map(|p| p.alpha())
.collect::<Vec<_>>();
- Ok(Some(alpha_mask))
+ Ok(Some((size, alpha_mask)))
}
pub fn render_pixmap(&self, bytes: &[u8], size: SvgSize) -> Result<Pixmap, usvg::Error> {
let tree = usvg::Tree::from_data(bytes, &self.usvg_options)?;
-
- let size = match size {
- SvgSize::Size(size) => size,
- SvgSize::ScaleFactor(scale) => crate::size(
- DevicePixels((tree.size().width() * scale) as i32),
- DevicePixels((tree.size().height() * scale) as i32),
- ),
+ let svg_size = tree.size();
+ let scale = match size {
+ SvgSize::Size(size) => size.width.0 as f32 / svg_size.width(),
+ SvgSize::ScaleFactor(scale) => scale,
};
// Render the SVG to a pixmap with the specified width and height.
- let mut pixmap = resvg::tiny_skia::Pixmap::new(size.width.into(), size.height.into())
- .ok_or(usvg::Error::InvalidSize)?;
+ let mut pixmap = resvg::tiny_skia::Pixmap::new(
+ (svg_size.width() * scale) as u32,
+ (svg_size.height() * scale) as u32,
+ )
+ .ok_or(usvg::Error::InvalidSize)?;
- let scale = size.width.0 as f32 / tree.size().width();
let transform = resvg::tiny_skia::Transform::from_scale(scale, scale);
resvg::render(&tree, transform, &mut pixmap.as_mut());
@@ -3082,22 +3082,31 @@ impl Window {
let Some(tile) =
self.sprite_atlas
.get_or_insert_with(¶ms.clone().into(), &mut || {
- let Some(bytes) = cx.svg_renderer.render(¶ms)? else {
+ let Some((size, bytes)) = cx.svg_renderer.render(¶ms)? else {
return Ok(None);
};
- Ok(Some((params.size, Cow::Owned(bytes))))
+ Ok(Some((size, Cow::Owned(bytes))))
})?
else {
return Ok(());
};
let content_mask = self.content_mask().scale(scale_factor);
+ let svg_bounds = Bounds {
+ origin: bounds.center()
+ - Point::new(
+ ScaledPixels(tile.bounds.size.width.0 as f32 / SMOOTH_SVG_SCALE_FACTOR / 2.),
+ ScaledPixels(tile.bounds.size.height.0 as f32 / SMOOTH_SVG_SCALE_FACTOR / 2.),
+ ),
+ size: tile
+ .bounds
+ .size
+ .map(|value| ScaledPixels(value.0 as f32 / SMOOTH_SVG_SCALE_FACTOR)),
+ };
self.next_frame.scene.insert_primitive(MonochromeSprite {
order: 0,
pad: 0,
- bounds: bounds
- .map_origin(|origin| origin.floor())
- .map_size(|size| size.ceil()),
+ bounds: svg_bounds,
content_mask,
color: color.opacity(element_opacity),
tile,