Merge fe0e78e86b
into 10a1140d49
This commit is contained in:
commit
46b8ebbe46
5 changed files with 123 additions and 49 deletions
|
@ -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"),
|
||||
),
|
||||
),
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -141,6 +141,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);
|
||||
|
@ -1117,7 +1123,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);
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -2996,22 +2996,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,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue