Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rework Size handling #57

Merged
merged 5 commits into from
Dec 4, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,14 @@ The sections should follow the order `Added`, `Changed`, `Fixed`, and `Removed`.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## Unreleased

## Changed

- `Size` now uses 6 floating point digits precision instead of rounding to 0.5.
- Add `Size::from_px`, `Size::as_px`, `Size::as_pt`, and `Size::scale`.
- Remove `Rasterizer::update_dpr`; users should scale fonts themselves.

## 0.5.2

- Minimum Rust version has been bumped to 1.65
Expand Down
23 changes: 9 additions & 14 deletions src/darwin/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -102,12 +102,11 @@ impl Descriptor {
pub struct CoreTextRasterizer {
fonts: HashMap<FontKey, Font>,
keys: HashMap<(FontDesc, Size), FontKey>,
device_pixel_ratio: f32,
}

impl crate::Rasterize for CoreTextRasterizer {
fn new(device_pixel_ratio: f32) -> Result<CoreTextRasterizer, Error> {
Ok(CoreTextRasterizer { fonts: HashMap::new(), keys: HashMap::new(), device_pixel_ratio })
fn new() -> Result<CoreTextRasterizer, Error> {
Ok(CoreTextRasterizer { fonts: HashMap::new(), keys: HashMap::new() })
}

/// Get metrics for font specified by FontKey.
Expand All @@ -118,13 +117,13 @@ impl crate::Rasterize for CoreTextRasterizer {
}

fn load_font(&mut self, desc: &FontDesc, size: Size) -> Result<FontKey, Error> {
let scaled_size = Size::new(size.as_f32_pts() * self.device_pixel_ratio);
self.keys.get(&(desc.to_owned(), scaled_size)).map(|k| Ok(*k)).unwrap_or_else(|| {
let size = Size::new(size.as_pt());
self.keys.get(&(desc.to_owned(), size)).map(|k| Ok(*k)).unwrap_or_else(|| {
let font = self.get_font(desc, size)?;
let key = FontKey::next();

self.fonts.insert(key, font);
self.keys.insert((desc.clone(), scaled_size), key);
self.keys.insert((desc.clone(), size), key);

Ok(key)
})
Expand Down Expand Up @@ -156,10 +155,6 @@ impl crate::Rasterize for CoreTextRasterizer {
fn kerning(&mut self, _left: GlyphKey, _right: GlyphKey) -> (f32, f32) {
(0., 0.)
}

fn update_dpr(&mut self, device_pixel_ratio: f32) {
self.device_pixel_ratio = device_pixel_ratio;
}
}

impl CoreTextRasterizer {
Expand All @@ -173,8 +168,8 @@ impl CoreTextRasterizer {
for descriptor in descriptors {
if descriptor.style_name == style {
// Found the font we want.
let scaled_size = f64::from(size.as_f32_pts()) * f64::from(self.device_pixel_ratio);
let font = descriptor.to_font(scaled_size, true);
let size = f64::from(size.as_pt());
let font = descriptor.to_font(size, true);
return Ok(font);
}
}
Expand All @@ -191,11 +186,11 @@ impl CoreTextRasterizer {
) -> Result<Font, Error> {
let bold = weight == Weight::Bold;
let italic = slant != Slant::Normal;
let scaled_size = f64::from(size.as_f32_pts()) * f64::from(self.device_pixel_ratio);
let size = f64::from(size.as_pt());

let descriptors = descriptors_for_family(&desc.name[..]);
for descriptor in descriptors {
let font = descriptor.to_font(scaled_size, true);
let font = descriptor.to_font(size, true);
if font.is_bold() == bold && font.is_italic() == italic {
// Found the font we want.
return Ok(font);
Expand Down
22 changes: 6 additions & 16 deletions src/directwrite/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ struct Font {
pub struct DirectWriteRasterizer {
fonts: HashMap<FontKey, Font>,
keys: HashMap<FontDesc, FontKey>,
device_pixel_ratio: f32,
available_fonts: FontCollection,
fallback_sequence: Option<FontFallback>,
}
Expand All @@ -48,7 +47,7 @@ impl DirectWriteRasterizer {
character: char,
glyph_index: u16,
) -> Result<RasterizedGlyph, Error> {
let em_size = em_size(size);
let em_size = f32::from(size.as_px());

let glyph_run = DWRITE_GLYPH_RUN {
fontFace: unsafe { face.as_ptr() },
Expand All @@ -63,13 +62,13 @@ impl DirectWriteRasterizer {

let rendering_mode = face.get_recommended_rendering_mode_default_params(
em_size,
self.device_pixel_ratio,
1.,
dwrote::DWRITE_MEASURING_MODE_NATURAL,
);

let glyph_analysis = GlyphRunAnalysis::create(
&glyph_run,
self.device_pixel_ratio,
1.,
None,
rendering_mode,
dwrote::DWRITE_MEASURING_MODE_NATURAL,
Expand Down Expand Up @@ -136,11 +135,10 @@ impl DirectWriteRasterizer {
}

impl crate::Rasterize for DirectWriteRasterizer {
fn new(device_pixel_ratio: f32) -> Result<DirectWriteRasterizer, Error> {
fn new() -> Result<DirectWriteRasterizer, Error> {
Ok(DirectWriteRasterizer {
fonts: HashMap::new(),
keys: HashMap::new(),
device_pixel_ratio,
available_fonts: FontCollection::system(),
fallback_sequence: FontFallback::get_system_fallback(),
})
Expand All @@ -150,7 +148,7 @@ impl crate::Rasterize for DirectWriteRasterizer {
let face = &self.get_loaded_font(key)?.face;
let vmetrics = face.metrics().metrics0();

let scale = em_size(size) * self.device_pixel_ratio / f32::from(vmetrics.designUnitsPerEm);
let scale = f32::from(size.as_px()) / f32::from(vmetrics.designUnitsPerEm);

let underline_position = f32::from(vmetrics.underlinePosition) * scale;
let underline_thickness = f32::from(vmetrics.underlineThickness) * scale;
Expand Down Expand Up @@ -252,17 +250,9 @@ impl crate::Rasterize for DirectWriteRasterizer {
}
}

fn kerning(&mut self, left: GlyphKey, right: GlyphKey) -> (f32, f32) {
fn kerning(&mut self, _left: GlyphKey, _right: GlyphKey) -> (f32, f32) {
(0., 0.)
}

fn update_dpr(&mut self, device_pixel_ratio: f32) {
self.device_pixel_ratio = device_pixel_ratio;
}
}

fn em_size(size: Size) -> f32 {
size.as_f32_pts() * (96.0 / 72.0)
}

impl From<dwrote::Font> for Font {
Expand Down
14 changes: 3 additions & 11 deletions src/ft/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,6 @@ impl fmt::Debug for FaceLoadingProperties {
pub struct FreeTypeRasterizer {
loader: FreeTypeLoader,
fallback_lists: HashMap<FontKey, FallbackList>,
device_pixel_ratio: f32,

/// Rasterizer creation time stamp to delay lazy font config updates
/// in `Rasterizer::load_font`.
Expand Down Expand Up @@ -133,11 +132,10 @@ impl IntoF32 for i64 {
}

impl Rasterize for FreeTypeRasterizer {
fn new(device_pixel_ratio: f32) -> Result<FreeTypeRasterizer, Error> {
fn new() -> Result<FreeTypeRasterizer, Error> {
Ok(FreeTypeRasterizer {
loader: FreeTypeLoader::new()?,
fallback_lists: HashMap::new(),
device_pixel_ratio,
creation_timestamp: Some(Instant::now()),
})
}
Expand Down Expand Up @@ -204,9 +202,7 @@ impl Rasterize for FreeTypeRasterizer {
let font_key = self.face_for_glyph(glyph_key);
let face = &self.loader.faces[&font_key];
let index = face.ft_face.get_char_index(glyph_key.character as usize);
let pixelsize = face
.non_scalable
.unwrap_or_else(|| glyph_key.size.as_f32_pts() * self.device_pixel_ratio * 96. / 72.);
let pixelsize = face.non_scalable.unwrap_or_else(|| glyph_key.size.as_px() as f32);

if !face.colored_bitmap {
face.ft_face.set_char_size(to_freetype_26_6(pixelsize), 0, 0, 0)?;
Expand Down Expand Up @@ -308,10 +304,6 @@ impl Rasterize for FreeTypeRasterizer {

(from_freetype_26_6(kerning.x), from_freetype_26_6(kerning.y))
}

fn update_dpr(&mut self, device_pixel_ratio: f32) {
self.device_pixel_ratio = device_pixel_ratio;
}
}

impl From<Slant> for fc::Slant {
Expand Down Expand Up @@ -342,7 +334,7 @@ impl FreeTypeRasterizer {
/// Load a font face according to `FontDesc`.
fn get_face(&mut self, desc: &FontDesc, size: Size) -> Result<FontKey, Error> {
// Adjust for DPR.
let size = f64::from(size.as_f32_pts() * self.device_pixel_ratio * 96. / 72.);
let size = f64::from(size.as_px());

let config = fc::Config::get_current();
let mut pattern = Pattern::new();
Expand Down
62 changes: 31 additions & 31 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
#![deny(clippy::all, clippy::if_not_else, clippy::enum_glob_use)]

use std::fmt::{self, Display, Formatter};
use std::ops::{Add, Mul};
use std::sync::atomic::{AtomicUsize, Ordering};

#[cfg(not(any(target_os = "macos", windows)))]
Expand All @@ -25,6 +24,11 @@ pub mod darwin;
#[cfg(target_os = "macos")]
pub use darwin::CoreTextRasterizer as Rasterizer;

/// Max font size in pt.
///
/// The value is picked based on `u32` max, since we use 6 digits for fract.
const MAX_FONT_PT_SIZE: f32 = 3999.;

#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct FontDesc {
name: String,
Expand Down Expand Up @@ -101,47 +105,46 @@ pub struct GlyphKey {
pub size: Size,
}

/// Font size stored as integer.
/// Font size stored as base and fraction.
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub struct Size(i16);
pub struct Size(u32);

impl Size {
/// Create a new `Size` from a f32 size in points.
///
/// The font size is automatically clamped to supported range of `[1.; 3999.]` pt.
pub fn new(size: f32) -> Size {
Size((size * Size::factor()) as i16)
let size = size.clamp(1., MAX_FONT_PT_SIZE);
Size((size * Self::factor()) as u32)
}

/// Scale factor between font "Size" type and point size.
#[inline]
pub fn factor() -> f32 {
2.0
/// Create a new `Size` from px.
///
/// The value will be clamped to the pt range of [`Size::new`].
pub fn from_px(size: u16) -> Self {
let pt = size as f32 * 72. / 96.;
Size::new(pt)
}

/// Get the f32 size in points.
pub fn as_f32_pts(self) -> f32 {
f32::from(self.0) / Size::factor()
/// Scale font size by the given amount.
pub fn scale(self, scale: f32) -> Self {
Self::new(self.as_pt() * scale)
}
}

impl<T: Into<Size>> Add<T> for Size {
type Output = Size;

fn add(self, other: T) -> Size {
Size(self.0.saturating_add(other.into().0))
/// Get size in `px`.
pub fn as_px(self) -> u16 {
(self.as_pt() * 96. / 72.).trunc() as u16
}
}

impl<T: Into<Size>> Mul<T> for Size {
type Output = Size;

fn mul(self, other: T) -> Size {
Size(self.0 * other.into().0)
/// Get the size in `pt`.
pub fn as_pt(self) -> f32 {
(f64::from(self.0) / Size::factor() as f64) as f32
}
}

impl From<f32> for Size {
fn from(float: f32) -> Size {
Size::new(float)
/// Scale factor between font "Size" type and point size.
#[inline]
fn factor() -> f32 {
1_000_000.
}
}

Expand Down Expand Up @@ -231,7 +234,7 @@ impl Display for Error {

pub trait Rasterize {
/// Create a new Rasterizer.
fn new(device_pixel_ratio: f32) -> Result<Self, Error>
fn new() -> Result<Self, Error>
where
Self: Sized;

Expand All @@ -244,9 +247,6 @@ pub trait Rasterize {
/// Rasterize the glyph described by `GlyphKey`..
fn get_glyph(&mut self, _: GlyphKey) -> Result<RasterizedGlyph, Error>;

/// Update the Rasterizer's DPI factor.
fn update_dpr(&mut self, device_pixel_ratio: f32);

/// Kerning between two characters.
fn kerning(&mut self, left: GlyphKey, right: GlyphKey) -> (f32, f32);
}