1 | #[cfg (all(feature = "libm" , not(feature = "std" )))] |
2 | use crate::nostd_float::FloatExt; |
3 | use 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)] |
21 | pub 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 | |
30 | impl 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 | |
41 | impl 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)] |
51 | pub 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 | /// ``` |
79 | pub 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 | |
229 | impl<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)] |
248 | pub struct PxScaleFont<F> { |
249 | pub font: F, |
250 | pub scale: PxScale, |
251 | } |
252 | |
253 | impl<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 | |
261 | impl<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 | } |
277 | |