1#[cfg(not(feature = "std"))]
13use alloc::string::ToString;
14use alloc::{boxed::Box, collections::btree_map::BTreeMap, string::String, vec::Vec};
15use core::{
16 fmt,
17 hash::{Hash, Hasher},
18 sync::atomic::{AtomicU64, AtomicUsize, Ordering as AtomicOrdering},
19};
20
21use azul_css::{
22 format_rust_code::GetHash,
23 props::basic::{
24 pixel::DEFAULT_FONT_SIZE, ColorU, FloatValue, FontRef, LayoutRect, LayoutSize,
25 StyleFontFamily, StyleFontFamilyVec, StyleFontSize,
26 },
27 system::SystemStyle,
28 AzString, F32Vec, LayoutDebugMessage, OptionI32, StringVec, U16Vec, U32Vec, U8Vec,
29};
30use rust_fontconfig::FcFontCache;
31
32pub use crate::callbacks::{
34 CoreImageCallback, CoreRenderImageCallback, CoreRenderImageCallbackType,
35};
36use crate::{
37 callbacks::{LayoutCallback, VirtualViewCallback},
38 dom::{DomId, NodeData, NodeType},
39 geom::{LogicalPosition, LogicalRect, LogicalSize},
40 gl::{OptionGlContextPtr, Texture},
41 hit_test::DocumentId,
42 id::NodeId,
43 prop_cache::CssPropertyCache,
44 refany::RefAny,
45 styled_dom::{
46 NodeHierarchyItemId, StyleFontFamiliesHash, StyleFontFamilyHash, StyledDom, StyledNodeState,
47 },
48 ui_solver::GlyphInstance,
49 window::{AzStringPair, OptionChar, StringPairVec},
50 xml::{
51 ComponentDef, ComponentDefVec, ComponentId, ComponentLibrary, ComponentLibraryVec,
52 ComponentSource, RegisterComponentFn, RegisterComponentLibraryFn,
53 },
54 FastBTreeSet, OrderedMap,
55};
56
57#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
58#[repr(C)]
59pub struct DpiScaleFactor {
60 pub inner: FloatValue,
61}
62
63impl DpiScaleFactor {
64 pub fn new(f: f32) -> Self {
65 Self {
66 inner: FloatValue::new(f),
67 }
68 }
69}
70
71#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
73#[repr(C)]
74pub enum AppTerminationBehavior {
75 ReturnToMain,
79 RunForever,
82 EndProcess,
85}
86
87impl Default for AppTerminationBehavior {
88 fn default() -> Self {
89 AppTerminationBehavior::EndProcess
91 }
92}
93
94#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
97#[repr(C)]
98pub struct NamedFont {
99 pub name: AzString,
101 pub bytes: U8Vec,
103}
104
105impl_option!(
106 NamedFont,
107 OptionNamedFont,
108 copy = false,
109 [Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash]
110);
111
112impl NamedFont {
113 pub fn new(name: AzString, bytes: U8Vec) -> Self {
114 Self { name, bytes }
115 }
116}
117
118impl_vec!(NamedFont, NamedFontVec, NamedFontVecDestructor, NamedFontVecDestructorType, NamedFontVecSlice, OptionNamedFont);
119impl_vec_mut!(NamedFont, NamedFontVec);
120impl_vec_debug!(NamedFont, NamedFontVec);
121impl_vec_partialeq!(NamedFont, NamedFontVec);
122impl_vec_eq!(NamedFont, NamedFontVec);
123impl_vec_partialord!(NamedFont, NamedFontVec);
124impl_vec_ord!(NamedFont, NamedFontVec);
125impl_vec_hash!(NamedFont, NamedFontVec);
126impl_vec_clone!(NamedFont, NamedFontVec, NamedFontVecDestructor);
127
128#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
130#[repr(C, u8)]
131pub enum FontLoadingConfig {
132 LoadAllSystemFonts,
134 LoadOnlyFamilies(StringVec),
137 BundledFontsOnly,
139}
140
141impl Default for FontLoadingConfig {
142 fn default() -> Self {
143 FontLoadingConfig::LoadAllSystemFonts
144 }
145}
146
147#[derive(Debug, Clone, Default)]
176#[repr(C)]
177pub struct CssMockEnvironment {
178 pub theme: azul_css::dynamic_selector::OptionThemeCondition,
180 pub language: azul_css::OptionString,
182 pub os_version: azul_css::dynamic_selector::OptionOsVersion,
184 pub os: azul_css::dynamic_selector::OptionOsCondition,
186 pub desktop_env: azul_css::dynamic_selector::OptionLinuxDesktopEnv,
188 pub viewport_width: azul_css::OptionF32,
191 pub viewport_height: azul_css::OptionF32,
192 pub prefers_reduced_motion: azul_css::OptionBool,
194 pub prefers_high_contrast: azul_css::OptionBool,
196}
197
198impl CssMockEnvironment {
199 pub fn linux() -> Self {
201 Self {
202 os: azul_css::dynamic_selector::OptionOsCondition::Some(azul_css::dynamic_selector::OsCondition::Linux),
203 ..Default::default()
204 }
205 }
206
207 pub fn windows() -> Self {
209 Self {
210 os: azul_css::dynamic_selector::OptionOsCondition::Some(azul_css::dynamic_selector::OsCondition::Windows),
211 ..Default::default()
212 }
213 }
214
215 pub fn macos() -> Self {
217 Self {
218 os: azul_css::dynamic_selector::OptionOsCondition::Some(azul_css::dynamic_selector::OsCondition::MacOS),
219 ..Default::default()
220 }
221 }
222
223 pub fn dark_theme() -> Self {
225 Self {
226 theme: azul_css::dynamic_selector::OptionThemeCondition::Some(azul_css::dynamic_selector::ThemeCondition::Dark),
227 ..Default::default()
228 }
229 }
230
231 pub fn light_theme() -> Self {
233 Self {
234 theme: azul_css::dynamic_selector::OptionThemeCondition::Some(azul_css::dynamic_selector::ThemeCondition::Light),
235 ..Default::default()
236 }
237 }
238
239 pub fn apply_to(&self, ctx: &mut azul_css::dynamic_selector::DynamicSelectorContext) {
241 if let azul_css::dynamic_selector::OptionOsCondition::Some(os) = self.os {
242 ctx.os = os;
243 }
244 if let azul_css::dynamic_selector::OptionOsVersion::Some(os_version) = self.os_version {
245 ctx.os_version = os_version;
246 }
247 if let azul_css::dynamic_selector::OptionLinuxDesktopEnv::Some(de) = self.desktop_env {
248 ctx.desktop_env = azul_css::dynamic_selector::OptionLinuxDesktopEnv::Some(de);
249 }
250 if let azul_css::dynamic_selector::OptionThemeCondition::Some(ref theme) = self.theme {
251 ctx.theme = theme.clone();
252 }
253 if let azul_css::OptionString::Some(ref lang) = self.language {
254 ctx.language = lang.clone();
255 }
256 if let azul_css::OptionBool::Some(reduced) = self.prefers_reduced_motion {
257 ctx.prefers_reduced_motion = if reduced {
258 azul_css::dynamic_selector::BoolCondition::True
259 } else {
260 azul_css::dynamic_selector::BoolCondition::False
261 };
262 }
263 if let azul_css::OptionBool::Some(high_contrast) = self.prefers_high_contrast {
264 ctx.prefers_high_contrast = if high_contrast {
265 azul_css::dynamic_selector::BoolCondition::True
266 } else {
267 azul_css::dynamic_selector::BoolCondition::False
268 };
269 }
270 if let azul_css::OptionF32::Some(w) = self.viewport_width {
271 ctx.viewport_width = w;
272 }
273 if let azul_css::OptionF32::Some(h) = self.viewport_height {
274 ctx.viewport_height = h;
275 }
276 }
277}
278
279impl_option!(
280 CssMockEnvironment,
281 OptionCssMockEnvironment,
282 copy = false,
283 [Debug, Clone]
284);
285
286#[repr(C)]
303pub struct Route {
304 pub pattern: AzString,
306 pub layout_callback: LayoutCallback,
308}
309
310impl Clone for Route {
311 fn clone(&self) -> Self {
312 Self { pattern: self.pattern.clone(), layout_callback: self.layout_callback.clone() }
313 }
314}
315impl fmt::Debug for Route {
316 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
317 f.debug_struct("Route")
318 .field("pattern", &self.pattern)
319 .field("layout_callback", &self.layout_callback)
320 .finish()
321 }
322}
323impl PartialEq for Route { fn eq(&self, o: &Self) -> bool { self.pattern == o.pattern && self.layout_callback == o.layout_callback } }
324impl Eq for Route {}
325impl PartialOrd for Route { fn partial_cmp(&self, o: &Self) -> Option<core::cmp::Ordering> { Some(self.cmp(o)) } }
326impl Ord for Route { fn cmp(&self, o: &Self) -> core::cmp::Ordering { self.pattern.cmp(&o.pattern).then_with(|| self.layout_callback.cmp(&o.layout_callback)) } }
327impl Hash for Route { fn hash<H: Hasher>(&self, state: &mut H) { self.pattern.hash(state); self.layout_callback.hash(state); } }
328
329impl_option!(Route, OptionRoute, copy = false, [Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash]);
330impl_vec!(Route, RouteVec, RouteVecDestructor, RouteVecDestructorType, RouteVecSlice, OptionRoute);
331impl_vec_mut!(Route, RouteVec);
332impl_vec_debug!(Route, RouteVec);
333impl_vec_clone!(Route, RouteVec, RouteVecDestructor);
334impl_vec_partialeq!(Route, RouteVec);
335impl_vec_eq!(Route, RouteVec);
336impl_vec_partialord!(Route, RouteVec);
337impl_vec_ord!(Route, RouteVec);
338impl_vec_hash!(Route, RouteVec);
339
340#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
345#[repr(C)]
346pub struct RouteMatch {
347 pub pattern: AzString,
349 pub params: StringPairVec,
351}
352
353impl RouteMatch {
354 pub fn get_param(&self, key: &str) -> Option<&AzString> {
356 self.params.get_key(key)
357 }
358}
359
360impl_option!(RouteMatch, OptionRouteMatch, copy = false, [Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash]);
361
362pub fn match_route(pattern: &str, path: &str) -> Option<RouteMatch> {
371 let pat_segs: Vec<&str> = pattern.split('/').filter(|s| !s.is_empty()).collect();
372 let path_segs: Vec<&str> = path.split('/').filter(|s| !s.is_empty()).collect();
373
374 if pat_segs.len() != path_segs.len() {
375 return None;
376 }
377
378 let mut params = Vec::new();
379 for (pat, val) in pat_segs.iter().zip(path_segs.iter()) {
380 if let Some(param_name) = pat.strip_prefix(':') {
381 params.push(AzStringPair {
382 key: AzString::from(param_name.to_string()),
383 value: AzString::from(val.to_string()),
384 });
385 } else if pat != val {
386 return None;
387 }
388 }
389
390 Some(RouteMatch {
391 pattern: AzString::from(pattern.to_string()),
392 params: StringPairVec::from_vec(params),
393 })
394}
395
396#[derive(Debug, Clone)]
398#[repr(C)]
399pub struct AppConfig {
400 pub log_level: AppLogLevel,
404 pub enable_visual_panic_hook: bool,
407 pub enable_logging_on_panic: bool,
410 pub enable_tab_navigation: bool,
413 pub termination_behavior: AppTerminationBehavior,
416 pub icon_provider: crate::icon::IconProviderHandle,
420 pub bundled_fonts: NamedFontVec,
423 pub font_loading: FontLoadingConfig,
426 pub mock_css_environment: OptionCssMockEnvironment,
436 pub system_style: SystemStyle,
442 pub component_libraries: ComponentLibraryVec,
452 pub routes: RouteVec,
459}
460
461impl AppConfig {
462 pub fn create() -> Self {
463 let log_level = AppLogLevel::Error;
464 let icon_provider = crate::icon::IconProviderHandle::new();
465 let bundled_fonts = NamedFontVec::from_const_slice(&[]);
466 let font_loading = FontLoadingConfig::default();
467 let system_style = SystemStyle::detect();
468 let mut s = Self {
469 log_level,
470 enable_visual_panic_hook: false,
471 enable_logging_on_panic: true,
472 enable_tab_navigation: true,
473 termination_behavior: AppTerminationBehavior::default(),
474 icon_provider,
475 bundled_fonts,
476 font_loading,
477 mock_css_environment: OptionCssMockEnvironment::None,
478 system_style,
479 component_libraries: ComponentLibraryVec::from_const_slice(&[]),
480 routes: RouteVec::from_const_slice(&[]),
481 };
482 s.add_component_library(
485 AzString::from_const_str("builtin"),
486 crate::xml::register_builtin_components as extern "C" fn() -> crate::xml::ComponentLibrary,
487 );
488 s
489 }
490
491 pub fn with_mock_environment(mut self, env: CssMockEnvironment) -> Self {
508 self.mock_css_environment = OptionCssMockEnvironment::Some(env);
509 self
510 }
511
512 pub fn add_component<R: Into<RegisterComponentFn>>(&mut self, library: AzString, register_fn: R) {
524 let register_fn = register_fn.into();
525 let component = (register_fn.cb)();
526 let empty_libs = ComponentLibraryVec::from_const_slice(&[]);
527 let mut libs = core::mem::replace(&mut self.component_libraries, empty_libs).into_library_owned_vec();
528
529 if let Some(existing_lib) = libs.iter_mut().find(|l| l.name.as_str() == library.as_str()) {
530 let empty_comps = ComponentDefVec::from_const_slice(&[]);
531 let mut comps = core::mem::replace(&mut existing_lib.components, empty_comps).into_library_owned_vec();
532 if let Some(ec) = comps.iter_mut().find(|c| c.id.name.as_str() == component.id.name.as_str()) {
533 *ec = component;
534 } else {
535 comps.push(component);
536 }
537 existing_lib.components = ComponentDefVec::from_vec(comps);
538 } else {
539 libs.push(ComponentLibrary {
540 name: library,
541 version: AzString::from_const_str("1.0.0"),
542 description: AzString::from_const_str(""),
543 components: ComponentDefVec::from_vec(alloc::vec![component]),
544 exportable: true,
545 modifiable: true,
546 data_models: crate::xml::ComponentDataModelVec::from_const_slice(&[]),
547 enum_models: crate::xml::ComponentEnumModelVec::from_const_slice(&[]),
548 });
549 }
550
551 self.component_libraries = ComponentLibraryVec::from_vec(libs);
552 }
553
554 pub fn add_component_library<R: Into<RegisterComponentLibraryFn>>(&mut self, name: AzString, register_fn: R) {
566 let register_fn = register_fn.into();
567 let mut library = (register_fn.cb)();
568 library.name = name;
569
570 let empty_libs = ComponentLibraryVec::from_const_slice(&[]);
571 let mut libs = core::mem::replace(&mut self.component_libraries, empty_libs).into_library_owned_vec();
572 if let Some(existing) = libs.iter_mut().find(|l| l.name.as_str() == library.name.as_str()) {
573 *existing = library;
574 } else {
575 libs.push(library);
576 }
577
578 self.component_libraries = ComponentLibraryVec::from_vec(libs);
579 }
580
581 pub fn add_route<P: Into<AzString>, L: Into<LayoutCallback>>(&mut self, pattern: P, layout_fn: L) {
592 let route = Route {
593 pattern: pattern.into(),
594 layout_callback: layout_fn.into(),
595 };
596 let empty = RouteVec::from_const_slice(&[]);
597 let mut routes = core::mem::replace(&mut self.routes, empty).into_library_owned_vec();
598 if let Some(existing) = routes.iter_mut().find(|r| r.pattern.as_str() == route.pattern.as_str()) {
600 *existing = route;
601 } else {
602 routes.push(route);
603 }
604 self.routes = RouteVec::from_vec(routes);
605 }
606
607 pub fn match_route_for_path(&self, path: &str) -> Option<(&Route, RouteMatch)> {
611 for route in self.routes.as_ref().iter() {
612 if let Some(m) = match_route(route.pattern.as_str(), path) {
613 return Some((route, m));
614 }
615 }
616 None
617 }
618}
619
620impl Default for AppConfig {
621 fn default() -> Self {
622 Self::create()
623 }
624}
625
626#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
627#[repr(C)]
628pub enum AppLogLevel {
629 Off,
630 Error,
631 Warn,
632 Info,
633 Debug,
634 Trace,
635}
636
637#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
639#[repr(C)]
640pub struct ImageDescriptor {
641 pub format: RawImageFormat,
643 pub width: usize,
645 pub height: usize,
646 pub stride: OptionI32,
651 pub offset: i32,
657 pub flags: ImageDescriptorFlags,
659}
660
661#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
663#[repr(C)]
664pub struct ImageDescriptorFlags {
665 pub is_opaque: bool,
668 pub allow_mipmaps: bool,
674}
675
676#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
677pub struct IdNamespace(pub u32);
678
679impl ::core::fmt::Display for IdNamespace {
680 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
681 write!(f, "IdNamespace({})", self.0)
682 }
683}
684
685impl ::core::fmt::Debug for IdNamespace {
686 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
687 write!(f, "{}", self)
688 }
689}
690
691#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
692#[repr(C)]
693pub enum RawImageFormat {
694 R8,
695 RG8,
696 RGB8,
697 RGBA8,
698 R16,
699 RG16,
700 RGB16,
701 RGBA16,
702 BGR8,
703 BGRA8,
704 RGBF32,
705 RGBAF32,
706}
707
708static IMAGE_KEY: AtomicU64 = AtomicU64::new(1);
710static FONT_KEY: AtomicU64 = AtomicU64::new(0);
711static FONT_INSTANCE_KEY: AtomicU64 = AtomicU64::new(0);
712
713#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
714pub struct ImageKey {
715 pub namespace: IdNamespace,
716 pub key: u64,
717}
718
719impl ImageKey {
720 pub const DUMMY: Self = Self {
721 namespace: IdNamespace(0),
722 key: 0,
723 };
724
725 pub fn unique(render_api_namespace: IdNamespace) -> Self {
726 Self {
727 namespace: render_api_namespace,
728 key: IMAGE_KEY.fetch_add(1, AtomicOrdering::SeqCst),
729 }
730 }
731}
732
733#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
734pub struct FontKey {
735 pub namespace: IdNamespace,
736 pub key: u64,
737}
738
739impl FontKey {
740 pub fn unique(render_api_namespace: IdNamespace) -> Self {
741 Self {
742 namespace: render_api_namespace,
743 key: FONT_KEY.fetch_add(1, AtomicOrdering::SeqCst),
744 }
745 }
746}
747
748#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
749pub struct FontInstanceKey {
750 pub namespace: IdNamespace,
751 pub key: u64,
752}
753
754impl FontInstanceKey {
755 pub fn unique(render_api_namespace: IdNamespace) -> Self {
756 Self {
757 namespace: render_api_namespace,
758 key: FONT_INSTANCE_KEY.fetch_add(1, AtomicOrdering::SeqCst),
759 }
760 }
761}
762
763#[derive(Debug)]
766pub enum DecodedImage {
767 NullImage {
770 width: usize,
771 height: usize,
772 format: RawImageFormat,
773 tag: Vec<u8>,
775 },
776 Gl(Texture),
778 Raw((ImageDescriptor, ImageData)),
780 Callback(CoreImageCallback),
782 }
787
788#[derive(Debug)]
789#[repr(C)]
790pub struct ImageRef {
791 pub data: *const DecodedImage,
793 pub copies: *const AtomicUsize,
795 pub run_destructor: bool,
796}
797
798impl ImageRef {
799 pub fn get_hash(&self) -> ImageRefHash {
800 image_ref_get_hash(self)
801 }
802}
803
804#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Hash, Ord, Eq)]
805#[repr(C)]
806pub struct ImageRefHash {
807 pub inner: usize,
808}
809
810impl_option!(
811 ImageRef,
812 OptionImageRef,
813 copy = false,
814 [Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash]
815);
816
817impl ImageRef {
818 pub fn into_inner(self) -> Option<DecodedImage> {
820 unsafe {
821 if self.copies.as_ref().map(|m| m.load(AtomicOrdering::SeqCst)) == Some(1) {
822 let data = Box::from_raw(self.data as *mut DecodedImage);
823 let _ = Box::from_raw(self.copies as *mut AtomicUsize);
824 core::mem::forget(self); Some(*data)
826 } else {
827 None
828 }
829 }
830 }
831
832 pub fn get_data<'a>(&'a self) -> &'a DecodedImage {
833 unsafe { &*self.data }
834 }
835
836 pub fn get_image_callback<'a>(&'a self) -> Option<&'a CoreImageCallback> {
837 if unsafe { self.copies.as_ref().map(|m| m.load(AtomicOrdering::SeqCst)) != Some(1) } {
838 return None; }
840
841 match unsafe { &*self.data } {
842 DecodedImage::Callback(gl_texture_callback) => Some(gl_texture_callback),
843 _ => None,
844 }
845 }
846
847 pub fn get_image_callback_mut<'a>(&'a mut self) -> Option<&'a mut CoreImageCallback> {
848 if unsafe { self.copies.as_ref().map(|m| m.load(AtomicOrdering::SeqCst)) != Some(1) } {
849 return None; }
851
852 match unsafe { &mut *(self.data as *mut DecodedImage) } {
853 DecodedImage::Callback(gl_texture_callback) => Some(gl_texture_callback),
854 _ => None,
855 }
856 }
857
858 pub fn deep_copy(&self) -> Self {
860 let new_data = match self.get_data() {
861 DecodedImage::NullImage {
862 width,
863 height,
864 format,
865 tag,
866 } => DecodedImage::NullImage {
867 width: *width,
868 height: *height,
869 format: *format,
870 tag: tag.clone(),
871 },
872 DecodedImage::Gl(tex) => DecodedImage::NullImage {
876 width: tex.size.width as usize,
877 height: tex.size.height as usize,
878 format: tex.format,
879 tag: Vec::new(),
880 },
881 DecodedImage::Raw((descriptor, data)) => {
884 DecodedImage::Raw((descriptor.clone(), data.clone()))
885 }
886 DecodedImage::Callback(cb) => DecodedImage::Callback(cb.clone()),
887 };
888
889 Self::new(new_data)
890 }
891
892 pub fn is_null_image(&self) -> bool {
893 match self.get_data() {
894 DecodedImage::NullImage { .. } => true,
895 _ => false,
896 }
897 }
898
899 pub fn is_gl_texture(&self) -> bool {
900 match self.get_data() {
901 DecodedImage::Gl(_) => true,
902 _ => false,
903 }
904 }
905
906 pub fn is_raw_image(&self) -> bool {
907 match self.get_data() {
908 DecodedImage::Raw((_, _)) => true,
909 _ => false,
910 }
911 }
912
913 pub fn is_callback(&self) -> bool {
914 match self.get_data() {
915 DecodedImage::Callback(_) => true,
916 _ => false,
917 }
918 }
919
920 pub fn get_rawimage(&self) -> Option<RawImage> {
922 match self.get_data() {
923 DecodedImage::Raw((image_descriptor, image_data)) => Some(RawImage {
924 pixels: match image_data {
925 ImageData::Raw(shared_data) => {
926 let data_clone = shared_data.clone();
929 if let Some(u8vec) = data_clone.into_inner() {
930 RawImageData::U8(u8vec)
931 } else {
932 RawImageData::U8(shared_data.as_ref().to_vec().into())
934 }
935 }
936 ImageData::External(_) => return None,
937 },
938 width: image_descriptor.width,
939 height: image_descriptor.height,
940 premultiplied_alpha: true,
941 data_format: image_descriptor.format,
942 tag: Vec::new().into(),
943 }),
944 _ => None,
945 }
946 }
947
948 pub fn get_bytes(&self) -> Option<&[u8]> {
951 match self.get_data() {
952 DecodedImage::Raw((_, image_data)) => match image_data {
953 ImageData::Raw(shared_data) => Some(shared_data.as_ref()),
954 ImageData::External(_) => None,
955 },
956 _ => None,
957 }
958 }
959
960 pub fn get_bytes_ptr(&self) -> *const u8 {
963 match self.get_data() {
964 DecodedImage::Raw((_, image_data)) => match image_data {
965 ImageData::Raw(shared_data) => shared_data.as_ptr(),
966 ImageData::External(_) => core::ptr::null(),
967 },
968 _ => core::ptr::null(),
969 }
970 }
971
972 pub fn get_size(&self) -> LogicalSize {
974 match self.get_data() {
975 DecodedImage::NullImage { width, height, .. } => {
976 LogicalSize::new(*width as f32, *height as f32)
977 }
978 DecodedImage::Gl(tex) => {
979 LogicalSize::new(tex.size.width as f32, tex.size.height as f32)
980 }
981 DecodedImage::Raw((image_descriptor, _)) => LogicalSize::new(
982 image_descriptor.width as f32,
983 image_descriptor.height as f32,
984 ),
985 DecodedImage::Callback(_) => LogicalSize::new(0.0, 0.0),
986 }
987 }
988
989 pub fn null_image(width: usize, height: usize, format: RawImageFormat, tag: Vec<u8>) -> Self {
990 Self::new(DecodedImage::NullImage {
991 width,
992 height,
993 format,
994 tag,
995 })
996 }
997
998 pub fn callback<C: Into<CoreRenderImageCallback>>(callback: C, data: RefAny) -> Self {
999 Self::new(DecodedImage::Callback(CoreImageCallback {
1000 callback: callback.into(),
1001 refany: data,
1002 }))
1003 }
1004
1005 pub fn new_rawimage(image_data: RawImage) -> Option<Self> {
1006 let (image_data, image_descriptor) = image_data.into_loaded_image_source()?;
1007 Some(Self::new(DecodedImage::Raw((image_descriptor, image_data))))
1008 }
1009
1010 pub fn new_gltexture(texture: Texture) -> Self {
1011 Self::new(DecodedImage::Gl(texture))
1012 }
1013
1014 fn new(data: DecodedImage) -> Self {
1015 Self {
1016 data: Box::into_raw(Box::new(data)),
1017 copies: Box::into_raw(Box::new(AtomicUsize::new(1))),
1018 run_destructor: true,
1019 }
1020 }
1021
1022 }
1024
1025unsafe impl Send for ImageRef {}
1026unsafe impl Sync for ImageRef {}
1027
1028impl PartialEq for ImageRef {
1029 fn eq(&self, rhs: &Self) -> bool {
1030 self.data as usize == rhs.data as usize
1031 }
1032}
1033
1034impl PartialOrd for ImageRef {
1035 fn partial_cmp(&self, other: &Self) -> Option<::core::cmp::Ordering> {
1036 Some((self.data as usize).cmp(&(other.data as usize)))
1037 }
1038}
1039
1040impl Ord for ImageRef {
1041 fn cmp(&self, other: &Self) -> core::cmp::Ordering {
1042 let self_data = self.data as usize;
1043 let other_data = other.data as usize;
1044 self_data.cmp(&other_data)
1045 }
1046}
1047
1048impl Eq for ImageRef {}
1049
1050impl Hash for ImageRef {
1051 fn hash<H>(&self, state: &mut H)
1052 where
1053 H: Hasher,
1054 {
1055 let self_data = self.data as usize;
1056 self_data.hash(state)
1057 }
1058}
1059
1060impl Clone for ImageRef {
1061 fn clone(&self) -> Self {
1062 unsafe {
1063 self.copies
1064 .as_ref()
1065 .map(|m| m.fetch_add(1, AtomicOrdering::SeqCst));
1066 }
1067 Self {
1068 data: self.data, copies: self.copies, run_destructor: true,
1071 }
1072 }
1073}
1074
1075impl Drop for ImageRef {
1076 fn drop(&mut self) {
1077 self.run_destructor = false;
1078 unsafe {
1079 let copies = (*self.copies).fetch_sub(1, AtomicOrdering::SeqCst);
1080 if copies == 1 {
1081 let _ = Box::from_raw(self.data as *mut DecodedImage);
1082 let _ = Box::from_raw(self.copies as *mut AtomicUsize);
1083 }
1084 }
1085 }
1086}
1087
1088pub fn image_ref_get_hash(ir: &ImageRef) -> ImageRefHash {
1089 ImageRefHash {
1090 inner: ir.data as usize,
1091 }
1092}
1093
1094pub fn image_ref_hash_to_image_key(hash: ImageRefHash, namespace: IdNamespace) -> ImageKey {
1099 ImageKey {
1100 namespace,
1101 key: hash.inner as u64,
1102 }
1103}
1104
1105pub fn font_ref_get_hash(fr: &FontRef) -> u64 {
1106 fr.get_hash()
1107}
1108
1109#[derive(Debug)]
1115pub struct ImageCache {
1116 pub image_id_map: OrderedMap<AzString, ImageRef>,
1122}
1123
1124impl Default for ImageCache {
1125 fn default() -> Self {
1126 Self {
1127 image_id_map: OrderedMap::default(),
1128 }
1129 }
1130}
1131
1132impl ImageCache {
1133 pub fn new() -> Self {
1134 Self::default()
1135 }
1136
1137 pub fn add_css_image_id(&mut self, css_id: AzString, image: ImageRef) {
1140 self.image_id_map.insert(css_id, image);
1141 }
1142
1143 pub fn get_css_image_id(&self, css_id: &AzString) -> Option<&ImageRef> {
1144 self.image_id_map.get(css_id)
1145 }
1146
1147 pub fn delete_css_image_id(&mut self, css_id: &AzString) {
1148 self.image_id_map.remove(css_id);
1149 }
1150}
1151
1152#[derive(Debug, Copy, Clone, PartialEq)]
1153pub struct ResolvedImage {
1154 pub key: ImageKey,
1155 pub descriptor: ImageDescriptor,
1156}
1157
1158pub trait RendererResourcesTrait: core::fmt::Debug {
1160 fn get_font_family(
1162 &self,
1163 style_font_families_hash: &StyleFontFamiliesHash,
1164 ) -> Option<&StyleFontFamilyHash>;
1165
1166 fn get_font_key(&self, style_font_family_hash: &StyleFontFamilyHash) -> Option<&FontKey>;
1168
1169 fn get_registered_font(
1171 &self,
1172 font_key: &FontKey,
1173 ) -> Option<&(FontRef, OrderedMap<(Au, DpiScaleFactor), FontInstanceKey>)>;
1174
1175 fn get_image(&self, hash: &ImageRefHash) -> Option<&ResolvedImage>;
1177
1178 fn update_image(
1180 &mut self,
1181 image_ref_hash: &ImageRefHash,
1182 descriptor: crate::resources::ImageDescriptor,
1183 );
1184}
1185
1186impl RendererResourcesTrait for RendererResources {
1188 fn get_font_family(
1189 &self,
1190 style_font_families_hash: &StyleFontFamiliesHash,
1191 ) -> Option<&StyleFontFamilyHash> {
1192 self.font_families_map.get(style_font_families_hash)
1193 }
1194
1195 fn get_font_key(&self, style_font_family_hash: &StyleFontFamilyHash) -> Option<&FontKey> {
1196 self.font_id_map.get(style_font_family_hash)
1197 }
1198
1199 fn get_registered_font(
1200 &self,
1201 font_key: &FontKey,
1202 ) -> Option<&(FontRef, OrderedMap<(Au, DpiScaleFactor), FontInstanceKey>)> {
1203 self.currently_registered_fonts.get(font_key)
1204 }
1205
1206 fn get_image(&self, hash: &ImageRefHash) -> Option<&ResolvedImage> {
1207 self.currently_registered_images.get(hash)
1208 }
1209
1210 fn update_image(
1211 &mut self,
1212 image_ref_hash: &ImageRefHash,
1213 descriptor: crate::resources::ImageDescriptor,
1214 ) {
1215 if let Some(s) = self.currently_registered_images.get_mut(image_ref_hash) {
1216 s.descriptor = descriptor;
1217 }
1218 }
1219}
1220
1221pub struct RendererResources {
1228 pub currently_registered_images: OrderedMap<ImageRefHash, ResolvedImage>,
1230 pub image_key_map: OrderedMap<ImageKey, ImageRefHash>,
1232 pub currently_registered_fonts:
1234 OrderedMap<FontKey, (FontRef, OrderedMap<(Au, DpiScaleFactor), FontInstanceKey>)>,
1235 pub last_frame_registered_fonts:
1242 OrderedMap<FontKey, OrderedMap<(Au, DpiScaleFactor), FontInstanceKey>>,
1243 pub font_families_map: OrderedMap<StyleFontFamiliesHash, StyleFontFamilyHash>,
1248 pub font_id_map: OrderedMap<StyleFontFamilyHash, FontKey>,
1250 pub font_hash_map: OrderedMap<u64, FontKey>,
1253}
1254
1255impl fmt::Debug for RendererResources {
1256 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1257 write!(
1258 f,
1259 "RendererResources {{
1260 currently_registered_images: {:#?},
1261 currently_registered_fonts: {:#?},
1262 font_families_map: {:#?},
1263 font_id_map: {:#?},
1264 }}",
1265 self.currently_registered_images.keys().collect::<Vec<_>>(),
1266 self.currently_registered_fonts.keys().collect::<Vec<_>>(),
1267 self.font_families_map.keys().collect::<Vec<_>>(),
1268 self.font_id_map.keys().collect::<Vec<_>>(),
1269 )
1270 }
1271}
1272
1273impl Default for RendererResources {
1274 fn default() -> Self {
1275 Self {
1276 currently_registered_images: OrderedMap::default(),
1277 image_key_map: OrderedMap::default(),
1278 currently_registered_fonts: OrderedMap::default(),
1279 last_frame_registered_fonts: OrderedMap::default(),
1280 font_families_map: OrderedMap::default(),
1281 font_id_map: OrderedMap::default(),
1282 font_hash_map: OrderedMap::default(),
1283 }
1284 }
1285}
1286
1287impl RendererResources {
1288 pub fn get_renderable_font_data(
1289 &self,
1290 font_instance_key: &FontInstanceKey,
1291 ) -> Option<(&FontRef, Au, DpiScaleFactor)> {
1292 self.currently_registered_fonts
1293 .iter()
1294 .find_map(|(font_key, (font_ref, instances))| {
1295 instances.iter().find_map(|((au, dpi), instance_key)| {
1296 if *instance_key == *font_instance_key {
1297 Some((font_ref, *au, *dpi))
1298 } else {
1299 None
1300 }
1301 })
1302 })
1303 }
1304
1305 pub fn get_font_instance_key_for_text(
1306 &self,
1307 font_size_px: f32,
1308 css_property_cache: &CssPropertyCache,
1309 node_data: &NodeData,
1310 node_id: &NodeId,
1311 styled_node_state: &StyledNodeState,
1312 dpi_scale: f32,
1313 ) -> Option<FontInstanceKey> {
1314 let font_size = StyleFontSize {
1316 inner: azul_css::props::basic::PixelValue::const_px(font_size_px as isize),
1317 };
1318
1319 let font_size_au = font_size_to_au(font_size);
1321
1322 let dpi_scale_factor = DpiScaleFactor {
1324 inner: FloatValue::new(dpi_scale),
1325 };
1326
1327 let font_family =
1329 css_property_cache.get_font_id_or_default(node_data, node_id, styled_node_state);
1330
1331 let font_families_hash = StyleFontFamiliesHash::new(font_family.as_ref());
1333
1334 self.get_font_instance_key(&font_families_hash, font_size_au, dpi_scale_factor)
1335 }
1336
1337 pub fn get_font_instance_key(
1338 &self,
1339 font_families_hash: &StyleFontFamiliesHash,
1340 font_size_au: Au,
1341 dpi_scale: DpiScaleFactor,
1342 ) -> Option<FontInstanceKey> {
1343 let font_family_hash = self.get_font_family(font_families_hash)?;
1344 let font_key = self.get_font_key(font_family_hash)?;
1345 let (_, instances) = self.get_registered_font(font_key)?;
1346 instances.get(&(font_size_au, dpi_scale)).copied()
1347 }
1348
1349 fn remove_font_families_with_zero_references(&mut self) {
1351 let font_family_to_delete = self
1352 .font_id_map
1353 .iter()
1354 .filter_map(|(font_family, font_key)| {
1355 if !self.currently_registered_fonts.contains_key(font_key) {
1356 Some(font_family.clone())
1357 } else {
1358 None
1359 }
1360 })
1361 .collect::<Vec<_>>();
1362
1363 for f in font_family_to_delete {
1364 self.font_id_map.remove(&f); }
1366
1367 let font_families_to_delete = self
1368 .font_families_map
1369 .iter()
1370 .filter_map(|(font_families, font_family)| {
1371 if !self.font_id_map.contains_key(font_family) {
1372 Some(font_families.clone())
1373 } else {
1374 None
1375 }
1376 })
1377 .collect::<Vec<_>>();
1378
1379 for f in font_families_to_delete {
1380 self.font_families_map.remove(&f); }
1382 }
1383}
1384
1385#[derive(Debug, Clone)]
1396pub struct UpdateImageResult {
1397 pub key_to_update: ImageKey,
1398 pub new_descriptor: ImageDescriptor,
1399 pub new_image_data: ImageData,
1400}
1401
1402#[derive(Debug, Default)]
1403pub struct GlTextureCache {
1404 pub solved_textures:
1405 BTreeMap<DomId, BTreeMap<NodeId, (ImageKey, ImageDescriptor, ExternalImageId)>>,
1406 pub hashes: BTreeMap<(DomId, NodeId, ImageRefHash), ImageRefHash>,
1407}
1408
1409unsafe impl Send for GlTextureCache {}
1411
1412impl GlTextureCache {
1413 pub fn empty() -> Self {
1415 Self {
1416 solved_textures: BTreeMap::new(),
1417 hashes: BTreeMap::new(),
1418 }
1419 }
1420
1421 pub fn update_texture(
1440 &mut self,
1441 dom_id: DomId,
1442 node_id: NodeId,
1443 document_id: DocumentId,
1444 epoch: Epoch,
1445 new_texture: Texture,
1446 insert_into_active_gl_textures_fn: &GlStoreImageFn,
1447 ) -> Option<ExternalImageId> {
1448 let new_descriptor = new_texture.get_descriptor();
1449 let di_map = self.solved_textures.get_mut(&dom_id)?;
1450 let entry = di_map.get_mut(&node_id)?;
1451
1452 entry.1 = new_descriptor;
1454
1455 let external_image_id = texture_external_image_id(dom_id, node_id);
1458 (insert_into_active_gl_textures_fn)(document_id, epoch, new_texture, external_image_id);
1459 entry.2 = external_image_id;
1460
1461 Some(external_image_id)
1462 }
1463}
1464
1465macro_rules! unique_id {
1466 ($struct_name:ident, $counter_name:ident) => {
1467 #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)]
1468 #[repr(C)]
1469 pub struct $struct_name {
1470 pub id: usize,
1471 }
1472
1473 impl $struct_name {
1474 pub fn unique() -> Self {
1475 Self {
1476 id: $counter_name.fetch_add(1, AtomicOrdering::SeqCst),
1477 }
1478 }
1479 }
1480 };
1481}
1482
1483static PROPERTY_KEY_COUNTER: AtomicUsize = AtomicUsize::new(0);
1485unique_id!(TransformKey, PROPERTY_KEY_COUNTER);
1486unique_id!(ColorKey, PROPERTY_KEY_COUNTER);
1487unique_id!(OpacityKey, PROPERTY_KEY_COUNTER);
1488
1489static IMAGE_ID_COUNTER: AtomicUsize = AtomicUsize::new(0);
1490unique_id!(ImageId, IMAGE_ID_COUNTER);
1491static FONT_ID_COUNTER: AtomicUsize = AtomicUsize::new(0);
1492unique_id!(FontId, FONT_ID_COUNTER);
1493
1494#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
1495#[repr(C)]
1496pub struct ImageMask {
1497 pub image: ImageRef,
1498 pub rect: LogicalRect,
1499 pub repeat: bool,
1500}
1501
1502impl_option!(
1503 ImageMask,
1504 OptionImageMask,
1505 copy = false,
1506 [Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash]
1507);
1508
1509#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
1510pub enum ImmediateFontId {
1511 Resolved((StyleFontFamilyHash, FontKey)),
1512 Unresolved(StyleFontFamilyVec),
1513}
1514
1515#[derive(Debug, Clone, PartialEq, PartialOrd)]
1516#[repr(C, u8)]
1517pub enum RawImageData {
1518 U8(U8Vec),
1520 U16(U16Vec),
1522 F32(F32Vec),
1524}
1525
1526impl RawImageData {
1527 pub fn get_u8_vec_ref(&self) -> Option<&U8Vec> {
1528 match self {
1529 RawImageData::U8(v) => Some(v),
1530 _ => None,
1531 }
1532 }
1533
1534 pub fn get_u16_vec_ref(&self) -> Option<&U16Vec> {
1535 match self {
1536 RawImageData::U16(v) => Some(v),
1537 _ => None,
1538 }
1539 }
1540
1541 pub fn get_f32_vec_ref(&self) -> Option<&F32Vec> {
1542 match self {
1543 RawImageData::F32(v) => Some(v),
1544 _ => None,
1545 }
1546 }
1547
1548 fn get_u8_vec(self) -> Option<U8Vec> {
1549 match self {
1550 RawImageData::U8(v) => Some(v),
1551 _ => None,
1552 }
1553 }
1554
1555 fn get_u16_vec(self) -> Option<U16Vec> {
1556 match self {
1557 RawImageData::U16(v) => Some(v),
1558 _ => None,
1559 }
1560 }
1561}
1562
1563#[derive(Debug, Clone, PartialEq, PartialOrd)]
1564#[repr(C)]
1565pub struct RawImage {
1566 pub pixels: RawImageData,
1567 pub width: usize,
1568 pub height: usize,
1569 pub premultiplied_alpha: bool,
1570 pub data_format: RawImageFormat,
1571 pub tag: U8Vec,
1572}
1573
1574impl RawImage {
1575 pub fn null_image() -> Self {
1577 Self {
1578 pixels: RawImageData::U8(Vec::new().into()),
1579 width: 0,
1580 height: 0,
1581 premultiplied_alpha: true,
1582 data_format: RawImageFormat::BGRA8,
1583 tag: Vec::new().into(),
1584 }
1585 }
1586
1587 pub fn allocate_mask(size: LayoutSize) -> Self {
1589 Self {
1590 pixels: RawImageData::U8(
1591 vec![0; size.width.max(0) as usize * size.height.max(0) as usize].into(),
1592 ),
1593 width: size.width as usize,
1594 height: size.height as usize,
1595 premultiplied_alpha: true,
1596 data_format: RawImageFormat::R8,
1597 tag: Vec::new().into(),
1598 }
1599 }
1600
1601 pub fn into_loaded_image_source(self) -> Option<(ImageData, ImageDescriptor)> {
1607 #[inline(always)]
1610 fn premultiply_alpha(array: &mut [u8]) {
1611 if array.len() != 4 {
1612 return;
1613 }
1614 let a = u32::from(array[3]);
1615 array[0] = (((array[0] as u32 * a) + 128) / 255) as u8;
1616 array[1] = (((array[1] as u32 * a) + 128) / 255) as u8;
1617 array[2] = (((array[2] as u32 * a) + 128) / 255) as u8;
1618 }
1619
1620 #[inline(always)]
1621 fn normalize_u16(i: u16) -> u8 {
1622 ((core::u16::MAX as f32 / i as f32) * core::u8::MAX as f32) as u8
1623 }
1624
1625 let RawImage {
1626 width,
1627 height,
1628 pixels,
1629 mut data_format,
1630 premultiplied_alpha,
1631 tag,
1632 } = self;
1633
1634 const FOUR_BPP: usize = 4;
1635 const TWO_CHANNELS: usize = 2;
1636 const THREE_CHANNELS: usize = 3;
1637 const FOUR_CHANNELS: usize = 4;
1638
1639 let mut is_opaque = true;
1640
1641 let expected_len = width * height;
1642
1643 let bytes: U8Vec = match data_format {
1644 RawImageFormat::R8 => {
1645 let pixels = pixels.get_u8_vec()?;
1649
1650 if pixels.len() != expected_len {
1651 return None;
1652 }
1653
1654 is_opaque = false;
1655 pixels
1656 }
1657 RawImageFormat::RG8 => {
1658 let pixels = pixels.get_u8_vec()?;
1659
1660 if pixels.len() != expected_len * TWO_CHANNELS {
1661 return None;
1662 }
1663
1664 let mut px = vec![0; expected_len * FOUR_BPP];
1665
1666 for (pixel_index, greyalpha) in
1668 pixels.as_ref().chunks_exact(TWO_CHANNELS).enumerate()
1669 {
1670 let grey = greyalpha[0];
1671 let alpha = greyalpha[1];
1672
1673 if alpha != 255 {
1674 is_opaque = false;
1675 }
1676
1677 px[pixel_index * FOUR_BPP] = grey;
1678 px[(pixel_index * FOUR_BPP) + 1] = grey;
1679 px[(pixel_index * FOUR_BPP) + 2] = grey;
1680 px[(pixel_index * FOUR_BPP) + 3] = alpha;
1681
1682 if !premultiplied_alpha {
1683 premultiply_alpha(
1684 &mut px[(pixel_index * FOUR_BPP)..((pixel_index * FOUR_BPP) + FOUR_BPP)],
1685 );
1686 }
1687 }
1688
1689 data_format = RawImageFormat::BGRA8;
1690 px.into()
1691 }
1692 RawImageFormat::RGB8 => {
1693 let pixels = pixels.get_u8_vec()?;
1694
1695 if pixels.len() != expected_len * THREE_CHANNELS {
1696 return None;
1697 }
1698
1699 let mut px = vec![0; expected_len * FOUR_BPP];
1700
1701 for (pixel_index, rgb) in pixels.as_ref().chunks_exact(THREE_CHANNELS).enumerate() {
1703 let red = rgb[0];
1704 let green = rgb[1];
1705 let blue = rgb[2];
1706
1707 px[pixel_index * FOUR_BPP] = blue;
1708 px[(pixel_index * FOUR_BPP) + 1] = green;
1709 px[(pixel_index * FOUR_BPP) + 2] = red;
1710 px[(pixel_index * FOUR_BPP) + 3] = 0xff;
1711 }
1712
1713 data_format = RawImageFormat::BGRA8;
1714 px.into()
1715 }
1716 RawImageFormat::RGBA8 => {
1717 let mut pixels: Vec<u8> = pixels.get_u8_vec()?.into_library_owned_vec();
1718
1719 if pixels.len() != expected_len * FOUR_CHANNELS {
1720 return None;
1721 }
1722
1723 if premultiplied_alpha {
1726 for rgba in pixels.chunks_exact_mut(4) {
1727 let (r, gba) = rgba.split_first_mut()?;
1728 core::mem::swap(r, gba.get_mut(1)?);
1729 let a = rgba.get_mut(3)?;
1730 if *a != 255 {
1731 is_opaque = false;
1732 }
1733 }
1734 } else {
1735 for rgba in pixels.chunks_exact_mut(4) {
1736 let (r, gba) = rgba.split_first_mut()?;
1738 core::mem::swap(r, gba.get_mut(1)?);
1739 let a = rgba.get_mut(3)?;
1740 if *a != 255 {
1741 is_opaque = false;
1742 }
1743 premultiply_alpha(rgba); }
1745 }
1746
1747 data_format = RawImageFormat::BGRA8;
1748 pixels.into()
1749 }
1750 RawImageFormat::R16 => {
1751 let pixels = pixels.get_u16_vec()?;
1752
1753 if pixels.len() != expected_len {
1754 return None;
1755 }
1756
1757 let mut px = vec![0; expected_len * FOUR_BPP];
1758
1759 for (pixel_index, grey_u16) in pixels.as_ref().iter().enumerate() {
1761 let grey_u8 = normalize_u16(*grey_u16);
1762 px[pixel_index * FOUR_BPP] = grey_u8;
1763 px[(pixel_index * FOUR_BPP) + 1] = grey_u8;
1764 px[(pixel_index * FOUR_BPP) + 2] = grey_u8;
1765 px[(pixel_index * FOUR_BPP) + 3] = 0xff;
1766 }
1767
1768 data_format = RawImageFormat::BGRA8;
1769 px.into()
1770 }
1771 RawImageFormat::RG16 => {
1772 let pixels = pixels.get_u16_vec()?;
1773
1774 if pixels.len() != expected_len * TWO_CHANNELS {
1775 return None;
1776 }
1777
1778 let mut px = vec![0; expected_len * FOUR_BPP];
1779
1780 for (pixel_index, greyalpha) in
1782 pixels.as_ref().chunks_exact(TWO_CHANNELS).enumerate()
1783 {
1784 let grey_u8 = normalize_u16(greyalpha[0]);
1785 let alpha_u8 = normalize_u16(greyalpha[1]);
1786
1787 if alpha_u8 != 255 {
1788 is_opaque = false;
1789 }
1790
1791 px[pixel_index * FOUR_BPP] = grey_u8;
1792 px[(pixel_index * FOUR_BPP) + 1] = grey_u8;
1793 px[(pixel_index * FOUR_BPP) + 2] = grey_u8;
1794 px[(pixel_index * FOUR_BPP) + 3] = alpha_u8;
1795 }
1796
1797 data_format = RawImageFormat::BGRA8;
1798 px.into()
1799 }
1800 RawImageFormat::RGB16 => {
1801 let pixels = pixels.get_u16_vec()?;
1802
1803 if pixels.len() != expected_len * THREE_CHANNELS {
1804 return None;
1805 }
1806
1807 let mut px = vec![0; expected_len * FOUR_BPP];
1808
1809 for (pixel_index, rgb) in pixels.as_ref().chunks_exact(THREE_CHANNELS).enumerate() {
1811 let red_u8 = normalize_u16(rgb[0]);
1812 let green_u8 = normalize_u16(rgb[1]);
1813 let blue_u8 = normalize_u16(rgb[2]);
1814
1815 px[pixel_index * FOUR_BPP] = blue_u8;
1816 px[(pixel_index * FOUR_BPP) + 1] = green_u8;
1817 px[(pixel_index * FOUR_BPP) + 2] = red_u8;
1818 px[(pixel_index * FOUR_BPP) + 3] = 0xff;
1819 }
1820
1821 data_format = RawImageFormat::BGRA8;
1822 px.into()
1823 }
1824 RawImageFormat::RGBA16 => {
1825 let pixels = pixels.get_u16_vec()?;
1826
1827 if pixels.len() != expected_len * FOUR_CHANNELS {
1828 return None;
1829 }
1830
1831 let mut px = vec![0; expected_len * FOUR_BPP];
1832
1833 if premultiplied_alpha {
1835 for (pixel_index, rgba) in
1836 pixels.as_ref().chunks_exact(FOUR_CHANNELS).enumerate()
1837 {
1838 let red_u8 = normalize_u16(rgba[0]);
1839 let green_u8 = normalize_u16(rgba[1]);
1840 let blue_u8 = normalize_u16(rgba[2]);
1841 let alpha_u8 = normalize_u16(rgba[3]);
1842
1843 if alpha_u8 != 255 {
1844 is_opaque = false;
1845 }
1846
1847 px[pixel_index * FOUR_BPP] = blue_u8;
1848 px[(pixel_index * FOUR_BPP) + 1] = green_u8;
1849 px[(pixel_index * FOUR_BPP) + 2] = red_u8;
1850 px[(pixel_index * FOUR_BPP) + 3] = alpha_u8;
1851 }
1852 } else {
1853 for (pixel_index, rgba) in
1854 pixels.as_ref().chunks_exact(FOUR_CHANNELS).enumerate()
1855 {
1856 let red_u8 = normalize_u16(rgba[0]);
1857 let green_u8 = normalize_u16(rgba[1]);
1858 let blue_u8 = normalize_u16(rgba[2]);
1859 let alpha_u8 = normalize_u16(rgba[3]);
1860
1861 if alpha_u8 != 255 {
1862 is_opaque = false;
1863 }
1864
1865 px[pixel_index * FOUR_BPP] = blue_u8;
1866 px[(pixel_index * FOUR_BPP) + 1] = green_u8;
1867 px[(pixel_index * FOUR_BPP) + 2] = red_u8;
1868 px[(pixel_index * FOUR_BPP) + 3] = alpha_u8;
1869 premultiply_alpha(
1870 &mut px
1871 [(pixel_index * FOUR_BPP)..((pixel_index * FOUR_BPP) + FOUR_BPP)],
1872 );
1873 }
1874 }
1875
1876 data_format = RawImageFormat::BGRA8;
1877 px.into()
1878 }
1879 RawImageFormat::BGR8 => {
1880 let pixels = pixels.get_u8_vec()?;
1881
1882 if pixels.len() != expected_len * THREE_CHANNELS {
1883 return None;
1884 }
1885
1886 let mut px = vec![0; expected_len * FOUR_BPP];
1887
1888 for (pixel_index, bgr) in pixels.as_ref().chunks_exact(THREE_CHANNELS).enumerate() {
1890 let blue = bgr[0];
1891 let green = bgr[1];
1892 let red = bgr[2];
1893
1894 px[pixel_index * FOUR_BPP] = blue;
1895 px[(pixel_index * FOUR_BPP) + 1] = green;
1896 px[(pixel_index * FOUR_BPP) + 2] = red;
1897 px[(pixel_index * FOUR_BPP) + 3] = 0xff;
1898 }
1899
1900 data_format = RawImageFormat::BGRA8;
1901 px.into()
1902 }
1903 RawImageFormat::BGRA8 => {
1904 if premultiplied_alpha {
1905 let pixels = pixels.get_u8_vec()?;
1907
1908 is_opaque = pixels
1909 .as_ref()
1910 .chunks_exact(FOUR_CHANNELS)
1911 .all(|bgra| bgra[3] == 255);
1912
1913 pixels
1914 } else {
1915 let mut pixels: Vec<u8> = pixels.get_u8_vec()?.into_library_owned_vec();
1916
1917 if pixels.len() != expected_len * FOUR_BPP {
1918 return None;
1919 }
1920
1921 for bgra in pixels.chunks_exact_mut(FOUR_CHANNELS) {
1922 if bgra[3] != 255 {
1923 is_opaque = false;
1924 }
1925 premultiply_alpha(bgra);
1926 }
1927 data_format = RawImageFormat::BGRA8;
1928 pixels.into()
1929 }
1930 }
1931 RawImageFormat::RGBF32 => {
1932 let pixels = pixels.get_f32_vec_ref()?;
1933
1934 if pixels.len() != expected_len * THREE_CHANNELS {
1935 return None;
1936 }
1937
1938 let mut px = vec![0; expected_len * FOUR_BPP];
1939
1940 for (pixel_index, rgb) in pixels.as_ref().chunks_exact(THREE_CHANNELS).enumerate() {
1942 let red_u8 = (rgb[0] * 255.0) as u8;
1943 let green_u8 = (rgb[1] * 255.0) as u8;
1944 let blue_u8 = (rgb[2] * 255.0) as u8;
1945
1946 px[pixel_index * FOUR_BPP] = blue_u8;
1947 px[(pixel_index * FOUR_BPP) + 1] = green_u8;
1948 px[(pixel_index * FOUR_BPP) + 2] = red_u8;
1949 px[(pixel_index * FOUR_BPP) + 3] = 0xff;
1950 }
1951
1952 data_format = RawImageFormat::BGRA8;
1953 px.into()
1954 }
1955 RawImageFormat::RGBAF32 => {
1956 let pixels = pixels.get_f32_vec_ref()?;
1957
1958 if pixels.len() != expected_len * FOUR_CHANNELS {
1959 return None;
1960 }
1961
1962 let mut px = vec![0; expected_len * FOUR_BPP];
1963
1964 if premultiplied_alpha {
1966 for (pixel_index, rgba) in
1967 pixels.as_ref().chunks_exact(FOUR_CHANNELS).enumerate()
1968 {
1969 let red_u8 = (rgba[0] * 255.0) as u8;
1970 let green_u8 = (rgba[1] * 255.0) as u8;
1971 let blue_u8 = (rgba[2] * 255.0) as u8;
1972 let alpha_u8 = (rgba[3] * 255.0) as u8;
1973
1974 if alpha_u8 != 255 {
1975 is_opaque = false;
1976 }
1977
1978 px[pixel_index * FOUR_BPP] = blue_u8;
1979 px[(pixel_index * FOUR_BPP) + 1] = green_u8;
1980 px[(pixel_index * FOUR_BPP) + 2] = red_u8;
1981 px[(pixel_index * FOUR_BPP) + 3] = alpha_u8;
1982 }
1983 } else {
1984 for (pixel_index, rgba) in
1985 pixels.as_ref().chunks_exact(FOUR_CHANNELS).enumerate()
1986 {
1987 let red_u8 = (rgba[0] * 255.0) as u8;
1988 let green_u8 = (rgba[1] * 255.0) as u8;
1989 let blue_u8 = (rgba[2] * 255.0) as u8;
1990 let alpha_u8 = (rgba[3] * 255.0) as u8;
1991
1992 if alpha_u8 != 255 {
1993 is_opaque = false;
1994 }
1995
1996 px[pixel_index * FOUR_BPP] = blue_u8;
1997 px[(pixel_index * FOUR_BPP) + 1] = green_u8;
1998 px[(pixel_index * FOUR_BPP) + 2] = red_u8;
1999 px[(pixel_index * FOUR_BPP) + 3] = alpha_u8;
2000 premultiply_alpha(
2001 &mut px
2002 [(pixel_index * FOUR_BPP)..((pixel_index * FOUR_BPP) + FOUR_BPP)],
2003 );
2004 }
2005 }
2006
2007 data_format = RawImageFormat::BGRA8;
2008 px.into()
2009 }
2010 };
2011
2012 let image_data = ImageData::Raw(SharedRawImageData::new(bytes));
2013 let image_descriptor = ImageDescriptor {
2014 format: data_format,
2015 width,
2016 height,
2017 offset: 0,
2018 stride: None.into(),
2019 flags: ImageDescriptorFlags {
2020 is_opaque,
2021 allow_mipmaps: true,
2022 },
2023 };
2024
2025 Some((image_data, image_descriptor))
2026 }
2027}
2028
2029impl_option!(
2030 RawImage,
2031 OptionRawImage,
2032 copy = false,
2033 [Debug, Clone, PartialEq, PartialOrd]
2034);
2035
2036pub fn font_size_to_au(font_size: StyleFontSize) -> Au {
2037 Au::from_px(font_size.inner.to_pixels_internal(0.0, DEFAULT_FONT_SIZE, DEFAULT_FONT_SIZE))
2038}
2039
2040pub type FontInstanceFlags = u32;
2041
2042pub const FONT_INSTANCE_FLAG_SYNTHETIC_BOLD: u32 = 1 << 1;
2044pub const FONT_INSTANCE_FLAG_EMBEDDED_BITMAPS: u32 = 1 << 2;
2045pub const FONT_INSTANCE_FLAG_SUBPIXEL_BGR: u32 = 1 << 3;
2046pub const FONT_INSTANCE_FLAG_TRANSPOSE: u32 = 1 << 4;
2047pub const FONT_INSTANCE_FLAG_FLIP_X: u32 = 1 << 5;
2048pub const FONT_INSTANCE_FLAG_FLIP_Y: u32 = 1 << 6;
2049pub const FONT_INSTANCE_FLAG_SUBPIXEL_POSITION: u32 = 1 << 7;
2050
2051pub const FONT_INSTANCE_FLAG_FORCE_GDI: u32 = 1 << 16;
2053
2054pub const FONT_INSTANCE_FLAG_FONT_SMOOTHING: u32 = 1 << 16;
2056
2057pub const FONT_INSTANCE_FLAG_FORCE_AUTOHINT: u32 = 1 << 16;
2059pub const FONT_INSTANCE_FLAG_NO_AUTOHINT: u32 = 1 << 17;
2060pub const FONT_INSTANCE_FLAG_VERTICAL_LAYOUT: u32 = 1 << 18;
2061pub const FONT_INSTANCE_FLAG_LCD_VERTICAL: u32 = 1 << 19;
2062
2063#[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash)]
2064pub struct GlyphOptions {
2065 pub render_mode: FontRenderMode,
2066 pub flags: FontInstanceFlags,
2067}
2068
2069#[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash)]
2070pub enum FontRenderMode {
2071 Mono,
2072 Alpha,
2073 Subpixel,
2074}
2075
2076#[cfg(target_arch = "wasm32")]
2077#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash)]
2078pub struct FontInstancePlatformOptions {
2079 }
2081
2082#[cfg(target_os = "windows")]
2083#[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash)]
2084pub struct FontInstancePlatformOptions {
2085 pub gamma: u16,
2086 pub contrast: u8,
2087 pub cleartype_level: u8,
2088}
2089
2090#[cfg(target_os = "macos")]
2091#[derive(Default, Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash)]
2092pub struct FontInstancePlatformOptions {
2093 pub unused: u32,
2094}
2095
2096#[cfg(target_os = "linux")]
2097#[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash)]
2098pub struct FontInstancePlatformOptions {
2099 pub lcd_filter: FontLCDFilter,
2100 pub hinting: FontHinting,
2101}
2102
2103#[cfg(any(target_os = "android", target_os = "ios"))]
2107#[derive(Default, Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash)]
2108pub struct FontInstancePlatformOptions {
2109 pub unused: u32,
2110}
2111
2112#[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash)]
2113pub enum FontHinting {
2114 None,
2115 Mono,
2116 Light,
2117 Normal,
2118 LCD,
2119}
2120
2121#[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash)]
2122pub enum FontLCDFilter {
2123 None,
2124 Default,
2125 Light,
2126 Legacy,
2127}
2128
2129impl Default for FontLCDFilter {
2130 fn default() -> Self {
2131 FontLCDFilter::Default
2132 }
2133}
2134
2135#[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash)]
2136pub struct FontInstanceOptions {
2137 pub render_mode: FontRenderMode,
2138 pub flags: FontInstanceFlags,
2139 pub bg_color: ColorU,
2140 pub synthetic_italics: SyntheticItalics,
2144}
2145
2146impl Default for FontInstanceOptions {
2147 fn default() -> FontInstanceOptions {
2148 FontInstanceOptions {
2149 render_mode: FontRenderMode::Subpixel,
2150 flags: 0,
2151 bg_color: ColorU::TRANSPARENT,
2152 synthetic_italics: SyntheticItalics::default(),
2153 }
2154 }
2155}
2156
2157#[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash)]
2158pub struct SyntheticItalics {
2159 pub angle: i16,
2160}
2161
2162impl Default for SyntheticItalics {
2163 fn default() -> Self {
2164 Self { angle: 0 }
2165 }
2166}
2167
2168#[derive(Debug)]
2174#[repr(C)]
2175pub struct SharedRawImageData {
2176 pub data: *const U8Vec,
2178 pub copies: *const AtomicUsize,
2180 pub run_destructor: bool,
2182}
2183
2184impl SharedRawImageData {
2185 pub fn new(data: U8Vec) -> Self {
2187 Self {
2188 data: Box::into_raw(Box::new(data)),
2189 copies: Box::into_raw(Box::new(AtomicUsize::new(1))),
2190 run_destructor: true,
2191 }
2192 }
2193
2194 pub fn as_ref(&self) -> &[u8] {
2196 unsafe { (*self.data).as_ref() }
2197 }
2198
2199 pub fn get_bytes(&self) -> &[u8] {
2201 self.as_ref()
2202 }
2203
2204 pub fn as_ptr(&self) -> *const u8 {
2206 unsafe { (*self.data).as_ref().as_ptr() }
2207 }
2208
2209 pub fn len(&self) -> usize {
2211 unsafe { (*self.data).len() }
2212 }
2213
2214 pub fn is_empty(&self) -> bool {
2216 self.len() == 0
2217 }
2218
2219 pub fn into_inner(self) -> Option<U8Vec> {
2222 unsafe {
2223 if self.copies.as_ref().map(|m| m.load(AtomicOrdering::SeqCst)) == Some(1) {
2224 let data = Box::from_raw(self.data as *mut U8Vec);
2225 let _ = Box::from_raw(self.copies as *mut AtomicUsize);
2226 core::mem::forget(self); Some(*data)
2228 } else {
2229 None
2230 }
2231 }
2232 }
2233}
2234
2235unsafe impl Send for SharedRawImageData {}
2236unsafe impl Sync for SharedRawImageData {}
2237
2238impl Clone for SharedRawImageData {
2239 fn clone(&self) -> Self {
2240 unsafe {
2241 self.copies
2242 .as_ref()
2243 .map(|m| m.fetch_add(1, AtomicOrdering::SeqCst));
2244 }
2245 Self {
2246 data: self.data,
2247 copies: self.copies,
2248 run_destructor: true,
2249 }
2250 }
2251}
2252
2253impl Drop for SharedRawImageData {
2254 fn drop(&mut self) {
2255 self.run_destructor = false;
2256 unsafe {
2257 let copies = (*self.copies).fetch_sub(1, AtomicOrdering::SeqCst);
2258 if copies == 1 {
2259 let _ = Box::from_raw(self.data as *mut U8Vec);
2260 let _ = Box::from_raw(self.copies as *mut AtomicUsize);
2261 }
2262 }
2263 }
2264}
2265
2266impl PartialEq for SharedRawImageData {
2267 fn eq(&self, rhs: &Self) -> bool {
2268 self.data as usize == rhs.data as usize
2269 }
2270}
2271
2272impl Eq for SharedRawImageData {}
2273
2274impl PartialOrd for SharedRawImageData {
2275 fn partial_cmp(&self, other: &Self) -> Option<::core::cmp::Ordering> {
2276 Some(self.cmp(other))
2277 }
2278}
2279
2280impl Ord for SharedRawImageData {
2281 fn cmp(&self, other: &Self) -> core::cmp::Ordering {
2282 (self.data as usize).cmp(&(other.data as usize))
2283 }
2284}
2285
2286impl Hash for SharedRawImageData {
2287 fn hash<H>(&self, state: &mut H)
2288 where
2289 H: Hasher,
2290 {
2291 (self.data as usize).hash(state)
2292 }
2293}
2294
2295#[derive(Debug, Clone, PartialEq, Eq, Ord, PartialOrd, Hash)]
2298#[repr(C, u8)]
2299pub enum ImageData {
2300 Raw(SharedRawImageData),
2303 External(ExternalImageData),
2306}
2307
2308#[derive(Debug, Copy, Clone, Eq, Hash, PartialEq, PartialOrd, Ord)]
2310#[repr(C, u8)]
2311pub enum ExternalImageType {
2312 TextureHandle(ImageBufferKind),
2314 Buffer,
2316}
2317
2318#[repr(C)]
2322#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, PartialOrd, Ord)]
2323pub struct ExternalImageId {
2324 pub inner: u64,
2325}
2326
2327static LAST_EXTERNAL_IMAGE_ID: AtomicUsize = AtomicUsize::new(0);
2328
2329impl ExternalImageId {
2330 pub fn new() -> Self {
2332 Self {
2333 inner: LAST_EXTERNAL_IMAGE_ID.fetch_add(1, AtomicOrdering::SeqCst) as u64,
2334 }
2335 }
2336}
2337
2338#[derive(Debug, Clone, PartialEq, PartialOrd)]
2339#[repr(C, u8)]
2340pub enum GlyphOutlineOperation {
2341 MoveTo(OutlineMoveTo),
2342 LineTo(OutlineLineTo),
2343 QuadraticCurveTo(OutlineQuadTo),
2344 CubicCurveTo(OutlineCubicTo),
2345 ClosePath,
2346}
2347
2348impl_option!(
2349 GlyphOutlineOperation,
2350 OptionGlyphOutlineOperation,
2351 copy = false,
2352 [Debug, Clone, PartialEq, PartialOrd]
2353);
2354
2355#[derive(Debug, Clone, PartialEq, PartialOrd)]
2357#[repr(C)]
2358pub struct OutlineMoveTo {
2359 pub x: i16,
2360 pub y: i16,
2361}
2362
2363#[derive(Debug, Clone, PartialEq, PartialOrd)]
2365#[repr(C)]
2366pub struct OutlineLineTo {
2367 pub x: i16,
2368 pub y: i16,
2369}
2370
2371#[derive(Debug, Clone, PartialEq, PartialOrd)]
2373#[repr(C)]
2374pub struct OutlineQuadTo {
2375 pub ctrl_1_x: i16,
2376 pub ctrl_1_y: i16,
2377 pub end_x: i16,
2378 pub end_y: i16,
2379}
2380
2381#[derive(Debug, Clone, PartialEq, PartialOrd)]
2383#[repr(C)]
2384pub struct OutlineCubicTo {
2385 pub ctrl_1_x: i16,
2386 pub ctrl_1_y: i16,
2387 pub ctrl_2_x: i16,
2388 pub ctrl_2_y: i16,
2389 pub end_x: i16,
2390 pub end_y: i16,
2391}
2392
2393#[derive(Debug, Clone, PartialEq, PartialOrd)]
2394#[repr(C)]
2395pub struct GlyphOutline {
2396 pub operations: GlyphOutlineOperationVec,
2397}
2398
2399azul_css::impl_vec!(GlyphOutlineOperation, GlyphOutlineOperationVec, GlyphOutlineOperationVecDestructor, GlyphOutlineOperationVecDestructorType, GlyphOutlineOperationVecSlice, OptionGlyphOutlineOperation);
2400azul_css::impl_vec_clone!(
2401 GlyphOutlineOperation,
2402 GlyphOutlineOperationVec,
2403 GlyphOutlineOperationVecDestructor
2404);
2405azul_css::impl_vec_debug!(GlyphOutlineOperation, GlyphOutlineOperationVec);
2406azul_css::impl_vec_partialord!(GlyphOutlineOperation, GlyphOutlineOperationVec);
2407azul_css::impl_vec_partialeq!(GlyphOutlineOperation, GlyphOutlineOperationVec);
2408
2409#[derive(Debug, Clone)]
2410#[repr(C)]
2411pub struct OwnedGlyphBoundingBox {
2412 pub max_x: i16,
2413 pub max_y: i16,
2414 pub min_x: i16,
2415 pub min_y: i16,
2416}
2417
2418#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
2420#[repr(C)]
2421pub enum ImageBufferKind {
2422 Texture2D = 0,
2424 TextureRect = 1,
2431 TextureExternal = 2,
2436}
2437
2438#[repr(C)]
2440#[derive(Debug, Clone, Eq, Hash, PartialEq, PartialOrd, Ord)]
2441pub struct ExternalImageData {
2442 pub id: ExternalImageId,
2444 pub channel_index: u8,
2447 pub image_type: ExternalImageType,
2449}
2450
2451pub type TileSize = u16;
2452
2453#[derive(Debug, Copy, Clone, PartialEq, PartialOrd)]
2454pub enum ImageDirtyRect {
2455 All,
2456 Partial(LayoutRect),
2457}
2458
2459#[derive(Debug, Clone, PartialEq, PartialOrd)]
2460pub enum ResourceUpdate {
2461 AddFont(AddFont),
2462 DeleteFont(FontKey),
2463 AddFontInstance(AddFontInstance),
2464 DeleteFontInstance(FontInstanceKey),
2465 AddImage(AddImage),
2466 UpdateImage(UpdateImage),
2467 DeleteImage(ImageKey),
2468}
2469
2470#[derive(Debug, Clone, PartialEq, Eq, Ord, PartialOrd, Hash)]
2471pub struct AddImage {
2472 pub key: ImageKey,
2473 pub descriptor: ImageDescriptor,
2474 pub data: ImageData,
2475 pub tiling: Option<TileSize>,
2476}
2477
2478#[derive(Debug, Clone, PartialEq, PartialOrd)]
2479pub struct UpdateImage {
2480 pub key: ImageKey,
2481 pub descriptor: ImageDescriptor,
2482 pub data: ImageData,
2483 pub dirty_rect: ImageDirtyRect,
2484}
2485
2486#[derive(Clone, PartialEq, Eq, Ord, PartialOrd, Hash)]
2489pub struct AddFont {
2490 pub key: FontKey,
2491 pub font: azul_css::props::basic::FontRef,
2492}
2493
2494impl fmt::Debug for AddFont {
2495 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
2496 write!(
2497 f,
2498 "AddFont {{ key: {:?}, font: {:?} }}",
2499 self.key, self.font
2500 )
2501 }
2502}
2503
2504#[derive(Debug, Clone, PartialEq, PartialOrd)]
2505pub struct AddFontInstance {
2506 pub key: FontInstanceKey,
2507 pub font_key: FontKey,
2508 pub glyph_size: (Au, DpiScaleFactor),
2509 pub options: Option<FontInstanceOptions>,
2510 pub platform_options: Option<FontInstancePlatformOptions>,
2511 pub variations: Vec<FontVariation>,
2512}
2513
2514#[repr(C)]
2515#[derive(Clone, Copy, Debug, PartialOrd, PartialEq)]
2516pub struct FontVariation {
2517 pub tag: u32,
2518 pub value: f32,
2519}
2520
2521#[repr(C)]
2522#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
2523pub struct Epoch {
2524 inner: u32,
2525}
2526
2527impl fmt::Display for Epoch {
2528 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
2529 write!(f, "{}", self.inner)
2530 }
2531}
2532
2533impl Epoch {
2534 pub const fn new() -> Self {
2538 Self { inner: 0 }
2539 }
2540 pub const fn from(i: u32) -> Self {
2541 Self { inner: i }
2542 }
2543 pub const fn into_u32(&self) -> u32 {
2544 self.inner
2545 }
2546
2547 pub fn increment(&mut self) {
2550 use core::u32;
2551 const MAX_ID: u32 = u32::MAX - 1;
2552 *self = match self.inner {
2553 MAX_ID => Epoch { inner: 0 },
2554 other => Epoch {
2555 inner: other.saturating_add(1),
2556 },
2557 };
2558 }
2559}
2560
2561#[derive(Debug, Clone, Copy, Hash, PartialEq, PartialOrd, Eq, Ord)]
2563pub struct Au(pub i32);
2564
2565pub const AU_PER_PX: i32 = 60;
2566pub const MAX_AU: i32 = (1 << 30) - 1;
2567pub const MIN_AU: i32 = -(1 << 30) - 1;
2568
2569impl Au {
2570 pub fn from_px(px: f32) -> Self {
2571 let target_app_units = (px * AU_PER_PX as f32) as i32;
2572 Au(target_app_units.min(MAX_AU).max(MIN_AU))
2573 }
2574 pub fn into_px(&self) -> f32 {
2575 self.0 as f32 / AU_PER_PX as f32
2576 }
2577}
2578
2579#[derive(Debug)]
2581pub enum AddFontMsg {
2582 Font(FontKey, StyleFontFamilyHash, FontRef),
2584 Instance(AddFontInstance, (Au, DpiScaleFactor)),
2585}
2586
2587impl AddFontMsg {
2588 pub fn into_resource_update(&self) -> ResourceUpdate {
2589 use self::AddFontMsg::*;
2590 match self {
2591 Font(font_key, _, font_ref) => ResourceUpdate::AddFont(AddFont {
2592 key: *font_key,
2593 font: font_ref.clone(),
2594 }),
2595 Instance(fi, _) => ResourceUpdate::AddFontInstance(fi.clone()),
2596 }
2597 }
2598}
2599
2600#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
2601pub enum DeleteFontMsg {
2602 Font(FontKey),
2603 Instance(FontInstanceKey, (Au, DpiScaleFactor)),
2604}
2605
2606impl DeleteFontMsg {
2607 pub fn into_resource_update(&self) -> ResourceUpdate {
2608 use self::DeleteFontMsg::*;
2609 match self {
2610 Font(f) => ResourceUpdate::DeleteFont(*f),
2611 Instance(fi, _) => ResourceUpdate::DeleteFontInstance(*fi),
2612 }
2613 }
2614}
2615
2616#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
2617pub struct AddImageMsg(pub AddImage);
2618
2619impl AddImageMsg {
2620 pub fn into_resource_update(&self) -> ResourceUpdate {
2621 ResourceUpdate::AddImage(self.0.clone())
2622 }
2623}
2624
2625#[derive(Debug, Clone, PartialEq, Eq, Hash)]
2626#[repr(C)]
2627pub struct LoadedFontSource {
2628 pub data: U8Vec,
2629 pub index: u32,
2630 pub load_outlines: bool,
2631}
2632
2633pub type LoadFontFn = fn(&StyleFontFamily, &FcFontCache) -> Option<LoadedFontSource>;
2635
2636pub type ParseFontFn = fn(LoadedFontSource) -> Option<FontRef>; pub type GlStoreImageFn = fn(DocumentId, Epoch, Texture, ExternalImageId);
2640
2641pub fn texture_external_image_id(dom_id: DomId, node_id: NodeId) -> ExternalImageId {
2646 let dom = dom_id.inner as u64;
2647 let node = node_id.index() as u64;
2648 debug_assert!(dom <= u32::MAX as u64, "DomId exceeds 32-bit range");
2649 debug_assert!(node <= u32::MAX as u64, "NodeId exceeds 32-bit range");
2650 ExternalImageId {
2651 inner: (dom << 32) | (node & 0xFFFFFFFF),
2652 }
2653}
2654
2655pub fn image_ref_hash_to_external_image_id(hash: ImageRefHash) -> ExternalImageId {
2659 ExternalImageId {
2660 inner: hash.inner as u64,
2661 }
2662}
2663
2664pub fn build_add_font_resource_updates(
2673 renderer_resources: &mut RendererResources,
2674 dpi: DpiScaleFactor,
2675 fc_cache: &FcFontCache,
2676 id_namespace: IdNamespace,
2677 fonts_in_dom: &OrderedMap<ImmediateFontId, FastBTreeSet<Au>>,
2678 font_source_load_fn: LoadFontFn,
2679 parse_font_fn: ParseFontFn,
2680) -> Vec<(StyleFontFamilyHash, AddFontMsg)> {
2681 let mut resource_updates = alloc::vec::Vec::new();
2682 let mut font_instances_added_this_frame = FastBTreeSet::new();
2683
2684 'outer: for (im_font_id, font_sizes) in fonts_in_dom {
2685 macro_rules! insert_font_instances {
2686 ($font_family_hash:expr, $font_key:expr, $font_size:expr) => {{
2687 let font_instance_key_exists = renderer_resources
2688 .currently_registered_fonts
2689 .get(&$font_key)
2690 .and_then(|(_, font_instances)| font_instances.get(&($font_size, dpi)))
2691 .is_some()
2692 || font_instances_added_this_frame.contains(&($font_key, ($font_size, dpi)));
2693
2694 if !font_instance_key_exists {
2695 let font_instance_key = FontInstanceKey::unique(id_namespace);
2696
2697 #[cfg(target_os = "windows")]
2699 let platform_options = FontInstancePlatformOptions {
2700 gamma: 300,
2701 contrast: 100,
2702 cleartype_level: 100,
2703 };
2704
2705 #[cfg(target_os = "linux")]
2706 let platform_options = FontInstancePlatformOptions {
2707 lcd_filter: FontLCDFilter::Default,
2708 hinting: FontHinting::Normal,
2709 };
2710
2711 #[cfg(target_os = "macos")]
2712 let platform_options = FontInstancePlatformOptions::default();
2713
2714 #[cfg(target_arch = "wasm32")]
2715 let platform_options = FontInstancePlatformOptions::default();
2716
2717 #[cfg(any(target_os = "android", target_os = "ios"))]
2718 let platform_options = FontInstancePlatformOptions::default();
2719
2720 let options = FontInstanceOptions {
2721 render_mode: FontRenderMode::Subpixel,
2722 flags: FONT_INSTANCE_FLAG_NO_AUTOHINT,
2723 ..Default::default()
2724 };
2725
2726 font_instances_added_this_frame.insert(($font_key, ($font_size, dpi)));
2727 resource_updates.push((
2728 $font_family_hash,
2729 AddFontMsg::Instance(
2730 AddFontInstance {
2731 key: font_instance_key,
2732 font_key: $font_key,
2733 glyph_size: ($font_size, dpi),
2734 options: Some(options),
2735 platform_options: Some(platform_options),
2736 variations: alloc::vec::Vec::new(),
2737 },
2738 ($font_size, dpi),
2739 ),
2740 ));
2741 }
2742 }};
2743 }
2744
2745 match im_font_id {
2746 ImmediateFontId::Resolved((font_family_hash, font_id)) => {
2747 for font_size in font_sizes.iter() {
2750 insert_font_instances!(*font_family_hash, *font_id, *font_size);
2751 }
2752 }
2753 ImmediateFontId::Unresolved(style_font_families) => {
2754 let mut font_family_hash = None;
2764 let font_families_hash = StyleFontFamiliesHash::new(style_font_families.as_ref());
2765
2766 'inner: for family in style_font_families.as_ref().iter() {
2768 let current_family_hash = StyleFontFamilyHash::new(&family);
2769
2770 if let Some(font_id) = renderer_resources.font_id_map.get(¤t_family_hash)
2771 {
2772 for font_size in font_sizes {
2774 insert_font_instances!(current_family_hash, *font_id, *font_size);
2775 }
2776 continue 'outer;
2777 }
2778
2779 let font_ref = match family {
2780 StyleFontFamily::Ref(r) => r.clone(), other => {
2782 let font_data = match (font_source_load_fn)(&other, fc_cache) {
2784 Some(s) => s,
2785 None => continue 'inner,
2786 };
2787
2788 let font_ref = match (parse_font_fn)(font_data) {
2789 Some(s) => s,
2790 None => continue 'inner,
2791 };
2792
2793 font_ref
2794 }
2795 };
2796
2797 font_family_hash = Some((current_family_hash, font_ref));
2799 break 'inner;
2800 }
2801
2802 let (font_family_hash, font_ref) = match font_family_hash {
2803 None => continue 'outer, Some(s) => s,
2805 };
2806
2807 let font_key = FontKey::unique(id_namespace);
2809 let add_font_msg = AddFontMsg::Font(font_key, font_family_hash, font_ref);
2810
2811 renderer_resources
2812 .font_id_map
2813 .insert(font_family_hash, font_key);
2814 renderer_resources
2815 .font_families_map
2816 .insert(font_families_hash, font_family_hash);
2817 resource_updates.push((font_family_hash, add_font_msg));
2818
2819 for font_size in font_sizes {
2821 insert_font_instances!(font_family_hash, font_key, *font_size);
2822 }
2823 }
2824 }
2825 }
2826
2827 resource_updates
2828}
2829
2830#[allow(unused_variables)]
2846pub fn build_add_image_resource_updates(
2847 renderer_resources: &RendererResources,
2848 id_namespace: IdNamespace,
2849 epoch: Epoch,
2850 document_id: &DocumentId,
2851 images_in_dom: &FastBTreeSet<ImageRef>,
2852 insert_into_active_gl_textures: GlStoreImageFn,
2853) -> Vec<(ImageRefHash, AddImageMsg)> {
2854 images_in_dom
2855 .iter()
2856 .filter_map(|image_ref| {
2857 let image_ref_hash = image_ref_get_hash(&image_ref);
2858
2859 if renderer_resources
2860 .currently_registered_images
2861 .contains_key(&image_ref_hash)
2862 {
2863 return None;
2864 }
2865
2866 match image_ref.get_data() {
2869 DecodedImage::Gl(texture) => {
2870 let descriptor = texture.get_descriptor();
2871 let key = image_ref_hash_to_image_key(image_ref_hash, id_namespace);
2872 let external_image_id = image_ref_hash_to_external_image_id(image_ref_hash);
2876 (insert_into_active_gl_textures)(
2878 *document_id,
2879 epoch,
2880 texture.clone(),
2881 external_image_id,
2882 );
2883 Some((
2884 image_ref_hash,
2885 AddImageMsg(AddImage {
2886 key,
2887 data: ImageData::External(ExternalImageData {
2888 id: external_image_id,
2889 channel_index: 0,
2890 image_type: ExternalImageType::TextureHandle(
2891 ImageBufferKind::Texture2D,
2892 ),
2893 }),
2894 descriptor,
2895 tiling: None,
2896 }),
2897 ))
2898 }
2899 DecodedImage::Raw((descriptor, data)) => {
2900 let key = image_ref_hash_to_image_key(image_ref_hash, id_namespace);
2901 Some((
2902 image_ref_hash,
2903 AddImageMsg(AddImage {
2904 key,
2905 data: data.clone(), descriptor: descriptor.clone(), tiling: None,
2909 }),
2910 ))
2911 }
2912 DecodedImage::NullImage {
2913 width: _,
2914 height: _,
2915 format: _,
2916 tag: _,
2917 } => None,
2918 DecodedImage::Callback(_) => None, }
2921 })
2922 .collect()
2923}
2924
2925pub fn add_resources(
2930 renderer_resources: &mut RendererResources,
2931 all_resource_updates: &mut Vec<ResourceUpdate>,
2932 add_font_resources: Vec<(StyleFontFamilyHash, AddFontMsg)>,
2933 add_image_resources: Vec<(ImageRefHash, AddImageMsg)>,
2934) {
2935 all_resource_updates.extend(
2936 add_font_resources
2937 .iter()
2938 .map(|(_, f)| f.into_resource_update()),
2939 );
2940 all_resource_updates.extend(
2941 add_image_resources
2942 .iter()
2943 .map(|(_, i)| i.into_resource_update()),
2944 );
2945
2946 for (image_ref_hash, add_image_msg) in add_image_resources.iter() {
2947 renderer_resources.currently_registered_images.insert(
2948 *image_ref_hash,
2949 ResolvedImage {
2950 key: add_image_msg.0.key,
2951 descriptor: add_image_msg.0.descriptor,
2952 },
2953 );
2954 }
2955
2956 for (_, add_font_msg) in add_font_resources {
2957 use self::AddFontMsg::*;
2958 match add_font_msg {
2959 Font(fk, font_family_hash, font_ref) => {
2960 renderer_resources
2961 .currently_registered_fonts
2962 .entry(fk)
2963 .or_insert_with(|| (font_ref.clone(), OrderedMap::default()));
2964
2965 renderer_resources
2967 .font_hash_map
2968 .insert(font_ref.get_hash(), fk);
2969 }
2970 Instance(fi, size) => {
2971 if let Some((_, instances)) = renderer_resources
2972 .currently_registered_fonts
2973 .get_mut(&fi.font_key)
2974 {
2975 instances.insert(size, fi.key);
2976 }
2977 }
2978 }
2979 }
2980}