| 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 | |