Add clipping back

This commit is contained in:
grovesNL 2023-01-26 00:07:05 -03:30 committed by Josh Groves
parent ba52cbdba6
commit 8c8cfba093
3 changed files with 87 additions and 63 deletions

View file

@ -1,5 +1,5 @@
use cosmic_text::{Attrs, Buffer, Color, Family, FontSystem, Metrics, SwashCache}; use cosmic_text::{Attrs, Buffer, Color, Family, FontSystem, Metrics, SwashCache};
use glyphon::{Resolution, TextAtlas, TextRenderer}; use glyphon::{Resolution, TextArea, TextAtlas, TextBounds, TextRenderer};
use wgpu::{ use wgpu::{
Backends, CommandEncoderDescriptor, CompositeAlphaMode, DeviceDescriptor, Features, Instance, Backends, CommandEncoderDescriptor, CompositeAlphaMode, DeviceDescriptor, Features, Instance,
Limits, LoadOp, Operations, PresentMode, RenderPassColorAttachment, RenderPassDescriptor, Limits, LoadOp, Operations, PresentMode, RenderPassColorAttachment, RenderPassDescriptor,
@ -74,7 +74,7 @@ async fn run() {
); );
buffer.set_size((width as f64 * scale_factor) as i32, (height as f64) as i32); buffer.set_size((width as f64 * scale_factor) as i32, (height as f64) as i32);
buffer.set_text( buffer.set_text(
include_str!("./emoji.txt"), include_str!("./emoji-zjw.txt"),
Attrs::new().monospaced(true).family(Family::Monospace), Attrs::new().monospaced(true).family(Family::Monospace),
); );
buffer.shape_until_scroll(); buffer.shape_until_scroll();
@ -103,7 +103,15 @@ async fn run() {
width: config.width, width: config.width,
height: config.height, height: config.height,
}, },
&buffer, &[TextArea {
buffer: &buffer,
bounds: TextBounds {
left: 10,
top: 10,
right: 500,
bottom: 50,
},
}],
Color::rgb(255, 255, 255), Color::rgb(255, 255, 255),
&mut cache, &mut cache,
) )

View file

