From 95da665095297f5f58a09a3eb57045bdfcfec2a8 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Tue, 14 Sep 2021 17:49:11 +0200 Subject: [PATCH] Allow passing a corner radius and borders to rendered images Co-Authored-By: Nathan Sobo --- gpui/src/color.rs | 4 + gpui/src/elements/image.rs | 30 +++++- gpui/src/platform/mac/renderer.rs | 8 ++ gpui/src/platform/mac/shaders/shaders.h | 6 ++ gpui/src/platform/mac/shaders/shaders.metal | 101 +++++++++++--------- gpui/src/scene.rs | 2 + 6 files changed, 101 insertions(+), 50 deletions(-) diff --git a/gpui/src/color.rs b/gpui/src/color.rs index 9c6de6247a..9e31530b27 100644 --- a/gpui/src/color.rs +++ b/gpui/src/color.rs @@ -29,6 +29,10 @@ impl Color { Self(ColorU::white()) } + pub fn red() -> Self { + Self(ColorU::from_u32(0xff0000ff)) + } + pub fn new(r: u8, g: u8, b: u8, a: u8) -> Self { Self(ColorU::new(r, g, b, a)) } diff --git a/gpui/src/elements/image.rs b/gpui/src/elements/image.rs index 44f39d8a30..2611478fe0 100644 --- a/gpui/src/elements/image.rs +++ b/gpui/src/elements/image.rs @@ -1,16 +1,34 @@ use crate::{ geometry::{rect::RectF, vector::Vector2F}, json::{json, ToJson}, - scene, DebugContext, Element, Event, EventContext, ImageData, LayoutContext, PaintContext, - SizeConstraint, + scene, Border, DebugContext, Element, Event, EventContext, ImageData, LayoutContext, + PaintContext, SizeConstraint, }; use std::sync::Arc; -pub struct Image(Arc); +pub struct Image { + data: Arc, + border: Border, + corner_radius: f32, +} impl Image { pub fn new(data: Arc) -> Self { - Self(data) + Self { + data, + border: Default::default(), + corner_radius: Default::default(), + } + } + + pub fn with_corner_radius(mut self, corner_radius: f32) -> Self { + self.corner_radius = corner_radius; + self + } + + pub fn with_border(mut self, border: Border) -> Self { + self.border = border; + self } } @@ -35,7 +53,9 @@ impl Element for Image { ) -> Self::PaintState { cx.scene.push_image(scene::Image { bounds, - data: self.0.clone(), + border: self.border, + corner_radius: self.corner_radius, + data: self.data.clone(), }); } diff --git a/gpui/src/platform/mac/renderer.rs b/gpui/src/platform/mac/renderer.rs index 215863d881..6f712f29b3 100644 --- a/gpui/src/platform/mac/renderer.rs +++ b/gpui/src/platform/mac/renderer.rs @@ -657,6 +657,8 @@ impl Renderer { for image in images { let origin = image.bounds.origin() * scale_factor; let target_size = image.bounds.size() * scale_factor; + let corner_radius = image.corner_radius * scale_factor; + let border_width = image.border.width * scale_factor; let (alloc_id, atlas_bounds) = self .prev_rendered_images .remove(&image.data.id) @@ -675,6 +677,12 @@ impl Renderer { target_size: target_size.to_float2(), source_size: atlas_bounds.size().to_float2(), atlas_origin: atlas_bounds.origin().to_float2(), + border_top: border_width * (image.border.top as usize as f32), + border_right: border_width * (image.border.right as usize as f32), + border_bottom: border_width * (image.border.bottom as usize as f32), + border_left: border_width * (image.border.left as usize as f32), + border_color: image.border.color.to_uchar4(), + corner_radius, }); } diff --git a/gpui/src/platform/mac/shaders/shaders.h b/gpui/src/platform/mac/shaders/shaders.h index b06e2e565f..1b6ad3f26f 100644 --- a/gpui/src/platform/mac/shaders/shaders.h +++ b/gpui/src/platform/mac/shaders/shaders.h @@ -97,4 +97,10 @@ typedef struct vector_float2 target_size; vector_float2 source_size; vector_float2 atlas_origin; + float border_top; + float border_right; + float border_bottom; + float border_left; + vector_uchar4 border_color; + float corner_radius; } GPUIImage; diff --git a/gpui/src/platform/mac/shaders/shaders.metal b/gpui/src/platform/mac/shaders/shaders.metal index c83bac43ad..00338b30e7 100644 --- a/gpui/src/platform/mac/shaders/shaders.metal +++ b/gpui/src/platform/mac/shaders/shaders.metal @@ -34,46 +34,19 @@ float blur_along_x(float x, float y, float sigma, float corner, float2 halfSize) struct QuadFragmentInput { float4 position [[position]]; - vector_float2 origin; - vector_float2 size; - vector_uchar4 background_color; + float2 atlas_position; // only used in the image shader + float2 origin; + float2 size; + float4 background_color; float border_top; float border_right; float border_bottom; float border_left; - vector_uchar4 border_color; + float4 border_color; float corner_radius; }; -vertex QuadFragmentInput quad_vertex( - uint unit_vertex_id [[vertex_id]], - uint quad_id [[instance_id]], - constant float2 *unit_vertices [[buffer(GPUIQuadInputIndexVertices)]], - constant GPUIQuad *quads [[buffer(GPUIQuadInputIndexQuads)]], - constant GPUIUniforms *uniforms [[buffer(GPUIQuadInputIndexUniforms)]] -) { - float2 unit_vertex = unit_vertices[unit_vertex_id]; - GPUIQuad quad = quads[quad_id]; - float2 position = unit_vertex * quad.size + quad.origin; - float4 device_position = to_device_position(position, uniforms->viewport_size); - - return QuadFragmentInput { - device_position, - quad.origin, - quad.size, - quad.background_color, - quad.border_top, - quad.border_right, - quad.border_bottom, - quad.border_left, - quad.border_color, - quad.corner_radius, - }; -} - -fragment float4 quad_fragment( - QuadFragmentInput input [[stage_in]] -) { +float4 quad_sdf(QuadFragmentInput input) { float2 half_size = input.size / 2.; float2 center = input.origin + half_size; float2 center_to_point = input.position.xy - center; @@ -95,12 +68,12 @@ fragment float4 quad_fragment( float4 color; if (border_width == 0.) { - color = coloru_to_colorf(input.background_color); + color = input.background_color; } else { float inset_distance = distance + border_width; color = mix( - coloru_to_colorf(input.border_color), - coloru_to_colorf(input.background_color), + input.border_color, + input.background_color, saturate(0.5 - inset_distance) ); } @@ -109,6 +82,39 @@ fragment float4 quad_fragment( return coverage * color; } +vertex QuadFragmentInput quad_vertex( + uint unit_vertex_id [[vertex_id]], + uint quad_id [[instance_id]], + constant float2 *unit_vertices [[buffer(GPUIQuadInputIndexVertices)]], + constant GPUIQuad *quads [[buffer(GPUIQuadInputIndexQuads)]], + constant GPUIUniforms *uniforms [[buffer(GPUIQuadInputIndexUniforms)]] +) { + float2 unit_vertex = unit_vertices[unit_vertex_id]; + GPUIQuad quad = quads[quad_id]; + float2 position = unit_vertex * quad.size + quad.origin; + float4 device_position = to_device_position(position, uniforms->viewport_size); + + return QuadFragmentInput { + device_position, + float2(0., 0.), + quad.origin, + quad.size, + coloru_to_colorf(quad.background_color), + quad.border_top, + quad.border_right, + quad.border_bottom, + quad.border_left, + coloru_to_colorf(quad.border_color), + quad.corner_radius, + }; +} + +fragment float4 quad_fragment( + QuadFragmentInput input [[stage_in]] +) { + return quad_sdf(input); +} + struct ShadowFragmentInput { float4 position [[position]]; vector_float2 origin; @@ -217,12 +223,7 @@ fragment float4 sprite_fragment( return color; } -struct ImageFragmentInput { - float4 position [[position]]; - float2 atlas_position; -}; - -vertex ImageFragmentInput image_vertex( +vertex QuadFragmentInput image_vertex( uint unit_vertex_id [[vertex_id]], uint image_id [[instance_id]], constant float2 *unit_vertices [[buffer(GPUIImageVertexInputIndexVertices)]], @@ -236,18 +237,28 @@ vertex ImageFragmentInput image_vertex( float4 device_position = to_device_position(position, *viewport_size); float2 atlas_position = (unit_vertex * image.source_size + image.atlas_origin) / *atlas_size; - return ImageFragmentInput { + return QuadFragmentInput { device_position, atlas_position, + image.origin, + image.target_size, + float4(0.), + image.border_top, + image.border_right, + image.border_bottom, + image.border_left, + coloru_to_colorf(image.border_color), + image.corner_radius, }; } fragment float4 image_fragment( - ImageFragmentInput input [[stage_in]], + QuadFragmentInput input [[stage_in]], texture2d atlas [[ texture(GPUIImageFragmentInputIndexAtlas) ]] ) { constexpr sampler atlas_sampler(mag_filter::linear, min_filter::linear); - return atlas.sample(atlas_sampler, input.atlas_position); + input.background_color = atlas.sample(atlas_sampler, input.atlas_position); + return quad_sdf(input); } struct PathAtlasVertexOutput { diff --git a/gpui/src/scene.rs b/gpui/src/scene.rs index ab6ca71a06..1b9c863647 100644 --- a/gpui/src/scene.rs +++ b/gpui/src/scene.rs @@ -128,6 +128,8 @@ pub struct PathVertex { pub struct Image { pub bounds: RectF, + pub border: Border, + pub corner_radius: f32, pub data: Arc, }