Allow passing a corner radius and borders to rendered images

Co-Authored-By: Nathan Sobo <nathan@zed.dev>
This commit is contained in:
Antonio Scandurra 2021-09-14 17:49:11 +02:00
parent 96ade8668f
commit 95da665095
6 changed files with 101 additions and 50 deletions

View File

@ -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))
}

View File

@ -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<ImageData>);
pub struct Image {
data: Arc<ImageData>,
border: Border,
corner_radius: f32,
}
impl Image {
pub fn new(data: Arc<ImageData>) -> 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(),
});
}

View File

@ -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,
});
}

View File

@ -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;

View File

@ -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<float> 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 {

View File

@ -128,6 +128,8 @@ pub struct PathVertex {
pub struct Image {
pub bounds: RectF,
pub border: Border,
pub corner_radius: f32,
pub data: Arc<ImageData>,
}