mirror of
https://github.com/zed-industries/zed.git
synced 2025-01-05 23:51:08 +00:00
Checkpoint: underlines
This commit is contained in:
parent
65c7765c07
commit
ca6eb5511c
9 changed files with 495 additions and 155 deletions
|
@ -50,10 +50,12 @@ fn generate_shader_bindings() -> PathBuf {
|
|||
"ScaledContentMask".into(),
|
||||
"Uniforms".into(),
|
||||
"AtlasTile".into(),
|
||||
"QuadInputIndex".into(),
|
||||
"Quad".into(),
|
||||
"ShadowInputIndex".into(),
|
||||
"Shadow".into(),
|
||||
"QuadInputIndex".into(),
|
||||
"Underline".into(),
|
||||
"UnderlineInputIndex".into(),
|
||||
"Quad".into(),
|
||||
"SpriteInputIndex".into(),
|
||||
"MonochromeSprite".into(),
|
||||
"PolychromeSprite".into(),
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use crate::{
|
||||
point, size, AtlasTextureId, DevicePixels, MetalAtlas, MonochromeSprite, PolychromeSprite,
|
||||
Quad, Scene, Shadow, Size,
|
||||
PrimitiveBatch, Quad, Scene, Shadow, Size, Underline,
|
||||
};
|
||||
use cocoa::{
|
||||
base::{NO, YES},
|
||||
|
@ -17,8 +17,9 @@ const INSTANCE_BUFFER_SIZE: usize = 8192 * 1024; // This is an arbitrary decisio
|
|||
pub struct MetalRenderer {
|
||||
layer: metal::MetalLayer,
|
||||
command_queue: CommandQueue,
|
||||
quads_pipeline_state: metal::RenderPipelineState,
|
||||
shadows_pipeline_state: metal::RenderPipelineState,
|
||||
quads_pipeline_state: metal::RenderPipelineState,
|
||||
underlines_pipeline_state: metal::RenderPipelineState,
|
||||
monochrome_sprites_pipeline_state: metal::RenderPipelineState,
|
||||
polychrome_sprites_pipeline_state: metal::RenderPipelineState,
|
||||
unit_vertices: metal::Buffer,
|
||||
|
@ -83,6 +84,14 @@ impl MetalRenderer {
|
|||
MTLResourceOptions::StorageModeManaged,
|
||||
);
|
||||
|
||||
let shadows_pipeline_state = build_pipeline_state(
|
||||
&device,
|
||||
&library,
|
||||
"shadows",
|
||||
"shadow_vertex",
|
||||
"shadow_fragment",
|
||||
PIXEL_FORMAT,
|
||||
);
|
||||
let quads_pipeline_state = build_pipeline_state(
|
||||
&device,
|
||||
&library,
|
||||
|
@ -91,12 +100,12 @@ impl MetalRenderer {
|
|||
"quad_fragment",
|
||||
PIXEL_FORMAT,
|
||||
);
|
||||
let shadows_pipeline_state = build_pipeline_state(
|
||||
let underlines_pipeline_state = build_pipeline_state(
|
||||
&device,
|
||||
&library,
|
||||
"shadows",
|
||||
"shadow_vertex",
|
||||
"shadow_fragment",
|
||||
"underlines",
|
||||
"underline_vertex",
|
||||
"underline_fragment",
|
||||
PIXEL_FORMAT,
|
||||
);
|
||||
let monochrome_sprites_pipeline_state = build_pipeline_state(
|
||||
|
@ -122,8 +131,9 @@ impl MetalRenderer {
|
|||
Self {
|
||||
layer,
|
||||
command_queue,
|
||||
quads_pipeline_state,
|
||||
shadows_pipeline_state,
|
||||
quads_pipeline_state,
|
||||
underlines_pipeline_state,
|
||||
monochrome_sprites_pipeline_state,
|
||||
polychrome_sprites_pipeline_state,
|
||||
unit_vertices,
|
||||
|
@ -184,10 +194,7 @@ impl MetalRenderer {
|
|||
let mut instance_offset = 0;
|
||||
for batch in scene.batches() {
|
||||
match batch {
|
||||
crate::PrimitiveBatch::Quads(quads) => {
|
||||
self.draw_quads(quads, &mut instance_offset, viewport_size, command_encoder);
|
||||
}
|
||||
crate::PrimitiveBatch::Shadows(shadows) => {
|
||||
PrimitiveBatch::Shadows(shadows) => {
|
||||
self.draw_shadows(
|
||||
shadows,
|
||||
&mut instance_offset,
|
||||
|
@ -195,7 +202,18 @@ impl MetalRenderer {
|
|||
command_encoder,
|
||||
);
|
||||
}
|
||||
crate::PrimitiveBatch::MonochromeSprites {
|
||||
PrimitiveBatch::Quads(quads) => {
|
||||
self.draw_quads(quads, &mut instance_offset, viewport_size, command_encoder);
|
||||
}
|
||||
PrimitiveBatch::Underlines(underlines) => {
|
||||
self.draw_underlines(
|
||||
underlines,
|
||||
&mut instance_offset,
|
||||
viewport_size,
|
||||
command_encoder,
|
||||
);
|
||||
}
|
||||
PrimitiveBatch::MonochromeSprites {
|
||||
texture_id,
|
||||
sprites,
|
||||
} => {
|
||||
|
@ -207,7 +225,7 @@ impl MetalRenderer {
|
|||
command_encoder,
|
||||
);
|
||||
}
|
||||
crate::PrimitiveBatch::PolychromeSprites {
|
||||
PrimitiveBatch::PolychromeSprites {
|
||||
texture_id,
|
||||
sprites,
|
||||
} => {
|
||||
|
@ -234,62 +252,6 @@ impl MetalRenderer {
|
|||
drawable.present();
|
||||
}
|
||||
|
||||
fn draw_quads(
|
||||
&mut self,
|
||||
quads: &[Quad],
|
||||
offset: &mut usize,
|
||||
viewport_size: Size<DevicePixels>,
|
||||
command_encoder: &metal::RenderCommandEncoderRef,
|
||||
) {
|
||||
if quads.is_empty() {
|
||||
return;
|
||||
}
|
||||
align_offset(offset);
|
||||
|
||||
command_encoder.set_render_pipeline_state(&self.quads_pipeline_state);
|
||||
command_encoder.set_vertex_buffer(
|
||||
QuadInputIndex::Vertices as u64,
|
||||
Some(&self.unit_vertices),
|
||||
0,
|
||||
);
|
||||
command_encoder.set_vertex_buffer(
|
||||
QuadInputIndex::Quads as u64,
|
||||
Some(&self.instances),
|
||||
*offset as u64,
|
||||
);
|
||||
command_encoder.set_fragment_buffer(
|
||||
QuadInputIndex::Quads as u64,
|
||||
Some(&self.instances),
|
||||
*offset as u64,
|
||||
);
|
||||
|
||||
command_encoder.set_vertex_bytes(
|
||||
QuadInputIndex::ViewportSize as u64,
|
||||
mem::size_of_val(&viewport_size) as u64,
|
||||
&viewport_size as *const Size<DevicePixels> as *const _,
|
||||
);
|
||||
|
||||
let quad_bytes_len = mem::size_of::<Quad>() * quads.len();
|
||||
let buffer_contents = unsafe { (self.instances.contents() as *mut u8).add(*offset) };
|
||||
unsafe {
|
||||
ptr::copy_nonoverlapping(quads.as_ptr() as *const u8, buffer_contents, quad_bytes_len);
|
||||
}
|
||||
|
||||
let next_offset = *offset + quad_bytes_len;
|
||||
assert!(
|
||||
next_offset <= INSTANCE_BUFFER_SIZE,
|
||||
"instance buffer exhausted"
|
||||
);
|
||||
|
||||
command_encoder.draw_primitives_instanced(
|
||||
metal::MTLPrimitiveType::Triangle,
|
||||
0,
|
||||
6,
|
||||
quads.len() as u64,
|
||||
);
|
||||
*offset = next_offset;
|
||||
}
|
||||
|
||||
fn draw_shadows(
|
||||
&mut self,
|
||||
shadows: &[Shadow],
|
||||
|
@ -350,6 +312,122 @@ impl MetalRenderer {
|
|||
*offset = next_offset;
|
||||
}
|
||||
|
||||
fn draw_quads(
|
||||
&mut self,
|
||||
quads: &[Quad],
|
||||
offset: &mut usize,
|
||||
viewport_size: Size<DevicePixels>,
|
||||
command_encoder: &metal::RenderCommandEncoderRef,
|
||||
) {
|
||||
if quads.is_empty() {
|
||||
return;
|
||||
}
|
||||
align_offset(offset);
|
||||
|
||||
command_encoder.set_render_pipeline_state(&self.quads_pipeline_state);
|
||||
command_encoder.set_vertex_buffer(
|
||||
QuadInputIndex::Vertices as u64,
|
||||
Some(&self.unit_vertices),
|
||||
0,
|
||||
);
|
||||
command_encoder.set_vertex_buffer(
|
||||
QuadInputIndex::Quads as u64,
|
||||
Some(&self.instances),
|
||||
*offset as u64,
|
||||
);
|
||||
command_encoder.set_fragment_buffer(
|
||||
QuadInputIndex::Quads as u64,
|
||||
Some(&self.instances),
|
||||
*offset as u64,
|
||||
);
|
||||
|
||||
command_encoder.set_vertex_bytes(
|
||||
QuadInputIndex::ViewportSize as u64,
|
||||
mem::size_of_val(&viewport_size) as u64,
|
||||
&viewport_size as *const Size<DevicePixels> as *const _,
|
||||
);
|
||||
|
||||
let quad_bytes_len = mem::size_of::<Quad>() * quads.len();
|
||||
let buffer_contents = unsafe { (self.instances.contents() as *mut u8).add(*offset) };
|
||||
unsafe {
|
||||
ptr::copy_nonoverlapping(quads.as_ptr() as *const u8, buffer_contents, quad_bytes_len);
|
||||
}
|
||||
|
||||
let next_offset = *offset + quad_bytes_len;
|
||||
assert!(
|
||||
next_offset <= INSTANCE_BUFFER_SIZE,
|
||||
"instance buffer exhausted"
|
||||
);
|
||||
|
||||
command_encoder.draw_primitives_instanced(
|
||||
metal::MTLPrimitiveType::Triangle,
|
||||
0,
|
||||
6,
|
||||
quads.len() as u64,
|
||||
);
|
||||
*offset = next_offset;
|
||||
}
|
||||
|
||||
fn draw_underlines(
|
||||
&mut self,
|
||||
underlines: &[Underline],
|
||||
offset: &mut usize,
|
||||
viewport_size: Size<DevicePixels>,
|
||||
command_encoder: &metal::RenderCommandEncoderRef,
|
||||
) {
|
||||
if underlines.is_empty() {
|
||||
return;
|
||||
}
|
||||
align_offset(offset);
|
||||
|
||||
command_encoder.set_render_pipeline_state(&self.underlines_pipeline_state);
|
||||
command_encoder.set_vertex_buffer(
|
||||
UnderlineInputIndex::Vertices as u64,
|
||||
Some(&self.unit_vertices),
|
||||
0,
|
||||
);
|
||||
command_encoder.set_vertex_buffer(
|
||||
UnderlineInputIndex::Underlines as u64,
|
||||
Some(&self.instances),
|
||||
*offset as u64,
|
||||
);
|
||||
command_encoder.set_fragment_buffer(
|
||||
UnderlineInputIndex::Underlines as u64,
|
||||
Some(&self.instances),
|
||||
*offset as u64,
|
||||
);
|
||||
|
||||
command_encoder.set_vertex_bytes(
|
||||
UnderlineInputIndex::ViewportSize as u64,
|
||||
mem::size_of_val(&viewport_size) as u64,
|
||||
&viewport_size as *const Size<DevicePixels> as *const _,
|
||||
);
|
||||
|
||||
let quad_bytes_len = mem::size_of::<Underline>() * underlines.len();
|
||||
let buffer_contents = unsafe { (self.instances.contents() as *mut u8).add(*offset) };
|
||||
unsafe {
|
||||
ptr::copy_nonoverlapping(
|
||||
underlines.as_ptr() as *const u8,
|
||||
buffer_contents,
|
||||
quad_bytes_len,
|
||||
);
|
||||
}
|
||||
|
||||
let next_offset = *offset + quad_bytes_len;
|
||||
assert!(
|
||||
next_offset <= INSTANCE_BUFFER_SIZE,
|
||||
"instance buffer exhausted"
|
||||
);
|
||||
|
||||
command_encoder.draw_primitives_instanced(
|
||||
metal::MTLPrimitiveType::Triangle,
|
||||
0,
|
||||
6,
|
||||
underlines.len() as u64,
|
||||
);
|
||||
*offset = next_offset;
|
||||
}
|
||||
|
||||
fn draw_monochrome_sprites(
|
||||
&mut self,
|
||||
texture_id: AtlasTextureId,
|
||||
|
@ -533,6 +611,13 @@ fn align_offset(offset: &mut usize) {
|
|||
*offset = ((*offset + 255) / 256) * 256;
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
enum ShadowInputIndex {
|
||||
Vertices = 0,
|
||||
Shadows = 1,
|
||||
ViewportSize = 2,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
enum QuadInputIndex {
|
||||
Vertices = 0,
|
||||
|
@ -541,9 +626,9 @@ enum QuadInputIndex {
|
|||
}
|
||||
|
||||
#[repr(C)]
|
||||
enum ShadowInputIndex {
|
||||
enum UnderlineInputIndex {
|
||||
Vertices = 0,
|
||||
Shadows = 1,
|
||||
Underlines = 1,
|
||||
ViewportSize = 2,
|
||||
}
|
||||
|
||||
|
|
|
@ -193,6 +193,53 @@ fragment float4 shadow_fragment(ShadowVertexOutput input [[stage_in]],
|
|||
return input.color * float4(1., 1., 1., alpha);
|
||||
}
|
||||
|
||||
struct UnderlineVertexOutput {
|
||||
float4 position [[position]];
|
||||
float4 color [[flat]];
|
||||
uint underline_id [[flat]];
|
||||
};
|
||||
|
||||
vertex UnderlineVertexOutput underline_vertex(
|
||||
uint unit_vertex_id [[vertex_id]], uint underline_id [[instance_id]],
|
||||
constant float2 *unit_vertices [[buffer(UnderlineInputIndex_Vertices)]],
|
||||
constant Underline *underlines [[buffer(UnderlineInputIndex_Underlines)]],
|
||||
constant Size_DevicePixels *viewport_size
|
||||
[[buffer(ShadowInputIndex_ViewportSize)]]) {
|
||||
float2 unit_vertex = unit_vertices[unit_vertex_id];
|
||||
Underline underline = underlines[underline_id];
|
||||
float4 device_position =
|
||||
to_device_position(unit_vertex, underline.bounds,
|
||||
underline.content_mask.bounds, viewport_size);
|
||||
float4 color = hsla_to_rgba(underline.color);
|
||||
return UnderlineVertexOutput{device_position, color, underline_id};
|
||||
}
|
||||
|
||||
fragment float4 underline_fragment(UnderlineVertexOutput input [[stage_in]],
|
||||
constant Underline *underlines
|
||||
[[buffer(UnderlineInputIndex_Underlines)]]) {
|
||||
Underline underline = underlines[input.underline_id];
|
||||
if (underline.wavy) {
|
||||
float half_thickness = underline.thickness * 0.5;
|
||||
float2 origin =
|
||||
float2(underline.bounds.origin.x, underline.bounds.origin.y);
|
||||
float2 st = ((input.position.xy - origin) / underline.bounds.size.height) -
|
||||
float2(0., 0.5);
|
||||
float frequency = (M_PI_F * (3. * underline.thickness)) / 8.;
|
||||
float amplitude = 1. / (2. * underline.thickness);
|
||||
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 * underline.bounds.size.height;
|
||||
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);
|
||||
} else {
|
||||
return input.color;
|
||||
}
|
||||
}
|
||||
|
||||
struct MonochromeSpriteVertexOutput {
|
||||
float4 position [[position]];
|
||||
float2 tile_position;
|
||||
|
@ -211,8 +258,8 @@ vertex MonochromeSpriteVertexOutput monochrome_sprite_vertex(
|
|||
|
||||
float2 unit_vertex = unit_vertices[unit_vertex_id];
|
||||
MonochromeSprite sprite = sprites[sprite_id];
|
||||
// Don't apply content mask at the vertex level because we don't have time to
|
||||
// make sampling from the texture match the mask.
|
||||
// Don't apply content mask at the vertex level because we don't have time
|
||||
// to make sampling from the texture match the mask.
|
||||
float4 device_position = to_device_position(unit_vertex, sprite.bounds,
|
||||
sprite.bounds, viewport_size);
|
||||
float2 tile_position = to_tile_position(unit_vertex, sprite.tile, atlas_size);
|
||||
|
@ -254,8 +301,8 @@ vertex PolychromeSpriteVertexOutput polychrome_sprite_vertex(
|
|||
|
||||
float2 unit_vertex = unit_vertices[unit_vertex_id];
|
||||
PolychromeSprite sprite = sprites[sprite_id];
|
||||
// Don't apply content mask at the vertex level because we don't have time to
|
||||
// make sampling from the texture match the mask.
|
||||
// Don't apply content mask at the vertex level because we don't have time
|
||||
// to make sampling from the texture match the mask.
|
||||
float4 device_position = to_device_position(unit_vertex, sprite.bounds,
|
||||
sprite.bounds, viewport_size);
|
||||
float2 tile_position = to_tile_position(unit_vertex, sprite.tile, atlas_size);
|
||||
|
|
|
@ -17,8 +17,9 @@ pub type DrawOrder = u32;
|
|||
pub struct Scene {
|
||||
pub(crate) scale_factor: f32,
|
||||
pub(crate) layers: BTreeMap<StackingOrder, LayerId>,
|
||||
pub quads: Vec<Quad>,
|
||||
pub shadows: Vec<Shadow>,
|
||||
pub quads: Vec<Quad>,
|
||||
pub underlines: Vec<Underline>,
|
||||
pub monochrome_sprites: Vec<MonochromeSprite>,
|
||||
pub polychrome_sprites: Vec<PolychromeSprite>,
|
||||
}
|
||||
|
@ -28,8 +29,9 @@ impl Scene {
|
|||
Scene {
|
||||
scale_factor,
|
||||
layers: BTreeMap::new(),
|
||||
quads: Vec::new(),
|
||||
shadows: Vec::new(),
|
||||
quads: Vec::new(),
|
||||
underlines: Vec::new(),
|
||||
monochrome_sprites: Vec::new(),
|
||||
polychrome_sprites: Vec::new(),
|
||||
}
|
||||
|
@ -39,8 +41,9 @@ impl Scene {
|
|||
Scene {
|
||||
scale_factor: self.scale_factor,
|
||||
layers: mem::take(&mut self.layers),
|
||||
quads: mem::take(&mut self.quads),
|
||||
shadows: mem::take(&mut self.shadows),
|
||||
quads: mem::take(&mut self.quads),
|
||||
underlines: mem::take(&mut self.underlines),
|
||||
monochrome_sprites: mem::take(&mut self.monochrome_sprites),
|
||||
polychrome_sprites: mem::take(&mut self.polychrome_sprites),
|
||||
}
|
||||
|
@ -51,13 +54,17 @@ impl Scene {
|
|||
let layer_id = *self.layers.entry(layer_id).or_insert(next_id);
|
||||
let primitive = primitive.into();
|
||||
match primitive {
|
||||
Primitive::Shadow(mut shadow) => {
|
||||
shadow.order = layer_id;
|
||||
self.shadows.push(shadow);
|
||||
}
|
||||
Primitive::Quad(mut quad) => {
|
||||
quad.order = layer_id;
|
||||
self.quads.push(quad);
|
||||
}
|
||||
Primitive::Shadow(mut shadow) => {
|
||||
shadow.order = layer_id;
|
||||
self.shadows.push(shadow);
|
||||
Primitive::Underline(mut underline) => {
|
||||
underline.order = layer_id;
|
||||
self.underlines.push(underline);
|
||||
}
|
||||
Primitive::MonochromeSprite(mut sprite) => {
|
||||
sprite.order = layer_id;
|
||||
|
@ -78,15 +85,26 @@ impl Scene {
|
|||
}
|
||||
|
||||
// Add all primitives to the BSP splitter to determine draw order
|
||||
// todo!("reuse the same splitter")
|
||||
let mut splitter = BspSplitter::new();
|
||||
|
||||
for (ix, shadow) in self.shadows.iter().enumerate() {
|
||||
let z = layer_z_values[shadow.order as LayerId as usize];
|
||||
splitter.add(shadow.bounds.to_bsp_polygon(z, (PrimitiveKind::Shadow, ix)));
|
||||
}
|
||||
|
||||
for (ix, quad) in self.quads.iter().enumerate() {
|
||||
let z = layer_z_values[quad.order as LayerId as usize];
|
||||
splitter.add(quad.bounds.to_bsp_polygon(z, (PrimitiveKind::Quad, ix)));
|
||||
}
|
||||
|
||||
for (ix, shadow) in self.shadows.iter().enumerate() {
|
||||
let z = layer_z_values[shadow.order as LayerId as usize];
|
||||
splitter.add(shadow.bounds.to_bsp_polygon(z, (PrimitiveKind::Shadow, ix)));
|
||||
for (ix, underline) in self.underlines.iter().enumerate() {
|
||||
let z = layer_z_values[underline.order as LayerId as usize];
|
||||
splitter.add(
|
||||
underline
|
||||
.bounds
|
||||
.to_bsp_polygon(z, (PrimitiveKind::Underline, ix)),
|
||||
);
|
||||
}
|
||||
|
||||
for (ix, monochrome_sprite) in self.monochrome_sprites.iter().enumerate() {
|
||||
|
@ -111,8 +129,11 @@ impl Scene {
|
|||
// We need primitives to be repr(C), hence the weird reuse of the order field for two different types.
|
||||
for (draw_order, polygon) in splitter.sort(Vector3D::new(0., 0., 1.)).iter().enumerate() {
|
||||
match polygon.anchor {
|
||||
(PrimitiveKind::Quad, ix) => self.quads[ix].order = draw_order as DrawOrder,
|
||||
(PrimitiveKind::Shadow, ix) => self.shadows[ix].order = draw_order as DrawOrder,
|
||||
(PrimitiveKind::Quad, ix) => self.quads[ix].order = draw_order as DrawOrder,
|
||||
(PrimitiveKind::Underline, ix) => {
|
||||
self.underlines[ix].order = draw_order as DrawOrder
|
||||
}
|
||||
(PrimitiveKind::MonochromeSprite, ix) => {
|
||||
self.monochrome_sprites[ix].order = draw_order as DrawOrder
|
||||
}
|
||||
|
@ -123,18 +144,22 @@ impl Scene {
|
|||
}
|
||||
|
||||
// Sort the primitives
|
||||
self.quads.sort_unstable();
|
||||
self.shadows.sort_unstable();
|
||||
self.quads.sort_unstable();
|
||||
self.underlines.sort_unstable();
|
||||
self.monochrome_sprites.sort_unstable();
|
||||
self.polychrome_sprites.sort_unstable();
|
||||
|
||||
BatchIterator {
|
||||
quads: &self.quads,
|
||||
quads_start: 0,
|
||||
quads_iter: self.quads.iter().peekable(),
|
||||
shadows: &self.shadows,
|
||||
shadows_start: 0,
|
||||
shadows_iter: self.shadows.iter().peekable(),
|
||||
quads: &self.quads,
|
||||
quads_start: 0,
|
||||
quads_iter: self.quads.iter().peekable(),
|
||||
underlines: &self.underlines,
|
||||
underlines_start: 0,
|
||||
underlines_iter: self.underlines.iter().peekable(),
|
||||
monochrome_sprites: &self.monochrome_sprites,
|
||||
monochrome_sprites_start: 0,
|
||||
monochrome_sprites_iter: self.monochrome_sprites.iter().peekable(),
|
||||
|
@ -152,6 +177,9 @@ struct BatchIterator<'a> {
|
|||
shadows: &'a [Shadow],
|
||||
shadows_start: usize,
|
||||
shadows_iter: Peekable<slice::Iter<'a, Shadow>>,
|
||||
underlines: &'a [Underline],
|
||||
underlines_start: usize,
|
||||
underlines_iter: Peekable<slice::Iter<'a, Underline>>,
|
||||
monochrome_sprites: &'a [MonochromeSprite],
|
||||
monochrome_sprites_start: usize,
|
||||
monochrome_sprites_iter: Peekable<slice::Iter<'a, MonochromeSprite>>,
|
||||
|
@ -165,11 +193,15 @@ impl<'a> Iterator for BatchIterator<'a> {
|
|||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
let mut orders_and_kinds = [
|
||||
(self.quads_iter.peek().map(|q| q.order), PrimitiveKind::Quad),
|
||||
(
|
||||
self.shadows_iter.peek().map(|s| s.order),
|
||||
PrimitiveKind::Shadow,
|
||||
),
|
||||
(self.quads_iter.peek().map(|q| q.order), PrimitiveKind::Quad),
|
||||
(
|
||||
self.underlines_iter.peek().map(|u| u.order),
|
||||
PrimitiveKind::Underline,
|
||||
),
|
||||
(
|
||||
self.monochrome_sprites_iter.peek().map(|s| s.order),
|
||||
PrimitiveKind::MonochromeSprite,
|
||||
|
@ -190,19 +222,6 @@ impl<'a> Iterator for BatchIterator<'a> {
|
|||
};
|
||||
|
||||
match batch_kind {
|
||||
PrimitiveKind::Quad => {
|
||||
let quads_start = self.quads_start;
|
||||
let mut quads_end = quads_start;
|
||||
while self
|
||||
.quads_iter
|
||||
.next_if(|quad| quad.order <= max_order)
|
||||
.is_some()
|
||||
{
|
||||
quads_end += 1;
|
||||
}
|
||||
self.quads_start = quads_end;
|
||||
Some(PrimitiveBatch::Quads(&self.quads[quads_start..quads_end]))
|
||||
}
|
||||
PrimitiveKind::Shadow => {
|
||||
let shadows_start = self.shadows_start;
|
||||
let mut shadows_end = shadows_start;
|
||||
|
@ -218,6 +237,34 @@ impl<'a> Iterator for BatchIterator<'a> {
|
|||
&self.shadows[shadows_start..shadows_end],
|
||||
))
|
||||
}
|
||||
PrimitiveKind::Quad => {
|
||||
let quads_start = self.quads_start;
|
||||
let mut quads_end = quads_start;
|
||||
while self
|
||||
.quads_iter
|
||||
.next_if(|quad| quad.order <= max_order)
|
||||
.is_some()
|
||||
{
|
||||
quads_end += 1;
|
||||
}
|
||||
self.quads_start = quads_end;
|
||||
Some(PrimitiveBatch::Quads(&self.quads[quads_start..quads_end]))
|
||||
}
|
||||
PrimitiveKind::Underline => {
|
||||
let underlines_start = self.underlines_start;
|
||||
let mut underlines_end = underlines_start;
|
||||
while self
|
||||
.underlines_iter
|
||||
.next_if(|underline| underline.order <= max_order)
|
||||
.is_some()
|
||||
{
|
||||
underlines_end += 1;
|
||||
}
|
||||
self.underlines_start = underlines_end;
|
||||
Some(PrimitiveBatch::Underlines(
|
||||
&self.underlines[underlines_start..underlines_end],
|
||||
))
|
||||
}
|
||||
PrimitiveKind::MonochromeSprite => {
|
||||
let texture_id = self.monochrome_sprites_iter.peek().unwrap().tile.texture_id;
|
||||
let sprites_start = self.monochrome_sprites_start;
|
||||
|
@ -265,22 +312,25 @@ pub enum PrimitiveKind {
|
|||
Shadow,
|
||||
#[default]
|
||||
Quad,
|
||||
Underline,
|
||||
MonochromeSprite,
|
||||
PolychromeSprite,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum Primitive {
|
||||
Quad(Quad),
|
||||
Shadow(Shadow),
|
||||
Quad(Quad),
|
||||
Underline(Underline),
|
||||
MonochromeSprite(MonochromeSprite),
|
||||
PolychromeSprite(PolychromeSprite),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) enum PrimitiveBatch<'a> {
|
||||
Quads(&'a [Quad]),
|
||||
Shadows(&'a [Shadow]),
|
||||
Quads(&'a [Quad]),
|
||||
Underlines(&'a [Underline]),
|
||||
MonochromeSprites {
|
||||
texture_id: AtlasTextureId,
|
||||
sprites: &'a [MonochromeSprite],
|
||||
|
@ -321,6 +371,35 @@ impl From<Quad> for Primitive {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
#[repr(C)]
|
||||
pub struct Underline {
|
||||
pub order: u32,
|
||||
pub bounds: Bounds<ScaledPixels>,
|
||||
pub content_mask: ScaledContentMask,
|
||||
pub thickness: ScaledPixels,
|
||||
pub color: Hsla,
|
||||
pub wavy: bool,
|
||||
}
|
||||
|
||||
impl Ord for Underline {
|
||||
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
||||
self.order.cmp(&other.order)
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd for Underline {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Underline> for Primitive {
|
||||
fn from(underline: Underline) -> Self {
|
||||
Primitive::Underline(underline)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
#[repr(C)]
|
||||
pub struct Shadow {
|
||||
|
|
|
@ -344,7 +344,7 @@ impl Default for Style {
|
|||
pub struct UnderlineStyle {
|
||||
pub thickness: Pixels,
|
||||
pub color: Option<Hsla>,
|
||||
pub squiggly: bool,
|
||||
pub wavy: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
|
|
|
@ -416,6 +416,96 @@ pub trait StyleHelpers: Styled<Style = Style> {
|
|||
self
|
||||
}
|
||||
|
||||
fn text_decoration_none(mut self) -> Self
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
self.text_style()
|
||||
.get_or_insert_with(Default::default)
|
||||
.underline = None;
|
||||
self
|
||||
}
|
||||
|
||||
fn text_decoration_color(mut self, color: impl Into<Hsla>) -> Self
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
let style = self.text_style().get_or_insert_with(Default::default);
|
||||
let underline = style.underline.get_or_insert_with(Default::default);
|
||||
underline.color = Some(color.into());
|
||||
self
|
||||
}
|
||||
|
||||
fn text_decoration_solid(mut self) -> Self
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
let style = self.text_style().get_or_insert_with(Default::default);
|
||||
let underline = style.underline.get_or_insert_with(Default::default);
|
||||
underline.wavy = false;
|
||||
self
|
||||
}
|
||||
|
||||
fn text_decoration_wavy(mut self) -> Self
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
let style = self.text_style().get_or_insert_with(Default::default);
|
||||
let underline = style.underline.get_or_insert_with(Default::default);
|
||||
underline.wavy = true;
|
||||
self
|
||||
}
|
||||
|
||||
fn text_decoration_0(mut self) -> Self
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
let style = self.text_style().get_or_insert_with(Default::default);
|
||||
let underline = style.underline.get_or_insert_with(Default::default);
|
||||
underline.thickness = px(0.);
|
||||
self
|
||||
}
|
||||
|
||||
fn text_decoration_1(mut self) -> Self
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
let style = self.text_style().get_or_insert_with(Default::default);
|
||||
let underline = style.underline.get_or_insert_with(Default::default);
|
||||
underline.thickness = px(1.);
|
||||
self
|
||||
}
|
||||
|
||||
fn text_decoration_2(mut self) -> Self
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
let style = self.text_style().get_or_insert_with(Default::default);
|
||||
let underline = style.underline.get_or_insert_with(Default::default);
|
||||
underline.thickness = px(2.);
|
||||
self
|
||||
}
|
||||
|
||||
fn text_decoration_4(mut self) -> Self
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
let style = self.text_style().get_or_insert_with(Default::default);
|
||||
let underline = style.underline.get_or_insert_with(Default::default);
|
||||
underline.thickness = px(4.);
|
||||
self
|
||||
}
|
||||
|
||||
fn text_decoration_8(mut self) -> Self
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
let style = self.text_style().get_or_insert_with(Default::default);
|
||||
let underline = style.underline.get_or_insert_with(Default::default);
|
||||
underline.thickness = px(8.);
|
||||
self
|
||||
}
|
||||
|
||||
fn font(mut self, family_name: impl Into<SharedString>) -> Self
|
||||
where
|
||||
Self: Sized,
|
||||
|
|
|
@ -134,9 +134,11 @@ impl Line {
|
|||
origin.y + baseline_offset.y + (self.layout.descent * 0.618),
|
||||
),
|
||||
UnderlineStyle {
|
||||
color: style_run.underline.color,
|
||||
color: Some(
|
||||
style_run.underline.color.unwrap_or(style_run.color),
|
||||
),
|
||||
thickness: style_run.underline.thickness,
|
||||
squiggly: style_run.underline.squiggly,
|
||||
wavy: style_run.underline.wavy,
|
||||
},
|
||||
));
|
||||
}
|
||||
|
@ -153,8 +155,12 @@ impl Line {
|
|||
continue;
|
||||
}
|
||||
|
||||
if let Some((_underline_origin, _underline_style)) = finished_underline {
|
||||
todo!()
|
||||
if let Some((underline_origin, underline_style)) = finished_underline {
|
||||
cx.paint_underline(
|
||||
underline_origin,
|
||||
glyph_origin.x - underline_origin.x,
|
||||
&underline_style,
|
||||
)?;
|
||||
}
|
||||
|
||||
if glyph.is_emoji {
|
||||
|
@ -171,15 +177,13 @@ impl Line {
|
|||
}
|
||||
}
|
||||
|
||||
if let Some((_underline_start, _underline_style)) = underline.take() {
|
||||
let _line_end_x = origin.x + self.layout.width;
|
||||
// cx.scene().push_underline(Underline {
|
||||
// origin: underline_start,
|
||||
// width: line_end_x - underline_start.x,
|
||||
// color: underline_style.color,
|
||||
// thickness: underline_style.thickness.into(),
|
||||
// squiggly: underline_style.squiggly,
|
||||
// });
|
||||
if let Some((underline_start, underline_style)) = underline.take() {
|
||||
let line_end_x = origin.x + self.layout.width;
|
||||
cx.paint_underline(
|
||||
underline_start,
|
||||
line_end_x - underline_start.x,
|
||||
&underline_style,
|
||||
)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
@ -188,7 +192,7 @@ impl Line {
|
|||
pub fn paint_wrapped(
|
||||
&self,
|
||||
origin: Point<Pixels>,
|
||||
_visible_bounds: Bounds<Pixels>,
|
||||
_visible_bounds: Bounds<Pixels>, // todo!("use clipping")
|
||||
line_height: Pixels,
|
||||
boundaries: &[ShapedBoundary],
|
||||
cx: &mut WindowContext,
|
||||
|
@ -213,14 +217,12 @@ impl Line {
|
|||
.map_or(false, |b| b.run_ix == run_ix && b.glyph_ix == glyph_ix)
|
||||
{
|
||||
boundaries.next();
|
||||
if let Some((_underline_origin, _underline_style)) = underline.take() {
|
||||
// cx.scene().push_underline(Underline {
|
||||
// origin: underline_origin,
|
||||
// width: glyph_origin.x - underline_origin.x,
|
||||
// thickness: underline_style.thickness.into(),
|
||||
// color: underline_style.color.unwrap(),
|
||||
// squiggly: underline_style.squiggly,
|
||||
// });
|
||||
if let Some((underline_origin, underline_style)) = underline.take() {
|
||||
cx.paint_underline(
|
||||
underline_origin,
|
||||
glyph_origin.x - underline_origin.x,
|
||||
&underline_style,
|
||||
)?;
|
||||
}
|
||||
|
||||
glyph_origin = point(origin.x, glyph_origin.y + line_height);
|
||||
|
@ -249,7 +251,7 @@ impl Line {
|
|||
style_run.underline.color.unwrap_or(style_run.color),
|
||||
),
|
||||
thickness: style_run.underline.thickness,
|
||||
squiggly: style_run.underline.squiggly,
|
||||
wavy: style_run.underline.wavy,
|
||||
},
|
||||
));
|
||||
}
|
||||
|
@ -260,14 +262,12 @@ impl Line {
|
|||
}
|
||||
}
|
||||
|
||||
if let Some((_underline_origin, _underline_style)) = finished_underline {
|
||||
// cx.scene().push_underline(Underline {
|
||||
// origin: underline_origin,
|
||||
// width: glyph_origin.x - underline_origin.x,
|
||||
// thickness: underline_style.thickness.into(),
|
||||
// color: underline_style.color.unwrap(),
|
||||
// squiggly: underline_style.squiggly,
|
||||
// });
|
||||
if let Some((underline_origin, underline_style)) = finished_underline {
|
||||
cx.paint_underline(
|
||||
underline_origin,
|
||||
glyph_origin.x - underline_origin.x,
|
||||
&underline_style,
|
||||
)?;
|
||||
}
|
||||
|
||||
let text_system = cx.text_system();
|
||||
|
@ -298,15 +298,13 @@ impl Line {
|
|||
}
|
||||
}
|
||||
|
||||
if let Some((_underline_origin, _underline_style)) = underline.take() {
|
||||
// let line_end_x = glyph_origin.x + self.layout.width - prev_position;
|
||||
// cx.scene().push_underline(Underline {
|
||||
// origin: underline_origin,
|
||||
// width: line_end_x - underline_origin.x,
|
||||
// thickness: underline_style.thickness.into(),
|
||||
// color: underline_style.color,
|
||||
// squiggly: underline_style.squiggly,
|
||||
// });
|
||||
if let Some((underline_origin, underline_style)) = underline.take() {
|
||||
let line_end_x = glyph_origin.x + self.layout.width - prev_position;
|
||||
cx.paint_underline(
|
||||
underline_origin,
|
||||
line_end_x - underline_origin.x,
|
||||
&underline_style,
|
||||
)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
use crate::{
|
||||
image_cache::RenderImageParams, px, AnyView, AppContext, AsyncWindowContext, AvailableSpace,
|
||||
BorrowAppContext, Bounds, Context, Corners, DevicePixels, DisplayId, Effect, Element, EntityId,
|
||||
FontId, GlyphId, Handle, Hsla, ImageData, IsZero, LayoutId, MainThread, MainThreadOnly,
|
||||
MonochromeSprite, Pixels, PlatformAtlas, PlatformWindow, Point, PolychromeSprite, Reference,
|
||||
RenderGlyphParams, RenderSvgParams, ScaledPixels, Scene, SharedString, Size, StackingOrder,
|
||||
Style, TaffyLayoutEngine, Task, WeakHandle, WindowOptions, SUBPIXEL_VARIANTS,
|
||||
image_cache::RenderImageParams, px, size, AnyView, AppContext, AsyncWindowContext,
|
||||
AvailableSpace, BorrowAppContext, Bounds, Context, Corners, DevicePixels, DisplayId, Effect,
|
||||
Element, EntityId, FontId, GlyphId, Handle, Hsla, ImageData, IsZero, LayoutId, MainThread,
|
||||
MainThreadOnly, MonochromeSprite, Pixels, PlatformAtlas, PlatformWindow, Point,
|
||||
PolychromeSprite, Reference, RenderGlyphParams, RenderSvgParams, ScaledPixels, Scene,
|
||||
SharedString, Size, StackingOrder, Style, TaffyLayoutEngine, Task, Underline, UnderlineStyle,
|
||||
WeakHandle, WindowOptions, SUBPIXEL_VARIANTS,
|
||||
};
|
||||
use anyhow::Result;
|
||||
use smallvec::SmallVec;
|
||||
|
@ -259,6 +260,38 @@ impl<'a, 'w> WindowContext<'a, 'w> {
|
|||
self.window.current_stacking_order.clone()
|
||||
}
|
||||
|
||||
pub fn paint_underline(
|
||||
&mut self,
|
||||
origin: Point<Pixels>,
|
||||
width: Pixels,
|
||||
style: &UnderlineStyle,
|
||||
) -> Result<()> {
|
||||
let scale_factor = self.scale_factor();
|
||||
let height = if style.wavy {
|
||||
style.thickness * 3.
|
||||
} else {
|
||||
style.thickness
|
||||
};
|
||||
let bounds = Bounds {
|
||||
origin,
|
||||
size: size(width, height),
|
||||
};
|
||||
let content_mask = self.content_mask();
|
||||
let layer_id = self.current_stacking_order();
|
||||
self.window.scene.insert(
|
||||
layer_id,
|
||||
Underline {
|
||||
order: 0,
|
||||
bounds: bounds.scale(scale_factor),
|
||||
content_mask: content_mask.scale(scale_factor),
|
||||
thickness: style.thickness.scale(scale_factor),
|
||||
color: style.color.unwrap_or_default(),
|
||||
wavy: style.wavy,
|
||||
},
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn paint_glyph(
|
||||
&mut self,
|
||||
origin: Point<Pixels>,
|
||||
|
|
|
@ -160,7 +160,13 @@ impl Titlebar {
|
|||
// .fill(theme.lowest.base.hovered.background)
|
||||
// .active()
|
||||
// .fill(theme.lowest.base.pressed.background)
|
||||
.child(div().text_sm().child("branch")),
|
||||
.child(
|
||||
div()
|
||||
.text_sm()
|
||||
.text_decoration_1()
|
||||
.text_decoration_wavy()
|
||||
.child("branch"),
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue