1
//! Resource management types for the application.
2
//!
3
//! This module contains the core types for managing application resources:
4
//! - `AppConfig`: application-level configuration (logging, fonts, routes, components)
5
//! - `ImageRef` / `ImageRefHash`: reference-counted decoded image handles
6
//! - `FontKey` / `FontInstanceKey` / `ImageKey`: renderer-scoped resource keys
7
//! - `RendererResources`: per-window font/image registry with frame-based GC
8
//! - `RawImage`: CPU-side pixel data with format conversion to BGRA8
9
//! - `build_add_font_resource_updates` / `build_add_image_resource_updates`:
10
//!   diff current frame against registered resources and produce WebRender updates
11

            
12
#[cfg(not(feature = "std"))]
13
use alloc::string::ToString;
14
use alloc::{boxed::Box, collections::btree_map::BTreeMap, string::String, vec::Vec};
15
use core::{
16
    fmt,
17
    hash::{Hash, Hasher},
18
    sync::atomic::{AtomicU64, AtomicUsize, Ordering as AtomicOrdering},
19
};
20

            
21
use azul_css::{
22
    format_rust_code::GetHash,
23
    props::basic::{
24
        pixel::DEFAULT_FONT_SIZE, ColorU, FloatValue, FontRef, LayoutRect, LayoutSize,
25
        StyleFontFamily, StyleFontFamilyVec, StyleFontSize,
26
    },
27
    system::SystemStyle,
28
    AzString, F32Vec, LayoutDebugMessage, OptionI32, StringVec, U16Vec, U32Vec, U8Vec,
29
};
30
use rust_fontconfig::FcFontCache;
31

            
32
// Re-export Core* callback types for public use
33
pub use crate::callbacks::{
34
    CoreImageCallback, CoreRenderImageCallback, CoreRenderImageCallbackType,
35
};
36
use crate::{
37
    callbacks::{LayoutCallback, VirtualViewCallback},
38
    dom::{DomId, NodeData, NodeType},
39
    geom::{LogicalPosition, LogicalRect, LogicalSize},
40
    gl::{OptionGlContextPtr, Texture},
41
    hit_test::DocumentId,
42
    id::NodeId,
43
    prop_cache::CssPropertyCache,
44
    refany::RefAny,
45
    styled_dom::{
46
        NodeHierarchyItemId, StyleFontFamiliesHash, StyleFontFamilyHash, StyledDom, StyledNodeState,
47
    },
48
    ui_solver::GlyphInstance,
49
    window::{AzStringPair, OptionChar, StringPairVec},
50
    xml::{
51
        ComponentDef, ComponentDefVec, ComponentId, ComponentLibrary, ComponentLibraryVec,
52
        ComponentSource, RegisterComponentFn, RegisterComponentLibraryFn,
53
    },
54
    FastBTreeSet, OrderedMap,
55
};
56

            
57
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
58
#[repr(C)]
59
pub struct DpiScaleFactor {
60
    pub inner: FloatValue,
61
}
62

            
63
impl DpiScaleFactor {
64
    pub fn new(f: f32) -> Self {
65
        Self {
66
            inner: FloatValue::new(f),
67
        }
68
    }
69
}
70

            
71
/// Determines what happens when all application windows are closed
72
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
73
#[repr(C)]
74
pub enum AppTerminationBehavior {
75
    /// Return control to main() when all windows are closed (if platform supports it).
76
    /// On macOS, this exits the NSApplication run loop and returns to main().
77
    /// This is useful if you want to clean up resources or restart the event loop.
78
    ReturnToMain,
79
    /// Keep the application running even when all windows are closed.
80
    /// This is the standard macOS behavior (app stays in dock until explicitly quit).
81
    RunForever,
82
    /// Immediately terminate the process when all windows are closed.
83
    /// Calls std::process::exit(0).
84
    EndProcess,
85
}
86

            
87
impl Default for AppTerminationBehavior {
88
    fn default() -> Self {
89
        // Default: End the process when all windows close (cross-platform behavior)
90
        AppTerminationBehavior::EndProcess
91
    }
92
}
93

            
94
/// A named font bundled with the application (name + raw bytes).
95
/// The name is used to reference the font in CSS (e.g. `font-family: "MyFont"`).
96
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
97
#[repr(C)]
98
pub struct NamedFont {
99
    /// The font family name to use in CSS (e.g. "Roboto", "MyCustomFont")
100
    pub name: AzString,
101
    /// Raw font file bytes (TTF, OTF, etc.)
102
    pub bytes: U8Vec,
103
}
104

            
105
impl_option!(
106
    NamedFont,
107
    OptionNamedFont,
108
    copy = false,
109
    [Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash]
110
);
111

            
112
impl NamedFont {
113
    pub fn new(name: AzString, bytes: U8Vec) -> Self {
114
        Self { name, bytes }
115
    }
116
}
117

            
118
impl_vec!(NamedFont, NamedFontVec, NamedFontVecDestructor, NamedFontVecDestructorType, NamedFontVecSlice, OptionNamedFont);
119
impl_vec_mut!(NamedFont, NamedFontVec);
120
impl_vec_debug!(NamedFont, NamedFontVec);
121
impl_vec_partialeq!(NamedFont, NamedFontVec);
122
impl_vec_eq!(NamedFont, NamedFontVec);
123
impl_vec_partialord!(NamedFont, NamedFontVec);
124
impl_vec_ord!(NamedFont, NamedFontVec);
125
impl_vec_hash!(NamedFont, NamedFontVec);
126
impl_vec_clone!(NamedFont, NamedFontVec, NamedFontVecDestructor);
127

            
128
/// Descriptor for a font that the layout engine currently has loaded in its
129
/// font cache.
130
///
131
/// Returned by `CallbackInfo::get_loaded_fonts()`. The `font_hash` field is
132
/// the same `u64` carried by `DisplayListItem::Text` glyph runs, so a callback
133
/// can correlate a loaded font with the text runs that use it and then fetch
134
/// the raw bytes via `CallbackInfo::get_loaded_font_bytes(font_hash)` (e.g. to
135
/// embed every font the layout actually used into a generated PDF).
136
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
137
#[repr(C)]
138
pub struct LoadedFont {
139
    /// Stable hash of the parsed font, identical to the `font_hash` stored on
140
    /// `DisplayListItem::Text` glyph runs. Use this to look up the bytes with
141
    /// `CallbackInfo::get_loaded_font_bytes`.
142
    pub font_hash: u64,
143
    /// PostScript / family name from the font's `name` table, or an empty
144
    /// string if the font did not provide one.
145
    pub family_name: AzString,
146
    /// Total number of glyphs in the font (from the `maxp` table).
147
    pub num_glyphs: u32,
148
    /// `true` if the source font bytes are retained and can be retrieved with
149
    /// `CallbackInfo::get_loaded_font_bytes(font_hash)`. Fonts loaded on the
150
    /// production (lazy mmap) path retain their bytes; some test-only fonts do
151
    /// not.
152
    pub has_bytes: bool,
153
}
154

            
155
impl_option!(
156
    LoadedFont,
157
    OptionLoadedFont,
158
    copy = false,
159
    [Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash]
160
);
161

            
162
impl LoadedFont {
163
    pub fn new(font_hash: u64, family_name: AzString, num_glyphs: u32, has_bytes: bool) -> Self {
164
        Self {
165
            font_hash,
166
            family_name,
167
            num_glyphs,
168
            has_bytes,
169
        }
170
    }
171
}
172

            
173
impl_vec!(LoadedFont, LoadedFontVec, LoadedFontVecDestructor, LoadedFontVecDestructorType, LoadedFontVecSlice, OptionLoadedFont);
174
impl_vec_mut!(LoadedFont, LoadedFontVec);
175
impl_vec_debug!(LoadedFont, LoadedFontVec);
176
impl_vec_partialeq!(LoadedFont, LoadedFontVec);
177
impl_vec_eq!(LoadedFont, LoadedFontVec);
178
impl_vec_partialord!(LoadedFont, LoadedFontVec);
179
impl_vec_ord!(LoadedFont, LoadedFontVec);
180
impl_vec_hash!(LoadedFont, LoadedFontVec);
181
impl_vec_clone!(LoadedFont, LoadedFontVec, LoadedFontVecDestructor);
182

            
183
/// Configuration for how fonts should be loaded at app startup.
184
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
185
#[repr(C, u8)]
186
pub enum FontLoadingConfig {
187
    /// Load all system fonts (default behavior, can be slow on systems with many fonts)
188
    LoadAllSystemFonts,
189
    /// Only load fonts for specific families (faster startup).
190
    /// Generic families like "sans-serif" are automatically expanded to OS-specific fonts.
191
    LoadOnlyFamilies(StringVec),
192
    /// Don't load any system fonts, only use bundled fonts
193
    BundledFontsOnly,
194
}
195

            
196
impl Default for FontLoadingConfig {
197
    fn default() -> Self {
198
        FontLoadingConfig::LoadAllSystemFonts
199
    }
200
}
201

            
202
/// Mock environment for CSS evaluation.
203
/// 
204
/// Allows overriding auto-detected system properties for testing and development.
205
/// Any field set to `None` will use the auto-detected value.
206
/// Any field set to `Some(...)` will override the auto-detected value.
207
/// 
208
/// # Example
209
/// ```rust
210
/// # use azul_core::resources::CssMockEnvironment;
211
/// use azul_css::dynamic_selector::{
212
///     OsCondition, ThemeCondition, OsVersion,
213
///     OptionOsCondition, OptionThemeCondition, OptionOsVersion,
214
/// };
215
/// 
216
/// // Mock a Linux dark theme environment on any platform
217
/// let mock = CssMockEnvironment {
218
///     os: OptionOsCondition::Some(OsCondition::Linux),
219
///     theme: OptionThemeCondition::Some(ThemeCondition::Dark),
220
///     ..Default::default()
221
/// };
222
/// 
223
/// // Mock Windows XP for retro testing
224
/// let mock = CssMockEnvironment {
225
///     os: OptionOsCondition::Some(OsCondition::Windows),
226
///     os_version: OptionOsVersion::Some(OsVersion::WIN_XP),
227
///     ..Default::default()
228
/// };
229
/// ```
230
#[derive(Debug, Clone, Default)]
231
#[repr(C)]
232
pub struct CssMockEnvironment {
233
    /// Override the current theme (light/dark)
234
    pub theme: azul_css::dynamic_selector::OptionThemeCondition,
235
    /// Override the current language (BCP 47 tag, e.g., "de-DE", "en-US")
236
    pub language: azul_css::OptionString,
237
    /// Override the detected OS version
238
    pub os_version: azul_css::dynamic_selector::OptionOsVersion,
239
    /// Override the detected operating system
240
    pub os: azul_css::dynamic_selector::OptionOsCondition,
241
    /// Override the Linux desktop environment (only applies when os = Linux)
242
    pub desktop_env: azul_css::dynamic_selector::OptionLinuxDesktopEnv,
243
    /// Override viewport dimensions (for @media queries)
244
    /// Only use for testing - normally set by window size
245
    pub viewport_width: azul_css::OptionF32,
246
    pub viewport_height: azul_css::OptionF32,
247
    /// Override the reduced motion preference
248
    pub prefers_reduced_motion: azul_css::OptionBool,
249
    /// Override the high contrast preference
250
    pub prefers_high_contrast: azul_css::OptionBool,
251
}
252

            
253
impl CssMockEnvironment {
254
    /// Create a mock for Linux environment
255
    pub fn linux() -> Self {
256
        Self {
257
            os: azul_css::dynamic_selector::OptionOsCondition::Some(azul_css::dynamic_selector::OsCondition::Linux),
258
            ..Default::default()
259
        }
260
    }
261
    
262
    /// Create a mock for Windows environment
263
    pub fn windows() -> Self {
264
        Self {
265
            os: azul_css::dynamic_selector::OptionOsCondition::Some(azul_css::dynamic_selector::OsCondition::Windows),
266
            ..Default::default()
267
        }
268
    }
269
    
270
    /// Create a mock for macOS environment
271
    pub fn macos() -> Self {
272
        Self {
273
            os: azul_css::dynamic_selector::OptionOsCondition::Some(azul_css::dynamic_selector::OsCondition::MacOS),
274
            ..Default::default()
275
        }
276
    }
277
    
278
    /// Create a mock for dark theme
279
    pub fn dark_theme() -> Self {
280
        Self {
281
            theme: azul_css::dynamic_selector::OptionThemeCondition::Some(azul_css::dynamic_selector::ThemeCondition::Dark),
282
            ..Default::default()
283
        }
284
    }
285
    
286
    /// Create a mock for light theme
287
    pub fn light_theme() -> Self {
288
        Self {
289
            theme: azul_css::dynamic_selector::OptionThemeCondition::Some(azul_css::dynamic_selector::ThemeCondition::Light),
290
            ..Default::default()
291
        }
292
    }
293
    
294
    /// Apply this mock to a DynamicSelectorContext
295
    pub fn apply_to(&self, ctx: &mut azul_css::dynamic_selector::DynamicSelectorContext) {
296
        if let azul_css::dynamic_selector::OptionOsCondition::Some(os) = self.os {
297
            ctx.os = os;
298
        }
299
        if let azul_css::dynamic_selector::OptionOsVersion::Some(os_version) = self.os_version {
300
            ctx.os_version = os_version;
301
        }
302
        if let azul_css::dynamic_selector::OptionLinuxDesktopEnv::Some(de) = self.desktop_env {
303
            ctx.desktop_env = azul_css::dynamic_selector::OptionLinuxDesktopEnv::Some(de);
304
        }
305
        if let azul_css::dynamic_selector::OptionThemeCondition::Some(ref theme) = self.theme {
306
            ctx.theme = theme.clone();
307
        }
308
        if let azul_css::OptionString::Some(ref lang) = self.language {
309
            ctx.language = lang.clone();
310
        }
311
        if let azul_css::OptionBool::Some(reduced) = self.prefers_reduced_motion {
312
            ctx.prefers_reduced_motion = if reduced {
313
                azul_css::dynamic_selector::BoolCondition::True
314
            } else {
315
                azul_css::dynamic_selector::BoolCondition::False
316
            };
317
        }
318
        if let azul_css::OptionBool::Some(high_contrast) = self.prefers_high_contrast {
319
            ctx.prefers_high_contrast = if high_contrast {
320
                azul_css::dynamic_selector::BoolCondition::True
321
            } else {
322
                azul_css::dynamic_selector::BoolCondition::False
323
            };
324
        }
325
        if let azul_css::OptionF32::Some(w) = self.viewport_width {
326
            ctx.viewport_width = w;
327
        }
328
        if let azul_css::OptionF32::Some(h) = self.viewport_height {
329
            ctx.viewport_height = h;
330
        }
331
    }
332
}
333

            
334
impl_option!(
335
    CssMockEnvironment,
336
    OptionCssMockEnvironment,
337
    copy = false,
338
    [Debug, Clone]
339
);
340

            
341
/// A route mapping a URL pattern to a layout callback.
342
///
343
/// Routes are cross-platform: on desktop, switching routes swaps the
344
/// active layout callback and triggers `RefreshDom`. On web, it also
345
/// calls `history.pushState()` for browser navigation.
346
///
347
/// # Pattern syntax
348
///
349
/// - `"/"` — exact root
350
/// - `"/about"` — exact path
351
/// - `"/user/:id"` — parameterized segment, `/user/42` yields `id = "42"`
352
///
353
/// # C API
354
/// ```c
355
/// AzAppConfig_addRoute(&config, AzString_fromConstStr("/user/:id"), layout_user);
356
/// ```
357
#[repr(C)]
358
pub struct Route {
359
    /// URL pattern (e.g. `"/"`, `"/about"`, `"/user/:id"`)
360
    pub pattern: AzString,
361
    /// Layout callback invoked when this route is active
362
    pub layout_callback: LayoutCallback,
363
}
364

            
365
impl Clone for Route {
366
    fn clone(&self) -> Self {
367
        Self { pattern: self.pattern.clone(), layout_callback: self.layout_callback.clone() }
368
    }
369
}
370
impl fmt::Debug for Route {
371
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
372
        f.debug_struct("Route")
373
            .field("pattern", &self.pattern)
374
            .field("layout_callback", &self.layout_callback)
375
            .finish()
376
    }