@ -1,5 +1,3 @@
use etagere::AllocId;
mod error; mod error;
mod recently_used; mod recently_used;
mod text_atlas; mod text_atlas;
@ -12,6 +10,7 @@ use text_render::ContentType;
pub use text_render::TextRenderer; pub use text_render::TextRenderer;
pub use cosmic_text; pub use cosmic_text;
use etagere::AllocId;
pub(crate) enum GpuCacheStatus { pub(crate) enum GpuCacheStatus {
InAtlas { InAtlas {
@ -58,11 +57,35 @@ pub(crate) struct Params {
_pad: [u32; 2], _pad: [u32; 2],
} }
/// Controls the overflow behavior of any glyphs that are outside of the layout bounds. /// Controls the visible area of the text. Any text outside of the visible area will be clipped.
pub enum TextOverflow { #[derive(Clone, Copy, Debug, Eq, PartialEq)]
/// Glyphs can overflow the bounds. pub struct TextBounds {
Overflow, /// The position of the left edge of the visible area.
/// Hide any glyphs outside the bounds. If a glyph is partially outside the bounds, it will be pub left: i32,
/// clipped to the bounds. /// The position of the top edge of the visible area.
Hide, pub top: i32,
/// The position of the right edge of the visible area.
pub right: i32,
/// The position of the bottom edge of the visible area.
pub bottom: i32,
}
/// The default visible area doesn't clip any text.
impl Default for TextBounds {
fn default() -> Self {
Self {
left: i32::MIN,
top: i32::MIN,
right: i32::MAX,
bottom: i32::MAX,
}
}
}
/// A text area containing text to be rendered along with its overflow behavior.
pub struct TextArea<'a> {
/// The buffer containing the text to be rendered.
pub buffer: &'a cosmic_text::Buffer<'a>,
/// The bounds of the text area.
pub bounds: TextBounds,
} }

View file

@ -1,6 +1,6 @@
use crate::{ use crate::{
GlyphDetails, GlyphToRender, GpuCacheStatus, Params, PrepareError, RenderError, Resolution, GlyphDetails, GlyphToRender, GpuCacheStatus, Params, PrepareError, RenderError, Resolution,
TextAtlas, TextOverflow, TextArea, TextAtlas,
}; };
use cosmic_text::{CacheKey, Color, SwashCache, SwashContent}; use cosmic_text::{CacheKey, Color, SwashCache, SwashContent};
use std::{collections::HashSet, iter, mem::size_of, num::NonZeroU32, slice}; use std::{collections::HashSet, iter, mem::size_of, num::NonZeroU32, slice};
@ -53,14 +53,14 @@ impl TextRenderer {
} }
} }
/// Prepares all of the provided layouts for rendering. /// Prepares all of the provided text areas for rendering.
pub fn prepare<'a>( pub fn prepare<'a>(
&mut self, &mut self,
device: &Device, device: &Device,
queue: &Queue, queue: &Queue,
atlas: &mut TextAtlas, atlas: &mut TextAtlas,
screen_resolution: Resolution, screen_resolution: Resolution,
buffer: &cosmic_text::Buffer<'a>, text_areas: &[TextArea<'a>],
default_color: Color, default_color: Color,
cache: &mut SwashCache, cache: &mut SwashCache,
) -> Result<(), PrepareError> { ) -> Result<(), PrepareError> {
@ -97,10 +97,8 @@ impl TextRenderer {
self.glyphs_in_use.clear(); self.glyphs_in_use.clear();
let mut buffers = [(buffer, TextOverflow::Hide)]; for text_area in text_areas.iter() {
for run in text_area.buffer.layout_runs() {
for (buffer, _) in buffers.iter_mut() {
for run in buffer.layout_runs() {
for glyph in run.glyphs.iter() { for glyph in run.glyphs.iter() {
self.glyphs_in_use.insert(glyph.cache_key); self.glyphs_in_use.insert(glyph.cache_key);
@ -240,15 +238,10 @@ impl TextRenderer {
let mut glyph_indices: Vec<u32> = Vec::new(); let mut glyph_indices: Vec<u32> = Vec::new();
let mut glyphs_added = 0; let mut glyphs_added = 0;
for (buffer, overflow) in buffers.iter() { for text_area in text_areas.iter() {
// Note: subpixel positioning is not currently handled, so we always truncate down to // Note: subpixel positioning is not currently handled, so we always truncate down to
// the nearest pixel. // the nearest pixel whenever necessary.
let bounds_min_x = i32::MIN; for run in text_area.buffer.layout_runs() {
let bounds_max_x = i32::MAX;
let bounds_min_y = i32::MIN;
let bounds_max_y = i32::MAX;
for run in buffer.layout_runs() {
let line_y = run.line_y; let line_y = run.line_y;
for glyph in run.glyphs.iter() { for glyph in run.glyphs.iter() {
@ -270,49 +263,49 @@ impl TextRenderer {
let mut width = details.width as i32; let mut width = details.width as i32;
let mut height = details.height as i32; let mut height = details.height as i32;
match overflow { let bounds_min_x = text_area.bounds.left.max(0);
TextOverflow::Overflow => {} let bounds_min_y = text_area.bounds.top.max(0);
TextOverflow::Hide => { let bounds_max_x = text_area.bounds.right.min(screen_resolution.width as i32);
// Starts beyond right edge or ends beyond left edge let bounds_max_y = text_area.bounds.bottom.min(screen_resolution.height as i32);
let max_x = x + width;
if x > bounds_max_x || max_x < bounds_min_x {
continue;
}
// Starts beyond bottom edge or ends beyond top edge // Starts beyond right edge or ends beyond left edge
let max_y = y + height; let max_x = x + width;
if y > bounds_max_y || max_y < bounds_min_y { if x > bounds_max_x || max_x < bounds_min_x {
continue; continue;
} }
// Clip left ege // Starts beyond bottom edge or ends beyond top edge
if x < bounds_min_x { let max_y = y + height;
let right_shift = bounds_min_x - x; if y > bounds_max_y || max_y < bounds_min_y {
continue;
}
x = bounds_min_x; // Clip left ege
width = max_x - bounds_min_x; if x < bounds_min_x {
atlas_x += right_shift as u16; let right_shift = bounds_min_x - x;
}
// Clip right edge x = bounds_min_x;
if x + width > bounds_max_x { width = max_x - bounds_min_x;
width = bounds_max_x - x; atlas_x += right_shift as u16;
} }
// Clip top edge // Clip right edge
if y < bounds_min_y { if x + width > bounds_max_x {
let bottom_shift = bounds_min_y - y; width = bounds_max_x - x;
}
y = bounds_min_y; // Clip top edge
height = max_y - bounds_min_y; if y < bounds_min_y {
atlas_y += bottom_shift as u16; let bottom_shift = bounds_min_y - y;
}
// Clip bottom edge y = bounds_min_y;
if y + height > bounds_max_y { height = max_y - bounds_min_y;
height = bounds_max_y - y; atlas_y += bottom_shift as u16;
} }
}
// Clip bottom edge
if y + height > bounds_max_y {
height = bounds_max_y - y;
} }
glyph_vertices.extend( glyph_vertices.extend(