diff options
Diffstat (limited to 'tiny_skia')
-rw-r--r-- | tiny_skia/Cargo.toml | 2 | ||||
-rw-r--r-- | tiny_skia/fonts/Iced-Icons.ttf | bin | 0 -> 5108 bytes | |||
-rw-r--r-- | tiny_skia/src/backend.rs | 50 | ||||
-rw-r--r-- | tiny_skia/src/geometry.rs | 50 | ||||
-rw-r--r-- | tiny_skia/src/text.rs | 54 | ||||
-rw-r--r-- | tiny_skia/src/window/compositor.rs | 76 |
6 files changed, 170 insertions, 62 deletions
diff --git a/tiny_skia/Cargo.toml b/tiny_skia/Cargo.toml index 400eee6a..ef993fb9 100644 --- a/tiny_skia/Cargo.toml +++ b/tiny_skia/Cargo.toml @@ -24,7 +24,7 @@ features = ["tiny-skia"] [dependencies.cosmic-text] git = "https://github.com/hecrj/cosmic-text.git" -rev = "b85d6a4f2376f8a8a7dadc0f8bcb89d4db10a1c9" +rev = "c3cd24dc972bb8fd55d016c81ac9fa637e0a4ada" [dependencies.twox-hash] version = "1.6" diff --git a/tiny_skia/fonts/Iced-Icons.ttf b/tiny_skia/fonts/Iced-Icons.ttf Binary files differnew file mode 100644 index 00000000..e3273141 --- /dev/null +++ b/tiny_skia/fonts/Iced-Icons.ttf diff --git a/tiny_skia/src/backend.rs b/tiny_skia/src/backend.rs index 87738174..ba038052 100644 --- a/tiny_skia/src/backend.rs +++ b/tiny_skia/src/backend.rs @@ -1,4 +1,5 @@ use crate::core::text; +use crate::core::Gradient; use crate::core::{Background, Color, Font, Point, Rectangle, Size, Vector}; use crate::graphics::backend; use crate::graphics::{Primitive, Viewport}; @@ -91,6 +92,7 @@ impl Backend { background_color, )), anti_alias: false, + blend_mode: tiny_skia::BlendMode::Source, ..Default::default() }, tiny_skia::FillRule::default(), @@ -194,6 +196,48 @@ impl Backend { *color, )) } + Background::Gradient(Gradient::Linear(linear)) => { + let (start, end) = + linear.angle.to_distance(bounds); + + let stops: Vec<tiny_skia::GradientStop> = + linear + .stops + .into_iter() + .flatten() + .map(|stop| { + tiny_skia::GradientStop::new( + stop.offset, + tiny_skia::Color::from_rgba( + stop.color.b, + stop.color.g, + stop.color.r, + stop.color.a, + ) + .expect("Create color"), + ) + }) + .collect(); + + tiny_skia::LinearGradient::new( + tiny_skia::Point { + x: start.x, + y: start.y, + }, + tiny_skia::Point { x: end.x, y: end.y }, + if stops.is_empty() { + vec![tiny_skia::GradientStop::new( + 0.0, + tiny_skia::Color::BLACK, + )] + } else { + stops + }, + tiny_skia::SpreadMode::Pad, + tiny_skia::Transform::identity(), + ) + .expect("Create linear gradient") + } }, anti_alias: true, ..tiny_skia::Paint::default() @@ -722,12 +766,6 @@ fn adjust_clip_mask(clip_mask: &mut tiny_skia::Mask, bounds: Rectangle) { ); } -impl iced_graphics::Backend for Backend { - fn trim_measurements(&mut self) { - self.text_pipeline.trim_measurement_cache(); - } -} - impl backend::Text for Backend { const ICON_FONT: Font = Font::with_name("Iced-Icons"); const CHECKMARK_ICON: char = '\u{f00c}'; diff --git a/tiny_skia/src/geometry.rs b/tiny_skia/src/geometry.rs index a445b561..ee347c73 100644 --- a/tiny_skia/src/geometry.rs +++ b/tiny_skia/src/geometry.rs @@ -1,8 +1,8 @@ -use crate::core::Gradient; use crate::core::{Point, Rectangle, Size, Vector}; use crate::graphics::geometry::fill::{self, Fill}; use crate::graphics::geometry::stroke::{self, Stroke}; use crate::graphics::geometry::{Path, Style, Text}; +use crate::graphics::Gradient; use crate::graphics::Primitive; pub struct Frame { @@ -127,9 +127,9 @@ impl Frame { self.transform = self.stack.pop().expect("Pop transform"); } - pub fn clip(&mut self, frame: Self, translation: Vector) { + pub fn clip(&mut self, frame: Self, at: Point) { self.primitives.push(Primitive::Translate { - translation, + translation: Vector::new(at.x, at.y), content: Box::new(frame.into_primitive()), }); } @@ -231,18 +231,11 @@ pub fn into_paint(style: Style) -> tiny_skia::Paint<'static> { .expect("Create color"), ), Style::Gradient(gradient) => match gradient { - Gradient::Linear(linear) => tiny_skia::LinearGradient::new( - tiny_skia::Point { - x: linear.start.x, - y: linear.start.y, - }, - tiny_skia::Point { - x: linear.end.x, - y: linear.end.y, - }, - linear - .color_stops + Gradient::Linear(linear) => { + let stops: Vec<tiny_skia::GradientStop> = linear + .stops .into_iter() + .flatten() .map(|stop| { tiny_skia::GradientStop::new( stop.offset, @@ -255,11 +248,30 @@ pub fn into_paint(style: Style) -> tiny_skia::Paint<'static> { .expect("Create color"), ) }) - .collect(), - tiny_skia::SpreadMode::Pad, - tiny_skia::Transform::identity(), - ) - .expect("Create linear gradient"), + .collect(); + + tiny_skia::LinearGradient::new( + tiny_skia::Point { + x: linear.start.x, + y: linear.start.y, + }, + tiny_skia::Point { + x: linear.end.x, + y: linear.end.y, + }, + if stops.is_empty() { + vec![tiny_skia::GradientStop::new( + 0.0, + tiny_skia::Color::BLACK, + )] + } else { + stops + }, + tiny_skia::SpreadMode::Pad, + tiny_skia::Transform::identity(), + ) + .expect("Create linear gradient") + } }, }, anti_alias: true, diff --git a/tiny_skia/src/text.rs b/tiny_skia/src/text.rs index a34c7317..3441da8f 100644 --- a/tiny_skia/src/text.rs +++ b/tiny_skia/src/text.rs @@ -14,8 +14,7 @@ use std::sync::Arc; pub struct Pipeline { font_system: RefCell<cosmic_text::FontSystem>, glyph_cache: GlyphCache, - measurement_cache: RefCell<Cache>, - render_cache: Cache, + cache: RefCell<Cache>, } impl Pipeline { @@ -23,14 +22,12 @@ impl Pipeline { Pipeline { font_system: RefCell::new(cosmic_text::FontSystem::new_with_fonts( [cosmic_text::fontdb::Source::Binary(Arc::new( - include_bytes!("../../wgpu/fonts/Iced-Icons.ttf") - .as_slice(), + include_bytes!("../fonts/Iced-Icons.ttf").as_slice(), ))] .into_iter(), )), glyph_cache: GlyphCache::new(), - measurement_cache: RefCell::new(Cache::new()), - render_cache: Cache::new(), + cache: RefCell::new(Cache::new()), } } @@ -38,6 +35,8 @@ impl Pipeline { self.font_system.get_mut().db_mut().load_font_source( cosmic_text::fontdb::Source::Binary(Arc::new(bytes.into_owned())), ); + + self.cache = RefCell::new(Cache::new()); } pub fn draw( @@ -55,20 +54,11 @@ impl Pipeline { pixels: &mut tiny_skia::PixmapMut<'_>, clip_mask: Option<&tiny_skia::Mask>, ) { - let line_height = - f32::from(line_height.to_absolute(Pixels(size))) * scale_factor; - - let bounds = bounds * scale_factor; - let size = size * scale_factor; + let line_height = f32::from(line_height.to_absolute(Pixels(size))); let font_system = self.font_system.get_mut(); let key = Key { - bounds: { - let size = bounds.size(); - - // TODO: Reuse buffers from layouting - Size::new(size.width.ceil(), size.height.ceil()) - }, + bounds: bounds.size(), content, font, size, @@ -76,7 +66,7 @@ impl Pipeline { shaping, }; - let (_, buffer) = self.render_cache.allocate(font_system, key); + let (_, buffer) = self.cache.get_mut().allocate(font_system, key); let (total_lines, max_width) = buffer .layout_runs() @@ -85,7 +75,10 @@ impl Pipeline { (i + 1, buffer.line_w.max(max)) }); - let total_height = total_lines as f32 * line_height; + let total_height = total_lines as f32 * line_height * scale_factor; + let max_width = max_width * scale_factor; + + let bounds = bounds * scale_factor; let x = match horizontal_alignment { alignment::Horizontal::Left => bounds.x, @@ -99,16 +92,14 @@ impl Pipeline { alignment::Vertical::Bottom => bounds.y - total_height, }; - // TODO: Subpixel glyph positioning - let x = x.round() as i32; - let y = y.round() as i32; - let mut swash = cosmic_text::SwashCache::new(); for run in buffer.layout_runs() { for glyph in run.glyphs { + let physical_glyph = glyph.physical((x, y), scale_factor); + if let Some((buffer, placement)) = self.glyph_cache.allocate( - glyph.cache_key, + physical_glyph.cache_key, color, font_system, &mut swash, @@ -121,8 +112,9 @@ impl Pipeline { .expect("Create glyph pixel map"); pixels.draw_pixmap( - x + glyph.x_int + placement.left, - y - glyph.y_int - placement.top + run.line_y as i32, + physical_glyph.x + placement.left, + physical_glyph.y - placement.top + + (run.line_y * scale_factor).round() as i32, pixmap, &tiny_skia::PixmapPaint::default(), tiny_skia::Transform::identity(), @@ -134,7 +126,7 @@ impl Pipeline { } pub fn trim_cache(&mut self) { - self.render_cache.trim(); + self.cache.get_mut().trim(); self.glyph_cache.trim(); } @@ -147,7 +139,7 @@ impl Pipeline { bounds: Size, shaping: Shaping, ) -> (f32, f32) { - let mut measurement_cache = self.measurement_cache.borrow_mut(); + let mut measurement_cache = self.cache.borrow_mut(); let line_height = f32::from(line_height.to_absolute(Pixels(size))); @@ -184,7 +176,7 @@ impl Pipeline { point: Point, _nearest_only: bool, ) -> Option<Hit> { - let mut measurement_cache = self.measurement_cache.borrow_mut(); + let mut measurement_cache = self.cache.borrow_mut(); let line_height = f32::from(line_height.to_absolute(Pixels(size))); @@ -204,10 +196,6 @@ impl Pipeline { Some(Hit::CharOffset(cursor.index)) } - - pub fn trim_measurement_cache(&mut self) { - self.measurement_cache.borrow_mut().trim(); - } } fn to_family(family: font::Family) -> cosmic_text::Family<'static> { diff --git a/tiny_skia/src/window/compositor.rs b/tiny_skia/src/window/compositor.rs index 9999a188..f3be3f16 100644 --- a/tiny_skia/src/window/compositor.rs +++ b/tiny_skia/src/window/compositor.rs @@ -1,5 +1,5 @@ -use crate::core::{Color, Rectangle}; -use crate::graphics::compositor::{self, Information, SurfaceError}; +use crate::core::{Color, Rectangle, Size}; +use crate::graphics::compositor::{self, Information}; use crate::graphics::damage; use crate::graphics::{Error, Primitive, Viewport}; use crate::{Backend, Renderer, Settings}; @@ -79,7 +79,7 @@ impl<Theme> crate::graphics::Compositor for Compositor<Theme> { viewport: &Viewport, background_color: Color, overlay: &[T], - ) -> Result<(), SurfaceError> { + ) -> Result<(), compositor::SurfaceError> { renderer.with_primitives(|backend, primitives| { present( backend, @@ -91,6 +91,26 @@ impl<Theme> crate::graphics::Compositor for Compositor<Theme> { ) }) } + + fn screenshot<T: AsRef<str>>( + &mut self, + renderer: &mut Self::Renderer, + surface: &mut Self::Surface, + viewport: &Viewport, + background_color: Color, + overlay: &[T], + ) -> Vec<u8> { + renderer.with_primitives(|backend, primitives| { + screenshot( + surface, + backend, + primitives, + viewport, + background_color, + overlay, + ) + }) + } } pub fn new<Theme>(settings: Settings) -> (Compositor<Theme>, Backend) { @@ -156,3 +176,53 @@ pub fn present<T: AsRef<str>>( Ok(()) } + +pub fn screenshot<T: AsRef<str>>( + surface: &mut Surface, + backend: &mut Backend, + primitives: &[Primitive], + viewport: &Viewport, + background_color: Color, + overlay: &[T], +) -> Vec<u8> { + let size = viewport.physical_size(); + + let mut offscreen_buffer: Vec<u32> = + vec![0; size.width as usize * size.height as usize]; + + backend.draw( + &mut tiny_skia::PixmapMut::from_bytes( + bytemuck::cast_slice_mut(&mut offscreen_buffer), + size.width, + size.height, + ) + .expect("Create offscreen pixel map"), + &mut surface.clip_mask, + primitives, + viewport, + &[Rectangle::with_size(Size::new( + size.width as f32, + size.height as f32, + ))], + background_color, + overlay, + ); + + offscreen_buffer.iter().fold( + Vec::with_capacity(offscreen_buffer.len() * 4), + |mut acc, pixel| { + const A_MASK: u32 = 0xFF_00_00_00; + const R_MASK: u32 = 0x00_FF_00_00; + const G_MASK: u32 = 0x00_00_FF_00; + const B_MASK: u32 = 0x00_00_00_FF; + + let a = ((A_MASK & pixel) >> 24) as u8; + let r = ((R_MASK & pixel) >> 16) as u8; + let g = ((G_MASK & pixel) >> 8) as u8; + let b = (B_MASK & pixel) as u8; + + acc.extend([r, g, b, a]); + acc + }, + ) +} |