377
}
378
impl PartialEq for Route { fn eq(&self, o: &Self) -> bool { self.pattern == o.pattern && self.layout_callback == o.layout_callback } }
379
impl Eq for Route {}
380
impl PartialOrd for Route { fn partial_cmp(&self, o: &Self) -> Option<core::cmp::Ordering> { Some(self.cmp(o)) } }
381
impl Ord for Route { fn cmp(&self, o: &Self) -> core::cmp::Ordering { self.pattern.cmp(&o.pattern).then_with(|| self.layout_callback.cmp(&o.layout_callback)) } }
382
impl Hash for Route { fn hash<H: Hasher>(&self, state: &mut H) { self.pattern.hash(state); self.layout_callback.hash(state); } }
383

            
384
impl_option!(Route, OptionRoute, copy = false, [Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash]);
385
impl_vec!(Route, RouteVec, RouteVecDestructor, RouteVecDestructorType, RouteVecSlice, OptionRoute);
386
impl_vec_mut!(Route, RouteVec);
387
impl_vec_debug!(Route, RouteVec);
388
impl_vec_clone!(Route, RouteVec, RouteVecDestructor);
389
impl_vec_partialeq!(Route, RouteVec);
390
impl_vec_eq!(Route, RouteVec);
391
impl_vec_partialord!(Route, RouteVec);
392
impl_vec_ord!(Route, RouteVec);
393
impl_vec_hash!(Route, RouteVec);
394

            
395
/// Result of matching a URL against a route pattern.
396
///
397
/// Stores the matched pattern and any extracted parameters.
398
/// Available to layout callbacks via `LayoutCallbackInfo::get_route_param()`.
399
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
400
#[repr(C)]
401
pub struct RouteMatch {
402
    /// The matched route pattern (e.g. `"/user/:id"`)
403
    pub pattern: AzString,
404
    /// Extracted parameters (e.g. `[("id", "42")]`)
405
    pub params: StringPairVec,
406
}
407

            
408
impl RouteMatch {
409
    /// Get a route parameter by key.
410
    pub fn get_param(&self, key: &str) -> Option<&AzString> {
411
        self.params.get_key(key)
412
    }
413
}
414

            
415
impl_option!(RouteMatch, OptionRouteMatch, copy = false, [Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash]);
416

            
417
/// Match a URL path against a route pattern, extracting parameters.
418
///
419
/// Returns `Some(RouteMatch)` with extracted params on match, `None` otherwise.
420
///
421
/// # Examples
422
/// - pattern `"/user/:id"`, path `"/user/42"` → `Some(RouteMatch { params: [("id","42")] })`
423
/// - pattern `"/"`, path `"/"` → `Some(RouteMatch { params: [] })`
424
/// - pattern `"/about"`, path `"/settings"` → `None`
425
pub fn match_route(pattern: &str, path: &str) -> Option<RouteMatch> {
426
    let pat_segs: Vec<&str> = pattern.split('/').filter(|s| !s.is_empty()).collect();
427
    let path_segs: Vec<&str> = path.split('/').filter(|s| !s.is_empty()).collect();
428

            
429
    if pat_segs.len() != path_segs.len() {
430
        return None;
431
    }
432

            
433
    let mut params = Vec::new();
434
    for (pat, val) in pat_segs.iter().zip(path_segs.iter()) {
435
        if let Some(param_name) = pat.strip_prefix(':') {
436
            params.push(AzStringPair {
437
                key: AzString::from(param_name.to_string()),
438
                value: AzString::from(val.to_string()),
439
            });
440
        } else if pat != val {
441
            return None;
442
        }
443
    }
444

            
445
    Some(RouteMatch {
446
        pattern: AzString::from(pattern.to_string()),
447
        params: StringPairVec::from_vec(params),
448
    })
449
}
450

            
451
/// Configuration for optional features, such as whether to enable logging or panic hooks
452
#[derive(Debug, Clone)]
453
#[repr(C)]
454
pub struct AppConfig {
455
    /// If enabled, logs error and info messages.
456
    ///
457
    /// Default is `LevelFilter::Error` to log all errors by default
458
    pub log_level: AppLogLevel,
459
    /// If the app crashes / panics, a window with a message box pops up.
460
    /// Setting this to `false` disables the popup box.
461
    pub enable_visual_panic_hook: bool,
462
    /// If this is set to `true` (the default), a backtrace + error information
463
    /// gets logged to stdout and the logging file (only if logging is enabled).
464
    pub enable_logging_on_panic: bool,
465
    /// (STUB) Whether keyboard navigation should be enabled (default: true).
466
    /// Currently not implemented.
467
    pub enable_tab_navigation: bool,
468
    /// Determines what happens when all windows are closed.
469
    /// Default: EndProcess (terminate when last window closes).
470
    pub termination_behavior: AppTerminationBehavior,
471
    /// Icon provider for the application.
472
    /// Register icons here before calling App::run().
473
    /// Each window will clone this provider (cheap, Arc-based).
474
    pub icon_provider: crate::icon::IconProviderHandle,
475
    /// Fonts bundled with the application.
476
    /// These fonts are loaded into memory and take priority over system fonts.
477
    pub bundled_fonts: NamedFontVec,
478
    /// Configuration for how system fonts should be loaded.
479
    /// Default: LoadAllSystemFonts (scan all system fonts at startup)
480
    pub font_loading: FontLoadingConfig,
481
    /// Optional mock environment for CSS evaluation.
482
    /// 
483
    /// When set, this overrides the auto-detected system properties (OS, theme, etc.)
484
    /// for CSS @-rules and dynamic selectors. This is useful for:
485
    /// - Testing OS-specific styles on a different platform
486
    /// - Screenshot testing with consistent environment
487
    /// - Previewing how the app looks on different systems
488
    /// 
489
    /// Default: None (use auto-detected system properties)
490
    pub mock_css_environment: OptionCssMockEnvironment,
491
    /// System style detected at startup (theme, colors, fonts, etc.)
492
    /// 
493
    /// This is detected once at `AppConfig::create()` and passed to all windows.
494
    /// You can override this after creation to use a custom system style,
495
    /// for example to test how your app looks on a different platform.
496
    pub system_style: SystemStyle,
497
    /// Component libraries registered at startup.
498
    ///
499
    /// Use `add_component()` to register individual components, or
500
    /// `add_component_library()` to register entire libraries.
501
    /// User-registered (and built-in) component libraries.
502
    ///
503
    /// The 52 built-in HTML elements are automatically registered by
504
    /// `AppConfig::create()` via `register_builtin_components`.
505
    /// Additional libraries can be added with `add_component_library`.
506
    pub component_libraries: ComponentLibraryVec,
507
    /// Registered routes mapping URL patterns to layout callbacks.
508
    ///
509
    /// Cross-platform: on desktop, the active route determines which layout
510
    /// callback runs. On web, routes map to HTTP endpoints and browser URLs.
511
    ///
512
    /// The first route (or `"/"`) is the default. Use `add_route()` to register.
513
    pub routes: RouteVec,
514
}
515

            
516
impl AppConfig {
517
    pub fn create() -> Self {
518
        let log_level = AppLogLevel::Error;
519
        let icon_provider = crate::icon::IconProviderHandle::new();
520
        let bundled_fonts = NamedFontVec::from_const_slice(&[]);
521
        let font_loading = FontLoadingConfig::default();
522
        let system_style = SystemStyle::detect();
523
        let mut s = Self {
524
            log_level,
525
            enable_visual_panic_hook: false,
526
            enable_logging_on_panic: true,
527
            enable_tab_navigation: true,
528
            termination_behavior: AppTerminationBehavior::default(),
529
            icon_provider,
530
            bundled_fonts,
531
            font_loading,
532
            mock_css_environment: OptionCssMockEnvironment::None,
533
            system_style,
534
            component_libraries: ComponentLibraryVec::from_const_slice(&[]),
535
            routes: RouteVec::from_const_slice(&[]),
536
        };
537
        // Dogfood: register the 52 built-in HTML elements via the
538
        // same `add_component_library` API that users call.
539
        s.add_component_library(
540
            AzString::from_const_str("builtin"),
541
            crate::xml::register_builtin_components as extern "C" fn() -> crate::xml::ComponentLibrary,
542
        );
543
        s
544
    }
545
    
546
    /// Create config with a mock CSS environment for testing
547
    /// 
548
    /// This allows you to simulate how your app would look on a different OS,
549
    /// with a different theme, language, or accessibility settings.
550
    /// 
551
    /// # Example
552
    /// ```rust
553
    /// # use azul_core::resources::{AppConfig, CssMockEnvironment};
554
    /// # use azul_css::dynamic_selector::{OsCondition, OptionOsCondition, ThemeCondition, OptionThemeCondition};
555
    /// let config = AppConfig::create()
556
    ///     .with_mock_environment(CssMockEnvironment {
557
    ///         os: OptionOsCondition::Some(OsCondition::Linux),
558
    ///         theme: OptionThemeCondition::Some(ThemeCondition::Dark),
559
    ///         ..Default::default()
560
    ///     });
561
    /// ```
562
    pub fn with_mock_environment(mut self, env: CssMockEnvironment) -> Self {
563
        self.mock_css_environment = OptionCssMockEnvironment::Some(env);
564
        self
565
    }
566

            
567
    /// Register a single component into a named library.
568
    ///
569
    /// Calls `register_fn` immediately and adds the returned `ComponentDef`
570
    /// to the library named `library`. If no library with that name exists,
571
    /// a new one is created. If a component with the same `id.name` already
572
    /// exists in the library, it is replaced.
573
    ///
574
    /// # C API
575
    /// ```c
576
    /// AzAppConfig_addComponent(&config, AzString_fromConstStr("mylib"), my_register_fn);
577
    /// ```
578
    pub fn add_component<R: Into<RegisterComponentFn>>(&mut self, library: AzString, register_fn: R) {
579
        let register_fn = register_fn.into();
580
        let component = (register_fn.cb)();
581
        let empty_libs = ComponentLibraryVec::from_const_slice(&[]);
582
        let mut libs = core::mem::replace(&mut self.component_libraries, empty_libs).into_library_owned_vec();
583

            
584
        if let Some(existing_lib) = libs.iter_mut().find(|l| l.name.as_str() == library.as_str()) {
585
            let empty_comps = ComponentDefVec::from_const_slice(&[]);
586
            let mut comps = core::mem::replace(&mut existing_lib.components, empty_comps).into_library_owned_vec();
587
            if let Some(ec) = comps.iter_mut().find(|c| c.id.name.as_str() == component.id.name.as_str()) {
588
                *ec = component;
589
            } else {
590
                comps.push(component);
591
            }
592
            existing_lib.components = ComponentDefVec::from_vec(comps);
593
        } else {
594
            libs.push(ComponentLibrary {
595
                name: library,
596
                version: AzString::from_const_str("1.0.0"),
597
                description: AzString::from_const_str(""),
598
                components: ComponentDefVec::from_vec(alloc::vec![component]),
599
                exportable: true,
600
                modifiable: true,
601
                data_models: crate::xml::ComponentDataModelVec::from_const_slice(&[]),
602
                enum_models: crate::xml::ComponentEnumModelVec::from_const_slice(&[]),
603
            });
604
        }
605

            
606
        self.component_libraries = ComponentLibraryVec::from_vec(libs);
607
    }
608

            
609
    /// Register an entire component library.
610
    ///
611
    /// Calls `register_fn` immediately and adds the returned
612
    /// `ComponentLibrary` to the config. Uses `name` as the library name
613
    /// (overriding whatever the function sets). If a library with the same
614
    /// name already exists, it is replaced wholesale.
615
    ///
616
    /// # C API
617
    /// ```c
618
    /// AzAppConfig_addComponentLibrary(&config, AzString_fromConstStr("vendor"), my_lib_fn);
619
    /// ```
620
    pub fn add_component_library<R: Into<RegisterComponentLibraryFn>>(&mut self, name: AzString, register_fn: R) {
621
        let register_fn = register_fn.into();
622
        let mut library = (register_fn.cb)();
623
        library.name = name;
624

            
625
        let empty_libs = ComponentLibraryVec::from_const_slice(&[]);
626
        let mut libs = core::mem::replace(&mut self.component_libraries, empty_libs).into_library_owned_vec();
627
        if let Some(existing) = libs.iter_mut().find(|l| l.name.as_str() == library.name.as_str()) {
628
            *existing = library;
629
        } else {
630
            libs.push(library);
631
        }
632

            
633
        self.component_libraries = ComponentLibraryVec::from_vec(libs);
634
    }
635

            
636
    /// Register a route mapping a URL pattern to a layout callback.
637
    ///
638
    /// On web: each route becomes an HTTP endpoint. On desktop: the first
639
    /// route (or `"/"`) is the initial layout, and `CallbackInfo::switch_route()`
640
    /// swaps the active callback.
641
    ///
642
    /// # C API
643
    /// ```c
644
    /// AzAppConfig_addRoute(&config, AzString_fromConstStr("/user/:id"), layout_user);
645
    /// ```
646
    pub fn add_route<P: Into<AzString>, L: Into<LayoutCallback>>(&mut self, pattern: P, layout_fn: L) {
647
        let route = Route {
648
            pattern: pattern.into(),
649
            layout_callback: layout_fn.into(),
650
        };
651
        let empty = RouteVec::from_const_slice(&[]);
652
        let mut routes = core::mem::replace(&mut self.routes, empty).into_library_owned_vec();
653
        // Replace existing route with the same pattern
654
        if let Some(existing) = routes.iter_mut().find(|r| r.pattern.as_str() == route.pattern.as_str()) {
655
            *existing = route;
656
        } else {
657
            routes.push(route);
658
        }
659
        self.routes = RouteVec::from_vec(routes);
660
    }
661

            
662
    /// Find the route matching a given URL path.
663
    ///
664
    /// Returns the matched `Route` and a `RouteMatch` with extracted parameters.
665
    pub fn match_route_for_path(&self, path: &str) -> Option<(&Route, RouteMatch)> {
666
        for route in self.routes.as_ref().iter() {
667
            if let Some(m) = match_route(route.pattern.as_str(), path) {
668
                return Some((route, m));
669
            }
670
        }
671
        None
672
    }
673
}
674

            
675
impl Default for AppConfig {
676
    fn default() -> Self {
677
        Self::create()
678
    }
679
}
680

            
681
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
682
#[repr(C)]
683
pub enum AppLogLevel {
684
    Off,
685
    Error,
686
    Warn,
687
    Info,
688
    Debug,
689
    Trace,
690
}
691

            
692
/// Metadata (but not storage) describing an image In WebRender.
693
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
694
#[repr(C)]
695
pub struct ImageDescriptor {
696
    /// Format of the image data.
697
    pub format: RawImageFormat,
698
    /// Width and height of the image data, in pixels.
699
    pub width: usize,
700
    pub height: usize,
701
    /// The number of bytes from the start of one row to the next. If non-None,
702
    /// `compute_stride` will return this value, otherwise it returns
703
    /// `width * bpp`. Different source of images have different alignment
704
    /// constraints for rows, so the stride isn't always equal to width * bpp.
705
    pub stride: OptionI32,
706
    /// Offset in bytes of the first pixel of this image in its backing buffer.
707
    /// This is used for tiling, wherein WebRender extracts chunks of input images
708
    /// in order to cache, manipulate, and render them individually. This offset
709
    /// tells the texture upload machinery where to find the bytes to upload for
710
    /// this tile. Non-tiled images generally set this to zero.
711
    pub offset: i32,
712
    /// Various bool flags related to this descriptor.
713
    pub flags: ImageDescriptorFlags,
714
}
715

            
716
/// Various flags that are part of an image descriptor.
717
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
718
#[repr(C)]
719
pub struct ImageDescriptorFlags {
720
    /// Whether this image is opaque, or has an alpha channel. Avoiding blending
721
    /// for opaque surfaces is an important optimization.
722
    pub is_opaque: bool,
723
    /// Whether to allow the driver to automatically generate mipmaps. If images
724
    /// are already downscaled appropriately, mipmap generation can be wasted
725
    /// work, and cause performance problems on some cards/drivers.
726
    ///
727
    /// See https://github.com/servo/webrender/pull/2555/
728
    pub allow_mipmaps: bool,
729
}
730

            
731
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
732
pub struct IdNamespace(pub u32);
733

            
734
impl ::core::fmt::Display for IdNamespace {
735
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
736
        write!(f, "IdNamespace({})", self.0)
737
    }
738
}
739

            
740
impl ::core::fmt::Debug for IdNamespace {
741
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
742
        write!(f, "{}", self)
743
    }
744
}
745

            
746
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
747
#[repr(C)]
748
pub enum RawImageFormat {
749
    R8,
750
    RG8,
751
    RGB8,
752
    RGBA8,
753
    R16,
754
    RG16,
755
    RGB16,
756
    RGBA16,
757
    BGR8,
758
    BGRA8,
759
    RGBF32,
760
    RGBAF32,
761
}
762

            
763
// NOTE: starts at 1 (0 = DUMMY)
764
static IMAGE_KEY: AtomicU64 = AtomicU64::new(1);
765
static FONT_KEY: AtomicU64 = AtomicU64::new(0);
766
static FONT_INSTANCE_KEY: AtomicU64 = AtomicU64::new(0);
767

            
768
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
769
pub struct ImageKey {
770
    pub namespace: IdNamespace,
771
    pub key: u64,
772
}
773

            
774
impl ImageKey {
775
    pub const DUMMY: Self = Self {
776
        namespace: IdNamespace(0),
777
        key: 0,
778
    };
779

            
780
    pub fn unique(render_api_namespace: IdNamespace) -> Self {
781
        Self {
782
            namespace: render_api_namespace,
783
            key: IMAGE_KEY.fetch_add(1, AtomicOrdering::SeqCst),
784
        }
785
    }
786
}
787

            
788
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
789
pub struct FontKey {
790
    pub namespace: IdNamespace,
791
    pub key: u64,
792
}
793

            
794
impl FontKey {
795
    pub fn unique(render_api_namespace: IdNamespace) -> Self {
796
        Self {
797
            namespace: render_api_namespace,
798
            key: FONT_KEY.fetch_add(1, AtomicOrdering::SeqCst),
799
        }
800
    }
801
}
802

            
803
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
804
pub struct FontInstanceKey {
805
    pub namespace: IdNamespace,
806
    pub key: u64,
807
}
808

            
809
impl FontInstanceKey {
810
    pub fn unique(render_api_namespace: IdNamespace) -> Self {
811
        Self {
812
            namespace: render_api_namespace,
813
            key: FONT_INSTANCE_KEY.fetch_add(1, AtomicOrdering::SeqCst),
814
        }
815
    }
816
}
817

            
818
// NOTE: This type should NOT be exposed in the API!
819
// The only public functions are the constructors
820
#[derive(Debug)]
821
pub enum DecodedImage {
822
    /// Image that has a reserved key, but no data, i.e it is not yet rendered
823
    /// or there was an error during rendering
824
    NullImage {
825
        width: usize,
826
        height: usize,
827
        format: RawImageFormat,
828
        /// Sometimes images need to be tagged with extra data
829
        tag: Vec<u8>,
830
    },
831
    // OpenGl texture
832
    Gl(Texture),
833
    // Image backed by CPU-rendered pixels
834
    Raw((ImageDescriptor, ImageData)),
835
    // Same as `Texture`, but rendered AFTER the layout has been done
836
    Callback(CoreImageCallback),
837
    // YUVImage(...)
838
    // VulkanSurface(...)
839
    // MetalSurface(...),
840
    // DirectXSurface(...)
841
}
842

            
843
#[derive(Debug)]
844
#[repr(C)]
845
pub struct ImageRef {
846
    /// Shared pointer to an opaque implementation of the decoded image
847
    pub data: *const DecodedImage,
848
    /// How many copies does this image have (if 0, the font data will be deleted on drop)
849
    pub copies: *const AtomicUsize,
850
    pub run_destructor: bool,
851
}
852

            
853
impl ImageRef {
854
    pub fn get_hash(&self) -> ImageRefHash {
855
        image_ref_get_hash(self)
856
    }
857
}
858

            
859
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Hash, Ord, Eq)]
860
#[repr(C)]
861
pub struct ImageRefHash {
862
    pub inner: usize,
863
}
864

            
865
impl_option!(
866
    ImageRef,
867
    OptionImageRef,
868
    copy = false,
869
    [Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash]
870
);
871

            
872
impl ImageRef {
873
    /// If *copies = 1, returns the internal image data
874
    pub fn into_inner(self) -> Option<DecodedImage> {
875
        unsafe {
876
            if self.copies.as_ref().map(|m| m.load(AtomicOrdering::SeqCst)) == Some(1) {
877
                let data = Box::from_raw(self.data as *mut DecodedImage);
878
                let _ = Box::from_raw(self.copies as *mut AtomicUsize);
879
                core::mem::forget(self); // do not run the destructor
880
                Some(*data)
881
            } else {
882
                None
883
            }
884
        }
885
    }
886

            
887
211
    pub fn get_data<'a>(&'a self) -> &'a DecodedImage {
888
211
        unsafe { &*self.data }
889
211
    }
890

            
891
    pub fn get_image_callback<'a>(&'a self) -> Option<&'a CoreImageCallback> {
892
        if unsafe { self.copies.as_ref().map(|m| m.load(AtomicOrdering::SeqCst)) != Some(1) } {
893
            return None; // not safe
894
        }
895

            
896
        match unsafe { &*self.data } {
897
            DecodedImage::Callback(gl_texture_callback) => Some(gl_texture_callback),
898
            _ => None,
899
        }
900
    }
901

            
902
    pub fn get_image_callback_mut<'a>(&'a mut self) -> Option<&'a mut CoreImageCallback> {
903
        if unsafe { self.copies.as_ref().map(|m| m.load(AtomicOrdering::SeqCst)) != Some(1) } {
904
            return None; // not safe
905
        }
906

            
907
        match unsafe { &mut *(self.data as *mut DecodedImage) } {
908
            DecodedImage::Callback(gl_texture_callback) => Some(gl_texture_callback),
909
            _ => None,
910
        }
911
    }
912

            
913
    /// In difference to the default shallow copy, creates a new image ref
914
    pub fn deep_copy(&self) -> Self {
915
        let new_data = match self.get_data() {
916
            DecodedImage::NullImage {
917
                width,
918
                height,
919
                format,
920
                tag,
921
            } => DecodedImage::NullImage {
922
                width: *width,
923
                height: *height,
924
                format: *format,
925
                tag: tag.clone(),
926
            },
927
            // NOTE: textures cannot be deep-copied yet (since the OpenGL calls for that
928
            // are missing from the trait), so calling clone() on a GL texture will result in an
929
            // empty image
930
            DecodedImage::Gl(tex) => DecodedImage::NullImage {
931
                width: tex.size.width as usize,
932
                height: tex.size.height as usize,
933
                format: tex.format,
934
                tag: Vec::new(),
935
            },
936
            // WARNING: the data may still be a U8Vec<'static> - the data may still not be
937
            // actually cloned. The data only gets cloned on a write operation
938
            DecodedImage::Raw((descriptor, data)) => {
939
                DecodedImage::Raw((descriptor.clone(), data.clone()))
940
            }
941
            DecodedImage::Callback(cb) => DecodedImage::Callback(cb.clone()),
942
        };
943

            
944
        Self::new(new_data)
945
    }
946

            
947
    pub fn is_null_image(&self) -> bool {
948
        match self.get_data() {
949
            DecodedImage::NullImage { .. } => true,
950
            _ => false,
951
        }
952
    }
953

            
954
    pub fn is_gl_texture(&self) -> bool {
955
        match self.get_data() {
956
            DecodedImage::Gl(_) => true,
957
            _ => false,
958
        }
959
    }
960

            
961
    pub fn is_raw_image(&self) -> bool {
962
        match self.get_data() {
963
            DecodedImage::Raw((_, _)) => true,
964
            _ => false,
965
        }
966
    }
967

            
968
    pub fn is_callback(&self) -> bool {
969
        match self.get_data() {
970
            DecodedImage::Callback(_) => true,
971
            _ => false,
972
        }
973
    }
974

            
975
    // OptionRawImage
