1use alloc::{boxed::Box, collections::btree_map::BTreeMap, string::ToString, vec::Vec};
2use core::fmt;
3
4use azul_css::{
5 BoxShadowClipMode, ColorU, ConicGradient, CssPropertyValue, LayoutBorderBottomWidth,
6 LayoutBorderLeftWidth, LayoutBorderRightWidth, LayoutBorderTopWidth, LayoutPoint, LayoutRect,
7 LayoutSize, LinearGradient, RadialGradient, StyleBackgroundPosition, StyleBackgroundRepeat,
8 StyleBackgroundSize, StyleBorderBottomColor, StyleBorderBottomLeftRadius,
9 StyleBorderBottomRightRadius, StyleBorderBottomStyle, StyleBorderLeftColor,
10 StyleBorderLeftStyle, StyleBorderRightColor, StyleBorderRightStyle, StyleBorderTopColor,
11 StyleBorderTopLeftRadius, StyleBorderTopRightRadius, StyleBorderTopStyle, StyleBoxShadow,
12 StyleMixBlendMode,
13};
14use rust_fontconfig::FcFontCache;
15
16use crate::{
17 app_resources::{
18 AddImageMsg, DpiScaleFactor, Epoch, ExternalImageId, FontInstanceKey, GlTextureCache,
19 GlyphOptions, IdNamespace, ImageCache, ImageDescriptor, ImageKey, LoadFontFn, OpacityKey,
20 ParseFontFn, PrimitiveFlags, RendererResources, ResourceUpdate, TransformKey,
21 },
22 callbacks::{DocumentId, DomNodeId, PipelineId},
23 dom::{ScrollTagId, TagId},
24 gl::{OptionGlContextPtr, Texture},
25 id_tree::NodeId,
26 styled_dom::{ContentGroup, DomId, NodeHierarchyItemId, StyledDom},
27 ui_solver::{ComputedTransform3D, ExternalScrollId, LayoutResult, PositionInfo},
28 window::{FullWindowState, LogicalPosition, LogicalRect, LogicalSize},
29};
30
31pub type GlyphIndex = u32;
32
33#[derive(Debug, Default, Copy, Clone, PartialEq, PartialOrd)]
34pub struct GlyphInstance {
35 pub index: GlyphIndex,
36 pub point: LogicalPosition,
37 pub size: LogicalSize,
38}
39
40impl GlyphInstance {
41 pub fn scale_for_dpi(&mut self, scale_factor: f32) {
42 self.point.scale_for_dpi(scale_factor);
43 self.size.scale_for_dpi(scale_factor);
44 }
45}
46
47#[derive(Debug, Copy, Clone, PartialEq, PartialOrd)]
48pub struct DisplayListImageMask {
49 pub image: ImageKey,
50 pub rect: LogicalRect,
51 pub repeat: bool,
52}
53
54impl DisplayListImageMask {
55 pub fn scale_for_dpi(&mut self, scale_factor: f32) {
56 self.rect.scale_for_dpi(scale_factor);
57 }
58}
59
60#[derive(Debug, Clone, PartialEq, PartialOrd)]
61pub struct CachedDisplayList {
62 pub root: DisplayListMsg,
63 pub root_size: LogicalSize,
64}
65
66impl CachedDisplayList {
67 pub fn empty() -> Self {
68 Self {
69 root: DisplayListMsg::Frame(DisplayListFrame::root(
70 LayoutSize::zero(),
71 LayoutPoint::zero(),
72 )),
73 root_size: LogicalSize::zero(),
74 }
75 }
76
77 pub fn scale_for_dpi(&mut self, scale_factor: f32) {
78 self.root_size.width *= scale_factor;
79 self.root_size.height *= scale_factor;
80 self.root.scale_for_dpi(scale_factor);
81 }
82}
83
84#[derive(Debug, Clone, PartialEq, PartialOrd)]
85pub enum DisplayListMsg {
86 IFrame(PipelineId, LogicalSize, Epoch, Box<CachedDisplayList>),
88 Frame(DisplayListFrame),
89 ScrollFrame(DisplayListScrollFrame),
90}
91
92impl DisplayListMsg {
93 pub fn scale_for_dpi(&mut self, scale_factor: f32) {
94 match self {
95 DisplayListMsg::IFrame(_, s, _, dl) => {
96 s.width *= scale_factor;
97 s.height *= scale_factor;
98 dl.scale_for_dpi(scale_factor);
99 }
100 DisplayListMsg::Frame(f) => {
101 f.scale_for_dpi(scale_factor);
102 }
103 DisplayListMsg::ScrollFrame(sf) => {
104 sf.scale_for_dpi(scale_factor);
105 }
106 }
107 }
108
109 pub fn get_transform_key(&self) -> Option<&(TransformKey, ComputedTransform3D)> {
110 use self::DisplayListMsg::*;
111 match self {
112 Frame(f) => f.transform.as_ref(),
113 ScrollFrame(sf) => sf.frame.transform.as_ref(),
114 IFrame(_, _, _, _) => None,
115 }
116 }
117
118 pub fn get_mix_blend_mode(&self) -> Option<&StyleMixBlendMode> {
119 use self::DisplayListMsg::*;
120 match self {
121 Frame(f) => f.mix_blend_mode.as_ref(),
122 ScrollFrame(sf) => sf.frame.mix_blend_mode.as_ref(),
123 IFrame(_, _, _, _) => None,
124 }
125 }
126
127 pub fn has_mix_blend_mode_children(&self) -> bool {
129 use self::DisplayListMsg::*;
130 match self {
131 Frame(f) => f.children.iter().any(|f| f.get_mix_blend_mode().is_some()),
132 ScrollFrame(sf) => sf
133 .frame
134 .children
135 .iter()
136 .any(|f| f.get_mix_blend_mode().is_some()),
137 IFrame(_, _, _, _) => false,
138 }
139 }
140
141 pub fn get_opacity_key(&self) -> Option<&(OpacityKey, f32)> {
142 use self::DisplayListMsg::*;
143 match self {
144 Frame(f) => f.opacity.as_ref(),
145 ScrollFrame(sf) => sf.frame.opacity.as_ref(),
146 IFrame(_, _, _, _) => None,
147 }
148 }
149
150 pub fn get_image_mask(&self) -> Option<&DisplayListImageMask> {
151 use self::DisplayListMsg::*;
152 match self {
153 Frame(f) => f.clip_mask.as_ref(),
154 ScrollFrame(sf) => sf.frame.clip_mask.as_ref(),
155 IFrame(_, _, _, _) => None,
156 }
157 }
158
159 pub fn get_position(&self) -> PositionInfo {
160 use self::DisplayListMsg::*;
161 use crate::ui_solver::PositionInfoInner;
162 match self {
163 Frame(f) => f.position.clone(),
164 ScrollFrame(sf) => sf.frame.position.clone(),
165 IFrame(_, _, _, _) => PositionInfo::Static(PositionInfoInner::zero()),
166 }
167 }
168
169 pub fn is_content_empty(&self) -> bool {
170 use self::DisplayListMsg::*;
171 match self {
172 Frame(f) => f.content.is_empty(),
173 ScrollFrame(sf) => sf.frame.content.is_empty(),
174 IFrame(_, _, _, _) => false,
175 }
176 }
177
178 pub fn has_no_children(&self) -> bool {
179 use self::DisplayListMsg::*;
180 match self {
181 Frame(f) => f.children.is_empty(),
182 ScrollFrame(sf) => sf.frame.children.is_empty(),
183 IFrame(_, _, _, _) => false,
184 }
185 }
186
187 pub fn push_content(&mut self, content: LayoutRectContent) {
188 use self::DisplayListMsg::*;
189 match self {
190 Frame(f) => {
191 f.content.push(content);
192 }
193 ScrollFrame(sf) => {
194 sf.frame.content.push(content);
195 }
196 IFrame(_, _, _, _) => {} }
198 }
199
200 pub fn append_child(&mut self, child: Self) {
201 use self::DisplayListMsg::*;
202 match self {
203 Frame(f) => {
204 f.children.push(child);
205 }
206 ScrollFrame(sf) => {
207 sf.frame.children.push(child);
208 }
209 IFrame(_, _, _, _) => {} }
211 }
212
213 pub fn append_children(&mut self, mut children: Vec<Self>) {
214 use self::DisplayListMsg::*;
215 match self {
216 Frame(f) => {
217 f.children.append(&mut children);
218 }
219 ScrollFrame(sf) => {
220 sf.frame.children.append(&mut children);
221 }
222 IFrame(_, _, _, _) => {} }
224 }
225
226 pub fn get_size(&self) -> LogicalSize {
227 use self::DisplayListMsg::*;
228 match self {
229 Frame(f) => f.size,
230 ScrollFrame(sf) => sf.frame.size,
231 IFrame(_, s, _, _) => *s,
232 }
233 }
234}
235
236#[derive(Clone, PartialEq, PartialOrd)]
237pub struct DisplayListScrollFrame {
238 pub parent_rect: LogicalRect,
240 pub content_rect: LogicalRect,
242 pub scroll_id: ExternalScrollId,
245 pub scroll_tag: ScrollTagId,
247 pub frame: DisplayListFrame,
249}
250
251impl DisplayListScrollFrame {
252 pub fn scale_for_dpi(&mut self, scale_factor: f32) {
253 self.parent_rect.scale_for_dpi(scale_factor);
254 self.content_rect.scale_for_dpi(scale_factor);
255 self.frame.scale_for_dpi(scale_factor);
256 }
257}
258
259impl fmt::Debug for DisplayListScrollFrame {
260 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
261 write!(f, "DisplayListScrollFrame {{\r\n")?;
262 write!(f, " parent_rect: {}\r\n", self.parent_rect)?;
263 write!(f, " content_rect: {}\r\n", self.content_rect)?;
264 write!(f, " scroll_tag: {}\r\n", self.scroll_tag)?;
265 write!(f, " frame: DisplayListFrame {{\r\n")?;
266 let frame = format!("{:#?}", self.frame);
267 let frame = frame
268 .lines()
269 .map(|l| format!(" {}", l))
270 .collect::<Vec<_>>()
271 .join("\r\n");
272 write!(f, "{}\r\n", frame)?;
273 write!(f, " }}\r\n")?;
274 write!(f, "}}")?;
275 Ok(())
276 }
277}
278
279#[derive(Clone, PartialEq, PartialOrd)]
280pub struct DisplayListFrame {
281 pub size: LogicalSize,
282 pub position: PositionInfo,
283 pub flags: PrimitiveFlags,
284 pub mix_blend_mode: Option<StyleMixBlendMode>,
285 pub clip_children: Option<LogicalSize>,
286 pub clip_mask: Option<DisplayListImageMask>,
287 pub border_radius: StyleBorderRadius,
289 pub tag: Option<TagId>,
290 pub box_shadow: Option<BoxShadow>,
292 pub transform: Option<(TransformKey, ComputedTransform3D)>,
293 pub opacity: Option<(OpacityKey, f32)>,
294 pub content: Vec<LayoutRectContent>,
295 pub children: Vec<DisplayListMsg>,
296}
297
298impl fmt::Debug for DisplayListFrame {
299 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
300 let print_no_comma_rect = !self.border_radius.is_none()
301 || self.tag.is_some()
302 || !self.content.is_empty()
303 || !self.children.is_empty();
304
305 write!(
306 f,
307 "rect: {:#?} @ {:?}{}",
308 self.size,
309 self.position,
310 if !print_no_comma_rect { "" } else { "," }
311 )?;
312
313 if !self.border_radius.is_none() {
314 write!(f, "\r\nborder_radius: {:#?}", self.border_radius)?;
315 }
316 if let Some(tag) = &self.tag {
317 write!(f, "\r\ntag: {}", tag.0)?;
318 }
319 if !self.content.is_empty() {
320 write!(f, "\r\ncontent: {:#?}", self.content)?;
321 }
322 if !self.children.is_empty() {
323 write!(f, "\r\nchildren: {:#?}", self.children)?;
324 }
325
326 Ok(())
327 }
328}
329
330impl DisplayListFrame {
331 pub fn scale_for_dpi(&mut self, scale_factor: f32) {
332 self.size.width *= scale_factor;
333 self.size.height *= scale_factor;
334 self.position.scale_for_dpi(scale_factor);
335 self.clip_children
336 .as_mut()
337 .map(|s| s.scale_for_dpi(scale_factor));
338 self.clip_mask
339 .as_mut()
340 .map(|s| s.scale_for_dpi(scale_factor));
341 self.border_radius.scale_for_dpi(scale_factor);
342 self.transform
343 .as_mut()
344 .map(|(k, v)| v.scale_for_dpi(scale_factor));
345 for c in self.content.iter_mut() {
346 c.scale_for_dpi(scale_factor);
347 }
348 for c in self.children.iter_mut() {
349 c.scale_for_dpi(scale_factor);
350 }
351 }
352
353 pub fn root(dimensions: LayoutSize, root_origin: LayoutPoint) -> Self {
354 use crate::ui_solver::PositionInfoInner;
355 DisplayListFrame {
356 tag: None,
357 size: LogicalSize::new(dimensions.width as f32, dimensions.height as f32),
358 clip_children: None,
359 mix_blend_mode: None,
360 position: PositionInfo::Static(PositionInfoInner {
361 x_offset: root_origin.x as f32,
362 y_offset: root_origin.y as f32,
363 static_x_offset: root_origin.x as f32,
364 static_y_offset: root_origin.y as f32,
365 }),
366 flags: PrimitiveFlags {
367 is_backface_visible: true,
368 is_scrollbar_container: false,
369 is_scrollbar_thumb: false,
370 prefer_compositor_surface: true,
371 supports_external_compositor_surface: true,
372 },
373 border_radius: StyleBorderRadius::default(),
374 box_shadow: None,
375 transform: None,
376 opacity: None,
377 content: vec![],
378 children: vec![],
379 clip_mask: None,
380 }
381 }
382}
383
384#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
385pub enum ImageRendering {
386 Auto,
387 CrispEdges,
388 Pixelated,
389}
390
391#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
392pub enum AlphaType {
393 Alpha,
394 PremultipliedAlpha,
395}
396
397#[derive(Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
398pub struct StyleBorderRadius {
399 pub top_left: Option<CssPropertyValue<StyleBorderTopLeftRadius>>,
400 pub top_right: Option<CssPropertyValue<StyleBorderTopRightRadius>>,
401 pub bottom_left: Option<CssPropertyValue<StyleBorderBottomLeftRadius>>,
402 pub bottom_right: Option<CssPropertyValue<StyleBorderBottomRightRadius>>,
403}
404
405impl StyleBorderRadius {
406 pub fn is_none(&self) -> bool {
407 self.top_left.is_none()
408 && self.top_right.is_none()
409 && self.bottom_left.is_none()
410 && self.bottom_right.is_none()
411 }
412
413 pub fn scale_for_dpi(&mut self, scale_factor: f32) {
414 self.top_left
415 .as_mut()
416 .map(|s| s.scale_for_dpi(scale_factor));
417 self.top_right
418 .as_mut()
419 .map(|s| s.scale_for_dpi(scale_factor));
420 self.bottom_left
421 .as_mut()
422 .map(|s| s.scale_for_dpi(scale_factor));
423 self.bottom_right
424 .as_mut()
425 .map(|s| s.scale_for_dpi(scale_factor));
426 }
427}
428
429impl fmt::Debug for StyleBorderRadius {
430 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
431 write!(f, "StyleBorderRadius {{")?;
432 if let Some(tl) = &self.top_left {
433 write!(f, "\r\n\ttop-left: {:?},", tl)?;
434 }
435 if let Some(tr) = &self.top_right {
436 write!(f, "\r\n\ttop-right: {:?},", tr)?;
437 }
438 if let Some(bl) = &self.bottom_left {
439 write!(f, "\r\n\tbottom-left: {:?},", bl)?;
440 }
441 if let Some(br) = &self.bottom_right {
442 write!(f, "\r\n\tbottom-right: {:?},", br)?;
443 }
444 write!(f, "\r\n}}")
445 }
446}
447
448macro_rules! tlbr_debug {
449 ($struct_name:ident) => {
450 impl fmt::Debug for $struct_name {
451 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
452 write!(f, "{} {{", stringify!($struct_name))?;
453 if let Some(t) = &self.top {
454 write!(f, "\r\n\ttop: {:?},", t)?;
455 }
456 if let Some(r) = &self.right {
457 write!(f, "\r\n\tright: {:?},", r)?;
458 }
459 if let Some(b) = &self.bottom {
460 write!(f, "\r\n\tbottom: {:?},", b)?;
461 }
462 if let Some(l) = &self.left {
463 write!(f, "\r\n\tleft: {:?},", l)?;
464 }
465 write!(f, "\r\n}}")
466 }
467 }
468 };
469}
470
471#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
472pub struct StyleBorderWidths {
473 pub top: Option<CssPropertyValue<LayoutBorderTopWidth>>,
474 pub right: Option<CssPropertyValue<LayoutBorderRightWidth>>,
475 pub bottom: Option<CssPropertyValue<LayoutBorderBottomWidth>>,
476 pub left: Option<CssPropertyValue<LayoutBorderLeftWidth>>,
477}
478
479impl StyleBorderWidths {
480 pub fn scale_for_dpi(&mut self, scale_factor: f32) {
481 self.top.as_mut().map(|s| s.scale_for_dpi(scale_factor));
482 self.right.as_mut().map(|s| s.scale_for_dpi(scale_factor));
483 self.bottom.as_mut().map(|s| s.scale_for_dpi(scale_factor));
484 self.left.as_mut().map(|s| s.scale_for_dpi(scale_factor));
485 }
486
487 #[inline]
488 pub fn left_width(&self) -> f32 {
489 self.left
490 .unwrap_or_default()
491 .get_property_owned()
492 .unwrap_or_default()
493 .inner
494 .to_pixels(0.0)
495 }
496
497 #[inline]
498 pub fn right_width(&self) -> f32 {
499 self.right
500 .unwrap_or_default()
501 .get_property_owned()
502 .unwrap_or_default()
503 .inner
504 .to_pixels(0.0)
505 }
506
507 #[inline]
508 pub fn top_width(&self) -> f32 {
509 self.top
510 .unwrap_or_default()
511 .get_property_owned()
512 .unwrap_or_default()
513 .inner
514 .to_pixels(0.0)
515 }
516
517 #[inline]
518 pub fn bottom_width(&self) -> f32 {
519 self.bottom
520 .unwrap_or_default()
521 .get_property_owned()
522 .unwrap_or_default()
523 .inner
524 .to_pixels(0.0)
525 }
526
527 #[inline]
528 pub fn total_horizontal(&self) -> f32 {
529 self.left_width() + self.right_width()
530 }
531
532 #[inline]
533 pub fn total_vertical(&self) -> f32 {
534 self.top_width() + self.bottom_width()
535 }
536}
537
538tlbr_debug!(StyleBorderWidths);
539
540#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
541pub struct StyleBorderColors {
542 pub top: Option<CssPropertyValue<StyleBorderTopColor>>,
543 pub right: Option<CssPropertyValue<StyleBorderRightColor>>,
544 pub bottom: Option<CssPropertyValue<StyleBorderBottomColor>>,
545 pub left: Option<CssPropertyValue<StyleBorderLeftColor>>,
546}
547
548tlbr_debug!(StyleBorderColors);
549
550#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
551pub struct StyleBorderStyles {
552 pub top: Option<CssPropertyValue<StyleBorderTopStyle>>,
553 pub right: Option<CssPropertyValue<StyleBorderRightStyle>>,
554 pub bottom: Option<CssPropertyValue<StyleBorderBottomStyle>>,
555 pub left: Option<CssPropertyValue<StyleBorderLeftStyle>>,
556}
557
558tlbr_debug!(StyleBorderStyles);
559
560#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
561pub struct BoxShadow {
562 pub clip_mode: BoxShadowClipMode,
563 pub top: Option<CssPropertyValue<StyleBoxShadow>>,
564 pub right: Option<CssPropertyValue<StyleBoxShadow>>,
565 pub bottom: Option<CssPropertyValue<StyleBoxShadow>>,
566 pub left: Option<CssPropertyValue<StyleBoxShadow>>,
567}
568
569tlbr_debug!(BoxShadow);
570
571#[derive(Clone, PartialEq, PartialOrd)]
572pub enum LayoutRectContent {
573 Text {
574 glyphs: Vec<GlyphInstance>,
575 font_instance_key: FontInstanceKey,
576 color: ColorU,
577 glyph_options: Option<GlyphOptions>,
578 overflow: (bool, bool),
579 text_shadow: Option<StyleBoxShadow>,
580 },
581 Background {
582 content: RectBackground,
583 size: Option<StyleBackgroundSize>,
584 offset: Option<StyleBackgroundPosition>,
585 repeat: Option<StyleBackgroundRepeat>,
586 },
587 Image {
588 size: LogicalSize,
589 offset: LogicalPosition,
590 image_rendering: ImageRendering,
591 alpha_type: AlphaType,
592 image_key: ImageKey,
593 background_color: ColorU,
594 },
595 Border {
596 widths: StyleBorderWidths,
597 colors: StyleBorderColors,
598 styles: StyleBorderStyles,
599 },
600}
601
602impl LayoutRectContent {
603 pub fn scale_for_dpi(&mut self, scale_factor: f32) {
604 use self::LayoutRectContent::*;
605 match self {
606 Text {
607 glyphs,
608 font_instance_key,
609 color,
610 glyph_options,
611 overflow,
612 text_shadow,
613 } => {
614 for g in glyphs.iter_mut() {
615 g.scale_for_dpi(scale_factor);
616 }
617 text_shadow.as_mut().map(|s| s.scale_for_dpi(scale_factor));
618 }
619 Background {
620 content,
621 size,
622 offset,
623 repeat,
624 } => {
625 content.scale_for_dpi(scale_factor);
626 size.as_mut().map(|s| s.scale_for_dpi(scale_factor));
627 offset.as_mut().map(|s| s.scale_for_dpi(scale_factor));
628 }
629 Image {
630 size,
631 offset,
632 image_rendering,
633 alpha_type,
634 image_key,
635 background_color,
636 } => {
637 size.scale_for_dpi(scale_factor);
638 offset.scale_for_dpi(scale_factor);
639 }
640 Border {
641 widths,
642 colors,
643 styles,
644 } => {
645 widths.scale_for_dpi(scale_factor);
646 }
647 }
648 }
649}
650
651impl fmt::Debug for LayoutRectContent {
652 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
653 use self::LayoutRectContent::*;
654 match self {
655 Text {
656 glyphs,
657 font_instance_key,
658 color,
659 glyph_options,
660 overflow,
661 text_shadow,
662 } => {
663 let glyphs_str = glyphs
664 .iter()
665 .map(|g| format!(" {:?}", g))
666 .collect::<Vec<_>>()
667 .join(",\r\n");
668 write!(
669 f,
670 "Text {{\r\n\
671 . glyphs: [\r\n{}\r\n],\r\n\
672 . font_instance_key: {:?},\r\n\
673 . color: {},\r\n\
674 . glyph_options: {:?},\r\n\
675 . overflow: {:?},\r\n\
676 . text_shadow: {:?},\r\n\
677 }}",
678 glyphs_str, font_instance_key.key, color, glyph_options, overflow, text_shadow
679 )
680 }
681 Background {
682 content,
683 size,
684 offset,
685 repeat,
686 } => {
687 write!(f, "Background {{\r\n")?;
688 write!(f, " content: {:?},\r\n", content)?;
689 write!(f, " size: {:?},\r\n", size)?;
690 write!(f, " offset: {:?},\r\n", offset)?;
691 write!(f, " repeat: {:?},\r\n", repeat)?;
692 write!(f, "}}")
693 }
694 Image {
695 size,
696 offset,
697 image_rendering,
698 alpha_type,
699 image_key,
700 background_color,
701 } => {
702 write!(
703 f,
704 "Image {{\r\nsize: {:?},\r\noffset: {:?},\r\nimage_rendering: \
705 {:?},\r\nalpha_type: {:?},\r\nimage_key: {:?},\r\nbackground_color: \
706 {:?}\r\n}}",
707 size, offset, image_rendering, alpha_type, image_key, background_color
708 )
709 }
710 Border {
711 widths,
712 colors,
713 styles,
714 } => {
715 write!(
716 f,
717 "Border {{\r\nwidths: {:?},\r\ncolors: {:?},\r\nstyles: {:?}\r\n}}",
718 widths, colors, styles,
719 )
720 }
721 }
722 }
723}
724
725#[derive(Clone, PartialEq, PartialOrd)]
726pub enum RectBackground {
727 LinearGradient(LinearGradient),
728 RadialGradient(RadialGradient),
729 ConicGradient(ConicGradient),
730 Image((ImageKey, ImageDescriptor)),
731 Color(ColorU),
732}
733
734impl fmt::Debug for RectBackground {
735 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
736 use self::RectBackground::*;
737 match self {
738 LinearGradient(l) => write!(f, "{:?}", l),
739 RadialGradient(r) => write!(f, "{:?}", r),
740 ConicGradient(c) => write!(f, "{:?}", c),
741 Image(id) => write!(f, "image({:#?})", id),
742 Color(c) => write!(f, "{}", c),
743 }
744 }
745}
746
747impl RectBackground {
748 pub fn scale_for_dpi(&mut self, scale_factor: f32) {
749 match self {
750 RectBackground::Image((_key, descriptor)) => {
751 descriptor.width =
752 libm::round(descriptor.width as f64 * scale_factor as f64) as usize;
753 descriptor.height =
754 libm::round(descriptor.height as f64 * scale_factor as f64) as usize;
755 }
756 _ => {}
757 }
758 }
759 pub fn get_content_size(&self) -> Option<(f32, f32)> {
760 match self {
761 RectBackground::Image((_key, descriptor)) => {
762 Some((descriptor.width as f32, descriptor.height as f32))
763 }
764 _ => None,
765 }
766 }
767}
768
769#[derive(Clone)]
778pub struct DisplayListParametersRef<'a> {
779 pub dom_id: DomId,
781 pub document_id: &'a DocumentId,
783 pub epoch: Epoch,
785 pub full_window_state: &'a FullWindowState,
787 pub layout_results: &'a [LayoutResult],
789 pub gl_texture_cache: &'a GlTextureCache,
791 pub image_cache: &'a ImageCache,
793 pub renderer_resources: &'a RendererResources,
795}
796
797pub type LayoutFn = fn(
799 StyledDom,
800 &ImageCache,
801 &FcFontCache,
802 &mut RendererResources,
803 DpiScaleFactor,
804 &mut Vec<ResourceUpdate>,
805 IdNamespace,
806 &DocumentId,
807 Epoch,
808 &RenderCallbacks,
809 &FullWindowState,
810) -> Vec<LayoutResult>;
811pub type GlStoreImageFn = fn(DocumentId, Epoch, Texture) -> ExternalImageId;
812
813#[derive(Debug, Default)]
814pub struct SolvedLayout {
815 pub layout_results: Vec<LayoutResult>,
816}
817
818#[derive(Clone)]
819pub struct RenderCallbacks {
820 pub insert_into_active_gl_textures_fn: GlStoreImageFn,
821 pub layout_fn: LayoutFn,
822 pub load_font_fn: LoadFontFn,
823 pub parse_font_fn: ParseFontFn,
824}
825
826impl SolvedLayout {
827 pub fn new(
829 styled_dom: StyledDom,
830 epoch: Epoch,
831 document_id: &DocumentId,
832 full_window_state: &FullWindowState,
833 all_resource_updates: &mut Vec<ResourceUpdate>,
834 id_namespace: IdNamespace,
835 image_cache: &ImageCache,
836 system_fonts: &FcFontCache,
837 callbacks: &RenderCallbacks,
838 renderer_resources: &mut RendererResources,
839 current_window_dpi: DpiScaleFactor,
840 ) -> Self {
841 Self {
842 layout_results: (callbacks.layout_fn)(
843 styled_dom,
844 image_cache,
845 system_fonts,
846 renderer_resources,
847 current_window_dpi,
848 all_resource_updates,
849 id_namespace,
850 document_id,
851 epoch,
852 callbacks,
853 &full_window_state,
854 ),
855 }
856 }
857}
858
859pub fn push_rectangles_into_displaylist<'a>(
860 root_content_group: &ContentGroup,
861 referenced_content: &DisplayListParametersRef<'a>,
862) -> Option<DisplayListMsg> {
863 let mut content = displaylist_handle_rect(
864 root_content_group.root.into_crate_internal().unwrap(),
865 referenced_content,
866 )?;
867
868 let children = root_content_group
869 .children
870 .as_ref()
871 .iter()
872 .filter_map(|child_content_group| {
873 push_rectangles_into_displaylist(child_content_group, referenced_content)
874 })
875 .collect();
876
877 content.append_children(children);
878
879 Some(content)
880}
881
882pub fn displaylist_handle_rect<'a>(
884 rect_idx: NodeId,
885 referenced_content: &DisplayListParametersRef<'a>,
886) -> Option<DisplayListMsg> {
887 use azul_css::LayoutDisplay;
888
889 use crate::{app_resources::ResolvedImage, dom::NodeType::*, styled_dom::AzTagId};
890
891 let DisplayListParametersRef {
892 dom_id,
893 layout_results,
894 gl_texture_cache,
895 renderer_resources,
896 image_cache,
897 ..
898 } = referenced_content;
899
900 let layout_result = &layout_results[dom_id.inner];
901 let styled_node = &layout_result.styled_dom.styled_nodes.as_container()[rect_idx];
902 let positioned_rect = &layout_result.rects.as_ref()[rect_idx];
903 let html_node = &layout_result.styled_dom.node_data.as_container()[rect_idx];
904
905 let tag_id = styled_node.tag_id.into_option().or({
906 layout_result
907 .scrollable_nodes
908 .overflowing_nodes
909 .get(&NodeHierarchyItemId::from_crate_internal(Some(rect_idx)))
910 .map(|scrolled| AzTagId::from_crate_internal(scrolled.scroll_tag_id.0))
911 });
912
913 let clip_mask = html_node.get_clip_mask().and_then(|m| {
914 let clip_mask_hash = m.image.get_hash();
915 let ResolvedImage { key, .. } = renderer_resources.get_image(&clip_mask_hash)?;
916 Some(DisplayListImageMask {
917 image: *key,
918 rect: m.rect,
919 repeat: m.repeat,
920 })
921 });
922
923 let display = layout_result
927 .styled_dom
928 .get_css_property_cache()
929 .get_display(&html_node, &rect_idx, &styled_node.state)
930 .cloned()
931 .unwrap_or_default();
932
933 if display == CssPropertyValue::None || display == CssPropertyValue::Exact(LayoutDisplay::None)
934 {
935 return None;
936 }
937
938 let overflow_horizontal_hidden = layout_result
939 .styled_dom
940 .get_css_property_cache()
941 .is_horizontal_overflow_hidden(&html_node, &rect_idx, &styled_node.state);
942 let overflow_vertical_hidden = layout_result
943 .styled_dom
944 .get_css_property_cache()
945 .is_vertical_overflow_hidden(&html_node, &rect_idx, &styled_node.state);
946
947 let overflow_horizontal_visible = layout_result
948 .styled_dom
949 .get_css_property_cache()
950 .is_horizontal_overflow_visible(&html_node, &rect_idx, &styled_node.state);
951 let overflow_vertical_visible = layout_result
952 .styled_dom
953 .get_css_property_cache()
954 .is_vertical_overflow_visible(&html_node, &rect_idx, &styled_node.state);
955
956 let mix_blend_mode = layout_result
957 .styled_dom
958 .get_css_property_cache()
959 .get_mix_blend_mode(&html_node, &rect_idx, &styled_node.state)
960 .and_then(|p| p.get_property())
961 .cloned();
962
963 let mut frame = DisplayListFrame {
964 tag: tag_id.map(|t| t.into_crate_internal()),
965 size: positioned_rect.size,
966 mix_blend_mode,
967 clip_children: match layout_result
968 .scrollable_nodes
969 .clip_nodes
970 .get(&rect_idx)
971 .copied()
972 {
973 Some(s) => {
974 if overflow_horizontal_visible && overflow_vertical_visible {
976 None
977 } else {
978 Some(s)
979 }
980 }
981 None => {
982 if overflow_horizontal_hidden && overflow_vertical_hidden {
985 Some(positioned_rect.size)
986 } else {
987 None
988 }
989 }
990 },
991 position: positioned_rect.position.clone(),
992 border_radius: StyleBorderRadius {
993 top_left: layout_result
994 .styled_dom
995 .get_css_property_cache()
996 .get_border_top_left_radius(&html_node, &rect_idx, &styled_node.state)
997 .cloned(),
998 top_right: layout_result
999 .styled_dom
1000 .get_css_property_cache()
1001 .get_border_top_right_radius(&html_node, &rect_idx, &styled_node.state)
1002 .cloned(),
1003 bottom_left: layout_result
1004 .styled_dom
1005 .get_css_property_cache()
1006 .get_border_bottom_left_radius(&html_node, &rect_idx, &styled_node.state)
1007 .cloned(),
1008 bottom_right: layout_result
1009 .styled_dom
1010 .get_css_property_cache()
1011 .get_border_bottom_right_radius(&html_node, &rect_idx, &styled_node.state)
1012 .cloned(),
1013 },
1014 flags: PrimitiveFlags {
1015 is_backface_visible: false, is_scrollbar_container: false,
1017 is_scrollbar_thumb: false,
1018 prefer_compositor_surface: false,
1019 supports_external_compositor_surface: false,
1020 },
1021 content: Vec::new(),
1022 children: Vec::new(),
1023 box_shadow: None,
1024 transform: layout_result
1025 .gpu_value_cache
1026 .transform_keys
1027 .get(&rect_idx)
1028 .and_then(|key| {
1029 Some((
1030 *key,
1031 layout_result
1032 .gpu_value_cache
1033 .current_transform_values
1034 .get(&rect_idx)
1035 .cloned()?,
1036 ))
1037 }),
1038 opacity: layout_result
1039 .gpu_value_cache
1040 .opacity_keys
1041 .get(&rect_idx)
1042 .and_then(|key| {
1043 Some((
1044 *key,
1045 layout_result
1046 .gpu_value_cache
1047 .current_opacity_values
1048 .get(&rect_idx)
1049 .cloned()?,
1050 ))
1051 }),
1052 clip_mask,
1053 };
1054
1055 let box_shadow_left = layout_result
1057 .styled_dom
1058 .get_css_property_cache()
1059 .get_box_shadow_left(&html_node, &rect_idx, &styled_node.state);
1060 let box_shadow_right = layout_result
1061 .styled_dom
1062 .get_css_property_cache()
1063 .get_box_shadow_right(&html_node, &rect_idx, &styled_node.state);
1064 let box_shadow_top = layout_result
1065 .styled_dom
1066 .get_css_property_cache()
1067 .get_box_shadow_top(&html_node, &rect_idx, &styled_node.state);
1068 let box_shadow_bottom = layout_result
1069 .styled_dom
1070 .get_css_property_cache()
1071 .get_box_shadow_bottom(&html_node, &rect_idx, &styled_node.state);
1072
1073 let box_shadows = [
1074 &box_shadow_left,
1075 &box_shadow_right,
1076 &box_shadow_top,
1077 &box_shadow_bottom,
1078 ];
1079
1080 let box_shadow = if box_shadows.iter().all(|b| b.is_some()) {
1081 let mut clip_mode = None;
1082
1083 if box_shadows.iter().all(|b| {
1084 b.and_then(|b| b.get_property().map(|p| p.clip_mode)) == Some(BoxShadowClipMode::Outset)
1085 }) {
1086 clip_mode = Some(BoxShadowClipMode::Outset);
1087 } else if box_shadows.iter().all(|b| {
1088 b.and_then(|b| b.get_property().map(|p| p.clip_mode)) == Some(BoxShadowClipMode::Inset)
1089 }) {
1090 clip_mode = Some(BoxShadowClipMode::Inset);
1091 }
1092
1093 clip_mode.map(|c| BoxShadow {
1094 clip_mode: c,
1095 left: box_shadow_left.cloned(),
1096 right: box_shadow_right.cloned(),
1097 top: box_shadow_top.cloned(),
1098 bottom: box_shadow_bottom.cloned(),
1099 })
1100 } else {
1101 None
1102 };
1103
1104 frame.box_shadow = box_shadow;
1105
1106 let bg_opt = layout_result
1108 .styled_dom
1109 .get_css_property_cache()
1110 .get_background_content(&html_node, &rect_idx, &styled_node.state);
1111
1112 if let Some(bg) = bg_opt.as_ref().and_then(|br| br.get_property()) {
1113 use azul_css::{
1114 StyleBackgroundPositionVec, StyleBackgroundRepeatVec, StyleBackgroundSizeVec,
1115 };
1116
1117 let default_bg_size_vec: StyleBackgroundSizeVec = Vec::new().into();
1118 let default_bg_position_vec: StyleBackgroundPositionVec = Vec::new().into();
1119 let default_bg_repeat_vec: StyleBackgroundRepeatVec = Vec::new().into();
1120
1121 let bg_sizes_opt = layout_result
1122 .styled_dom
1123 .get_css_property_cache()
1124 .get_background_size(&html_node, &rect_idx, &styled_node.state);
1125 let bg_positions_opt = layout_result
1126 .styled_dom
1127 .get_css_property_cache()
1128 .get_background_position(&html_node, &rect_idx, &styled_node.state);
1129 let bg_repeats_opt = layout_result
1130 .styled_dom
1131 .get_css_property_cache()
1132 .get_background_repeat(&html_node, &rect_idx, &styled_node.state);
1133
1134 let bg_sizes = bg_sizes_opt
1135 .as_ref()
1136 .and_then(|p| p.get_property())
1137 .unwrap_or(&default_bg_size_vec);
1138 let bg_positions = bg_positions_opt
1139 .as_ref()
1140 .and_then(|p| p.get_property())
1141 .unwrap_or(&default_bg_position_vec);
1142 let bg_repeats = bg_repeats_opt
1143 .as_ref()
1144 .and_then(|p| p.get_property())
1145 .unwrap_or(&default_bg_repeat_vec);
1146
1147 for (bg_index, bg) in bg.iter().enumerate() {
1148 use azul_css::{AzString, StyleBackgroundContent::*};
1149
1150 fn get_image_background_key(
1151 renderer_resources: &RendererResources,
1152 image_cache: &ImageCache,
1153 background_image_id: &AzString,
1154 ) -> Option<(ImageKey, ImageDescriptor)> {
1155 let image_ref = image_cache.get_css_image_id(background_image_id)?;
1156 let image_ref_hash = image_ref.get_hash();
1157 let ResolvedImage { key, descriptor } =
1158 renderer_resources.get_image(&image_ref_hash)?;
1159 Some((*key, descriptor.clone()))
1160 }
1161
1162 let background_content = match bg {
1163 LinearGradient(lg) => Some(RectBackground::LinearGradient(lg.clone())),
1164 RadialGradient(rg) => Some(RectBackground::RadialGradient(rg.clone())),
1165 ConicGradient(cg) => Some(RectBackground::ConicGradient(cg.clone())),
1166 Image(i) => get_image_background_key(&renderer_resources, &image_cache, i)
1167 .map(RectBackground::Image),
1168 Color(c) => Some(RectBackground::Color(*c)),
1169 };
1170
1171 let bg_size = bg_sizes.get(bg_index).or(bg_sizes.get(0)).copied();
1172 let bg_position = bg_positions.get(bg_index).or(bg_positions.get(0)).copied();
1173 let bg_repeat = bg_repeats.get(bg_index).or(bg_repeats.get(0)).copied();
1174
1175 if let Some(background_content) = background_content {
1176 frame.content.push(LayoutRectContent::Background {
1177 content: background_content,
1178 size: bg_size.clone(),
1179 offset: bg_position.clone(),
1180 repeat: bg_repeat.clone(),
1181 });
1182 }
1183 }
1184 }
1185
1186 match html_node.get_node_type() {
1187 Div | Body | Br => {}
1188 Text(_) => {
1189 use crate::app_resources::get_inline_text;
1190
1191 if let (
1196 Some(words),
1197 Some(shaped_words),
1198 Some(word_positions),
1199 Some((_, inline_text_layout)),
1200 ) = (
1201 layout_result.words_cache.get(&rect_idx),
1202 layout_result.shaped_words_cache.get(&rect_idx),
1203 layout_result.positioned_words_cache.get(&rect_idx),
1204 positioned_rect.resolved_text_layout_options.as_ref(),
1205 ) {
1206 let inline_text = get_inline_text(
1207 &words,
1208 &shaped_words,
1209 &word_positions.0,
1210 &inline_text_layout,
1211 );
1212 let layouted_glyphs = inline_text.get_layouted_glyphs();
1213
1214 if !layouted_glyphs.glyphs.is_empty() {
1215 let font_instance_key = word_positions.1;
1216 let text_color = layout_result
1217 .styled_dom
1218 .get_css_property_cache()
1219 .get_text_color_or_default(&html_node, &rect_idx, &styled_node.state);
1220
1221 let text_shadow = layout_result
1222 .styled_dom
1223 .get_css_property_cache()
1224 .get_text_shadow(&html_node, &rect_idx, &styled_node.state)
1225 .and_then(|p| p.get_property())
1226 .cloned();
1227
1228 frame.content.push(LayoutRectContent::Text {
1229 text_shadow,
1230 glyphs: layouted_glyphs.glyphs,
1231 font_instance_key,
1232 color: text_color.inner,
1233 glyph_options: None,
1234 overflow: (overflow_horizontal_visible, overflow_vertical_visible),
1235 });
1236 }
1237 }
1238 }
1239 Image(image_ref) => {
1240 use crate::app_resources::DecodedImage;
1241
1242 let image_hash = image_ref.get_hash();
1243 let image_size = image_ref.get_size();
1244
1245 match image_ref.get_data() {
1246 DecodedImage::NullImage { .. } => frame.content.push(LayoutRectContent::Image {
1247 size: image_size,
1248 offset: LogicalPosition::zero(),
1249 image_rendering: ImageRendering::Auto,
1250 alpha_type: AlphaType::Alpha,
1251 image_key: ImageKey::DUMMY,
1252 background_color: ColorU::WHITE,
1253 }),
1254 DecodedImage::Gl(_) | DecodedImage::Raw(_) => {
1255 if let Some(ResolvedImage { key, .. }) =
1256 renderer_resources.get_image(&image_hash)
1257 {
1258 frame.content.push(LayoutRectContent::Image {
1259 size: image_size,
1260 offset: LogicalPosition::zero(),
1261 image_rendering: ImageRendering::Auto,
1262 alpha_type: AlphaType::PremultipliedAlpha,
1263 image_key: *key,
1264 background_color: ColorU::WHITE,
1265 });
1266 }
1267 }
1268 DecodedImage::Callback(_) => {
1269 if let Some((key, descriptor, _)) = gl_texture_cache
1270 .solved_textures
1271 .get(&dom_id)
1272 .and_then(|textures| textures.get(&rect_idx))
1273 {
1274 frame.content.push(LayoutRectContent::Image {
1275 size: LogicalSize::new(
1276 descriptor.width as f32,
1277 descriptor.height as f32,
1278 ),
1279 offset: LogicalPosition::zero(),
1280 image_rendering: ImageRendering::Auto,
1281 alpha_type: AlphaType::Alpha,
1282 image_key: *key,
1283 background_color: ColorU::WHITE,
1284 })
1285 }
1286 }
1287 }
1288 }
1289 IFrame(_) => {
1290 if let Some(iframe_dom_id) =
1291 layout_result
1292 .iframe_mapping
1293 .iter()
1294 .find_map(|(node_id, dom_id)| {
1295 if *node_id == rect_idx {
1296 Some(*dom_id)
1297 } else {
1298 None
1299 }
1300 })
1301 {
1302 let iframe_pipeline_id = PipelineId(
1303 iframe_dom_id.inner.max(core::u32::MAX as usize) as u32,
1304 referenced_content.document_id.id,
1305 );
1306 let cached_display_list = LayoutResult::get_cached_display_list(
1307 referenced_content.document_id,
1308 iframe_dom_id, referenced_content.epoch,
1310 referenced_content.layout_results,
1311 referenced_content.full_window_state,
1312 referenced_content.gl_texture_cache,
1313 referenced_content.renderer_resources,
1314 referenced_content.image_cache,
1315 );
1316 let iframe_clip_size = positioned_rect.size;
1317 frame.children.push(DisplayListMsg::IFrame(
1318 iframe_pipeline_id,
1319 iframe_clip_size,
1320 referenced_content.epoch,
1321 Box::new(cached_display_list),
1322 ));
1323 }
1324 }
1325 };
1326
1327 if layout_result
1328 .styled_dom
1329 .get_css_property_cache()
1330 .has_border(&html_node, &rect_idx, &styled_node.state)
1331 {
1332 frame.content.push(LayoutRectContent::Border {
1333 widths: StyleBorderWidths {
1334 top: layout_result
1335 .styled_dom
1336 .get_css_property_cache()
1337 .get_border_top_width(&html_node, &rect_idx, &styled_node.state)
1338 .cloned(),
1339 left: layout_result
1340 .styled_dom
1341 .get_css_property_cache()
1342 .get_border_left_width(&html_node, &rect_idx, &styled_node.state)
1343 .cloned(),
1344 bottom: layout_result
1345 .styled_dom
1346 .get_css_property_cache()
1347 .get_border_bottom_width(&html_node, &rect_idx, &styled_node.state)
1348 .cloned(),
1349 right: layout_result
1350 .styled_dom
1351 .get_css_property_cache()
1352 .get_border_right_width(&html_node, &rect_idx, &styled_node.state)
1353 .cloned(),
1354 },
1355 colors: StyleBorderColors {
1356 top: layout_result
1357 .styled_dom
1358 .get_css_property_cache()
1359 .get_border_top_color(&html_node, &rect_idx, &styled_node.state)
1360 .cloned(),
1361 left: layout_result
1362 .styled_dom
1363 .get_css_property_cache()
1364 .get_border_left_color(&html_node, &rect_idx, &styled_node.state)
1365 .cloned(),
1366 bottom: layout_result
1367 .styled_dom
1368 .get_css_property_cache()
1369 .get_border_bottom_color(&html_node, &rect_idx, &styled_node.state)
1370 .cloned(),
1371 right: layout_result
1372 .styled_dom
1373 .get_css_property_cache()
1374 .get_border_right_color(&html_node, &rect_idx, &styled_node.state)
1375 .cloned(),
1376 },
1377 styles: StyleBorderStyles {
1378 top: layout_result
1379 .styled_dom
1380 .get_css_property_cache()
1381 .get_border_top_style(&html_node, &rect_idx, &styled_node.state)
1382 .cloned(),
1383 left: layout_result
1384 .styled_dom
1385 .get_css_property_cache()
1386 .get_border_left_style(&html_node, &rect_idx, &styled_node.state)
1387 .cloned(),
1388 bottom: layout_result
1389 .styled_dom
1390 .get_css_property_cache()
1391 .get_border_bottom_style(&html_node, &rect_idx, &styled_node.state)
1392 .cloned(),
1393 right: layout_result
1394 .styled_dom
1395 .get_css_property_cache()
1396 .get_border_right_style(&html_node, &rect_idx, &styled_node.state)
1397 .cloned(),
1398 },
1399 });
1400 }
1401
1402 match layout_result
1403 .scrollable_nodes
1404 .overflowing_nodes
1405 .get(&NodeHierarchyItemId::from_crate_internal(Some(rect_idx)))
1406 {
1407 Some(scroll_node) => Some(DisplayListMsg::ScrollFrame(DisplayListScrollFrame {
1408 parent_rect: scroll_node.parent_rect,
1409 content_rect: scroll_node.child_rect,
1410 scroll_id: scroll_node.parent_external_scroll_id,
1411 scroll_tag: scroll_node.scroll_tag_id,
1412 frame,
1413 })),
1414 None => Some(DisplayListMsg::Frame(frame)),
1415 }
1416}