Skip to main content

azul_core/
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
10use azul_css::{
11    format_rust_code::GetHash,
12    props::basic::{
13        pixel::DEFAULT_FONT_SIZE, ColorU, FloatValue, FontRef, LayoutRect, LayoutSize,
14        StyleFontFamily, StyleFontFamilyVec, StyleFontSize,
15    },
16    system::SystemStyle,
17    AzString, F32Vec, LayoutDebugMessage, OptionI32, StringVec, U16Vec, U32Vec, U8Vec,
18};
19use rust_fontconfig::FcFontCache;
20
21// Re-export Core* callback types for public use
22pub use crate::callbacks::{
23    CoreImageCallback, CoreRenderImageCallback, CoreRenderImageCallbackType,
24};
25use crate::{
26    callbacks::IFrameCallback,
27    dom::{DomId, NodeData, NodeType},
28    geom::{LogicalPosition, LogicalRect, LogicalSize},
29    gl::{OptionGlContextPtr, Texture},
30    hit_test::DocumentId,
31    id::NodeId,
32    prop_cache::CssPropertyCache,
33    refany::RefAny,
34    styled_dom::{
35        NodeHierarchyItemId, StyleFontFamiliesHash, StyleFontFamilyHash, StyledDom, StyledNodeState,
36    },
37    ui_solver::GlyphInstance,
38    window::OptionChar,
39    FastBTreeSet, FastHashMap,
40};
41
42#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
43#[repr(C)]
44pub struct DpiScaleFactor {
45    pub inner: FloatValue,
46}
47
48impl DpiScaleFactor {
49    pub fn new(f: f32) -> Self {
50        Self {
51            inner: FloatValue::new(f),
52        }
53    }
54}
55
56/// Determines what happens when all application windows are closed
57#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
58#[repr(C)]
59pub enum AppTerminationBehavior {
60    /// Return control to main() when all windows are closed (if platform supports it).
61    /// On macOS, this exits the NSApplication run loop and returns to main().
62    /// This is useful if you want to clean up resources or restart the event loop.
63    ReturnToMain,
64    /// Keep the application running even when all windows are closed.
65    /// This is the standard macOS behavior (app stays in dock until explicitly quit).
66    RunForever,
67    /// Immediately terminate the process when all windows are closed.
68    /// Calls std::process::exit(0).
69    EndProcess,
70}
71
72impl Default for AppTerminationBehavior {
73    fn default() -> Self {
74        // Default: End the process when all windows close (cross-platform behavior)
75        AppTerminationBehavior::EndProcess
76    }
77}
78
79/// A named font bundled with the application (name + raw bytes).
80/// The name is used to reference the font in CSS (e.g. `font-family: "MyFont"`).
81#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
82#[repr(C)]
83pub struct NamedFont {
84    /// The font family name to use in CSS (e.g. "Roboto", "MyCustomFont")
85    pub name: AzString,
86    /// Raw font file bytes (TTF, OTF, etc.)
87    pub bytes: U8Vec,
88}
89
90impl_option!(
91    NamedFont,
92    OptionNamedFont,
93    copy = false,
94    [Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash]
95);
96
97impl NamedFont {
98    pub fn new(name: AzString, bytes: U8Vec) -> Self {
99        Self { name, bytes }
100    }
101}
102
103impl_vec!(NamedFont, NamedFontVec, NamedFontVecDestructor, NamedFontVecDestructorType, NamedFontVecSlice, OptionNamedFont);
104impl_vec_mut!(NamedFont, NamedFontVec);
105impl_vec_debug!(NamedFont, NamedFontVec);
106impl_vec_partialeq!(NamedFont, NamedFontVec);
107impl_vec_eq!(NamedFont, NamedFontVec);
108impl_vec_partialord!(NamedFont, NamedFontVec);
109impl_vec_ord!(NamedFont, NamedFontVec);
110impl_vec_hash!(NamedFont, NamedFontVec);
111impl_vec_clone!(NamedFont, NamedFontVec, NamedFontVecDestructor);
112
113/// Configuration for how fonts should be loaded at app startup.
114#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
115#[repr(C, u8)]
116pub enum FontLoadingConfig {
117    /// Load all system fonts (default behavior, can be slow on systems with many fonts)
118    LoadAllSystemFonts,
119    /// Only load fonts for specific families (faster startup).
120    /// Generic families like "sans-serif" are automatically expanded to OS-specific fonts.
121    LoadOnlyFamilies(StringVec),
122    /// Don't load any system fonts, only use bundled fonts
123    BundledFontsOnly,
124}
125
126impl Default for FontLoadingConfig {
127    fn default() -> Self {
128        FontLoadingConfig::LoadAllSystemFonts
129    }
130}
131
132/// Mock environment for CSS evaluation.
133/// 
134/// Allows overriding auto-detected system properties for testing and development.
135/// Any field set to `None` will use the auto-detected value.
136/// Any field set to `Some(...)` will override the auto-detected value.
137/// 
138/// # Example
139/// ```rust
140/// use azul_css::dynamic_selector::{OsCondition, ThemeCondition, OsVersion};
141/// 
142/// // Mock a Linux dark theme environment on any platform
143/// let mock = CssMockEnvironment {
144///     os: Some(OsCondition::Linux),
145///     theme: Some(ThemeCondition::Dark),
146///     ..Default::default()
147/// };
148/// 
149/// // Mock Windows XP for retro testing
150/// let mock = CssMockEnvironment {
151///     os: Some(OsCondition::Windows),
152///     os_version: Some(OsVersion::WIN_XP),
153///     ..Default::default()
154/// };
155/// ```
156#[derive(Debug, Clone, Default)]
157#[repr(C)]
158pub struct CssMockEnvironment {
159    /// Override the detected operating system
160    pub os: azul_css::dynamic_selector::OptionOsCondition,
161    /// Override the detected OS version
162    pub os_version: azul_css::dynamic_selector::OptionOsVersion,
163    /// Override the Linux desktop environment (only applies when os = Linux)
164    pub desktop_env: azul_css::dynamic_selector::OptionLinuxDesktopEnv,
165    /// Override the current theme (light/dark)
166    pub theme: azul_css::dynamic_selector::OptionThemeCondition,
167    /// Override the current language (BCP 47 tag, e.g., "de-DE", "en-US")
168    pub language: azul_css::OptionString,
169    /// Override the reduced motion preference
170    pub prefers_reduced_motion: azul_css::OptionBool,
171    /// Override the high contrast preference
172    pub prefers_high_contrast: azul_css::OptionBool,
173    /// Override viewport dimensions (for @media queries)
174    /// Only use for testing - normally set by window size
175    pub viewport_width: azul_css::OptionF32,
176    pub viewport_height: azul_css::OptionF32,
177}
178
179impl CssMockEnvironment {
180    /// Create a mock for Linux environment
181    pub fn linux() -> Self {
182        Self {
183            os: azul_css::dynamic_selector::OptionOsCondition::Some(azul_css::dynamic_selector::OsCondition::Linux),
184            ..Default::default()
185        }
186    }
187    
188    /// Create a mock for Windows environment
189    pub fn windows() -> Self {
190        Self {
191            os: azul_css::dynamic_selector::OptionOsCondition::Some(azul_css::dynamic_selector::OsCondition::Windows),
192            ..Default::default()
193        }
194    }
195    
196    /// Create a mock for macOS environment
197    pub fn macos() -> Self {
198        Self {
199            os: azul_css::dynamic_selector::OptionOsCondition::Some(azul_css::dynamic_selector::OsCondition::MacOS),
200            ..Default::default()
201        }
202    }
203    
204    /// Create a mock for dark theme
205    pub fn dark_theme() -> Self {
206        Self {
207            theme: azul_css::dynamic_selector::OptionThemeCondition::Some(azul_css::dynamic_selector::ThemeCondition::Dark),
208            ..Default::default()
209        }
210    }
211    
212    /// Create a mock for light theme
213    pub fn light_theme() -> Self {
214        Self {
215            theme: azul_css::dynamic_selector::OptionThemeCondition::Some(azul_css::dynamic_selector::ThemeCondition::Light),
216            ..Default::default()
217        }
218    }
219    
220    /// Apply this mock to a DynamicSelectorContext
221    pub fn apply_to(&self, ctx: &mut azul_css::dynamic_selector::DynamicSelectorContext) {
222        if let azul_css::dynamic_selector::OptionOsCondition::Some(os) = self.os {
223            ctx.os = os;
224        }
225        if let azul_css::dynamic_selector::OptionOsVersion::Some(os_version) = self.os_version {
226            ctx.os_version = os_version;
227        }
228        if let azul_css::dynamic_selector::OptionLinuxDesktopEnv::Some(de) = self.desktop_env {
229            ctx.desktop_env = azul_css::dynamic_selector::OptionLinuxDesktopEnv::Some(de);
230        }
231        if let azul_css::dynamic_selector::OptionThemeCondition::Some(ref theme) = self.theme {
232            ctx.theme = theme.clone();
233        }
234        if let azul_css::OptionString::Some(ref lang) = self.language {
235            ctx.language = lang.clone();
236        }
237        if let azul_css::OptionBool::Some(reduced) = self.prefers_reduced_motion {
238            ctx.prefers_reduced_motion = if reduced {
239                azul_css::dynamic_selector::BoolCondition::True
240            } else {
241                azul_css::dynamic_selector::BoolCondition::False
242            };
243        }
244        if let azul_css::OptionBool::Some(high_contrast) = self.prefers_high_contrast {
245            ctx.prefers_high_contrast = if high_contrast {
246                azul_css::dynamic_selector::BoolCondition::True
247            } else {
248                azul_css::dynamic_selector::BoolCondition::False
249            };
250        }
251        if let azul_css::OptionF32::Some(w) = self.viewport_width {
252            ctx.viewport_width = w;
253        }
254        if let azul_css::OptionF32::Some(h) = self.viewport_height {
255            ctx.viewport_height = h;
256        }
257    }
258}
259
260impl_option!(
261    CssMockEnvironment,
262    OptionCssMockEnvironment,
263    copy = false,
264    [Debug, Clone]
265);
266
267/// Configuration for optional features, such as whether to enable logging or panic hooks
268#[derive(Debug, Clone)]
269#[repr(C)]
270pub struct AppConfig {
271    /// If enabled, logs error and info messages.
272    ///
273    /// Default is `LevelFilter::Error` to log all errors by default
274    pub log_level: AppLogLevel,
275    /// If the app crashes / panics, a window with a message box pops up.
276    /// Setting this to `false` disables the popup box.
277    pub enable_visual_panic_hook: bool,
278    /// If this is set to `true` (the default), a backtrace + error information
279    /// gets logged to stdout and the logging file (only if logging is enabled).
280    pub enable_logging_on_panic: bool,
281    /// (STUB) Whether keyboard navigation should be enabled (default: true).
282    /// Currently not implemented.
283    pub enable_tab_navigation: bool,
284    /// Determines what happens when all windows are closed.
285    /// Default: EndProcess (terminate when last window closes).
286    pub termination_behavior: AppTerminationBehavior,
287    /// Icon provider for the application.
288    /// Register icons here before calling App::run().
289    /// Each window will clone this provider (cheap, Arc-based).
290    pub icon_provider: crate::icon::IconProviderHandle,
291    /// Fonts bundled with the application.
292    /// These fonts are loaded into memory and take priority over system fonts.
293    pub bundled_fonts: NamedFontVec,
294    /// Configuration for how system fonts should be loaded.
295    /// Default: LoadAllSystemFonts (scan all system fonts at startup)
296    pub font_loading: FontLoadingConfig,
297    /// Optional mock environment for CSS evaluation.
298    /// 
299    /// When set, this overrides the auto-detected system properties (OS, theme, etc.)
300    /// for CSS @-rules and dynamic selectors. This is useful for:
301    /// - Testing OS-specific styles on a different platform
302    /// - Screenshot testing with consistent environment
303    /// - Previewing how the app looks on different systems
304    /// 
305    /// Default: None (use auto-detected system properties)
306    pub mock_css_environment: OptionCssMockEnvironment,
307    /// System style detected at startup (theme, colors, fonts, etc.)
308    /// 
309    /// This is detected once at `AppConfig::create()` and passed to all windows.
310    /// You can override this after creation to use a custom system style,
311    /// for example to test how your app looks on a different platform.
312    pub system_style: SystemStyle,
313}
314
315impl AppConfig {
316    pub fn create() -> Self {
317        let log_level = AppLogLevel::Error;
318        let icon_provider = crate::icon::IconProviderHandle::new();
319        let bundled_fonts = NamedFontVec::from_const_slice(&[]);
320        let font_loading = FontLoadingConfig::default();
321        let system_style = SystemStyle::detect();
322        Self {
323            log_level,
324            enable_visual_panic_hook: false,
325            enable_logging_on_panic: true,
326            enable_tab_navigation: true,
327            termination_behavior: AppTerminationBehavior::default(),
328            icon_provider,
329            bundled_fonts,
330            font_loading,
331            mock_css_environment: OptionCssMockEnvironment::None,
332            system_style,
333        }
334    }
335    
336    /// Create config with a mock CSS environment for testing
337    /// 
338    /// This allows you to simulate how your app would look on a different OS,
339    /// with a different theme, language, or accessibility settings.
340    /// 
341    /// # Example
342    /// ```rust
343    /// let config = AppConfig::create()
344    ///     .with_mock_environment(CssMockEnvironment {
345    ///         os: OptionOsCondition::Some(OsCondition::Linux),
346    ///         theme: OptionThemeCondition::Some(ThemeCondition::Dark),
347    ///         ..Default::default()
348    ///     });
349    /// ```
350    pub fn with_mock_environment(mut self, env: CssMockEnvironment) -> Self {
351        self.mock_css_environment = OptionCssMockEnvironment::Some(env);
352        self
353    }
354}
355
356impl Default for AppConfig {
357    fn default() -> Self {
358        Self::create()
359    }
360}
361
362#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
363#[repr(C)]
364pub enum AppLogLevel {
365    Off,
366    Error,
367    Warn,
368    Info,
369    Debug,
370    Trace,
371}
372
373#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
374pub struct PrimitiveFlags {
375    /// The CSS backface-visibility property (yes, it can be really granular)
376    pub is_backface_visible: bool,
377    /// If set, this primitive represents a scroll bar container
378    pub is_scrollbar_container: bool,
379    /// If set, this primitive represents a scroll bar thumb
380    pub is_scrollbar_thumb: bool,
381    /// This is used as a performance hint - this primitive may be promoted to a native
382    /// compositor surface under certain (implementation specific) conditions. This
383    /// is typically used for large videos, and canvas elements.
384    pub prefer_compositor_surface: bool,
385    /// If set, this primitive can be passed directly to the compositor via its
386    /// ExternalImageId, and the compositor will use the native image directly.
387    /// Used as a further extension on top of PREFER_COMPOSITOR_SURFACE.
388    pub supports_external_compositor_surface: bool,
389}
390
391/// Metadata (but not storage) describing an image In WebRender.
392#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
393#[repr(C)]
394pub struct ImageDescriptor {
395    /// Format of the image data.
396    pub format: RawImageFormat,
397    /// Width and height of the image data, in pixels.
398    pub width: usize,
399    pub height: usize,
400    /// The number of bytes from the start of one row to the next. If non-None,
401    /// `compute_stride` will return this value, otherwise it returns
402    /// `width * bpp`. Different source of images have different alignment
403    /// constraints for rows, so the stride isn't always equal to width * bpp.
404    pub stride: OptionI32,
405    /// Offset in bytes of the first pixel of this image in its backing buffer.
406    /// This is used for tiling, wherein WebRender extracts chunks of input images
407    /// in order to cache, manipulate, and render them individually. This offset
408    /// tells the texture upload machinery where to find the bytes to upload for
409    /// this tile. Non-tiled images generally set this to zero.
410    pub offset: i32,
411    /// Various bool flags related to this descriptor.
412    pub flags: ImageDescriptorFlags,
413}
414
415/// Various flags that are part of an image descriptor.
416#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
417#[repr(C)]
418pub struct ImageDescriptorFlags {
419    /// Whether this image is opaque, or has an alpha channel. Avoiding blending
420    /// for opaque surfaces is an important optimization.
421    pub is_opaque: bool,
422    /// Whether to allow the driver to automatically generate mipmaps. If images
423    /// are already downscaled appropriately, mipmap generation can be wasted
424    /// work, and cause performance problems on some cards/drivers.
425    ///
426    /// See https://github.com/servo/webrender/pull/2555/
427    pub allow_mipmaps: bool,
428}
429
430#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
431pub struct IdNamespace(pub u32);
432
433impl ::core::fmt::Display for IdNamespace {
434    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
435        write!(f, "IdNamespace({})", self.0)
436    }
437}
438
439impl ::core::fmt::Debug for IdNamespace {
440    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
441        write!(f, "{}", self)
442    }
443}
444
445#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
446#[repr(C)]
447pub enum RawImageFormat {
448    R8,
449    RG8,
450    RGB8,
451    RGBA8,
452    R16,
453    RG16,
454    RGB16,
455    RGBA16,
456    BGR8,
457    BGRA8,
458    RGBF32,
459    RGBAF32,
460}
461
462// NOTE: starts at 1 (0 = DUMMY)
463static IMAGE_KEY: AtomicU32 = AtomicU32::new(1);
464static FONT_KEY: AtomicU32 = AtomicU32::new(0);
465static FONT_INSTANCE_KEY: AtomicU32 = AtomicU32::new(0);
466
467#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
468pub struct ImageKey {
469    pub namespace: IdNamespace,
470    pub key: u32,
471}
472
473impl ImageKey {
474    pub const DUMMY: Self = Self {
475        namespace: IdNamespace(0),
476        key: 0,
477    };
478
479    pub fn unique(render_api_namespace: IdNamespace) -> Self {
480        Self {
481            namespace: render_api_namespace,
482            key: IMAGE_KEY.fetch_add(1, AtomicOrdering::SeqCst),
483        }
484    }
485}
486
487#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
488pub struct FontKey {
489    pub namespace: IdNamespace,
490    pub key: u32,
491}
492
493impl FontKey {
494    pub fn unique(render_api_namespace: IdNamespace) -> Self {
495        Self {
496            namespace: render_api_namespace,
497            key: FONT_KEY.fetch_add(1, AtomicOrdering::SeqCst),
498        }
499    }
500}
501
502#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
503pub struct FontInstanceKey {
504    pub namespace: IdNamespace,
505    pub key: u32,
506}
507
508impl FontInstanceKey {
509    pub fn unique(render_api_namespace: IdNamespace) -> Self {
510        Self {
511            namespace: render_api_namespace,
512            key: FONT_INSTANCE_KEY.fetch_add(1, AtomicOrdering::SeqCst),
513        }
514    }
515}
516
517// NOTE: This type should NOT be exposed in the API!
518// The only public functions are the constructors
519#[derive(Debug)]
520pub enum DecodedImage {
521    /// Image that has a reserved key, but no data, i.e it is not yet rendered
522    /// or there was an error during rendering
523    NullImage {
524        width: usize,
525        height: usize,
526        format: RawImageFormat,
527        /// Sometimes images need to be tagged with extra data
528        tag: Vec<u8>,
529    },
530    // OpenGl texture
531    Gl(Texture),
532    // Image backed by CPU-rendered pixels
533    Raw((ImageDescriptor, ImageData)),
534    // Same as `Texture`, but rendered AFTER the layout has been done
535    Callback(CoreImageCallback),
536    // YUVImage(...)
537    // VulkanSurface(...)
538    // MetalSurface(...),
539    // DirectXSurface(...)
540}
541
542#[derive(Debug)]
543#[repr(C)]
544pub struct ImageRef {
545    /// Shared pointer to an opaque implementation of the decoded image
546    pub data: *const DecodedImage,
547    /// How many copies does this image have (if 0, the font data will be deleted on drop)
548    pub copies: *const AtomicUsize,
549    pub run_destructor: bool,
550}
551
552impl ImageRef {
553    pub fn get_hash(&self) -> ImageRefHash {
554        image_ref_get_hash(self)
555    }
556}
557
558#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Hash, Ord, Eq)]
559#[repr(C)]
560pub struct ImageRefHash {
561    pub inner: usize,
562}
563
564impl_option!(
565    ImageRef,
566    OptionImageRef,
567    copy = false,
568    [Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash]
569);
570
571impl ImageRef {
572    /// If *copies = 1, returns the internal image data
573    pub fn into_inner(self) -> Option<DecodedImage> {
574        unsafe {
575            if self.copies.as_ref().map(|m| m.load(AtomicOrdering::SeqCst)) == Some(1) {
576                let data = Box::from_raw(self.data as *mut DecodedImage);
577                let _ = Box::from_raw(self.copies as *mut AtomicUsize);
578                core::mem::forget(self); // do not run the destructor
579                Some(*data)
580            } else {
581                None
582            }
583        }
584    }
585
586    pub fn get_data<'a>(&'a self) -> &'a DecodedImage {
587        unsafe { &*self.data }
588    }
589
590    pub fn get_image_callback<'a>(&'a self) -> Option<&'a CoreImageCallback> {
591        if unsafe { self.copies.as_ref().map(|m| m.load(AtomicOrdering::SeqCst)) != Some(1) } {
592            return None; // not safe
593        }
594
595        match unsafe { &*self.data } {
596            DecodedImage::Callback(gl_texture_callback) => Some(gl_texture_callback),
597            _ => None,
598        }
599    }
600
601    pub fn get_image_callback_mut<'a>(&'a mut self) -> Option<&'a mut CoreImageCallback> {
602        if unsafe { self.copies.as_ref().map(|m| m.load(AtomicOrdering::SeqCst)) != Some(1) } {
603            return None; // not safe
604        }
605
606        match unsafe { &mut *(self.data as *mut DecodedImage) } {
607            DecodedImage::Callback(gl_texture_callback) => Some(gl_texture_callback),
608            _ => None,
609        }
610    }
611
612    /// In difference to the default shallow copy, creates a new image ref
613    pub fn deep_copy(&self) -> Self {
614        let new_data = match self.get_data() {
615            DecodedImage::NullImage {
616                width,
617                height,
618                format,
619                tag,
620            } => DecodedImage::NullImage {
621                width: *width,
622                height: *height,
623                format: *format,
624                tag: tag.clone(),
625            },
626            // NOTE: textures cannot be deep-copied yet (since the OpenGL calls for that
627            // are missing from the trait), so calling clone() on a GL texture will result in an
628            // empty image
629            DecodedImage::Gl(tex) => DecodedImage::NullImage {
630                width: tex.size.width as usize,
631                height: tex.size.height as usize,
632                format: tex.format,
633                tag: Vec::new(),
634            },
635            // WARNING: the data may still be a U8Vec<'static> - the data may still not be
636            // actually cloned. The data only gets cloned on a write operation
637            DecodedImage::Raw((descriptor, data)) => {
638                DecodedImage::Raw((descriptor.clone(), data.clone()))
639            }
640            DecodedImage::Callback(cb) => DecodedImage::Callback(cb.clone()),
641        };
642
643        Self::new(new_data)
644    }
645
646    pub fn is_null_image(&self) -> bool {
647        match self.get_data() {
648            DecodedImage::NullImage { .. } => true,
649            _ => false,
650        }
651    }
652
653    pub fn is_gl_texture(&self) -> bool {
654        match self.get_data() {
655            DecodedImage::Gl(_) => true,
656            _ => false,
657        }
658    }
659
660    pub fn is_raw_image(&self) -> bool {
661        match self.get_data() {
662            DecodedImage::Raw((_, _)) => true,
663            _ => false,
664        }
665    }
666
667    pub fn is_callback(&self) -> bool {
668        match self.get_data() {
669            DecodedImage::Callback(_) => true,
670            _ => false,
671        }
672    }
673
674    // OptionRawImage
675    pub fn get_rawimage(&self) -> Option<RawImage> {
676        match self.get_data() {
677            DecodedImage::Raw((image_descriptor, image_data)) => Some(RawImage {
678                pixels: match image_data {
679                    ImageData::Raw(shared_data) => {
680                        // Clone the SharedRawImageData (increments ref count),
681                        // then try to extract or convert to U8Vec
682                        let data_clone = shared_data.clone();
683                        if let Some(u8vec) = data_clone.into_inner() {
684                            RawImageData::U8(u8vec)
685                        } else {
686                            // Multiple references exist, need to copy the data
687                            RawImageData::U8(shared_data.as_ref().to_vec().into())
688                        }
689                    }
690                    ImageData::External(_) => return None,
691                },
692                width: image_descriptor.width,
693                height: image_descriptor.height,
694                premultiplied_alpha: true,
695                data_format: image_descriptor.format,
696                tag: Vec::new().into(),
697            }),
698            _ => None,
699        }
700    }
701
702    /// Get raw bytes from the image as a slice
703    /// Returns None if this is not a Raw image or if it's an External image
704    pub fn get_bytes(&self) -> Option<&[u8]> {
705        match self.get_data() {
706            DecodedImage::Raw((_, image_data)) => match image_data {
707                ImageData::Raw(shared_data) => Some(shared_data.as_ref()),
708                ImageData::External(_) => None,
709            },
710            _ => None,
711        }
712    }
713
714    /// Get a pointer to the raw bytes for debugging/profiling purposes
715    /// Returns a unique pointer for this ImageRef's data
716    pub fn get_bytes_ptr(&self) -> *const u8 {
717        match self.get_data() {
718            DecodedImage::Raw((_, image_data)) => match image_data {
719                ImageData::Raw(shared_data) => shared_data.as_ptr(),
720                ImageData::External(_) => core::ptr::null(),
721            },
722            _ => core::ptr::null(),
723        }
724    }
725
726    /// NOTE: returns (0, 0) for a Callback
727    pub fn get_size(&self) -> LogicalSize {
728        match self.get_data() {
729            DecodedImage::NullImage { width, height, .. } => {
730                LogicalSize::new(*width as f32, *height as f32)
731            }
732            DecodedImage::Gl(tex) => {
733                LogicalSize::new(tex.size.width as f32, tex.size.height as f32)
734            }
735            DecodedImage::Raw((image_descriptor, _)) => LogicalSize::new(
736                image_descriptor.width as f32,
737                image_descriptor.height as f32,
738            ),
739            DecodedImage::Callback(_) => LogicalSize::new(0.0, 0.0),
740        }
741    }
742
743    pub fn null_image(width: usize, height: usize, format: RawImageFormat, tag: Vec<u8>) -> Self {
744        Self::new(DecodedImage::NullImage {
745            width,
746            height,
747            format,
748            tag,
749        })
750    }
751
752    pub fn callback<C: Into<CoreRenderImageCallback>>(callback: C, data: RefAny) -> Self {
753        Self::new(DecodedImage::Callback(CoreImageCallback {
754            callback: callback.into(),
755            refany: data,
756        }))
757    }
758
759    pub fn new_rawimage(image_data: RawImage) -> Option<Self> {
760        let (image_data, image_descriptor) = image_data.into_loaded_image_source()?;
761        Some(Self::new(DecodedImage::Raw((image_descriptor, image_data))))
762    }
763
764    pub fn new_gltexture(texture: Texture) -> Self {
765        Self::new(DecodedImage::Gl(texture))
766    }
767
768    fn new(data: DecodedImage) -> Self {
769        Self {
770            data: Box::into_raw(Box::new(data)),
771            copies: Box::into_raw(Box::new(AtomicUsize::new(1))),
772            run_destructor: true,
773        }
774    }
775
776    // pub fn new_vulkan(...) -> Self
777}
778
779unsafe impl Send for ImageRef {}
780unsafe impl Sync for ImageRef {}
781
782impl PartialEq for ImageRef {
783    fn eq(&self, rhs: &Self) -> bool {
784        self.data as usize == rhs.data as usize
785    }
786}
787
788impl PartialOrd for ImageRef {
789    fn partial_cmp(&self, other: &Self) -> Option<::core::cmp::Ordering> {
790        Some((self.data as usize).cmp(&(other.data as usize)))
791    }
792}
793
794impl Ord for ImageRef {
795    fn cmp(&self, other: &Self) -> core::cmp::Ordering {
796        let self_data = self.data as usize;
797        let other_data = other.data as usize;
798        self_data.cmp(&other_data)
799    }
800}
801
802impl Eq for ImageRef {}
803
804impl Hash for ImageRef {
805    fn hash<H>(&self, state: &mut H)
806    where
807        H: Hasher,
808    {
809        let self_data = self.data as usize;
810        self_data.hash(state)
811    }
812}
813
814impl Clone for ImageRef {
815    fn clone(&self) -> Self {
816        unsafe {
817            self.copies
818                .as_ref()
819                .map(|m| m.fetch_add(1, AtomicOrdering::SeqCst));
820        }
821        Self {
822            data: self.data,     // copy the pointer
823            copies: self.copies, // copy the pointer
824            run_destructor: true,
825        }
826    }
827}
828
829impl Drop for ImageRef {
830    fn drop(&mut self) {
831        self.run_destructor = false;
832        unsafe {
833            let copies = unsafe { (*self.copies).fetch_sub(1, AtomicOrdering::SeqCst) };
834            if copies == 1 {
835                let _ = Box::from_raw(self.data as *mut DecodedImage);
836                let _ = Box::from_raw(self.copies as *mut AtomicUsize);
837            }
838        }
839    }
840}
841
842pub fn image_ref_get_hash(ir: &ImageRef) -> ImageRefHash {
843    ImageRefHash {
844        inner: ir.data as usize,
845    }
846}
847
848/// Convert a stable ImageRefHash directly to an ImageKey.
849/// Since ImageKey is just a (namespace, u32) pair, we can directly use
850/// the hash value as the key. This avoids the need for a separate mapping table.
851pub fn image_ref_hash_to_image_key(hash: ImageRefHash, namespace: IdNamespace) -> ImageKey {
852    ImageKey {
853        namespace,
854        key: hash.inner as u32,
855    }
856}
857
858pub fn font_ref_get_hash(fr: &FontRef) -> u64 {
859    fr.get_hash()
860}
861
862/// Stores the resources for the application, souch as fonts, images and cached
863/// texts, also clipboard strings
864///
865/// Images and fonts can be references across window contexts (not yet tested,
866/// but should work).
867#[derive(Debug)]
868pub struct ImageCache {
869    /// The AzString is the string used in the CSS, i.e. url("my_image") = "my_image" -> ImageId(4)
870    ///
871    /// NOTE: This is the only map that is modifiable by the user and that has to be manually
872    /// managed all other maps are library-internal only and automatically delete their
873    /// resources once they aren't needed anymore
874    pub image_id_map: FastHashMap<AzString, ImageRef>,
875}
876
877impl Default for ImageCache {
878    fn default() -> Self {
879        Self {
880            image_id_map: FastHashMap::default(),
881        }
882    }
883}
884
885impl ImageCache {
886    pub fn new() -> Self {
887        Self::default()
888    }
889
890    // -- ImageId cache
891
892    pub fn add_css_image_id(&mut self, css_id: AzString, image: ImageRef) {
893        self.image_id_map.insert(css_id, image);
894    }
895
896    pub fn get_css_image_id(&self, css_id: &AzString) -> Option<&ImageRef> {
897        self.image_id_map.get(css_id)
898    }
899
900    pub fn delete_css_image_id(&mut self, css_id: &AzString) {
901        self.image_id_map.remove(css_id);
902    }
903}
904
905/// What type of image is this?
906#[derive(Debug, Copy, Clone, PartialEq)]
907pub enum ImageType {
908    /// CSS background-image
909    Background,
910    /// DOM node content
911    Content,
912    /// DOM node clip-mask
913    ClipMask,
914}
915
916#[derive(Debug, Copy, Clone, PartialEq)]
917pub struct ResolvedImage {
918    pub key: ImageKey,
919    pub descriptor: ImageDescriptor,
920}
921
922/// Represents an exclusion area for handling floats
923#[derive(Debug, Clone, PartialEq, PartialOrd)]
924pub struct TextExclusionArea {
925    pub rect: LogicalRect,
926    pub side: ExclusionSide,
927}
928
929/// Side of the exclusion area
930#[derive(Debug, Clone, PartialEq, PartialOrd)]
931pub enum ExclusionSide {
932    Left,
933    Right,
934    Both,
935    None,
936}
937
938/// Trait for accessing font resources
939pub trait RendererResourcesTrait: core::fmt::Debug {
940    /// Get a font family hash from a font families hash
941    fn get_font_family(
942        &self,
943        style_font_families_hash: &StyleFontFamiliesHash,
944    ) -> Option<&StyleFontFamilyHash>;
945
946    /// Get a font key from a font family hash
947    fn get_font_key(&self, style_font_family_hash: &StyleFontFamilyHash) -> Option<&FontKey>;
948
949    /// Get a registered font and its instances from a font key
950    fn get_registered_font(
951        &self,
952        font_key: &FontKey,
953    ) -> Option<&(FontRef, FastHashMap<(Au, DpiScaleFactor), FontInstanceKey>)>;
954
955    /// Get image information from an image hash
956    fn get_image(&self, hash: &ImageRefHash) -> Option<&ResolvedImage>;
957
958    /// Update an image descriptor for an existing image hash
959    fn update_image(
960        &mut self,
961        image_ref_hash: &ImageRefHash,
962        descriptor: crate::resources::ImageDescriptor,
963    );
964}
965
966// Implementation for the original RendererResources struct
967impl RendererResourcesTrait for RendererResources {
968    fn get_font_family(
969        &self,
970        style_font_families_hash: &StyleFontFamiliesHash,
971    ) -> Option<&StyleFontFamilyHash> {
972        self.font_families_map.get(style_font_families_hash)
973    }
974
975    fn get_font_key(&self, style_font_family_hash: &StyleFontFamilyHash) -> Option<&FontKey> {
976        self.font_id_map.get(style_font_family_hash)
977    }
978
979    fn get_registered_font(
980        &self,
981        font_key: &FontKey,
982    ) -> Option<&(FontRef, FastHashMap<(Au, DpiScaleFactor), FontInstanceKey>)> {
983        self.currently_registered_fonts.get(font_key)
984    }
985
986    fn get_image(&self, hash: &ImageRefHash) -> Option<&ResolvedImage> {
987        self.currently_registered_images.get(hash)
988    }
989
990    fn update_image(
991        &mut self,
992        image_ref_hash: &ImageRefHash,
993        descriptor: crate::resources::ImageDescriptor,
994    ) {
995        if let Some(s) = self.currently_registered_images.get_mut(image_ref_hash) {
996            s.descriptor = descriptor;
997        }
998    }
999}
1000
1001/// Renderer resources that manage font, image and font instance keys.
1002/// RendererResources are local to each renderer / window, since the
1003/// keys are not shared across renderers
1004///
1005/// The resources are automatically managed, meaning that they each new frame
1006/// (signified by start_frame_gc and end_frame_gc)
1007pub struct RendererResources {
1008    /// All image keys currently active in the RenderApi
1009    pub currently_registered_images: FastHashMap<ImageRefHash, ResolvedImage>,
1010    /// Reverse lookup: ImageKey -> ImageRefHash for display list translation
1011    pub image_key_map: FastHashMap<ImageKey, ImageRefHash>,
1012    /// All font keys currently active in the RenderApi
1013    pub currently_registered_fonts:
1014        FastHashMap<FontKey, (FontRef, FastHashMap<(Au, DpiScaleFactor), FontInstanceKey>)>,
1015    /// Fonts registered on the last frame
1016    ///
1017    /// Fonts differ from images in that regard that we can't immediately
1018    /// delete them on a new frame, instead we have to delete them on "current frame + 1"
1019    /// This is because when the frame is being built, we do not know
1020    /// whether the font will actually be successfully loaded
1021    pub last_frame_registered_fonts:
1022        FastHashMap<FontKey, FastHashMap<(Au, DpiScaleFactor), FontInstanceKey>>,
1023    /// Map from the calculated families vec (["Arial", "Helvectia"])
1024    /// to the final loaded font that could be loaded
1025    /// (in this case "Arial" on Windows and "Helvetica" on Mac,
1026    /// because the fonts are loaded in fallback-order)
1027    pub font_families_map: FastHashMap<StyleFontFamiliesHash, StyleFontFamilyHash>,
1028    /// Same as AzString -> ImageId, but for fonts, i.e. "Roboto" -> FontId(9)
1029    pub font_id_map: FastHashMap<StyleFontFamilyHash, FontKey>,
1030    /// Direct mapping from font hash (from FontRef) to FontKey
1031    /// TODO: This should become part of SharedFontRegistry
1032    pub font_hash_map: FastHashMap<u64, FontKey>,
1033}
1034
1035impl fmt::Debug for RendererResources {
1036    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1037        write!(
1038            f,
1039            "RendererResources {{
1040                currently_registered_images: {:#?},
1041                currently_registered_fonts: {:#?},
1042                font_families_map: {:#?},
1043                font_id_map: {:#?},
1044            }}",
1045            self.currently_registered_images.keys().collect::<Vec<_>>(),
1046            self.currently_registered_fonts.keys().collect::<Vec<_>>(),
1047            self.font_families_map.keys().collect::<Vec<_>>(),
1048            self.font_id_map.keys().collect::<Vec<_>>(),
1049        )
1050    }
1051}
1052
1053impl Default for RendererResources {
1054    fn default() -> Self {
1055        Self {
1056            currently_registered_images: FastHashMap::default(),
1057            image_key_map: FastHashMap::default(),
1058            currently_registered_fonts: FastHashMap::default(),
1059            last_frame_registered_fonts: FastHashMap::default(),
1060            font_families_map: FastHashMap::default(),
1061            font_id_map: FastHashMap::default(),
1062            font_hash_map: FastHashMap::default(),
1063        }
1064    }
1065}
1066
1067impl RendererResources {
1068    pub fn get_renderable_font_data(
1069        &self,
1070        font_instance_key: &FontInstanceKey,
1071    ) -> Option<(&FontRef, Au, DpiScaleFactor)> {
1072        self.currently_registered_fonts
1073            .iter()
1074            .find_map(|(font_key, (font_ref, instances))| {
1075                instances.iter().find_map(|((au, dpi), instance_key)| {
1076                    if *instance_key == *font_instance_key {
1077                        Some((font_ref, *au, *dpi))
1078                    } else {
1079                        None
1080                    }
1081                })
1082            })
1083    }
1084
1085    pub fn get_image(&self, hash: &ImageRefHash) -> Option<&ResolvedImage> {
1086        self.currently_registered_images.get(hash)
1087    }
1088
1089    pub fn get_font_family(
1090        &self,
1091        style_font_families_hash: &StyleFontFamiliesHash,
1092    ) -> Option<&StyleFontFamilyHash> {
1093        self.font_families_map.get(style_font_families_hash)
1094    }
1095
1096    pub fn get_font_key(&self, style_font_family_hash: &StyleFontFamilyHash) -> Option<&FontKey> {
1097        self.font_id_map.get(style_font_family_hash)
1098    }
1099
1100    pub fn get_registered_font(
1101        &self,
1102        font_key: &FontKey,
1103    ) -> Option<&(FontRef, FastHashMap<(Au, DpiScaleFactor), FontInstanceKey>)> {
1104        self.currently_registered_fonts.get(font_key)
1105    }
1106
1107    pub fn update_image(&mut self, image_ref_hash: &ImageRefHash, descriptor: ImageDescriptor) {
1108        if let Some(s) = self.currently_registered_images.get_mut(image_ref_hash) {
1109            s.descriptor = descriptor; // key stays the same, only descriptor changes
1110        }
1111    }
1112
1113    pub fn get_font_instance_key_for_text(
1114        &self,
1115        font_size_px: f32,
1116        css_property_cache: &CssPropertyCache,
1117        node_data: &NodeData,
1118        node_id: &NodeId,
1119        styled_node_state: &StyledNodeState,
1120        dpi_scale: f32,
1121    ) -> Option<FontInstanceKey> {
1122        // Convert font size to StyleFontSize
1123        let font_size = StyleFontSize {
1124            inner: azul_css::props::basic::PixelValue::const_px(font_size_px as isize),
1125        };
1126
1127        // Convert to application units
1128        let font_size_au = font_size_to_au(font_size);
1129
1130        // Create DPI scale factor
1131        let dpi_scale_factor = DpiScaleFactor {
1132            inner: FloatValue::new(dpi_scale),
1133        };
1134
1135        // Get font family
1136        let font_family =
1137            css_property_cache.get_font_id_or_default(node_data, node_id, styled_node_state);
1138
1139        // Calculate hash and lookup font instance key
1140        let font_families_hash = StyleFontFamiliesHash::new(font_family.as_ref());
1141
1142        self.get_font_instance_key(&font_families_hash, font_size_au, dpi_scale_factor)
1143    }
1144
1145    pub fn get_font_instance_key(
1146        &self,
1147        font_families_hash: &StyleFontFamiliesHash,
1148        font_size_au: Au,
1149        dpi_scale: DpiScaleFactor,
1150    ) -> Option<FontInstanceKey> {
1151        let font_family_hash = self.get_font_family(font_families_hash)?;
1152        let font_key = self.get_font_key(font_family_hash)?;
1153        let (_, instances) = self.get_registered_font(font_key)?;
1154        instances.get(&(font_size_au, dpi_scale)).copied()
1155    }
1156
1157    // Delete all font family hashes that do not have a font key anymore
1158    fn remove_font_families_with_zero_references(&mut self) {
1159        let font_family_to_delete = self
1160            .font_id_map
1161            .iter()
1162            .filter_map(|(font_family, font_key)| {
1163                if !self.currently_registered_fonts.contains_key(font_key) {
1164                    Some(font_family.clone())
1165                } else {
1166                    None
1167                }
1168            })
1169            .collect::<Vec<_>>();
1170
1171        for f in font_family_to_delete {
1172            self.font_id_map.remove(&f); // font key does not exist anymore
1173        }
1174
1175        let font_families_to_delete = self
1176            .font_families_map
1177            .iter()
1178            .filter_map(|(font_families, font_family)| {
1179                if !self.font_id_map.contains_key(font_family) {
1180                    Some(font_families.clone())
1181                } else {
1182                    None
1183                }
1184            })
1185            .collect::<Vec<_>>();
1186
1187        for f in font_families_to_delete {
1188            self.font_families_map.remove(&f); // font family does not exist anymore
1189        }
1190    }
1191}
1192
1193// Result returned from rerender_image_callback() - should be used as:
1194//
1195// ```rust
1196// txn.update_image(
1197//     wr_translate_image_key(key),
1198//     wr_translate_image_descriptor(descriptor),
1199//     wr_translate_image_data(data),
1200//     &WrImageDirtyRect::All,
1201// );
1202// ```
1203#[derive(Debug, Clone)]
1204pub struct UpdateImageResult {
1205    pub key_to_update: ImageKey,
1206    pub new_descriptor: ImageDescriptor,
1207    pub new_image_data: ImageData,
1208}
1209
1210#[derive(Debug, Default)]
1211pub struct GlTextureCache {
1212    pub solved_textures:
1213        BTreeMap<DomId, BTreeMap<NodeId, (ImageKey, ImageDescriptor, ExternalImageId)>>,
1214    pub hashes: BTreeMap<(DomId, NodeId, ImageRefHash), ImageRefHash>,
1215}
1216
1217// necessary so the display list can be built in parallel
1218unsafe impl Send for GlTextureCache {}
1219
1220impl GlTextureCache {
1221    /// Initializes an empty cache
1222    pub fn empty() -> Self {
1223        Self {
1224            solved_textures: BTreeMap::new(),
1225            hashes: BTreeMap::new(),
1226        }
1227    }
1228
1229    /// Updates a given texture
1230    ///
1231    /// This is called when a texture needs to be re-rendered (e.g., on resize or animation frame).
1232    /// It updates the texture in the WebRender external image cache and updates the internal
1233    /// descriptor to reflect the new size.
1234    ///
1235    /// # Arguments
1236    ///
1237    /// * `dom_id` - The DOM ID containing the texture
1238    /// * `node_id` - The node ID of the image element
1239    /// * `document_id` - The WebRender document ID
1240    /// * `epoch` - The current frame epoch
1241    /// * `new_texture` - The new texture to use
1242    /// * `insert_into_active_gl_textures_fn` - Function to insert the texture into the cache
1243    ///
1244    /// # Returns
1245    ///
1246    /// The ExternalImageId if successful, None if the texture wasn't found in the cache
1247    pub fn update_texture(
1248        &mut self,
1249        dom_id: DomId,
1250        node_id: NodeId,
1251        document_id: DocumentId,
1252        epoch: Epoch,
1253        new_texture: Texture,
1254        insert_into_active_gl_textures_fn: &GlStoreImageFn,
1255    ) -> Option<ExternalImageId> {
1256        let new_descriptor = new_texture.get_descriptor();
1257        let di_map = self.solved_textures.get_mut(&dom_id)?;
1258        let entry = di_map.get_mut(&node_id)?;
1259
1260        // Update the descriptor
1261        entry.1 = new_descriptor;
1262
1263        // Insert the new texture and get its external image ID
1264        let external_image_id =
1265            (insert_into_active_gl_textures_fn)(document_id, epoch, new_texture);
1266
1267        // Update the external image ID in the cache
1268        entry.2 = external_image_id;
1269
1270        Some(external_image_id)
1271    }
1272}
1273
1274macro_rules! unique_id {
1275    ($struct_name:ident, $counter_name:ident) => {
1276        #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)]
1277        #[repr(C)]
1278        pub struct $struct_name {
1279            pub id: usize,
1280        }
1281
1282        impl $struct_name {
1283            pub fn unique() -> Self {
1284                Self {
1285                    id: $counter_name.fetch_add(1, AtomicOrdering::SeqCst),
1286                }
1287            }
1288        }
1289    };
1290}
1291
1292// NOTE: the property key is unique across transform, color and opacity properties
1293static PROPERTY_KEY_COUNTER: AtomicUsize = AtomicUsize::new(0);
1294unique_id!(TransformKey, PROPERTY_KEY_COUNTER);
1295unique_id!(ColorKey, PROPERTY_KEY_COUNTER);
1296unique_id!(OpacityKey, PROPERTY_KEY_COUNTER);
1297
1298static IMAGE_ID_COUNTER: AtomicUsize = AtomicUsize::new(0);
1299unique_id!(ImageId, IMAGE_ID_COUNTER);
1300static FONT_ID_COUNTER: AtomicUsize = AtomicUsize::new(0);
1301unique_id!(FontId, FONT_ID_COUNTER);
1302
1303#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
1304#[repr(C)]
1305pub struct ImageMask {
1306    pub image: ImageRef,
1307    pub rect: LogicalRect,
1308    pub repeat: bool,
1309}
1310
1311impl_option!(
1312    ImageMask,
1313    OptionImageMask,
1314    copy = false,
1315    [Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash]
1316);
1317
1318#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
1319pub enum ImmediateFontId {
1320    Resolved((StyleFontFamilyHash, FontKey)),
1321    Unresolved(StyleFontFamilyVec),
1322}
1323
1324#[derive(Debug, Clone, PartialEq, PartialOrd)]
1325#[repr(C, u8)]
1326pub enum RawImageData {
1327    // 8-bit image data
1328    U8(U8Vec),
1329    // 16-bit image data
1330    U16(U16Vec),
1331    // HDR image data
1332    F32(F32Vec),
1333}
1334
1335impl RawImageData {
1336    pub fn get_u8_vec_ref(&self) -> Option<&U8Vec> {
1337        match self {
1338            RawImageData::U8(v) => Some(v),
1339            _ => None,
1340        }
1341    }
1342
1343    pub fn get_u16_vec_ref(&self) -> Option<&U16Vec> {
1344        match self {
1345            RawImageData::U16(v) => Some(v),
1346            _ => None,
1347        }
1348    }
1349
1350    pub fn get_f32_vec_ref(&self) -> Option<&F32Vec> {
1351        match self {
1352            RawImageData::F32(v) => Some(v),
1353            _ => None,
1354        }
1355    }
1356
1357    fn get_u8_vec(self) -> Option<U8Vec> {
1358        match self {
1359            RawImageData::U8(v) => Some(v),
1360            _ => None,
1361        }
1362    }
1363
1364    fn get_u16_vec(self) -> Option<U16Vec> {
1365        match self {
1366            RawImageData::U16(v) => Some(v),
1367            _ => None,
1368        }
1369    }
1370}
1371
1372#[derive(Debug, Clone, PartialEq, PartialOrd)]
1373#[repr(C)]
1374pub struct RawImage {
1375    pub pixels: RawImageData,
1376    pub width: usize,
1377    pub height: usize,
1378    pub premultiplied_alpha: bool,
1379    pub data_format: RawImageFormat,
1380    pub tag: U8Vec,
1381}
1382
1383impl RawImage {
1384    /// Returns a null / empty image
1385    pub fn null_image() -> Self {
1386        Self {
1387            pixels: RawImageData::U8(Vec::new().into()),
1388            width: 0,
1389            height: 0,
1390            premultiplied_alpha: true,
1391            data_format: RawImageFormat::BGRA8,
1392            tag: Vec::new().into(),
1393        }
1394    }
1395
1396    /// Allocates a width * height, single-channel mask, used for drawing CPU image masks
1397    pub fn allocate_mask(size: LayoutSize) -> Self {
1398        Self {
1399            pixels: RawImageData::U8(
1400                vec![0; size.width.max(0) as usize * size.height.max(0) as usize].into(),
1401            ),
1402            width: size.width as usize,
1403            height: size.height as usize,
1404            premultiplied_alpha: true,
1405            data_format: RawImageFormat::R8,
1406            tag: Vec::new().into(),
1407        }
1408    }
1409
1410    /// Encodes a RawImage as BGRA8 bytes and premultiplies it if the alpha is not premultiplied
1411    ///
1412    /// Returns None if the width * height * BPP does not match
1413    ///
1414    /// TODO: autovectorization fails spectacularly, need to manually optimize!
1415    pub fn into_loaded_image_source(self) -> Option<(ImageData, ImageDescriptor)> {
1416        // From webrender/wrench
1417        // These are slow. Gecko's gfx/2d/Swizzle.cpp has better versions
1418        #[inline(always)]
1419        fn premultiply_alpha(array: &mut [u8]) {
1420            if array.len() != 4 {
1421                return;
1422            }
1423            let a = u32::from(array[3]);
1424            array[0] = (((array[0] as u32 * a) + 128) / 255) as u8;
1425            array[1] = (((array[1] as u32 * a) + 128) / 255) as u8;
1426            array[2] = (((array[2] as u32 * a) + 128) / 255) as u8;
1427        }
1428
1429        #[inline(always)]
1430        fn normalize_u16(i: u16) -> u8 {
1431            ((core::u16::MAX as f32 / i as f32) * core::u8::MAX as f32) as u8
1432        }
1433
1434        let RawImage {
1435            width,
1436            height,
1437            pixels,
1438            mut data_format,
1439            premultiplied_alpha,
1440            tag,
1441        } = self;
1442
1443        const FOUR_BPP: usize = 4;
1444        const TWO_CHANNELS: usize = 2;
1445        const THREE_CHANNELS: usize = 3;
1446        const FOUR_CHANNELS: usize = 4;
1447
1448        let mut is_opaque = true;
1449
1450        let expected_len = width * height;
1451
1452        let bytes: U8Vec = match data_format {
1453            RawImageFormat::R8 => {
1454                // just return the vec
1455                let pixels = pixels.get_u8_vec()?;
1456
1457                if pixels.len() != expected_len {
1458                    return None;
1459                }
1460
1461                let pixels_ref = pixels.as_ref();
1462                let mut px = vec![0; pixels_ref.len() * 4];
1463                for (i, r) in pixels_ref.iter().enumerate() {
1464                    px[i * 4 + 0] = *r;
1465                    px[i * 4 + 1] = *r;
1466                    px[i * 4 + 2] = *r;
1467                    px[i * 4 + 3] = 0xff;
1468                }
1469
1470                data_format = RawImageFormat::BGRA8;
1471                px.into()
1472            }
1473            RawImageFormat::RG8 => {
1474                let pixels = pixels.get_u8_vec()?;
1475
1476                if pixels.len() != expected_len * TWO_CHANNELS {
1477                    return None;
1478                }
1479
1480                let mut px = vec![0; expected_len * FOUR_BPP];
1481
1482                // TODO: premultiply alpha!
1483                // TODO: check that this function is SIMD optimized
1484                for (pixel_index, greyalpha) in
1485                    pixels.as_ref().chunks_exact(TWO_CHANNELS).enumerate()
1486                {
1487                    let grey = greyalpha[0];
1488                    let alpha = greyalpha[1];
1489
1490                    if alpha != 255 {
1491                        is_opaque = false;
1492                    }
1493
1494                    px[pixel_index * FOUR_BPP] = grey;
1495                    px[(pixel_index * FOUR_BPP) + 1] = grey;
1496                    px[(pixel_index * FOUR_BPP) + 2] = grey;
1497                    px[(pixel_index * FOUR_BPP) + 3] = alpha;
1498                }
1499
1500                data_format = RawImageFormat::BGRA8;
1501                px.into()
1502            }
1503            RawImageFormat::RGB8 => {
1504                let pixels = pixels.get_u8_vec()?;
1505
1506                if pixels.len() != expected_len * THREE_CHANNELS {
1507                    return None;
1508                }
1509
1510                let mut px = vec![0; expected_len * FOUR_BPP];
1511
1512                // TODO: check that this function is SIMD optimized
1513                for (pixel_index, rgb) in pixels.as_ref().chunks_exact(THREE_CHANNELS).enumerate() {
1514                    let red = rgb[0];
1515                    let green = rgb[1];
1516                    let blue = rgb[2];
1517
1518                    px[pixel_index * FOUR_BPP] = blue;
1519                    px[(pixel_index * FOUR_BPP) + 1] = green;
1520                    px[(pixel_index * FOUR_BPP) + 2] = red;
1521                    px[(pixel_index * FOUR_BPP) + 3] = 0xff;
1522                }
1523
1524                data_format = RawImageFormat::BGRA8;
1525                px.into()
1526            }
1527            RawImageFormat::RGBA8 => {
1528                let mut pixels: Vec<u8> = pixels.get_u8_vec()?.into_library_owned_vec();
1529
1530                if pixels.len() != expected_len * FOUR_CHANNELS {
1531                    return None;
1532                }
1533
1534                // TODO: check that this function is SIMD optimized
1535                // no extra allocation necessary, but swizzling
1536                if premultiplied_alpha {
1537                    for rgba in pixels.chunks_exact_mut(4) {
1538                        let (r, gba) = rgba.split_first_mut()?;
1539                        core::mem::swap(r, gba.get_mut(1)?);
1540                        let a = rgba.get_mut(3)?;
1541                        if *a != 255 {
1542                            is_opaque = false;
1543                        }
1544                    }
1545                } else {
1546                    for rgba in pixels.chunks_exact_mut(4) {
1547                        // RGBA => BGRA
1548                        let (r, gba) = rgba.split_first_mut()?;
1549                        core::mem::swap(r, gba.get_mut(1)?);
1550                        let a = rgba.get_mut(3)?;
1551                        if *a != 255 {
1552                            is_opaque = false;
1553                        }
1554                        premultiply_alpha(rgba); // <-
1555                    }
1556                }
1557
1558                data_format = RawImageFormat::BGRA8;
1559                pixels.into()
1560            }
1561            RawImageFormat::R16 => {
1562                let pixels = pixels.get_u16_vec()?;
1563
1564                if pixels.len() != expected_len {
1565                    return None;
1566                }
1567
1568                let mut px = vec![0; expected_len * FOUR_BPP];
1569
1570                // TODO: check that this function is SIMD optimized
1571                for (pixel_index, grey_u16) in pixels.as_ref().iter().enumerate() {
1572                    let grey_u8 = normalize_u16(*grey_u16);
1573                    px[pixel_index * FOUR_BPP] = grey_u8;
1574                    px[(pixel_index * FOUR_BPP) + 1] = grey_u8;
1575                    px[(pixel_index * FOUR_BPP) + 2] = grey_u8;
1576                    px[(pixel_index * FOUR_BPP) + 3] = 0xff;
1577                }
1578
1579                data_format = RawImageFormat::BGRA8;
1580                px.into()
1581            }
1582            RawImageFormat::RG16 => {
1583                let pixels = pixels.get_u16_vec()?;
1584
1585                if pixels.len() != expected_len * TWO_CHANNELS {
1586                    return None;
1587                }
1588
1589                let mut px = vec![0; expected_len * FOUR_BPP];
1590
1591                // TODO: check that this function is SIMD optimized
1592                for (pixel_index, greyalpha) in
1593                    pixels.as_ref().chunks_exact(TWO_CHANNELS).enumerate()
1594                {
1595                    let grey_u8 = normalize_u16(greyalpha[0]);
1596                    let alpha_u8 = normalize_u16(greyalpha[1]);
1597
1598                    if alpha_u8 != 255 {
1599                        is_opaque = false;
1600                    }
1601
1602                    px[pixel_index * FOUR_BPP] = grey_u8;
1603                    px[(pixel_index * FOUR_BPP) + 1] = grey_u8;
1604                    px[(pixel_index * FOUR_BPP) + 2] = grey_u8;
1605                    px[(pixel_index * FOUR_BPP) + 3] = alpha_u8;
1606                }
1607
1608                data_format = RawImageFormat::BGRA8;
1609                px.into()
1610            }
1611            RawImageFormat::RGB16 => {
1612                let pixels = pixels.get_u16_vec()?;
1613
1614                if pixels.len() != expected_len * THREE_CHANNELS {
1615                    return None;
1616                }
1617
1618                let mut px = vec![0; expected_len * FOUR_BPP];
1619
1620                // TODO: check that this function is SIMD optimized
1621                for (pixel_index, rgb) in pixels.as_ref().chunks_exact(THREE_CHANNELS).enumerate() {
1622                    let red_u8 = normalize_u16(rgb[0]);
1623                    let green_u8 = normalize_u16(rgb[1]);
1624                    let blue_u8 = normalize_u16(rgb[2]);
1625
1626                    px[pixel_index * FOUR_BPP] = blue_u8;
1627                    px[(pixel_index * FOUR_BPP) + 1] = green_u8;
1628                    px[(pixel_index * FOUR_BPP) + 2] = red_u8;
1629                    px[(pixel_index * FOUR_BPP) + 3] = 0xff;
1630                }
1631
1632                data_format = RawImageFormat::BGRA8;
1633                px.into()
1634            }
1635            RawImageFormat::RGBA16 => {
1636                let pixels = pixels.get_u16_vec()?;
1637
1638                if pixels.len() != expected_len * FOUR_CHANNELS {
1639                    return None;
1640                }
1641
1642                let mut px = vec![0; expected_len * FOUR_BPP];
1643
1644                // TODO: check that this function is SIMD optimized
1645                if premultiplied_alpha {
1646                    for (pixel_index, rgba) in
1647                        pixels.as_ref().chunks_exact(FOUR_CHANNELS).enumerate()
1648                    {
1649                        let red_u8 = normalize_u16(rgba[0]);
1650                        let green_u8 = normalize_u16(rgba[1]);
1651                        let blue_u8 = normalize_u16(rgba[2]);
1652                        let alpha_u8 = normalize_u16(rgba[3]);
1653
1654                        if alpha_u8 != 255 {
1655                            is_opaque = false;
1656                        }
1657
1658                        px[pixel_index * FOUR_BPP] = blue_u8;
1659                        px[(pixel_index * FOUR_BPP) + 1] = green_u8;
1660                        px[(pixel_index * FOUR_BPP) + 2] = red_u8;
1661                        px[(pixel_index * FOUR_BPP) + 3] = alpha_u8;
1662                    }
1663                } else {
1664                    for (pixel_index, rgba) in
1665                        pixels.as_ref().chunks_exact(FOUR_CHANNELS).enumerate()
1666                    {
1667                        let red_u8 = normalize_u16(rgba[0]);
1668                        let green_u8 = normalize_u16(rgba[1]);
1669                        let blue_u8 = normalize_u16(rgba[2]);
1670                        let alpha_u8 = normalize_u16(rgba[3]);
1671
1672                        if alpha_u8 != 255 {
1673                            is_opaque = false;
1674                        }
1675
1676                        px[pixel_index * FOUR_BPP] = blue_u8;
1677                        px[(pixel_index * FOUR_BPP) + 1] = green_u8;
1678                        px[(pixel_index * FOUR_BPP) + 2] = red_u8;
1679                        px[(pixel_index * FOUR_BPP) + 3] = alpha_u8;
1680                        premultiply_alpha(
1681                            &mut px
1682                                [(pixel_index * FOUR_BPP)..((pixel_index * FOUR_BPP) + FOUR_BPP)],
1683                        );
1684                    }
1685                }
1686
1687                data_format = RawImageFormat::BGRA8;
1688                px.into()
1689            }
1690            RawImageFormat::BGR8 => {
1691                let pixels = pixels.get_u8_vec()?;
1692
1693                if pixels.len() != expected_len * THREE_CHANNELS {
1694                    return None;
1695                }
1696
1697                let mut px = vec![0; expected_len * FOUR_BPP];
1698
1699                // TODO: check that this function is SIMD optimized
1700                for (pixel_index, bgr) in pixels.as_ref().chunks_exact(THREE_CHANNELS).enumerate() {
1701                    let blue = bgr[0];
1702                    let green = bgr[1];
1703                    let red = bgr[2];
1704
1705                    px[pixel_index * FOUR_BPP] = blue;
1706                    px[(pixel_index * FOUR_BPP) + 1] = green;
1707                    px[(pixel_index * FOUR_BPP) + 2] = red;
1708                    px[(pixel_index * FOUR_BPP) + 3] = 0xff;
1709                }
1710
1711                data_format = RawImageFormat::BGRA8;
1712                px.into()
1713            }
1714            RawImageFormat::BGRA8 => {
1715                if premultiplied_alpha {
1716                    // DO NOT CLONE THE IMAGE HERE!
1717                    let pixels = pixels.get_u8_vec()?;
1718
1719                    is_opaque = pixels
1720                        .as_ref()
1721                        .chunks_exact(FOUR_CHANNELS)
1722                        .all(|bgra| bgra[3] == 255);
1723
1724                    pixels
1725                } else {
1726                    let mut pixels: Vec<u8> = pixels.get_u8_vec()?.into_library_owned_vec();
1727
1728                    if pixels.len() != expected_len * FOUR_BPP {
1729                        return None;
1730                    }
1731
1732                    for bgra in pixels.chunks_exact_mut(FOUR_CHANNELS) {
1733                        if bgra[3] != 255 {
1734                            is_opaque = false;
1735                        }
1736                        premultiply_alpha(bgra);
1737                    }
1738                    data_format = RawImageFormat::BGRA8;
1739                    pixels.into()
1740                }
1741            }
1742            RawImageFormat::RGBF32 => {
1743                let pixels = pixels.get_f32_vec_ref()?;
1744
1745                if pixels.len() != expected_len * THREE_CHANNELS {
1746                    return None;
1747                }
1748
1749                let mut px = vec![0; expected_len * FOUR_BPP];
1750
1751                // TODO: check that this function is SIMD optimized
1752                for (pixel_index, rgb) in pixels.as_ref().chunks_exact(THREE_CHANNELS).enumerate() {
1753                    let red_u8 = (rgb[0] * 255.0) as u8;
1754                    let green_u8 = (rgb[1] * 255.0) as u8;
1755                    let blue_u8 = (rgb[2] * 255.0) as u8;
1756
1757                    px[pixel_index * FOUR_BPP] = blue_u8;
1758                    px[(pixel_index * FOUR_BPP) + 1] = green_u8;
1759                    px[(pixel_index * FOUR_BPP) + 2] = red_u8;
1760                    px[(pixel_index * FOUR_BPP) + 3] = 0xff;
1761                }
1762
1763                data_format = RawImageFormat::BGRA8;
1764                px.into()
1765            }
1766            RawImageFormat::RGBAF32 => {
1767                let pixels = pixels.get_f32_vec_ref()?;
1768
1769                if pixels.len() != expected_len * FOUR_CHANNELS {
1770                    return None;
1771                }
1772
1773                let mut px = vec![0; expected_len * FOUR_BPP];
1774
1775                // TODO: check that this function is SIMD optimized
1776                if premultiplied_alpha {
1777                    for (pixel_index, rgba) in
1778                        pixels.as_ref().chunks_exact(FOUR_CHANNELS).enumerate()
1779                    {
1780                        let red_u8 = (rgba[0] * 255.0) as u8;
1781                        let green_u8 = (rgba[1] * 255.0) as u8;
1782                        let blue_u8 = (rgba[2] * 255.0) as u8;
1783                        let alpha_u8 = (rgba[3] * 255.0) as u8;
1784
1785                        if alpha_u8 != 255 {
1786                            is_opaque = false;
1787                        }
1788
1789                        px[pixel_index * FOUR_BPP] = blue_u8;
1790                        px[(pixel_index * FOUR_BPP) + 1] = green_u8;
1791                        px[(pixel_index * FOUR_BPP) + 2] = red_u8;
1792                        px[(pixel_index * FOUR_BPP) + 3] = alpha_u8;
1793                    }
1794                } else {
1795                    for (pixel_index, rgba) in
1796                        pixels.as_ref().chunks_exact(FOUR_CHANNELS).enumerate()
1797                    {
1798                        let red_u8 = (rgba[0] * 255.0) as u8;
1799                        let green_u8 = (rgba[1] * 255.0) as u8;
1800                        let blue_u8 = (rgba[2] * 255.0) as u8;
1801                        let alpha_u8 = (rgba[3] * 255.0) as u8;
1802
1803                        if alpha_u8 != 255 {
1804                            is_opaque = false;
1805                        }
1806
1807                        px[pixel_index * FOUR_BPP] = blue_u8;
1808                        px[(pixel_index * FOUR_BPP) + 1] = green_u8;
1809                        px[(pixel_index * FOUR_BPP) + 2] = red_u8;
1810                        px[(pixel_index * FOUR_BPP) + 3] = alpha_u8;
1811                        premultiply_alpha(
1812                            &mut px
1813                                [(pixel_index * FOUR_BPP)..((pixel_index * FOUR_BPP) + FOUR_BPP)],
1814                        );
1815                    }
1816                }
1817
1818                data_format = RawImageFormat::BGRA8;
1819                px.into()
1820            }
1821        };
1822
1823        let image_data = ImageData::Raw(SharedRawImageData::new(bytes));
1824        let image_descriptor = ImageDescriptor {
1825            format: data_format,
1826            width,
1827            height,
1828            offset: 0,
1829            stride: None.into(),
1830            flags: ImageDescriptorFlags {
1831                is_opaque,
1832                allow_mipmaps: true,
1833            },
1834        };
1835
1836        Some((image_data, image_descriptor))
1837    }
1838}
1839
1840impl_option!(
1841    RawImage,
1842    OptionRawImage,
1843    copy = false,
1844    [Debug, Clone, PartialEq, PartialOrd]
1845);
1846
1847pub fn font_size_to_au(font_size: StyleFontSize) -> Au {
1848    Au::from_px(font_size.inner.to_pixels_internal(0.0, DEFAULT_FONT_SIZE))
1849}
1850
1851pub type FontInstanceFlags = u32;
1852
1853// Common flags
1854pub const FONT_INSTANCE_FLAG_SYNTHETIC_BOLD: u32 = 1 << 1;
1855pub const FONT_INSTANCE_FLAG_EMBEDDED_BITMAPS: u32 = 1 << 2;
1856pub const FONT_INSTANCE_FLAG_SUBPIXEL_BGR: u32 = 1 << 3;
1857pub const FONT_INSTANCE_FLAG_TRANSPOSE: u32 = 1 << 4;
1858pub const FONT_INSTANCE_FLAG_FLIP_X: u32 = 1 << 5;
1859pub const FONT_INSTANCE_FLAG_FLIP_Y: u32 = 1 << 6;
1860pub const FONT_INSTANCE_FLAG_SUBPIXEL_POSITION: u32 = 1 << 7;
1861
1862// Windows flags
1863pub const FONT_INSTANCE_FLAG_FORCE_GDI: u32 = 1 << 16;
1864
1865// Mac flags
1866pub const FONT_INSTANCE_FLAG_FONT_SMOOTHING: u32 = 1 << 16;
1867
1868// FreeType flags
1869pub const FONT_INSTANCE_FLAG_FORCE_AUTOHINT: u32 = 1 << 16;
1870pub const FONT_INSTANCE_FLAG_NO_AUTOHINT: u32 = 1 << 17;
1871pub const FONT_INSTANCE_FLAG_VERTICAL_LAYOUT: u32 = 1 << 18;
1872pub const FONT_INSTANCE_FLAG_LCD_VERTICAL: u32 = 1 << 19;
1873
1874#[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash)]
1875pub struct GlyphOptions {
1876    pub render_mode: FontRenderMode,
1877    pub flags: FontInstanceFlags,
1878}
1879
1880#[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash)]
1881pub enum FontRenderMode {
1882    Mono,
1883    Alpha,
1884    Subpixel,
1885}
1886
1887#[cfg(target_arch = "wasm32")]
1888#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash)]
1889pub struct FontInstancePlatformOptions {
1890    // empty for now
1891}
1892
1893#[cfg(target_os = "windows")]
1894#[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash)]
1895pub struct FontInstancePlatformOptions {
1896    pub gamma: u16,
1897    pub contrast: u8,
1898    pub cleartype_level: u8,
1899}
1900
1901#[cfg(target_os = "macos")]
1902#[derive(Default, Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash)]
1903pub struct FontInstancePlatformOptions {
1904    pub unused: u32,
1905}
1906
1907#[cfg(target_os = "linux")]
1908#[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash)]
1909pub struct FontInstancePlatformOptions {
1910    pub lcd_filter: FontLCDFilter,
1911    pub hinting: FontHinting,
1912}
1913
1914#[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash)]
1915pub enum FontHinting {
1916    None,
1917    Mono,
1918    Light,
1919    Normal,
1920    LCD,
1921}
1922
1923#[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash)]
1924pub enum FontLCDFilter {
1925    None,
1926    Default,
1927    Light,
1928    Legacy,
1929}
1930
1931impl Default for FontLCDFilter {
1932    fn default() -> Self {
1933        FontLCDFilter::Default
1934    }
1935}
1936
1937#[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash)]
1938pub struct FontInstanceOptions {
1939    pub render_mode: FontRenderMode,
1940    pub flags: FontInstanceFlags,
1941    pub bg_color: ColorU,
1942    /// When bg_color.a is != 0 and render_mode is FontRenderMode::Subpixel,
1943    /// the text will be rendered with bg_color.r/g/b as an opaque estimated
1944    /// background color.
1945    pub synthetic_italics: SyntheticItalics,
1946}
1947
1948impl Default for FontInstanceOptions {
1949    fn default() -> FontInstanceOptions {
1950        FontInstanceOptions {
1951            render_mode: FontRenderMode::Subpixel,
1952            flags: 0,
1953            bg_color: ColorU::TRANSPARENT,
1954            synthetic_italics: SyntheticItalics::default(),
1955        }
1956    }
1957}
1958
1959#[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash)]
1960pub struct SyntheticItalics {
1961    pub angle: i16,
1962}
1963
1964impl Default for SyntheticItalics {
1965    fn default() -> Self {
1966        Self { angle: 0 }
1967    }
1968}
1969
1970/// Reference-counted wrapper around raw image bytes (U8Vec).
1971/// This allows sharing image data between azul-core and webrender without cloning.
1972///
1973/// Similar to ImageRef but specifically for raw byte data, avoiding the overhead
1974/// of the full DecodedImage enum when we just need the bytes.
1975#[derive(Debug)]
1976#[repr(C)]
1977pub struct SharedRawImageData {
1978    /// Shared pointer to the raw image bytes
1979    pub data: *const U8Vec,
1980    /// Reference counter - when it reaches 0, the data is deallocated
1981    pub copies: *const AtomicUsize,
1982    /// Whether to run the destructor (for FFI safety)
1983    pub run_destructor: bool,
1984}
1985
1986impl SharedRawImageData {
1987    /// Create a new SharedRawImageData from a U8Vec
1988    pub fn new(data: U8Vec) -> Self {
1989        Self {
1990            data: Box::into_raw(Box::new(data)),
1991            copies: Box::into_raw(Box::new(AtomicUsize::new(1))),
1992            run_destructor: true,
1993        }
1994    }
1995
1996    /// Get a reference to the underlying bytes
1997    pub fn as_ref(&self) -> &[u8] {
1998        unsafe { (*self.data).as_ref() }
1999    }
2000
2001    /// Alias for as_ref() - get the raw bytes as a slice
2002    pub fn get_bytes(&self) -> &[u8] {
2003        self.as_ref()
2004    }
2005
2006    /// Get a pointer to the raw bytes for hashing/identification
2007    pub fn as_ptr(&self) -> *const u8 {
2008        unsafe { (*self.data).as_ref().as_ptr() }
2009    }
2010
2011    /// Get the length of the data
2012    pub fn len(&self) -> usize {
2013        unsafe { (*self.data).len() }
2014    }
2015
2016    /// Check if the data is empty
2017    pub fn is_empty(&self) -> bool {
2018        self.len() == 0
2019    }
2020
2021    /// Try to extract the U8Vec if this is the only reference
2022    /// Returns None if there are other references
2023    pub fn into_inner(self) -> Option<U8Vec> {
2024        unsafe {
2025            if self.copies.as_ref().map(|m| m.load(AtomicOrdering::SeqCst)) == Some(1) {
2026                let data = Box::from_raw(self.data as *mut U8Vec);
2027                let _ = Box::from_raw(self.copies as *mut AtomicUsize);
2028                core::mem::forget(self); // don't run the destructor
2029                Some(*data)
2030            } else {
2031                None
2032            }
2033        }
2034    }
2035}
2036
2037unsafe impl Send for SharedRawImageData {}
2038unsafe impl Sync for SharedRawImageData {}
2039
2040impl Clone for SharedRawImageData {
2041    fn clone(&self) -> Self {
2042        unsafe {
2043            self.copies
2044                .as_ref()
2045                .map(|m| m.fetch_add(1, AtomicOrdering::SeqCst));
2046        }
2047        Self {
2048            data: self.data,
2049            copies: self.copies,
2050            run_destructor: true,
2051        }
2052    }
2053}
2054
2055impl Drop for SharedRawImageData {
2056    fn drop(&mut self) {
2057        self.run_destructor = false;
2058        unsafe {
2059            let copies = (*self.copies).fetch_sub(1, AtomicOrdering::SeqCst);
2060            if copies == 1 {
2061                let _ = Box::from_raw(self.data as *mut U8Vec);
2062                let _ = Box::from_raw(self.copies as *mut AtomicUsize);
2063            }
2064        }
2065    }
2066}
2067
2068impl PartialEq for SharedRawImageData {
2069    fn eq(&self, rhs: &Self) -> bool {
2070        self.data as usize == rhs.data as usize
2071    }
2072}
2073
2074impl Eq for SharedRawImageData {}
2075
2076impl PartialOrd for SharedRawImageData {
2077    fn partial_cmp(&self, other: &Self) -> Option<::core::cmp::Ordering> {
2078        Some(self.cmp(other))
2079    }
2080}
2081
2082impl Ord for SharedRawImageData {
2083    fn cmp(&self, other: &Self) -> core::cmp::Ordering {
2084        (self.data as usize).cmp(&(other.data as usize))
2085    }
2086}
2087
2088impl Hash for SharedRawImageData {
2089    fn hash<H>(&self, state: &mut H)
2090    where
2091        H: Hasher,
2092    {
2093        (self.data as usize).hash(state)
2094    }
2095}
2096
2097/// Represents the backing store of an arbitrary series of pixels for display by
2098/// WebRender. This storage can take several forms.
2099#[derive(Debug, Clone, PartialEq, Eq, Ord, PartialOrd, Hash)]
2100#[repr(C, u8)]
2101pub enum ImageData {
2102    /// A simple series of bytes, provided by the embedding and owned by WebRender.
2103    /// The format is stored out-of-band, currently in ImageDescriptor.
2104    Raw(SharedRawImageData),
2105    /// An image owned by the embedding, and referenced by WebRender. This may
2106    /// take the form of a texture or a heap-allocated buffer.
2107    External(ExternalImageData),
2108}
2109
2110/// Storage format identifier for externally-managed images.
2111#[derive(Debug, Copy, Clone, Eq, Hash, PartialEq, PartialOrd, Ord)]
2112#[repr(C, u8)]
2113pub enum ExternalImageType {
2114    /// The image is texture-backed.
2115    TextureHandle(ImageBufferKind),
2116    /// The image is heap-allocated by the embedding.
2117    Buffer,
2118}
2119
2120/// An arbitrary identifier for an external image provided by the
2121/// application. It must be a unique identifier for each external
2122/// image.
2123#[repr(C)]
2124#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, PartialOrd, Ord)]
2125pub struct ExternalImageId {
2126    pub inner: u64,
2127}
2128
2129static LAST_EXTERNAL_IMAGE_ID: AtomicUsize = AtomicUsize::new(0);
2130
2131impl ExternalImageId {
2132    /// Creates a new, unique ExternalImageId
2133    pub fn new() -> Self {
2134        Self {
2135            inner: LAST_EXTERNAL_IMAGE_ID.fetch_add(1, AtomicOrdering::SeqCst) as u64,
2136        }
2137    }
2138}
2139
2140#[derive(Debug, Clone, PartialEq, PartialOrd)]
2141#[repr(C, u8)]
2142pub enum GlyphOutlineOperation {
2143    MoveTo(OutlineMoveTo),
2144    LineTo(OutlineLineTo),
2145    QuadraticCurveTo(OutlineQuadTo),
2146    CubicCurveTo(OutlineCubicTo),
2147    ClosePath,
2148}
2149
2150impl_option!(
2151    GlyphOutlineOperation,
2152    OptionGlyphOutlineOperation,
2153    copy = false,
2154    [Debug, Clone, PartialEq, PartialOrd]
2155);
2156
2157// MoveTo in em units
2158#[derive(Debug, Clone, PartialEq, PartialOrd)]
2159#[repr(C)]
2160pub struct OutlineMoveTo {
2161    pub x: i16,
2162    pub y: i16,
2163}
2164
2165// LineTo in em units
2166#[derive(Debug, Clone, PartialEq, PartialOrd)]
2167#[repr(C)]
2168pub struct OutlineLineTo {
2169    pub x: i16,
2170    pub y: i16,
2171}
2172
2173// QuadTo in em units
2174#[derive(Debug, Clone, PartialEq, PartialOrd)]
2175#[repr(C)]
2176pub struct OutlineQuadTo {
2177    pub ctrl_1_x: i16,
2178    pub ctrl_1_y: i16,
2179    pub end_x: i16,
2180    pub end_y: i16,
2181}
2182
2183// CubicTo in em units
2184#[derive(Debug, Clone, PartialEq, PartialOrd)]
2185#[repr(C)]
2186pub struct OutlineCubicTo {
2187    pub ctrl_1_x: i16,
2188    pub ctrl_1_y: i16,
2189    pub ctrl_2_x: i16,
2190    pub ctrl_2_y: i16,
2191    pub end_x: i16,
2192    pub end_y: i16,
2193}
2194
2195#[derive(Debug, Clone, PartialEq, PartialOrd)]
2196#[repr(C)]
2197pub struct GlyphOutline {
2198    pub operations: GlyphOutlineOperationVec,
2199}
2200
2201azul_css::impl_vec!(GlyphOutlineOperation, GlyphOutlineOperationVec, GlyphOutlineOperationVecDestructor, GlyphOutlineOperationVecDestructorType, GlyphOutlineOperationVecSlice, OptionGlyphOutlineOperation);
2202azul_css::impl_vec_clone!(
2203    GlyphOutlineOperation,
2204    GlyphOutlineOperationVec,
2205    GlyphOutlineOperationVecDestructor
2206);
2207azul_css::impl_vec_debug!(GlyphOutlineOperation, GlyphOutlineOperationVec);
2208azul_css::impl_vec_partialord!(GlyphOutlineOperation, GlyphOutlineOperationVec);
2209azul_css::impl_vec_partialeq!(GlyphOutlineOperation, GlyphOutlineOperationVec);
2210
2211#[derive(Debug, Clone)]
2212#[repr(C)]
2213pub struct OwnedGlyphBoundingBox {
2214    pub max_x: i16,
2215    pub max_y: i16,
2216    pub min_x: i16,
2217    pub min_y: i16,
2218}
2219
2220/// Specifies the type of texture target in driver terms.
2221#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
2222#[repr(C)]
2223pub enum ImageBufferKind {
2224    /// Standard texture. This maps to GL_TEXTURE_2D in OpenGL.
2225    Texture2D = 0,
2226    /// Rectangle texture. This maps to GL_TEXTURE_RECTANGLE in OpenGL. This
2227    /// is similar to a standard texture, with a few subtle differences
2228    /// (no mipmaps, non-power-of-two dimensions, different coordinate space)
2229    /// that make it useful for representing the kinds of textures we use
2230    /// in WebRender. See https://www.khronos.org/opengl/wiki/Rectangle_Texture
2231    /// for background on Rectangle textures.
2232    TextureRect = 1,
2233    /// External texture. This maps to GL_TEXTURE_EXTERNAL_OES in OpenGL, which
2234    /// is an extension. This is used for image formats that OpenGL doesn't
2235    /// understand, particularly YUV. See
2236    /// https://www.khronos.org/registry/OpenGL/extensions/OES/OES_EGL_image_external.txt
2237    TextureExternal = 2,
2238}
2239
2240/// Descriptor for external image resources. See `ImageData`.
2241#[repr(C)]
2242#[derive(Debug, Clone, Eq, Hash, PartialEq, PartialOrd, Ord)]
2243pub struct ExternalImageData {
2244    /// The identifier of this external image, provided by the embedding.
2245    pub id: ExternalImageId,
2246    /// For multi-plane images (i.e. YUV), indicates the plane of the
2247    /// original image that this struct represents. 0 for single-plane images.
2248    pub channel_index: u8,
2249    /// Storage format identifier.
2250    pub image_type: ExternalImageType,
2251}
2252
2253pub type TileSize = u16;
2254
2255#[derive(Debug, Copy, Clone, PartialEq, PartialOrd)]
2256pub enum ImageDirtyRect {
2257    All,
2258    Partial(LayoutRect),
2259}
2260
2261#[derive(Debug, Clone, PartialEq, PartialOrd)]
2262pub enum ResourceUpdate {
2263    AddFont(AddFont),
2264    DeleteFont(FontKey),
2265    AddFontInstance(AddFontInstance),
2266    DeleteFontInstance(FontInstanceKey),
2267    AddImage(AddImage),
2268    UpdateImage(UpdateImage),
2269    DeleteImage(ImageKey),
2270}
2271
2272#[derive(Debug, Clone, PartialEq, Eq, Ord, PartialOrd, Hash)]
2273pub struct AddImage {
2274    pub key: ImageKey,
2275    pub descriptor: ImageDescriptor,
2276    pub data: ImageData,
2277    pub tiling: Option<TileSize>,
2278}
2279
2280#[derive(Debug, Clone, PartialEq, PartialOrd)]
2281pub struct UpdateImage {
2282    pub key: ImageKey,
2283    pub descriptor: ImageDescriptor,
2284    pub data: ImageData,
2285    pub dirty_rect: ImageDirtyRect,
2286}
2287
2288/// Message to add a font to WebRender.
2289/// Contains a reference to the parsed font data.
2290#[derive(Clone, PartialEq, Eq, Ord, PartialOrd, Hash)]
2291pub struct AddFont {
2292    pub key: FontKey,
2293    pub font: azul_css::props::basic::FontRef,
2294}
2295
2296impl fmt::Debug for AddFont {
2297    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
2298        write!(
2299            f,
2300            "AddFont {{ key: {:?}, font: {:?} }}",
2301            self.key, self.font
2302        )
2303    }
2304}
2305
2306#[derive(Debug, Clone, PartialEq, PartialOrd)]
2307pub struct AddFontInstance {
2308    pub key: FontInstanceKey,
2309    pub font_key: FontKey,
2310    pub glyph_size: (Au, DpiScaleFactor),
2311    pub options: Option<FontInstanceOptions>,
2312    pub platform_options: Option<FontInstancePlatformOptions>,
2313    pub variations: Vec<FontVariation>,
2314}
2315
2316#[repr(C)]
2317#[derive(Clone, Copy, Debug, PartialOrd, PartialEq)]
2318pub struct FontVariation {
2319    pub tag: u32,
2320    pub value: f32,
2321}
2322
2323#[repr(C)]
2324#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
2325pub struct Epoch {
2326    inner: u32,
2327}
2328
2329impl fmt::Display for Epoch {
2330    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
2331        write!(f, "{}", self.inner)
2332    }
2333}
2334
2335impl Epoch {
2336    // prevent raw access to the .inner field so that
2337    // you can grep the codebase for .increment() to see
2338    // exactly where the epoch is being incremented
2339    pub const fn new() -> Self {
2340        Self { inner: 0 }
2341    }
2342    pub const fn from(i: u32) -> Self {
2343        Self { inner: i }
2344    }
2345    pub const fn into_u32(&self) -> u32 {
2346        self.inner
2347    }
2348
2349    // We don't want the epoch to increase to u32::MAX, since
2350    // u32::MAX represents an invalid epoch, which could confuse webrender
2351    pub fn increment(&mut self) {
2352        use core::u32;
2353        const MAX_ID: u32 = u32::MAX - 1;
2354        *self = match self.inner {
2355            MAX_ID => Epoch { inner: 0 },
2356            other => Epoch {
2357                inner: other.saturating_add(1),
2358            },
2359        };
2360    }
2361}
2362
2363// App units that this font instance was registered for
2364#[derive(Debug, Clone, Copy, Hash, PartialEq, PartialOrd, Eq, Ord)]
2365pub struct Au(pub i32);
2366
2367pub const AU_PER_PX: i32 = 60;
2368pub const MAX_AU: i32 = (1 << 30) - 1;
2369pub const MIN_AU: i32 = -(1 << 30) - 1;
2370
2371impl Au {
2372    pub fn from_px(px: f32) -> Self {
2373        let target_app_units = (px * AU_PER_PX as f32) as i32;
2374        Au(target_app_units.min(MAX_AU).max(MIN_AU))
2375    }
2376    pub fn into_px(&self) -> f32 {
2377        self.0 as f32 / AU_PER_PX as f32
2378    }
2379}
2380
2381// Debug, PartialEq, Eq, PartialOrd, Ord
2382#[derive(Debug)]
2383pub enum AddFontMsg {
2384    // add font: font key, font bytes + font index
2385    Font(FontKey, StyleFontFamilyHash, FontRef),
2386    Instance(AddFontInstance, (Au, DpiScaleFactor)),
2387}
2388
2389impl AddFontMsg {
2390    pub fn into_resource_update(&self) -> ResourceUpdate {
2391        use self::AddFontMsg::*;
2392        match self {
2393            Font(font_key, _, font_ref) => ResourceUpdate::AddFont(AddFont {
2394                key: *font_key,
2395                font: font_ref.clone(),
2396            }),
2397            Instance(fi, _) => ResourceUpdate::AddFontInstance(fi.clone()),
2398        }
2399    }
2400}
2401
2402#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
2403pub enum DeleteFontMsg {
2404    Font(FontKey),
2405    Instance(FontInstanceKey, (Au, DpiScaleFactor)),
2406}
2407
2408impl DeleteFontMsg {
2409    pub fn into_resource_update(&self) -> ResourceUpdate {
2410        use self::DeleteFontMsg::*;
2411        match self {
2412            Font(f) => ResourceUpdate::DeleteFont(*f),
2413            Instance(fi, _) => ResourceUpdate::DeleteFontInstance(*fi),
2414        }
2415    }
2416}
2417
2418#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
2419pub struct AddImageMsg(pub AddImage);
2420
2421impl AddImageMsg {
2422    pub fn into_resource_update(&self) -> ResourceUpdate {
2423        ResourceUpdate::AddImage(self.0.clone())
2424    }
2425}
2426
2427#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
2428pub struct DeleteImageMsg(ImageKey);
2429
2430impl DeleteImageMsg {
2431    pub fn into_resource_update(&self) -> ResourceUpdate {
2432        ResourceUpdate::DeleteImage(self.0.clone())
2433    }
2434}
2435
2436#[derive(Debug, Clone, PartialEq, Eq, Hash)]
2437#[repr(C)]
2438pub struct LoadedFontSource {
2439    pub data: U8Vec,
2440    pub index: u32,
2441    pub load_outlines: bool,
2442}
2443
2444// function to load the font source from a file
2445pub type LoadFontFn = fn(&StyleFontFamily, &FcFontCache) -> Option<LoadedFontSource>;
2446
2447// function to parse the font given the loaded font source
2448pub type ParseFontFn = fn(LoadedFontSource) -> Option<FontRef>; // = Option<Box<azul_text_layout::Font>>
2449
2450pub type GlStoreImageFn = fn(DocumentId, Epoch, Texture) -> ExternalImageId;
2451
2452/// Given the fonts of the current frame, returns `AddFont` and `AddFontInstance`s of
2453/// which fonts / instances are currently not in the `current_registered_fonts` and
2454/// need to be added.
2455///
2456/// Deleting fonts can only be done after the entire frame has finished drawing,
2457/// otherwise (if removing fonts would happen after every DOM) we'd constantly
2458/// add-and-remove fonts after every IFrameCallback, which would cause a lot of
2459/// I/O waiting.
2460pub fn build_add_font_resource_updates(
2461    renderer_resources: &mut RendererResources,
2462    dpi: DpiScaleFactor,
2463    fc_cache: &FcFontCache,
2464    id_namespace: IdNamespace,
2465    fonts_in_dom: &FastHashMap<ImmediateFontId, FastBTreeSet<Au>>,
2466    font_source_load_fn: LoadFontFn,
2467    parse_font_fn: ParseFontFn,
2468) -> Vec<(StyleFontFamilyHash, AddFontMsg)> {
2469    let mut resource_updates = alloc::vec::Vec::new();
2470    let mut font_instances_added_this_frame = FastBTreeSet::new();
2471
2472    'outer: for (im_font_id, font_sizes) in fonts_in_dom {
2473        macro_rules! insert_font_instances {
2474            ($font_family_hash:expr, $font_key:expr, $font_size:expr) => {{
2475                let font_instance_key_exists = renderer_resources
2476                    .currently_registered_fonts
2477                    .get(&$font_key)
2478                    .and_then(|(_, font_instances)| font_instances.get(&($font_size, dpi)))
2479                    .is_some()
2480                    || font_instances_added_this_frame.contains(&($font_key, ($font_size, dpi)));
2481
2482                if !font_instance_key_exists {
2483                    let font_instance_key = FontInstanceKey::unique(id_namespace);
2484
2485                    // For some reason the gamma is way to low on Windows
2486                    #[cfg(target_os = "windows")]
2487                    let platform_options = FontInstancePlatformOptions {
2488                        gamma: 300,
2489                        contrast: 100,
2490                        cleartype_level: 100,
2491                    };
2492
2493                    #[cfg(target_os = "linux")]
2494                    let platform_options = FontInstancePlatformOptions {
2495                        lcd_filter: FontLCDFilter::Default,
2496                        hinting: FontHinting::Normal,
2497                    };
2498
2499                    #[cfg(target_os = "macos")]
2500                    let platform_options = FontInstancePlatformOptions::default();
2501
2502                    #[cfg(target_arch = "wasm32")]
2503                    let platform_options = FontInstancePlatformOptions::default();
2504
2505                    let options = FontInstanceOptions {
2506                        render_mode: FontRenderMode::Subpixel,
2507                        flags: 0 | FONT_INSTANCE_FLAG_NO_AUTOHINT,
2508                        ..Default::default()
2509                    };
2510
2511                    font_instances_added_this_frame.insert(($font_key, ($font_size, dpi)));
2512                    resource_updates.push((
2513                        $font_family_hash,
2514                        AddFontMsg::Instance(
2515                            AddFontInstance {
2516                                key: font_instance_key,
2517                                font_key: $font_key,
2518                                glyph_size: ($font_size, dpi),
2519                                options: Some(options),
2520                                platform_options: Some(platform_options),
2521                                variations: alloc::vec::Vec::new(),
2522                            },
2523                            ($font_size, dpi),
2524                        ),
2525                    ));
2526                }
2527            }};
2528        }
2529
2530        match im_font_id {
2531            ImmediateFontId::Resolved((font_family_hash, font_id)) => {
2532                // nothing to do, font is already added,
2533                // just insert the missing font instances
2534                for font_size in font_sizes.iter() {
2535                    insert_font_instances!(*font_family_hash, *font_id, *font_size);
2536                }
2537            }
2538            ImmediateFontId::Unresolved(style_font_families) => {
2539                // If the font is already loaded during the current frame,
2540                // do not attempt to load it again
2541                //
2542                // This prevents duplicated loading for fonts in different orders, i.e.
2543                // - vec!["Times New Roman", "serif"] and
2544                // - vec!["sans", "Times New Roman"]
2545                // ... will resolve to the same font instead of creating two fonts
2546
2547                // If there is no font key, that means there's also no font instances
2548                let mut font_family_hash = None;
2549                let font_families_hash = StyleFontFamiliesHash::new(style_font_families.as_ref());
2550
2551                // Find the first font that can be loaded and parsed
2552                'inner: for family in style_font_families.as_ref().iter() {
2553                    let current_family_hash = StyleFontFamilyHash::new(&family);
2554
2555                    if let Some(font_id) = renderer_resources.font_id_map.get(&current_family_hash)
2556                    {
2557                        // font key already exists
2558                        for font_size in font_sizes {
2559                            insert_font_instances!(current_family_hash, *font_id, *font_size);
2560                        }
2561                        continue 'outer;
2562                    }
2563
2564                    let font_ref = match family {
2565                        StyleFontFamily::Ref(r) => r.clone(), // Clone the FontRef
2566                        other => {
2567                            // Load and parse the font
2568                            let font_data = match (font_source_load_fn)(&other, fc_cache) {
2569                                Some(s) => s,
2570                                None => continue 'inner,
2571                            };
2572
2573                            let font_ref = match (parse_font_fn)(font_data) {
2574                                Some(s) => s,
2575                                None => continue 'inner,
2576                            };
2577
2578                            font_ref
2579                        }
2580                    };
2581
2582                    // font loaded properly
2583                    font_family_hash = Some((current_family_hash, font_ref));
2584                    break 'inner;
2585                }
2586
2587                let (font_family_hash, font_ref) = match font_family_hash {
2588                    None => continue 'outer, // No font could be loaded, try again next frame
2589                    Some(s) => s,
2590                };
2591
2592                // Generate a new font key, store the mapping between hash and font key
2593                let font_key = FontKey::unique(id_namespace);
2594                let add_font_msg = AddFontMsg::Font(font_key, font_family_hash, font_ref);
2595
2596                renderer_resources
2597                    .font_id_map
2598                    .insert(font_family_hash, font_key);
2599                renderer_resources
2600                    .font_families_map
2601                    .insert(font_families_hash, font_family_hash);
2602                resource_updates.push((font_family_hash, add_font_msg));
2603
2604                // Insert font sizes for the newly generated font key
2605                for font_size in font_sizes {
2606                    insert_font_instances!(font_family_hash, font_key, *font_size);
2607                }
2608            }
2609        }
2610    }
2611
2612    resource_updates
2613}
2614
2615/// Given the images of the current frame, returns `AddImage`s of
2616/// which image keys are currently not in the `current_registered_images` and
2617/// need to be added.
2618///
2619/// Returns Vec<(ImageRefHash, AddImageMsg)> where:
2620/// - ImageRefHash: Stable hash of the ImageRef pointer
2621/// - AddImageMsg: Message to add the image to WebRender
2622///
2623/// The ImageKey in AddImageMsg is generated directly from the ImageRefHash using
2624/// image_ref_hash_to_image_key(), so no separate mapping table is needed.
2625///
2626/// Deleting images can only be done after the entire frame has finished drawing,
2627/// otherwise (if removing images would happen after every DOM) we'd constantly
2628/// add-and-remove images after every IFrameCallback, which would cause a lot of
2629/// I/O waiting.
2630#[allow(unused_variables)]
2631pub fn build_add_image_resource_updates(
2632    renderer_resources: &RendererResources,
2633    id_namespace: IdNamespace,
2634    epoch: Epoch,
2635    document_id: &DocumentId,
2636    images_in_dom: &FastBTreeSet<ImageRef>,
2637    insert_into_active_gl_textures: GlStoreImageFn,
2638) -> Vec<(ImageRefHash, AddImageMsg)> {
2639    images_in_dom
2640        .iter()
2641        .filter_map(|image_ref| {
2642            let image_ref_hash = image_ref_get_hash(&image_ref);
2643
2644            if renderer_resources
2645                .currently_registered_images
2646                .contains_key(&image_ref_hash)
2647            {
2648                return None;
2649            }
2650
2651            // NOTE: The image_ref.clone() is a shallow clone,
2652            // does not actually clone the data
2653            match image_ref.get_data() {
2654                DecodedImage::Gl(texture) => {
2655                    let descriptor = texture.get_descriptor();
2656                    let key = image_ref_hash_to_image_key(image_ref_hash, id_namespace);
2657                    // NOTE: The texture is not really cloned here,
2658                    let external_image_id =
2659                        (insert_into_active_gl_textures)(*document_id, epoch, texture.clone());
2660                    Some((
2661                        image_ref_hash,
2662                        AddImageMsg(AddImage {
2663                            key,
2664                            data: ImageData::External(ExternalImageData {
2665                                id: external_image_id,
2666                                channel_index: 0,
2667                                image_type: ExternalImageType::TextureHandle(
2668                                    ImageBufferKind::Texture2D,
2669                                ),
2670                            }),
2671                            descriptor,
2672                            tiling: None,
2673                        }),
2674                    ))
2675                }
2676                DecodedImage::Raw((descriptor, data)) => {
2677                    let key = image_ref_hash_to_image_key(image_ref_hash, id_namespace);
2678                    Some((
2679                        image_ref_hash,
2680                        AddImageMsg(AddImage {
2681                            key,
2682                            data: data.clone(), // deep-copy except in the &'static case
2683                            descriptor: descriptor.clone(), /* deep-copy, but struct is not very
2684                                                 * large */
2685                            tiling: None,
2686                        }),
2687                    ))
2688                }
2689                DecodedImage::NullImage {
2690                    width: _,
2691                    height: _,
2692                    format: _,
2693                    tag: _,
2694                } => None,
2695                DecodedImage::Callback(_) => None, /* Texture callbacks are handled after layout
2696                                                    * is done */
2697            }
2698        })
2699        .collect()
2700}
2701
2702fn add_gl_resources(
2703    renderer_resources: &mut RendererResources,
2704    all_resource_updates: &mut Vec<ResourceUpdate>,
2705    add_image_resources: Vec<(ImageRefHash, ImageRefHash, AddImageMsg)>,
2706) {
2707    let add_image_resources = add_image_resources
2708        .into_iter()
2709        // use the callback_imageref_hash for indexing!
2710        .map(|(_, k, v)| (k, v))
2711        .collect::<Vec<_>>();
2712
2713    add_resources(
2714        renderer_resources,
2715        all_resource_updates,
2716        Vec::new(),
2717        add_image_resources,
2718    );
2719}
2720
2721/// Submits the `AddFont`, `AddFontInstance` and `AddImage` resources to the RenderApi.
2722/// Extends `currently_registered_images` and `currently_registered_fonts` by the
2723/// `last_frame_image_keys` and `last_frame_font_keys`, so that we don't lose track of
2724/// what font and image keys are currently in the API.
2725pub fn add_resources(
2726    renderer_resources: &mut RendererResources,
2727    all_resource_updates: &mut Vec<ResourceUpdate>,
2728    add_font_resources: Vec<(StyleFontFamilyHash, AddFontMsg)>,
2729    add_image_resources: Vec<(ImageRefHash, AddImageMsg)>,
2730) {
2731    all_resource_updates.extend(
2732        add_font_resources
2733            .iter()
2734            .map(|(_, f)| f.into_resource_update()),
2735    );
2736    all_resource_updates.extend(
2737        add_image_resources
2738            .iter()
2739            .map(|(_, i)| i.into_resource_update()),
2740    );
2741
2742    for (image_ref_hash, add_image_msg) in add_image_resources.iter() {
2743        renderer_resources.currently_registered_images.insert(
2744            *image_ref_hash,
2745            ResolvedImage {
2746                key: add_image_msg.0.key,
2747                descriptor: add_image_msg.0.descriptor,
2748            },
2749        );
2750    }
2751
2752    for (_, add_font_msg) in add_font_resources {
2753        use self::AddFontMsg::*;
2754        match add_font_msg {
2755            Font(fk, font_family_hash, font_ref) => {
2756                renderer_resources
2757                    .currently_registered_fonts
2758                    .entry(fk)
2759                    .or_insert_with(|| (font_ref.clone(), FastHashMap::default()));
2760
2761                // CRITICAL: Map font_hash to FontKey so we can look it up during rendering
2762                renderer_resources
2763                    .font_hash_map
2764                    .insert(font_ref.get_hash(), fk);
2765            }
2766            Instance(fi, size) => {
2767                if let Some((_, instances)) = renderer_resources
2768                    .currently_registered_fonts
2769                    .get_mut(&fi.font_key)
2770                {
2771                    instances.insert(size, fi.key);
2772                }
2773            }
2774        }
2775    }
2776}