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#[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 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 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 pub is_backface_visible: bool,
154 pub is_scrollbar_container: bool,
156 pub is_scrollbar_thumb: bool,
158}
159
160#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
162pub struct ImageDescriptor {
163 pub format: RawImageFormat,
165 pub dimensions: (usize, usize),
167 pub stride: Option<i32>,
172 pub offset: i32,
178 pub flags: ImageDescriptorFlags,
180}
181
182#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
184pub struct ImageDescriptorFlags {
185 pub is_opaque: bool,
188 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#[derive(Default)]
247pub struct AppResources {
248 pub css_ids_to_image_ids: FastHashMap<CssImageId, ImageId>,
250 pub css_ids_to_font_ids: FastHashMap<CssFontId, FontId>,
252 pub image_sources: FastHashMap<ImageId, ImageSource>,
254 pub font_sources: FastHashMap<FontId, FontSource>,
256 pub currently_registered_images: FastHashMap<PipelineId, FastHashMap<ImageId, ImageInfo>>,
258 pub currently_registered_fonts: FastHashMap<PipelineId, FastHashMap<ImmediateFontId, LoadedFont>>,
260 pub last_frame_image_keys: FastHashMap<PipelineId, FastHashSet<ImageId>>,
265 pub last_frame_font_keys: FastHashMap<PipelineId, FastHashMap<ImmediateFontId, FastHashSet<Au>>>,
272 pub text_cache: TextCache,
274}
275
276impl AppResources {
277
278 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 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 Embedded(&'static [u8]),
336 Raw(RawImage),
338 File(PathBuf),
340}
341
342#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
343pub enum FontSource {
344 Embedded(&'static [u8]),
346 File(PathBuf),
348 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#[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 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#[derive(Debug, Default, Clone)]
405pub struct TextCache {
406 pub string_cache: FastHashMap<TextId, Words>,
411
412 }
421
422impl TextCache {
423
424 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 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#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
447pub struct Words {
448 pub items: Vec<Word>,
450 pub internal_str: String,
452 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#[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#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
481pub enum WordType {
482 Word,
484 Tab,
486 Return,
488 Space,
490}
491
492#[derive(Debug, Clone)]
495pub struct ScaledWords {
496 pub font_size_px: f32,
498 pub baseline_px: f32,
500 pub items: Vec<ScaledWord>,
502 pub longest_word_width: f32,
505 pub space_advance_px: f32,
507 pub space_codepoint: u32,
509 pub font_metrics: FontMetrics,
511}
512
513#[derive(Debug, Clone)]
515pub struct ScaledWord {
516 pub glyph_infos: Vec<GlyphInfo>,
518 pub glyph_positions: Vec<GlyphPosition>,
521 pub word_width: f32,
523}
524
525#[derive(Debug, Clone, PartialEq)]
527pub struct WordPositions {
528 pub text_layout_options: ResolvedTextLayoutOptions,
531 pub word_positions: Vec<LayoutPoint>,
533 pub line_breaks: Vec<(WordIndex, LineLength)>,
536 pub trailing: f32,
542 pub number_of_words: usize,
544 pub number_of_lines: usize,
546 pub content_size: LayoutSize,
551}
552
553#[derive(Debug, Clone, PartialEq)]
555pub struct LayoutedGlyphs {
556 pub glyphs: Vec<GlyphInstance>,
557}
558
559#[derive(Debug, Clone)]
567pub struct ClusterIterator<'a> {
568 cur_codepoint: Option<u32>,
570 cluster_count: usize,
572 word: &'a ScaledWord,
573 cur_glyph_idx: usize,
575}
576
577#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
579pub struct ClusterInfo {
580 pub cluster_idx: usize,
582 pub codepoint: u32,
584 pub glyph_idx: usize,
586}
587
588impl<'a> Iterator for ClusterIterator<'a> {
589
590 type Item = ClusterInfo;
591
592 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 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 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 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 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 pub fn has_image_source(&self, image_id: &ImageId) -> bool {
686 self.image_sources.get(image_id).is_some()
687 }
688
689 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 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 pub fn get_font_source(&self, font_id: &FontId) -> Option<&FontSource> {
743 self.font_sources.get(font_id)
744 }
745
746 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 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 pub fn delete_text(&mut self, id: TextId) {
773 self.text_cache.delete_text(id);
774 }
775
776 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#[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
801impl 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
810pub 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
833pub 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
858pub 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
867pub const FONT_INSTANCE_FLAG_FORCE_GDI: u32 = 1 << 16;
869
870pub const FONT_INSTANCE_FLAG_FONT_SMOOTHING: u32 = 1 << 16;
872
873pub 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 }
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 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#[derive(Debug, Clone, PartialEq, Eq, Ord, PartialOrd, Hash)]
975pub enum ImageData {
976 Raw(Arc<Vec<u8>>),
979 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#[repr(u32)]
996#[derive(Debug, Copy, Clone, Eq, Hash, PartialEq, PartialOrd, Ord)]
997pub enum ExternalImageType {
998 TextureHandle(TextureTarget),
1000 Buffer,
1002}
1003
1004#[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 pub fn new() -> Self {
1016 Self(LAST_EXTERNAL_IMAGE_ID.fetch_add(1, Ordering::SeqCst) as u64)
1017 }
1018}
1019
1020#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
1022pub enum TextureTarget {
1023 Default = 0,
1025 Array = 1,
1029 Rect = 2,
1036 External = 3,
1041}
1042
1043#[repr(C)]
1045#[derive(Debug, Clone, Eq, Hash, PartialEq, PartialOrd, Ord)]
1046pub struct ExternalImageData {
1047 pub id: ExternalImageId,
1049 pub channel_index: u8,
1052 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
1151pub 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
1187pub 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#[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 pub font_bytes: Vec<u8>,
1277 pub font_index: i32,
1280 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
1289pub 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 #[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 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#[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
1434pub 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 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 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 resource_updates.push((font_id.clone(), DeleteFontMsg::Font(loaded_font.font_key)));
1497 }
1498 }
1499
1500 resource_updates
1501}
1502
1503pub 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
1571pub 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}