azul_core/
app_resources.rs

1#[cfg(not(feature = "std"))]
2use alloc::string::ToString;
3use alloc::{boxed::Box, collections::btree_map::BTreeMap, string::String, vec::Vec};
4use core::{
5    fmt,
6    hash::{Hash, Hasher},
7    sync::atomic::{AtomicU32, AtomicUsize, Ordering as AtomicOrdering},
8};
9
10pub use azul_css::FontMetrics;
11use azul_css::{
12    AzString, ColorU, F32Vec, FloatValue, FontRef, LayoutRect, LayoutSize, OptionI32,
13    StyleFontFamily, StyleFontFamilyVec, StyleFontSize, U16Vec, U32Vec, U8Vec,
14};
15use rust_fontconfig::FcFontCache;
16
17use crate::{
18    callbacks::{
19        DocumentId, DomNodeId, InlineText, RefAny, RenderImageCallback, RenderImageCallbackType,
20        UpdateImageType,
21    },
22    display_list::{GlStoreImageFn, GlyphInstance, RenderCallbacks},
23    dom::NodeType,
24    gl::{OptionGlContextPtr, Texture},
25    id_tree::NodeId,
26    styled_dom::{
27        DomId, NodeHierarchyItemId, StyleFontFamiliesHash, StyleFontFamilyHash, StyledDom,
28    },
29    task::ExternalSystemCallbacks,
30    ui_solver::{InlineTextLayout, InlineTextLine, LayoutResult, ResolvedTextLayoutOptions},
31    window::{LogicalPosition, LogicalRect, LogicalSize, OptionChar},
32    FastBTreeSet, FastHashMap,
33};
34
35#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
36#[repr(C)]
37pub struct DpiScaleFactor {
38    pub inner: FloatValue,
39}
40
41/// Configuration for optional features, such as whether to enable logging or panic hooks
42#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
43#[repr(C)]
44pub struct AppConfig {
45    /// Which layout model to use - used for versioning changes in the layout
46    /// solver so that upgrading azul won't break existing apps
47    pub layout_solver: LayoutSolverVersion,
48    /// If enabled, logs error and info messages.
49    ///
50    /// Default is `LevelFilter::Error` to log all errors by default
51    pub log_level: AppLogLevel,
52    /// If the app crashes / panics, a window with a message box pops up.
53    /// Setting this to `false` disables the popup box.
54    pub enable_visual_panic_hook: bool,
55    /// If this is set to `true` (the default), a backtrace + error information
56    /// gets logged to stdout and the logging file (only if logging is enabled).
57    pub enable_logging_on_panic: bool,
58    /// (STUB) Whether keyboard navigation should be enabled (default: true).
59    /// Currently not implemented.
60    pub enable_tab_navigation: bool,
61    /// External callbacks to create a thread or get the curent time
62    pub system_callbacks: ExternalSystemCallbacks,
63}
64
65impl AppConfig {
66    pub fn new(layout_solver: LayoutSolverVersion) -> Self {
67        Self {
68            layout_solver,
69            log_level: AppLogLevel::Error,
70            enable_visual_panic_hook: true,
71            enable_logging_on_panic: true,
72            enable_tab_navigation: true,
73            system_callbacks: ExternalSystemCallbacks::rust_internal(),
74        }
75    }
76}
77
78#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
79#[repr(C)]
80pub enum LayoutSolverVersion {
81    /// Current default layout model
82    Default,
83}
84
85#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
86#[repr(C)]
87pub enum AppLogLevel {
88    Off,
89    Error,
90    Warn,
91    Info,
92    Debug,
93    Trace,
94}
95
96pub type WordIndex = usize;
97pub type GlyphIndex = usize;
98pub type LineLength = f32;
99pub type IndexOfLineBreak = usize;
100pub type RemainingSpaceToRight = f32;
101pub type LineBreaks = Vec<(GlyphIndex, RemainingSpaceToRight)>;
102
103#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
104pub struct PrimitiveFlags {
105    /// The CSS backface-visibility property (yes, it can be really granular)
106    pub is_backface_visible: bool,
107    /// If set, this primitive represents a scroll bar container
108    pub is_scrollbar_container: bool,
109    /// If set, this primitive represents a scroll bar thumb
110    pub is_scrollbar_thumb: bool,
111    /// This is used as a performance hint - this primitive may be promoted to a native
112    /// compositor surface under certain (implementation specific) conditions. This
113    /// is typically used for large videos, and canvas elements.
114    pub prefer_compositor_surface: bool,
115    /// If set, this primitive can be passed directly to the compositor via its
116    /// ExternalImageId, and the compositor will use the native image directly.
117    /// Used as a further extension on top of PREFER_COMPOSITOR_SURFACE.
118    pub supports_external_compositor_surface: bool,
119}
120
121/// Metadata (but not storage) describing an image In WebRender.
122#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
123#[repr(C)]
124pub struct ImageDescriptor {
125    /// Format of the image data.
126    pub format: RawImageFormat,
127    /// Width and height of the image data, in pixels.
128    pub width: usize,
129    pub height: usize,
130    /// The number of bytes from the start of one row to the next. If non-None,
131    /// `compute_stride` will return this value, otherwise it returns
132    /// `width * bpp`. Different source of images have different alignment
133    /// constraints for rows, so the stride isn't always equal to width * bpp.
134    pub stride: OptionI32,
135    /// Offset in bytes of the first pixel of this image in its backing buffer.
136    /// This is used for tiling, wherein WebRender extracts chunks of input images
137    /// in order to cache, manipulate, and render them individually. This offset
138    /// tells the texture upload machinery where to find the bytes to upload for
139    /// this tile. Non-tiled images generally set this to zero.
140    pub offset: i32,
141    /// Various bool flags related to this descriptor.
142    pub flags: ImageDescriptorFlags,
143}
144
145/// Various flags that are part of an image descriptor.
146#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
147#[repr(C)]
148pub struct ImageDescriptorFlags {
149    /// Whether this image is opaque, or has an alpha channel. Avoiding blending
150    /// for opaque surfaces is an important optimization.
151    pub is_opaque: bool,
152    /// Whether to allow the driver to automatically generate mipmaps. If images
153    /// are already downscaled appropriately, mipmap generation can be wasted
154    /// work, and cause performance problems on some cards/drivers.
155    ///
156    /// See https://github.com/servo/webrender/pull/2555/
157    pub allow_mipmaps: bool,
158}
159
160#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
161pub struct IdNamespace(pub u32);
162
163impl ::core::fmt::Display for IdNamespace {
164    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
165        write!(f, "IdNamespace({})", self.0)
166    }
167}
168
169impl ::core::fmt::Debug for IdNamespace {
170    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
171        write!(f, "{}", self)
172    }
173}
174
175#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
176#[repr(C)]
177pub enum RawImageFormat {
178    R8,
179    RG8,
180    RGB8,
181    RGBA8,
182    R16,
183    RG16,
184    RGB16,
185    RGBA16,
186    BGR8,
187    BGRA8,
188    RGBF32,
189    RGBAF32,
190}
191
192static IMAGE_KEY: AtomicU32 = AtomicU32::new(1); // NOTE: starts at 1 (0 = DUMMY)
193static FONT_KEY: AtomicU32 = AtomicU32::new(0);
194static FONT_INSTANCE_KEY: AtomicU32 = AtomicU32::new(0);
195
196#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
197pub struct ImageKey {
198    pub namespace: IdNamespace,
199    pub key: u32,
200}
201
202impl ImageKey {
203    pub const DUMMY: Self = Self {
204        namespace: IdNamespace(0),
205        key: 0,
206    };
207
208    pub fn unique(render_api_namespace: IdNamespace) -> Self {
209        Self {
210            namespace: render_api_namespace,
211            key: IMAGE_KEY.fetch_add(1, AtomicOrdering::SeqCst),
212        }
213    }
214}
215
216#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
217pub struct FontKey {
218    pub namespace: IdNamespace,
219    pub key: u32,
220}
221
222impl FontKey {
223    pub fn unique(render_api_namespace: IdNamespace) -> Self {
224        Self {
225            namespace: render_api_namespace,
226            key: FONT_KEY.fetch_add(1, AtomicOrdering::SeqCst),
227        }
228    }
229}
230
231#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
232pub struct FontInstanceKey {
233    pub namespace: IdNamespace,
234    pub key: u32,
235}
236
237impl FontInstanceKey {
238    pub fn unique(render_api_namespace: IdNamespace) -> Self {
239        Self {
240            namespace: render_api_namespace,
241            key: FONT_INSTANCE_KEY.fetch_add(1, AtomicOrdering::SeqCst),
242        }
243    }
244}
245
246#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
247#[repr(C)]
248pub struct ImageCallback {
249    pub data: RefAny,
250    pub callback: RenderImageCallback,
251}
252
253// NOTE: This type should NOT be exposed in the API!
254// The only public functions are the constructors
255#[derive(Debug)]
256pub enum DecodedImage {
257    /// Image that has a reserved key, but no data, i.e it is not yet rendered
258    /// or there was an error during rendering
259    NullImage {
260        width: usize,
261        height: usize,
262        format: RawImageFormat,
263        /// Sometimes images need to be tagged with extra data
264        tag: Vec<u8>,
265    },
266    // OpenGl texture
267    Gl(Texture),
268    // Image backed by CPU-rendered pixels
269    Raw((ImageDescriptor, ImageData)),
270    // Same as `Texture`, but rendered AFTER the layout has been done
271    Callback(ImageCallback),
272    // YUVImage(...)
273    // VulkanSurface(...)
274    // MetalSurface(...),
275    // DirectXSurface(...)
276}
277
278#[derive(Debug)]
279#[repr(C)]
280pub struct ImageRef {
281    /// Shared pointer to an opaque implementation of the decoded image
282    pub data: *const DecodedImage,
283    /// How many copies does this image have (if 0, the font data will be deleted on drop)
284    pub copies: *const AtomicUsize,
285    pub run_destructor: bool,
286}
287
288#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Hash, Ord, Eq)]
289pub struct ImageRefHash(pub usize);
290
291impl_option!(
292    ImageRef,
293    OptionImageRef,
294    copy = false,
295    [Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash]
296);
297
298impl ImageRef {
299    /// If *copies = 1, returns the internal image data
300    pub fn into_inner(self) -> Option<DecodedImage> {
301        unsafe {
302            if self.copies.as_ref().map(|m| m.load(AtomicOrdering::SeqCst)) == Some(1) {
303                let data = Box::from_raw(self.data as *mut DecodedImage);
304                let _ = Box::from_raw(self.copies as *mut AtomicUsize);
305                core::mem::forget(self); // do not run the destructor
306                Some(*data)
307            } else {
308                None
309            }
310        }
311    }
312
313    pub fn get_data<'a>(&'a self) -> &'a DecodedImage {
314        unsafe { &*self.data }
315    }
316
317    pub fn get_image_callback<'a>(&'a self) -> Option<&'a ImageCallback> {
318        if unsafe { self.copies.as_ref().map(|m| m.load(AtomicOrdering::SeqCst)) != Some(1) } {
319            return None; // not safe
320        }
321
322        match unsafe { &*self.data } {
323            DecodedImage::Callback(gl_texture_callback) => Some(gl_texture_callback),
324            _ => None,
325        }
326    }
327
328    pub fn get_image_callback_mut<'a>(&'a mut self) -> Option<&'a mut ImageCallback> {
329        if unsafe { self.copies.as_ref().map(|m| m.load(AtomicOrdering::SeqCst)) != Some(1) } {
330            return None; // not safe
331        }
332
333        match unsafe { &mut *(self.data as *mut DecodedImage) } {
334            DecodedImage::Callback(gl_texture_callback) => Some(gl_texture_callback),
335            _ => None,
336        }
337    }
338
339    /// In difference to the default shallow copy, creates a new image ref
340    pub fn deep_copy(&self) -> Self {
341        let new_data = match self.get_data() {
342            DecodedImage::NullImage {
343                width,
344                height,
345                format,
346                tag,
347            } => DecodedImage::NullImage {
348                width: *width,
349                height: *height,
350                format: *format,
351                tag: tag.clone(),
352            },
353            // NOTE: textures cannot be deep-copied yet (since the OpenGL calls for that are missing
354            // from the trait), so calling clone() on a GL texture will result in an
355            // empty image
356            DecodedImage::Gl(tex) => DecodedImage::NullImage {
357                width: tex.size.width as usize,
358                height: tex.size.height as usize,
359                format: tex.format,
360                tag: Vec::new(),
361            },
362            // WARNING: the data may still be a U8Vec<'static> - the data may still not be
363            // actually cloned. The data only gets cloned on a write operation
364            DecodedImage::Raw((descriptor, data)) => {
365                DecodedImage::Raw((descriptor.clone(), data.clone()))
366            }
367            DecodedImage::Callback(cb) => DecodedImage::Callback(cb.clone()),
368        };
369
370        Self::new(new_data)
371    }
372
373    pub fn is_null_image(&self) -> bool {
374        match self.get_data() {
375            DecodedImage::NullImage { .. } => true,
376            _ => false,
377        }
378    }
379
380    pub fn is_gl_texture(&self) -> bool {
381        match self.get_data() {
382            DecodedImage::Gl(_) => true,
383            _ => false,
384        }
385    }
386
387    pub fn is_raw_image(&self) -> bool {
388        match self.get_data() {
389            DecodedImage::Raw((_, _)) => true,
390            _ => false,
391        }
392    }
393
394    pub fn is_callback(&self) -> bool {
395        match self.get_data() {
396            DecodedImage::Callback(_) => true,
397            _ => false,
398        }
399    }
400
401    // OptionRawImage
402    pub fn get_rawimage(&self) -> Option<RawImage> {
403        match self.get_data() {
404            DecodedImage::Raw((image_descriptor, image_data)) => Some(RawImage {
405                pixels: match image_data {
406                    ImageData::Raw(u8_bytes) => RawImageData::U8(u8_bytes.clone()),
407                    ImageData::External(_) => return None,
408                },
409                width: image_descriptor.width,
410                height: image_descriptor.height,
411                premultiplied_alpha: true,
412                data_format: image_descriptor.format,
413                tag: Vec::new().into(),
414            }),
415            _ => None,
416        }
417    }
418
419    /// NOTE: returns (0, 0) for a Callback
420    pub fn get_size(&self) -> LogicalSize {
421        match self.get_data() {
422            DecodedImage::NullImage { width, height, .. } => {
423                LogicalSize::new(*width as f32, *height as f32)
424            }
425            DecodedImage::Gl(tex) => {
426                LogicalSize::new(tex.size.width as f32, tex.size.height as f32)
427            }
428            DecodedImage::Raw((image_descriptor, _)) => LogicalSize::new(
429                image_descriptor.width as f32,
430                image_descriptor.height as f32,
431            ),
432            DecodedImage::Callback(_) => LogicalSize::new(0.0, 0.0),
433        }
434    }
435
436    pub fn get_hash(&self) -> ImageRefHash {
437        ImageRefHash(self.data as usize)
438    }
439
440    pub fn null_image(width: usize, height: usize, format: RawImageFormat, tag: Vec<u8>) -> Self {
441        Self::new(DecodedImage::NullImage {
442            width,
443            height,
444            format,
445            tag,
446        })
447    }
448
449    pub fn callback(gl_callback: RenderImageCallbackType, data: RefAny) -> Self {
450        Self::new(DecodedImage::Callback(ImageCallback {
451            callback: RenderImageCallback { cb: gl_callback },
452            data,
453        }))
454    }
455
456    pub fn new_rawimage(image_data: RawImage) -> Option<Self> {
457        let (image_data, image_descriptor) = image_data.into_loaded_image_source()?;
458        Some(Self::new(DecodedImage::Raw((image_descriptor, image_data))))
459    }
460
461    pub fn new_gltexture(texture: Texture) -> Self {
462        Self::new(DecodedImage::Gl(texture))
463    }
464
465    fn new(data: DecodedImage) -> Self {
466        Self {
467            data: Box::into_raw(Box::new(data)),
468            copies: Box::into_raw(Box::new(AtomicUsize::new(1))),
469            run_destructor: true,
470        }
471    }
472
473    // pub fn new_vulkan(...) -> Self
474}
475
476unsafe impl Send for ImageRef {}
477unsafe impl Sync for ImageRef {}
478
479impl PartialEq for ImageRef {
480    fn eq(&self, rhs: &Self) -> bool {
481        self.data as usize == rhs.data as usize
482    }
483}
484
485impl PartialOrd for ImageRef {
486    fn partial_cmp(&self, other: &Self) -> Option<::core::cmp::Ordering> {
487        Some((self.data as usize).cmp(&(other.data as usize)))
488    }
489}
490
491impl Ord for ImageRef {
492    fn cmp(&self, other: &Self) -> core::cmp::Ordering {
493        let self_data = self.data as usize;
494        let other_data = other.data as usize;
495        self_data.cmp(&other_data)
496    }
497}
498
499impl Eq for ImageRef {}
500
501impl Hash for ImageRef {
502    fn hash<H>(&self, state: &mut H)
503    where
504        H: Hasher,
505    {
506        let self_data = self.data as usize;
507        self_data.hash(state)
508    }
509}
510
511impl Clone for ImageRef {
512    fn clone(&self) -> Self {
513        unsafe {
514            self.copies
515                .as_ref()
516                .map(|m| m.fetch_add(1, AtomicOrdering::SeqCst));
517        }
518        Self {
519            data: self.data,     // copy the pointer
520            copies: self.copies, // copy the pointer
521            run_destructor: true,
522        }
523    }
524}
525
526impl Drop for ImageRef {
527    fn drop(&mut self) {
528        self.run_destructor = false;
529        unsafe {
530            let copies = unsafe { (*self.copies).fetch_sub(1, AtomicOrdering::SeqCst) };
531            if copies == 1 {
532                let _ = Box::from_raw(self.data as *mut DecodedImage);
533                let _ = Box::from_raw(self.copies as *mut AtomicUsize);
534            }
535        }
536    }
537}
538
539pub fn font_ref_get_hash(fr: &FontRef) -> u64 {
540    use crate::css::GetHash;
541    fr.get_hash()
542}
543
544/// Stores the resources for the application, souch as fonts, images and cached
545/// texts, also clipboard strings
546///
547/// Images and fonts can be references across window contexts (not yet tested,
548/// but should work).
549#[derive(Debug)]
550pub struct ImageCache {
551    /// The AzString is the string used in the CSS, i.e. url("my_image") = "my_image" -> ImageId(4)
552    ///
553    /// NOTE: This is the only map that is modifiable by the user and that has to be manually
554    /// managed all other maps are library-internal only and automatically delete their
555    /// resources once they aren't needed anymore
556    pub image_id_map: FastHashMap<AzString, ImageRef>,
557}
558
559impl Default for ImageCache {
560    fn default() -> Self {
561        Self {
562            image_id_map: FastHashMap::default(),
563        }
564    }
565}
566
567impl ImageCache {
568    pub fn new() -> Self {
569        Self::default()
570    }
571
572    // -- ImageId cache
573
574    pub fn add_css_image_id(&mut self, css_id: AzString, image: ImageRef) {
575        self.image_id_map.insert(css_id, image);
576    }
577
578    pub fn get_css_image_id(&self, css_id: &AzString) -> Option<&ImageRef> {
579        self.image_id_map.get(css_id)
580    }
581
582    pub fn delete_css_image_id(&mut self, css_id: &AzString) {
583        self.image_id_map.remove(css_id);
584    }
585}
586
587/// What type of image is this?
588#[derive(Debug, Copy, Clone, PartialEq)]
589pub enum ImageType {
590    /// CSS background-image
591    Background,
592    /// DOM node content
593    Content,
594    /// DOM node clip-mask
595    ClipMask,
596}
597
598#[derive(Debug, Copy, Clone, PartialEq)]
599pub struct ResolvedImage {
600    pub key: ImageKey,
601    pub descriptor: ImageDescriptor,
602}
603
604/// Renderer resources that manage font, image and font instance keys.
605/// RendererResources are local to each renderer / window, since the
606/// keys are not shared across renderers
607///
608/// The resources are automatically managed, meaning that they each new frame
609/// (signified by start_frame_gc and end_frame_gc)
610pub struct RendererResources {
611    /// All image keys currently active in the RenderApi
612    currently_registered_images: FastHashMap<ImageRefHash, ResolvedImage>,
613    /// All font keys currently active in the RenderApi
614    currently_registered_fonts:
615        FastHashMap<FontKey, (FontRef, FastHashMap<(Au, DpiScaleFactor), FontInstanceKey>)>,
616    /// Fonts registered on the last frame
617    ///
618    /// Fonts differ from images in that regard that we can't immediately
619    /// delete them on a new frame, instead we have to delete them on "current frame + 1"
620    /// This is because when the frame is being built, we do not know
621    /// whether the font will actually be successfully loaded
622    last_frame_registered_fonts:
623        FastHashMap<FontKey, FastHashMap<(Au, DpiScaleFactor), FontInstanceKey>>,
624    /// Map from the calculated families vec (["Arial", "Helvectia"])
625    /// to the final loaded font that could be loaded
626    /// (in this case "Arial" on Windows and "Helvetica" on Mac,
627    /// because the fonts are loaded in fallback-order)
628    font_families_map: FastHashMap<StyleFontFamiliesHash, StyleFontFamilyHash>,
629    /// Same as AzString -> ImageId, but for fonts, i.e. "Roboto" -> FontId(9)
630    font_id_map: FastHashMap<StyleFontFamilyHash, FontKey>,
631}
632
633impl fmt::Debug for RendererResources {
634    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
635        write!(
636            f,
637            "RendererResources {{
638                currently_registered_images: {:#?},
639                currently_registered_fonts: {:#?},
640                font_families_map: {:#?},
641                font_id_map: {:#?},
642            }}",
643            self.currently_registered_images.keys().collect::<Vec<_>>(),
644            self.currently_registered_fonts.keys().collect::<Vec<_>>(),
645            self.font_families_map.keys().collect::<Vec<_>>(),
646            self.font_id_map.keys().collect::<Vec<_>>(),
647        )
648    }
649}
650
651impl Default for RendererResources {
652    fn default() -> Self {
653        Self {
654            currently_registered_images: FastHashMap::default(),
655            currently_registered_fonts: FastHashMap::default(),
656            last_frame_registered_fonts: FastHashMap::default(),
657            font_families_map: FastHashMap::default(),
658            font_id_map: FastHashMap::default(),
659        }
660    }
661}
662
663impl RendererResources {
664    pub fn get_image(&self, hash: &ImageRefHash) -> Option<&ResolvedImage> {
665        self.currently_registered_images.get(hash)
666    }
667
668    pub fn get_font_family(
669        &self,
670        style_font_families_hash: &StyleFontFamiliesHash,
671    ) -> Option<&StyleFontFamilyHash> {
672        self.font_families_map.get(style_font_families_hash)
673    }
674
675    pub fn get_font_key(&self, style_font_family_hash: &StyleFontFamilyHash) -> Option<&FontKey> {
676        self.font_id_map.get(style_font_family_hash)
677    }
678
679    pub fn get_registered_font(
680        &self,
681        font_key: &FontKey,
682    ) -> Option<&(FontRef, FastHashMap<(Au, DpiScaleFactor), FontInstanceKey>)> {
683        self.currently_registered_fonts.get(font_key)
684    }
685
686    pub fn update_image(&mut self, image_ref_hash: &ImageRefHash, descriptor: ImageDescriptor) {
687        if let Some(s) = self.currently_registered_images.get_mut(image_ref_hash) {
688            s.descriptor = descriptor; // key stays the same, only descriptor changes
689        }
690    }
691
692    /// Updates the internal cache, adds `ResourceUpdate::Remove()`
693    /// to the `all_resource_updates`
694    ///
695    /// This function will query all current images and fonts submitted
696    /// into the cache and set them for the next frame so that unused
697    /// resources will be cleaned up.
698    ///
699    /// This function should be called after the StyledDom has been
700    /// exchanged for the next frame and AFTER all OpenGL textures
701    /// and image callbacks have been resolved.
702    pub fn do_gc(
703        &mut self,
704        all_resource_updates: &mut Vec<ResourceUpdate>,
705        css_image_cache: &ImageCache,
706        // layout calculated for the NEXT frame
707        new_layout_results: &[LayoutResult],
708        // initialized texture cache of the NEXT frame
709        gl_texture_cache: &GlTextureCache,
710    ) {
711        use alloc::collections::btree_set::BTreeSet;
712
713        // Get all fonts / images that are in the DOM for the next frame
714        let mut next_frame_image_keys = BTreeSet::new();
715
716        for layout_result in new_layout_results {
717            for image_key in layout_result
718                .styled_dom
719                .scan_for_image_keys(css_image_cache)
720            {
721                let hash = image_key.get_hash();
722                next_frame_image_keys.insert(hash);
723            }
724        }
725
726        for ((_dom_id, _node_id, _callback_imageref_hash), image_ref_hash) in
727            gl_texture_cache.hashes.iter()
728        {
729            next_frame_image_keys.insert(*image_ref_hash);
730        }
731
732        // If the current frame contains a font key but the next frame doesn't, delete the font key
733        let mut delete_font_resources = Vec::new();
734        for (font_key, font_instances) in self.last_frame_registered_fonts.iter() {
735            delete_font_resources.extend(
736                font_instances
737                    .iter()
738                    .filter(|(au, _)| {
739                        !(self
740                            .currently_registered_fonts
741                            .get(font_key)
742                            .map(|f| f.1.contains_key(au))
743                            .unwrap_or(false))
744                    })
745                    .map(|(au, font_instance_key)| {
746                        (
747                            font_key.clone(),
748                            DeleteFontMsg::Instance(*font_instance_key, *au),
749                        )
750                    }),
751            );
752            // Delete the font and all instances if there are no more instances of the font
753            // NOTE: deletion is in reverse order - instances are deleted first, then the font is
754            // deleted
755            if !self.currently_registered_fonts.contains_key(font_key) || font_instances.is_empty()
756            {
757                delete_font_resources
758                    .push((font_key.clone(), DeleteFontMsg::Font(font_key.clone())));
759            }
760        }
761
762        // If the current frame contains an image, but the next frame does not, delete it
763        let delete_image_resources = self
764            .currently_registered_images
765            .iter()
766            .filter(|(image_ref_hash, _)| !next_frame_image_keys.contains(image_ref_hash))
767            .map(|(image_ref_hash, resolved_image)| {
768                (
769                    image_ref_hash.clone(),
770                    DeleteImageMsg(resolved_image.key.clone()),
771                )
772            })
773            .collect::<Vec<_>>();
774
775        for (image_ref_hash_to_delete, _) in delete_image_resources.iter() {
776            self.currently_registered_images
777                .remove(image_ref_hash_to_delete);
778        }
779
780        all_resource_updates.extend(
781            delete_font_resources
782                .iter()
783                .map(|(_, f)| f.into_resource_update()),
784        );
785        all_resource_updates.extend(
786            delete_image_resources
787                .iter()
788                .map(|(_, i)| i.into_resource_update()),
789        );
790
791        self.last_frame_registered_fonts = self
792            .currently_registered_fonts
793            .iter()
794            .map(|(fk, (_, fi))| (fk.clone(), fi.clone()))
795            .collect();
796
797        self.remove_font_families_with_zero_references();
798    }
799
800    // Delete all font family hashes that do not have a font key anymore
801    fn remove_font_families_with_zero_references(&mut self) {
802        let font_family_to_delete = self
803            .font_id_map
804            .iter()
805            .filter_map(|(font_family, font_key)| {
806                if !self.currently_registered_fonts.contains_key(font_key) {
807                    Some(font_family.clone())
808                } else {
809                    None
810                }
811            })
812            .collect::<Vec<_>>();
813
814        for f in font_family_to_delete {
815            self.font_id_map.remove(&f); // font key does not exist anymore
816        }
817
818        let font_families_to_delete = self
819            .font_families_map
820            .iter()
821            .filter_map(|(font_families, font_family)| {
822                if !self.font_id_map.contains_key(font_family) {
823                    Some(font_families.clone())
824                } else {
825                    None
826                }
827            })
828            .collect::<Vec<_>>();
829
830        for f in font_families_to_delete {
831            self.font_families_map.remove(&f); // font family does not exist anymore
832        }
833    }
834
835    // Re-invokes the RenderImageCallback on the given node (if there is any),
836    // updates the internal texture (without exchanging the hashes, so that
837    // the GC still works) and updates the internal texture cache.
838    #[must_use]
839    pub fn rerender_image_callback(
840        &mut self,
841        dom_id: DomId,
842        node_id: NodeId,
843        document_id: DocumentId,
844        epoch: Epoch,
845        id_namespace: IdNamespace,
846        gl_context: &OptionGlContextPtr,
847        image_cache: &ImageCache,
848        system_fonts: &FcFontCache,
849        hidpi_factor: f32,
850        callbacks: &RenderCallbacks,
851        layout_results: &mut [LayoutResult],
852        gl_texture_cache: &mut GlTextureCache,
853    ) -> Option<UpdateImageResult> {
854        use crate::{
855            callbacks::{HidpiAdjustedBounds, RenderImageCallbackInfo},
856            gl::{insert_into_active_gl_textures, remove_single_texture_from_active_gl_textures},
857        };
858
859        let mut layout_result = layout_results.get_mut(dom_id.inner)?;
860        let mut node_data_vec = layout_result.styled_dom.node_data.as_container_mut();
861        let mut node_data = node_data_vec.get_mut(node_id)?;
862        let (mut render_image_callback, render_image_callback_hash) =
863            node_data.get_render_image_callback_node()?;
864
865        let callback_domnode_id = DomNodeId {
866            dom: dom_id,
867            node: NodeHierarchyItemId::from_crate_internal(Some(node_id)),
868        };
869
870        let rect_size = layout_result.rects.as_ref().get(node_id)?.size.clone();
871
872        let size = LayoutSize::new(
873            rect_size.width.round() as isize,
874            rect_size.height.round() as isize,
875        );
876
877        // NOTE: all of these extra arguments are necessary so that the callback
878        // has access to information about the text layout, which is used to render
879        // the "text selection" highlight (the text selection is nothing but an image
880        // or an image mask).
881        let mut gl_callback_info = RenderImageCallbackInfo::new(
882            /* gl_context: */ gl_context,
883            /* image_cache: */ image_cache,
884            /* system_fonts: */ system_fonts,
885            /* node_hierarchy */ &layout_result.styled_dom.node_hierarchy,
886            /* words_cache */ &layout_result.words_cache,
887            /* shaped_words_cache */ &layout_result.shaped_words_cache,
888            /* positioned_words_cache */ &layout_result.positioned_words_cache,
889            /* positioned_rects */ &layout_result.rects,
890            /* bounds: */ HidpiAdjustedBounds::from_bounds(size, hidpi_factor),
891            /* hit_dom_node */ callback_domnode_id,
892        );
893
894        let new_imageref = (render_image_callback.callback.cb)(
895            &mut render_image_callback.data,
896            &mut gl_callback_info,
897        );
898
899        // remove old imageref from GlTextureCache and active textures
900        let existing_image_key = gl_texture_cache
901            .solved_textures
902            .get(&dom_id)
903            .and_then(|m| m.get(&node_id))
904            .map(|k| k.0.clone())
905            .or(self
906                .currently_registered_images
907                .get(&render_image_callback_hash)
908                .map(|i| i.key.clone()))?;
909
910        if let Some(dom_map) = gl_texture_cache.solved_textures.get_mut(&dom_id) {
911            if let Some((image_key, image_descriptor, external_image_id)) = dom_map.remove(&node_id)
912            {
913                remove_single_texture_from_active_gl_textures(
914                    &document_id,
915                    &epoch,
916                    &external_image_id,
917                );
918            }
919        }
920
921        match new_imageref.into_inner()? {
922            DecodedImage::Gl(new_tex) => {
923                // for GL textures, generate a new external image ID
924                let new_descriptor = new_tex.get_descriptor();
925                let new_external_id = insert_into_active_gl_textures(document_id, epoch, new_tex);
926                let new_image_data = ImageData::External(ExternalImageData {
927                    id: new_external_id,
928                    channel_index: 0,
929                    image_type: ExternalImageType::TextureHandle(ImageBufferKind::Texture2D),
930                });
931
932                gl_texture_cache
933                    .solved_textures
934                    .entry(dom_id)
935                    .or_insert_with(|| BTreeMap::new())
936                    .insert(
937                        node_id,
938                        (existing_image_key, new_descriptor.clone(), new_external_id),
939                    );
940
941                Some(UpdateImageResult {
942                    key_to_update: existing_image_key,
943                    new_descriptor,
944                    new_image_data,
945                })
946            }
947            DecodedImage::Raw((descriptor, data)) => {
948                if let Some(existing_image) = self
949                    .currently_registered_images
950                    .get_mut(&render_image_callback_hash)
951                {
952                    existing_image.descriptor = descriptor.clone(); // update descriptor, key stays the same
953                    Some(UpdateImageResult {
954                        key_to_update: existing_image_key,
955                        new_descriptor: descriptor,
956                        new_image_data: data,
957                    })
958                } else {
959                    None
960                }
961            }
962            _ => None,
963        }
964    }
965
966    // Updates images and image mask resources
967    // NOTE: assumes the GL context is made current
968    #[must_use]
969    pub fn update_image_resources(
970        &mut self,
971        layout_results: &[LayoutResult],
972        images_to_update: BTreeMap<DomId, BTreeMap<NodeId, (ImageRef, UpdateImageType)>>,
973        image_masks_to_update: BTreeMap<DomId, BTreeMap<NodeId, ImageMask>>,
974        callbacks: &RenderCallbacks,
975        image_cache: &ImageCache,
976        gl_texture_cache: &mut GlTextureCache,
977        document_id: DocumentId,
978        epoch: Epoch,
979    ) -> Vec<UpdateImageResult> {
980        use crate::dom::NodeType;
981
982        let mut updated_images = Vec::new();
983        let mut renderer_resources: &mut RendererResources = self;
984
985        // update images
986        for (dom_id, image_map) in images_to_update {
987            let layout_result = match layout_results.get(dom_id.inner) {
988                Some(s) => s,
989                None => continue,
990            };
991
992            for (node_id, (image_ref, image_type)) in image_map {
993                // get the existing key + extents of the image
994                let existing_image_ref_hash = match image_type {
995                    UpdateImageType::Content => {
996                        match layout_result
997                            .styled_dom
998                            .node_data
999                            .as_container()
1000                            .get(node_id)
1001                            .map(|n| n.get_node_type())
1002                        {
1003                            Some(NodeType::Image(image_ref)) => image_ref.get_hash(),
1004                            _ => continue,
1005                        }
1006                    }
1007                    UpdateImageType::Background => {
1008                        let node_data = layout_result.styled_dom.node_data.as_container();
1009                        let node_data = match node_data.get(node_id) {
1010                            Some(s) => s,
1011                            None => continue,
1012                        };
1013
1014                        let styled_node_states =
1015                            layout_result.styled_dom.styled_nodes.as_container();
1016                        let node_state = match styled_node_states.get(node_id) {
1017                            Some(s) => s.state.clone(),
1018                            None => continue,
1019                        };
1020
1021                        let default = azul_css::StyleBackgroundContentVec::from_const_slice(&[]);
1022
1023                        // TODO: only updates the first image background - usually not a problem
1024                        let bg_hash = layout_result
1025                            .styled_dom
1026                            .css_property_cache
1027                            .ptr
1028                            .get_background_content(node_data, &node_id, &node_state)
1029                            .and_then(|bg| {
1030                                bg.get_property()
1031                                    .unwrap_or(&default)
1032                                    .as_ref()
1033                                    .iter()
1034                                    .find_map(|b| match b {
1035                                        azul_css::StyleBackgroundContent::Image(id) => {
1036                                            let image_ref = image_cache.get_css_image_id(id)?;
1037                                            Some(image_ref.get_hash())
1038                                        }
1039                                        _ => None,
1040                                    })
1041                            });
1042
1043                        match bg_hash {
1044                            Some(h) => h,
1045                            None => continue,
1046                        }
1047                    }
1048                };
1049
1050                let new_image_ref_hash = image_ref.get_hash();
1051
1052                let decoded_image = match image_ref.into_inner() {
1053                    Some(s) => s,
1054                    None => continue,
1055                };
1056
1057                // Try getting the existing image key either
1058                // from the textures or from the renderer resources
1059                let existing_key = gl_texture_cache
1060                    .solved_textures
1061                    .get(&dom_id)
1062                    .and_then(|map| map.get(&node_id))
1063                    .map(|val| val.0);
1064
1065                let existing_key = match existing_key {
1066                    Some(s) => Some(s),
1067                    None => renderer_resources
1068                        .get_image(&existing_image_ref_hash)
1069                        .map(|resolved_image| resolved_image.key),
1070                };
1071
1072                let key = match existing_key {
1073                    Some(s) => s,
1074                    None => continue, /* updating an image requires at
1075                                       * least one image to be present */
1076                };
1077
1078                let (descriptor, data) = match decoded_image {
1079                    DecodedImage::Gl(texture) => {
1080                        let descriptor = texture.get_descriptor();
1081                        let new_external_image_id = match gl_texture_cache.update_texture(
1082                            dom_id,
1083                            node_id,
1084                            document_id,
1085                            epoch,
1086                            texture,
1087                            callbacks,
1088                        ) {
1089                            Some(s) => s,
1090                            None => continue,
1091                        };
1092
1093                        let data = ImageData::External(ExternalImageData {
1094                            id: new_external_image_id,
1095                            channel_index: 0,
1096                            image_type: ExternalImageType::TextureHandle(
1097                                ImageBufferKind::Texture2D,
1098                            ),
1099                        });
1100
1101                        (descriptor, data)
1102                    }
1103                    DecodedImage::Raw((descriptor, data)) => {
1104                        // use the hash to get the existing image key
1105                        // TODO: may lead to problems when the same ImageRef is used more than once?
1106                        renderer_resources.update_image(&existing_image_ref_hash, descriptor);
1107                        (descriptor, data)
1108                    }
1109                    DecodedImage::NullImage { .. } => continue, // TODO: NULL image descriptor?
1110                    DecodedImage::Callback(callback) => {
1111                        // TODO: re-render image callbacks?
1112                        /*
1113                        let (key, descriptor) = match gl_texture_cache.solved_textures.get(&dom_id).and_then(|textures| textures.get(&node_id)) {
1114                            Some((k, d)) => (k, d),
1115                            None => continue,
1116                        };*/
1117
1118                        continue;
1119                    }
1120                };
1121
1122                // update the image descriptor in the renderer resources
1123
1124                updated_images.push(UpdateImageResult {
1125                    key_to_update: key,
1126                    new_descriptor: descriptor,
1127                    new_image_data: data,
1128                });
1129            }
1130        }
1131
1132        // TODO: update image masks
1133        for (dom_id, image_mask_map) in image_masks_to_update {}
1134
1135        updated_images
1136    }
1137}
1138
1139// Result returned from rerender_image_callback() - should be used as:
1140//
1141// ```rust
1142// txn.update_image(
1143//     wr_translate_image_key(key),
1144//     wr_translate_image_descriptor(descriptor),
1145//     wr_translate_image_data(data),
1146//     &WrImageDirtyRect::All,
1147// );
1148// ```
1149#[derive(Debug, Clone)]
1150pub struct UpdateImageResult {
1151    pub key_to_update: ImageKey,
1152    pub new_descriptor: ImageDescriptor,
1153    pub new_image_data: ImageData,
1154}
1155
1156#[derive(Debug, Default)]
1157pub struct GlTextureCache {
1158    pub solved_textures:
1159        BTreeMap<DomId, BTreeMap<NodeId, (ImageKey, ImageDescriptor, ExternalImageId)>>,
1160    pub hashes: BTreeMap<(DomId, NodeId, ImageRefHash), ImageRefHash>,
1161}
1162
1163// necessary so the display list can be built in parallel
1164unsafe impl Send for GlTextureCache {}
1165
1166impl GlTextureCache {
1167    /// Initializes an empty cache
1168    pub fn empty() -> Self {
1169        Self {
1170            solved_textures: BTreeMap::new(),
1171            hashes: BTreeMap::new(),
1172        }
1173    }
1174
1175    /// Invokes all ImageCallbacks with the sizes given by the LayoutResult
1176    /// and adds them to the `RendererResources`.
1177    pub fn new(
1178        layout_results: &mut [LayoutResult],
1179        gl_context: &OptionGlContextPtr,
1180        id_namespace: IdNamespace,
1181        document_id: &DocumentId,
1182        epoch: Epoch,
1183        hidpi_factor: f32,
1184        image_cache: &ImageCache,
1185        system_fonts: &FcFontCache,
1186        callbacks: &RenderCallbacks,
1187        all_resource_updates: &mut Vec<ResourceUpdate>,
1188        renderer_resources: &mut RendererResources,
1189    ) -> Self {
1190        use gl_context_loader::gl;
1191
1192        use crate::{
1193            app_resources::{
1194                add_resources, AddImage, DecodedImage, ExternalImageData, ExternalImageType,
1195                ImageBufferKind, ImageData, ImageRef,
1196            },
1197            callbacks::{HidpiAdjustedBounds, RenderImageCallbackInfo},
1198            dom::NodeType,
1199        };
1200
1201        let mut solved_image_callbacks = BTreeMap::new();
1202
1203        // Now that the layout is done, render the OpenGL textures and add them to the RenderAPI
1204        for (dom_id, layout_result) in layout_results.iter_mut().enumerate() {
1205            for callback_node_id in layout_result.styled_dom.scan_for_gltexture_callbacks() {
1206                // Invoke OpenGL callback, render texture
1207                let rect_size = layout_result.rects.as_ref()[callback_node_id].size;
1208
1209                let callback_image = {
1210                    let callback_domnode_id = DomNodeId {
1211                        dom: DomId { inner: dom_id },
1212                        node: NodeHierarchyItemId::from_crate_internal(Some(callback_node_id)),
1213                    };
1214
1215                    let size = LayoutSize::new(
1216                        rect_size.width.round() as isize,
1217                        rect_size.height.round() as isize,
1218                    );
1219
1220                    // NOTE: all of these extra arguments are necessary so that the callback
1221                    // has access to information about the text layout, which is used to render
1222                    // the "text selection" highlight (the text selection is nothing but an image
1223                    // or an image mask).
1224                    let mut gl_callback_info = RenderImageCallbackInfo::new(
1225                        /* gl_context: */ &gl_context,
1226                        /* image_cache: */ image_cache,
1227                        /* system_fonts: */ system_fonts,
1228                        /* node_hierarchy */ &layout_result.styled_dom.node_hierarchy,
1229                        /* words_cache */ &layout_result.words_cache,
1230                        /* shaped_words_cache */ &layout_result.shaped_words_cache,
1231                        /* positioned_words_cache */ &layout_result.positioned_words_cache,
1232                        /* positioned_rects */ &layout_result.rects,
1233                        /* bounds: */ HidpiAdjustedBounds::from_bounds(size, hidpi_factor),
1234                        /* hit_dom_node */ callback_domnode_id,
1235                    );
1236
1237                    let callback_image: Option<(ImageRef, ImageRefHash)> = {
1238                        // get a MUTABLE reference to the RefAny inside of the DOM
1239                        let mut node_data_mut =
1240                            layout_result.styled_dom.node_data.as_container_mut();
1241                        match &mut node_data_mut[callback_node_id].node_type {
1242                            NodeType::Image(img) => {
1243                                let callback_imageref_hash = img.get_hash();
1244
1245                                img.get_image_callback_mut().map(|gl_texture_callback| {
1246                                    (
1247                                        (gl_texture_callback.callback.cb)(
1248                                            &mut gl_texture_callback.data,
1249                                            &mut gl_callback_info,
1250                                        ),
1251                                        callback_imageref_hash,
1252                                    )
1253                                })
1254                            }
1255                            _ => None,
1256                        }
1257                    };
1258
1259                    // Reset the framebuffer and SRGB color target to 0
1260                    if let Some(gl) = gl_context.as_ref() {
1261                        gl.bind_framebuffer(gl::FRAMEBUFFER, 0);
1262                        gl.disable(gl::FRAMEBUFFER_SRGB);
1263                        gl.disable(gl::MULTISAMPLE);
1264                    }
1265
1266                    callback_image
1267                };
1268
1269                if let Some((image_ref, callback_imageref_hash)) = callback_image {
1270                    solved_image_callbacks
1271                        .entry(layout_result.dom_id.clone())
1272                        .or_insert_with(|| BTreeMap::default())
1273                        .insert(callback_node_id, (callback_imageref_hash, image_ref));
1274                }
1275            }
1276        }
1277
1278        let mut image_resource_updates = Vec::new();
1279        let mut gl_texture_cache = Self::empty();
1280
1281        for (dom_id, image_refs) in solved_image_callbacks {
1282            for (node_id, (callback_imageref_hash, image_ref)) in image_refs {
1283                // callback_imageref_hash = the hash of the ImageRef::callback()
1284                // that is currently in the DOM
1285                //
1286                // image_ref_hash = the hash of the ImageRef::gl_texture() that was
1287                // returned by invoking the ImageRef::callback()
1288
1289                let image_ref_hash = image_ref.get_hash();
1290                let image_data = match image_ref.into_inner() {
1291                    Some(s) => s,
1292                    None => continue,
1293                };
1294
1295                let image_result = match image_data {
1296                    DecodedImage::Gl(texture) => {
1297                        let descriptor = texture.get_descriptor();
1298                        let key = ImageKey::unique(id_namespace);
1299                        let external_image_id = (callbacks.insert_into_active_gl_textures_fn)(
1300                            *document_id,
1301                            epoch,
1302                            texture,
1303                        );
1304
1305                        gl_texture_cache
1306                            .solved_textures
1307                            .entry(dom_id.clone())
1308                            .or_insert_with(|| BTreeMap::new())
1309                            .insert(node_id, (key, descriptor, external_image_id));
1310
1311                        gl_texture_cache
1312                            .hashes
1313                            .insert((dom_id, node_id, callback_imageref_hash), image_ref_hash);
1314
1315                        Some((
1316                            image_ref_hash,
1317                            AddImageMsg(AddImage {
1318                                key,
1319                                data: ImageData::External(ExternalImageData {
1320                                    id: external_image_id,
1321                                    channel_index: 0,
1322                                    image_type: ExternalImageType::TextureHandle(
1323                                        ImageBufferKind::Texture2D,
1324                                    ),
1325                                }),
1326                                descriptor,
1327                                tiling: None,
1328                            }),
1329                        ))
1330                    }
1331                    DecodedImage::Raw((descriptor, data)) => {
1332                        let key = ImageKey::unique(id_namespace);
1333                        Some((
1334                            image_ref_hash,
1335                            AddImageMsg(AddImage {
1336                                key,
1337                                data,
1338                                descriptor,
1339                                tiling: None,
1340                            }),
1341                        ))
1342                    }
1343                    DecodedImage::NullImage {
1344                        width: _,
1345                        height: _,
1346                        format: _,
1347                        tag: _,
1348                    } => None,
1349                    // Texture callbacks inside of texture callbacks are not rendered
1350                    DecodedImage::Callback(_) => None,
1351                };
1352
1353                if let Some((image_ref_hash, add_img_msg)) = image_result {
1354                    image_resource_updates.push((
1355                        callback_imageref_hash,
1356                        image_ref_hash,
1357                        add_img_msg,
1358                    ));
1359                }
1360            }
1361        }
1362
1363        // Add the new rendered images to the RenderApi
1364        add_gl_resources(
1365            renderer_resources,
1366            all_resource_updates,
1367            image_resource_updates,
1368        );
1369
1370        gl_texture_cache
1371    }
1372
1373    /// Updates a given texture
1374    pub fn update_texture(
1375        &mut self,
1376        dom_id: DomId,
1377        node_id: NodeId,
1378        document_id: DocumentId,
1379        epoch: Epoch,
1380        new_texture: Texture,
1381        callbacks: &RenderCallbacks,
1382    ) -> Option<ExternalImageId> {
1383        let new_descriptor = new_texture.get_descriptor();
1384        let di_map = self.solved_textures.get_mut(&dom_id)?;
1385        let i = di_map.get_mut(&node_id)?;
1386        i.1 = new_descriptor;
1387        let external_image_id =
1388            (callbacks.insert_into_active_gl_textures_fn)(document_id, epoch, new_texture);
1389        Some(external_image_id)
1390    }
1391}
1392
1393macro_rules! unique_id {
1394    ($struct_name:ident, $counter_name:ident) => {
1395        #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)]
1396        #[repr(C)]
1397        pub struct $struct_name {
1398            pub id: usize,
1399        }
1400
1401        impl $struct_name {
1402            pub fn unique() -> Self {
1403                Self {
1404                    id: $counter_name.fetch_add(1, AtomicOrdering::SeqCst),
1405                }
1406            }
1407        }
1408    };
1409}
1410
1411// NOTE: the property key is unique across transform, color and opacity properties
1412static PROPERTY_KEY_COUNTER: AtomicUsize = AtomicUsize::new(0);
1413unique_id!(TransformKey, PROPERTY_KEY_COUNTER);
1414unique_id!(ColorKey, PROPERTY_KEY_COUNTER);
1415unique_id!(OpacityKey, PROPERTY_KEY_COUNTER);
1416
1417static IMAGE_ID_COUNTER: AtomicUsize = AtomicUsize::new(0);
1418unique_id!(ImageId, IMAGE_ID_COUNTER);
1419static FONT_ID_COUNTER: AtomicUsize = AtomicUsize::new(0);
1420unique_id!(FontId, FONT_ID_COUNTER);
1421
1422#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
1423#[repr(C)]
1424pub struct ImageMask {
1425    pub image: ImageRef,
1426    pub rect: LogicalRect,
1427    pub repeat: bool,
1428}
1429
1430impl_option!(
1431    ImageMask,
1432    OptionImageMask,
1433    copy = false,
1434    [Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash]
1435);
1436
1437#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
1438pub enum ImmediateFontId {
1439    Resolved((StyleFontFamilyHash, FontKey)),
1440    Unresolved(StyleFontFamilyVec),
1441}
1442
1443#[derive(Debug, Clone, PartialEq, PartialOrd)]
1444#[repr(C, u8)]
1445pub enum RawImageData {
1446    // 8-bit image data
1447    U8(U8Vec),
1448    // 16-bit image data
1449    U16(U16Vec),
1450    // HDR image data
1451    F32(F32Vec),
1452}
1453
1454impl RawImageData {
1455    pub fn get_u8_vec_ref(&self) -> Option<&U8Vec> {
1456        match self {
1457            RawImageData::U8(v) => Some(v),
1458            _ => None,
1459        }
1460    }
1461
1462    pub fn get_u16_vec_ref(&self) -> Option<&U16Vec> {
1463        match self {
1464            RawImageData::U16(v) => Some(v),
1465            _ => None,
1466        }
1467    }
1468
1469    pub fn get_f32_vec_ref(&self) -> Option<&F32Vec> {
1470        match self {
1471            RawImageData::F32(v) => Some(v),
1472            _ => None,
1473        }
1474    }
1475
1476    fn get_u8_vec(self) -> Option<U8Vec> {
1477        match self {
1478            RawImageData::U8(v) => Some(v),
1479            _ => None,
1480        }
1481    }
1482
1483    fn get_u16_vec(self) -> Option<U16Vec> {
1484        match self {
1485            RawImageData::U16(v) => Some(v),
1486            _ => None,
1487        }
1488    }
1489}
1490
1491#[derive(Debug, Clone, PartialEq, PartialOrd)]
1492#[repr(C)]
1493pub struct RawImage {
1494    pub pixels: RawImageData,
1495    pub width: usize,
1496    pub height: usize,
1497    pub premultiplied_alpha: bool,
1498    pub data_format: RawImageFormat,
1499    pub tag: U8Vec,
1500}
1501
1502impl RawImage {
1503    /// Returns a null / empty image
1504    pub fn null_image() -> Self {
1505        Self {
1506            pixels: RawImageData::U8(Vec::new().into()),
1507            width: 0,
1508            height: 0,
1509            premultiplied_alpha: true,
1510            data_format: RawImageFormat::BGRA8,
1511            tag: Vec::new().into(),
1512        }
1513    }
1514
1515    /// Allocates a width * height, single-channel mask, used for drawing CPU image masks
1516    pub fn allocate_mask(size: LayoutSize) -> Self {
1517        Self {
1518            pixels: RawImageData::U8(
1519                vec![0; size.width.max(0) as usize * size.height.max(0) as usize].into(),
1520            ),
1521            width: size.width as usize,
1522            height: size.height as usize,
1523            premultiplied_alpha: true,
1524            data_format: RawImageFormat::R8,
1525            tag: Vec::new().into(),
1526        }
1527    }
1528
1529    /// Encodes a RawImage as BGRA8 bytes and premultiplies it if the alpha is not premultiplied
1530    ///
1531    /// Returns None if the width * height * BPP does not match
1532    ///
1533    /// TODO: autovectorization fails spectacularly, need to manually optimize!
1534    pub fn into_loaded_image_source(self) -> Option<(ImageData, ImageDescriptor)> {
1535        // From webrender/wrench
1536        // These are slow. Gecko's gfx/2d/Swizzle.cpp has better versions
1537        #[inline(always)]
1538        fn premultiply_alpha(array: &mut [u8]) {
1539            if array.len() != 4 {
1540                return;
1541            }
1542            let a = u32::from(array[3]);
1543            array[0] = (((array[0] as u32 * a) + 128) / 255) as u8;
1544            array[1] = (((array[1] as u32 * a) + 128) / 255) as u8;
1545            array[2] = (((array[2] as u32 * a) + 128) / 255) as u8;
1546        }
1547
1548        #[inline(always)]
1549        fn normalize_u16(i: u16) -> u8 {
1550            ((core::u16::MAX as f32 / i as f32) * core::u8::MAX as f32) as u8
1551        }
1552
1553        let RawImage {
1554            width,
1555            height,
1556            pixels,
1557            mut data_format,
1558            premultiplied_alpha,
1559            tag,
1560        } = self;
1561
1562        const FOUR_BPP: usize = 4;
1563        const TWO_CHANNELS: usize = 2;
1564        const THREE_CHANNELS: usize = 3;
1565        const FOUR_CHANNELS: usize = 4;
1566
1567        let mut is_opaque = true;
1568
1569        let expected_len = width * height;
1570
1571        let bytes: U8Vec = match data_format {
1572            RawImageFormat::R8 => {
1573                // just return the vec
1574                let pixels = pixels.get_u8_vec()?;
1575
1576                if pixels.len() != expected_len {
1577                    return None;
1578                }
1579
1580                let pixels_ref = pixels.as_ref();
1581                let mut px = vec![0; pixels_ref.len() * 4];
1582                for (i, r) in pixels_ref.iter().enumerate() {
1583                    px[i * 4 + 0] = *r;
1584                    px[i * 4 + 1] = *r;
1585                    px[i * 4 + 2] = *r;
1586                    px[i * 4 + 3] = 0xff;
1587                }
1588
1589                data_format = RawImageFormat::BGRA8;
1590                px.into()
1591            }
1592            RawImageFormat::RG8 => {
1593                let pixels = pixels.get_u8_vec()?;
1594
1595                if pixels.len() != expected_len * TWO_CHANNELS {
1596                    return None;
1597                }
1598
1599                let mut px = vec![0; expected_len * FOUR_BPP];
1600
1601                // TODO: premultiply alpha!
1602                // TODO: check that this function is SIMD optimized
1603                for (pixel_index, greyalpha) in
1604                    pixels.as_ref().chunks_exact(TWO_CHANNELS).enumerate()
1605                {
1606                    let grey = greyalpha[0];
1607                    let alpha = greyalpha[1];
1608
1609                    if alpha != 255 {
1610                        is_opaque = false;
1611                    }
1612
1613                    px[pixel_index * FOUR_BPP] = grey;
1614                    px[(pixel_index * FOUR_BPP) + 1] = grey;
1615                    px[(pixel_index * FOUR_BPP) + 2] = grey;
1616                    px[(pixel_index * FOUR_BPP) + 3] = alpha;
1617                }
1618
1619                data_format = RawImageFormat::BGRA8;
1620                px.into()
1621            }
1622            RawImageFormat::RGB8 => {
1623                let pixels = pixels.get_u8_vec()?;
1624
1625                if pixels.len() != expected_len * THREE_CHANNELS {
1626                    return None;
1627                }
1628
1629                let mut px = vec![0; expected_len * FOUR_BPP];
1630
1631                // TODO: check that this function is SIMD optimized
1632                for (pixel_index, rgb) in pixels.as_ref().chunks_exact(THREE_CHANNELS).enumerate() {
1633                    let red = rgb[0];
1634                    let green = rgb[1];
1635                    let blue = rgb[2];
1636
1637                    px[pixel_index * FOUR_BPP] = blue;
1638                    px[(pixel_index * FOUR_BPP) + 1] = green;
1639                    px[(pixel_index * FOUR_BPP) + 2] = red;
1640                    px[(pixel_index * FOUR_BPP) + 3] = 0xff;
1641                }
1642
1643                data_format = RawImageFormat::BGRA8;
1644                px.into()
1645            }
1646            RawImageFormat::RGBA8 => {
1647                let mut pixels: Vec<u8> = pixels.get_u8_vec()?.into_library_owned_vec();
1648
1649                if pixels.len() != expected_len * FOUR_CHANNELS {
1650                    return None;
1651                }
1652
1653                // TODO: check that this function is SIMD optimized
1654                // no extra allocation necessary, but swizzling
1655                if premultiplied_alpha {
1656                    for rgba in pixels.chunks_exact_mut(4) {
1657                        let (r, gba) = rgba.split_first_mut()?;
1658                        core::mem::swap(r, gba.get_mut(1)?);
1659                        let a = rgba.get_mut(3)?;
1660                        if *a != 255 {
1661                            is_opaque = false;
1662                        }
1663                    }
1664                } else {
1665                    for rgba in pixels.chunks_exact_mut(4) {
1666                        // RGBA => BGRA
1667                        let (r, gba) = rgba.split_first_mut()?;
1668                        core::mem::swap(r, gba.get_mut(1)?);
1669                        let a = rgba.get_mut(3)?;
1670                        if *a != 255 {
1671                            is_opaque = false;
1672                        }
1673                        premultiply_alpha(rgba); // <-
1674                    }
1675                }
1676
1677                data_format = RawImageFormat::BGRA8;
1678                pixels.into()
1679            }
1680            RawImageFormat::R16 => {
1681                let pixels = pixels.get_u16_vec()?;
1682
1683                if pixels.len() != expected_len {
1684                    return None;
1685                }
1686
1687                let mut px = vec![0; expected_len * FOUR_BPP];
1688
1689                // TODO: check that this function is SIMD optimized
1690                for (pixel_index, grey_u16) in pixels.as_ref().iter().enumerate() {
1691                    let grey_u8 = normalize_u16(*grey_u16);
1692                    px[pixel_index * FOUR_BPP] = grey_u8;
1693                    px[(pixel_index * FOUR_BPP) + 1] = grey_u8;
1694                    px[(pixel_index * FOUR_BPP) + 2] = grey_u8;
1695                    px[(pixel_index * FOUR_BPP) + 3] = 0xff;
1696                }
1697
1698                data_format = RawImageFormat::BGRA8;
1699                px.into()
1700            }
1701            RawImageFormat::RG16 => {
1702                let pixels = pixels.get_u16_vec()?;
1703
1704                if pixels.len() != expected_len * TWO_CHANNELS {
1705                    return None;
1706                }
1707
1708                let mut px = vec![0; expected_len * FOUR_BPP];
1709
1710                // TODO: check that this function is SIMD optimized
1711                for (pixel_index, greyalpha) in
1712                    pixels.as_ref().chunks_exact(TWO_CHANNELS).enumerate()
1713                {
1714                    let grey_u8 = normalize_u16(greyalpha[0]);
1715                    let alpha_u8 = normalize_u16(greyalpha[1]);
1716
1717                    if alpha_u8 != 255 {
1718                        is_opaque = false;
1719                    }
1720
1721                    px[pixel_index * FOUR_BPP] = grey_u8;
1722                    px[(pixel_index * FOUR_BPP) + 1] = grey_u8;
1723                    px[(pixel_index * FOUR_BPP) + 2] = grey_u8;
1724                    px[(pixel_index * FOUR_BPP) + 3] = alpha_u8;
1725                }
1726
1727                data_format = RawImageFormat::BGRA8;
1728                px.into()
1729            }
1730            RawImageFormat::RGB16 => {
1731                let pixels = pixels.get_u16_vec()?;
1732
1733                if pixels.len() != expected_len * THREE_CHANNELS {
1734                    return None;
1735                }
1736
1737                let mut px = vec![0; expected_len * FOUR_BPP];
1738
1739                // TODO: check that this function is SIMD optimized
1740                for (pixel_index, rgb) in pixels.as_ref().chunks_exact(THREE_CHANNELS).enumerate() {
1741                    let red_u8 = normalize_u16(rgb[0]);
1742                    let green_u8 = normalize_u16(rgb[1]);
1743                    let blue_u8 = normalize_u16(rgb[2]);
1744
1745                    px[pixel_index * FOUR_BPP] = blue_u8;
1746                    px[(pixel_index * FOUR_BPP) + 1] = green_u8;
1747                    px[(pixel_index * FOUR_BPP) + 2] = red_u8;
1748                    px[(pixel_index * FOUR_BPP) + 3] = 0xff;
1749                }
1750
1751                data_format = RawImageFormat::BGRA8;
1752                px.into()
1753            }
1754            RawImageFormat::RGBA16 => {
1755                let pixels = pixels.get_u16_vec()?;
1756
1757                if pixels.len() != expected_len * FOUR_CHANNELS {
1758                    return None;
1759                }
1760
1761                let mut px = vec![0; expected_len * FOUR_BPP];
1762
1763                // TODO: check that this function is SIMD optimized
1764                if premultiplied_alpha {
1765                    for (pixel_index, rgba) in
1766                        pixels.as_ref().chunks_exact(FOUR_CHANNELS).enumerate()
1767                    {
1768                        let red_u8 = normalize_u16(rgba[0]);
1769                        let green_u8 = normalize_u16(rgba[1]);
1770                        let blue_u8 = normalize_u16(rgba[2]);
1771                        let alpha_u8 = normalize_u16(rgba[3]);
1772
1773                        if alpha_u8 != 255 {
1774                            is_opaque = false;
1775                        }
1776
1777                        px[pixel_index * FOUR_BPP] = blue_u8;
1778                        px[(pixel_index * FOUR_BPP) + 1] = green_u8;
1779                        px[(pixel_index * FOUR_BPP) + 2] = red_u8;
1780                        px[(pixel_index * FOUR_BPP) + 3] = alpha_u8;
1781                    }
1782                } else {
1783                    for (pixel_index, rgba) in
1784                        pixels.as_ref().chunks_exact(FOUR_CHANNELS).enumerate()
1785                    {
1786                        let red_u8 = normalize_u16(rgba[0]);
1787                        let green_u8 = normalize_u16(rgba[1]);
1788                        let blue_u8 = normalize_u16(rgba[2]);
1789                        let alpha_u8 = normalize_u16(rgba[3]);
1790
1791                        if alpha_u8 != 255 {
1792                            is_opaque = false;
1793                        }
1794
1795                        px[pixel_index * FOUR_BPP] = blue_u8;
1796                        px[(pixel_index * FOUR_BPP) + 1] = green_u8;
1797                        px[(pixel_index * FOUR_BPP) + 2] = red_u8;
1798                        px[(pixel_index * FOUR_BPP) + 3] = alpha_u8;
1799                        premultiply_alpha(
1800                            &mut px
1801                                [(pixel_index * FOUR_BPP)..((pixel_index * FOUR_BPP) + FOUR_BPP)],
1802                        );
1803                    }
1804                }
1805
1806                data_format = RawImageFormat::BGRA8;
1807                px.into()
1808            }
1809            RawImageFormat::BGR8 => {
1810                let pixels = pixels.get_u8_vec()?;
1811
1812                if pixels.len() != expected_len * THREE_CHANNELS {
1813                    return None;
1814                }
1815
1816                let mut px = vec![0; expected_len * FOUR_BPP];
1817
1818                // TODO: check that this function is SIMD optimized
1819                for (pixel_index, bgr) in pixels.as_ref().chunks_exact(THREE_CHANNELS).enumerate() {
1820                    let blue = bgr[0];
1821                    let green = bgr[1];
1822                    let red = bgr[2];
1823
1824                    px[pixel_index * FOUR_BPP] = blue;
1825                    px[(pixel_index * FOUR_BPP) + 1] = green;
1826                    px[(pixel_index * FOUR_BPP) + 2] = red;
1827                    px[(pixel_index * FOUR_BPP) + 3] = 0xff;
1828                }
1829
1830                data_format = RawImageFormat::BGRA8;
1831                px.into()
1832            }
1833            RawImageFormat::BGRA8 => {
1834                if premultiplied_alpha {
1835                    // DO NOT CLONE THE IMAGE HERE!
1836                    let pixels = pixels.get_u8_vec()?;
1837
1838                    is_opaque = pixels
1839                        .as_ref()
1840                        .chunks_exact(FOUR_CHANNELS)
1841                        .all(|bgra| bgra[3] == 255);
1842
1843                    pixels
1844                } else {
1845                    let mut pixels: Vec<u8> = pixels.get_u8_vec()?.into_library_owned_vec();
1846
1847                    if pixels.len() != expected_len * FOUR_BPP {
1848                        return None;
1849                    }
1850
1851                    for bgra in pixels.chunks_exact_mut(FOUR_CHANNELS) {
1852                        if bgra[3] != 255 {
1853                            is_opaque = false;
1854                        }
1855                        premultiply_alpha(bgra);
1856                    }
1857                    data_format = RawImageFormat::BGRA8;
1858                    pixels.into()
1859                }
1860            }
1861            RawImageFormat::RGBF32 => {
1862                let pixels = pixels.get_f32_vec_ref()?;
1863
1864                if pixels.len() != expected_len * THREE_CHANNELS {
1865                    return None;
1866                }
1867
1868                let mut px = vec![0; expected_len * FOUR_BPP];
1869
1870                // TODO: check that this function is SIMD optimized
1871                for (pixel_index, rgb) in pixels.as_ref().chunks_exact(THREE_CHANNELS).enumerate() {
1872                    let red_u8 = (rgb[0] * 255.0) as u8;
1873                    let green_u8 = (rgb[1] * 255.0) as u8;
1874                    let blue_u8 = (rgb[2] * 255.0) as u8;
1875
1876                    px[pixel_index * FOUR_BPP] = blue_u8;
1877                    px[(pixel_index * FOUR_BPP) + 1] = green_u8;
1878                    px[(pixel_index * FOUR_BPP) + 2] = red_u8;
1879                    px[(pixel_index * FOUR_BPP) + 3] = 0xff;
1880                }
1881
1882                data_format = RawImageFormat::BGRA8;
1883                px.into()
1884            }
1885            RawImageFormat::RGBAF32 => {
1886                let pixels = pixels.get_f32_vec_ref()?;
1887
1888                if pixels.len() != expected_len * FOUR_CHANNELS {
1889                    return None;
1890                }
1891
1892                let mut px = vec![0; expected_len * FOUR_BPP];
1893
1894                // TODO: check that this function is SIMD optimized
1895                if premultiplied_alpha {
1896                    for (pixel_index, rgba) in
1897                        pixels.as_ref().chunks_exact(FOUR_CHANNELS).enumerate()
1898                    {
1899                        let red_u8 = (rgba[0] * 255.0) as u8;
1900                        let green_u8 = (rgba[1] * 255.0) as u8;
1901                        let blue_u8 = (rgba[2] * 255.0) as u8;
1902                        let alpha_u8 = (rgba[3] * 255.0) as u8;
1903
1904                        if alpha_u8 != 255 {
1905                            is_opaque = false;
1906                        }
1907
1908                        px[pixel_index * FOUR_BPP] = blue_u8;
1909                        px[(pixel_index * FOUR_BPP) + 1] = green_u8;
1910                        px[(pixel_index * FOUR_BPP) + 2] = red_u8;
1911                        px[(pixel_index * FOUR_BPP) + 3] = alpha_u8;
1912                    }
1913                } else {
1914                    for (pixel_index, rgba) in
1915                        pixels.as_ref().chunks_exact(FOUR_CHANNELS).enumerate()
1916                    {
1917                        let red_u8 = (rgba[0] * 255.0) as u8;
1918                        let green_u8 = (rgba[1] * 255.0) as u8;
1919                        let blue_u8 = (rgba[2] * 255.0) as u8;
1920                        let alpha_u8 = (rgba[3] * 255.0) as u8;
1921
1922                        if alpha_u8 != 255 {
1923                            is_opaque = false;
1924                        }
1925
1926                        px[pixel_index * FOUR_BPP] = blue_u8;
1927                        px[(pixel_index * FOUR_BPP) + 1] = green_u8;
1928                        px[(pixel_index * FOUR_BPP) + 2] = red_u8;
1929                        px[(pixel_index * FOUR_BPP) + 3] = alpha_u8;
1930                        premultiply_alpha(
1931                            &mut px
1932                                [(pixel_index * FOUR_BPP)..((pixel_index * FOUR_BPP) + FOUR_BPP)],
1933                        );
1934                    }
1935                }
1936
1937                data_format = RawImageFormat::BGRA8;
1938                px.into()
1939            }
1940        };
1941
1942        let image_data = ImageData::Raw(bytes);
1943        let image_descriptor = ImageDescriptor {
1944            format: data_format,
1945            width,
1946            height,
1947            offset: 0,
1948            stride: None.into(),
1949            flags: ImageDescriptorFlags {
1950                is_opaque,
1951                allow_mipmaps: true,
1952            },
1953        };
1954
1955        Some((image_data, image_descriptor))
1956    }
1957}
1958
1959impl_option!(
1960    RawImage,
1961    OptionRawImage,
1962    copy = false,
1963    [Debug, Clone, PartialEq, PartialOrd]
1964);
1965
1966/// Text broken up into `Tab`, `Word()`, `Return` characters
1967#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
1968#[repr(C)]
1969pub struct Words {
1970    /// Words (and spaces), broken up into semantic items
1971    pub items: WordVec,
1972    /// String that makes up this paragraph of words
1973    pub internal_str: AzString,
1974    /// `internal_chars` is used in order to enable copy-paste (since taking a sub-string isn't
1975    /// possible using UTF-8)
1976    pub internal_chars: U32Vec,
1977}
1978
1979impl Words {
1980    pub fn get_substr(&self, word: &Word) -> String {
1981        self.internal_chars.as_ref()[word.start..word.end]
1982            .iter()
1983            .filter_map(|c| core::char::from_u32(*c))
1984            .collect()
1985    }
1986
1987    pub fn get_str(&self) -> &str {
1988        &self.internal_str.as_str()
1989    }
1990
1991    pub fn get_char(&self, idx: usize) -> Option<char> {
1992        self.internal_chars
1993            .as_ref()
1994            .get(idx)
1995            .and_then(|c| core::char::from_u32(*c))
1996    }
1997}
1998
1999/// Section of a certain type
2000#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
2001#[repr(C)]
2002pub struct Word {
2003    pub start: usize,
2004    pub end: usize,
2005    pub word_type: WordType,
2006}
2007
2008impl_vec!(Word, WordVec, WordVecDestructor);
2009impl_vec_clone!(Word, WordVec, WordVecDestructor);
2010impl_vec_debug!(Word, WordVec);
2011impl_vec_partialeq!(Word, WordVec);
2012impl_vec_eq!(Word, WordVec);
2013impl_vec_ord!(Word, WordVec);
2014impl_vec_partialord!(Word, WordVec);
2015impl_vec_hash!(Word, WordVec);
2016
2017/// Either a white-space delimited word, tab or return character
2018#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
2019#[repr(C)]
2020pub enum WordType {
2021    /// Encountered a word (delimited by spaces)
2022    Word,
2023    // `\t` or `x09`
2024    Tab,
2025    /// `\r`, `\n` or `\r\n`, escaped: `\x0D`, `\x0A` or `\x0D\x0A`
2026    Return,
2027    /// Space character
2028    Space,
2029}
2030
2031/// A paragraph of words that are shaped and scaled (* but not yet layouted / positioned*!)
2032/// according to their final size in pixels.
2033#[derive(Debug, Clone)]
2034#[repr(C)]
2035pub struct ShapedWords {
2036    /// Words scaled to their appropriate font size, but not yet positioned on the screen
2037    pub items: ShapedWordVec,
2038    /// Longest word in the `self.scaled_words`, necessary for
2039    /// calculating overflow rectangles.
2040    pub longest_word_width: usize,
2041    /// Horizontal advance of the space glyph
2042    pub space_advance: usize,
2043    /// Units per EM square
2044    pub font_metrics_units_per_em: u16,
2045    /// Descender of the font
2046    pub font_metrics_ascender: i16,
2047    pub font_metrics_descender: i16,
2048    pub font_metrics_line_gap: i16,
2049}
2050
2051impl ShapedWords {
2052    pub fn get_longest_word_width_px(&self, target_font_size: f32) -> f32 {
2053        self.longest_word_width as f32 / self.font_metrics_units_per_em as f32 * target_font_size
2054    }
2055    pub fn get_space_advance_px(&self, target_font_size: f32) -> f32 {
2056        self.space_advance as f32 / self.font_metrics_units_per_em as f32 * target_font_size
2057    }
2058    /// Get the distance from the top of the text to the baseline of the text (= ascender)
2059    pub fn get_baseline_px(&self, target_font_size: f32) -> f32 {
2060        target_font_size + self.get_descender(target_font_size)
2061    }
2062    /// NOTE: descender is NEGATIVE
2063    pub fn get_descender(&self, target_font_size: f32) -> f32 {
2064        self.font_metrics_descender as f32 / self.font_metrics_units_per_em as f32
2065            * target_font_size
2066    }
2067
2068    /// `height = sTypoAscender - sTypoDescender + sTypoLineGap`
2069    pub fn get_line_height(&self, target_font_size: f32) -> f32 {
2070        self.font_metrics_ascender as f32 / self.font_metrics_units_per_em as f32
2071            - self.font_metrics_descender as f32 / self.font_metrics_units_per_em as f32
2072            + self.font_metrics_line_gap as f32 / self.font_metrics_units_per_em as f32
2073                * target_font_size
2074    }
2075
2076    pub fn get_ascender(&self, target_font_size: f32) -> f32 {
2077        self.font_metrics_ascender as f32 / self.font_metrics_units_per_em as f32 * target_font_size
2078    }
2079}
2080
2081/// A Unicode variation selector.
2082///
2083/// VS04-VS14 are omitted as they aren't currently used.
2084#[derive(Debug, Copy, PartialEq, PartialOrd, Clone, Hash)]
2085#[repr(C)]
2086pub enum VariationSelector {
2087    /// VARIATION SELECTOR-1
2088    VS01 = 1,
2089    /// VARIATION SELECTOR-2
2090    VS02 = 2,
2091    /// VARIATION SELECTOR-3
2092    VS03 = 3,
2093    /// Text presentation
2094    VS15 = 15,
2095    /// Emoji presentation
2096    VS16 = 16,
2097}
2098
2099impl_option!(
2100    VariationSelector,
2101    OptionVariationSelector,
2102    [Debug, Copy, PartialEq, PartialOrd, Clone, Hash]
2103);
2104
2105#[derive(Debug, Copy, PartialEq, PartialOrd, Clone, Hash)]
2106#[repr(C, u8)]
2107pub enum GlyphOrigin {
2108    Char(char),
2109    Direct,
2110}
2111
2112#[derive(Debug, Copy, PartialEq, PartialOrd, Clone, Hash)]
2113#[repr(C)]
2114pub struct PlacementDistance {
2115    pub x: i32,
2116    pub y: i32,
2117}
2118
2119/// When not Attachment::None indicates that this glyph
2120/// is an attachment with placement indicated by the variant.
2121#[derive(Debug, Copy, PartialEq, PartialOrd, Clone, Hash)]
2122#[repr(C, u8)]
2123pub enum Placement {
2124    None,
2125    Distance(PlacementDistance),
2126    MarkAnchor(MarkAnchorPlacement),
2127    /// An overprint mark.
2128    ///
2129    /// This mark is shown at the same position as the base glyph.
2130    ///
2131    /// Fields: (base glyph index in `Vec<GlyphInfo>`)
2132    MarkOverprint(usize),
2133    CursiveAnchor(CursiveAnchorPlacement),
2134}
2135
2136/// Cursive anchored placement.
2137///
2138/// https://docs.microsoft.com/en-us/typography/opentype/spec/gpos#lookup-type-3-cursive-attachment-positioning-subtable
2139#[derive(Debug, Copy, PartialEq, PartialOrd, Clone, Hash)]
2140#[repr(C)]
2141pub struct CursiveAnchorPlacement {
2142    /// exit glyph index in the `Vec<GlyphInfo>`
2143    pub exit_glyph_index: usize,
2144    /// RIGHT_TO_LEFT flag from lookup table
2145    pub right_to_left: bool,
2146    /// exit glyph anchor
2147    pub exit_glyph_anchor: Anchor,
2148    /// entry glyph anchor
2149    pub entry_glyph_anchor: Anchor,
2150}
2151
2152/// An anchored mark.
2153///
2154/// This is a mark where its anchor is aligned with the base glyph anchor.
2155#[derive(Debug, Copy, PartialEq, PartialOrd, Clone, Hash)]
2156#[repr(C)]
2157pub struct MarkAnchorPlacement {
2158    /// base glyph index in `Vec<GlyphInfo>`
2159    pub base_glyph_index: usize,
2160    /// base glyph anchor
2161    pub base_glyph_anchor: Anchor,
2162    /// mark anchor
2163    pub mark_anchor: Anchor,
2164}
2165
2166#[derive(Debug, Copy, PartialEq, PartialOrd, Clone, Hash)]
2167#[repr(C)]
2168pub struct Anchor {
2169    pub x: i16,
2170    pub y: i16,
2171}
2172
2173#[derive(Debug, Copy, PartialEq, PartialOrd, Clone, Hash)]
2174#[repr(C)]
2175pub struct RawGlyph {
2176    pub unicode_codepoint: OptionChar, // Option<char>
2177    pub glyph_index: u16,
2178    pub liga_component_pos: u16,
2179    pub glyph_origin: GlyphOrigin,
2180    pub small_caps: bool,
2181    pub multi_subst_dup: bool,
2182    pub is_vert_alt: bool,
2183    pub fake_bold: bool,
2184    pub fake_italic: bool,
2185    pub variation: OptionVariationSelector,
2186}
2187
2188impl RawGlyph {
2189    pub fn has_codepoint(&self) -> bool {
2190        self.unicode_codepoint.is_some()
2191    }
2192
2193    pub fn get_codepoint(&self) -> Option<char> {
2194        self.unicode_codepoint
2195            .as_ref()
2196            .and_then(|u| core::char::from_u32(*u))
2197    }
2198}
2199
2200#[derive(Debug, PartialEq, PartialOrd, Clone, Hash)]
2201#[repr(C)]
2202pub struct GlyphInfo {
2203    pub glyph: RawGlyph,
2204    pub size: Advance,
2205    pub kerning: i16,
2206    pub placement: Placement,
2207}
2208
2209pub fn get_inline_text(
2210    words: &Words,
2211    shaped_words: &ShapedWords,
2212    word_positions: &WordPositions,
2213    inline_text_layout: &InlineTextLayout,
2214) -> InlineText {
2215    use crate::callbacks::{InlineGlyph, InlineLine, InlineTextContents, InlineWord};
2216
2217    // check the range so that in the worst case there isn't a random crash here
2218    fn get_range_checked_inclusive_end(
2219        input: &[Word],
2220        word_start: usize,
2221        word_end: usize,
2222    ) -> Option<&[Word]> {
2223        if word_start < input.len() && word_end < input.len() && word_start <= word_end {
2224            Some(&input[word_start..=word_end])
2225        } else {
2226            None
2227        }
2228    }
2229
2230    let font_size_px = word_positions.text_layout_options.font_size_px;
2231    let descender_px = &shaped_words.get_descender(font_size_px); // descender is NEGATIVE
2232    let letter_spacing_px = word_positions
2233        .text_layout_options
2234        .letter_spacing
2235        .as_ref()
2236        .copied()
2237        .unwrap_or(0.0);
2238    let units_per_em = shaped_words.font_metrics_units_per_em;
2239
2240    let inline_lines = inline_text_layout
2241        .lines
2242        .as_ref()
2243        .iter()
2244        .filter_map(|line| {
2245            let word_items = words.items.as_ref();
2246            let word_start = line.word_start.min(line.word_end);
2247            let word_end = line.word_end.max(line.word_start);
2248
2249            let words = get_range_checked_inclusive_end(word_items, word_start, word_end)?
2250                .iter()
2251                .enumerate()
2252                .filter_map(|(word_idx, word)| {
2253                    let word_idx = word_start + word_idx;
2254                    match word.word_type {
2255                        WordType::Word => {
2256                            let word_position = word_positions.word_positions.get(word_idx)?;
2257                            let shaped_word_index = word_position.shaped_word_index?;
2258                            let shaped_word = shaped_words.items.get(shaped_word_index)?;
2259
2260                            // most words are less than 16 chars, avg length of an english word is
2261                            // 4.7 chars
2262                            let mut all_glyphs_in_this_word = Vec::<InlineGlyph>::with_capacity(16);
2263                            let mut x_pos_in_word_px = 0.0;
2264
2265                            // all words only store the unscaled horizontal advance + horizontal
2266                            // kerning
2267                            for glyph_info in shaped_word.glyph_infos.iter() {
2268                                // local x and y displacement of the glyph - does NOT advance the
2269                                // horizontal cursor!
2270                                let mut displacement = LogicalPosition::zero();
2271
2272                                // if the character is a mark, the mark displacement has to be added
2273                                // ON TOP OF the existing displacement
2274                                // the origin should be relative to the word, not the final text
2275                                let (letter_spacing_for_glyph, origin) = match glyph_info.placement
2276                                {
2277                                    Placement::None => (
2278                                        letter_spacing_px,
2279                                        LogicalPosition::new(
2280                                            x_pos_in_word_px + displacement.x,
2281                                            displacement.y,
2282                                        ),
2283                                    ),
2284                                    Placement::Distance(PlacementDistance { x, y }) => {
2285                                        let font_metrics_divisor =
2286                                            units_per_em as f32 / font_size_px;
2287                                        displacement = LogicalPosition {
2288                                            x: x as f32 / font_metrics_divisor,
2289                                            y: y as f32 / font_metrics_divisor,
2290                                        };
2291                                        (
2292                                            letter_spacing_px,
2293                                            LogicalPosition::new(
2294                                                x_pos_in_word_px + displacement.x,
2295                                                displacement.y,
2296                                            ),
2297                                        )
2298                                    }
2299                                    Placement::MarkAnchor(MarkAnchorPlacement {
2300                                        base_glyph_index,
2301                                        ..
2302                                    }) => {
2303                                        let anchor = &all_glyphs_in_this_word[base_glyph_index];
2304                                        (0.0, anchor.bounds.origin + displacement)
2305                                        // TODO: wrong
2306                                    }
2307                                    Placement::MarkOverprint(index) => {
2308                                        let anchor = &all_glyphs_in_this_word[index];
2309                                        (0.0, anchor.bounds.origin + displacement)
2310                                    }
2311                                    Placement::CursiveAnchor(CursiveAnchorPlacement {
2312                                        exit_glyph_index,
2313                                        ..
2314                                    }) => {
2315                                        let anchor = &all_glyphs_in_this_word[exit_glyph_index];
2316                                        (0.0, anchor.bounds.origin + displacement)
2317                                        // TODO: wrong
2318                                    }
2319                                };
2320
2321                                let glyph_scale_x = glyph_info
2322                                    .size
2323                                    .get_x_size_scaled(units_per_em, font_size_px);
2324                                let glyph_scale_y = glyph_info
2325                                    .size
2326                                    .get_y_size_scaled(units_per_em, font_size_px);
2327
2328                                let glyph_advance_x = glyph_info
2329                                    .size
2330                                    .get_x_advance_scaled(units_per_em, font_size_px);
2331                                let kerning_x = glyph_info
2332                                    .size
2333                                    .get_kerning_scaled(units_per_em, font_size_px);
2334
2335                                let inline_char = InlineGlyph {
2336                                    bounds: LogicalRect::new(
2337                                        origin,
2338                                        LogicalSize::new(glyph_scale_x, glyph_scale_y),
2339                                    ),
2340                                    unicode_codepoint: glyph_info.glyph.unicode_codepoint,
2341                                    glyph_index: glyph_info.glyph.glyph_index as u32,
2342                                };
2343
2344                                x_pos_in_word_px +=
2345                                    glyph_advance_x + kerning_x + letter_spacing_for_glyph;
2346
2347                                all_glyphs_in_this_word.push(inline_char);
2348                            }
2349
2350                            let inline_word = InlineWord::Word(InlineTextContents {
2351                                glyphs: all_glyphs_in_this_word.into(),
2352                                bounds: LogicalRect::new(
2353                                    word_position.position,
2354                                    word_position.size,
2355                                ),
2356                            });
2357
2358                            Some(inline_word)
2359                        }
2360                        WordType::Tab => Some(InlineWord::Tab),
2361                        WordType::Return => Some(InlineWord::Return),
2362                        WordType::Space => Some(InlineWord::Space),
2363                    }
2364                })
2365                .collect::<Vec<InlineWord>>();
2366
2367            Some(InlineLine {
2368                words: words.into(),
2369                bounds: line.bounds,
2370            })
2371        })
2372        .collect::<Vec<InlineLine>>();
2373
2374    InlineText {
2375        lines: inline_lines.into(), // relative to 0, 0
2376        content_size: word_positions.content_size,
2377        font_size_px,
2378        last_word_index: word_positions.number_of_shaped_words,
2379        baseline_descender_px: *descender_px,
2380    }
2381}
2382
2383impl_vec!(GlyphInfo, GlyphInfoVec, GlyphInfoVecDestructor);
2384impl_vec_clone!(GlyphInfo, GlyphInfoVec, GlyphInfoVecDestructor);
2385impl_vec_debug!(GlyphInfo, GlyphInfoVec);
2386impl_vec_partialeq!(GlyphInfo, GlyphInfoVec);
2387impl_vec_partialord!(GlyphInfo, GlyphInfoVec);
2388impl_vec_hash!(GlyphInfo, GlyphInfoVec);
2389
2390#[derive(Debug, Default, Copy, PartialEq, PartialOrd, Clone, Hash)]
2391#[repr(C)]
2392pub struct Advance {
2393    pub advance_x: u16,
2394    pub size_x: i32,
2395    pub size_y: i32,
2396    pub kerning: i16,
2397}
2398
2399impl Advance {
2400    #[inline]
2401    pub const fn get_x_advance_total_unscaled(&self) -> i32 {
2402        self.advance_x as i32 + self.kerning as i32
2403    }
2404    #[inline]
2405    pub const fn get_x_advance_unscaled(&self) -> u16 {
2406        self.advance_x
2407    }
2408    #[inline]
2409    pub const fn get_x_size_unscaled(&self) -> i32 {
2410        self.size_x
2411    }
2412    #[inline]
2413    pub const fn get_y_size_unscaled(&self) -> i32 {
2414        self.size_y
2415    }
2416    #[inline]
2417    pub const fn get_kerning_unscaled(&self) -> i16 {
2418        self.kerning
2419    }
2420
2421    #[inline]
2422    pub fn get_x_advance_total_scaled(&self, units_per_em: u16, target_font_size: f32) -> f32 {
2423        self.get_x_advance_total_unscaled() as f32 / units_per_em as f32 * target_font_size
2424    }
2425    #[inline]
2426    pub fn get_x_advance_scaled(&self, units_per_em: u16, target_font_size: f32) -> f32 {
2427        self.get_x_advance_unscaled() as f32 / units_per_em as f32 * target_font_size
2428    }
2429    #[inline]
2430    pub fn get_x_size_scaled(&self, units_per_em: u16, target_font_size: f32) -> f32 {
2431        self.get_x_size_unscaled() as f32 / units_per_em as f32 * target_font_size
2432    }
2433    #[inline]
2434    pub fn get_y_size_scaled(&self, units_per_em: u16, target_font_size: f32) -> f32 {
2435        self.get_y_size_unscaled() as f32 / units_per_em as f32 * target_font_size
2436    }
2437    #[inline]
2438    pub fn get_kerning_scaled(&self, units_per_em: u16, target_font_size: f32) -> f32 {
2439        self.get_kerning_unscaled() as f32 / units_per_em as f32 * target_font_size
2440    }
2441}
2442
2443/// Word that is scaled (to a font / font instance), but not yet positioned
2444#[derive(PartialEq, PartialOrd, Clone)]
2445#[repr(C)]
2446pub struct ShapedWord {
2447    /// Glyph codepoint, glyph ID + kerning data
2448    pub glyph_infos: GlyphInfoVec,
2449    /// The sum of the width of all the characters in this word
2450    pub word_width: usize,
2451}
2452
2453impl fmt::Debug for ShapedWord {
2454    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
2455        write!(
2456            f,
2457            "ShapedWord {{ glyph_infos: {} glyphs, word_width: {} }}",
2458            self.glyph_infos.len(),
2459            self.word_width
2460        )
2461    }
2462}
2463
2464impl_vec!(ShapedWord, ShapedWordVec, ShapedWordVecDestructor);
2465impl_vec_clone!(ShapedWord, ShapedWordVec, ShapedWordVecDestructor);
2466impl_vec_partialeq!(ShapedWord, ShapedWordVec);
2467impl_vec_partialord!(ShapedWord, ShapedWordVec);
2468impl_vec_debug!(ShapedWord, ShapedWordVec);
2469
2470impl ShapedWord {
2471    pub fn get_word_width(&self, units_per_em: u16, target_font_size: f32) -> f32 {
2472        self.word_width as f32 / units_per_em as f32 * target_font_size
2473    }
2474    /// Returns the number of glyphs THAT ARE NOT DIACRITIC MARKS
2475    pub fn number_of_glyphs(&self) -> usize {
2476        self.glyph_infos
2477            .iter()
2478            .filter(|i| i.placement == Placement::None)
2479            .count()
2480    }
2481}
2482
2483/// Stores the positions of the vertically laid out texts
2484#[derive(Debug, Clone, PartialEq)]
2485pub struct WordPositions {
2486    /// Options like word spacing, character spacing, etc. that were
2487    /// used to layout these glyphs
2488    pub text_layout_options: ResolvedTextLayoutOptions,
2489    /// Stores the positions of words.
2490    pub word_positions: Vec<WordPosition>,
2491    /// Index of the word at which the line breaks + length of line
2492    /// (useful for text selection + horizontal centering)
2493    pub line_breaks: Vec<InlineTextLine>,
2494    /// Horizontal width of the last line (in pixels), necessary for inline layout later on,
2495    /// so that the next text run can contine where the last text run left off.
2496    ///
2497    /// Usually, the "trailing" of the current text block is the "leading" of the
2498    /// next text block, to make it seem like two text runs push into each other.
2499    pub trailing: f32,
2500    /// How many words are in the text?
2501    pub number_of_shaped_words: usize,
2502    /// How many lines (NOTE: virtual lines, meaning line breaks in the layouted text) are there?
2503    pub number_of_lines: usize,
2504    /// Horizontal and vertical boundaries of the layouted words.
2505    ///
2506    /// Note that the vertical extent can be larger than the last words' position,
2507    /// because of trailing negative glyph advances.
2508    pub content_size: LogicalSize,
2509}
2510
2511#[derive(Debug, Clone, PartialEq)]
2512pub struct WordPosition {
2513    pub shaped_word_index: Option<usize>,
2514    pub position: LogicalPosition,
2515    pub size: LogicalSize,
2516}
2517
2518/// Returns the layouted glyph instances
2519#[derive(Debug, Clone, PartialEq)]
2520pub struct LayoutedGlyphs {
2521    pub glyphs: Vec<GlyphInstance>,
2522}
2523
2524/// Scans the `StyledDom` for new images and fonts. After this call,
2525/// the `all_resource_updates` contains all the `AddFont` / `AddImage`
2526/// / `AddFontInstance` messages.
2527pub fn add_fonts_and_images(
2528    image_cache: &ImageCache,
2529    renderer_resources: &mut RendererResources,
2530    current_window_dpi: DpiScaleFactor,
2531    fc_cache: &FcFontCache,
2532    render_api_namespace: IdNamespace,
2533    epoch: Epoch,
2534    document_id: &DocumentId,
2535    all_resource_updates: &mut Vec<ResourceUpdate>,
2536    styled_dom: &StyledDom,
2537    load_font_fn: LoadFontFn,
2538    parse_font_fn: ParseFontFn,
2539    insert_into_active_gl_textures: GlStoreImageFn,
2540) {
2541    let new_image_keys = styled_dom.scan_for_image_keys(&image_cache);
2542    let new_font_keys = styled_dom.scan_for_font_keys(&renderer_resources);
2543
2544    let add_image_resource_updates = build_add_image_resource_updates(
2545        renderer_resources,
2546        render_api_namespace,
2547        epoch,
2548        document_id,
2549        &new_image_keys,
2550        insert_into_active_gl_textures,
2551    );
2552
2553    let add_font_resource_updates = build_add_font_resource_updates(
2554        renderer_resources,
2555        current_window_dpi,
2556        fc_cache,
2557        render_api_namespace,
2558        &new_font_keys,
2559        load_font_fn,
2560        parse_font_fn,
2561    );
2562
2563    add_resources(
2564        renderer_resources,
2565        all_resource_updates,
2566        add_font_resource_updates,
2567        add_image_resource_updates,
2568    );
2569}
2570
2571pub fn font_size_to_au(font_size: StyleFontSize) -> Au {
2572    use crate::ui_solver::DEFAULT_FONT_SIZE_PX;
2573    Au::from_px(font_size.inner.to_pixels(DEFAULT_FONT_SIZE_PX as f32))
2574}
2575
2576pub type FontInstanceFlags = u32;
2577
2578// Common flags
2579pub const FONT_INSTANCE_FLAG_SYNTHETIC_BOLD: u32 = 1 << 1;
2580pub const FONT_INSTANCE_FLAG_EMBEDDED_BITMAPS: u32 = 1 << 2;
2581pub const FONT_INSTANCE_FLAG_SUBPIXEL_BGR: u32 = 1 << 3;
2582pub const FONT_INSTANCE_FLAG_TRANSPOSE: u32 = 1 << 4;
2583pub const FONT_INSTANCE_FLAG_FLIP_X: u32 = 1 << 5;
2584pub const FONT_INSTANCE_FLAG_FLIP_Y: u32 = 1 << 6;
2585pub const FONT_INSTANCE_FLAG_SUBPIXEL_POSITION: u32 = 1 << 7;
2586
2587// Windows flags
2588pub const FONT_INSTANCE_FLAG_FORCE_GDI: u32 = 1 << 16;
2589
2590// Mac flags
2591pub const FONT_INSTANCE_FLAG_FONT_SMOOTHING: u32 = 1 << 16;
2592
2593// FreeType flags
2594pub const FONT_INSTANCE_FLAG_FORCE_AUTOHINT: u32 = 1 << 16;
2595pub const FONT_INSTANCE_FLAG_NO_AUTOHINT: u32 = 1 << 17;
2596pub const FONT_INSTANCE_FLAG_VERTICAL_LAYOUT: u32 = 1 << 18;
2597pub const FONT_INSTANCE_FLAG_LCD_VERTICAL: u32 = 1 << 19;
2598
2599#[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash)]
2600pub struct GlyphOptions {
2601    pub render_mode: FontRenderMode,
2602    pub flags: FontInstanceFlags,
2603}
2604
2605#[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash)]
2606pub enum FontRenderMode {
2607    Mono,
2608    Alpha,
2609    Subpixel,
2610}
2611
2612#[cfg(target_arch = "wasm32")]
2613#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash)]
2614pub struct FontInstancePlatformOptions {
2615    // empty for now
2616}
2617
2618#[cfg(target_os = "windows")]
2619#[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash)]
2620pub struct FontInstancePlatformOptions {
2621    pub gamma: u16,
2622    pub contrast: u8,
2623    pub cleartype_level: u8,
2624}
2625
2626#[cfg(target_os = "macos")]
2627#[derive(Default, Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash)]
2628pub struct FontInstancePlatformOptions {
2629    pub unused: u32,
2630}
2631
2632#[cfg(target_os = "linux")]
2633#[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash)]
2634pub struct FontInstancePlatformOptions {
2635    pub lcd_filter: FontLCDFilter,
2636    pub hinting: FontHinting,
2637}
2638
2639#[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash)]
2640pub enum FontHinting {
2641    None,
2642    Mono,
2643    Light,
2644    Normal,
2645    LCD,
2646}
2647
2648#[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash)]
2649pub enum FontLCDFilter {
2650    None,
2651    Default,
2652    Light,
2653    Legacy,
2654}
2655
2656impl Default for FontLCDFilter {
2657    fn default() -> Self {
2658        FontLCDFilter::Default
2659    }
2660}
2661
2662#[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash)]
2663pub struct FontInstanceOptions {
2664    pub render_mode: FontRenderMode,
2665    pub flags: FontInstanceFlags,
2666    pub bg_color: ColorU,
2667    /// When bg_color.a is != 0 and render_mode is FontRenderMode::Subpixel,
2668    /// the text will be rendered with bg_color.r/g/b as an opaque estimated
2669    /// background color.
2670    pub synthetic_italics: SyntheticItalics,
2671}
2672
2673impl Default for FontInstanceOptions {
2674    fn default() -> FontInstanceOptions {
2675        FontInstanceOptions {
2676            render_mode: FontRenderMode::Subpixel,
2677            flags: 0,
2678            bg_color: ColorU::TRANSPARENT,
2679            synthetic_italics: SyntheticItalics::default(),
2680        }
2681    }
2682}
2683
2684#[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash)]
2685pub struct SyntheticItalics {
2686    pub angle: i16,
2687}
2688
2689impl Default for SyntheticItalics {
2690    fn default() -> Self {
2691        Self { angle: 0 }
2692    }
2693}
2694
2695/// Represents the backing store of an arbitrary series of pixels for display by
2696/// WebRender. This storage can take several forms.
2697#[derive(Debug, Clone, PartialEq, Eq, Ord, PartialOrd, Hash)]
2698#[repr(C, u8)]
2699pub enum ImageData {
2700    /// A simple series of bytes, provided by the embedding and owned by WebRender.
2701    /// The format is stored out-of-band, currently in ImageDescriptor.
2702    Raw(U8Vec),
2703    /// An image owned by the embedding, and referenced by WebRender. This may
2704    /// take the form of a texture or a heap-allocated buffer.
2705    External(ExternalImageData),
2706}
2707
2708/// Storage format identifier for externally-managed images.
2709#[derive(Debug, Copy, Clone, Eq, Hash, PartialEq, PartialOrd, Ord)]
2710#[repr(C, u8)]
2711pub enum ExternalImageType {
2712    /// The image is texture-backed.
2713    TextureHandle(ImageBufferKind),
2714    /// The image is heap-allocated by the embedding.
2715    Buffer,
2716}
2717
2718/// An arbitrary identifier for an external image provided by the
2719/// application. It must be a unique identifier for each external
2720/// image.
2721#[repr(C)]
2722#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, PartialOrd, Ord)]
2723pub struct ExternalImageId {
2724    pub inner: u64,
2725}
2726
2727static LAST_EXTERNAL_IMAGE_ID: AtomicUsize = AtomicUsize::new(0);
2728
2729impl ExternalImageId {
2730    /// Creates a new, unique ExternalImageId
2731    pub fn new() -> Self {
2732        Self {
2733            inner: LAST_EXTERNAL_IMAGE_ID.fetch_add(1, AtomicOrdering::SeqCst) as u64,
2734        }
2735    }
2736}
2737
2738/// Specifies the type of texture target in driver terms.
2739#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
2740#[repr(C)]
2741pub enum ImageBufferKind {
2742    /// Standard texture. This maps to GL_TEXTURE_2D in OpenGL.
2743    Texture2D = 0,
2744    /// Rectangle texture. This maps to GL_TEXTURE_RECTANGLE in OpenGL. This
2745    /// is similar to a standard texture, with a few subtle differences
2746    /// (no mipmaps, non-power-of-two dimensions, different coordinate space)
2747    /// that make it useful for representing the kinds of textures we use
2748    /// in WebRender. See https://www.khronos.org/opengl/wiki/Rectangle_Texture
2749    /// for background on Rectangle textures.
2750    TextureRect = 1,
2751    /// External texture. This maps to GL_TEXTURE_EXTERNAL_OES in OpenGL, which
2752    /// is an extension. This is used for image formats that OpenGL doesn't
2753    /// understand, particularly YUV. See
2754    /// https://www.khronos.org/registry/OpenGL/extensions/OES/OES_EGL_image_external.txt
2755    TextureExternal = 2,
2756}
2757
2758/// Descriptor for external image resources. See `ImageData`.
2759#[repr(C)]
2760#[derive(Debug, Clone, Eq, Hash, PartialEq, PartialOrd, Ord)]
2761pub struct ExternalImageData {
2762    /// The identifier of this external image, provided by the embedding.
2763    pub id: ExternalImageId,
2764    /// For multi-plane images (i.e. YUV), indicates the plane of the
2765    /// original image that this struct represents. 0 for single-plane images.
2766    pub channel_index: u8,
2767    /// Storage format identifier.
2768    pub image_type: ExternalImageType,
2769}
2770
2771pub type TileSize = u16;
2772
2773#[derive(Debug, Copy, Clone, PartialEq, PartialOrd)]
2774pub enum ImageDirtyRect {
2775    All,
2776    Partial(LayoutRect),
2777}
2778
2779#[derive(Debug, Clone, PartialEq, PartialOrd)]
2780pub enum ResourceUpdate {
2781    AddFont(AddFont),
2782    DeleteFont(FontKey),
2783    AddFontInstance(AddFontInstance),
2784    DeleteFontInstance(FontInstanceKey),
2785    AddImage(AddImage),
2786    UpdateImage(UpdateImage),
2787    DeleteImage(ImageKey),
2788}
2789
2790#[derive(Debug, Clone, PartialEq, Eq, Ord, PartialOrd, Hash)]
2791pub struct AddImage {
2792    pub key: ImageKey,
2793    pub descriptor: ImageDescriptor,
2794    pub data: ImageData,
2795    pub tiling: Option<TileSize>,
2796}
2797
2798#[derive(Debug, Clone, PartialEq, PartialOrd)]
2799pub struct UpdateImage {
2800    pub key: ImageKey,
2801    pub descriptor: ImageDescriptor,
2802    pub data: ImageData,
2803    pub dirty_rect: ImageDirtyRect,
2804}
2805
2806#[derive(Clone, PartialEq, Eq, Ord, PartialOrd, Hash)]
2807pub struct AddFont {
2808    pub key: FontKey,
2809    pub font_bytes: U8Vec,
2810    pub font_index: u32,
2811}
2812
2813impl fmt::Debug for AddFont {
2814    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
2815        write!(
2816            f,
2817            "AddFont {{ key: {:?}, font_bytes: [u8;{}], font_index: {} }}",
2818            self.key,
2819            self.font_bytes.len(),
2820            self.font_index
2821        )
2822    }
2823}
2824
2825#[derive(Debug, Clone, PartialEq, PartialOrd)]
2826pub struct AddFontInstance {
2827    pub key: FontInstanceKey,
2828    pub font_key: FontKey,
2829    pub glyph_size: (Au, DpiScaleFactor),
2830    pub options: Option<FontInstanceOptions>,
2831    pub platform_options: Option<FontInstancePlatformOptions>,
2832    pub variations: Vec<FontVariation>,
2833}
2834
2835#[repr(C)]
2836#[derive(Clone, Copy, Debug, PartialOrd, PartialEq)]
2837pub struct FontVariation {
2838    pub tag: u32,
2839    pub value: f32,
2840}
2841
2842#[repr(C)]
2843#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
2844pub struct Epoch {
2845    inner: u32,
2846}
2847
2848impl fmt::Display for Epoch {
2849    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
2850        write!(f, "{}", self.inner)
2851    }
2852}
2853
2854impl Epoch {
2855    // prevent raw access to the .inner field so that
2856    // you can grep the codebase for .increment() to see
2857    // exactly where the epoch is being incremented
2858    pub const fn new() -> Self {
2859        Self { inner: 0 }
2860    }
2861    pub const fn from(i: u32) -> Self {
2862        Self { inner: i }
2863    }
2864    pub const fn into_u32(&self) -> u32 {
2865        self.inner
2866    }
2867
2868    // We don't want the epoch to increase to u32::MAX, since
2869    // u32::MAX represents an invalid epoch, which could confuse webrender
2870    pub fn increment(&mut self) {
2871        use core::u32;
2872        const MAX_ID: u32 = u32::MAX - 1;
2873        *self = match self.inner {
2874            MAX_ID => Epoch { inner: 0 },
2875            other => Epoch {
2876                inner: other.saturating_add(1),
2877            },
2878        };
2879    }
2880}
2881
2882// App units that this font instance was registered for
2883#[derive(Debug, Clone, Copy, Hash, PartialEq, PartialOrd, Eq, Ord)]
2884pub struct Au(pub i32);
2885
2886pub const AU_PER_PX: i32 = 60;
2887pub const MAX_AU: i32 = (1 << 30) - 1;
2888pub const MIN_AU: i32 = -(1 << 30) - 1;
2889
2890impl Au {
2891    pub fn from_px(px: f32) -> Self {
2892        let target_app_units = (px * AU_PER_PX as f32) as i32;
2893        Au(target_app_units.min(MAX_AU).max(MIN_AU))
2894    }
2895    pub fn into_px(&self) -> f32 {
2896        self.0 as f32 / AU_PER_PX as f32
2897    }
2898}
2899
2900// Debug, PartialEq, Eq, PartialOrd, Ord
2901#[derive(Debug)]
2902pub enum AddFontMsg {
2903    // add font: font key, font bytes + font index
2904    Font(FontKey, StyleFontFamilyHash, FontRef),
2905    Instance(AddFontInstance, (Au, DpiScaleFactor)),
2906}
2907
2908impl AddFontMsg {
2909    pub fn into_resource_update(&self) -> ResourceUpdate {
2910        use self::AddFontMsg::*;
2911        match self {
2912            Font(font_key, _, font_ref) => ResourceUpdate::AddFont(AddFont {
2913                key: *font_key,
2914                font_bytes: font_ref.get_data().bytes.clone(),
2915                font_index: font_ref.get_data().font_index,
2916            }),
2917            Instance(fi, _) => ResourceUpdate::AddFontInstance(fi.clone()),
2918        }
2919    }
2920}
2921
2922#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
2923pub enum DeleteFontMsg {
2924    Font(FontKey),
2925    Instance(FontInstanceKey, (Au, DpiScaleFactor)),
2926}
2927
2928impl DeleteFontMsg {
2929    pub fn into_resource_update(&self) -> ResourceUpdate {
2930        use self::DeleteFontMsg::*;
2931        match self {
2932            Font(f) => ResourceUpdate::DeleteFont(*f),
2933            Instance(fi, _) => ResourceUpdate::DeleteFontInstance(*fi),
2934        }
2935    }
2936}
2937
2938#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
2939pub struct AddImageMsg(pub AddImage);
2940
2941impl AddImageMsg {
2942    pub fn into_resource_update(&self) -> ResourceUpdate {
2943        ResourceUpdate::AddImage(self.0.clone())
2944    }
2945}
2946
2947#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
2948pub struct DeleteImageMsg(ImageKey);
2949
2950impl DeleteImageMsg {
2951    pub fn into_resource_update(&self) -> ResourceUpdate {
2952        ResourceUpdate::DeleteImage(self.0.clone())
2953    }
2954}
2955
2956#[derive(Debug, Clone, PartialEq, Eq, Hash)]
2957#[repr(C)]
2958pub struct LoadedFontSource {
2959    pub data: U8Vec,
2960    pub index: u32,
2961    pub load_outlines: bool,
2962}
2963
2964// function to load the font source from a file
2965pub type LoadFontFn = fn(&StyleFontFamily, &FcFontCache) -> Option<LoadedFontSource>;
2966
2967// function to parse the font given the loaded font source
2968pub type ParseFontFn = fn(LoadedFontSource) -> Option<FontRef>; // = Option<Box<azul_text_layout::Font>>
2969
2970/// Given the fonts of the current frame, returns `AddFont` and `AddFontInstance`s of
2971/// which fonts / instances are currently not in the `current_registered_fonts` and
2972/// need to be added.
2973///
2974/// Deleting fonts can only be done after the entire frame has finished drawing,
2975/// otherwise (if removing fonts would happen after every DOM) we'd constantly
2976/// add-and-remove fonts after every IFrameCallback, which would cause a lot of
2977/// I/O waiting.
2978pub fn build_add_font_resource_updates(
2979    renderer_resources: &mut RendererResources,
2980    dpi: DpiScaleFactor,
2981    fc_cache: &FcFontCache,
2982    id_namespace: IdNamespace,
2983    fonts_in_dom: &FastHashMap<ImmediateFontId, FastBTreeSet<Au>>,
2984    font_source_load_fn: LoadFontFn,
2985    parse_font_fn: ParseFontFn,
2986) -> Vec<(StyleFontFamilyHash, AddFontMsg)> {
2987    let mut resource_updates = alloc::vec::Vec::new();
2988    let mut font_instances_added_this_frame = FastBTreeSet::new();
2989
2990    'outer: for (im_font_id, font_sizes) in fonts_in_dom {
2991        macro_rules! insert_font_instances {
2992            ($font_family_hash:expr, $font_key:expr, $font_size:expr) => {{
2993                let font_instance_key_exists = renderer_resources
2994                    .currently_registered_fonts
2995                    .get(&$font_key)
2996                    .and_then(|(_, font_instances)| font_instances.get(&($font_size, dpi)))
2997                    .is_some()
2998                    || font_instances_added_this_frame.contains(&($font_key, ($font_size, dpi)));
2999
3000                if !font_instance_key_exists {
3001                    let font_instance_key = FontInstanceKey::unique(id_namespace);
3002
3003                    // For some reason the gamma is way to low on Windows
3004                    #[cfg(target_os = "windows")]
3005                    let platform_options = FontInstancePlatformOptions {
3006                        gamma: 300,
3007                        contrast: 100,
3008                        cleartype_level: 100,
3009                    };
3010
3011                    #[cfg(target_os = "linux")]
3012                    let platform_options = FontInstancePlatformOptions {
3013                        lcd_filter: FontLCDFilter::Default,
3014                        hinting: FontHinting::Normal,
3015                    };
3016
3017                    #[cfg(target_os = "macos")]
3018                    let platform_options = FontInstancePlatformOptions::default();
3019
3020                    #[cfg(target_arch = "wasm32")]
3021                    let platform_options = FontInstancePlatformOptions::default();
3022
3023                    let options = FontInstanceOptions {
3024                        render_mode: FontRenderMode::Subpixel,
3025                        flags: 0 | FONT_INSTANCE_FLAG_NO_AUTOHINT,
3026                        ..Default::default()
3027                    };
3028
3029                    font_instances_added_this_frame.insert(($font_key, ($font_size, dpi)));
3030                    resource_updates.push((
3031                        $font_family_hash,
3032                        AddFontMsg::Instance(
3033                            AddFontInstance {
3034                                key: font_instance_key,
3035                                font_key: $font_key,
3036                                glyph_size: ($font_size, dpi),
3037                                options: Some(options),
3038                                platform_options: Some(platform_options),
3039                                variations: alloc::vec::Vec::new(),
3040                            },
3041                            ($font_size, dpi),
3042                        ),
3043                    ));
3044                }
3045            }};
3046        }
3047
3048        match im_font_id {
3049            ImmediateFontId::Resolved((font_family_hash, font_id)) => {
3050                // nothing to do, font is already added,
3051                // just insert the missing font instances
3052                for font_size in font_sizes.iter() {
3053                    insert_font_instances!(*font_family_hash, *font_id, *font_size);
3054                }
3055            }
3056            ImmediateFontId::Unresolved(style_font_families) => {
3057                // If the font is already loaded during the current frame,
3058                // do not attempt to load it again
3059                //
3060                // This prevents duplicated loading for fonts in different orders, i.e.
3061                // - vec!["Times New Roman", "serif"] and
3062                // - vec!["sans", "Times New Roman"]
3063                // ... will resolve to the same font instead of creating two fonts
3064
3065                // If there is no font key, that means there's also no font instances
3066                let mut font_family_hash = None;
3067                let font_families_hash = StyleFontFamiliesHash::new(style_font_families.as_ref());
3068
3069                // Find the first font that can be loaded and parsed
3070                'inner: for family in style_font_families.as_ref().iter() {
3071                    let current_family_hash = StyleFontFamilyHash::new(&family);
3072
3073                    if let Some(font_id) = renderer_resources.font_id_map.get(&current_family_hash)
3074                    {
3075                        // font key already exists
3076                        for font_size in font_sizes {
3077                            insert_font_instances!(current_family_hash, *font_id, *font_size);
3078                        }
3079                        continue 'outer;
3080                    }
3081
3082                    let font_ref = match family {
3083                        StyleFontFamily::Ref(r) => r.clone(), // Clone the FontRef
3084                        other => {
3085                            // Load and parse the font
3086                            let font_data = match (font_source_load_fn)(&other, fc_cache) {
3087                                Some(s) => s,
3088                                None => continue 'inner,
3089                            };
3090
3091                            let font_ref = match (parse_font_fn)(font_data) {
3092                                Some(s) => s,
3093                                None => continue 'inner,
3094                            };
3095
3096                            font_ref
3097                        }
3098                    };
3099
3100                    // font loaded properly
3101                    font_family_hash = Some((current_family_hash, font_ref));
3102                    break 'inner;
3103                }
3104
3105                let (font_family_hash, font_ref) = match font_family_hash {
3106                    None => continue 'outer, // No font could be loaded, try again next frame
3107                    Some(s) => s,
3108                };
3109
3110                // Generate a new font key, store the mapping between hash and font key
3111                let font_key = FontKey::unique(id_namespace);
3112                let add_font_msg = AddFontMsg::Font(font_key, font_family_hash, font_ref);
3113
3114                renderer_resources
3115                    .font_id_map
3116                    .insert(font_family_hash, font_key);
3117                renderer_resources
3118                    .font_families_map
3119                    .insert(font_families_hash, font_family_hash);
3120                resource_updates.push((font_family_hash, add_font_msg));
3121
3122                // Insert font sizes for the newly generated font key
3123                for font_size in font_sizes {
3124                    insert_font_instances!(font_family_hash, font_key, *font_size);
3125                }
3126            }
3127        }
3128    }
3129
3130    resource_updates
3131}
3132
3133/// Given the images of the current frame, returns `AddImage`s of
3134/// which image keys are currently not in the `current_registered_images` and
3135/// need to be added.
3136///
3137/// Deleting images can only be done after the entire frame has finished drawing,
3138/// otherwise (if removing images would happen after every DOM) we'd constantly
3139/// add-and-remove images after every IFrameCallback, which would cause a lot of
3140/// I/O waiting.
3141#[allow(unused_variables)]
3142pub fn build_add_image_resource_updates(
3143    renderer_resources: &RendererResources,
3144    id_namespace: IdNamespace,
3145    epoch: Epoch,
3146    document_id: &DocumentId,
3147    images_in_dom: &FastBTreeSet<ImageRef>,
3148    insert_into_active_gl_textures: GlStoreImageFn,
3149) -> Vec<(ImageRefHash, AddImageMsg)> {
3150    images_in_dom
3151        .iter()
3152        .filter_map(|image_ref| {
3153            let image_ref_hash = image_ref.get_hash();
3154
3155            if renderer_resources
3156                .currently_registered_images
3157                .contains_key(&image_ref_hash)
3158            {
3159                return None;
3160            }
3161
3162            // NOTE: The image_ref.clone() is a shallow clone,
3163            // does not actually clone the data
3164            match image_ref.get_data() {
3165                DecodedImage::Gl(texture) => {
3166                    let descriptor = texture.get_descriptor();
3167                    let key = ImageKey::unique(id_namespace);
3168                    // NOTE: The texture is not really cloned here,
3169                    let external_image_id =
3170                        (insert_into_active_gl_textures)(*document_id, epoch, texture.clone());
3171                    Some((
3172                        image_ref_hash,
3173                        AddImageMsg(AddImage {
3174                            key,
3175                            data: ImageData::External(ExternalImageData {
3176                                id: external_image_id,
3177                                channel_index: 0,
3178                                image_type: ExternalImageType::TextureHandle(
3179                                    ImageBufferKind::Texture2D,
3180                                ),
3181                            }),
3182                            descriptor,
3183                            tiling: None,
3184                        }),
3185                    ))
3186                }
3187                DecodedImage::Raw((descriptor, data)) => {
3188                    let key = ImageKey::unique(id_namespace);
3189                    Some((
3190                        image_ref_hash,
3191                        AddImageMsg(AddImage {
3192                            key,
3193                            data: data.clone(), // deep-copy except in the &'static case
3194                            descriptor: descriptor.clone(), /* deep-copy, but struct is not very
3195                                                 * large */
3196                            tiling: None,
3197                        }),
3198                    ))
3199                }
3200                DecodedImage::NullImage {
3201                    width: _,
3202                    height: _,
3203                    format: _,
3204                    tag: _,
3205                } => None,
3206                DecodedImage::Callback(_) => None, /* Texture callbacks are handled after layout
3207                                                    * is done */
3208            }
3209        })
3210        .collect()
3211}
3212
3213fn add_gl_resources(
3214    renderer_resources: &mut RendererResources,
3215    all_resource_updates: &mut Vec<ResourceUpdate>,
3216    add_image_resources: Vec<(ImageRefHash, ImageRefHash, AddImageMsg)>,
3217) {
3218    let add_image_resources = add_image_resources
3219        .into_iter()
3220        // use the callback_imageref_hash for indexing!
3221        .map(|(_, k, v)| (k, v))
3222        .collect::<Vec<_>>();
3223
3224    add_resources(
3225        renderer_resources,
3226        all_resource_updates,
3227        Vec::new(),
3228        add_image_resources,
3229    );
3230}
3231
3232/// Submits the `AddFont`, `AddFontInstance` and `AddImage` resources to the RenderApi.
3233/// Extends `currently_registered_images` and `currently_registered_fonts` by the
3234/// `last_frame_image_keys` and `last_frame_font_keys`, so that we don't lose track of
3235/// what font and image keys are currently in the API.
3236pub fn add_resources(
3237    renderer_resources: &mut RendererResources,
3238    all_resource_updates: &mut Vec<ResourceUpdate>,
3239    add_font_resources: Vec<(StyleFontFamilyHash, AddFontMsg)>,
3240    add_image_resources: Vec<(ImageRefHash, AddImageMsg)>,
3241) {
3242    all_resource_updates.extend(
3243        add_font_resources
3244            .iter()
3245            .map(|(_, f)| f.into_resource_update()),
3246    );
3247    all_resource_updates.extend(
3248        add_image_resources
3249            .iter()
3250            .map(|(_, i)| i.into_resource_update()),
3251    );
3252
3253    for (image_ref_hash, add_image_msg) in add_image_resources.iter() {
3254        renderer_resources.currently_registered_images.insert(
3255            *image_ref_hash,
3256            ResolvedImage {
3257                key: add_image_msg.0.key,
3258                descriptor: add_image_msg.0.descriptor,
3259            },
3260        );
3261    }
3262
3263    for (_, add_font_msg) in add_font_resources {
3264        use self::AddFontMsg::*;
3265        match add_font_msg {
3266            Font(fk, _hash, font_ref) => {
3267                renderer_resources
3268                    .currently_registered_fonts
3269                    .entry(fk)
3270                    .or_insert_with(|| (font_ref, FastHashMap::default()));
3271            }
3272            Instance(fi, size) => {
3273                if let Some((_, instances)) = renderer_resources
3274                    .currently_registered_fonts
3275                    .get_mut(&fi.font_key)
3276                {
3277                    instances.insert(size, fi.key);
3278                }
3279            }
3280        }
3281    }
3282}