azul_core/
app_resources.rs

1use std::{
2    fmt,
3    path::PathBuf,
4    sync::{Arc, atomic::{AtomicUsize, Ordering}},
5};
6use azul_css::{
7    LayoutPoint, LayoutRect, LayoutSize,
8    RectStyle, StyleFontSize, ColorU,
9};
10use crate::{
11    FastHashMap, FastHashSet,
12    ui_solver::{ResolvedTextLayoutOptions},
13    display_list::{DisplayList, GlyphInstance},
14    callbacks::PipelineId,
15    id_tree::NodeDataContainer,
16    dom::NodeData,
17};
18
19pub type CssImageId = String;
20pub type CssFontId = String;
21
22// since it's repr(C), can be casted directly from a `hb_glyph_info_t`
23#[repr(C)]
24#[derive(Copy, Clone)]
25pub struct GlyphInfo {
26    pub codepoint: u32,
27    pub mask: u32,
28    pub cluster: u32,
29    pub var1: HbVarIntT,
30    pub var2: HbVarIntT,
31}
32
33impl fmt::Debug for GlyphInfo {
34    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
35        write!(f, "GlyphInfo {{ codepoint: {}, mask: {}, cluster: {} }}", self.codepoint, self.mask, self.cluster)
36    }
37}
38
39#[repr(C)]
40#[derive(Copy, Clone)]
41pub struct GlyphPosition {
42    pub x_advance: i32,
43    pub y_advance: i32,
44    pub x_offset: i32,
45    pub y_offset: i32,
46    pub var: HbVarIntT,
47}
48
49impl fmt::Debug for GlyphPosition {
50    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
51        write!(f,
52            "GlyphPosition {{ x_advance: {}, y_advance: {}, x_offset: {}, y_offset: {},  }}",
53            self.x_advance, self.y_advance, self.x_offset, self.y_offset
54        )
55    }
56}
57
58#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
59pub struct FontMetrics {
60    /// Font size that these metrics were created for, usually 1000px
61    /// (so every metric has to be divided by 1000 before it can be used for measurements)
62    pub font_size: usize,
63    pub x_ppem: u16,
64    pub y_ppem: u16,
65    pub x_scale: i64,
66    pub y_scale: i64,
67    pub ascender: i64,
68    pub descender: i64,
69    pub height: i64,
70    pub max_advance: i64,
71}
72
73impl FontMetrics {
74
75    // Only for testing, zero-sized font, will always return 0 for every metric
76    pub fn zero() -> Self {
77        Self {
78            font_size: 1000,
79            x_ppem: 0,
80            y_ppem: 0,
81            x_scale: 0,
82            y_scale: 0,
83            ascender: 0,
84            descender: 0,
85            height: 0,
86            max_advance: 0,
87        }
88    }
89
90    pub fn get_x_ppem(&self, target_font_size: f32) -> f32 {
91        let s = self.x_ppem as f32;
92        s / (self.font_size as f32) * target_font_size
93    }
94
95    pub fn get_y_ppem(&self, target_font_size: f32) -> f32 {
96        let s = self.y_ppem as f32;
97        s / (self.font_size as f32) * target_font_size
98    }
99
100    pub fn get_x_scale(&self, target_font_size: f32) -> f32 {
101        let s = self.x_scale as f32;
102        s / (self.font_size as f32) * target_font_size
103    }
104
105    pub fn get_y_scale(&self, target_font_size: f32) -> f32 {
106        let s = self.y_scale as f32;
107        s / (self.font_size as f32) * target_font_size
108    }
109
110    pub fn get_ascender(&self, target_font_size: f32) -> f32 {
111        let s = self.ascender as f32;
112        s / (self.font_size as f32) * target_font_size
113    }
114
115    pub fn get_descender(&self, target_font_size: f32) -> f32 {
116        let s = self.descender as f32;
117        s / (self.font_size as f32) * target_font_size
118    }
119
120    pub fn get_height(&self, target_font_size: f32) -> f32 {
121        let s = self.height as f32;
122        s / (self.font_size as f32) * target_font_size
123    }
124
125    pub fn get_max_advance(&self, target_font_size: f32) -> f32 {
126        let s = self.max_advance as f32;
127        s / (self.font_size as f32) * target_font_size
128    }
129}
130
131#[repr(C)]
132#[derive(Copy, Clone)]
133pub union HbVarIntT {
134    pub u32: u32,
135    pub i32: i32,
136    pub u16: [u16; 2usize],
137    pub i16: [i16; 2usize],
138    pub u8: [u8; 4usize],
139    pub i8: [i8; 4usize],
140    _bindgen_union_align: u32,
141}
142
143pub type WordIndex = usize;
144pub type GlyphIndex = usize;
145pub type LineLength = f32;
146pub type IndexOfLineBreak = usize;
147pub type RemainingSpaceToRight = f32;
148pub type LineBreaks = Vec<(GlyphIndex, RemainingSpaceToRight)>;
149
150#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
151pub struct PrimitiveFlags {
152    /// The CSS backface-visibility property (yes, it can be really granular)
153    pub is_backface_visible: bool,
154    /// If set, this primitive represents a scroll bar container
155    pub is_scrollbar_container: bool,
156    /// If set, this primitive represents a scroll bar thumb
157    pub is_scrollbar_thumb: bool,
158}
159
160/// Metadata (but not storage) describing an image In WebRender.
161#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
162pub struct ImageDescriptor {
163    /// Format of the image data.
164    pub format: RawImageFormat,
165    /// Width and height of the image data, in pixels.
166    pub dimensions: (usize, usize),
167    /// The number of bytes from the start of one row to the next. If non-None,
168    /// `compute_stride` will return this value, otherwise it returns
169    /// `width * bpp`. Different source of images have different alignment
170    /// constraints for rows, so the stride isn't always equal to width * bpp.
171    pub stride: Option<i32>,
172    /// Offset in bytes of the first pixel of this image in its backing buffer.
173    /// This is used for tiling, wherein WebRender extracts chunks of input images
174    /// in order to cache, manipulate, and render them individually. This offset
175    /// tells the texture upload machinery where to find the bytes to upload for
176    /// this tile. Non-tiled images generally set this to zero.
177    pub offset: i32,
178    /// Various bool flags related to this descriptor.
179    pub flags: ImageDescriptorFlags,
180}
181
182/// Various flags that are part of an image descriptor.
183#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
184pub struct ImageDescriptorFlags {
185    /// Whether this image is opaque, or has an alpha channel. Avoiding blending
186    /// for opaque surfaces is an important optimization.
187    pub is_opaque: bool,
188    /// Whether to allow the driver to automatically generate mipmaps. If images
189    /// are already downscaled appropriately, mipmap generation can be wasted
190    /// work, and cause performance problems on some cards/drivers.
191    ///
192    /// See https://github.com/servo/webrender/pull/2555/
193    pub allow_mipmaps: bool,
194}
195
196#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
197pub struct IdNamespace(pub u32);
198
199impl ::std::fmt::Display for IdNamespace {
200    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
201        write!(f, "IdNamespace({})", self.0)
202    }
203}
204
205impl ::std::fmt::Debug for IdNamespace {
206    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
207        write!(f, "{}", self)
208    }
209}
210
211#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
212pub enum RawImageFormat {
213    R8,
214    R16,
215    RG16,
216    BGRA8,
217    RGBAF32,
218    RG8,
219    RGBAI32,
220    RGBA8,
221}
222
223#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
224pub struct ImageKey {
225    pub namespace: IdNamespace,
226    pub key: u32,
227}
228
229#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
230pub struct FontInstanceKey {
231    pub namespace: IdNamespace,
232    pub key: u32,
233}
234
235#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
236pub struct FontKey {
237    pub namespace: IdNamespace,
238    pub key: u32,
239}
240
241/// Stores the resources for the application, souch as fonts, images and cached
242/// texts, also clipboard strings
243///
244/// Images and fonts can be references across window contexts (not yet tested,
245/// but should work).
246#[derive(Default)]
247pub struct AppResources {
248    /// The CssImageId is the string used in the CSS, i.e. "my_image" -> ImageId(4)
249    pub css_ids_to_image_ids: FastHashMap<CssImageId, ImageId>,
250    /// Same as CssImageId -> ImageId, but for fonts, i.e. "Roboto" -> FontId(9)
251    pub css_ids_to_font_ids: FastHashMap<CssFontId, FontId>,
252    /// Stores where the images were loaded from
253    pub image_sources: FastHashMap<ImageId, ImageSource>,
254    /// Stores where the fonts were loaded from
255    pub font_sources: FastHashMap<FontId, FontSource>,
256    /// All image keys currently active in the RenderApi
257    pub currently_registered_images: FastHashMap<PipelineId, FastHashMap<ImageId, ImageInfo>>,
258    /// All font keys currently active in the RenderApi
259    pub currently_registered_fonts: FastHashMap<PipelineId, FastHashMap<ImmediateFontId, LoadedFont>>,
260    /// If an image isn't displayed, it is deleted from memory, only
261    /// the `ImageSource` (i.e. the path / source where the image was loaded from) remains.
262    ///
263    /// This way the image can be re-loaded if necessary but doesn't have to reside in memory at all times.
264    pub last_frame_image_keys: FastHashMap<PipelineId, FastHashSet<ImageId>>,
265    /// If a font does not get used for one frame, the corresponding instance key gets
266    /// deleted. If a FontId has no FontInstanceKeys anymore, the font key gets deleted.
267    ///
268    /// The only thing remaining in memory permanently is the FontSource (which is only
269    /// the string of the file path where the font was loaded from, so no huge memory pressure).
270    /// The reason for this agressive strategy is that the
271    pub last_frame_font_keys: FastHashMap<PipelineId, FastHashMap<ImmediateFontId, FastHashSet<Au>>>,
272    /// Stores long texts across frames
273    pub text_cache: TextCache,
274}
275
276impl AppResources {
277
278    /// Add a new pipeline to the app resources
279    pub fn add_pipeline(&mut self, pipeline_id: PipelineId) {
280        self.currently_registered_fonts.insert(pipeline_id, FastHashMap::default());
281        self.currently_registered_images.insert(pipeline_id, FastHashMap::default());
282        self.last_frame_font_keys.insert(pipeline_id, FastHashMap::default());
283        self.last_frame_image_keys.insert(pipeline_id, FastHashSet::default());
284    }
285
286    /// Delete and remove all fonts & font instance keys from a given pipeline
287    pub fn delete_pipeline<T: FontImageApi>(&mut self, pipeline_id: &PipelineId, render_api: &mut T) {
288        let mut delete_font_resources = Vec::new();
289
290        for (font_id, loaded_font) in self.currently_registered_fonts[&pipeline_id].iter() {
291            delete_font_resources.extend(
292                loaded_font.font_instances.iter()
293                .map(|(au, font_instance_key)| (font_id.clone(), DeleteFontMsg::Instance(*font_instance_key, *au)))
294            );
295            delete_font_resources.push((font_id.clone(), DeleteFontMsg::Font(loaded_font.font_key)));
296        }
297
298        let delete_image_resources = self.currently_registered_images[&pipeline_id].iter()
299        .map(|(id, info)| (*id, DeleteImageMsg(info.key, *info)))
300        .collect();
301
302        delete_resources(self, render_api, pipeline_id, delete_font_resources, delete_image_resources);
303
304        self.currently_registered_fonts.remove(pipeline_id);
305        self.currently_registered_images.remove(pipeline_id);
306        self.last_frame_font_keys.remove(pipeline_id);
307        self.last_frame_image_keys.remove(pipeline_id);
308    }
309}
310
311macro_rules! unique_id {($struct_name:ident, $counter_name:ident) => {
312
313    static $counter_name: ::std::sync::atomic::AtomicUsize = ::std::sync::atomic::AtomicUsize::new(0);
314
315    #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)]
316    pub struct $struct_name {
317        id: usize,
318    }
319
320    impl $struct_name {
321
322        pub fn new() -> Self {
323            Self { id: $counter_name.fetch_add(1, ::std::sync::atomic::Ordering::SeqCst) }
324        }
325    }
326}}
327
328unique_id!(TextId, TEXT_ID_COUNTER);
329unique_id!(ImageId, IMAGE_ID_COUNTER);
330unique_id!(FontId, FONT_ID_COUNTER);
331
332#[derive(Debug, Clone, PartialEq, Eq)]
333pub enum ImageSource {
334    /// The image is embedded inside the binary file
335    Embedded(&'static [u8]),
336    /// The image is already decoded and loaded from a set of bytes
337    Raw(RawImage),
338    /// The image is loaded from a file
339    File(PathBuf),
340}
341
342#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
343pub enum FontSource {
344    /// The font is embedded inside the binary file
345    Embedded(&'static [u8]),
346    /// The font is loaded from a file
347    File(PathBuf),
348    /// The font is a system built-in font
349    System(String),
350}
351
352impl fmt::Display for FontSource {
353    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
354        use self::FontSource::*;
355        match self {
356            Embedded(e) => write!(f, "Embedded(0x{:x})", e as *const _ as usize),
357            File(p) => write!(f, "\"{}\"", p.as_path().to_string_lossy()),
358            System(id) => write!(f, "\"{}\"", id),
359        }
360    }
361}
362
363#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
364pub enum ImmediateFontId {
365    Resolved(FontId),
366    Unresolved(CssFontId),
367}
368
369/// Raw image made up of raw pixels (either BRGA8 or A8)
370#[derive(Debug, Clone, PartialEq, Eq)]
371pub struct RawImage {
372    pub pixels: Vec<u8>,
373    pub image_dimensions: (usize, usize),
374    pub data_format: RawImageFormat,
375}
376
377#[derive(Clone)]
378pub struct LoadedFont {
379    pub font_key: FontKey,
380    pub font_bytes: Vec<u8>,
381    /// Index of the font in case the bytes indicate a font collection
382    pub font_index: i32,
383    pub font_instances: FastHashMap<Au, FontInstanceKey>,
384    pub font_metrics: FontMetrics,
385}
386
387impl fmt::Debug for LoadedFont {
388    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
389        write!(f,
390            "LoadedFont {{ font_key: {:?}, font_bytes: [u8;{}], font_index: {}, font_instances: {:#?} }}",
391            self.font_key, self.font_bytes.len(), self.font_index, self.font_instances,
392        )
393    }
394}
395
396impl LoadedFont {
397
398    pub fn delete_font_instance(&mut self, size: &Au) {
399        self.font_instances.remove(size);
400    }
401}
402
403/// Cache for accessing large amounts of text
404#[derive(Debug, Default, Clone)]
405pub struct TextCache {
406    /// Mapping from the TextID to the actual, UTF-8 String
407    ///
408    /// This is stored outside of the actual glyph calculation, because usually you don't
409    /// need the string, except for rebuilding a cached string (for example, when the font is changed)
410    pub string_cache: FastHashMap<TextId, Words>,
411
412    // -- for now, don't cache ScaledWords, it's too complicated...
413
414    // /// Caches the layout of the strings / words.
415    // ///
416    // /// TextId -> FontId (to look up by font)
417    // /// FontId -> PixelValue (to categorize by size within a font)
418    // /// PixelValue -> layouted words (to cache the glyph widths on a per-font-size basis)
419    // pub(crate) layouted_strings_cache: FastHashMap<TextId, FastHashMap<FontInstanceKey, ScaledWords>>,
420}
421
422impl TextCache {
423
424    /// Add a new, large text to the resources
425    pub fn add_text(&mut self, words: Words) -> TextId {
426        let id = TextId::new();
427        self.string_cache.insert(id, words);
428        id
429    }
430
431    pub fn get_text(&self, text_id: &TextId) -> Option<&Words> {
432        self.string_cache.get(text_id)
433    }
434
435    /// Removes a string from the string cache, but not the layouted text cache
436    pub fn delete_text(&mut self, id: TextId) {
437        self.string_cache.remove(&id);
438    }
439
440    pub fn clear_all_texts(&mut self) {
441        self.string_cache.clear();
442    }
443}
444
445/// Text broken up into `Tab`, `Word()`, `Return` characters
446#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
447pub struct Words {
448    /// Words (and spaces), broken up into semantic items
449    pub items: Vec<Word>,
450    /// String that makes up this paragraph of words
451    pub internal_str: String,
452    /// `internal_chars` is used in order to enable copy-paste (since taking a sub-string isn't possible using UTF-8)
453    pub internal_chars: Vec<char>,
454}
455
456impl Words {
457
458    pub fn get_substr(&self, word: &Word) -> String {
459        self.internal_chars[word.start..word.end].iter().collect()
460    }
461
462    pub fn get_str(&self) -> &str {
463        &self.internal_str
464    }
465
466    pub fn get_char(&self, idx: usize) -> Option<char> {
467        self.internal_chars.get(idx).cloned()
468    }
469}
470
471/// Section of a certain type
472#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
473pub struct Word {
474    pub start: usize,
475    pub end: usize,
476    pub word_type: WordType,
477}
478
479/// Either a white-space delimited word, tab or return character
480#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
481pub enum WordType {
482    /// Encountered a word (delimited by spaces)
483    Word,
484    // `\t` or `x09`
485    Tab,
486    /// `\r`, `\n` or `\r\n`, escaped: `\x0D`, `\x0A` or `\x0D\x0A`
487    Return,
488    /// Space character
489    Space,
490}
491
492/// A paragraph of words that are shaped and scaled (* but not yet layouted / positioned*!)
493/// according to their final size in pixels.
494#[derive(Debug, Clone)]
495pub struct ScaledWords {
496    /// Font size (in pixels) that was used to scale these words
497    pub font_size_px: f32,
498    /// Baseline of the font (usually lower than the font size)
499    pub baseline_px: f32,
500    /// Words scaled to their appropriate font size, but not yet positioned on the screen
501    pub items: Vec<ScaledWord>,
502    /// Longest word in the `self.scaled_words`, necessary for
503    /// calculating overflow rectangles.
504    pub longest_word_width: f32,
505    /// Horizontal advance of the space glyph
506    pub space_advance_px: f32,
507    /// Glyph index of the space character
508    pub space_codepoint: u32,
509    /// Metrics necessary for baseline calculation
510    pub font_metrics: FontMetrics,
511}
512
513/// Word that is scaled (to a font / font instance), but not yet positioned
514#[derive(Debug, Clone)]
515pub struct ScaledWord {
516    /// Glyphs, positions are relative to the first character of the word
517    pub glyph_infos: Vec<GlyphInfo>,
518    /// Horizontal advances of each glyph, necessary for
519    /// hit-testing characters later on (for text selection).
520    pub glyph_positions: Vec<GlyphPosition>,
521    /// The sum of the width of all the characters in this word
522    pub word_width: f32,
523}
524
525/// Stores the positions of the vertically laid out texts
526#[derive(Debug, Clone, PartialEq)]
527pub struct WordPositions {
528    /// Options like word spacing, character spacing, etc. that were
529    /// used to layout these glyphs
530    pub text_layout_options: ResolvedTextLayoutOptions,
531    /// Stores the positions of words.
532    pub word_positions: Vec<LayoutPoint>,
533    /// Index of the word at which the line breaks + length of line
534    /// (useful for text selection + horizontal centering)
535    pub line_breaks: Vec<(WordIndex, LineLength)>,
536    /// Horizontal width of the last line (in pixels), necessary for inline layout later on,
537    /// so that the next text run can contine where the last text run left off.
538    ///
539    /// Usually, the "trailing" of the current text block is the "leading" of the
540    /// next text block, to make it seem like two text runs push into each other.
541    pub trailing: f32,
542    /// How many words are in the text?
543    pub number_of_words: usize,
544    /// How many lines (NOTE: virtual lines, meaning line breaks in the layouted text) are there?
545    pub number_of_lines: usize,
546    /// Horizontal and vertical boundaries of the layouted words.
547    ///
548    /// Note that the vertical extent can be larger than the last words' position,
549    /// because of trailing negative glyph advances.
550    pub content_size: LayoutSize,
551}
552
553/// Returns the layouted glyph instances
554#[derive(Debug, Clone, PartialEq)]
555pub struct LayoutedGlyphs {
556    pub glyphs: Vec<GlyphInstance>,
557}
558
559/// Iterator over glyphs that returns information about the cluster that this glyph belongs to.
560/// Returned by the `ScaledWord::cluster_iter()` function.
561///
562/// For each glyph, returns information about what cluster this glyph belongs to. Useful for
563/// doing operations per-cluster instead of per-glyph.
564/// *Note*: The iterator returns once-per-glyph, not once-per-cluster, however
565/// you can merge the clusters into groups by using the `ClusterInfo.cluster_idx`.
566#[derive(Debug, Clone)]
567pub struct ClusterIterator<'a> {
568    /// What codepoint does the current glyph have - set to `None` if the first character isn't yet processed.
569    cur_codepoint: Option<u32>,
570    /// What cluster *index* are we currently at - default: 0
571    cluster_count: usize,
572    word: &'a ScaledWord,
573    /// Store what glyph we are currently processing in this word
574    cur_glyph_idx: usize,
575}
576
577/// Info about what cluster a certain glyph belongs to.
578#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
579pub struct ClusterInfo {
580    /// Cluster index in this word
581    pub cluster_idx: usize,
582    /// Codepoint of this cluster
583    pub codepoint: u32,
584    /// What the glyph index of this cluster is
585    pub glyph_idx: usize,
586}
587
588impl<'a> Iterator for ClusterIterator<'a> {
589
590    type Item = ClusterInfo;
591
592    /// Returns an iterator over the clusters in this word.
593    ///
594    /// Note: This will return one `ClusterInfo` per glyph, so you can't just
595    /// use `.cluster_iter().count()` to count the glyphs: Instead, use `.cluster_iter().last().cluster_idx`.
596    fn next(&mut self) -> Option<ClusterInfo> {
597
598        let next_glyph = self.word.glyph_infos.get(self.cur_glyph_idx)?;
599
600        let glyph_idx = self.cur_glyph_idx;
601
602        if self.cur_codepoint != Some(next_glyph.cluster) {
603            self.cur_codepoint = Some(next_glyph.cluster);
604            self.cluster_count += 1;
605        }
606
607        self.cur_glyph_idx += 1;
608
609        Some(ClusterInfo {
610            cluster_idx: self.cluster_count,
611            codepoint: self.cur_codepoint.unwrap_or(0),
612            glyph_idx,
613        })
614    }
615}
616
617impl ScaledWord {
618
619    /// Creates an iterator over clusters instead of glyphs
620    pub fn cluster_iter<'a>(&'a self) -> ClusterIterator<'a> {
621        ClusterIterator {
622            cur_codepoint: None,
623            cluster_count: 0,
624            word: &self,
625            cur_glyph_idx: 0,
626        }
627    }
628
629    pub fn number_of_clusters(&self) -> usize {
630        self.cluster_iter().last().map(|l| l.cluster_idx).unwrap_or(0)
631    }
632}
633
634#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
635pub struct ImageInfo {
636    pub key: ImageKey,
637    pub descriptor: ImageDescriptor,
638}
639
640impl ImageInfo {
641    /// Returns the (width, height) of this image.
642    pub fn get_dimensions(&self) -> (usize, usize) {
643        self.descriptor.dimensions
644    }
645}
646
647impl AppResources {
648
649    pub fn new() -> Self {
650        Self::default()
651    }
652
653    /// Returns the IDs of all currently loaded fonts in `self.font_data`
654    pub fn get_loaded_font_ids(&self) -> Vec<FontId> {
655        self.font_sources.keys().cloned().collect()
656    }
657
658    pub fn get_loaded_image_ids(&self) -> Vec<ImageId> {
659        self.image_sources.keys().cloned().collect()
660    }
661
662    pub fn get_loaded_css_image_ids(&self) -> Vec<CssImageId> {
663        self.css_ids_to_image_ids.keys().cloned().collect()
664    }
665
666    pub fn get_loaded_css_font_ids(&self) -> Vec<CssFontId> {
667        self.css_ids_to_font_ids.keys().cloned().collect()
668    }
669
670    pub fn get_loaded_text_ids(&self) -> Vec<TextId> {
671        self.text_cache.string_cache.keys().cloned().collect()
672    }
673
674    // -- ImageId cache
675
676    /// Add an image from a PNG, JPEG or other source.
677    ///
678    /// Note: For specialized image formats, you'll have to enable them as
679    /// features in the Cargo.toml file.
680    pub fn add_image_source(&mut self, image_id: ImageId, image_source: ImageSource) {
681        self.image_sources.insert(image_id, image_source);
682    }
683
684    /// Returns whether the AppResources has currently a certain image ID registered
685    pub fn has_image_source(&self, image_id: &ImageId) -> bool {
686        self.image_sources.get(image_id).is_some()
687    }
688
689    /// Given an `ImageId`, returns the decoded bytes of that image or `None`, if the `ImageId` is invalid.
690    /// Returns an error on IO failure / image decoding failure or image
691    pub fn get_image_source(&self, image_id: &ImageId) -> Option<&ImageSource> {
692        self.image_sources.get(image_id)
693    }
694
695    pub fn delete_image_source(&mut self, image_id: &ImageId) {
696        self.image_sources.remove(image_id);
697    }
698
699    pub fn add_css_image_id<S: Into<String>>(&mut self, css_id: S) -> ImageId {
700        *self.css_ids_to_image_ids.entry(css_id.into()).or_insert_with(|| ImageId::new())
701    }
702
703    pub fn has_css_image_id(&self, css_id: &str) -> bool {
704        self.get_css_image_id(css_id).is_some()
705    }
706
707    pub fn get_css_image_id(&self, css_id: &str) -> Option<&ImageId> {
708        self.css_ids_to_image_ids.get(css_id)
709    }
710
711    pub fn delete_css_image_id(&mut self, css_id: &str) -> Option<ImageId> {
712        self.css_ids_to_image_ids.remove(css_id)
713    }
714
715    pub fn get_image_info(&self, pipeline_id: &PipelineId, image_key: &ImageId) -> Option<&ImageInfo> {
716        self.currently_registered_images.get(pipeline_id).and_then(|map| map.get(image_key))
717    }
718
719    // -- FontId cache
720
721    pub fn add_css_font_id<S: Into<String>>(&mut self, css_id: S) -> FontId {
722        *self.css_ids_to_font_ids.entry(css_id.into()).or_insert_with(|| FontId::new())
723    }
724
725    pub fn has_css_font_id(&self, css_id: &str) -> bool {
726        self.get_css_font_id(css_id).is_some()
727    }
728
729    pub fn get_css_font_id(&self, css_id: &str) -> Option<&FontId> {
730        self.css_ids_to_font_ids.get(css_id)
731    }
732
733    pub fn delete_css_font_id(&mut self, css_id: &str) -> Option<FontId> {
734        self.css_ids_to_font_ids.remove(css_id)
735    }
736
737    pub fn add_font_source(&mut self, font_id: FontId, font_source: FontSource) {
738        self.font_sources.insert(font_id, font_source);
739    }
740
741    /// Given a `FontId`, returns the bytes for that font or `None`, if the `FontId` is invalid.
742    pub fn get_font_source(&self, font_id: &FontId) -> Option<&FontSource> {
743        self.font_sources.get(font_id)
744    }
745
746    /// Checks if a `FontId` is valid, i.e. if a font is currently ready-to-use
747    pub fn has_font_source(&self, id: &FontId) -> bool {
748        self.font_sources.get(id).is_some()
749    }
750
751    pub fn delete_font_source(&mut self, id: &FontId) {
752        self.font_sources.remove(id);
753    }
754
755    pub fn get_loaded_font(&self, pipeline_id: &PipelineId, font_id: &ImmediateFontId) -> Option<&LoadedFont> {
756        self.currently_registered_fonts.get(pipeline_id).and_then(|map| map.get(font_id))
757    }
758
759    // -- TextId cache
760
761    /// Adds a string to the internal text cache, but only store it as a string,
762    /// without caching the layout of the string.
763    pub fn add_text(&mut self, words: Words) -> TextId {
764        self.text_cache.add_text(words)
765    }
766
767    pub fn get_text(&self, id: &TextId) -> Option<&Words> {
768        self.text_cache.get_text(id)
769    }
770
771    /// Removes a string from both the string cache and the layouted text cache
772    pub fn delete_text(&mut self, id: TextId) {
773        self.text_cache.delete_text(id);
774    }
775
776    /// Empties the entire internal text cache, invalidating all `TextId`s. Use with care.
777    pub fn clear_all_texts(&mut self) {
778        self.text_cache.clear_all_texts();
779    }
780}
781
782pub trait FontImageApi {
783    fn new_image_key(&self) -> ImageKey;
784    fn new_font_key(&self) -> FontKey;
785    fn new_font_instance_key(&self) -> FontInstanceKey;
786    fn update_resources(&self, _: Vec<ResourceUpdate>);
787    fn flush_scene_builder(&self);
788}
789
790/// Used only for debugging, so that the AppResource garbage
791/// collection tests can run without a real RenderApi
792#[derive(Debug, Copy, Clone, PartialEq, Eq)]
793pub struct FakeRenderApi { }
794
795impl FakeRenderApi { pub fn new() -> Self { Self { } } }
796
797static LAST_FAKE_IMAGE_KEY: AtomicUsize = AtomicUsize::new(0);
798static LAST_FAKE_FONT_KEY: AtomicUsize = AtomicUsize::new(0);
799static LAST_FAKE_FONT_INSTANCE_KEY: AtomicUsize = AtomicUsize::new(0);
800
801// Fake RenderApi for unit testing
802impl FontImageApi for FakeRenderApi {
803    fn new_image_key(&self) -> ImageKey { ImageKey { key: LAST_FAKE_IMAGE_KEY.fetch_add(1, Ordering::SeqCst) as u32, namespace: IdNamespace(0) } }
804    fn new_font_key(&self) -> FontKey { FontKey { key: LAST_FAKE_FONT_KEY.fetch_add(1, Ordering::SeqCst) as u32, namespace: IdNamespace(0) } }
805    fn new_font_instance_key(&self) -> FontInstanceKey { FontInstanceKey { key: LAST_FAKE_FONT_INSTANCE_KEY.fetch_add(1, Ordering::SeqCst) as u32, namespace: IdNamespace(0) } }
806    fn update_resources(&self, _: Vec<ResourceUpdate>) { }
807    fn flush_scene_builder(&self) { }
808}
809
810/// Scans the DisplayList for new images and fonts. After this call, the RenderApi is
811/// guaranteed to know about all FontKeys and FontInstanceKey
812pub fn add_fonts_and_images<U: FontImageApi>(
813    app_resources: &mut AppResources,
814    render_api: &mut U,
815    pipeline_id: &PipelineId,
816    display_list: &DisplayList,
817    node_data: &NodeDataContainer<NodeData>,
818    load_font_fn: LoadFontFn,
819    load_image_fn: LoadImageFn,
820) {
821    let font_keys = scan_ui_description_for_font_keys(&app_resources, display_list, node_data);
822    let image_keys = scan_ui_description_for_image_keys(&app_resources, display_list, node_data);
823
824    app_resources.last_frame_font_keys.get_mut(pipeline_id).unwrap().extend(font_keys.clone().into_iter());
825    app_resources.last_frame_image_keys.get_mut(pipeline_id).unwrap().extend(image_keys.clone().into_iter());
826
827    let add_font_resource_updates = build_add_font_resource_updates(app_resources, render_api, pipeline_id, &font_keys, load_font_fn);
828    let add_image_resource_updates = build_add_image_resource_updates(app_resources, render_api, pipeline_id, &image_keys, load_image_fn);
829
830    add_resources(app_resources, render_api, pipeline_id, add_font_resource_updates, add_image_resource_updates);
831}
832
833/// To be called at the end of a frame (after the UI has rendered):
834/// Deletes all FontKeys and FontImageKeys that weren't used in
835/// the last frame, to save on memory. If the font needs to be recreated, it
836/// needs to be reloaded from the `FontSource`.
837pub fn garbage_collect_fonts_and_images<U: FontImageApi>(
838    app_resources: &mut AppResources,
839    render_api: &mut U,
840    pipeline_id: &PipelineId,
841) {
842    let delete_font_resource_updates = build_delete_font_resource_updates(app_resources, pipeline_id);
843    let delete_image_resource_updates = build_delete_image_resource_updates(app_resources, pipeline_id);
844
845    delete_resources(app_resources, render_api, pipeline_id, delete_font_resource_updates, delete_image_resource_updates);
846
847    app_resources.last_frame_font_keys.get_mut(pipeline_id).unwrap().clear();
848    app_resources.last_frame_image_keys.get_mut(pipeline_id).unwrap().clear();
849}
850
851pub fn font_size_to_au(font_size: StyleFontSize) -> Au {
852    use crate::ui_solver::DEFAULT_FONT_SIZE_PX;
853    Au::from_px(font_size.0.to_pixels(DEFAULT_FONT_SIZE_PX as f32))
854}
855
856pub type FontInstanceFlags = u32;
857
858// Common flags
859pub const FONT_INSTANCE_FLAG_SYNTHETIC_BOLD: u32    = 1 << 1;
860pub const FONT_INSTANCE_FLAG_EMBEDDED_BITMAPS: u32  = 1 << 2;
861pub const FONT_INSTANCE_FLAG_SUBPIXEL_BGR: u32      = 1 << 3;
862pub const FONT_INSTANCE_FLAG_TRANSPOSE: u32         = 1 << 4;
863pub const FONT_INSTANCE_FLAG_FLIP_X: u32            = 1 << 5;
864pub const FONT_INSTANCE_FLAG_FLIP_Y: u32            = 1 << 6;
865pub const FONT_INSTANCE_FLAG_SUBPIXEL_POSITION: u32 = 1 << 7;
866
867// Windows flags
868pub const FONT_INSTANCE_FLAG_FORCE_GDI: u32         = 1 << 16;
869
870// Mac flags
871pub const FONT_INSTANCE_FLAG_FONT_SMOOTHING: u32    = 1 << 16;
872
873// FreeType flags
874pub const FONT_INSTANCE_FLAG_FORCE_AUTOHINT: u32    = 1 << 16;
875pub const FONT_INSTANCE_FLAG_NO_AUTOHINT: u32       = 1 << 17;
876pub const FONT_INSTANCE_FLAG_VERTICAL_LAYOUT: u32   = 1 << 18;
877pub const FONT_INSTANCE_FLAG_LCD_VERTICAL: u32      = 1 << 19;
878
879#[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash)]
880pub struct GlyphOptions {
881    pub render_mode: FontRenderMode,
882    pub flags: FontInstanceFlags,
883}
884
885#[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash)]
886pub enum FontRenderMode {
887    Mono,
888    Alpha,
889    Subpixel,
890}
891
892#[cfg(target_arch = "wasm32")]
893#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash)]
894pub struct FontInstancePlatformOptions {
895    // empty for now
896}
897
898#[cfg(target_os = "windows")]
899#[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash)]
900pub struct FontInstancePlatformOptions {
901    pub gamma: u16,
902    pub contrast: u16,
903}
904
905#[cfg(target_os = "macos")]
906#[derive(Default, Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash)]
907pub struct FontInstancePlatformOptions {
908    pub unused: u32,
909}
910
911#[cfg(target_os = "linux")]
912#[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash)]
913pub struct FontInstancePlatformOptions {
914    pub lcd_filter: FontLCDFilter,
915    pub hinting: FontHinting,
916}
917
918#[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash)]
919pub enum FontHinting {
920    None,
921    Mono,
922    Light,
923    Normal,
924    LCD,
925}
926
927#[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash)]
928pub enum FontLCDFilter {
929    None,
930    Default,
931    Light,
932    Legacy,
933}
934
935impl Default for FontLCDFilter {
936    fn default() -> Self { FontLCDFilter::Default }
937}
938
939#[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash)]
940pub struct FontInstanceOptions {
941    pub render_mode: FontRenderMode,
942    pub flags: FontInstanceFlags,
943    pub bg_color: ColorU,
944    /// When bg_color.a is != 0 and render_mode is FontRenderMode::Subpixel, the text will be
945    /// rendered with bg_color.r/g/b as an opaque estimated background color.
946    pub synthetic_italics: SyntheticItalics,
947}
948
949impl Default for FontInstanceOptions {
950    fn default() -> FontInstanceOptions {
951        FontInstanceOptions {
952            render_mode: FontRenderMode::Subpixel,
953            flags: 0,
954            bg_color: ColorU::TRANSPARENT,
955            synthetic_italics: SyntheticItalics::default(),
956        }
957    }
958}
959
960
961#[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash)]
962pub struct SyntheticItalics {
963    pub angle: i16,
964}
965
966impl Default for SyntheticItalics {
967    fn default() -> Self {
968        Self { angle: 0 }
969    }
970}
971
972/// Represents the backing store of an arbitrary series of pixels for display by
973/// WebRender. This storage can take several forms.
974#[derive(Debug, Clone, PartialEq, Eq, Ord, PartialOrd, Hash)]
975pub enum ImageData {
976    /// A simple series of bytes, provided by the embedding and owned by WebRender.
977    /// The format is stored out-of-band, currently in ImageDescriptor.
978    Raw(Arc<Vec<u8>>),
979    /// An image owned by the embedding, and referenced by WebRender. This may
980    /// take the form of a texture or a heap-allocated buffer.
981    External(ExternalImageData),
982}
983
984impl ImageData {
985
986    pub fn new_raw(data: Vec<u8>) -> Self {
987        ImageData::Raw(Arc::new(data))
988    }
989
990    pub fn new_external(data: ExternalImageData) -> Self {
991        ImageData::External(data)
992    }
993}
994/// Storage format identifier for externally-managed images.
995#[repr(u32)]
996#[derive(Debug, Copy, Clone, Eq, Hash, PartialEq, PartialOrd, Ord)]
997pub enum ExternalImageType {
998    /// The image is texture-backed.
999    TextureHandle(TextureTarget),
1000    /// The image is heap-allocated by the embedding.
1001    Buffer,
1002}
1003
1004/// An arbitrary identifier for an external image provided by the
1005/// application. It must be a unique identifier for each external
1006/// image.
1007#[repr(C)]
1008#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, PartialOrd, Ord)]
1009pub struct ExternalImageId(pub u64);
1010
1011static LAST_EXTERNAL_IMAGE_ID: AtomicUsize = AtomicUsize::new(0);
1012
1013impl ExternalImageId {
1014    /// Creates a new, unique ExternalImageId
1015    pub fn new() -> Self {
1016        Self(LAST_EXTERNAL_IMAGE_ID.fetch_add(1, Ordering::SeqCst) as u64)
1017    }
1018}
1019
1020/// Specifies the type of texture target in driver terms.
1021#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
1022pub enum TextureTarget {
1023    /// Standard texture. This maps to GL_TEXTURE_2D in OpenGL.
1024    Default = 0,
1025    /// Array texture. This maps to GL_TEXTURE_2D_ARRAY in OpenGL. See
1026    /// https://www.khronos.org/opengl/wiki/Array_Texture for background
1027    /// on Array textures.
1028    Array = 1,
1029    /// Rectange texture. This maps to GL_TEXTURE_RECTANGLE in OpenGL. This
1030    /// is similar to a standard texture, with a few subtle differences
1031    /// (no mipmaps, non-power-of-two dimensions, different coordinate space)
1032    /// that make it useful for representing the kinds of textures we use
1033    /// in WebRender. See https://www.khronos.org/opengl/wiki/Rectangle_Texture
1034    /// for background on Rectangle textures.
1035    Rect = 2,
1036    /// External texture. This maps to GL_TEXTURE_EXTERNAL_OES in OpenGL, which
1037    /// is an extension. This is used for image formats that OpenGL doesn't
1038    /// understand, particularly YUV. See
1039    /// https://www.khronos.org/registry/OpenGL/extensions/OES/OES_EGL_image_external.txt
1040    External = 3,
1041}
1042
1043/// Descriptor for external image resources. See `ImageData`.
1044#[repr(C)]
1045#[derive(Debug, Clone, Eq, Hash, PartialEq, PartialOrd, Ord)]
1046pub struct ExternalImageData {
1047    /// The identifier of this external image, provided by the embedding.
1048    pub id: ExternalImageId,
1049    /// For multi-plane images (i.e. YUV), indicates the plane of the
1050    /// original image that this struct represents. 0 for single-plane images.
1051    pub channel_index: u8,
1052    /// Storage format identifier.
1053    pub image_type: ExternalImageType,
1054}
1055
1056pub type TileSize = u16;
1057
1058#[derive(Debug, Copy, Clone, PartialEq, PartialOrd)]
1059pub enum ImageDirtyRect {
1060    All,
1061    Partial(LayoutRect)
1062}
1063
1064#[derive(Debug, Clone, PartialEq, PartialOrd)]
1065pub enum ResourceUpdate {
1066    AddFont(AddFont),
1067    DeleteFont(FontKey),
1068    AddFontInstance(AddFontInstance),
1069    DeleteFontInstance(FontInstanceKey),
1070    AddImage(AddImage),
1071    UpdateImage(UpdateImage),
1072    DeleteImage(ImageKey),
1073}
1074
1075#[derive(Debug, Clone, PartialEq, Eq, Ord, PartialOrd, Hash)]
1076pub struct AddImage {
1077    pub key: ImageKey,
1078    pub descriptor: ImageDescriptor,
1079    pub data: ImageData,
1080    pub tiling: Option<TileSize>,
1081}
1082
1083#[derive(Debug, Clone, PartialEq, PartialOrd)]
1084pub struct UpdateImage {
1085    pub key: ImageKey,
1086    pub descriptor: ImageDescriptor,
1087    pub data: ImageData,
1088    pub dirty_rect: ImageDirtyRect,
1089}
1090
1091#[derive(Clone, PartialEq, Eq, Ord, PartialOrd, Hash)]
1092pub struct AddFont {
1093    pub key: FontKey,
1094    pub font_bytes: Vec<u8>,
1095    pub font_index: u32,
1096}
1097
1098impl fmt::Debug for AddFont {
1099    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1100        write!(f, "AddFont {{ key: {:?}, font_bytes: [u8;{}], font_index: {} }}", self.key, self.font_bytes.len(), self.font_index)
1101    }
1102}
1103
1104#[derive(Debug, Clone, PartialEq, PartialOrd)]
1105pub struct AddFontInstance {
1106    pub key: FontInstanceKey,
1107    pub font_key: FontKey,
1108    pub glyph_size: Au,
1109    pub options: Option<FontInstanceOptions>,
1110    pub platform_options: Option<FontInstancePlatformOptions>,
1111    pub variations: Vec<FontVariation>,
1112}
1113
1114#[repr(C)]
1115#[derive(Clone, Copy, Debug, PartialOrd, PartialEq)]
1116pub struct FontVariation {
1117    pub tag: u32,
1118    pub value: f32,
1119}
1120
1121#[repr(C)]
1122#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
1123pub struct Epoch(pub u32);
1124
1125#[derive(Debug, Clone, Copy, Hash, PartialEq, PartialOrd, Eq, Ord)]
1126pub struct Au(pub i32);
1127
1128pub const AU_PER_PX: i32 = 60;
1129pub const MAX_AU: i32 = (1 << 30) - 1;
1130pub const MIN_AU: i32 = -(1 << 30) - 1;
1131
1132impl Au {
1133    pub fn from_px(px: f32) -> Self {
1134        let target_app_units = (px * AU_PER_PX as f32) as i32;
1135        Au(target_app_units.min(MAX_AU).max(MIN_AU))
1136    }
1137}
1138
1139pub fn get_font_id(rect_style: &RectStyle) -> &str {
1140    use crate::ui_solver::DEFAULT_FONT_ID;
1141    let font_id = rect_style.font_family.as_ref().and_then(|family| family.get_property()?.fonts.get(0));
1142    font_id.map(|f| f.get_str()).unwrap_or(DEFAULT_FONT_ID)
1143}
1144
1145pub fn get_font_size(rect_style: &RectStyle) -> StyleFontSize {
1146    use crate::ui_solver::DEFAULT_FONT_SIZE;
1147    rect_style.font_size.and_then(|fs| fs.get_property().cloned()).unwrap_or(DEFAULT_FONT_SIZE)
1148}
1149
1150
1151/// Scans the display list for all font IDs + their font size
1152pub fn scan_ui_description_for_font_keys(
1153    app_resources: &AppResources,
1154    display_list: &DisplayList,
1155    node_data: &NodeDataContainer<NodeData>,
1156) -> FastHashMap<ImmediateFontId, FastHashSet<Au>> {
1157
1158    use crate::dom::NodeType::*;
1159
1160    let mut font_keys = FastHashMap::default();
1161
1162    for node_id in display_list.rectangles.linear_iter() {
1163
1164        let display_rect = &display_list.rectangles[node_id];
1165        let node_data = &node_data[node_id];
1166
1167        match node_data.get_node_type() {
1168            Text(_) | Label(_) => {
1169                let css_font_id = get_font_id(&display_rect.style);
1170                let font_id = match app_resources.css_ids_to_font_ids.get(css_font_id) {
1171                    Some(s) => ImmediateFontId::Resolved(*s),
1172                    None => ImmediateFontId::Unresolved(css_font_id.to_string()),
1173                };
1174                let font_size = get_font_size(&display_rect.style);
1175                font_keys
1176                    .entry(font_id)
1177                    .or_insert_with(|| FastHashSet::default())
1178                    .insert(font_size_to_au(font_size));
1179            },
1180            _ => { }
1181        }
1182    }
1183
1184    font_keys
1185}
1186
1187/// Scans the display list for all image keys
1188pub fn scan_ui_description_for_image_keys(
1189    app_resources: &AppResources,
1190    display_list: &DisplayList,
1191    node_data: &NodeDataContainer<NodeData>,
1192) -> FastHashSet<ImageId> {
1193
1194    use crate::dom::NodeType::*;
1195
1196    display_list.rectangles
1197    .iter()
1198    .zip(node_data.iter())
1199    .filter_map(|(display_rect, node_data)| {
1200        match node_data.get_node_type() {
1201            Image(id) => Some(*id),
1202            _ => {
1203                let background = display_rect.style.background.as_ref().and_then(|bg| bg.get_property())?;
1204                let css_image_id = background.get_css_image_id()?;
1205                let image_id = app_resources.get_css_image_id(&css_image_id.0)?;
1206                Some(*image_id)
1207            }
1208        }
1209    }).collect()
1210}
1211
1212// Debug, PartialEq, Eq, PartialOrd, Ord
1213#[derive(Debug, Clone)]
1214pub enum AddFontMsg {
1215    Font(LoadedFont),
1216    Instance(AddFontInstance, Au),
1217}
1218
1219impl AddFontMsg {
1220    pub fn into_resource_update(&self) -> ResourceUpdate {
1221        use self::AddFontMsg::*;
1222        match self {
1223            Font(f) => ResourceUpdate::AddFont(AddFont {
1224                key: f.font_key,
1225                font_bytes: f.font_bytes.clone(),
1226                font_index: f.font_index as u32
1227            }),
1228            Instance(fi, _) => ResourceUpdate::AddFontInstance(fi.clone()),
1229        }
1230    }
1231}
1232
1233#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
1234pub enum DeleteFontMsg {
1235    Font(FontKey),
1236    Instance(FontInstanceKey, Au),
1237}
1238
1239impl DeleteFontMsg {
1240    pub fn into_resource_update(&self) -> ResourceUpdate {
1241        use self::DeleteFontMsg::*;
1242        match self {
1243            Font(f) => ResourceUpdate::DeleteFont(*f),
1244            Instance(fi, _) => ResourceUpdate::DeleteFontInstance(*fi),
1245        }
1246    }
1247}
1248
1249#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
1250pub struct AddImageMsg(pub AddImage, pub ImageInfo);
1251
1252impl AddImageMsg {
1253    pub fn into_resource_update(&self) -> ResourceUpdate {
1254        ResourceUpdate::AddImage(self.0.clone())
1255    }
1256}
1257
1258#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
1259pub struct DeleteImageMsg(ImageKey, ImageInfo);
1260
1261impl DeleteImageMsg {
1262    pub fn into_resource_update(&self) -> ResourceUpdate {
1263        ResourceUpdate::DeleteImage(self.0.clone())
1264    }
1265}
1266
1267#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1268pub struct LoadedImageSource {
1269    pub image_bytes_decoded: ImageData,
1270    pub image_descriptor: ImageDescriptor,
1271}
1272
1273#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1274pub struct LoadedFontSource {
1275    /// Bytes of the font file
1276    pub font_bytes: Vec<u8>,
1277    /// Index of the font in the file (if not known, set to 0) -
1278    /// only relevant if the file is a font collection
1279    pub font_index: i32,
1280    /// Important baseline / character metrics of the font
1281    pub font_metrics: FontMetrics,
1282}
1283
1284pub struct LoadFontFn(pub fn(&FontSource) -> Option<LoadedFontSource>);
1285impl_callback!(LoadFontFn);
1286pub struct LoadImageFn(pub fn(&ImageSource) -> Option<LoadedImageSource>);
1287impl_callback!(LoadImageFn);
1288
1289/// Given the fonts of the current frame, returns `AddFont` and `AddFontInstance`s of
1290/// which fonts / instances are currently not in the `current_registered_fonts` and
1291/// need to be added.
1292///
1293/// Deleting fonts can only be done after the entire frame has finished drawing,
1294/// otherwise (if removing fonts would happen after every DOM) we'd constantly
1295/// add-and-remove fonts after every IFrameCallback, which would cause a lot of
1296/// I/O waiting.
1297pub fn build_add_font_resource_updates<T: FontImageApi>(
1298    app_resources: &AppResources,
1299    render_api: &mut T,
1300    pipeline_id: &PipelineId,
1301    fonts_in_dom: &FastHashMap<ImmediateFontId, FastHashSet<Au>>,
1302    font_source_load_fn: LoadFontFn,
1303) -> Vec<(ImmediateFontId, AddFontMsg)> {
1304
1305    let mut resource_updates = Vec::new();
1306
1307    for (im_font_id, font_sizes) in fonts_in_dom {
1308
1309        macro_rules! insert_font_instances {($font_id:expr, $font_key:expr, $font_index:expr, $font_size:expr) => ({
1310
1311            let font_instance_key_exists = app_resources.currently_registered_fonts[pipeline_id]
1312                .get(&$font_id)
1313                .and_then(|loaded_font| loaded_font.font_instances.get(&$font_size))
1314                .is_some();
1315
1316            if !font_instance_key_exists {
1317
1318                let font_instance_key = render_api.new_font_instance_key();
1319
1320                // For some reason the gamma is way to low on Windows
1321                #[cfg(target_os = "windows")]
1322                let platform_options = FontInstancePlatformOptions {
1323                    gamma: 300,
1324                    contrast: 100,
1325                };
1326
1327                #[cfg(target_os = "linux")]
1328                let platform_options = FontInstancePlatformOptions {
1329                    lcd_filter: FontLCDFilter::Default,
1330                    hinting: FontHinting::LCD,
1331                };
1332
1333                #[cfg(target_os = "macos")]
1334                let platform_options = FontInstancePlatformOptions::default();
1335
1336                #[cfg(target_arch = "wasm32")]
1337                let platform_options = FontInstancePlatformOptions::default();
1338
1339                let options = FontInstanceOptions {
1340                    render_mode: FontRenderMode::Subpixel,
1341                    flags: 0 | FONT_INSTANCE_FLAG_NO_AUTOHINT,
1342                    .. Default::default()
1343                };
1344
1345                resource_updates.push(($font_id, AddFontMsg::Instance(AddFontInstance {
1346                    key: font_instance_key,
1347                    font_key: $font_key,
1348                    glyph_size: $font_size,
1349                    options: Some(options),
1350                    platform_options: Some(platform_options),
1351                    variations: Vec::new(),
1352                }, $font_size)));
1353            }
1354        })}
1355
1356        match app_resources.currently_registered_fonts[pipeline_id].get(im_font_id) {
1357            Some(loaded_font) => {
1358                for font_size in font_sizes.iter() {
1359                    insert_font_instances!(im_font_id.clone(), loaded_font.font_key, loaded_font.font_index, *font_size);
1360                }
1361            },
1362            None => {
1363                use self::ImmediateFontId::*;
1364
1365                // If there is no font key, that means there's also no font instances
1366                let font_source = match im_font_id {
1367                    Resolved(font_id) => {
1368                        match app_resources.font_sources.get(font_id) {
1369                            Some(s) => s.clone(),
1370                            None => continue,
1371                        }
1372                    },
1373                    Unresolved(css_font_id) => FontSource::System(css_font_id.clone()),
1374                };
1375
1376                let loaded_font_source = match (font_source_load_fn.0)(&font_source) {
1377                    Some(s) => s,
1378                    None => continue,
1379                };
1380
1381                let LoadedFontSource { font_bytes, font_index, font_metrics } = loaded_font_source;
1382
1383                if !font_sizes.is_empty() {
1384                    let font_key = render_api.new_font_key();
1385                    let loaded_font = LoadedFont {
1386                        font_key,
1387                        font_bytes,
1388                        font_index,
1389                        font_metrics,
1390                        font_instances: FastHashMap::new(),
1391                    };
1392
1393                    resource_updates.push((im_font_id.clone(), AddFontMsg::Font(loaded_font)));
1394
1395                    for font_size in font_sizes {
1396                        insert_font_instances!(im_font_id.clone(), font_key, font_index, *font_size);
1397                    }
1398                }
1399            }
1400        }
1401    }
1402
1403    resource_updates
1404}
1405
1406/// Given the images of the current frame, returns `AddImage`s of
1407/// which image keys are currently not in the `current_registered_fonts` and
1408/// need to be added. Modifies `last_frame_image_keys` to contain the added image keys
1409///
1410/// Deleting images can only be done after the entire frame has finished drawing,
1411/// otherwise (if removing images would happen after every DOM) we'd constantly
1412/// add-and-remove images after every IFrameCallback, which would cause a lot of
1413/// I/O waiting.
1414#[allow(unused_variables)]
1415pub fn build_add_image_resource_updates<T: FontImageApi>(
1416    app_resources: &AppResources,
1417    render_api: &mut T,
1418    pipeline_id: &PipelineId,
1419    images_in_dom: &FastHashSet<ImageId>,
1420    image_source_load_fn: LoadImageFn,
1421) -> Vec<(ImageId, AddImageMsg)> {
1422
1423    images_in_dom.iter()
1424    .filter(|image_id| !app_resources.currently_registered_images[pipeline_id].contains_key(*image_id))
1425    .filter_map(|image_id| {
1426        let image_source = app_resources.image_sources.get(image_id)?;
1427        let LoadedImageSource { image_bytes_decoded, image_descriptor } = (image_source_load_fn.0)(image_source)?;
1428        let key = render_api.new_image_key();
1429        let add_image = AddImage { key, data: image_bytes_decoded, descriptor: image_descriptor, tiling: None };
1430        Some((*image_id, AddImageMsg(add_image, ImageInfo { key, descriptor: image_descriptor })))
1431    }).collect()
1432}
1433
1434/// Submits the `AddFont`, `AddFontInstance` and `AddImage` resources to the RenderApi.
1435/// Extends `currently_registered_images` and `currently_registered_fonts` by the
1436/// `last_frame_image_keys` and `last_frame_font_keys`, so that we don't lose track of
1437/// what font and image keys are currently in the API.
1438pub fn add_resources<T: FontImageApi>(
1439    app_resources: &mut AppResources,
1440    render_api: &mut T,
1441    pipeline_id: &PipelineId,
1442    add_font_resources: Vec<(ImmediateFontId, AddFontMsg)>,
1443    add_image_resources: Vec<(ImageId, AddImageMsg)>,
1444) {
1445    let mut merged_resource_updates = Vec::new();
1446
1447    merged_resource_updates.extend(add_font_resources.iter().map(|(_, f)| f.into_resource_update()));
1448    merged_resource_updates.extend(add_image_resources.iter().map(|(_, i)| i.into_resource_update()));
1449
1450    if !merged_resource_updates.is_empty() {
1451        render_api.update_resources(merged_resource_updates);
1452        // Assure that the AddFont / AddImage updates get processed immediately
1453        render_api.flush_scene_builder();
1454    }
1455
1456    for (image_id, add_image_msg) in add_image_resources.iter() {
1457        app_resources.currently_registered_images
1458        .get_mut(pipeline_id).unwrap()
1459        .insert(*image_id, add_image_msg.1);
1460    }
1461
1462    for (font_id, add_font_msg) in add_font_resources {
1463        use self::AddFontMsg::*;
1464        match add_font_msg {
1465            Font(f) => {
1466                app_resources.currently_registered_fonts
1467                .get_mut(pipeline_id).unwrap()
1468                .insert(font_id, f);
1469            },
1470            Instance(fi, size) => {
1471                app_resources.currently_registered_fonts
1472                    .get_mut(pipeline_id).unwrap()
1473                    .get_mut(&font_id).unwrap()
1474                    .font_instances.insert(size, fi.key);
1475            },
1476        }
1477    }
1478}
1479
1480pub fn build_delete_font_resource_updates(
1481    app_resources: &AppResources,
1482    pipeline_id: &PipelineId,
1483) -> Vec<(ImmediateFontId, DeleteFontMsg)> {
1484
1485    let mut resource_updates = Vec::new();
1486
1487    // Delete fonts that were not used in the last frame or have zero font instances
1488    for (font_id, loaded_font) in app_resources.currently_registered_fonts[pipeline_id].iter() {
1489        resource_updates.extend(
1490            loaded_font.font_instances.iter()
1491            .filter(|(au, _)| !(app_resources.last_frame_font_keys[pipeline_id].get(font_id).map(|f| f.contains(au)).unwrap_or(false)))
1492            .map(|(au, font_instance_key)| (font_id.clone(), DeleteFontMsg::Instance(*font_instance_key, *au)))
1493        );
1494        if !app_resources.last_frame_font_keys[&pipeline_id].contains_key(font_id) || loaded_font.font_instances.is_empty() {
1495            // Delete the font and all instances if there are no more instances of the font
1496            resource_updates.push((font_id.clone(), DeleteFontMsg::Font(loaded_font.font_key)));
1497        }
1498    }
1499
1500    resource_updates
1501}
1502
1503/// At the end of the frame, all images that are registered, but weren't used in the last frame
1504pub fn build_delete_image_resource_updates(
1505    app_resources: &AppResources,
1506    pipeline_id: &PipelineId,
1507) -> Vec<(ImageId, DeleteImageMsg)> {
1508    app_resources.currently_registered_images[&pipeline_id].iter()
1509    .filter(|(id, _info)| !app_resources.last_frame_image_keys[&pipeline_id].contains(id))
1510    .map(|(id, info)| (*id, DeleteImageMsg(info.key, *info)))
1511    .collect()
1512}
1513
1514pub fn delete_resources<T: FontImageApi>(
1515    app_resources: &mut AppResources,
1516    render_api: &mut T,
1517    pipeline_id: &PipelineId,
1518    delete_font_resources: Vec<(ImmediateFontId, DeleteFontMsg)>,
1519    delete_image_resources: Vec<(ImageId, DeleteImageMsg)>,
1520) {
1521    let mut merged_resource_updates = Vec::new();
1522
1523    merged_resource_updates.extend(delete_font_resources.iter().map(|(_, f)| f.into_resource_update()));
1524    merged_resource_updates.extend(delete_image_resources.iter().map(|(_, i)| i.into_resource_update()));
1525
1526    if !merged_resource_updates.is_empty() {
1527        render_api.update_resources(merged_resource_updates);
1528    }
1529
1530    for (removed_id, _removed_info) in delete_image_resources {
1531        app_resources.currently_registered_images
1532        .get_mut(pipeline_id).unwrap()
1533        .remove(&removed_id);
1534    }
1535
1536    for (font_id, delete_font_msg) in delete_font_resources {
1537        use self::DeleteFontMsg::*;
1538        match delete_font_msg {
1539            Font(_) => {
1540                app_resources.currently_registered_fonts
1541                .get_mut(pipeline_id).unwrap()
1542                .remove(&font_id);
1543            },
1544            Instance(_, size) => {
1545                app_resources.currently_registered_fonts
1546                .get_mut(pipeline_id).unwrap()
1547                .get_mut(&font_id).unwrap()
1548                .delete_font_instance(&size);
1549            },
1550        }
1551    }
1552}
1553
1554pub fn is_image_opaque(format: RawImageFormat, bytes: &[u8]) -> bool {
1555    match format {
1556        RawImageFormat::BGRA8 => {
1557            let mut is_opaque = true;
1558            for i in 0..(bytes.len() / 4) {
1559                if bytes[i * 4 + 3] != 255 {
1560                    is_opaque = false;
1561                    break;
1562                }
1563            }
1564            is_opaque
1565        }
1566        RawImageFormat::R8 => true,
1567        _ => unreachable!(),
1568    }
1569}
1570
1571// From webrender/wrench
1572// These are slow. Gecko's gfx/2d/Swizzle.cpp has better versions
1573pub fn premultiply(data: &mut [u8]) {
1574    for pixel in data.chunks_mut(4) {
1575        let a = u32::from(pixel[3]);
1576        pixel[0] = (((pixel[0] as u32 * a) + 128) / 255) as u8;
1577        pixel[1] = (((pixel[1] as u32 * a) + 128) / 255) as u8;
1578        pixel[2] = (((pixel[2] as u32 * a) + 128) / 255) as u8;
1579    }
1580}
1581
1582#[test]
1583fn test_premultiply() {
1584    let mut color = [255, 0, 0, 127];
1585    premultiply(&mut color);
1586    assert_eq!(color, [127, 0, 0, 127]);
1587}