azul_webrender_api/
font.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5#[cfg(target_os = "macos")]
6use core_foundation::string::CFString;
7#[cfg(target_os = "macos")]
8use core_graphics::font::CGFont;
9use peek_poke::PeekPoke;
10#[cfg(target_os = "macos")]
11use serde::de::{self, Deserialize, Deserializer};
12#[cfg(target_os = "macos")]
13use serde::ser::{Serialize, Serializer};
14use std::cmp::Ordering;
15use std::hash::{Hash, Hasher};
16#[cfg(not(target_os = "macos"))]
17use std::path::PathBuf;
18use std::sync::{Arc, RwLock, RwLockReadGuard};
19use std::collections::HashMap;
20// local imports
21use crate::IdNamespace;
22use crate::channel::Sender;
23use crate::color::ColorU;
24use crate::units::LayoutPoint;
25
26/// Hashable floating-point storage for font size.
27#[repr(C)]
28#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, PartialOrd, Deserialize, Serialize)]
29pub struct FontSize(pub f32);
30
31impl Ord for FontSize {
32    fn cmp(&self, other: &FontSize) -> Ordering {
33        self.partial_cmp(other).unwrap_or(Ordering::Equal)
34    }
35}
36
37impl Eq for FontSize {}
38
39impl Hash for FontSize {
40    fn hash<H: Hasher>(&self, state: &mut H) {
41        self.0.to_bits().hash(state);
42    }
43}
44
45impl From<f32> for FontSize {
46    fn from(size: f32) -> Self { FontSize(size) }
47}
48
49impl From<FontSize> for f32 {
50    fn from(size: FontSize) -> Self { size.0 }
51}
52
53impl FontSize {
54    pub fn zero() -> Self { FontSize(0.0) }
55
56    pub fn from_f32_px(size: f32) -> Self { FontSize(size) }
57
58    pub fn to_f32_px(&self) -> f32 { self.0 }
59
60    pub fn from_f64_px(size: f64) -> Self { FontSize(size as f32) }
61
62    pub fn to_f64_px(&self) -> f64 { self.0 as f64 }
63}
64
65/// Immutable description of a font instance requested by the user of the API.
66///
67/// `BaseFontInstance` can be identified by a `FontInstanceKey` so we should
68/// never need to hash it.
69#[derive(Clone, PartialEq, Eq, Debug, Ord, PartialOrd, MallocSizeOf)]
70#[cfg_attr(feature = "serialize", derive(Serialize))]
71#[cfg_attr(feature = "deserialize", derive(Deserialize))]
72pub struct BaseFontInstance {
73    ///
74    pub instance_key: FontInstanceKey,
75    ///
76    pub font_key: FontKey,
77    ///
78    pub size: FontSize,
79    ///
80    pub bg_color: ColorU,
81    ///
82    pub render_mode: FontRenderMode,
83    ///
84    pub flags: FontInstanceFlags,
85    ///
86    pub synthetic_italics: SyntheticItalics,
87    ///
88    #[cfg_attr(any(feature = "serialize", feature = "deserialize"), serde(skip))]
89    pub platform_options: Option<FontInstancePlatformOptions>,
90    ///
91    pub variations: Vec<FontVariation>,
92}
93
94pub type FontInstanceMap = HashMap<FontInstanceKey, Arc<BaseFontInstance>>;
95/// A map of font instance data accessed concurrently from multiple threads.
96#[derive(Clone)]
97#[cfg_attr(feature = "serialize", derive(Serialize))]
98#[cfg_attr(feature = "deserialize", derive(Deserialize))]
99pub struct SharedFontInstanceMap {
100    map: Arc<RwLock<FontInstanceMap>>,
101}
102
103impl SharedFontInstanceMap {
104    /// Creates an empty shared map.
105    pub fn new() -> Self {
106        SharedFontInstanceMap {
107            map: Arc::new(RwLock::new(HashMap::default()))
108        }
109    }
110
111    /// Acquires a write lock on the shared map.
112    pub fn lock(&mut self) -> Option<RwLockReadGuard<FontInstanceMap>> {
113        self.map.read().ok()
114    }
115
116    ///
117    pub fn get_font_instance_data(&self, key: FontInstanceKey) -> Option<FontInstanceData> {
118        match self.map.read().unwrap().get(&key) {
119            Some(instance) => Some(FontInstanceData {
120                font_key: instance.font_key,
121                size: instance.size.into(),
122                options: Some(FontInstanceOptions {
123                  render_mode: instance.render_mode,
124                  flags: instance.flags,
125                  bg_color: instance.bg_color,
126                  synthetic_italics: instance.synthetic_italics,
127                }),
128                platform_options: instance.platform_options,
129                variations: instance.variations.clone(),
130            }),
131            None => None,
132        }
133    }
134
135    /// Replace the shared map with the provided map.
136    pub fn set(&mut self, map: FontInstanceMap) {
137        *self.map.write().unwrap() = map;
138    }
139
140    ///
141    pub fn get_font_instance(&self, instance_key: FontInstanceKey) -> Option<Arc<BaseFontInstance>> {
142        let instance_map = self.map.read().unwrap();
143        instance_map.get(&instance_key).map(|instance| { Arc::clone(instance) })
144    }
145
146    ///
147    pub fn add_font_instance(
148        &mut self,
149        instance_key: FontInstanceKey,
150        font_key: FontKey,
151        size: f32,
152        options: Option<FontInstanceOptions>,
153        platform_options: Option<FontInstancePlatformOptions>,
154        variations: Vec<FontVariation>,
155    ) {
156        let FontInstanceOptions {
157            render_mode,
158            flags,
159            bg_color,
160            synthetic_italics,
161            ..
162        } = options.unwrap_or_default();
163
164        let instance = Arc::new(BaseFontInstance {
165            instance_key,
166            font_key,
167            size: size.into(),
168            bg_color,
169            render_mode,
170            flags,
171            synthetic_italics,
172            platform_options,
173            variations,
174        });
175
176        self.map
177            .write()
178            .unwrap()
179            .insert(instance_key, instance);
180    }
181
182    ///
183    pub fn delete_font_instance(&mut self, instance_key: FontInstanceKey) {
184        self.map.write().unwrap().remove(&instance_key);
185    }
186
187    ///
188    pub fn clear_namespace(&mut self, namespace: IdNamespace) {
189        self.map
190            .write()
191            .unwrap()
192            .retain(|key, _| key.0 != namespace);
193    }
194
195    ///
196    pub fn clone_map(&self) -> FontInstanceMap {
197        self.map.read().unwrap().clone()
198    }
199}
200
201#[cfg(not(target_os = "macos"))]
202#[derive(Clone, Debug, Serialize, Deserialize)]
203pub struct NativeFontHandle {
204    pub path: PathBuf,
205    pub index: u32,
206}
207
208#[cfg(target_os = "macos")]
209#[derive(Clone)]
210pub struct NativeFontHandle(pub CGFont);
211
212#[cfg(target_os = "macos")]
213impl Serialize for NativeFontHandle {
214    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
215    where
216        S: Serializer,
217    {
218        self.0
219            .postscript_name()
220            .to_string()
221            .serialize(serializer)
222    }
223}
224
225#[cfg(target_os = "macos")]
226impl<'de> Deserialize<'de> for NativeFontHandle {
227    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
228    where
229        D: Deserializer<'de>,
230    {
231        let postscript_name: String = Deserialize::deserialize(deserializer)?;
232
233        match CGFont::from_name(&CFString::new(&*postscript_name)) {
234            Ok(font) => Ok(NativeFontHandle(font)),
235            Err(_) => Err(de::Error::custom(
236                "Couldn't find a font with that PostScript name!",
237            )),
238        }
239    }
240}
241
242#[repr(C)]
243#[derive(Copy, Clone, Deserialize, Serialize, Debug)]
244pub struct GlyphDimensions {
245    pub left: i32,
246    pub top: i32,
247    pub width: i32,
248    pub height: i32,
249    pub advance: f32,
250}
251
252pub struct GlyphDimensionRequest {
253    pub key: FontInstanceKey,
254    pub glyph_indices: Vec<GlyphIndex>,
255    pub sender: Sender<Vec<Option<GlyphDimensions>>>,
256}
257
258pub struct GlyphIndexRequest {
259    pub key: FontKey,
260    pub text: String,
261    pub sender: Sender<Vec<Option<u32>>>,
262}
263
264#[repr(C)]
265#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize, Ord, PartialOrd)]
266pub struct FontKey(pub IdNamespace, pub u32);
267
268impl FontKey {
269    pub fn new(namespace: IdNamespace, key: u32) -> FontKey {
270        FontKey(namespace, key)
271    }
272}
273
274/// Container for the raw data describing a font. This might be a stream of
275/// bytes corresponding to a downloaded font, or a handle to a native font from
276/// the operating system.
277///
278/// Note that fonts need to be instantiated before being used, which involves
279/// assigning size and various other options. The word 'template' here is
280/// intended to distinguish this data from instance-specific data.
281#[derive(Clone)]
282pub enum FontTemplate {
283    Raw(Arc<Vec<u8>>, u32),
284    Native(NativeFontHandle),
285}
286
287#[repr(u8)]
288#[derive(Debug, Copy, Clone, Hash, Eq, MallocSizeOf, PartialEq, Serialize, Deserialize, Ord, PartialOrd, PeekPoke)]
289pub enum FontRenderMode {
290    Mono = 0,
291    Alpha,
292    Subpixel,
293}
294
295impl Default for FontRenderMode {
296    fn default() -> Self {
297        FontRenderMode::Mono
298    }
299}
300
301impl FontRenderMode {
302    // Combine two font render modes such that the lesser amount of AA limits the AA of the result.
303    pub fn limit_by(self, other: FontRenderMode) -> FontRenderMode {
304        match (self, other) {
305            (FontRenderMode::Subpixel, _) | (_, FontRenderMode::Mono) => other,
306            _ => self,
307        }
308    }
309}
310
311#[repr(C)]
312#[derive(Clone, Copy, Debug, MallocSizeOf, PartialOrd, Deserialize, Serialize)]
313pub struct FontVariation {
314    pub tag: u32,
315    pub value: f32,
316}
317
318impl Ord for FontVariation {
319    fn cmp(&self, other: &FontVariation) -> Ordering {
320        self.tag.cmp(&other.tag)
321            .then(self.value.to_bits().cmp(&other.value.to_bits()))
322    }
323}
324
325impl PartialEq for FontVariation {
326    fn eq(&self, other: &FontVariation) -> bool {
327        self.tag == other.tag &&
328        self.value.to_bits() == other.value.to_bits()
329    }
330}
331
332impl Eq for FontVariation {}
333
334impl Hash for FontVariation {
335    fn hash<H: Hasher>(&self, state: &mut H) {
336        self.tag.hash(state);
337        self.value.to_bits().hash(state);
338    }
339}
340
341#[repr(C)]
342#[derive(Clone, Copy, Debug, Deserialize, Hash, Eq, PartialEq, PartialOrd, Ord, Serialize, PeekPoke)]
343pub struct GlyphOptions {
344    pub render_mode: FontRenderMode,
345    pub flags: FontInstanceFlags,
346}
347
348impl Default for GlyphOptions {
349    fn default() -> Self {
350        GlyphOptions {
351            render_mode: FontRenderMode::Subpixel,
352            flags: FontInstanceFlags::empty(),
353        }
354    }
355}
356
357bitflags! {
358    #[repr(C)]
359    #[derive(Deserialize, MallocSizeOf, Serialize, PeekPoke)]
360    pub struct FontInstanceFlags: u32 {
361        // Common flags
362        const SYNTHETIC_BOLD    = 1 << 1;
363        const EMBEDDED_BITMAPS  = 1 << 2;
364        const SUBPIXEL_BGR      = 1 << 3;
365        const TRANSPOSE         = 1 << 4;
366        const FLIP_X            = 1 << 5;
367        const FLIP_Y            = 1 << 6;
368        const SUBPIXEL_POSITION = 1 << 7;
369        const VERTICAL          = 1 << 8;
370
371        // Internal flags
372        const TRANSFORM_GLYPHS  = 1 << 12;
373        const TEXTURE_PADDING   = 1 << 13;
374
375        // Windows flags
376        const FORCE_GDI         = 1 << 16;
377        const FORCE_SYMMETRIC   = 1 << 17;
378        const NO_SYMMETRIC      = 1 << 18;
379
380        // Mac flags
381        const FONT_SMOOTHING    = 1 << 16;
382
383        // FreeType flags
384        const FORCE_AUTOHINT    = 1 << 16;
385        const NO_AUTOHINT       = 1 << 17;
386        const VERTICAL_LAYOUT   = 1 << 18;
387        const LCD_VERTICAL      = 1 << 19;
388    }
389}
390
391impl Default for FontInstanceFlags {
392    #[cfg(target_os = "windows")]
393    fn default() -> FontInstanceFlags {
394        FontInstanceFlags::SUBPIXEL_POSITION
395    }
396
397    #[cfg(target_os = "macos")]
398    fn default() -> FontInstanceFlags {
399        FontInstanceFlags::SUBPIXEL_POSITION |
400        FontInstanceFlags::FONT_SMOOTHING
401    }
402
403    #[cfg(not(any(target_os = "macos", target_os = "windows")))]
404    fn default() -> FontInstanceFlags {
405        FontInstanceFlags::SUBPIXEL_POSITION
406    }
407}
408
409
410#[repr(C)]
411#[derive(Clone, Copy, Debug, Deserialize, Hash, Eq, MallocSizeOf, PartialEq, PartialOrd, Ord, Serialize)]
412pub struct SyntheticItalics {
413    // Angle in degrees (-90..90) for synthetic italics in 8.8 fixed-point.
414    pub angle: i16,
415}
416
417impl SyntheticItalics {
418    pub const ANGLE_SCALE: f32 = 256.0;
419
420    pub fn from_degrees(degrees: f32) -> Self {
421        SyntheticItalics { angle: (degrees.max(-89.0).min(89.0) * Self::ANGLE_SCALE) as i16 }
422    }
423
424    pub fn to_degrees(self) -> f32 {
425        self.angle as f32 / Self::ANGLE_SCALE
426    }
427
428    pub fn to_radians(self) -> f32 {
429        self.to_degrees().to_radians()
430    }
431
432    pub fn to_skew(self) -> f32 {
433        self.to_radians().tan()
434    }
435
436    pub fn enabled() -> Self {
437        Self::from_degrees(14.0)
438    }
439
440    pub fn disabled() -> Self {
441        SyntheticItalics { angle: 0 }
442    }
443
444    pub fn is_enabled(self) -> bool {
445        self.angle != 0
446    }
447}
448
449impl Default for SyntheticItalics {
450    fn default() -> Self {
451        SyntheticItalics::disabled()
452    }
453}
454
455#[repr(C)]
456#[derive(Clone, Copy, Debug, Deserialize, Hash, Eq, PartialEq, PartialOrd, Ord, Serialize)]
457pub struct FontInstanceOptions {
458    pub render_mode: FontRenderMode,
459    pub flags: FontInstanceFlags,
460    /// When bg_color.a is != 0 and render_mode is FontRenderMode::Subpixel,
461    /// the text will be rendered with bg_color.r/g/b as an opaque estimated
462    /// background color.
463    pub bg_color: ColorU,
464    pub synthetic_italics: SyntheticItalics,
465}
466
467impl Default for FontInstanceOptions {
468    fn default() -> FontInstanceOptions {
469        FontInstanceOptions {
470            render_mode: FontRenderMode::Subpixel,
471            flags: Default::default(),
472            bg_color: ColorU::new(0, 0, 0, 0),
473            synthetic_italics: SyntheticItalics::disabled(),
474        }
475    }
476}
477
478#[cfg(target_os = "windows")]
479#[repr(C)]
480#[derive(Clone, Copy, Debug, Deserialize, Hash, Eq, MallocSizeOf, PartialEq, PartialOrd, Ord, Serialize)]
481pub struct FontInstancePlatformOptions {
482    pub gamma: u16, // percent
483    pub contrast: u8, // percent
484    pub cleartype_level: u8, // percent
485}
486
487#[cfg(target_os = "windows")]
488impl Default for FontInstancePlatformOptions {
489    fn default() -> FontInstancePlatformOptions {
490        FontInstancePlatformOptions {
491            gamma: 180, // Default DWrite gamma
492            contrast: 100,
493            cleartype_level: 100,
494        }
495    }
496}
497
498#[cfg(target_os = "macos")]
499#[repr(C)]
500#[derive(Clone, Copy, Debug, Deserialize, Hash, Eq, MallocSizeOf, PartialEq, PartialOrd, Ord, Serialize)]
501pub struct FontInstancePlatformOptions {
502    pub unused: u32,
503}
504
505#[cfg(target_os = "macos")]
506impl Default for FontInstancePlatformOptions {
507    fn default() -> FontInstancePlatformOptions {
508        FontInstancePlatformOptions {
509            unused: 0,
510        }
511    }
512}
513
514#[cfg(not(any(target_os = "macos", target_os = "windows")))]
515#[repr(u8)]
516#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, PartialOrd, Ord, Serialize)]
517pub enum FontLCDFilter {
518    None,
519    Default,
520    Light,
521    Legacy,
522}
523
524#[cfg(not(any(target_os = "macos", target_os = "windows")))]
525#[repr(u8)]
526#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, PartialOrd, Ord, Serialize)]
527pub enum FontHinting {
528    None,
529    Mono,
530    Light,
531    Normal,
532    LCD,
533}
534
535#[cfg(not(any(target_os = "macos", target_os = "windows")))]
536#[repr(C)]
537#[derive(Clone, Copy, Debug, Deserialize, Hash, Eq, MallocSizeOf, PartialEq, PartialOrd, Ord, Serialize)]
538pub struct FontInstancePlatformOptions {
539    pub lcd_filter: FontLCDFilter,
540    pub hinting: FontHinting,
541}
542
543#[cfg(not(any(target_os = "macos", target_os = "windows")))]
544impl Default for FontInstancePlatformOptions {
545    fn default() -> FontInstancePlatformOptions {
546        FontInstancePlatformOptions {
547            lcd_filter: FontLCDFilter::Default,
548            hinting: FontHinting::LCD,
549        }
550    }
551}
552
553#[repr(C)]
554#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq, Ord, PartialOrd, MallocSizeOf, PeekPoke)]
555#[derive(Deserialize, Serialize)]
556pub struct FontInstanceKey(pub IdNamespace, pub u32);
557
558impl FontInstanceKey {
559    pub fn new(namespace: IdNamespace, key: u32) -> FontInstanceKey {
560        FontInstanceKey(namespace, key)
561    }
562}
563
564/// Data corresponding to an instantiation of a font, with size and
565/// other options specified.
566///
567/// Note that the actual font is stored out-of-band in `FontTemplate`.
568#[derive(Clone)]
569pub struct FontInstanceData {
570    pub font_key: FontKey,
571    pub size: f32,
572    pub options: Option<FontInstanceOptions>,
573    pub platform_options: Option<FontInstancePlatformOptions>,
574    pub variations: Vec<FontVariation>,
575}
576
577pub type GlyphIndex = u32;
578
579#[repr(C)]
580#[derive(Clone, Copy, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize, PeekPoke)]
581pub struct GlyphInstance {
582    pub index: GlyphIndex,
583    pub point: LayoutPoint,
584}
585
586impl Default for GlyphInstance {
587    fn default() -> Self {
588        GlyphInstance {
589            index: 0,
590            point: LayoutPoint::zero(),
591        }
592    }
593}
594
595impl Eq for GlyphInstance {}
596
597#[cfg_attr(feature = "cargo-clippy", allow(clippy::derive_hash_xor_eq))]
598impl Hash for GlyphInstance {
599    fn hash<H: Hasher>(&self, state: &mut H) {
600        // Note: this is inconsistent with the Eq impl for -0.0 (don't care).
601        self.index.hash(state);
602        self.point.x.to_bits().hash(state);
603        self.point.y.to_bits().hash(state);
604    }
605}