ab_glyph/
scale.rs

1#[cfg(all(feature = "libm", not(feature = "std")))]
2use crate::nostd_float::FloatExt;
3use crate::{Font, Glyph, GlyphId, OutlinedGlyph, Rect};
4
5/// Pixel scale.
6///
7/// This is the pixel-height of text.
8///
9/// Usually one uses `x == y`, but one may use a different ratio to stretch a
10/// font horizontally or vertically.
11///
12/// To convert pt size into pixel-scale see [`Font::pt_to_px_scale`].
13///
14/// # Example
15/// ```
16/// use ab_glyph::PxScale;
17///
18/// let uniform_scale_24px = PxScale::from(24.0);
19/// ```
20#[derive(Copy, Clone, Debug, PartialEq, PartialOrd)]
21pub struct PxScale {
22    /// Horizontal scale in pixels.
23    pub x: f32,
24    /// Vertical scale in pixels.
25    ///
26    /// By definition, this is the pixel-height of a font.
27    pub y: f32,
28}
29
30impl PxScale {
31    /// Returns a `PxScale` with both x & y scale values set to the nearest integer.
32    #[inline]
33    pub fn round(self) -> Self {
34        Self {
35            x: self.x.round(),
36            y: self.y.round(),
37        }
38    }
39}
40
41impl From<f32> for PxScale {
42    /// Uniform scaling where x & y are the same.
43    #[inline]
44    fn from(s: f32) -> Self {
45        PxScale { x: s, y: s }
46    }
47}
48
49/// 2D scale factors for use with unscaled metrics.
50#[derive(Copy, Clone, Debug, PartialEq, PartialOrd)]
51pub struct PxScaleFactor {
52    pub horizontal: f32,
53    pub vertical: f32,
54}
55
56/// A [`Font`] with an associated pixel scale. This can be used to provide
57/// pixel scale values for glyph advances, heights etc.
58///
59/// # Example
60/// ```
61/// use ab_glyph::{Font, FontRef, PxScale, ScaleFont};
62///
63/// # fn main() -> Result<(), ab_glyph::InvalidFont> {
64/// let font = FontRef::try_from_slice(include_bytes!("../../dev/fonts/Exo2-Light.otf"))?;
65///
66/// // Associate the font with a scale of 45px
67/// let scaled_font = font.as_scaled(PxScale::from(45.0));
68///
69/// assert_eq!(scaled_font.height(), 45.0);
70/// assert_eq!(scaled_font.h_advance(scaled_font.glyph_id('b')), 21.225);
71///
72/// // Replace associated scale with another
73/// let scaled_font = scaled_font.with_scale(180.0);
74///
75/// assert_eq!(scaled_font.height(), 180.0);
76/// assert_eq!(scaled_font.h_advance(scaled_font.glyph_id('b')), 84.9);
77/// # Ok(()) }
78/// ```
79pub trait ScaleFont<F: Font> {
80    /// Returns the pixel scale associated with this font.
81    fn scale(&self) -> PxScale;
82
83    /// Returns a font reference.
84    fn font(&self) -> &F;
85
86    /// Scale factor for unscaled font horizontal values.
87    #[inline]
88    fn h_scale_factor(&self) -> f32 {
89        self.scale().x / self.font().height_unscaled()
90    }
91
92    /// Scale factor for unscaled font vertical values.
93    #[inline]
94    fn v_scale_factor(&self) -> f32 {
95        self.scale().y / self.font().height_unscaled()
96    }
97
98    #[inline]
99    fn scale_factor(&self) -> PxScaleFactor {
100        PxScaleFactor {
101            horizontal: self.h_scale_factor(),
102            vertical: self.v_scale_factor(),
103        }
104    }
105
106    /// Pixel scaled glyph ascent. See [glyph layout concepts](Font#glyph-layout-concepts).
107    #[inline]
108    fn ascent(&self) -> f32 {
109        self.v_scale_factor() * self.font().ascent_unscaled()
110    }
111
112    /// Pixel scaled glyph descent. See [glyph layout concepts](Font#glyph-layout-concepts).
113    #[inline]
114    fn descent(&self) -> f32 {
115        self.v_scale_factor() * self.font().descent_unscaled()
116    }
117
118    /// Pixel scaled height `ascent - descent`. See [glyph layout concepts](Font#glyph-layout-concepts).
119    ///
120    /// By definition of [`PxScale`], this is `self.scale().y`.
121    #[inline]
122    fn height(&self) -> f32 {
123        self.scale().y
124    }
125
126    /// Pixel scaled line gap. See [glyph layout concepts](Font#glyph-layout-concepts).
127    #[inline]
128    fn line_gap(&self) -> f32 {
129        self.v_scale_factor() * self.font().line_gap_unscaled()
130    }
131
132    /// Lookup a `GlyphId` matching a given `char`.
133    #[inline]
134    fn glyph_id(&self, c: char) -> GlyphId {
135        self.font().glyph_id(c)
136    }
137
138    /// Construct a [`Glyph`] with the font's pixel scale at
139    /// position `point(0.0, 0.0)`.
140    ///
141    /// # Example
142    /// ```
143    /// # use ab_glyph::*;
144    /// # let font = FontRef::try_from_slice(include_bytes!("../../dev/fonts/Exo2-Light.otf")).unwrap();
145    /// let scaled_font = font.as_scaled(50.0);
146    ///
147    /// let a1 = scaled_font.scaled_glyph('a');
148    /// let a2 = font.glyph_id('a').with_scale(50.0); // equivalent
149    ///
150    /// # assert_eq!(a1.id, a2.id);
151    /// assert_eq!(a1.scale, PxScale::from(50.0));
152    /// assert_eq!(a1.position, point(0.0, 0.0));
153    /// ```
154    #[inline]
155    fn scaled_glyph(&self, c: char) -> Glyph {
156        self.font().glyph_id(c).with_scale(self.scale())
157    }
158
159    /// Pixel scaled horizontal advance for a given glyph.
160    /// See [glyph layout concepts](Font#glyph-layout-concepts).
161    #[inline]
162    fn h_advance(&self, id: GlyphId) -> f32 {
163        self.h_scale_factor() * self.font().h_advance_unscaled(id)
164    }
165
166    /// Pixel scaled horizontal side bearing for a given glyph.
167    /// See [glyph layout concepts](Font#glyph-layout-concepts).
168    #[inline]
169    fn h_side_bearing(&self, id: GlyphId) -> f32 {
170        self.h_scale_factor() * self.font().h_side_bearing_unscaled(id)
171    }
172
173    /// Pixel scaled vertical advance for a given glyph.
174    #[inline]
175    fn v_advance(&self, id: GlyphId) -> f32 {
176        self.v_scale_factor() * self.font().v_advance_unscaled(id)
177    }
178
179    /// Pixel scaled vertical side bearing for a given glyph.
180    #[inline]
181    fn v_side_bearing(&self, id: GlyphId) -> f32 {
182        self.v_scale_factor() * self.font().v_side_bearing_unscaled(id)
183    }
184
185    /// Returns additional pixel scaled kerning to apply for a particular pair of glyphs.
186    #[inline]
187    fn kern(&self, first: GlyphId, second: GlyphId) -> f32 {
188        self.h_scale_factor() * self.font().kern_unscaled(first, second)
189    }
190
191    /// Returns the layout bounds of this glyph.
192    ///
193    /// Horizontally: Glyph position +/- h_advance/h_side_bearing.
194    /// Vertically: Glyph position +/- ascent/descent.
195    ///
196    /// These are *not* the same as [`OutlinedGlyph::px_bounds`]. If you are drawing pixels
197    /// you should use `px_bounds` and not this method as outlines are not bound by layout
198    /// values.
199    ///
200    /// Note this method does not make use of the associated scale, as `Glyph`
201    /// already includes one of it's own.
202    #[inline]
203    fn glyph_bounds(&self, glyph: &Glyph) -> Rect {
204        self.font().glyph_bounds(glyph)
205    }
206
207    /// The number of glyphs present in this font. Glyph identifiers for this
208    /// font will always be in the range `0..self.glyph_count()`
209    #[inline]
210    fn glyph_count(&self) -> usize {
211        self.font().glyph_count()
212    }
213
214    /// Returns an iterator of all distinct `(GlyphId, char)` pairs. Not ordered.
215    ///
216    /// Same as [`Font::codepoint_ids`].
217    fn codepoint_ids(&self) -> crate::CodepointIdIter<'_>;
218
219    /// Compute glyph outline ready for drawing.
220    ///
221    /// Note this method does not make use of the associated scale, as `Glyph`
222    /// already includes one of it's own.
223    #[inline]
224    fn outline_glyph(&self, glyph: Glyph) -> Option<OutlinedGlyph> {
225        self.font().outline_glyph(glyph)
226    }
227}
228
229impl<F: Font, SF: ScaleFont<F>> ScaleFont<F> for &SF {
230    #[inline]
231    fn scale(&self) -> PxScale {
232        (*self).scale()
233    }
234
235    #[inline]
236    fn font(&self) -> &F {
237        (*self).font()
238    }
239
240    #[inline]
241    fn codepoint_ids(&self) -> crate::CodepointIdIter<'_> {
242        (*self).codepoint_ids()
243    }
244}
245
246/// A [`Font`] and an associated pixel scale.
247#[derive(Clone, Copy, Debug)]
248pub struct PxScaleFont<F> {
249    pub font: F,
250    pub scale: PxScale,
251}
252
253impl<F> PxScaleFont<F> {
254    #[inline]
255    pub fn with_scale<S: Into<PxScale>>(mut self, scale: S) -> Self {
256        self.scale = scale.into();
257        self
258    }
259}
260
261impl<F: Font> ScaleFont<F> for PxScaleFont<F> {
262    #[inline]
263    fn scale(&self) -> PxScale {
264        self.scale
265    }
266
267    #[inline]
268    fn font(&self) -> &F {
269        &self.font
270    }
271
272    #[inline]
273    fn codepoint_ids(&self) -> crate::CodepointIdIter<'_> {
274        self.font.codepoint_ids()
275    }
276}