WIP: Start on squiggly underlines
This commit is contained in:
parent
a890787923
commit
52594fe250
4 changed files with 141 additions and 3 deletions
|
@ -26,6 +26,7 @@ pub struct Renderer {
|
||||||
sprite_pipeline_state: metal::RenderPipelineState,
|
sprite_pipeline_state: metal::RenderPipelineState,
|
||||||
image_pipeline_state: metal::RenderPipelineState,
|
image_pipeline_state: metal::RenderPipelineState,
|
||||||
path_atlas_pipeline_state: metal::RenderPipelineState,
|
path_atlas_pipeline_state: metal::RenderPipelineState,
|
||||||
|
underline_pipeline_state: metal::RenderPipelineState,
|
||||||
unit_vertices: metal::Buffer,
|
unit_vertices: metal::Buffer,
|
||||||
instances: metal::Buffer,
|
instances: metal::Buffer,
|
||||||
}
|
}
|
||||||
|
@ -109,6 +110,14 @@ impl Renderer {
|
||||||
"path_atlas_fragment",
|
"path_atlas_fragment",
|
||||||
MTLPixelFormat::R16Float,
|
MTLPixelFormat::R16Float,
|
||||||
);
|
);
|
||||||
|
let underline_pipeline_state = build_pipeline_state(
|
||||||
|
&device,
|
||||||
|
&library,
|
||||||
|
"underline",
|
||||||
|
"underline_vertex",
|
||||||
|
"underline_fragment",
|
||||||
|
pixel_format,
|
||||||
|
);
|
||||||
Self {
|
Self {
|
||||||
sprite_cache,
|
sprite_cache,
|
||||||
image_cache,
|
image_cache,
|
||||||
|
@ -118,6 +127,7 @@ impl Renderer {
|
||||||
sprite_pipeline_state,
|
sprite_pipeline_state,
|
||||||
image_pipeline_state,
|
image_pipeline_state,
|
||||||
path_atlas_pipeline_state,
|
path_atlas_pipeline_state,
|
||||||
|
underline_pipeline_state,
|
||||||
unit_vertices,
|
unit_vertices,
|
||||||
instances,
|
instances,
|
||||||
}
|
}
|
||||||
|
@ -339,7 +349,7 @@ impl Renderer {
|
||||||
drawable_size,
|
drawable_size,
|
||||||
command_encoder,
|
command_encoder,
|
||||||
);
|
);
|
||||||
self.render_quads(
|
self.render_underlines(
|
||||||
layer.underlines(),
|
layer.underlines(),
|
||||||
scale_factor,
|
scale_factor,
|
||||||
offset,
|
offset,
|
||||||
|
@ -821,6 +831,73 @@ impl Renderer {
|
||||||
);
|
);
|
||||||
*offset = next_offset;
|
*offset = next_offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn render_underlines(
|
||||||
|
&mut self,
|
||||||
|
underlines: &[Quad],
|
||||||
|
scale_factor: f32,
|
||||||
|
offset: &mut usize,
|
||||||
|
drawable_size: Vector2F,
|
||||||
|
command_encoder: &metal::RenderCommandEncoderRef,
|
||||||
|
) {
|
||||||
|
if underlines.is_empty() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
align_offset(offset);
|
||||||
|
let next_offset = *offset + underlines.len() * mem::size_of::<shaders::GPUIUnderline>();
|
||||||
|
assert!(
|
||||||
|
next_offset <= INSTANCE_BUFFER_SIZE,
|
||||||
|
"instance buffer exhausted"
|
||||||
|
);
|
||||||
|
|
||||||
|
command_encoder.set_render_pipeline_state(&self.underline_pipeline_state);
|
||||||
|
command_encoder.set_vertex_buffer(
|
||||||
|
shaders::GPUIUnderlineInputIndex_GPUIUnderlineInputIndexVertices as u64,
|
||||||
|
Some(&self.unit_vertices),
|
||||||
|
0,
|
||||||
|
);
|
||||||
|
command_encoder.set_vertex_buffer(
|
||||||
|
shaders::GPUIUnderlineInputIndex_GPUIUnderlineInputIndexUnderlines as u64,
|
||||||
|
Some(&self.instances),
|
||||||
|
*offset as u64,
|
||||||
|
);
|
||||||
|
command_encoder.set_vertex_bytes(
|
||||||
|
shaders::GPUIUnderlineInputIndex_GPUIUnderlineInputIndexUniforms as u64,
|
||||||
|
mem::size_of::<shaders::GPUIUniforms>() as u64,
|
||||||
|
[shaders::GPUIUniforms {
|
||||||
|
viewport_size: drawable_size.to_float2(),
|
||||||
|
}]
|
||||||
|
.as_ptr() as *const c_void,
|
||||||
|
);
|
||||||
|
|
||||||
|
let buffer_contents = unsafe {
|
||||||
|
(self.instances.contents() as *mut u8).offset(*offset as isize)
|
||||||
|
as *mut shaders::GPUIUnderline
|
||||||
|
};
|
||||||
|
for (ix, quad) in underlines.iter().enumerate() {
|
||||||
|
let bounds = quad.bounds * scale_factor;
|
||||||
|
let shader_quad = shaders::GPUIUnderline {
|
||||||
|
origin: bounds.origin().round().to_float2(),
|
||||||
|
size: bounds.size().round().to_float2(),
|
||||||
|
thickness: 1. * scale_factor,
|
||||||
|
color: quad
|
||||||
|
.background
|
||||||
|
.unwrap_or(Color::transparent_black())
|
||||||
|
.to_uchar4(),
|
||||||
|
};
|
||||||
|
unsafe {
|
||||||
|
*(buffer_contents.offset(ix as isize)) = shader_quad;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
command_encoder.draw_primitives_instanced(
|
||||||
|
metal::MTLPrimitiveType::Triangle,
|
||||||
|
0,
|
||||||
|
6,
|
||||||
|
underlines.len() as u64,
|
||||||
|
);
|
||||||
|
*offset = next_offset;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_path_atlas_texture_descriptor() -> metal::TextureDescriptor {
|
fn build_path_atlas_texture_descriptor() -> metal::TextureDescriptor {
|
||||||
|
|
|
@ -104,3 +104,18 @@ typedef struct
|
||||||
vector_uchar4 border_color;
|
vector_uchar4 border_color;
|
||||||
float corner_radius;
|
float corner_radius;
|
||||||
} GPUIImage;
|
} GPUIImage;
|
||||||
|
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
GPUIUnderlineInputIndexVertices = 0,
|
||||||
|
GPUIUnderlineInputIndexUnderlines = 1,
|
||||||
|
GPUIUnderlineInputIndexUniforms = 2,
|
||||||
|
} GPUIUnderlineInputIndex;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
vector_float2 origin;
|
||||||
|
vector_float2 size;
|
||||||
|
float thickness;
|
||||||
|
vector_uchar4 color;
|
||||||
|
} GPUIUnderline;
|
||||||
|
|
|
@ -304,3 +304,49 @@ fragment float4 path_atlas_fragment(
|
||||||
float alpha = saturate(0.5 - distance);
|
float alpha = saturate(0.5 - distance);
|
||||||
return float4(alpha, 0., 0., 1.);
|
return float4(alpha, 0., 0., 1.);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct UnderlineFragmentInput {
|
||||||
|
float4 position [[position]];
|
||||||
|
float2 origin;
|
||||||
|
float2 size;
|
||||||
|
float thickness;
|
||||||
|
float4 color;
|
||||||
|
};
|
||||||
|
|
||||||
|
vertex UnderlineFragmentInput underline_vertex(
|
||||||
|
uint unit_vertex_id [[vertex_id]],
|
||||||
|
uint underline_id [[instance_id]],
|
||||||
|
constant float2 *unit_vertices [[buffer(GPUIUnderlineInputIndexVertices)]],
|
||||||
|
constant GPUIUnderline *underlines [[buffer(GPUIUnderlineInputIndexUnderlines)]],
|
||||||
|
constant GPUIUniforms *uniforms [[buffer(GPUIUnderlineInputIndexUniforms)]]
|
||||||
|
) {
|
||||||
|
float2 unit_vertex = unit_vertices[unit_vertex_id];
|
||||||
|
GPUIUnderline underline = underlines[underline_id];
|
||||||
|
float2 position = unit_vertex * underline.size + underline.origin;
|
||||||
|
float4 device_position = to_device_position(position, uniforms->viewport_size);
|
||||||
|
|
||||||
|
return UnderlineFragmentInput {
|
||||||
|
device_position,
|
||||||
|
underline.origin,
|
||||||
|
underline.size,
|
||||||
|
underline.thickness,
|
||||||
|
coloru_to_colorf(underline.color),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fragment float4 underline_fragment(
|
||||||
|
UnderlineFragmentInput input [[stage_in]]
|
||||||
|
) {
|
||||||
|
float half_thickness = input.thickness * 0.5;
|
||||||
|
float2 st = ((input.position.xy - input.origin) / input.size.y) - float2(0., 0.5);
|
||||||
|
float frequency = M_PI_F * 0.75;
|
||||||
|
float amplitude = 0.3;
|
||||||
|
float sine = sin(st.x * frequency) * amplitude;
|
||||||
|
float dSine = cos(st.x * frequency) * amplitude * frequency;
|
||||||
|
float distance = (st.y - sine) / sqrt(1. + dSine * dSine);
|
||||||
|
float distance_in_pixels = distance * input.size.y;
|
||||||
|
float distance_from_top_border = distance_in_pixels - half_thickness;
|
||||||
|
float distance_from_bottom_border = distance_in_pixels + half_thickness;
|
||||||
|
float alpha = saturate(0.5 - max(-distance_from_bottom_border, distance_from_top_border));
|
||||||
|
return input.color * float4(1., 1., 1., alpha);
|
||||||
|
}
|
||||||
|
|
|
@ -290,7 +290,7 @@ impl Line {
|
||||||
|
|
||||||
if let Some((underline_origin, underline_color)) = finished_underline {
|
if let Some((underline_origin, underline_color)) = finished_underline {
|
||||||
cx.scene.push_underline(scene::Quad {
|
cx.scene.push_underline(scene::Quad {
|
||||||
bounds: RectF::from_points(underline_origin, glyph_origin + vec2f(0., 1.)),
|
bounds: RectF::from_points(underline_origin, glyph_origin + vec2f(0., 3.)),
|
||||||
background: Some(underline_color),
|
background: Some(underline_color),
|
||||||
border: Default::default(),
|
border: Default::default(),
|
||||||
corner_radius: 0.,
|
corner_radius: 0.,
|
||||||
|
@ -311,7 +311,7 @@ impl Line {
|
||||||
let line_end = origin + baseline_offset + vec2f(self.layout.width, 0.);
|
let line_end = origin + baseline_offset + vec2f(self.layout.width, 0.);
|
||||||
|
|
||||||
cx.scene.push_underline(scene::Quad {
|
cx.scene.push_underline(scene::Quad {
|
||||||
bounds: RectF::from_points(underline_start, line_end + vec2f(0., 1.)),
|
bounds: RectF::from_points(underline_start, line_end + vec2f(0., 3.)),
|
||||||
background: Some(underline_color),
|
background: Some(underline_color),
|
||||||
border: Default::default(),
|
border: Default::default(),
|
||||||
corner_radius: 0.,
|
corner_radius: 0.,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue