From 26a0c944b27cafd5f2d83a3a2c6576ae7acc960d Mon Sep 17 00:00:00 2001 From: Olivier Goffart Date: Thu, 17 Oct 2024 08:41:03 +0200 Subject: [PATCH] WIP: Signed DistanceField font rendering in swrenderer --- internal/compiler/embedded_resources.rs | 10 +- internal/compiler/generator/cpp.rs | 2 + internal/compiler/generator/rust.rs | 3 +- internal/compiler/passes/embed_glyphs.rs | 44 +++-- internal/core/graphics/bitmapfont.rs | 3 + internal/core/graphics/image.rs | 6 + internal/core/software_renderer.rs | 151 ++++++++++-------- .../core/software_renderer/draw_functions.rs | 58 +++++-- internal/core/software_renderer/fixed.rs | 19 +++ internal/core/software_renderer/fonts.rs | 11 +- .../core/software_renderer/fonts/pixelfont.rs | 66 ++++---- .../software_renderer/fonts/vectorfont.rs | 2 + internal/core/software_renderer/scene.rs | 7 +- 13 files changed, 244 insertions(+), 138 deletions(-) diff --git a/internal/compiler/embedded_resources.rs b/internal/compiler/embedded_resources.rs index b2fe444accf..ea78d857acd 100644 --- a/internal/compiler/embedded_resources.rs +++ b/internal/compiler/embedded_resources.rs @@ -53,13 +53,14 @@ pub struct BitmapGlyph { pub width: i16, pub height: i16, pub x_advance: i16, - pub data: Vec, // 8bit alpha map or SDF if `BitMapGlyphs`'s pixel_size is 0. + /// 8bit alpha map or SDF if `BitMapGlyphs`'s `sdf` is `true`. + pub data: Vec, } #[cfg(feature = "software-renderer")] #[derive(Debug, Clone)] pub struct BitmapGlyphs { - pub pixel_size: i16, // pixel size 0 means it's a scalable font (via SDF). + pub pixel_size: i16, pub glyph_data: Vec, } @@ -74,7 +75,8 @@ pub struct CharacterMapEntry { #[derive(Debug, Clone)] pub struct BitmapFont { pub family_name: String, - pub character_map: Vec, // map of available glyphs, sorted by char + /// map of available glyphs, sorted by char + pub character_map: Vec, pub units_per_em: f32, pub ascent: f32, pub descent: f32, @@ -83,6 +85,8 @@ pub struct BitmapFont { pub glyphs: Vec, pub weight: u16, pub italic: bool, + /// true when the font is represented as a signed distance field + pub sdf: bool, } #[derive(Debug, Clone)] diff --git a/internal/compiler/generator/cpp.rs b/internal/compiler/generator/cpp.rs index 99063603044..ac04a4fe7c3 100644 --- a/internal/compiler/generator/cpp.rs +++ b/internal/compiler/generator/cpp.rs @@ -941,6 +941,7 @@ fn embed_resource( glyphs, weight, italic, + sdf, }, ) => { let family_name_var = format!("slint_embedded_resource_{}_family_name", resource.id); @@ -1042,6 +1043,7 @@ fn embed_resource( .glyphs = slint::cbindgen_private::Slice{{ {glyphsets_var}, {glyphsets_size} }}, .weight = {weight}, .italic = {italic}, + .sdf = {sdf}, }}" ); diff --git a/internal/compiler/generator/rust.rs b/internal/compiler/generator/rust.rs index 52415198e59..fc9082ac32d 100644 --- a/internal/compiler/generator/rust.rs +++ b/internal/compiler/generator/rust.rs @@ -3005,7 +3005,7 @@ fn generate_resources(doc: &Document) -> Vec { ) }, #[cfg(feature = "software-renderer")] - crate::embedded_resources::EmbeddedResourcesKind::BitmapFontData(crate::embedded_resources::BitmapFont { family_name, character_map, units_per_em, ascent, descent, x_height, cap_height, glyphs, weight, italic }) => { + crate::embedded_resources::EmbeddedResourcesKind::BitmapFontData(crate::embedded_resources::BitmapFont { family_name, character_map, units_per_em, ascent, descent, x_height, cap_height, glyphs, weight, italic, sdf }) => { let character_map_size = character_map.len(); @@ -3066,6 +3066,7 @@ fn generate_resources(doc: &Document) -> Vec { }), weight: #weight, italic: #italic, + sdf: #sdf, }; ) }, diff --git a/internal/compiler/passes/embed_glyphs.rs b/internal/compiler/passes/embed_glyphs.rs index 9cb92b18977..c4895c72a1f 100644 --- a/internal/compiler/passes/embed_glyphs.rs +++ b/internal/compiler/passes/embed_glyphs.rs @@ -390,6 +390,7 @@ fn embed_font( glyphs, weight: face_info.weight.0, italic: face_info.style != fontdb::Style::Normal, + sdf: cfg!(feature = "embed-glyphs-as-sdf"), } } @@ -441,7 +442,7 @@ fn embed_sdf_glyphs( font: &Font, fallback_fonts: &[Font], ) -> Vec { - let Some(target_pixel_size) = pixel_sizes.iter().max().map(|max_size| max_size / 2) else { + let Some(target_pixel_size) = pixel_sizes.iter().max().map(|max_size| max_size * 2 / 3) else { return vec![]; }; @@ -471,7 +472,7 @@ fn embed_sdf_glyphs( glyph_data[*glyph_index as usize] = glyph; } - vec![BitmapGlyphs { pixel_size: 0 /* indicates SDF */, glyph_data }] + vec![BitmapGlyphs { pixel_size: target_pixel_size, glyph_data }] } #[cfg(all(not(target_arch = "wasm32"), feature = "embed-glyphs-as-sdf"))] @@ -493,19 +494,16 @@ fn generate_sdf_for_glyph( let metrics = font.metrics(); - const RANGE: f64 = 256.0; + const RANGE: f64 = 2.; let target_pixel_size = target_pixel_size as f64; let scale = target_pixel_size / metrics.units_per_em as f64; + let width = ((bbox.x_max as f64 - bbox.x_min as f64) * scale + 2. * RANGE).ceil() as u32; + let height = ((bbox.y_max as f64 - bbox.y_min as f64) * scale + 2. * RANGE).ceil() as u32; let transformation = nalgebra::convert::<_, Affine2>(Similarity2::new( - Vector2::new( - target_pixel_size - (bbox.x_min as f64 * scale), - target_pixel_size - (bbox.y_min as f64 * scale), - ), - 0.0, + Vector2::new(RANGE - bbox.x_min as f64 * scale, RANGE - bbox.y_min as f64 * scale), + 0., scale, )); - let width = ((bbox.x_max as f64 - bbox.x_min as f64) * scale).ceil() as u32; - let height = ((bbox.y_max as f64 - bbox.y_min as f64) * scale).ceil() as u32; // Unlike msdfgen, the transformation is not passed into the // `generate_msdf` function – the coordinates of the control points @@ -526,17 +524,29 @@ fn generate_sdf_for_glyph( fdsm::bezier::scanline::FillRule::Nonzero, ); - let glyph_data = sdf.into_raw(); + let mut glyph_data = sdf.into_raw(); + + // normalize around 0 + for x in &mut glyph_data { + *x = x.wrapping_sub(128); + } + + // invert the y coordinate (as the fsdm crate has the y axis inverted) + let (w, h) = (width as usize, height as usize); + for idx in 0..glyph_data.len() / 2 { + glyph_data.swap(idx, (h - idx / w - 1) * w + idx % w); + } + + // Add a "0" so that we can always access pos+1 without going out of bound + // (so that the last row will look like `data[len-1]*1 + data[len]*0`) + glyph_data.push(0); let metrics = fontdue::Metrics { - // NOTE! This is the x pos in font design space, so it needs to be multiplied by the target size and divided by units per em. - xmin: bbox.x_min as _, - // NOTE! This is the y pos in font design space, so it needs to be multiplied by the target size and divided by units per em. - ymin: bbox.y_min as _, + xmin: -(RANGE - bbox.x_min as f64 * scale).floor() as i32, + ymin: -(RANGE - bbox.y_min as f64 * scale).floor() as i32, width: width as usize, height: height as usize, - // NOTE! This is the advance in font design space, so it needs to be multiplied by the target size and divided by units per em. - advance_width: face.glyph_hor_advance(glyph_id).unwrap() as _, + advance_width: (face.glyph_hor_advance(glyph_id).unwrap() as f64 * scale) as f32, advance_height: 0., /*unused */ bounds: Default::default(), /*unused */ }; diff --git a/internal/core/graphics/bitmapfont.rs b/internal/core/graphics/bitmapfont.rs index f6eba5f40e6..34de5aa9335 100644 --- a/internal/core/graphics/bitmapfont.rs +++ b/internal/core/graphics/bitmapfont.rs @@ -18,6 +18,7 @@ pub struct BitmapGlyph { /// The horizontal distance to the next glyph pub x_advance: i16, /// The 8-bit alpha map that's to be blended with the current text color + /// or 8-bit signed distance field depending on `BitmapFont::sdf` pub data: Slice<'static, u8>, } @@ -67,4 +68,6 @@ pub struct BitmapFont { pub weight: u16, /// Whether the type-face is rendered italic. pub italic: bool, + /// Whether the format of the font is a signed distance field + pub sdf: bool, } diff --git a/internal/core/graphics/image.rs b/internal/core/graphics/image.rs index 54c698ab8c7..0c73882ebcd 100644 --- a/internal/core/graphics/image.rs +++ b/internal/core/graphics/image.rs @@ -234,6 +234,8 @@ pub enum PixelFormat { RgbaPremultiplied, /// Alpha map. 8bits. Each pixel is an alpha value. The color is specified separately. AlphaMap, + /// Distance field. 8bit interpreted as i8 + SignedDistanceField, } impl PixelFormat { @@ -244,6 +246,7 @@ impl PixelFormat { PixelFormat::Rgba => 4, PixelFormat::RgbaPremultiplied => 4, PixelFormat::AlphaMap => 1, + PixelFormat::SignedDistanceField => 1, } } } @@ -473,6 +476,9 @@ impl ImageInner { }); slice.fill_with(|| iter.next().unwrap()); } + PixelFormat::SignedDistanceField => { + todo!("converting from a signed distance field to an image") + } }; } } diff --git a/internal/core/software_renderer.rs b/internal/core/software_renderer.rs index 78b71d0ab5f..da2732713bd 100644 --- a/internal/core/software_renderer.rs +++ b/internal/core/software_renderer.rs @@ -1499,7 +1499,7 @@ impl<'a, T: ProcessScene> SceneBuilder<'a, T> { for positioned_glyph in glyphs { let glyph = paragraph.layout.font.render_glyph(positioned_glyph.glyph_id); - let src_rect = PhysicalRect::new( + let target_rect = PhysicalRect::new( PhysicalPoint::from_lengths( line_x + positioned_glyph.x + glyph.x, baseline_y - glyph.y - glyph.height, @@ -1515,71 +1515,96 @@ impl<'a, T: ProcessScene> SceneBuilder<'a, T> { _ => color, }; - if let Some(clipped_src) = src_rect.intersection(&physical_clip) { - let geometry = clipped_src.translate(offset).round(); - let origin = (geometry.origin - offset.round()).round().cast::(); - let actual_x = (origin.x - src_rect.origin.x as i16) as usize; - let actual_y = (origin.y - src_rect.origin.y as i16) as usize; - let pixel_stride = glyph.width.get() as u16; - let mut geometry = geometry.cast(); - if geometry.size.width > glyph.width.get() - (actual_x as i16) { - geometry.size.width = glyph.width.get() - (actual_x as i16) - } - if geometry.size.height > glyph.height.get() - (actual_y as i16) { - geometry.size.height = glyph.height.get() - (actual_y as i16) - } - let source_size = geometry.size; - if source_size.is_empty() { - continue; + let Some(clipped_target) = physical_clip.intersection(&target_rect) else { + continue; + }; + let geometry = clipped_target.translate(offset).round(); + let origin = (geometry.origin - offset.round()).round().cast::(); + let actual_x = (origin.x - target_rect.origin.x as i16) as usize; + let actual_y = (origin.y - target_rect.origin.y as i16) as usize; + let pixel_stride = glyph.pixel_stride; + let mut geometry = geometry.cast(); + if geometry.size.width > glyph.width.get() - (actual_x as i16) { + geometry.size.width = glyph.width.get() - (actual_x as i16) + } + if geometry.size.height > glyph.height.get() - (actual_y as i16) { + geometry.size.height = glyph.height.get() - (actual_y as i16) + } + let source_size = geometry.size; + if source_size.is_empty() { + continue; + } + match &glyph.alpha_map { + fonts::GlyphAlphaMap::Static(data) => { + let texture = if !glyph.sdf { + SceneTexture { + data: &data[actual_x + actual_y * pixel_stride as usize..], + pixel_stride, + format: PixelFormat::AlphaMap, + extra: SceneTextureExtra { + colorize: color, + // color already is mixed with global alpha + alpha: color.alpha(), + rotation: self.rotation.orientation, + dx: Fixed::from_integer(1), + dy: Fixed::from_integer(1), + off_x: Fixed::from_integer(0), + off_y: Fixed::from_integer(0), + }, + } + } else { + let dx = Fixed::from_integer(pixel_stride - 1) + / glyph.width.get() as u16; + let dy = Fixed::from_integer((data.len() as u16 - 1) / pixel_stride - 1) + / glyph.height.get() as u16; + let off_x = Fixed::::from_fixed(dx) + * (clipped_target.origin.x - target_rect.origin.x) as i32; + let off_y = Fixed::::from_fixed(dy) + * (clipped_target.origin.y - target_rect.origin.y) as i32; + SceneTexture { + data: data, + pixel_stride, + format: PixelFormat::SignedDistanceField, + extra: SceneTextureExtra { + colorize: color, + // color already is mixed with global alpha + alpha: color.alpha(), + rotation: self.rotation.orientation, + dx, + dy, + off_x: Fixed::try_from_fixed(off_x).unwrap(), + off_y: Fixed::try_from_fixed(off_y).unwrap(), + }, + } + }; + self.processor + .process_texture(geometry.transformed(self.rotation), texture); } - match &glyph.alpha_map { - fonts::GlyphAlphaMap::Static(data) => { - self.processor.process_texture( - geometry.transformed(self.rotation), - SceneTexture { - data: &data - [actual_x + actual_y * pixel_stride as usize..], - pixel_stride, - format: PixelFormat::AlphaMap, - extra: SceneTextureExtra { - colorize: color, - // color already is mixed with global alpha - alpha: color.alpha(), - rotation: self.rotation.orientation, - dx: Fixed::from_integer(1), - dy: Fixed::from_integer(1), - off_x: Fixed::from_integer(0), - off_y: Fixed::from_integer(0), - }, + fonts::GlyphAlphaMap::Shared(data) => { + self.processor.process_shared_image_buffer( + geometry.transformed(self.rotation), + SharedBufferCommand { + buffer: SharedBufferData::AlphaMap { + data: data.clone(), + width: pixel_stride, }, - ); - } - fonts::GlyphAlphaMap::Shared(data) => { - self.processor.process_shared_image_buffer( - geometry.transformed(self.rotation), - SharedBufferCommand { - buffer: SharedBufferData::AlphaMap { - data: data.clone(), - width: pixel_stride, - }, - source_rect: PhysicalRect::new( - PhysicalPoint::new(actual_x as _, actual_y as _), - source_size, - ), - extra: SceneTextureExtra { - colorize: color, - // color already is mixed with global alpha - alpha: color.alpha(), - rotation: self.rotation.orientation, - dx: Fixed::from_integer(1), - dy: Fixed::from_integer(1), - off_x: Fixed::from_integer(0), - off_y: Fixed::from_integer(0), - }, + source_rect: PhysicalRect::new( + PhysicalPoint::new(actual_x as _, actual_y as _), + source_size, + ), + extra: SceneTextureExtra { + colorize: color, + // color already is mixed with global alpha + alpha: color.alpha(), + rotation: self.rotation.orientation, + dx: Fixed::from_integer(1), + dy: Fixed::from_integer(1), + off_x: Fixed::from_integer(0), + off_y: Fixed::from_integer(0), }, - ); - } - }; + }, + ); + } } } core::ops::ControlFlow::Continue(()) diff --git a/internal/core/software_renderer/draw_functions.rs b/internal/core/software_renderer/draw_functions.rs index a0dec0da441..431059d737c 100644 --- a/internal/core/software_renderer/draw_functions.rs +++ b/internal/core/software_renderer/draw_functions.rs @@ -6,10 +6,9 @@ //! This is the module for the functions that are drawing the pixels //! on the line buffer -use super::{PhysicalLength, PhysicalRect}; +use super::{PhysicalLength, PhysicalRect, Fixed}; use crate::graphics::{PixelFormat, Rgb8Pixel}; use crate::lengths::{PointLengths, SizeLengths}; -use crate::software_renderer::fixed::Fixed; use crate::Color; use derive_more::{Add, Mul, Sub}; use integer_sqrt::IntegerSquareRoot; @@ -43,9 +42,10 @@ pub(super) fn draw_texture_line( if !rotation.is_transpose() { let mut delta = dx; + let row = off_y + dy * y; // The position where to start in the image array for a this row - let mut init = Fixed::from_integer((off_y + dy * y).truncate() % source_size.height) - * pixel_stride as i32; + let mut init = + Fixed::from_integer(row.truncate() % source_size.height) * pixel_stride as i32; // the size of the tile in physical pixels in the target let tile_len = (Fixed::from_integer(source_size.width) / delta) as usize; @@ -86,9 +86,10 @@ pub(super) fn draw_texture_line( data, alpha, colorize, + (pixel_stride as usize, row.fract(), dy), #[inline(always)] |bpp| { - let p = pos.truncate() as usize * bpp; + let p = (pos.truncate() as usize * bpp, pos.fract()); pos += delta; p }, @@ -114,6 +115,7 @@ pub(super) fn draw_texture_line( } else { let bpp = format.bpp(); let col = off_x + dx * y; + let col_fract = col.fract(); let col = (col.truncate() % source_size.width) as usize * bpp; let stride = pixel_stride as usize * bpp; let mut row_delta = dy; @@ -150,9 +152,10 @@ pub(super) fn draw_texture_line( data, alpha, colorize, + (stride, col_fract, dx), #[inline(always)] |_| { - let pos = row.truncate() as usize * stride + col; + let pos = (row.truncate() as usize * stride + col, row.fract()); row += row_delta; pos }, @@ -183,12 +186,13 @@ pub(super) fn draw_texture_line( data: &[u8], alpha: u8, color: Color, - mut pos: impl FnMut(usize) -> usize, + (stride, row_f, delta): (usize, u8, Fixed), + mut pos: impl FnMut(usize) -> (usize, u8), ) { match format { PixelFormat::Rgb => { for pix in line_buffer { - let pos = pos(3); + let pos = pos(3).0; let p = &data[pos..pos + 3]; if alpha == 0xff { *pix = TargetPixel::from_rgb(p[0], p[1], p[2]); @@ -202,7 +206,7 @@ pub(super) fn draw_texture_line( PixelFormat::Rgba => { if color.alpha() == 0 { for pix in line_buffer { - let pos = pos(4); + let pos = pos(4).0; let alpha = ((data[pos + 3] as u16 * alpha as u16) / 255) as u8; let c = PremultipliedRgbaColor::premultiply(Color::from_argb_u8( alpha, @@ -214,7 +218,7 @@ pub(super) fn draw_texture_line( } } else { for pix in line_buffer { - let pos = pos(4); + let pos = pos(4).0; let alpha = ((data[pos + 3] as u16 * alpha as u16) / 255) as u8; let c = PremultipliedRgbaColor::premultiply(Color::from_argb_u8( alpha, @@ -229,7 +233,7 @@ pub(super) fn draw_texture_line( PixelFormat::RgbaPremultiplied => { if color.alpha() > 0 { for pix in line_buffer { - let pos = pos(4); + let pos = pos(4).0; let c = PremultipliedRgbaColor::premultiply(Color::from_argb_u8( ((data[pos + 3] as u16 * alpha as u16) / 255) as u8, color.red(), @@ -240,7 +244,7 @@ pub(super) fn draw_texture_line( } } else if alpha == 0xff { for pix in line_buffer { - let pos = pos(4); + let pos = pos(4).0; let c = PremultipliedRgbaColor { alpha: data[pos + 3], red: data[pos + 0], @@ -251,7 +255,7 @@ pub(super) fn draw_texture_line( } } else { for pix in line_buffer { - let pos = pos(4); + let pos = pos(4).0; let c = PremultipliedRgbaColor { alpha: (data[pos + 3] as u16 * alpha as u16 / 255) as u8, red: (data[pos + 0] as u16 * alpha as u16 / 255) as u8, @@ -264,7 +268,7 @@ pub(super) fn draw_texture_line( } PixelFormat::AlphaMap => { for pix in line_buffer { - let pos = pos(1); + let pos = pos(1).0; let c = PremultipliedRgbaColor::premultiply(Color::from_argb_u8( ((data[pos] as u16 * alpha as u16) / 255) as u8, color.red(), @@ -274,6 +278,32 @@ pub(super) fn draw_texture_line( pix.blend(c); } } + PixelFormat::SignedDistanceField => { + const RANGE: i32 = 2; + let factor = (256 * 256 / delta.0) * RANGE; + for pix in line_buffer { + let (pos, f) = pos(1); + let (f, row_f) = (f as i32, row_f as i32); + let mut dist = ((data[pos] as i8 as i32) * (256 - f) + + (data[pos + 1] as i8 as i32) * f) + * (256 - row_f); + if pos + stride + 1 < data.len() { + dist += ((data[pos + stride] as i8 as i32) * (256 - f) + + (data[pos + stride + 1] as i8 as i32) * f) + * row_f + } else { + debug_assert_eq!(row_f, 0); + } + let a = ((((dist >> 8) * factor) >> 16) + 128).clamp(0, 255) * alpha as i32; + let c = PremultipliedRgbaColor::premultiply(Color::from_argb_u8( + (a / 255) as u8, + color.red(), + color.green(), + color.blue(), + )); + pix.blend(c); + } + } }; } } diff --git a/internal/core/software_renderer/fixed.rs b/internal/core/software_renderer/fixed.rs index b1b56016490..fe53349f5d8 100644 --- a/internal/core/software_renderer/fixed.rs +++ b/internal/core/software_renderer/fixed.rs @@ -25,6 +25,18 @@ impl< self.0 >> SHIFT } + /// Return the fractional part of the fixed point value + pub fn fract(self) -> u8 + where + T: num_traits::AsPrimitive, + { + if SHIFT < 8 { + (self.0 >> (SHIFT - 8)).as_() + } else { + (self.0 << (8 - SHIFT)).as_() + } + } + pub fn from_fixed< T2: core::ops::Shl + core::ops::Shr + Into, const SHIFT2: usize, @@ -113,3 +125,10 @@ impl, const SHIFT: usize> core::ops::Rem for Fixed Self(self.0 % rhs.0) } } + +impl, const SHIFT: usize> core::ops::Div for Fixed { + type Output = Self; + fn div(self, rhs: T) -> Self::Output { + Self(self.0 / rhs) + } +} diff --git a/internal/core/software_renderer/fonts.rs b/internal/core/software_renderer/fonts.rs index e8ccd309087..e14694e5fb0 100644 --- a/internal/core/software_renderer/fonts.rs +++ b/internal/core/software_renderer/fonts.rs @@ -33,6 +33,8 @@ pub struct RenderableGlyph { pub width: PhysicalLength, pub height: PhysicalLength, pub alpha_map: GlyphAlphaMap, + pub pixel_stride: u16, + pub sdf: bool, } impl RenderableGlyph { @@ -158,10 +160,15 @@ pub fn match_font(request: &FontRequest, scale_factor: ScaleFactor) -> Font { .glyphs .partition_point(|glyphs| glyphs.pixel_size() <= requested_pixel_size) .saturating_sub(1); - let matching_glyphs = &font.glyphs[nearest_pixel_size]; - pixelfont::PixelFont { bitmap_font: font, glyphs: matching_glyphs }.into() + let pixel_size = if font.sdf { + requested_pixel_size + } else { + PhysicalLength::new(nearest_pixel_size as i16) + }; + + pixelfont::PixelFont { bitmap_font: font, glyphs: matching_glyphs, pixel_size }.into() } pub fn text_layout_for_font<'a, Font>( diff --git a/internal/core/software_renderer/fonts/pixelfont.rs b/internal/core/software_renderer/fonts/pixelfont.rs index 5aab43ab316..8e7ade634cf 100644 --- a/internal/core/software_renderer/fonts/pixelfont.rs +++ b/internal/core/software_renderer/fonts/pixelfont.rs @@ -4,54 +4,37 @@ use crate::{ graphics::{BitmapFont, BitmapGlyphs}, software_renderer::PhysicalLength, - textlayout::{Glyph, TextShaper}, + textlayout::{FontMetrics, Glyph, TextShaper}, }; use super::{GlyphRenderer, RenderableGlyph}; impl BitmapGlyphs { - fn ascent(&self, font: &BitmapFont) -> PhysicalLength { - (PhysicalLength::new(self.pixel_size).cast() * font.ascent / font.units_per_em).cast() - } - fn descent(&self, font: &BitmapFont) -> PhysicalLength { - (PhysicalLength::new(self.pixel_size).cast() * font.descent / font.units_per_em).cast() - } - fn height(&self, font: &BitmapFont) -> PhysicalLength { - // The descent is negative (relative to the baseline) - (PhysicalLength::new(self.pixel_size).cast() * (font.ascent - font.descent) - / font.units_per_em) - .cast() - } /// Returns the size of the pre-rendered font in pixels. pub fn pixel_size(&self) -> PhysicalLength { PhysicalLength::new(self.pixel_size) } - /// Returns the x-height of the font scaled to the font's pixel size. - pub fn x_height(&self, font: &BitmapFont) -> PhysicalLength { - (PhysicalLength::new(self.pixel_size).cast() * font.x_height / font.units_per_em).cast() - } - /// Returns the cap-height of the font scaled to the font's pixel size. - pub fn cap_height(&self, font: &BitmapFont) -> PhysicalLength { - (PhysicalLength::new(self.pixel_size).cast() * font.cap_height / font.units_per_em).cast() - } } // A font that is resolved to a specific pixel size. pub struct PixelFont { pub bitmap_font: &'static BitmapFont, pub glyphs: &'static BitmapGlyphs, + pub pixel_size: PhysicalLength, } impl PixelFont { - pub fn pixel_size(&self) -> PhysicalLength { - self.glyphs.pixel_size() - } pub fn glyph_index_to_glyph_id(index: usize) -> core::num::NonZeroU16 { core::num::NonZeroU16::new(index as u16 + 1).unwrap() } pub fn glyph_id_to_glyph_index(id: core::num::NonZeroU16) -> usize { id.get() as usize - 1 } + + /// Convert from the glyph coordinate to the target coordinate + pub fn scale_glyph_length(&self, v: i16) -> PhysicalLength { + self.pixel_size * v / self.glyphs.pixel_size + } } impl GlyphRenderer for PixelFont { @@ -59,11 +42,13 @@ impl GlyphRenderer for PixelFont { let glyph_index = Self::glyph_id_to_glyph_index(glyph_id); let bitmap_glyph = &self.glyphs.glyph_data[glyph_index]; RenderableGlyph { - x: PhysicalLength::new(bitmap_glyph.x), - y: PhysicalLength::new(bitmap_glyph.y), - width: PhysicalLength::new(bitmap_glyph.width), - height: PhysicalLength::new(bitmap_glyph.height), + x: self.scale_glyph_length(bitmap_glyph.x), + y: self.scale_glyph_length(bitmap_glyph.y), + width: self.scale_glyph_length(bitmap_glyph.width), + height: self.scale_glyph_length(bitmap_glyph.height), alpha_map: bitmap_glyph.data.as_slice().into(), + pixel_stride: bitmap_glyph.width as u16, + sdf: self.bitmap_font.sdf, } } } @@ -86,8 +71,11 @@ impl TextShaper for PixelFont { self.bitmap_font.character_map[char_map_index].glyph_index as usize }); let x_advance = glyph_index.map_or_else( - || self.pixel_size(), - |glyph_index| PhysicalLength::new(self.glyphs.glyph_data[glyph_index].x_advance), + || self.pixel_size, + |glyph_index| { + self.pixel_size * self.glyphs.glyph_data[glyph_index].x_advance + / self.glyphs.pixel_size + }, ); Glyph { glyph_id: glyph_index.map(Self::glyph_index_to_glyph_id), @@ -119,28 +107,32 @@ impl TextShaper for PixelFont { } fn max_lines(&self, max_height: PhysicalLength) -> usize { - (max_height / self.glyphs.height(self.bitmap_font)).get() as _ + (max_height / self.height()).get() as _ } } -impl crate::textlayout::FontMetrics for PixelFont { +impl FontMetrics for PixelFont { fn ascent(&self) -> PhysicalLength { - self.glyphs.ascent(self.bitmap_font) + (self.pixel_size.cast() * self.bitmap_font.ascent / self.bitmap_font.units_per_em).cast() } fn descent(&self) -> PhysicalLength { - self.glyphs.descent(self.bitmap_font) + (self.pixel_size.cast() * self.bitmap_font.descent / self.bitmap_font.units_per_em).cast() } fn height(&self) -> PhysicalLength { - self.glyphs.height(self.bitmap_font) + // The descent is negative (relative to the baseline) + (self.pixel_size.cast() * (self.bitmap_font.ascent - self.bitmap_font.descent) + / self.bitmap_font.units_per_em) + .cast() } fn x_height(&self) -> PhysicalLength { - self.glyphs.x_height(&self.bitmap_font) + (self.pixel_size.cast() * self.bitmap_font.x_height / self.bitmap_font.units_per_em).cast() } fn cap_height(&self) -> PhysicalLength { - self.glyphs.cap_height(&self.bitmap_font) + (self.pixel_size.cast() * self.bitmap_font.cap_height / self.bitmap_font.units_per_em) + .cast() } } diff --git a/internal/core/software_renderer/fonts/vectorfont.rs b/internal/core/software_renderer/fonts/vectorfont.rs index 4b68af7221f..d015353902d 100644 --- a/internal/core/software_renderer/fonts/vectorfont.rs +++ b/internal/core/software_renderer/fonts/vectorfont.rs @@ -211,6 +211,8 @@ impl super::GlyphRenderer for VectorFont { width: PhysicalLength::new(metrics.width.try_into().unwrap()), height: PhysicalLength::new(metrics.height.try_into().unwrap()), alpha_map: alpha_map.into(), + sdf: false, + pixel_stride: metrics.width.try_into().unwrap(), }; cache.put_with_weight(cache_key, glyph.clone()).ok(); diff --git a/internal/core/software_renderer/scene.rs b/internal/core/software_renderer/scene.rs index fae9b70f95c..4bc9be5dfa1 100644 --- a/internal/core/software_renderer/scene.rs +++ b/internal/core/software_renderer/scene.rs @@ -294,7 +294,12 @@ pub struct SceneTexture<'a> { impl<'a> SceneTexture<'a> { pub fn source_size(&self) -> PhysicalSize { - let len = self.data.len() / self.format.bpp(); + let mut len = self.data.len(); + if self.format == PixelFormat::SignedDistanceField { + len -= 1; + } else { + len /= self.format.bpp(); + } let stride = self.pixel_stride as usize; let h = len / stride; let w = len % stride;