diff --git a/graphics/src/damage.rs b/graphics/src/damage.rs index 595cc2746d..59e9f5b4c0 100644 --- a/graphics/src/damage.rs +++ b/graphics/src/damage.rs @@ -73,6 +73,11 @@ impl Damage for Primitive { bounds.expand(1.5) } + Self::RawText(raw) => { + // TODO: Add `size` field to `raw` to compute more accurate + // damage bounds (?) + raw.clip_bounds.expand(1.5) + } Self::Quad { bounds, .. } | Self::Image { bounds, .. } | Self::Svg { bounds, .. } => bounds.expand(1.0), diff --git a/graphics/src/primitive.rs b/graphics/src/primitive.rs index ed75776c15..20affaaf66 100644 --- a/graphics/src/primitive.rs +++ b/graphics/src/primitive.rs @@ -57,6 +57,8 @@ pub enum Primitive { /// The clip bounds of the editor. clip_bounds: Rectangle, }, + /// A raw `cosmic-text` primitive + RawText(crate::text::Raw), /// A quad primitive Quad { /// The bounds of the quad diff --git a/graphics/src/text.rs b/graphics/src/text.rs index fc7694c248..8fd037fe68 100644 --- a/graphics/src/text.rs +++ b/graphics/src/text.rs @@ -12,11 +12,11 @@ pub use cosmic_text; use crate::color; use crate::core::font::{self, Font}; use crate::core::text::Shaping; -use crate::core::{Color, Size}; +use crate::core::{Color, Point, Rectangle, Size}; use once_cell::sync::OnceCell; use std::borrow::Cow; -use std::sync::{Arc, RwLock}; +use std::sync::{Arc, RwLock, Weak}; /// Returns the global [`FontSystem`]. pub fn font_system() -> &'static RwLock { @@ -68,6 +68,29 @@ impl FontSystem { #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] pub struct Version(u32); +/// A weak reference to a [`cosmic-text::Buffer`] that can be drawn. +#[derive(Debug, Clone)] +pub struct Raw { + /// A weak reference to a [`cosmic_text::Buffer`]. + pub buffer: Weak, + /// The position of the text. + pub position: Point, + /// The color of the text. + pub color: Color, + /// The clip bounds of the text. + pub clip_bounds: Rectangle, +} + +impl PartialEq for Raw { + fn eq(&self, _other: &Self) -> bool { + // TODO: There is no proper way to compare raw buffers + // For now, no two instances of `Raw` text will be equal. + // This should be fine, but could trigger unnecessary redraws + // in the future. + false + } +} + /// Measures the dimensions of the given [`cosmic_text::Buffer`]. pub fn measure(buffer: &cosmic_text::Buffer) -> Size { let (width, total_lines) = buffer diff --git a/tiny_skia/src/backend.rs b/tiny_skia/src/backend.rs index 3e9bd2a5e2..706db40e50 100644 --- a/tiny_skia/src/backend.rs +++ b/tiny_skia/src/backend.rs @@ -1,5 +1,6 @@ use crate::core::{Background, Color, Gradient, Rectangle, Vector}; use crate::graphics::backend; +use crate::graphics::text; use crate::graphics::Viewport; use crate::primitive::{self, Primitive}; @@ -444,6 +445,35 @@ impl Backend { clip_mask, ); } + Primitive::RawText(text::Raw { + buffer, + position, + color, + clip_bounds: text_clip_bounds, + }) => { + let Some(buffer) = buffer.upgrade() else { + return; + }; + + let physical_bounds = + (*text_clip_bounds + translation) * scale_factor; + + if !clip_bounds.intersects(&physical_bounds) { + return; + } + + let clip_mask = (!physical_bounds.is_within(&clip_bounds)) + .then_some(clip_mask as &_); + + self.text_pipeline.draw_raw( + &buffer, + *position + translation, + *color, + scale_factor, + pixels, + clip_mask, + ); + } #[cfg(feature = "image")] Primitive::Image { handle, diff --git a/tiny_skia/src/text.rs b/tiny_skia/src/text.rs index 70e95d0170..a5a0a1b64f 100644 --- a/tiny_skia/src/text.rs +++ b/tiny_skia/src/text.rs @@ -1,6 +1,6 @@ use crate::core::alignment; use crate::core::text::{LineHeight, Shaping}; -use crate::core::{Color, Font, Pixels, Point, Rectangle}; +use crate::core::{Color, Font, Pixels, Point, Rectangle, Size}; use crate::graphics::color; use crate::graphics::text::cache::{self, Cache}; use crate::graphics::text::editor; @@ -149,6 +149,33 @@ impl Pipeline { ); } + pub fn draw_raw( + &mut self, + buffer: &cosmic_text::Buffer, + position: Point, + color: Color, + scale_factor: f32, + pixels: &mut tiny_skia::PixmapMut<'_>, + clip_mask: Option<&tiny_skia::Mask>, + ) { + let mut font_system = font_system().write().expect("Write font system"); + + let (width, height) = buffer.size(); + + draw( + font_system.raw(), + &mut self.glyph_cache, + buffer, + Rectangle::new(position, Size::new(width, height)), + color, + alignment::Horizontal::Left, + alignment::Vertical::Top, + scale_factor, + pixels, + clip_mask, + ); + } + pub fn trim_cache(&mut self) { self.cache.get_mut().trim(); self.glyph_cache.trim(); diff --git a/wgpu/src/layer.rs b/wgpu/src/layer.rs index 557a76335a..4ad12a883c 100644 --- a/wgpu/src/layer.rs +++ b/wgpu/src/layer.rs @@ -177,6 +177,21 @@ impl<'a> Layer<'a> { clip_bounds: *clip_bounds + translation, })); } + graphics::Primitive::RawText(graphics::text::Raw { + buffer, + position, + color, + clip_bounds, + }) => { + let layer = &mut layers[current_layer]; + + layer.text.push(Text::Raw(graphics::text::Raw { + buffer: buffer.clone(), + position: *position + translation, + color: *color, + clip_bounds: *clip_bounds + translation, + })); + } Primitive::Quad { bounds, background, diff --git a/wgpu/src/layer/text.rs b/wgpu/src/layer/text.rs index df2f2875d4..37ee524728 100644 --- a/wgpu/src/layer/text.rs +++ b/wgpu/src/layer/text.rs @@ -1,6 +1,7 @@ use crate::core::alignment; use crate::core::text; use crate::core::{Color, Font, Pixels, Point, Rectangle}; +use crate::graphics; use crate::graphics::text::editor; use crate::graphics::text::paragraph; @@ -23,8 +24,10 @@ pub enum Text<'a> { color: Color, clip_bounds: Rectangle, }, - /// A cached text. + /// Some cached text. Cached(Cached<'a>), + /// Some raw text. + Raw(graphics::text::Raw), } #[derive(Debug, Clone)] diff --git a/wgpu/src/text.rs b/wgpu/src/text.rs index 888b192486..dca09cb8a8 100644 --- a/wgpu/src/text.rs +++ b/wgpu/src/text.rs @@ -7,6 +7,7 @@ use crate::layer::Text; use std::borrow::Cow; use std::cell::RefCell; +use std::sync::Arc; #[allow(missing_debug_implementations)] pub struct Pipeline { @@ -76,6 +77,7 @@ impl Pipeline { Paragraph(Paragraph), Editor(Editor), Cache(cache::KeyHash), + Raw(Arc), } let allocations: Vec<_> = sections @@ -107,6 +109,7 @@ impl Pipeline { Some(Allocation::Cache(key)) } + Text::Raw(text) => text.buffer.upgrade().map(Allocation::Raw), }) .collect(); @@ -185,6 +188,25 @@ impl Pipeline { text.clip_bounds, ) } + Text::Raw(text) => { + let Some(Allocation::Raw(buffer)) = allocation else { + return None; + }; + + let (width, height) = buffer.size(); + + ( + buffer.as_ref(), + Rectangle::new( + text.position, + Size::new(width, height), + ), + alignment::Horizontal::Left, + alignment::Vertical::Top, + text.color, + text.clip_bounds, + ) + } }; let bounds = bounds * scale_factor;