fonts/platform/freetype/
library_handle.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 https://mozilla.org/MPL/2.0/. */
4
5use std::os::raw::{c_long, c_void};
6use std::ptr;
7use std::sync::OnceLock;
8use std::sync::atomic::{AtomicUsize, Ordering};
9
10use freetype_sys::{
11    FT_Add_Default_Modules, FT_Done_Library, FT_Library, FT_Memory, FT_MemoryRec, FT_New_Library,
12};
13use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
14use parking_lot::ReentrantMutex;
15use servo_allocator::libc_compat::{free, malloc, realloc};
16use servo_allocator::usable_size;
17
18static FREETYPE_MEMORY_USAGE: AtomicUsize = AtomicUsize::new(0);
19static FREETYPE_LIBRARY_HANDLE: OnceLock<ReentrantMutex<FreeTypeLibraryHandle>> = OnceLock::new();
20
21extern "C" fn ft_alloc(_: FT_Memory, req_size: c_long) -> *mut c_void {
22    unsafe {
23        let pointer = malloc(req_size as usize);
24        FREETYPE_MEMORY_USAGE.fetch_add(usable_size(pointer), Ordering::Relaxed);
25        pointer
26    }
27}
28
29extern "C" fn ft_free(_: FT_Memory, pointer: *mut c_void) {
30    unsafe {
31        FREETYPE_MEMORY_USAGE.fetch_sub(usable_size(pointer), Ordering::Relaxed);
32        free(pointer as *mut _);
33    }
34}
35
36extern "C" fn ft_realloc(
37    _: FT_Memory,
38    _old_size: c_long,
39    new_req_size: c_long,
40    old_pointer: *mut c_void,
41) -> *mut c_void {
42    unsafe {
43        FREETYPE_MEMORY_USAGE.fetch_sub(usable_size(old_pointer), Ordering::Relaxed);
44        let new_pointer = realloc(old_pointer, new_req_size as usize);
45        FREETYPE_MEMORY_USAGE.fetch_add(usable_size(new_pointer), Ordering::Relaxed);
46        new_pointer
47    }
48}
49
50/// A FreeType library handle to be used for creating and dropping FreeType font faces.
51/// It is very important that this handle lives as long as the faces themselves, which
52/// is why only one of these is created for the entire execution of Servo and never
53/// dropped during execution.
54#[derive(Clone, Debug)]
55pub(crate) struct FreeTypeLibraryHandle {
56    pub(crate) freetype_library: FT_Library,
57    freetype_memory: FT_Memory,
58}
59
60unsafe impl Sync for FreeTypeLibraryHandle {}
61unsafe impl Send for FreeTypeLibraryHandle {}
62
63impl Drop for FreeTypeLibraryHandle {
64    #[allow(unused)]
65    fn drop(&mut self) {
66        assert!(!self.freetype_library.is_null());
67        unsafe {
68            FT_Done_Library(self.freetype_library);
69            Box::from_raw(self.freetype_memory);
70        }
71    }
72}
73
74impl MallocSizeOf for FreeTypeLibraryHandle {
75    fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
76        unsafe {
77            FREETYPE_MEMORY_USAGE.load(Ordering::Relaxed) +
78                ops.malloc_size_of(self.freetype_library as *const _) +
79                ops.malloc_size_of(self.freetype_memory as *const _)
80        }
81    }
82}
83
84impl FreeTypeLibraryHandle {
85    /// Get the shared FreeType library handle. This is protected by a mutex because according to
86    /// the FreeType documentation:
87    ///
88    /// > [Since 2.5.6] In multi-threaded applications it is easiest to use one FT_Library object per
89    /// > thread. In case this is too cumbersome, a single FT_Library object across threads is possible
90    /// > also, as long as a mutex lock is used around FT_New_Face and FT_Done_Face.
91    ///
92    /// See <https://freetype.org/freetype2/docs/reference/ft2-library_setup.html>.
93    pub(crate) fn get() -> &'static ReentrantMutex<FreeTypeLibraryHandle> {
94        FREETYPE_LIBRARY_HANDLE.get_or_init(|| {
95            let freetype_memory = Box::into_raw(Box::new(FT_MemoryRec {
96                user: ptr::null_mut(),
97                alloc: ft_alloc,
98                free: ft_free,
99                realloc: ft_realloc,
100            }));
101            unsafe {
102                let mut freetype_library: FT_Library = ptr::null_mut();
103                let result = FT_New_Library(freetype_memory, &mut freetype_library);
104                if 0 != result {
105                    panic!("Unable to initialize FreeType library");
106                }
107                FT_Add_Default_Modules(freetype_library);
108                ReentrantMutex::new(FreeTypeLibraryHandle {
109                    freetype_library,
110                    freetype_memory,
111                })
112            }
113        })
114    }
115}