976
    pub fn get_rawimage(&self) -> Option<RawImage> {
977
        match self.get_data() {
978
            DecodedImage::Raw((image_descriptor, image_data)) => Some(RawImage {
979
                pixels: match image_data {
980
                    ImageData::Raw(shared_data) => {
981
                        // Clone the SharedRawImageData (increments ref count),
982
                        // then try to extract or convert to U8Vec
983
                        let data_clone = shared_data.clone();
984
                        if let Some(u8vec) = data_clone.into_inner() {
985
                            RawImageData::U8(u8vec)
986
                        } else {
987
                            // Multiple references exist, need to copy the data
988
                            RawImageData::U8(shared_data.as_ref().to_vec().into())
989
                        }
990
                    }
991
                    ImageData::External(_) => return None,
992
                },
993
                width: image_descriptor.width,
994
                height: image_descriptor.height,
995
                premultiplied_alpha: true,
996
                data_format: image_descriptor.format,
997
                tag: Vec::new().into(),
998
            }),
999
            _ => None,
        }
    }
    /// Get raw bytes from the image as a slice
    /// Returns None if this is not a Raw image or if it's an External image
    pub fn get_bytes(&self) -> Option<&[u8]> {
        match self.get_data() {
            DecodedImage::Raw((_, image_data)) => match image_data {
                ImageData::Raw(shared_data) => Some(shared_data.as_ref()),
                ImageData::External(_) => None,
            },
            _ => None,
        }
    }
    /// Get a pointer to the raw bytes for debugging/profiling purposes
    /// Returns a unique pointer for this ImageRef's data
    pub fn get_bytes_ptr(&self) -> *const u8 {
        match self.get_data() {
            DecodedImage::Raw((_, image_data)) => match image_data {
                ImageData::Raw(shared_data) => shared_data.as_ptr(),
                ImageData::External(_) => core::ptr::null(),
            },
            _ => core::ptr::null(),
        }
    }
    /// NOTE: returns (0, 0) for a Callback
    pub fn get_size(&self) -> LogicalSize {
        match self.get_data() {
            DecodedImage::NullImage { width, height, .. } => {
                LogicalSize::new(*width as f32, *height as f32)
            }
            DecodedImage::Gl(tex) => {
                LogicalSize::new(tex.size.width as f32, tex.size.height as f32)
            }
            DecodedImage::Raw((image_descriptor, _)) => LogicalSize::new(
                image_descriptor.width as f32,
                image_descriptor.height as f32,
            ),
            DecodedImage::Callback(_) => LogicalSize::new(0.0, 0.0),
        }
    }
3
    pub fn null_image(width: usize, height: usize, format: RawImageFormat, tag: Vec<u8>) -> Self {
3
        Self::new(DecodedImage::NullImage {
3
            width,
3
            height,
3
            format,
3
            tag,
3
        })
3
    }
    pub fn callback<C: Into<CoreRenderImageCallback>>(callback: C, data: RefAny) -> Self {
        Self::new(DecodedImage::Callback(CoreImageCallback {
            callback: callback.into(),
            refany: data,
        }))
    }
210
    pub fn new_rawimage(image_data: RawImage) -> Option<Self> {
210
        let (image_data, image_descriptor) = image_data.into_loaded_image_source()?;
210
        Some(Self::new(DecodedImage::Raw((image_descriptor, image_data))))
210
    }
    pub fn new_gltexture(texture: Texture) -> Self {
        Self::new(DecodedImage::Gl(texture))
    }
213
    fn new(data: DecodedImage) -> Self {
213
        Self {
213
            data: Box::into_raw(Box::new(data)),
213
            copies: Box::into_raw(Box::new(AtomicUsize::new(1))),
213
            run_destructor: true,
213
        }
213
    }
    // pub fn new_vulkan(...) -> Self
}
unsafe impl Send for ImageRef {}
unsafe impl Sync for ImageRef {}
impl PartialEq for ImageRef {
    fn eq(&self, rhs: &Self) -> bool {
        self.data as usize == rhs.data as usize
    }
}
impl PartialOrd for ImageRef {
    fn partial_cmp(&self, other: &Self) -> Option<::core::cmp::Ordering> {
        Some((self.data as usize).cmp(&(other.data as usize)))
    }
}
impl Ord for ImageRef {
    fn cmp(&self, other: &Self) -> core::cmp::Ordering {
        let self_data = self.data as usize;
        let other_data = other.data as usize;
        self_data.cmp(&other_data)
    }
}
impl Eq for ImageRef {}
impl Hash for ImageRef {
    fn hash<H>(&self, state: &mut H)
    where
        H: Hasher,
    {
        let self_data = self.data as usize;
        self_data.hash(state)
    }
}
impl Clone for ImageRef {
210
    fn clone(&self) -> Self {
        unsafe {
210
            self.copies
210
                .as_ref()
210
                .map(|m| m.fetch_add(1, AtomicOrdering::SeqCst));
        }
210
        Self {
210
            data: self.data,     // copy the pointer
210
            copies: self.copies, // copy the pointer
210
            run_destructor: true,
210
        }
210
    }
}
impl Drop for ImageRef {
423
    fn drop(&mut self) {
423
        self.run_destructor = false;
        unsafe {
423
            let copies = (*self.copies).fetch_sub(1, AtomicOrdering::SeqCst);
423
            if copies == 1 {
213
                let _ = Box::from_raw(self.data as *mut DecodedImage);
213
                let _ = Box::from_raw(self.copies as *mut AtomicUsize);
213
            }
        }
423
    }
}
pub fn image_ref_get_hash(ir: &ImageRef) -> ImageRefHash {
    ImageRefHash {
        inner: ir.data as usize,
    }
}
/// Convert a stable ImageRefHash directly to an ImageKey.
///
/// `ImageKey.key` is a `u64`, so the pointer-derived `ImageRefHash.inner: usize`
/// round-trips losslessly on both 32- and 64-bit platforms without any folding.
pub fn image_ref_hash_to_image_key(hash: ImageRefHash, namespace: IdNamespace) -> ImageKey {
    ImageKey {
        namespace,
        key: hash.inner as u64,
    }
}
pub fn font_ref_get_hash(fr: &FontRef) -> u64 {
    fr.get_hash()
}
/// Stores the resources for the application, such as fonts, images and cached
/// texts, also clipboard strings
///
/// Images and fonts can be references across window contexts (not yet tested,
/// but should work).
#[derive(Debug)]
pub struct ImageCache {
    /// The AzString is the string used in the CSS, i.e. url("my_image") = "my_image" -> ImageId(4)
    ///
    /// NOTE: This is the only map that is modifiable by the user and that has to be manually
    /// managed all other maps are library-internal only and automatically delete their
    /// resources once they aren't needed anymore
    pub image_id_map: OrderedMap<AzString, ImageRef>,
}
impl Default for ImageCache {
5256
    fn default() -> Self {
5256
        Self {
5256
            image_id_map: OrderedMap::default(),
5256
        }
5256
    }
}
impl ImageCache {
    pub fn new() -> Self {
        Self::default()
    }
    // -- ImageId cache
    pub fn add_css_image_id(&mut self, css_id: AzString, image: ImageRef) {
        self.image_id_map.insert(css_id, image);
    }
    pub fn get_css_image_id(&self, css_id: &AzString) -> Option<&ImageRef> {
        self.image_id_map.get(css_id)
    }
    pub fn delete_css_image_id(&mut self, css_id: &AzString) {
        self.image_id_map.remove(css_id);
    }
}
#[derive(Debug, Copy, Clone, PartialEq)]
pub struct ResolvedImage {
    pub key: ImageKey,
    pub descriptor: ImageDescriptor,
}
/// Trait for accessing font resources
pub trait RendererResourcesTrait: core::fmt::Debug {
    /// Get a font family hash from a font families hash
    fn get_font_family(
        &self,
        style_font_families_hash: &StyleFontFamiliesHash,
    ) -> Option<&StyleFontFamilyHash>;
    /// Get a font key from a font family hash
    fn get_font_key(&self, style_font_family_hash: &StyleFontFamilyHash) -> Option<&FontKey>;
    /// Get a registered font and its instances from a font key
    fn get_registered_font(
        &self,
        font_key: &FontKey,
    ) -> Option<&(FontRef, OrderedMap<(Au, DpiScaleFactor), FontInstanceKey>)>;
    /// Get image information from an image hash
    fn get_image(&self, hash: &ImageRefHash) -> Option<&ResolvedImage>;
    /// Update an image descriptor for an existing image hash
    fn update_image(
        &mut self,
        image_ref_hash: &ImageRefHash,
        descriptor: crate::resources::ImageDescriptor,
    );
}
// Implementation for the original RendererResources struct
impl RendererResourcesTrait for RendererResources {
    fn get_font_family(
        &self,
        style_font_families_hash: &StyleFontFamiliesHash,
    ) -> Option<&StyleFontFamilyHash> {
        self.font_families_map.get(style_font_families_hash)
    }
    fn get_font_key(&self, style_font_family_hash: &StyleFontFamilyHash) -> Option<&FontKey> {
        self.font_id_map.get(style_font_family_hash)
    }
    fn get_registered_font(
        &self,
        font_key: &FontKey,
    ) -> Option<&(FontRef, OrderedMap<(Au, DpiScaleFactor), FontInstanceKey>)> {
        self.currently_registered_fonts.get(font_key)
    }
    fn get_image(&self, hash: &ImageRefHash) -> Option<&ResolvedImage> {
        self.currently_registered_images.get(hash)
    }
    fn update_image(
        &mut self,
        image_ref_hash: &ImageRefHash,
        descriptor: crate::resources::ImageDescriptor,
    ) {
        if let Some(s) = self.currently_registered_images.get_mut(image_ref_hash) {
            s.descriptor = descriptor;
        }
    }
}
/// Renderer resources that manage font, image and font instance keys.
/// RendererResources are local to each renderer / window, since the
/// keys are not shared across renderers
///
/// The resources are automatically managed, meaning that they each new frame
/// (signified by start_frame_gc and end_frame_gc)
pub struct RendererResources {
    /// All image keys currently active in the RenderApi
    pub currently_registered_images: OrderedMap<ImageRefHash, ResolvedImage>,
    /// Reverse lookup: ImageKey -> ImageRefHash for display list translation
    pub image_key_map: OrderedMap<ImageKey, ImageRefHash>,
    /// All font keys currently active in the RenderApi
    pub currently_registered_fonts:
        OrderedMap<FontKey, (FontRef, OrderedMap<(Au, DpiScaleFactor), FontInstanceKey>)>,
    /// Fonts registered on the last frame
    ///
    /// Fonts differ from images in that regard that we can't immediately
    /// delete them on a new frame, instead we have to delete them on "current frame + 1"
    /// This is because when the frame is being built, we do not know
    /// whether the font will actually be successfully loaded
    pub last_frame_registered_fonts:
        OrderedMap<FontKey, OrderedMap<(Au, DpiScaleFactor), FontInstanceKey>>,
    /// Map from the calculated families vec (["Arial", "Helvetica"])
    /// to the final loaded font that could be loaded
    /// (in this case "Arial" on Windows and "Helvetica" on Mac,
    /// because the fonts are loaded in fallback-order)
    pub font_families_map: OrderedMap<StyleFontFamiliesHash, StyleFontFamilyHash>,
    /// Same as AzString -> ImageId, but for fonts, i.e. "Roboto" -> FontId(9)
    pub font_id_map: OrderedMap<StyleFontFamilyHash, FontKey>,
    /// Direct mapping from font hash (from FontRef) to FontKey
    /// TODO: This should become part of SharedFontRegistry
    pub font_hash_map: OrderedMap<u64, FontKey>,
}
impl fmt::Debug for RendererResources {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(
            f,
            "RendererResources {{
                currently_registered_images: {:#?},
                currently_registered_fonts: {:#?},
                font_families_map: {:#?},
                font_id_map: {:#?},
            }}",
            self.currently_registered_images.keys().collect::<Vec<_>>(),
            self.currently_registered_fonts.keys().collect::<Vec<_>>(),
            self.font_families_map.keys().collect::<Vec<_>>(),
            self.font_id_map.keys().collect::<Vec<_>>(),
        )
    }
}
impl Default for RendererResources {
7596
    fn default() -> Self {
7596
        Self {
7596
            currently_registered_images: OrderedMap::default(),
7596
            image_key_map: OrderedMap::default(),
7596
            currently_registered_fonts: OrderedMap::default(),
7596
            last_frame_registered_fonts: OrderedMap::default(),
7596
            font_families_map: OrderedMap::default(),
7596
            font_id_map: OrderedMap::default(),
7596
            font_hash_map: OrderedMap::default(),
7596
        }
7596
    }
}
impl RendererResources {
    pub fn get_renderable_font_data(
        &self,
        font_instance_key: &FontInstanceKey,
    ) -> Option<(&FontRef, Au, DpiScaleFactor)> {
        self.currently_registered_fonts
            .iter()
            .find_map(|(font_key, (font_ref, instances))| {
                instances.iter().find_map(|((au, dpi), instance_key)| {
                    if *instance_key == *font_instance_key {
                        Some((font_ref, *au, *dpi))
                    } else {
                        None
                    }
                })
            })
    }
    pub fn get_font_instance_key_for_text(
        &self,
        font_size_px: f32,
        css_property_cache: &CssPropertyCache,
        node_data: &NodeData,
        node_id: &NodeId,
        styled_node_state: &StyledNodeState,
        dpi_scale: f32,
    ) -> Option<FontInstanceKey> {
        // Convert font size to StyleFontSize
        let font_size = StyleFontSize {
            inner: azul_css::props::basic::PixelValue::const_px(font_size_px as isize),
        };
        // Convert to application units
        let font_size_au = font_size_to_au(font_size);
        // Create DPI scale factor
        let dpi_scale_factor = DpiScaleFactor {
            inner: FloatValue::new(dpi_scale),
        };
        // Get font family
        let font_family =
            css_property_cache.get_font_id_or_default(node_data, node_id, styled_node_state);
        // Calculate hash and lookup font instance key
        let font_families_hash = StyleFontFamiliesHash::new(font_family.as_ref());
        self.get_font_instance_key(&font_families_hash, font_size_au, dpi_scale_factor)
    }
    pub fn get_font_instance_key(
        &self,
        font_families_hash: &StyleFontFamiliesHash,
        font_size_au: Au,
        dpi_scale: DpiScaleFactor,
    ) -> Option<FontInstanceKey> {
        let font_family_hash = self.get_font_family(font_families_hash)?;
        let font_key = self.get_font_key(font_family_hash)?;
        let (_, instances) = self.get_registered_font(font_key)?;
        instances.get(&(font_size_au, dpi_scale)).copied()
    }
    // Delete all font family hashes that do not have a font key anymore
    fn remove_font_families_with_zero_references(&mut self) {
        let font_family_to_delete = self
            .font_id_map
            .iter()
            .filter_map(|(font_family, font_key)| {
                if !self.currently_registered_fonts.contains_key(font_key) {
                    Some(font_family.clone())
                } else {
                    None
                }
            })
            .collect::<Vec<_>>();
        for f in font_family_to_delete {
            self.font_id_map.remove(&f); // font key does not exist anymore
        }
        let font_families_to_delete = self
            .font_families_map
            .iter()
            .filter_map(|(font_families, font_family)| {
                if !self.font_id_map.contains_key(font_family) {
                    Some(font_families.clone())
                } else {
                    None
                }
            })
            .collect::<Vec<_>>();
        for f in font_families_to_delete {
            self.font_families_map.remove(&f); // font family does not exist anymore
        }
    }
}
// Result returned from rerender_image_callback() - should be used as:
//
// ```rust
// txn.update_image(
//     wr_translate_image_key(key),
//     wr_translate_image_descriptor(descriptor),
//     wr_translate_image_data(data),
//     &WrImageDirtyRect::All,
// );
// ```
#[derive(Debug, Clone)]
pub struct UpdateImageResult {
    pub key_to_update: ImageKey,
    pub new_descriptor: ImageDescriptor,
    pub new_image_data: ImageData,
}
#[derive(Debug, Default)]
pub struct GlTextureCache {
    pub solved_textures:
        BTreeMap<DomId, BTreeMap<NodeId, (ImageKey, ImageDescriptor, ExternalImageId)>>,
    pub hashes: BTreeMap<(DomId, NodeId, ImageRefHash), ImageRefHash>,
}
// necessary so the display list can be built in parallel
unsafe impl Send for GlTextureCache {}
impl GlTextureCache {
    /// Initializes an empty cache
    pub fn empty() -> Self {
        Self {
            solved_textures: BTreeMap::new(),
            hashes: BTreeMap::new(),
        }
    }
    /// Updates a given texture
    ///
    /// This is called when a texture needs to be re-rendered (e.g., on resize or animation frame).
    /// It updates the texture in the WebRender external image cache and updates the internal
    /// descriptor to reflect the new size.
    ///
    /// # Arguments
    ///
    /// * `dom_id` - The DOM ID containing the texture
    /// * `node_id` - The node ID of the image element
    /// * `document_id` - The WebRender document ID
    /// * `epoch` - The current frame epoch
    /// * `new_texture` - The new texture to use
    /// * `insert_into_active_gl_textures_fn` - Function to insert the texture into the cache
    ///
    /// # Returns
    ///
    /// The ExternalImageId if successful, None if the texture wasn't found in the cache
    pub fn update_texture(
        &mut self,
        dom_id: DomId,
        node_id: NodeId,
        document_id: DocumentId,
        epoch: Epoch,
        new_texture: Texture,
        insert_into_active_gl_textures_fn: &GlStoreImageFn,
    ) -> Option<ExternalImageId> {
        let new_descriptor = new_texture.get_descriptor();
        let di_map = self.solved_textures.get_mut(&dom_id)?;
        let entry = di_map.get_mut(&node_id)?;
        // Update the descriptor
        entry.1 = new_descriptor;
        // The ExternalImageId is deterministic from (dom_id, node_id), so the cache
        // entry can keep referencing the same id across re-renders.
        let external_image_id = texture_external_image_id(dom_id, node_id);
        (insert_into_active_gl_textures_fn)(document_id, epoch, new_texture, external_image_id);
        entry.2 = external_image_id;
        Some(external_image_id)
    }
}
macro_rules! unique_id {
    ($struct_name:ident, $counter_name:ident) => {
        #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)]
        #[repr(C)]
        pub struct $struct_name {
            pub id: usize,
        }
        impl $struct_name {
210
            pub fn unique() -> Self {
210
                Self {
210
                    id: $counter_name.fetch_add(1, AtomicOrdering::SeqCst),
210
                }
210
            }
        }
    };
}
// NOTE: the property key is unique across transform, color and opacity properties
static PROPERTY_KEY_COUNTER: AtomicUsize = AtomicUsize::new(0);
unique_id!(TransformKey, PROPERTY_KEY_COUNTER);
unique_id!(ColorKey, PROPERTY_KEY_COUNTER);
unique_id!(OpacityKey, PROPERTY_KEY_COUNTER);
static IMAGE_ID_COUNTER: AtomicUsize = AtomicUsize::new(0);
unique_id!(ImageId, IMAGE_ID_COUNTER);
static FONT_ID_COUNTER: AtomicUsize = AtomicUsize::new(0);
unique_id!(FontId, FONT_ID_COUNTER);
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[repr(C)]
pub struct ImageMask {
    pub image: ImageRef,
    pub rect: LogicalRect,
    pub repeat: bool,
}
impl_option!(
    ImageMask,
    OptionImageMask,
    copy = false,
    [Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash]
);
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum ImmediateFontId {
    Resolved((StyleFontFamilyHash, FontKey)),
    Unresolved(StyleFontFamilyVec),
}
#[derive(Debug, Clone, PartialEq, PartialOrd)]
#[repr(C, u8)]
pub enum RawImageData {
    // 8-bit image data
    U8(U8Vec),
    // 16-bit image data
    U16(U16Vec),
    // HDR image data
    F32(F32Vec),
}
impl RawImageData {
    pub fn get_u8_vec_ref(&self) -> Option<&U8Vec> {
        match self {
            RawImageData::U8(v) => Some(v),
            _ => None,
        }
    }
    pub fn get_u16_vec_ref(&self) -> Option<&U16Vec> {
        match self {
            RawImageData::U16(v) => Some(v),
            _ => None,
        }
    }
    pub fn get_f32_vec_ref(&self) -> Option<&F32Vec> {
        match self {
            RawImageData::F32(v) => Some(v),
            _ => None,
        }
    }
210
    fn get_u8_vec(self) -> Option<U8Vec> {
210
        match self {
210
            RawImageData::U8(v) => Some(v),
            _ => None,
        }
210
    }
    fn get_u16_vec(self) -> Option<U16Vec> {
        match self {
            RawImageData::U16(v) => Some(v),
            _ => None,
        }
    }
}
#[derive(Debug, Clone, PartialEq, PartialOrd)]
#[repr(C)]
pub struct RawImage {
    pub pixels: RawImageData,
    pub width: usize,
    pub height: usize,
    pub premultiplied_alpha: bool,
    pub data_format: RawImageFormat,
    pub tag: U8Vec,
}
impl RawImage {
    /// Returns a null / empty image
    pub fn null_image() -> Self {
        Self {
            pixels: RawImageData::U8(Vec::new().into()),
            width: 0,
            height: 0,
            premultiplied_alpha: true,
            data_format: RawImageFormat::BGRA8,
            tag: Vec::new().into(),
        }
    }
    /// Allocates a width * height, single-channel mask, used for drawing CPU image masks
    pub fn allocate_mask(size: LayoutSize) -> Self {
        Self {
            pixels: RawImageData::U8(
                vec![0; size.width.max(0) as usize * size.height.max(0) as usize].into(),
            ),
            width: size.width as usize,
            height: size.height as usize,
            premultiplied_alpha: true,
            data_format: RawImageFormat::R8,
            tag: Vec::new().into(),
        }
    }
    /// Encodes a RawImage as BGRA8 bytes and premultiplies it if the alpha is not premultiplied
    ///
    /// Returns None if the width * height * BPP does not match
    ///
    /// TODO: autovectorization fails spectacularly, need to manually optimize!
210
    pub fn into_loaded_image_source(self) -> Option<(ImageData, ImageDescriptor)> {
        // From webrender/wrench
        // These are slow. Gecko's gfx/2d/Swizzle.cpp has better versions
        #[inline(always)]
        fn premultiply_alpha(array: &mut [u8]) {
            if array.len() != 4 {
                return;
            }
            let a = u32::from(array[3]);
            array[0] = (((array[0] as u32 * a) + 128) / 255) as u8;
            array[1] = (((array[1] as u32 * a) + 128) / 255) as u8;
            array[2] = (((array[2] as u32 * a) + 128) / 255) as u8;
        }
        #[inline(always)]
        fn normalize_u16(i: u16) -> u8 {
            ((core::u16::MAX as f32 / i as f32) * core::u8::MAX as f32) as u8
        }
        let RawImage {
210
            width,
210
            height,
210
            pixels,
210
            mut data_format,
210
            premultiplied_alpha,
210
            tag,
210
        } = self;
        const FOUR_BPP: usize = 4;
        const TWO_CHANNELS: usize = 2;
        const THREE_CHANNELS: usize = 3;
        const FOUR_CHANNELS: usize = 4;
210
        let mut is_opaque = true;
210
        let expected_len = width * height;
210
        let bytes: U8Vec = match data_format {
            RawImageFormat::R8 => {
                // Keep R8 data as-is — WebRender supports R8 natively.
                // This is important for image mask clips which need the
                // single-channel data (white=visible, black=clipped).
210
                let pixels = pixels.get_u8_vec()?;
210
                if pixels.len() != expected_len {
                    return None;
210
                }
210
                is_opaque = false;
210
                pixels
            }
            RawImageFormat::RG8 => {
                let pixels = pixels.get_u8_vec()?;
                if pixels.len() != expected_len * TWO_CHANNELS {
                    return None;
                }
                let mut px = vec![0; expected_len * FOUR_BPP];
                // TODO: check that this function is SIMD optimized
                for (pixel_index, greyalpha) in
                    pixels.as_ref().chunks_exact(TWO_CHANNELS).enumerate()
                {
                    let grey = greyalpha[0];
                    let alpha = greyalpha[1];
                    if alpha != 255 {
                        is_opaque = false;
                    }
                    px[pixel_index * FOUR_BPP] = grey;
                    px[(pixel_index * FOUR_BPP) + 1] = grey;
                    px[(pixel_index * FOUR_BPP) + 2] = grey;
                    px[(pixel_index * FOUR_BPP) + 3] = alpha;
                    if !premultiplied_alpha {
                        premultiply_alpha(
                            &mut px[(pixel_index * FOUR_BPP)..((pixel_index * FOUR_BPP) + FOUR_BPP)],
                        );
                    }
                }
                data_format = RawImageFormat::BGRA8;
                px.into()
            }
            RawImageFormat::RGB8 => {
                let pixels = pixels.get_u8_vec()?;
                if pixels.len() != expected_len * THREE_CHANNELS {
                    return None;
                }
                let mut px = vec![0; expected_len * FOUR_BPP];
                // TODO: check that this function is SIMD optimized
                for (pixel_index, rgb) in pixels.as_ref().chunks_exact(THREE_CHANNELS).enumerate() {
                    let red = rgb[0];
                    let green = rgb[1];
                    let blue = rgb[2];
                    px[pixel_index * FOUR_BPP] = blue;
                    px[(pixel_index * FOUR_BPP) + 1] = green;
                    px[(pixel_index * FOUR_BPP) + 2] = red;
                    px[(pixel_index * FOUR_BPP) + 3] = 0xff;
                }
                data_format = RawImageFormat::BGRA8;
                px.into()
            }
            RawImageFormat::RGBA8 => {
                let mut pixels: Vec<u8> = pixels.get_u8_vec()?.into_library_owned_vec();
                if pixels.len() != expected_len * FOUR_CHANNELS {
                    return None;
                }
                // TODO: check that this function is SIMD optimized
                // no extra allocation necessary, but swizzling
                if premultiplied_alpha {
                    for rgba in pixels.chunks_exact_mut(4) {
                        let (r, gba) = rgba.split_first_mut()?;
                        core::mem::swap(r, gba.get_mut(1)?);
                        let a = rgba.get_mut(3)?;
                        if *a != 255 {
                            is_opaque = false;
                        }
                    }
                } else {
                    for rgba in pixels.chunks_exact_mut(4) {
                        // RGBA => BGRA
                        let (r, gba) = rgba.split_first_mut()?;
                        core::mem::swap(r, gba.get_mut(1)?);
                        let a = rgba.get_mut(3)?;
                        if *a != 255 {
                            is_opaque = false;
                        }
                        premultiply_alpha(rgba); // <-
                    }
                }
                data_format = RawImageFormat::BGRA8;
                pixels.into()
            }
            RawImageFormat::R16 => {
                let pixels = pixels.get_u16_vec()?;
                if pixels.len() != expected_len {
                    return None;
                }
                let mut px = vec![0; expected_len * FOUR_BPP];
                // TODO: check that this function is SIMD optimized
                for (pixel_index, grey_u16) in pixels.as_ref().iter().enumerate() {
                    let grey_u8 = normalize_u16(*grey_u16);
                    px[pixel_index * FOUR_BPP] = grey_u8;
                    px[(pixel_index * FOUR_BPP) + 1] = grey_u8;
                    px[(pixel_index * FOUR_BPP) + 2] = grey_u8;
                    px[(pixel_index * FOUR_BPP) + 3] = 0xff;
                }
                data_format = RawImageFormat::BGRA8;
                px.into()
            }
            RawImageFormat::RG16 => {
                let pixels = pixels.get_u16_vec()?;
                if pixels.len() != expected_len * TWO_CHANNELS {
                    return None;
                }
                let mut px = vec![0; expected_len * FOUR_BPP];
                // TODO: check that this function is SIMD optimized
                for (pixel_index, greyalpha) in
                    pixels.as_ref().chunks_exact(TWO_CHANNELS).enumerate()
                {
                    let grey_u8 = normalize_u16(greyalpha[0]);
                    let alpha_u8 = normalize_u16(greyalpha[1]);
                    if alpha_u8 != 255 {
                        is_opaque = false;
                    }
                    px[pixel_index * FOUR_BPP] = grey_u8;
                    px[(pixel_index * FOUR_BPP) + 1] = grey_u8;
                    px[(pixel_index * FOUR_BPP) + 2] = grey_u8;
                    px[(pixel_index * FOUR_BPP) + 3] = alpha_u8;
                }
                data_format = RawImageFormat::BGRA8;
                px.into()
            }
            RawImageFormat::RGB16 => {
                let pixels = pixels.get_u16_vec()?;
                if pixels.len() != expected_len * THREE_CHANNELS {
                    return None;
                }
                let mut px = vec![0; expected_len * FOUR_BPP];
                // TODO: check that this function is SIMD optimized
                for (pixel_index, rgb) in pixels.as_ref().chunks_exact(THREE_CHANNELS).enumerate() {
                    let red_u8 = normalize_u16(rgb[0]);
                    let green_u8 = normalize_u16(rgb[1]);
                    let blue_u8 = normalize_u16(rgb[2]);
                    px[pixel_index * FOUR_BPP] = blue_u8;
                    px[(pixel_index * FOUR_BPP) + 1] = green_u8;
                    px[(pixel_index * FOUR_BPP) + 2] = red_u8;
                    px[(pixel_index * FOUR_BPP) + 3] = 0xff;
                }
                data_format = RawImageFormat::BGRA8;
                px.into()
            }
            RawImageFormat::RGBA16 => {
                let pixels = pixels.get_u16_vec()?;
                if pixels.len() != expected_len * FOUR_CHANNELS {
                    return None;
                }
                let mut px = vec![0; expected_len * FOUR_BPP];
                // TODO: check that this function is SIMD optimized
                if premultiplied_alpha {
                    for (pixel_index, rgba) in
                        pixels.as_ref().chunks_exact(FOUR_CHANNELS).enumerate()
                    {
                        let red_u8 = normalize_u16(rgba[0]);
                        let green_u8 = normalize_u16(rgba[1]);
                        let blue_u8 = normalize_u16(rgba[2]);
                        let alpha_u8 = normalize_u16(rgba[3]);
                        if alpha_u8 != 255 {
                            is_opaque = false;
                        }
                        px[pixel_index * FOUR_BPP] = blue_u8;
                        px[(pixel_index * FOUR_BPP) + 1] = green_u8;
                        px[(pixel_index * FOUR_BPP) + 2] = red_u8;
                        px[(pixel_index * FOUR_BPP) + 3] = alpha_u8;
                    }
                } else {
                    for (pixel_index, rgba) in
                        pixels.as_ref().chunks_exact(FOUR_CHANNELS).enumerate()
                    {
                        let red_u8 = normalize_u16(rgba[0]);
                        let green_u8 = normalize_u16(rgba[1]);
                        let blue_u8 = normalize_u16(rgba[2]);
                        let alpha_u8 = normalize_u16(rgba[3]);
                        if alpha_u8 != 255 {
                            is_opaque = false;
                        }
                        px[pixel_index * FOUR_BPP] = blue_u8;
                        px[(pixel_index * FOUR_BPP) + 1] = green_u8;
                        px[(pixel_index * FOUR_BPP) + 2] = red_u8;
                        px[(pixel_index * FOUR_BPP) + 3] = alpha_u8;
                        premultiply_alpha(
                            &mut px
                                [(pixel_index * FOUR_BPP)..((pixel_index * FOUR_BPP) + FOUR_BPP)],
                        );
                    }
                }
                data_format = RawImageFormat::BGRA8;
                px.into()
            }
            RawImageFormat::BGR8 => {
                let pixels = pixels.get_u8_vec()?;
                if pixels.len() != expected_len * THREE_CHANNELS {
                    return None;
                }
                let mut px = vec![0; expected_len * FOUR_BPP];
                // TODO: check that this function is SIMD optimized
                for (pixel_index, bgr) in pixels.as_ref().chunks_exact(THREE_CHANNELS).enumerate() {
                    let blue = bgr[0];
                    let green = bgr[1];
                    let red = bgr[2];
                    px[pixel_index * FOUR_BPP] = blue;
                    px[(pixel_index * FOUR_BPP) + 1] = green;
                    px[(pixel_index * FOUR_BPP) + 2] = red;
                    px[(pixel_index * FOUR_BPP) + 3] = 0xff;
                }
                data_format = RawImageFormat::BGRA8;
                px.into()
            }
            RawImageFormat::BGRA8 => {
                if premultiplied_alpha {
                    // DO NOT CLONE THE IMAGE HERE!
                    let pixels = pixels.get_u8_vec()?;
                    is_opaque = pixels
                        .as_ref()
                        .chunks_exact(FOUR_CHANNELS)
                        .all(|bgra| bgra[3] == 255);
                    pixels
                } else {
                    let mut pixels: Vec<u8> = pixels.get_u8_vec()?.into_library_owned_vec();
                    if pixels.len() != expected_len * FOUR_BPP {
                        return None;
                    }
                    for bgra in pixels.chunks_exact_mut(FOUR_CHANNELS) {
                        if bgra[3] != 255 {
                            is_opaque = false;
                        }
                        premultiply_alpha(bgra);
                    }
                    data_format = RawImageFormat::BGRA8;
                    pixels.into()
                }
            }
            RawImageFormat::RGBF32 => {
                let pixels = pixels.get_f32_vec_ref()?;
                if pixels.len() != expected_len * THREE_CHANNELS {
                    return None;
                }
                let mut px = vec![0; expected_len * FOUR_BPP];
                // TODO: check that this function is SIMD optimized
                for (pixel_index, rgb) in pixels.as_ref().chunks_exact(THREE_CHANNELS).enumerate() {
                    let red_u8 = (rgb[0] * 255.0) as u8;
                    let green_u8 = (rgb[1] * 255.0) as u8;
                    let blue_u8 = (rgb[2] * 255.0) as u8;
                    px[pixel_index * FOUR_BPP] = blue_u8;
                    px[(pixel_index * FOUR_BPP) + 1] = green_u8;
                    px[(pixel_index * FOUR_BPP) + 2] = red_u8;
                    px[(pixel_index * FOUR_BPP) + 3] = 0xff;
                }
                data_format = RawImageFormat::BGRA8;
                px.into()
            }
            RawImageFormat::RGBAF32 => {
                let pixels = pixels.get_f32_vec_ref()?;
                if pixels.len() != expected_len * FOUR_CHANNELS {
                    return None;
                }
                let mut px = vec![0; expected_len * FOUR_BPP];
                // TODO: check that this function is SIMD optimized
                if premultiplied_alpha {
                    for (pixel_index, rgba) in
                        pixels.as_ref().chunks_exact(FOUR_CHANNELS).enumerate()
                    {
                        let red_u8 = (rgba[0] * 255.0) as u8;
                        let green_u8 = (rgba[1] * 255.0) as u8;
                        let blue_u8 = (rgba[2] * 255.0) as u8;
                        let alpha_u8 = (rgba[3] * 255.0) as u8;
                        if alpha_u8 != 255 {
                            is_opaque = false;
                        }
                        px[pixel_index * FOUR_BPP] = blue_u8;
                        px[(pixel_index * FOUR_BPP) + 1] = green_u8;
                        px[(pixel_index * FOUR_BPP) + 2] = red_u8;
                        px[(pixel_index * FOUR_BPP) + 3] = alpha_u8;
                    }
                } else {
                    for (pixel_index, rgba) in
                        pixels.as_ref().chunks_exact(FOUR_CHANNELS).enumerate()
                    {
                        let red_u8 = (rgba[0] * 255.0) as u8;
                        let green_u8 = (rgba[1] * 255.0) as u8;
                        let blue_u8 = (rgba[2] * 255.0) as u8;
                        let alpha_u8 = (rgba[3] * 255.0) as u8;
                        if alpha_u8 != 255 {
                            is_opaque = false;
                        }
                        px[pixel_index * FOUR_BPP] = blue_u8;
                        px[(pixel_index * FOUR_BPP) + 1] = green_u8;
                        px[(pixel_index * FOUR_BPP) + 2] = red_u8;
                        px[(pixel_index * FOUR_BPP) + 3] = alpha_u8;
                        premultiply_alpha(
                            &mut px
                                [(pixel_index * FOUR_BPP)..((pixel_index * FOUR_BPP) + FOUR_BPP)],
                        );
                    }
                }
                data_format = RawImageFormat::BGRA8;
                px.into()
            }
        };
210
        let image_data = ImageData::Raw(SharedRawImageData::new(bytes));
210
        let image_descriptor = ImageDescriptor {
210
            format: data_format,
210
            width,
210
            height,
210
            offset: 0,
210
            stride: None.into(),
210
            flags: ImageDescriptorFlags {
210
                is_opaque,
210
                allow_mipmaps: true,
210
            },
210
        };
210
        Some((image_data, image_descriptor))
210
    }
}
impl_option!(
    RawImage,
    OptionRawImage,
    copy = false,
    [Debug, Clone, PartialEq, PartialOrd]
);
pub fn font_size_to_au(font_size: StyleFontSize) -> Au {
    Au::from_px(font_size.inner.to_pixels_internal(0.0, DEFAULT_FONT_SIZE, DEFAULT_FONT_SIZE))
}
pub type FontInstanceFlags = u32;
// Common flags
pub const FONT_INSTANCE_FLAG_SYNTHETIC_BOLD: u32 = 1 << 1;
pub const FONT_INSTANCE_FLAG_EMBEDDED_BITMAPS: u32 = 1 << 2;
pub const FONT_INSTANCE_FLAG_SUBPIXEL_BGR: u32 = 1 << 3;
pub const FONT_INSTANCE_FLAG_TRANSPOSE: u32 = 1 << 4;
pub const FONT_INSTANCE_FLAG_FLIP_X: u32 = 1 << 5;
pub const FONT_INSTANCE_FLAG_FLIP_Y: u32 = 1 << 6;
pub const FONT_INSTANCE_FLAG_SUBPIXEL_POSITION: u32 = 1 << 7;
// Windows flags
pub const FONT_INSTANCE_FLAG_FORCE_GDI: u32 = 1 << 16;
// Mac flags
pub const FONT_INSTANCE_FLAG_FONT_SMOOTHING: u32 = 1 << 16;
// FreeType flags
pub const FONT_INSTANCE_FLAG_FORCE_AUTOHINT: u32 = 1 << 16;
pub const FONT_INSTANCE_FLAG_NO_AUTOHINT: u32 = 1 << 17;
pub const FONT_INSTANCE_FLAG_VERTICAL_LAYOUT: u32 = 1 << 18;
pub const FONT_INSTANCE_FLAG_LCD_VERTICAL: u32 = 1 << 19;
#[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash)]
pub struct GlyphOptions {
    pub render_mode: FontRenderMode,
    pub flags: FontInstanceFlags,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash)]
pub enum FontRenderMode {
    Mono,
    Alpha,
    Subpixel,
}
#[cfg(target_arch = "wasm32")]
#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash)]
pub struct FontInstancePlatformOptions {
    // empty for now
}
#[cfg(target_os = "windows")]
#[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash)]
pub struct FontInstancePlatformOptions {
    pub gamma: u16,
    pub contrast: u8,
    pub cleartype_level: u8,
}
#[cfg(target_os = "macos")]
#[derive(Default, Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash)]
pub struct FontInstancePlatformOptions {
    pub unused: u32,
}
#[cfg(target_os = "linux")]
#[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash)]
pub struct FontInstancePlatformOptions {
    pub lcd_filter: FontLCDFilter,
    pub hinting: FontHinting,
}
// Mobile targets — empty platform-options struct keeps the
// `FontInstanceOptions { platform_options: Option<...>, .. }` field
// well-typed without inheriting Linux's freetype-specific tunables.
#[cfg(any(target_os = "android", target_os = "ios"))]
#[derive(Default, Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash)]
pub struct FontInstancePlatformOptions {
    pub unused: u32,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash)]
pub enum FontHinting {
    None,
    Mono,
    Light,
    Normal,
    LCD,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash)]
pub enum FontLCDFilter {
    None,
    Default,
    Light,
    Legacy,
}
impl Default for FontLCDFilter {
    fn default() -> Self {
        FontLCDFilter::Default
    }
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash)]
pub struct FontInstanceOptions {
    pub render_mode: FontRenderMode,
    pub flags: FontInstanceFlags,
    pub bg_color: ColorU,
    /// When bg_color.a is != 0 and render_mode is FontRenderMode::Subpixel,
    /// the text will be rendered with bg_color.r/g/b as an opaque estimated
    /// background color.
    pub synthetic_italics: SyntheticItalics,
}
impl Default for FontInstanceOptions {
    fn default() -> FontInstanceOptions {
        FontInstanceOptions {
            render_mode: FontRenderMode::Subpixel,
            flags: 0,
            bg_color: ColorU::TRANSPARENT,
            synthetic_italics: SyntheticItalics::default(),
        }
    }
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash)]
pub struct SyntheticItalics {
    pub angle: i16,
}
impl Default for SyntheticItalics {
    fn default() -> Self {
        Self { angle: 0 }
    }
}
/// Reference-counted wrapper around raw image bytes (U8Vec).
/// This allows sharing image data between azul-core and webrender without cloning.
///
/// Similar to ImageRef but specifically for raw byte data, avoiding the overhead
/// of the full DecodedImage enum when we just need the bytes.
#[derive(Debug)]
#[repr(C)]
pub struct SharedRawImageData {
    /// Shared pointer to the raw image bytes
    pub data: *const U8Vec,
    /// Reference counter - when it reaches 0, the data is deallocated
    pub copies: *const AtomicUsize,
    /// Whether to run the destructor (for FFI safety)
    pub run_destructor: bool,
}
impl SharedRawImageData {
    /// Create a new SharedRawImageData from a U8Vec
210
    pub fn new(data: U8Vec) -> Self {
210
        Self {
210
            data: Box::into_raw(Box::new(data)),
210
            copies: Box::into_raw(Box::new(AtomicUsize::new(1))),
210
            run_destructor: true,
210
        }
210
    }
    /// Get a reference to the underlying bytes
210
    pub fn as_ref(&self) -> &[u8] {
210
        unsafe { (*self.data).as_ref() }
210
    }
    /// Alias for as_ref() - get the raw bytes as a slice
    pub fn get_bytes(&self) -> &[u8] {
        self.as_ref()
    }
    /// Get a pointer to the raw bytes for hashing/identification
    pub fn as_ptr(&self) -> *const u8 {
        unsafe { (*self.data).as_ref().as_ptr() }
    }
    /// Get the length of the data
    pub fn len(&self) -> usize {
        unsafe { (*self.data).len() }
    }
    /// Check if the data is empty
    pub fn is_empty(&self) -> bool {
        self.len() == 0
    }
    /// Try to extract the U8Vec if this is the only reference
    /// Returns None if there are other references
    pub fn into_inner(self) -> Option<U8Vec> {
        unsafe {
            if self.copies.as_ref().map(|m| m.load(AtomicOrdering::SeqCst)) == Some(1) {
                let data = Box::from_raw(self.data as *mut U8Vec);
                let _ = Box::from_raw(self.copies as *mut AtomicUsize);
                core::mem::forget(self); // don't run the destructor
                Some(*data)
            } else {
                None
            }
        }
    }
}
unsafe impl Send for SharedRawImageData {}
unsafe impl Sync for SharedRawImageData {}
impl Clone for SharedRawImageData {
    fn clone(&self) -> Self {
        unsafe {
            self.copies
                .as_ref()
                .map(|m| m.fetch_add(1, AtomicOrdering::SeqCst));
        }
        Self {
            data: self.data,
            copies: self.copies,
            run_destructor: true,
        }
    }
}
impl Drop for SharedRawImageData {
210
    fn drop(&mut self) {
210
        self.run_destructor = false;
        unsafe {
210
            let copies = (*self.copies).fetch_sub(1, AtomicOrdering::SeqCst);
210
            if copies == 1 {
210
                let _ = Box::from_raw(self.data as *mut U8Vec);
210
                let _ = Box::from_raw(self.copies as *mut AtomicUsize);
210
            }
        }
210
    }
}
impl PartialEq for SharedRawImageData {
    fn eq(&self, rhs: &Self) -> bool {
        self.data as usize == rhs.data as usize
    }
}
impl Eq for SharedRawImageData {}
impl PartialOrd for SharedRawImageData {
    fn partial_cmp(&self, other: &Self) -> Option<::core::cmp::Ordering> {
        Some(self.cmp(other))
    }
}
impl Ord for SharedRawImageData {
    fn cmp(&self, other: &Self) -> core::cmp::Ordering {
        (self.data as usize).cmp(&(other.data as usize))
    }
}
impl Hash for SharedRawImageData {
    fn hash<H>(&self, state: &mut H)
    where
        H: Hasher,
    {
        (self.data as usize).hash(state)
    }
}
/// Represents the backing store of an arbitrary series of pixels for display by
/// WebRender. This storage can take several forms.
#[derive(Debug, Clone, PartialEq, Eq, Ord, PartialOrd, Hash)]
#[repr(C, u8)]
pub enum ImageData {
    /// A simple series of bytes, provided by the embedding and owned by WebRender.
    /// The format is stored out-of-band, currently in ImageDescriptor.
    Raw(SharedRawImageData),
    /// An image owned by the embedding, and referenced by WebRender. This may
    /// take the form of a texture or a heap-allocated buffer.
    External(ExternalImageData),
}
/// Storage format identifier for externally-managed images.
#[derive(Debug, Copy, Clone, Eq, Hash, PartialEq, PartialOrd, Ord)]
#[repr(C, u8)]
pub enum ExternalImageType {
    /// The image is texture-backed.
    TextureHandle(ImageBufferKind),
    /// The image is heap-allocated by the embedding.
    Buffer,
}
/// An arbitrary identifier for an external image provided by the
/// application. It must be a unique identifier for each external
/// image.
#[repr(C)]
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, PartialOrd, Ord)]
pub struct ExternalImageId {
    pub inner: u64,
}
static LAST_EXTERNAL_IMAGE_ID: AtomicUsize = AtomicUsize::new(0);
impl ExternalImageId {
    /// Creates a new, unique ExternalImageId
    pub fn new() -> Self {
        Self {
            inner: LAST_EXTERNAL_IMAGE_ID.fetch_add(1, AtomicOrdering::SeqCst) as u64,
        }
    }
}
#[derive(Debug, Clone, PartialEq, PartialOrd)]
#[repr(C, u8)]
pub enum GlyphOutlineOperation {
    MoveTo(OutlineMoveTo),
    LineTo(OutlineLineTo),
    QuadraticCurveTo(OutlineQuadTo),
    CubicCurveTo(OutlineCubicTo),
    ClosePath,
}
impl_option!(
    GlyphOutlineOperation,
    OptionGlyphOutlineOperation,
    copy = false,
    [Debug, Clone, PartialEq, PartialOrd]
);
// MoveTo in em units
#[derive(Debug, Clone, PartialEq, PartialOrd)]
#[repr(C)]
pub struct OutlineMoveTo {
    pub x: i16,
    pub y: i16,
}
// LineTo in em units
#[derive(Debug, Clone, PartialEq, PartialOrd)]
#[repr(C)]
pub struct OutlineLineTo {
    pub x: i16,
    pub y: i16,
}
// QuadTo in em units
#[derive(Debug, Clone, PartialEq, PartialOrd)]
#[repr(C)]
pub struct OutlineQuadTo {
    pub ctrl_1_x: i16,
    pub ctrl_1_y: i16,
    pub end_x: i16,
    pub end_y: i16,
}
// CubicTo in em units
#[derive(Debug, Clone, PartialEq, PartialOrd)]
#[repr(C)]
pub struct OutlineCubicTo {
    pub ctrl_1_x: i16,
    pub ctrl_1_y: i16,
    pub ctrl_2_x: i16,
    pub ctrl_2_y: i16,
    pub end_x: i16,
    pub end_y: i16,
}
#[derive(Debug, Clone, PartialEq, PartialOrd)]
#[repr(C)]
pub struct GlyphOutline {
    pub operations: GlyphOutlineOperationVec,
}
azul_css::impl_vec!(GlyphOutlineOperation, GlyphOutlineOperationVec, GlyphOutlineOperationVecDestructor, GlyphOutlineOperationVecDestructorType, GlyphOutlineOperationVecSlice, OptionGlyphOutlineOperation);
azul_css::impl_vec_clone!(
    GlyphOutlineOperation,
    GlyphOutlineOperationVec,
    GlyphOutlineOperationVecDestructor
);
azul_css::impl_vec_debug!(GlyphOutlineOperation, GlyphOutlineOperationVec);
azul_css::impl_vec_partialord!(GlyphOutlineOperation, GlyphOutlineOperationVec);
azul_css::impl_vec_partialeq!(GlyphOutlineOperation, GlyphOutlineOperationVec);
#[derive(Debug, Clone)]
#[repr(C)]
pub struct OwnedGlyphBoundingBox {
    pub max_x: i16,
    pub max_y: i16,
    pub min_x: i16,
    pub min_y: i16,
}
/// Specifies the type of texture target in driver terms.
#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
#[repr(C)]
pub enum ImageBufferKind {
    /// Standard texture. This maps to GL_TEXTURE_2D in OpenGL.
    Texture2D = 0,
    /// Rectangle texture. This maps to GL_TEXTURE_RECTANGLE in OpenGL. This
    /// is similar to a standard texture, with a few subtle differences
    /// (no mipmaps, non-power-of-two dimensions, different coordinate space)
    /// that make it useful for representing the kinds of textures we use
    /// in WebRender. See https://www.khronos.org/opengl/wiki/Rectangle_Texture
    /// for background on Rectangle textures.
    TextureRect = 1,
    /// External texture. This maps to GL_TEXTURE_EXTERNAL_OES in OpenGL, which
    /// is an extension. This is used for image formats that OpenGL doesn't
    /// understand, particularly YUV. See
    /// https://www.khronos.org/registry/OpenGL/extensions/OES/OES_EGL_image_external.txt
    TextureExternal = 2,
}
/// Descriptor for external image resources. See `ImageData`.
#[repr(C)]
#[derive(Debug, Clone, Eq, Hash, PartialEq, PartialOrd, Ord)]
pub struct ExternalImageData {
    /// The identifier of this external image, provided by the embedding.
    pub id: ExternalImageId,
    /// For multi-plane images (i.e. YUV), indicates the plane of the
    /// original image that this struct represents. 0 for single-plane images.
    pub channel_index: u8,
    /// Storage format identifier.
    pub image_type: ExternalImageType,
}
pub type TileSize = u16;
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd)]
pub enum ImageDirtyRect {
    All,
    Partial(LayoutRect),
}
#[derive(Debug, Clone, PartialEq, PartialOrd)]
pub enum ResourceUpdate {
    AddFont(AddFont),
    DeleteFont(FontKey),
    AddFontInstance(AddFontInstance),
    DeleteFontInstance(FontInstanceKey),
    AddImage(AddImage),
    UpdateImage(UpdateImage),
    DeleteImage(ImageKey),
}
#[derive(Debug, Clone, PartialEq, Eq, Ord, PartialOrd, Hash)]
pub struct AddImage {
    pub key: ImageKey,
    pub descriptor: ImageDescriptor,
    pub data: ImageData,
    pub tiling: Option<TileSize>,
}
#[derive(Debug, Clone, PartialEq, PartialOrd)]
pub struct UpdateImage {
    pub key: ImageKey,
    pub descriptor: ImageDescriptor,
    pub data: ImageData,
    pub dirty_rect: ImageDirtyRect,
}
/// Message to add a font to WebRender.
/// Contains a reference to the parsed font data.
#[derive(Clone, PartialEq, Eq, Ord, PartialOrd, Hash)]
pub struct AddFont {
    pub key: FontKey,
    pub font: azul_css::props::basic::FontRef,
}
impl fmt::Debug for AddFont {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(
            f,
            "AddFont {{ key: {:?}, font: {:?} }}",
            self.key, self.font
        )
    }
}
#[derive(Debug, Clone, PartialEq, PartialOrd)]
pub struct AddFontInstance {
    pub key: FontInstanceKey,
    pub font_key: FontKey,
    pub glyph_size: (Au, DpiScaleFactor),
    pub options: Option<FontInstanceOptions>,
    pub platform_options: Option<FontInstancePlatformOptions>,
    pub variations: Vec<FontVariation>,
}
#[repr(C)]
#[derive(Clone, Copy, Debug, PartialOrd, PartialEq)]
pub struct FontVariation {
    pub tag: u32,
    pub value: f32,
}
#[repr(C)]
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct Epoch {
    inner: u32,
}
impl fmt::Display for Epoch {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.inner)
    }
}
impl Epoch {
    // prevent raw access to the .inner field so that
    // you can grep the codebase for .increment() to see
    // exactly where the epoch is being incremented
2772
    pub const fn new() -> Self {
2772
        Self { inner: 0 }
2772
    }
    pub const fn from(i: u32) -> Self {
        Self { inner: i }
    }
    pub const fn into_u32(&self) -> u32 {
        self.inner
    }
    // We don't want the epoch to increase to u32::MAX, since
    // u32::MAX represents an invalid epoch, which could confuse webrender
    pub fn increment(&mut self) {
        use core::u32;
        const MAX_ID: u32 = u32::MAX - 1;
        *self = match self.inner {
            MAX_ID => Epoch { inner: 0 },
            other => Epoch {
                inner: other.saturating_add(1),
            },
        };
    }
}
// App units that this font instance was registered for
#[derive(Debug, Clone, Copy, Hash, PartialEq, PartialOrd, Eq, Ord)]
pub struct Au(pub i32);
pub const AU_PER_PX: i32 = 60;
pub const MAX_AU: i32 = (1 << 30) - 1;
pub const MIN_AU: i32 = -(1 << 30) - 1;
impl Au {
    pub fn from_px(px: f32) -> Self {
        let target_app_units = (px * AU_PER_PX as f32) as i32;
        Au(target_app_units.min(MAX_AU).max(MIN_AU))
    }
    pub fn into_px(&self) -> f32 {
        self.0 as f32 / AU_PER_PX as f32
    }
}
// Debug, PartialEq, Eq, PartialOrd, Ord
#[derive(Debug)]
pub enum AddFontMsg {
    // add font: font key, font bytes + font index
    Font(FontKey, StyleFontFamilyHash, FontRef),
    Instance(AddFontInstance, (Au, DpiScaleFactor)),
}
impl AddFontMsg {
    pub fn into_resource_update(&self) -> ResourceUpdate {
        use self::AddFontMsg::*;
        match self {
            Font(font_key, _, font_ref) => ResourceUpdate::AddFont(AddFont {
                key: *font_key,
                font: font_ref.clone(),
            }),
            Instance(fi, _) => ResourceUpdate::AddFontInstance(fi.clone()),
        }
    }
}
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
pub enum DeleteFontMsg {
    Font(FontKey),
    Instance(FontInstanceKey, (Au, DpiScaleFactor)),
}
impl DeleteFontMsg {
    pub fn into_resource_update(&self) -> ResourceUpdate {
        use self::DeleteFontMsg::*;
        match self {
            Font(f) => ResourceUpdate::DeleteFont(*f),
            Instance(fi, _) => ResourceUpdate::DeleteFontInstance(*fi),
        }
    }
}
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
pub struct AddImageMsg(pub AddImage);
impl AddImageMsg {
    pub fn into_resource_update(&self) -> ResourceUpdate {
        ResourceUpdate::AddImage(self.0.clone())
    }
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[repr(C)]
pub struct LoadedFontSource {
    pub data: U8Vec,
    pub index: u32,
    pub load_outlines: bool,
}
// function to load the font source from a file
pub type LoadFontFn = fn(&StyleFontFamily, &FcFontCache) -> Option<LoadedFontSource>;
// function to parse the font given the loaded font source
pub type ParseFontFn = fn(LoadedFontSource) -> Option<FontRef>; // = Option<Box<azul_text_layout::Font>>
pub type GlStoreImageFn = fn(DocumentId, Epoch, Texture, ExternalImageId);
/// Compute the deterministic `ExternalImageId` that the OpenGL texture cache uses
/// for a texture bound to a specific DOM node. The same `(DomId, NodeId)` always
/// maps to the same `ExternalImageId`, so cached display lists keep working across
/// frames.
pub fn texture_external_image_id(dom_id: DomId, node_id: NodeId) -> ExternalImageId {
    let dom = dom_id.inner as u64;
    let node = node_id.index() as u64;
    debug_assert!(dom <= u32::MAX as u64, "DomId exceeds 32-bit range");
    debug_assert!(node <= u32::MAX as u64, "NodeId exceeds 32-bit range");
    ExternalImageId {
        inner: (dom << 32) | (node & 0xFFFFFFFF),
    }
}
/// Compute the `ExternalImageId` for a static GL texture identified by its
/// `ImageRefHash`. Mirrors `image_ref_hash_to_image_key` so a given image hash
/// produces the same identifiers everywhere.
pub fn image_ref_hash_to_external_image_id(hash: ImageRefHash) -> ExternalImageId {
    ExternalImageId {
        inner: hash.inner as u64,
    }
}
/// Given the fonts of the current frame, returns `AddFont` and `AddFontInstance`s of
/// which fonts / instances are currently not in the `current_registered_fonts` and
/// need to be added.
///
/// Deleting fonts can only be done after the entire frame has finished drawing,
/// otherwise (if removing fonts would happen after every DOM) we'd constantly
/// add-and-remove fonts after every VirtualViewCallback, which would cause a lot of
/// I/O waiting.
pub fn build_add_font_resource_updates(
    renderer_resources: &mut RendererResources,
    dpi: DpiScaleFactor,
    fc_cache: &FcFontCache,
    id_namespace: IdNamespace,
    fonts_in_dom: &OrderedMap<ImmediateFontId, FastBTreeSet<Au>>,
    font_source_load_fn: LoadFontFn,
    parse_font_fn: ParseFontFn,
) -> Vec<(StyleFontFamilyHash, AddFontMsg)> {
    let mut resource_updates = alloc::vec::Vec::new();
    let mut font_instances_added_this_frame = FastBTreeSet::new();
    'outer: for (im_font_id, font_sizes) in fonts_in_dom {
        macro_rules! insert_font_instances {
            ($font_family_hash:expr, $font_key:expr, $font_size:expr) => {{
                let font_instance_key_exists = renderer_resources
                    .currently_registered_fonts
                    .get(&$font_key)
                    .and_then(|(_, font_instances)| font_instances.get(&($font_size, dpi)))
                    .is_some()
                    || font_instances_added_this_frame.contains(&($font_key, ($font_size, dpi)));
                if !font_instance_key_exists {
                    let font_instance_key = FontInstanceKey::unique(id_namespace);
                    // For some reason the gamma is way to low on Windows
                    #[cfg(target_os = "windows")]
                    let platform_options = FontInstancePlatformOptions {
                        gamma: 300,
                        contrast: 100,
                        cleartype_level: 100,
                    };
                    #[cfg(target_os = "linux")]
                    let platform_options = FontInstancePlatformOptions {
                        lcd_filter: FontLCDFilter::Default,
                        hinting: FontHinting::Normal,
                    };
                    #[cfg(target_os = "macos")]
                    let platform_options = FontInstancePlatformOptions::default();
                    #[cfg(target_arch = "wasm32")]
                    let platform_options = FontInstancePlatformOptions::default();
                    #[cfg(any(target_os = "android", target_os = "ios"))]
                    let platform_options = FontInstancePlatformOptions::default();
                    let options = FontInstanceOptions {
                        render_mode: FontRenderMode::Subpixel,
                        flags: FONT_INSTANCE_FLAG_NO_AUTOHINT,
                        ..Default::default()
                    };
                    font_instances_added_this_frame.insert(($font_key, ($font_size, dpi)));
                    resource_updates.push((
                        $font_family_hash,
                        AddFontMsg::Instance(
                            AddFontInstance {
                                key: font_instance_key,
                                font_key: $font_key,
                                glyph_size: ($font_size, dpi),
                                options: Some(options),
                                platform_options: Some(platform_options),
                                variations: alloc::vec::Vec::new(),
                            },
                            ($font_size, dpi),
                        ),
                    ));
                }
            }};
        }
        match im_font_id {
            ImmediateFontId::Resolved((font_family_hash, font_id)) => {
                // nothing to do, font is already added,
                // just insert the missing font instances
                for font_size in font_sizes.iter() {
                    insert_font_instances!(*font_family_hash, *font_id, *font_size);
                }
            }
            ImmediateFontId::Unresolved(style_font_families) => {
                // If the font is already loaded during the current frame,
                // do not attempt to load it again
                //
                // This prevents duplicated loading for fonts in different orders, i.e.
                // - vec!["Times New Roman", "serif"] and
                // - vec!["sans", "Times New Roman"]
                // ... will resolve to the same font instead of creating two fonts
                // If there is no font key, that means there's also no font instances
                let mut font_family_hash = None;
                let font_families_hash = StyleFontFamiliesHash::new(style_font_families.as_ref());
                // Find the first font that can be loaded and parsed
                'inner: for family in style_font_families.as_ref().iter() {
                    let current_family_hash = StyleFontFamilyHash::new(&family);
                    if let Some(font_id) = renderer_resources.font_id_map.get(&current_family_hash)
                    {
                        // font key already exists
                        for font_size in font_sizes {
                            insert_font_instances!(current_family_hash, *font_id, *font_size);
                        }
                        continue 'outer;
                    }
                    let font_ref = match family {
                        StyleFontFamily::Ref(r) => r.clone(), // Clone the FontRef
                        other => {
                            // Load and parse the font
                            let font_data = match (font_source_load_fn)(&other, fc_cache) {
                                Some(s) => s,
                                None => continue 'inner,
                            };
                            let font_ref = match (parse_font_fn)(font_data) {
                                Some(s) => s,
                                None => continue 'inner,
                            };
                            font_ref
                        }
                    };
                    // font loaded properly
                    font_family_hash = Some((current_family_hash, font_ref));
                    break 'inner;
                }
                let (font_family_hash, font_ref) = match font_family_hash {
                    None => continue 'outer, // No font could be loaded, try again next frame
                    Some(s) => s,
                };
                // Generate a new font key, store the mapping between hash and font key
                let font_key = FontKey::unique(id_namespace);
                let add_font_msg = AddFontMsg::Font(font_key, font_family_hash, font_ref);
                renderer_resources
                    .font_id_map
                    .insert(font_family_hash, font_key);
                renderer_resources
                    .font_families_map
                    .insert(font_families_hash, font_family_hash);
                resource_updates.push((font_family_hash, add_font_msg));
                // Insert font sizes for the newly generated font key
                for font_size in font_sizes {
                    insert_font_instances!(font_family_hash, font_key, *font_size);
                }
            }
        }
    }
    resource_updates
}
/// Given the images of the current frame, returns `AddImage`s of
/// which image keys are currently not in the `current_registered_images` and
/// need to be added.
///
/// Returns Vec<(ImageRefHash, AddImageMsg)> where:
/// - ImageRefHash: Stable hash of the ImageRef pointer
/// - AddImageMsg: Message to add the image to WebRender
///
/// The ImageKey in AddImageMsg is generated directly from the ImageRefHash using
/// image_ref_hash_to_image_key(), so no separate mapping table is needed.
///
/// Deleting images can only be done after the entire frame has finished drawing,
/// otherwise (if removing images would happen after every DOM) we'd constantly
/// add-and-remove images after every VirtualViewCallback, which would cause a lot of
/// I/O waiting.
#[allow(unused_variables)]
pub fn build_add_image_resource_updates(
    renderer_resources: &RendererResources,
    id_namespace: IdNamespace,
    epoch: Epoch,
    document_id: &DocumentId,
    images_in_dom: &FastBTreeSet<ImageRef>,
    insert_into_active_gl_textures: GlStoreImageFn,
) -> Vec<(ImageRefHash, AddImageMsg)> {
    images_in_dom
        .iter()
        .filter_map(|image_ref| {
            let image_ref_hash = image_ref_get_hash(&image_ref);
            if renderer_resources
                .currently_registered_images
                .contains_key(&image_ref_hash)
            {
                return None;
            }
            // NOTE: The image_ref.clone() is a shallow clone,
            // does not actually clone the data
            match image_ref.get_data() {
                DecodedImage::Gl(texture) => {
                    let descriptor = texture.get_descriptor();
                    let key = image_ref_hash_to_image_key(image_ref_hash, id_namespace);
                    // The ExternalImageId is derived from the same stable hash that
                    // produces the ImageKey, so the GL texture cache and WebRender
                    // agree on a single identifier for this texture.
                    let external_image_id = image_ref_hash_to_external_image_id(image_ref_hash);
                    // NOTE: The texture is not really cloned here,
                    (insert_into_active_gl_textures)(
                        *document_id,
                        epoch,
                        texture.clone(),
                        external_image_id,
                    );
                    Some((
                        image_ref_hash,
                        AddImageMsg(AddImage {
                            key,
                            data: ImageData::External(ExternalImageData {
                                id: external_image_id,
                                channel_index: 0,
                                image_type: ExternalImageType::TextureHandle(
                                    ImageBufferKind::Texture2D,
                                ),
                            }),
                            descriptor,
                            tiling: None,
                        }),
                    ))
                }
                DecodedImage::Raw((descriptor, data)) => {
                    let key = image_ref_hash_to_image_key(image_ref_hash, id_namespace);
                    Some((
                        image_ref_hash,
                        AddImageMsg(AddImage {
                            key,
                            data: data.clone(), // deep-copy except in the &'static case
                            descriptor: descriptor.clone(), /* deep-copy, but struct is not very
                                                 * large */
                            tiling: None,
                        }),
                    ))
                }
                DecodedImage::NullImage {
                    width: _,
                    height: _,
                    format: _,
                    tag: _,
                } => None,
                DecodedImage::Callback(_) => None, /* Texture callbacks are handled after layout
                                                    * is done */
            }
        })
        .collect()
}
/// Submits the `AddFont`, `AddFontInstance` and `AddImage` resources to the RenderApi.
/// Extends `currently_registered_images` and `currently_registered_fonts` by the
/// `last_frame_image_keys` and `last_frame_font_keys`, so that we don't lose track of
/// what font and image keys are currently in the API.
pub fn add_resources(
    renderer_resources: &mut RendererResources,
    all_resource_updates: &mut Vec<ResourceUpdate>,
    add_font_resources: Vec<(StyleFontFamilyHash, AddFontMsg)>,
    add_image_resources: Vec<(ImageRefHash, AddImageMsg)>,
) {
    all_resource_updates.extend(
        add_font_resources
            .iter()
            .map(|(_, f)| f.into_resource_update()),
    );
    all_resource_updates.extend(
        add_image_resources
            .iter()
            .map(|(_, i)| i.into_resource_update()),
    );
    for (image_ref_hash, add_image_msg) in add_image_resources.iter() {
        renderer_resources.currently_registered_images.insert(
            *image_ref_hash,
            ResolvedImage {
                key: add_image_msg.0.key,
                descriptor: add_image_msg.0.descriptor,
            },
        );
    }
    for (_, add_font_msg) in add_font_resources {
        use self::AddFontMsg::*;
        match add_font_msg {
            Font(fk, font_family_hash, font_ref) => {
                renderer_resources
                    .currently_registered_fonts
                    .entry(fk)
                    .or_insert_with(|| (font_ref.clone(), OrderedMap::default()));
                // CRITICAL: Map font_hash to FontKey so we can look it up during rendering
                renderer_resources
                    .font_hash_map
                    .insert(font_ref.get_hash(), fk);
            }
            Instance(fi, size) => {
                if let Some((_, instances)) = renderer_resources
                    .currently_registered_fonts
                    .get_mut(&fi.font_key)
                {
                    instances.insert(size, fi.key);
                }
            }
        }
    }
}