Skip to main content

azul_core/
resources.rs

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