improv: extract decoration metrics from the font
This commit is contained in:
parent
2758919c80
commit
c12b3a9bf2
4 changed files with 62 additions and 13 deletions
|
|
@ -255,6 +255,12 @@ impl TextDecoration {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, Default, PartialEq)]
|
||||||
|
pub struct DecorationMetrics {
|
||||||
|
pub offset: f32,
|
||||||
|
pub thickness: f32,
|
||||||
|
}
|
||||||
|
|
||||||
/// Text attributes
|
/// Text attributes
|
||||||
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
|
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
|
||||||
pub struct Attrs<'a> {
|
pub struct Attrs<'a> {
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
use core::fmt::Display;
|
use core::fmt::Display;
|
||||||
|
|
||||||
use crate::{math, CacheKey, CacheKeyFlags, Color, TextDecoration};
|
use crate::{math, CacheKey, CacheKeyFlags, Color, DecorationMetrics, TextDecoration};
|
||||||
#[cfg(not(feature = "std"))]
|
#[cfg(not(feature = "std"))]
|
||||||
use alloc::vec::Vec;
|
use alloc::vec::Vec;
|
||||||
|
|
||||||
|
|
@ -60,6 +60,10 @@ pub struct LayoutGlyph {
|
||||||
pub cache_key_flags: CacheKeyFlags,
|
pub cache_key_flags: CacheKeyFlags,
|
||||||
/// Text decoration (underline, strikethough, overline)
|
/// Text decoration (underline, strikethough, overline)
|
||||||
pub text_decoration: TextDecoration,
|
pub text_decoration: TextDecoration,
|
||||||
|
/// Underline offset and thickness extracted from the font
|
||||||
|
pub underline_metrics: DecorationMetrics,
|
||||||
|
/// Strikethrough offset and thickness extracted from the font
|
||||||
|
pub strikethrough_metrics: DecorationMetrics,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
|
|
|
||||||
|
|
@ -67,8 +67,13 @@ fn draw_decoration_group<R: Renderer>(
|
||||||
let font_size = first.font_size;
|
let font_size = first.font_size;
|
||||||
let x_start = first.x;
|
let x_start = first.x;
|
||||||
let x_end = last.x + last.w;
|
let x_end = last.x + last.w;
|
||||||
let width = (x_end - x_start) as u32;
|
let width = x_end - x_start;
|
||||||
if width <= 0 {
|
if width <= 0.0 {
|
||||||
|
// first check to see if it's below 0.0
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let width = width as u32;
|
||||||
|
if width == 0 {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Underline
|
// Underline
|
||||||
|
|
@ -79,8 +84,8 @@ fn draw_decoration_group<R: Renderer>(
|
||||||
.underline_color_opt
|
.underline_color_opt
|
||||||
.or(first.color_opt)
|
.or(first.color_opt)
|
||||||
.unwrap_or(default_color);
|
.unwrap_or(default_color);
|
||||||
let thickness = (font_size * 14.0).max(1.0);
|
let thickness = (first.underline_metrics.thickness * font_size).max(1.0);
|
||||||
let y = run.line_y + font_size * 0.125;
|
let y = run.line_y - first.underline_metrics.offset * font_size;
|
||||||
renderer.rectangle(x_start as i32, y as i32, width, thickness as u32, color);
|
renderer.rectangle(x_start as i32, y as i32, width, thickness as u32, color);
|
||||||
}
|
}
|
||||||
UnderlineStyle::Double => {
|
UnderlineStyle::Double => {
|
||||||
|
|
@ -88,9 +93,9 @@ fn draw_decoration_group<R: Renderer>(
|
||||||
.underline_color_opt
|
.underline_color_opt
|
||||||
.or(first.color_opt)
|
.or(first.color_opt)
|
||||||
.unwrap_or(default_color);
|
.unwrap_or(default_color);
|
||||||
let thickness = (font_size * 14.0).max(1.0);
|
let thickness = (first.underline_metrics.thickness * font_size).max(1.0);
|
||||||
let gap = thickness;
|
let gap = thickness;
|
||||||
let y = run.line_y + font_size * 0.125;
|
let y = run.line_y - first.underline_metrics.offset * font_size;
|
||||||
renderer.rectangle(x_start as i32, y as i32, width, thickness as u32, color);
|
renderer.rectangle(x_start as i32, y as i32, width, thickness as u32, color);
|
||||||
renderer.rectangle(
|
renderer.rectangle(
|
||||||
x_start as i32,
|
x_start as i32,
|
||||||
|
|
@ -108,8 +113,8 @@ fn draw_decoration_group<R: Renderer>(
|
||||||
.strikethrough_color_opt
|
.strikethrough_color_opt
|
||||||
.or(first.color_opt)
|
.or(first.color_opt)
|
||||||
.unwrap_or(default_color);
|
.unwrap_or(default_color);
|
||||||
let thickness = (font_size / 14.0).max(1.0);
|
let thickness = (first.strikethrough_metrics.thickness * font_size).max(1.0);
|
||||||
let y = run.line_y - font_size * 0.3;
|
let y = run.line_y - first.strikethrough_metrics.offset * font_size;
|
||||||
renderer.rectangle(x_start as i32, y as i32, width, thickness as u32, color);
|
renderer.rectangle(x_start as i32, y as i32, width, thickness as u32, color);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -119,8 +124,11 @@ fn draw_decoration_group<R: Renderer>(
|
||||||
.overline_color_opt
|
.overline_color_opt
|
||||||
.or(first.color_opt)
|
.or(first.color_opt)
|
||||||
.unwrap_or(default_color);
|
.unwrap_or(default_color);
|
||||||
let thickness = (font_size / 14.0).max(1.0);
|
// we're reusing underline thickness for overline
|
||||||
let y = run.line_top;
|
let thickness = (first.underline_metrics.thickness * font_size).max(1.0);
|
||||||
|
let y = run.line_top; //TODO: this should be run.line_y - ascent
|
||||||
|
// but we don't have ascent in GlyphLayout
|
||||||
|
// using line_top as an approximation for now, which should be good enough for most fonts
|
||||||
renderer.rectangle(x_start as i32, y as i32, width, thickness as u32, color);
|
renderer.rectangle(x_start as i32, y as i32, width, thickness as u32, color);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
35
src/shape.rs
35
src/shape.rs
|
|
@ -4,8 +4,9 @@
|
||||||
|
|
||||||
use crate::fallback::FontFallbackIter;
|
use crate::fallback::FontFallbackIter;
|
||||||
use crate::{
|
use crate::{
|
||||||
math, Align, Attrs, AttrsList, CacheKeyFlags, Color, Ellipsize, EllipsizeHeightLimit, Font,
|
math, Align, Attrs, AttrsList, CacheKeyFlags, Color, DecorationMetrics, Ellipsize,
|
||||||
FontSystem, Hinting, LayoutGlyph, LayoutLine, Metrics, TextDecoration, Wrap,
|
EllipsizeHeightLimit, Font, FontSystem, Hinting, LayoutGlyph, LayoutLine, Metrics,
|
||||||
|
TextDecoration, Wrap,
|
||||||
};
|
};
|
||||||
#[cfg(not(feature = "std"))]
|
#[cfg(not(feature = "std"))]
|
||||||
use alloc::{format, vec, vec::Vec};
|
use alloc::{format, vec, vec::Vec};
|
||||||
|
|
@ -15,6 +16,7 @@ use core::cmp::{max, min};
|
||||||
use core::fmt;
|
use core::fmt;
|
||||||
use core::mem;
|
use core::mem;
|
||||||
use core::ops::Range;
|
use core::ops::Range;
|
||||||
|
use skrifa::metrics::Decoration;
|
||||||
|
|
||||||
#[cfg(not(feature = "std"))]
|
#[cfg(not(feature = "std"))]
|
||||||
use core_maths::CoreFloat;
|
use core_maths::CoreFloat;
|
||||||
|
|
@ -130,6 +132,7 @@ fn shape_fallback(
|
||||||
let font_scale = font.metrics().units_per_em as f32;
|
let font_scale = font.metrics().units_per_em as f32;
|
||||||
let ascent = font.metrics().ascent / font_scale;
|
let ascent = font.metrics().ascent / font_scale;
|
||||||
let descent = -font.metrics().descent / font_scale;
|
let descent = -font.metrics().descent / font_scale;
|
||||||
|
let (underline_metrics, strikethrough_metrics) = decoration_metrics(font);
|
||||||
|
|
||||||
let mut buffer = scratch.harfrust_buffer.take().unwrap_or_default();
|
let mut buffer = scratch.harfrust_buffer.take().unwrap_or_default();
|
||||||
buffer.set_direction(if span_rtl {
|
buffer.set_direction(if span_rtl {
|
||||||
|
|
@ -237,6 +240,8 @@ fn shape_fallback(
|
||||||
cache_key_flags: override_fake_italic(attrs.cache_key_flags, font, &attrs),
|
cache_key_flags: override_fake_italic(attrs.cache_key_flags, font, &attrs),
|
||||||
metrics_opt: attrs.metrics_opt.map(Into::into),
|
metrics_opt: attrs.metrics_opt.map(Into::into),
|
||||||
text_decoration: attrs.text_decoration,
|
text_decoration: attrs.text_decoration,
|
||||||
|
underline_metrics,
|
||||||
|
strikethrough_metrics,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -504,6 +509,8 @@ fn shape_skip(
|
||||||
let metrics = swash_font.metrics(&[]);
|
let metrics = swash_font.metrics(&[]);
|
||||||
let glyph_metrics = swash_font.glyph_metrics(&[]).scale(1.0);
|
let glyph_metrics = swash_font.glyph_metrics(&[]).scale(1.0);
|
||||||
|
|
||||||
|
let (underline_metrics, strikethrough_metrics) = decoration_metrics(font.as_ref());
|
||||||
|
|
||||||
let ascent = metrics.ascent / f32::from(metrics.units_per_em);
|
let ascent = metrics.ascent / f32::from(metrics.units_per_em);
|
||||||
let descent = metrics.descent / f32::from(metrics.units_per_em);
|
let descent = metrics.descent / f32::from(metrics.units_per_em);
|
||||||
|
|
||||||
|
|
@ -538,6 +545,8 @@ fn shape_skip(
|
||||||
),
|
),
|
||||||
metrics_opt: attrs.metrics_opt.map(Into::into),
|
metrics_opt: attrs.metrics_opt.map(Into::into),
|
||||||
text_decoration: attrs.text_decoration,
|
text_decoration: attrs.text_decoration,
|
||||||
|
underline_metrics,
|
||||||
|
strikethrough_metrics,
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
@ -575,6 +584,8 @@ pub struct ShapeGlyph {
|
||||||
pub cache_key_flags: CacheKeyFlags,
|
pub cache_key_flags: CacheKeyFlags,
|
||||||
pub metrics_opt: Option<Metrics>,
|
pub metrics_opt: Option<Metrics>,
|
||||||
pub text_decoration: TextDecoration,
|
pub text_decoration: TextDecoration,
|
||||||
|
pub underline_metrics: DecorationMetrics,
|
||||||
|
pub strikethrough_metrics: DecorationMetrics,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ShapeGlyph {
|
impl ShapeGlyph {
|
||||||
|
|
@ -605,6 +616,8 @@ impl ShapeGlyph {
|
||||||
metadata: self.metadata,
|
metadata: self.metadata,
|
||||||
cache_key_flags: self.cache_key_flags,
|
cache_key_flags: self.cache_key_flags,
|
||||||
text_decoration: self.text_decoration,
|
text_decoration: self.text_decoration,
|
||||||
|
underline_metrics: self.underline_metrics,
|
||||||
|
strikethrough_metrics: self.strikethrough_metrics,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -615,6 +628,24 @@ impl ShapeGlyph {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn decoration_metrics(font: &Font) -> (DecorationMetrics, DecorationMetrics) {
|
||||||
|
let metrics = font.metrics();
|
||||||
|
let upem = metrics.units_per_em as f32;
|
||||||
|
if upem == 0.0 {
|
||||||
|
return (DecorationMetrics::default(), DecorationMetrics::default());
|
||||||
|
}
|
||||||
|
(
|
||||||
|
DecorationMetrics {
|
||||||
|
offset: metrics.underline.map_or(-0.125, |d| d.offset / upem),
|
||||||
|
thickness: metrics.underline.map_or(1.0 / 14.0, |d| d.thickness / upem),
|
||||||
|
},
|
||||||
|
DecorationMetrics {
|
||||||
|
offset: metrics.strikeout.map_or(0.3, |d| d.offset / upem),
|
||||||
|
thickness: metrics.strikeout.map_or(1.0 / 14.0, |d| d.thickness / upem),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
/// span index used in VlRange to indicate this range is the ellipsis.
|
/// span index used in VlRange to indicate this range is the ellipsis.
|
||||||
const ELLIPSIS_SPAN: usize = usize::MAX;
|
const ELLIPSIS_SPAN: usize = usize::MAX;
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue