1#[cfg(not(feature = "std"))]
2use alloc::string::{String, ToString};
3use alloc::{boxed::Box, collections::btree_map::BTreeMap, vec::Vec};
4#[cfg(target_arch = "x86_64")]
5use core::arch::x86_64::__m256;
6use core::{
7 fmt,
8 sync::atomic::{AtomicBool, Ordering as AtomicOrdering},
9};
10
11use azul_css::{
12 ColorU as StyleColorU, CssPropertyValue, LayoutBorderBottomWidth, LayoutBorderLeftWidth,
13 LayoutBorderRightWidth, LayoutBorderTopWidth, LayoutBottom, LayoutBoxSizing, LayoutDisplay,
14 LayoutFlexDirection, LayoutJustifyContent, LayoutLeft, LayoutMarginBottom, LayoutMarginLeft,
15 LayoutMarginRight, LayoutMarginTop, LayoutOverflow, LayoutPaddingBottom, LayoutPaddingLeft,
16 LayoutPaddingRight, LayoutPaddingTop, LayoutPoint, LayoutPosition, LayoutRect, LayoutRectVec,
17 LayoutRight, LayoutSize, LayoutTop, OptionF32, PixelValue, StyleBoxShadow, StyleFontSize,
18 StyleTextAlign, StyleTextColor, StyleTransform, StyleTransformOrigin, StyleVerticalAlign,
19};
20use rust_fontconfig::FcFontCache;
21
22use crate::{
23 app_resources::{
24 Epoch, FontInstanceKey, GlTextureCache, IdNamespace, ImageCache, OpacityKey,
25 RendererResources, ShapedWords, TransformKey, UpdateImageResult, WordPositions, Words,
26 },
27 callbacks::{
28 DocumentId, HidpiAdjustedBounds, HitTestItem, IFrameCallbackInfo, IFrameCallbackReturn,
29 PipelineId, ScrollHitTestItem,
30 },
31 display_list::{CachedDisplayList, RenderCallbacks},
32 dom::{DomNodeHash, ScrollTagId, TagId},
33 gl::OptionGlContextPtr,
34 id_tree::{NodeDataContainer, NodeDataContainerRef, NodeId},
35 styled_dom::{DomId, NodeHierarchyItemId, StyledDom},
36 window::{
37 FullWindowState, LogicalPosition, LogicalRect, LogicalRectVec, LogicalSize, ScrollStates,
38 WindowSize, WindowTheme,
39 },
40 window_state::RelayoutFn,
41};
42
43static INITIALIZED: AtomicBool = AtomicBool::new(false);
44static USE_AVX: AtomicBool = AtomicBool::new(false);
45static USE_SSE: AtomicBool = AtomicBool::new(false);
46
47pub const DEFAULT_FONT_SIZE_PX: isize = 16;
48pub const DEFAULT_FONT_SIZE: StyleFontSize = StyleFontSize {
49 inner: PixelValue::const_px(DEFAULT_FONT_SIZE_PX),
50};
51pub const DEFAULT_FONT_ID: &str = "serif";
52pub const DEFAULT_TEXT_COLOR: StyleTextColor = StyleTextColor {
53 inner: StyleColorU {
54 r: 0,
55 b: 0,
56 g: 0,
57 a: 255,
58 },
59};
60pub const DEFAULT_LINE_HEIGHT: f32 = 1.0;
61pub const DEFAULT_WORD_SPACING: f32 = 1.0;
62pub const DEFAULT_LETTER_SPACING: f32 = 0.0;
63pub const DEFAULT_TAB_WIDTH: f32 = 4.0;
64
65#[derive(Debug, Clone, PartialEq, PartialOrd)]
66#[repr(C)]
67pub struct InlineTextLayout {
68 pub lines: InlineTextLineVec,
69 pub content_size: LogicalSize,
70}
71
72impl_vec!(
73 InlineTextLayout,
74 InlineTextLayoutVec,
75 InlineTextLayoutVecDestructor
76);
77impl_vec_clone!(
78 InlineTextLayout,
79 InlineTextLayoutVec,
80 InlineTextLayoutVecDestructor
81);
82impl_vec_debug!(InlineTextLayout, InlineTextLayoutVec);
83impl_vec_partialeq!(InlineTextLayout, InlineTextLayoutVec);
84impl_vec_partialord!(InlineTextLayout, InlineTextLayoutVec);
85
86#[derive(Debug, Clone, PartialEq, PartialOrd)]
89#[repr(C)]
90pub struct InlineTextLine {
91 pub bounds: LogicalRect,
92 pub word_start: usize,
94 pub word_end: usize,
96}
97
98impl_vec!(
99 InlineTextLine,
100 InlineTextLineVec,
101 InlineTextLineVecDestructor
102);
103impl_vec_clone!(
104 InlineTextLine,
105 InlineTextLineVec,
106 InlineTextLineVecDestructor
107);
108impl_vec_mut!(InlineTextLine, InlineTextLineVec);
109impl_vec_debug!(InlineTextLine, InlineTextLineVec);
110impl_vec_partialeq!(InlineTextLine, InlineTextLineVec);
111impl_vec_partialord!(InlineTextLine, InlineTextLineVec);
112
113impl InlineTextLine {
114 pub const fn new(bounds: LogicalRect, word_start: usize, word_end: usize) -> Self {
115 Self {
116 bounds,
117 word_start,
118 word_end,
119 }
120 }
121}
122
123impl InlineTextLayout {
124 #[inline]
125 pub fn get_leading(&self) -> f32 {
126 match self.lines.as_ref().first() {
127 None => 0.0,
128 Some(s) => s.bounds.origin.x as f32,
129 }
130 }
131
132 #[inline]
133 pub fn get_trailing(&self) -> f32 {
134 match self.lines.as_ref().first() {
135 None => 0.0,
136 Some(s) => (s.bounds.origin.x + s.bounds.size.width) as f32,
137 }
138 }
139
140 pub fn align_children_horizontal(
142 &mut self,
143 parent_size: &LogicalSize,
144 horizontal_alignment: StyleTextAlign,
145 ) {
146 let shift_multiplier = match calculate_horizontal_shift_multiplier(horizontal_alignment) {
147 None => return,
148 Some(s) => s,
149 };
150
151 for line in self.lines.as_mut().iter_mut() {
152 line.bounds.origin.x += shift_multiplier * (parent_size.width - line.bounds.size.width);
153 }
154 }
155
156 pub fn align_children_vertical_in_parent_bounds(
158 &mut self,
159 parent_size: &LogicalSize,
160 vertical_alignment: StyleVerticalAlign,
161 ) {
162 let shift_multiplier = match calculate_vertical_shift_multiplier(vertical_alignment) {
163 None => return,
164 Some(s) => s,
165 };
166
167 let glyphs_vertical_bottom = self
168 .lines
169 .as_ref()
170 .last()
171 .map(|l| l.bounds.origin.y)
172 .unwrap_or(0.0);
173 let vertical_shift = (parent_size.height - glyphs_vertical_bottom) * shift_multiplier;
174
175 for line in self.lines.as_mut().iter_mut() {
176 line.bounds.origin.y += vertical_shift;
177 }
178 }
179}
180
181#[inline]
182pub fn calculate_horizontal_shift_multiplier(horizontal_alignment: StyleTextAlign) -> Option<f32> {
183 use azul_css::StyleTextAlign::*;
184 match horizontal_alignment {
185 Left => None,
186 Center => Some(0.5), Right => Some(1.0), }
189}
190
191#[inline]
192pub fn calculate_vertical_shift_multiplier(vertical_alignment: StyleVerticalAlign) -> Option<f32> {
193 use azul_css::StyleVerticalAlign::*;
194 match vertical_alignment {
195 Top => None,
196 Center => Some(0.5), Bottom => Some(1.0), }
199}
200
201#[derive(Clone, Copy, Eq, Hash, PartialEq, Ord, PartialOrd)]
202#[repr(C)]
203pub struct ExternalScrollId(pub u64, pub PipelineId);
204
205impl ::core::fmt::Display for ExternalScrollId {
206 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
207 write!(f, "ExternalScrollId({})", self.0)
208 }
209}
210
211impl ::core::fmt::Debug for ExternalScrollId {
212 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
213 write!(f, "{}", self)
214 }
215}
216
217#[derive(Debug, Default, Clone, PartialEq, PartialOrd)]
218pub struct ScrolledNodes {
219 pub overflowing_nodes: BTreeMap<NodeHierarchyItemId, OverflowingScrollNode>,
220 pub clip_nodes: BTreeMap<NodeId, LogicalSize>,
223 pub tags_to_node_ids: BTreeMap<ScrollTagId, NodeHierarchyItemId>,
224}
225
226#[derive(Debug, Copy, Clone, PartialEq, PartialOrd)]
227pub struct OverflowingScrollNode {
228 pub parent_rect: LogicalRect,
229 pub child_rect: LogicalRect,
230 pub virtual_child_rect: LogicalRect,
231 pub parent_external_scroll_id: ExternalScrollId,
232 pub parent_dom_hash: DomNodeHash,
233 pub scroll_tag_id: ScrollTagId,
234}
235
236impl Default for OverflowingScrollNode {
237 fn default() -> Self {
238 use crate::dom::TagId;
239 Self {
240 parent_rect: LogicalRect::zero(),
241 child_rect: LogicalRect::zero(),
242 virtual_child_rect: LogicalRect::zero(),
243 parent_external_scroll_id: ExternalScrollId(0, PipelineId::DUMMY),
244 parent_dom_hash: DomNodeHash(0),
245 scroll_tag_id: ScrollTagId(TagId(0)),
246 }
247 }
248}
249
250#[derive(Debug, Copy, Clone, PartialEq)]
251pub enum WhConstraint {
252 Between(f32, f32),
254 EqualTo(f32),
256 Unconstrained,
258}
259
260impl Default for WhConstraint {
261 fn default() -> Self {
262 WhConstraint::Unconstrained
263 }
264}
265
266impl WhConstraint {
267 pub fn min_needed_space(&self) -> Option<f32> {
270 use self::WhConstraint::*;
271 match self {
272 Between(min, _) => Some(*min),
273 EqualTo(exact) => Some(*exact),
274 Unconstrained => None,
275 }
276 }
277
278 pub fn max_available_space(&self) -> Option<f32> {
281 use self::WhConstraint::*;
282 match self {
283 Between(_, max) => Some(*max),
284 EqualTo(exact) => Some(*exact),
285 Unconstrained => None,
286 }
287 }
288
289 pub fn is_fixed_constraint(&self) -> bool {
291 use self::WhConstraint::*;
292 match self {
293 EqualTo(_) => true,
294 _ => false,
295 }
296 }
297
298 pub fn calculate_from_relative_parent(&self, relative_parent_width: f32) -> f32 {
301 match self {
302 WhConstraint::EqualTo(e) => *e,
303 WhConstraint::Between(min, max) => relative_parent_width.max(*min).min(*max),
304 WhConstraint::Unconstrained => relative_parent_width,
305 }
306 }
307}
308
309#[derive(Debug, Default, Copy, Clone, PartialEq)]
310pub struct WidthCalculatedRect {
311 pub preferred_width: WhConstraint,
312
313 pub margin_right: Option<CssPropertyValue<LayoutMarginRight>>,
314 pub margin_left: Option<CssPropertyValue<LayoutMarginLeft>>,
315
316 pub padding_right: Option<CssPropertyValue<LayoutPaddingRight>>,
317 pub padding_left: Option<CssPropertyValue<LayoutPaddingLeft>>,
318
319 pub border_right: Option<CssPropertyValue<LayoutBorderRightWidth>>,
320 pub border_left: Option<CssPropertyValue<LayoutBorderLeftWidth>>,
321
322 pub box_sizing: LayoutBoxSizing,
323
324 pub left: Option<CssPropertyValue<LayoutLeft>>,
325 pub right: Option<CssPropertyValue<LayoutRight>>,
326
327 pub flex_grow_px: f32,
328 pub min_inner_size_px: f32,
329}
330
331impl WidthCalculatedRect {
332 pub fn overflow_width(&self) -> f32 {
333 if !self.flex_grow_px.is_sign_positive() {
334 self.min_inner_size_px
335 } else {
336 self.min_inner_size_px + self.flex_grow_px
337 }
338 }
339
340 pub fn get_border_left(&self, percent_resolve: f32) -> f32 {
341 self.border_left
342 .as_ref()
343 .and_then(|p| {
344 p.get_property()
345 .map(|px| px.inner.to_pixels(percent_resolve))
346 })
347 .unwrap_or(0.0)
348 }
349
350 pub fn get_border_right(&self, percent_resolve: f32) -> f32 {
351 self.border_right
352 .as_ref()
353 .and_then(|p| {
354 p.get_property()
355 .map(|px| px.inner.to_pixels(percent_resolve))
356 })
357 .unwrap_or(0.0)
358 }
359
360 pub fn get_raw_padding_left(&self, percent_resolve: f32) -> f32 {
361 self.padding_left
362 .as_ref()
363 .and_then(|p| {
364 p.get_property()
365 .map(|px| px.inner.to_pixels(percent_resolve))
366 })
367 .unwrap_or(0.0)
368 }
369
370 pub fn get_raw_padding_right(&self, percent_resolve: f32) -> f32 {
371 self.padding_right
372 .as_ref()
373 .and_then(|p| {
374 p.get_property()
375 .map(|px| px.inner.to_pixels(percent_resolve))
376 })
377 .unwrap_or(0.0)
378 }
379
380 pub fn get_padding_left(&self, percent_resolve: f32) -> f32 {
381 self.get_raw_padding_left(percent_resolve) + self.get_border_left(percent_resolve)
382 }
383
384 pub fn get_padding_right(&self, percent_resolve: f32) -> f32 {
385 self.get_raw_padding_right(percent_resolve) + self.get_border_right(percent_resolve)
386 }
387
388 pub fn get_margin_left(&self, percent_resolve: f32) -> f32 {
389 self.margin_left
390 .as_ref()
391 .and_then(|p| {
392 p.get_property()
393 .map(|px| px.inner.to_pixels(percent_resolve))
394 })
395 .unwrap_or(0.0)
396 }
397
398 pub fn get_margin_right(&self, percent_resolve: f32) -> f32 {
399 self.margin_right
400 .as_ref()
401 .and_then(|p| {
402 p.get_property()
403 .map(|px| px.inner.to_pixels(percent_resolve))
404 })
405 .unwrap_or(0.0)
406 }
407
408 pub fn get_flex_basis_horizontal(&self, parent_width: f32) -> f32 {
411 self.min_inner_size_px
412 + self.get_margin_left(parent_width)
413 + self.get_margin_right(parent_width)
414 + self.get_raw_padding_left(parent_width)
415 + self.get_raw_padding_right(parent_width)
416 + self.get_border_left(parent_width)
417 + self.get_border_right(parent_width)
418 }
419
420 pub fn get_horizontal_border(&self, parent_width: f32) -> f32 {
421 self.get_border_left(parent_width) + self.get_border_right(parent_width)
422 }
423
424 pub fn get_horizontal_padding(&self, parent_width: f32) -> f32 {
426 self.get_padding_left(parent_width) + self.get_padding_right(parent_width)
427 }
428
429 pub fn get_horizontal_margin(&self, parent_width: f32) -> f32 {
431 self.get_margin_left(parent_width) + self.get_margin_right(parent_width)
432 }
433
434 pub fn total(&self) -> f32 {
436 self.min_inner_size_px + self.flex_grow_px
437 }
438
439 pub fn solved_result(&self) -> WidthSolvedResult {
440 WidthSolvedResult {
441 min_width: self.min_inner_size_px,
442 space_added: self.flex_grow_px,
443 }
444 }
445}
446
447#[derive(Debug, Default, Copy, Clone, PartialEq)]
448pub struct HeightCalculatedRect {
449 pub preferred_height: WhConstraint,
450
451 pub margin_top: Option<CssPropertyValue<LayoutMarginTop>>,
452 pub margin_bottom: Option<CssPropertyValue<LayoutMarginBottom>>,
453
454 pub padding_top: Option<CssPropertyValue<LayoutPaddingTop>>,
455 pub padding_bottom: Option<CssPropertyValue<LayoutPaddingBottom>>,
456
457 pub border_top: Option<CssPropertyValue<LayoutBorderTopWidth>>,
458 pub border_bottom: Option<CssPropertyValue<LayoutBorderBottomWidth>>,
459
460 pub top: Option<CssPropertyValue<LayoutTop>>,
461 pub bottom: Option<CssPropertyValue<LayoutBottom>>,
462
463 pub box_sizing: LayoutBoxSizing,
464
465 pub flex_grow_px: f32,
466 pub min_inner_size_px: f32,
467}
468
469impl HeightCalculatedRect {
470 pub fn overflow_height(&self) -> f32 {
471 if !self.flex_grow_px.is_sign_positive() {
472 self.min_inner_size_px
473 } else {
474 self.min_inner_size_px + self.flex_grow_px
475 }
476 }
477
478 pub fn get_border_top(&self, percent_resolve: f32) -> f32 {
479 self.border_top
480 .as_ref()
481 .and_then(|p| {
482 p.get_property()
483 .map(|px| px.inner.to_pixels(percent_resolve))
484 })
485 .unwrap_or(0.0)
486 }
487
488 pub fn get_border_bottom(&self, percent_resolve: f32) -> f32 {
489 self.border_bottom
490 .as_ref()
491 .and_then(|p| {
492 p.get_property()
493 .map(|px| px.inner.to_pixels(percent_resolve))
494 })
495 .unwrap_or(0.0)
496 }
497
498 pub fn get_raw_padding_top(&self, percent_resolve: f32) -> f32 {
499 self.padding_top
500 .as_ref()
501 .and_then(|p| {
502 p.get_property()
503 .map(|px| px.inner.to_pixels(percent_resolve))
504 })
505 .unwrap_or(0.0)
506 }
507
508 pub fn get_raw_padding_bottom(&self, percent_resolve: f32) -> f32 {
509 self.padding_bottom
510 .as_ref()
511 .and_then(|p| {
512 p.get_property()
513 .map(|px| px.inner.to_pixels(percent_resolve))
514 })
515 .unwrap_or(0.0)
516 }
517
518 pub fn get_padding_bottom(&self, percent_resolve: f32) -> f32 {
519 self.get_raw_padding_bottom(percent_resolve) + self.get_border_bottom(percent_resolve)
520 }
521
522 pub fn get_padding_top(&self, percent_resolve: f32) -> f32 {
523 self.get_raw_padding_top(percent_resolve) + self.get_border_top(percent_resolve)
524 }
525
526 pub fn get_margin_top(&self, percent_resolve: f32) -> f32 {
527 self.margin_top
528 .as_ref()
529 .and_then(|p| {
530 p.get_property()
531 .map(|px| px.inner.to_pixels(percent_resolve))
532 })
533 .unwrap_or(0.0)
534 }
535
536 pub fn get_margin_bottom(&self, percent_resolve: f32) -> f32 {
537 self.margin_bottom
538 .as_ref()
539 .and_then(|p| {
540 p.get_property()
541 .map(|px| px.inner.to_pixels(percent_resolve))
542 })
543 .unwrap_or(0.0)
544 }
545
546 pub fn get_flex_basis_vertical(&self, parent_height: f32) -> f32 {
549 self.min_inner_size_px
550 + self.get_margin_top(parent_height)
551 + self.get_margin_bottom(parent_height)
552 + self.get_raw_padding_top(parent_height)
553 + self.get_raw_padding_bottom(parent_height)
554 + self.get_border_top(parent_height)
555 + self.get_border_bottom(parent_height)
556 }
557
558 pub fn get_vertical_padding(&self, parent_height: f32) -> f32 {
560 self.get_padding_top(parent_height) + self.get_padding_bottom(parent_height)
561 }
562
563 pub fn get_vertical_border(&self, parent_height: f32) -> f32 {
565 self.get_border_top(parent_height) + self.get_border_bottom(parent_height)
566 }
567
568 pub fn get_vertical_margin(&self, parent_height: f32) -> f32 {
570 self.get_margin_top(parent_height) + self.get_margin_bottom(parent_height)
571 }
572
573 pub fn total(&self) -> f32 {
575 self.min_inner_size_px + self.flex_grow_px
576 }
577
578 pub fn solved_result(&self) -> HeightSolvedResult {
580 HeightSolvedResult {
581 min_height: self.min_inner_size_px,
582 space_added: self.flex_grow_px,
583 }
584 }
585}
586
587#[derive(Debug, Copy, Clone, PartialEq)]
588pub struct WidthSolvedResult {
589 pub min_width: f32,
590 pub space_added: f32,
591}
592
593#[derive(Debug, Copy, Clone, PartialEq)]
594pub struct HeightSolvedResult {
595 pub min_height: f32,
596 pub space_added: f32,
597}
598
599#[repr(transparent)]
600#[derive(Debug, Copy, Clone, PartialEq, PartialOrd)]
601pub struct HorizontalSolvedPosition(pub f32);
602
603#[repr(transparent)]
604#[derive(Debug, Copy, Clone, PartialEq, PartialOrd)]
605pub struct VerticalSolvedPosition(pub f32);
606
607pub struct LayoutResult {
608 pub dom_id: DomId,
609 pub parent_dom_id: Option<DomId>,
610 pub styled_dom: StyledDom,
611 pub root_size: LayoutSize,
612 pub root_position: LayoutPoint,
613 pub preferred_widths: NodeDataContainer<Option<f32>>,
614 pub preferred_heights: NodeDataContainer<Option<f32>>,
615 pub width_calculated_rects: NodeDataContainer<WidthCalculatedRect>, pub height_calculated_rects: NodeDataContainer<HeightCalculatedRect>, pub solved_pos_x: NodeDataContainer<HorizontalSolvedPosition>,
620 pub solved_pos_y: NodeDataContainer<VerticalSolvedPosition>,
621 pub layout_flex_grows: NodeDataContainer<f32>,
622 pub layout_displays: NodeDataContainer<CssPropertyValue<LayoutDisplay>>,
623 pub layout_positions: NodeDataContainer<LayoutPosition>,
624 pub layout_flex_directions: NodeDataContainer<LayoutFlexDirection>,
625 pub layout_justify_contents: NodeDataContainer<LayoutJustifyContent>,
626 pub rects: NodeDataContainer<PositionedRectangle>, pub words_cache: BTreeMap<NodeId, Words>,
628 pub shaped_words_cache: BTreeMap<NodeId, ShapedWords>,
629 pub positioned_words_cache: BTreeMap<NodeId, (WordPositions, FontInstanceKey)>,
630 pub scrollable_nodes: ScrolledNodes,
631 pub iframe_mapping: BTreeMap<NodeId, DomId>,
632 pub gpu_value_cache: GpuValueCache,
633}
634
635impl fmt::Debug for LayoutResult {
636 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
637 write!(
638 f,
639 "LayoutResult {{
640 dom_id: {},
641 bounds: {:?} @ {:?},
642 styled_dom (len = {}): {:#?},
643 preferred_widths(len = {}),
644 preferred_heights(len = {}),
645 width_calculated_rects(len = {}),
646 height_calculated_rects(len = {}),
647 solved_pos_x(len = {}),
648 solved_pos_y(len = {}),
649 layout_flex_grows(len = {}),
650 layout_displays(len = {}),
651 layout_positions(len = {}),
652 layout_flex_directions(len = {}),
653 layout_justify_contents(len = {}),
654 rects(len = {}),
655 words_cache(len = {}),
656 shaped_words_cache(len = {}),
657 positioned_words_cache(len = {}),
658 scrollable_nodes: {:#?},
659 iframe_mapping(len = {}): {:#?},
660 gpu_value_cache: {:#?},
661 }}",
662 self.dom_id.inner,
663 self.root_size,
664 self.root_position,
665 self.styled_dom.node_hierarchy.len(),
666 self.styled_dom,
667 self.preferred_widths.len(),
668 self.preferred_heights.len(),
669 self.width_calculated_rects.len(),
670 self.height_calculated_rects.len(),
671 self.solved_pos_x.len(),
672 self.solved_pos_y.len(),
673 self.layout_flex_grows.len(),
674 self.layout_displays.len(),
675 self.layout_positions.len(),
676 self.layout_flex_directions.len(),
677 self.layout_justify_contents.len(),
678 self.rects.len(),
679 self.words_cache.len(),
680 self.shaped_words_cache.len(),
681 self.positioned_words_cache.len(),
682 self.scrollable_nodes,
683 self.iframe_mapping.len(),
684 self.iframe_mapping,
685 self.gpu_value_cache,
686 )
687 }
688}
689
690pub struct QuickResizeResult {
691 pub gpu_event_changes: GpuEventChanges,
692 pub updated_images: Vec<UpdateImageResult>,
693 pub resized_nodes: BTreeMap<DomId, Vec<NodeId>>,
694}
695
696impl LayoutResult {
697 pub fn get_bounds(&self) -> LayoutRect {
698 LayoutRect::new(self.root_position, self.root_size)
699 }
700
701 pub fn get_cached_display_list(
702 document_id: &DocumentId,
703 dom_id: DomId,
704 epoch: Epoch,
705 layout_results: &[LayoutResult],
706 full_window_state: &FullWindowState,
707 gl_texture_cache: &GlTextureCache,
708 renderer_resources: &RendererResources,
709 image_cache: &ImageCache,
710 ) -> CachedDisplayList {
711 use crate::display_list::{
712 displaylist_handle_rect, push_rectangles_into_displaylist, DisplayListFrame,
713 DisplayListMsg, DisplayListParametersRef, LayoutRectContent, RectBackground,
714 };
715
716 let layout_result = match layout_results.get(dom_id.inner) {
717 Some(s) => s,
718 None => return CachedDisplayList::empty(),
719 };
720
721 let rects_in_rendering_order = layout_result.styled_dom.get_rects_in_rendering_order();
722 let referenced_content = DisplayListParametersRef {
723 dom_id,
724 document_id,
725 epoch,
726 full_window_state,
727 layout_results,
728 gl_texture_cache,
729 renderer_resources,
730 image_cache,
731 };
732
733 let root_width =
734 layout_result.width_calculated_rects.as_ref()[NodeId::ZERO].overflow_width();
735 let root_height =
736 layout_result.height_calculated_rects.as_ref()[NodeId::ZERO].overflow_height();
737 let root_size = LogicalSize::new(root_width, root_height);
738
739 let mut root_content = displaylist_handle_rect(
740 rects_in_rendering_order.root.into_crate_internal().unwrap(),
741 &referenced_content,
742 )
743 .unwrap_or(DisplayListMsg::Frame(DisplayListFrame::root(
744 LayoutSize::zero(),
745 LayoutPoint::zero(),
746 )));
747
748 let children = rects_in_rendering_order
749 .children
750 .as_ref()
751 .iter()
752 .filter_map(|child_content_group| {
753 push_rectangles_into_displaylist(child_content_group, &referenced_content)
754 })
755 .collect();
756
757 root_content.append_children(children);
758
759 let mut dl = CachedDisplayList {
760 root: root_content,
761 root_size,
762 };
763
764 if dl.root.is_content_empty() {
767 dl.root.push_content(LayoutRectContent::Background {
768 content: RectBackground::Color(full_window_state.background_color),
769 size: None,
770 offset: None,
771 repeat: None,
772 });
773 }
774
775 dl
776 }
777
778 #[must_use]
783 pub fn do_quick_resize(
784 id_namespace: IdNamespace,
785 document_id: DocumentId,
786 epoch: Epoch,
787 dom_id: DomId,
788 image_cache: &ImageCache,
789 gl_context: &OptionGlContextPtr,
790 layout_results: &mut [LayoutResult],
791 gl_texture_cache: &mut GlTextureCache,
792 renderer_resources: &mut RendererResources,
793 callbacks: &RenderCallbacks,
794 relayout_fn: RelayoutFn,
795 fc_cache: &FcFontCache,
796 window_size: &WindowSize,
797 window_theme: WindowTheme,
798 ) -> QuickResizeResult {
799 let dom_bounds = LogicalRect::new(LogicalPosition::zero(), window_size.dimensions);
800 let mut dom_ids_to_resize = vec![(dom_id, dom_bounds)];
801 let mut gpu_event_changes = GpuEventChanges::default();
802 let mut rsn = BTreeMap::new(); loop {
805 let mut new_dom_ids_to_resize = Vec::new();
806
807 for (dom_id, new_size) in dom_ids_to_resize.iter() {
808 let layout_size = new_size.to_layout_rect();
809
810 let mut resized_nodes = (relayout_fn)(
812 *dom_id,
813 layout_size,
814 &mut layout_results[dom_id.inner],
815 image_cache,
816 renderer_resources,
817 &document_id,
818 None, None, );
821
822 rsn.insert(*dom_id, resized_nodes.resized_nodes.clone());
823
824 gpu_event_changes.merge(&mut resized_nodes.gpu_key_changes);
825
826 for node_id in resized_nodes.resized_nodes.into_iter() {
827 let iframe_dom_id =
828 match layout_results[dom_id.inner].iframe_mapping.get(&node_id) {
829 Some(dom_id) => *dom_id,
830 None => continue,
831 };
832
833 let iframe_rect_relative_to_parent = LayoutRect {
834 origin: layout_results[iframe_dom_id.inner].root_position,
835 size: layout_results[iframe_dom_id.inner].root_size,
836 };
837
838 let iframe_needs_to_be_invoked =
839 !layout_size.contains_rect(&iframe_rect_relative_to_parent);
840
841 if !iframe_needs_to_be_invoked {
842 continue; }
844
845 let iframe_return: IFrameCallbackReturn = {
846 let layout_result = &mut layout_results[dom_id.inner];
847 let mut node_data_mut =
848 layout_result.styled_dom.node_data.as_container_mut();
849 let mut node = &mut node_data_mut[node_id];
850 let iframe_node = match node.get_iframe_node() {
851 Some(iframe_node) => iframe_node,
852 None => continue, };
854
855 let hidpi_bounds = HidpiAdjustedBounds::from_bounds(
857 layout_size.size,
858 window_size.get_hidpi_factor(),
859 );
860 let scroll_node = layout_result
861 .scrollable_nodes
862 .overflowing_nodes
863 .get(&NodeHierarchyItemId::from_crate_internal(Some(node_id)))
864 .cloned()
865 .unwrap_or_default();
866
867 let mut iframe_callback_info = IFrameCallbackInfo::new(
868 fc_cache,
869 image_cache,
870 window_theme,
871 hidpi_bounds,
872 scroll_node.child_rect.size,
875 scroll_node.child_rect.origin - scroll_node.parent_rect.origin,
877 scroll_node.virtual_child_rect.size,
878 scroll_node.virtual_child_rect.origin - scroll_node.parent_rect.origin,
880 );
881 (iframe_node.callback.cb)(&mut iframe_node.data, &mut iframe_callback_info)
882 };
883
884 layout_results[iframe_dom_id.inner].styled_dom = iframe_return.dom;
888
889 let new_iframe_rect = LogicalRect {
890 origin: LogicalPosition::zero(),
892 size: layout_results[dom_id.inner].rects.as_ref()[node_id].size,
893 };
894
895 let osn = layout_results[dom_id.inner]
898 .scrollable_nodes
899 .overflowing_nodes
900 .entry(NodeHierarchyItemId::from_crate_internal(Some(node_id)))
901 .or_insert_with(|| OverflowingScrollNode::default());
902
903 osn.child_rect = LogicalRect {
904 origin: iframe_return.scroll_offset,
905 size: iframe_return.scroll_size,
906 };
907 osn.virtual_child_rect = LogicalRect {
908 origin: iframe_return.virtual_scroll_offset,
909 size: iframe_return.virtual_scroll_size,
910 };
911
912 new_dom_ids_to_resize.push((iframe_dom_id, new_iframe_rect));
913 }
914 }
915
916 if new_dom_ids_to_resize.is_empty() {
917 break;
918 } else {
919 dom_ids_to_resize = new_dom_ids_to_resize; }
921 }
922
923 let updated_images = Self::resize_images(
924 id_namespace,
925 document_id,
926 epoch,
927 dom_id,
928 image_cache,
929 gl_context,
930 layout_results,
931 gl_texture_cache,
932 renderer_resources,
933 callbacks,
934 relayout_fn,
935 fc_cache,
936 window_size,
937 window_theme,
938 &rsn,
939 );
940
941 QuickResizeResult {
942 gpu_event_changes,
943 updated_images,
944 resized_nodes: rsn,
945 }
946 }
947
948 pub fn resize_images(
949 id_namespace: IdNamespace,
950 document_id: DocumentId,
951 epoch: Epoch,
952 dom_id: DomId,
953 image_cache: &ImageCache,
954 gl_context: &OptionGlContextPtr,
955 layout_results: &mut [LayoutResult],
956 gl_texture_cache: &mut GlTextureCache,
957 renderer_resources: &mut RendererResources,
958 callbacks: &RenderCallbacks,
959 relayout_fn: RelayoutFn,
960 fc_cache: &FcFontCache,
961 window_size: &WindowSize,
962 window_theme: WindowTheme,
963 rsn: &BTreeMap<DomId, Vec<NodeId>>,
964 ) -> Vec<UpdateImageResult> {
965 let mut updated_images = Vec::new();
966
967 for (dom_id, node_ids) in rsn.iter() {
968 for node_id in node_ids.iter() {
969 if let Some(update) = renderer_resources.rerender_image_callback(
970 *dom_id,
971 *node_id,
972 document_id,
973 epoch,
974 id_namespace,
975 gl_context,
976 image_cache,
977 fc_cache,
978 window_size.get_hidpi_factor(),
979 callbacks,
980 layout_results,
981 gl_texture_cache,
982 ) {
983 updated_images.push(update);
984 }
985 }
986 }
987
988 updated_images
989 }
990
991 pub fn scroll_iframes(
994 document_id: &DocumentId,
995 dom_id: DomId,
996 epoch: Epoch,
997 layout_results: &[LayoutResult],
998 full_window_state: &FullWindowState,
999 gl_texture_cache: &GlTextureCache,
1000 renderer_resources: &RendererResources,
1001 image_cache: &ImageCache,
1002 ) {
1003 }
1005}
1006
1007#[derive(Default, Debug, Clone, PartialEq, PartialOrd)]
1008pub struct GpuValueCache {
1009 pub transform_keys: BTreeMap<NodeId, TransformKey>,
1010 pub current_transform_values: BTreeMap<NodeId, ComputedTransform3D>,
1011 pub opacity_keys: BTreeMap<NodeId, OpacityKey>,
1012 pub current_opacity_values: BTreeMap<NodeId, f32>,
1013}
1014
1015#[derive(Debug, Clone, PartialEq, PartialOrd)]
1016pub enum GpuTransformKeyEvent {
1017 Added(NodeId, TransformKey, ComputedTransform3D),
1018 Changed(
1019 NodeId,
1020 TransformKey,
1021 ComputedTransform3D,
1022 ComputedTransform3D,
1023 ),
1024 Removed(NodeId, TransformKey),
1025}
1026
1027#[derive(Debug, Clone, PartialEq, PartialOrd)]
1028pub enum GpuOpacityKeyEvent {
1029 Added(NodeId, OpacityKey, f32),
1030 Changed(NodeId, OpacityKey, f32, f32),
1031 Removed(NodeId, OpacityKey),
1032}
1033
1034#[derive(Default, Debug, Clone, PartialEq, PartialOrd)]
1035pub struct GpuEventChanges {
1036 pub transform_key_changes: Vec<GpuTransformKeyEvent>,
1037 pub opacity_key_changes: Vec<GpuOpacityKeyEvent>,
1038}
1039
1040impl GpuEventChanges {
1041 pub fn empty() -> Self {
1042 Self::default()
1043 }
1044 pub fn is_empty(&self) -> bool {
1045 self.transform_key_changes.is_empty() && self.opacity_key_changes.is_empty()
1046 }
1047 pub fn merge(&mut self, other: &mut Self) {
1048 self.transform_key_changes
1049 .extend(other.transform_key_changes.drain(..));
1050 self.opacity_key_changes
1051 .extend(other.opacity_key_changes.drain(..));
1052 }
1053}
1054
1055#[derive(Default, Debug, Clone, PartialEq, PartialOrd)]
1056pub struct RelayoutChanges {
1057 pub resized_nodes: Vec<NodeId>,
1058 pub gpu_key_changes: GpuEventChanges,
1059}
1060
1061impl RelayoutChanges {
1062 pub const EMPTY: RelayoutChanges = RelayoutChanges {
1063 resized_nodes: Vec::new(),
1064 gpu_key_changes: GpuEventChanges {
1065 transform_key_changes: Vec::new(),
1066 opacity_key_changes: Vec::new(),
1067 },
1068 };
1069
1070 pub fn empty() -> Self {
1071 Self::EMPTY.clone()
1072 }
1073}
1074
1075impl GpuValueCache {
1076 pub fn empty() -> Self {
1077 Self::default()
1078 }
1079
1080 #[must_use]
1081 pub fn synchronize<'a>(
1082 &mut self,
1083 positioned_rects: &NodeDataContainerRef<'a, PositionedRectangle>,
1084 styled_dom: &StyledDom,
1085 ) -> GpuEventChanges {
1086 let css_property_cache = styled_dom.get_css_property_cache();
1087 let node_data = styled_dom.node_data.as_container();
1088 let node_states = styled_dom.styled_nodes.as_container();
1089
1090 let default_transform_origin = StyleTransformOrigin::default();
1091
1092 #[cfg(target_arch = "x86_64")]
1093 unsafe {
1094 if !INITIALIZED.load(AtomicOrdering::SeqCst) {
1095 use core::arch::x86_64::__cpuid;
1096
1097 let mut cpuid = __cpuid(0);
1098 let n_ids = cpuid.eax;
1099
1100 if n_ids > 0 {
1101 cpuid = __cpuid(1);
1103 USE_SSE.store((cpuid.edx & (1_u32 << 25)) != 0, AtomicOrdering::SeqCst);
1104 USE_AVX.store((cpuid.ecx & (1_u32 << 28)) != 0, AtomicOrdering::SeqCst);
1105 }
1106 INITIALIZED.store(true, AtomicOrdering::SeqCst);
1107 }
1108 }
1109
1110 let all_current_transform_events = (0..styled_dom.node_data.len())
1112 .into_iter()
1113 .filter_map(|node_id| {
1114 let node_id = NodeId::new(node_id);
1115 let styled_node_state = &node_states[node_id].state;
1116 let node_data = &node_data[node_id];
1117 let current_transform = css_property_cache
1118 .get_transform(node_data, &node_id, styled_node_state)?
1119 .get_property()
1120 .map(|t| {
1121 let parent_size = positioned_rects[node_id].size;
1122 let transform_origin = css_property_cache.get_transform_origin(
1123 node_data,
1124 &node_id,
1125 styled_node_state,
1126 );
1127 let transform_origin = transform_origin
1128 .as_ref()
1129 .and_then(|o| o.get_property())
1130 .unwrap_or(&default_transform_origin);
1131
1132 ComputedTransform3D::from_style_transform_vec(
1133 t.as_ref(),
1134 transform_origin,
1135 parent_size.width,
1136 parent_size.height,
1137 RotationMode::ForWebRender,
1138 )
1139 });
1140
1141 let existing_transform = self.current_transform_values.get(&node_id);
1142
1143 match (existing_transform, current_transform) {
1144 (None, None) => None, (None, Some(new)) => Some(GpuTransformKeyEvent::Added(
1146 node_id,
1147 TransformKey::unique(),
1148 new,
1149 )),
1150 (Some(old), Some(new)) => Some(GpuTransformKeyEvent::Changed(
1151 node_id,
1152 self.transform_keys.get(&node_id).copied()?,
1153 *old,
1154 new,
1155 )),
1156 (Some(_old), None) => Some(GpuTransformKeyEvent::Removed(
1157 node_id,
1158 self.transform_keys.get(&node_id).copied()?,
1159 )),
1160 }
1161 })
1162 .collect::<Vec<GpuTransformKeyEvent>>();
1163
1164 for event in all_current_transform_events.iter() {
1166 match &event {
1167 GpuTransformKeyEvent::Added(node_id, key, matrix) => {
1168 self.transform_keys.insert(*node_id, *key);
1169 self.current_transform_values.insert(*node_id, *matrix);
1170 }
1171 GpuTransformKeyEvent::Changed(node_id, _key, _old_state, new_state) => {
1172 self.current_transform_values.insert(*node_id, *new_state);
1173 }
1174 GpuTransformKeyEvent::Removed(node_id, _key) => {
1175 self.transform_keys.remove(node_id);
1176 self.current_transform_values.remove(node_id);
1177 }
1178 }
1179 }
1180
1181 let all_current_opacity_events = (0..styled_dom.node_data.len())
1183 .into_iter()
1184 .filter_map(|node_id| {
1185 let node_id = NodeId::new(node_id);
1186 let styled_node_state = &node_states[node_id].state;
1187 let node_data = &node_data[node_id];
1188 let current_opacity =
1189 css_property_cache.get_opacity(node_data, &node_id, styled_node_state)?;
1190 let current_opacity = current_opacity.get_property();
1191 let existing_opacity = self.current_opacity_values.get(&node_id);
1192
1193 match (existing_opacity, current_opacity) {
1194 (None, None) => None, (None, Some(new)) => Some(GpuOpacityKeyEvent::Added(
1196 node_id,
1197 OpacityKey::unique(),
1198 new.inner.normalized(),
1199 )),
1200 (Some(old), Some(new)) => Some(GpuOpacityKeyEvent::Changed(
1201 node_id,
1202 self.opacity_keys.get(&node_id).copied()?,
1203 *old,
1204 new.inner.normalized(),
1205 )),
1206 (Some(_old), None) => Some(GpuOpacityKeyEvent::Removed(
1207 node_id,
1208 self.opacity_keys.get(&node_id).copied()?,
1209 )),
1210 }
1211 })
1212 .collect::<Vec<GpuOpacityKeyEvent>>();
1213
1214 for event in all_current_opacity_events.iter() {
1216 match &event {
1217 GpuOpacityKeyEvent::Added(node_id, key, opacity) => {
1218 self.opacity_keys.insert(*node_id, *key);
1219 self.current_opacity_values.insert(*node_id, *opacity);
1220 }
1221 GpuOpacityKeyEvent::Changed(node_id, _key, _old_state, new_state) => {
1222 self.current_opacity_values.insert(*node_id, *new_state);
1223 }
1224 GpuOpacityKeyEvent::Removed(node_id, _key) => {
1225 self.opacity_keys.remove(node_id);
1226 self.current_opacity_values.remove(node_id);
1227 }
1228 }
1229 }
1230
1231 GpuEventChanges {
1232 transform_key_changes: all_current_transform_events,
1233 opacity_key_changes: all_current_opacity_events,
1234 }
1235 }
1236}
1237
1238#[derive(Debug, Clone, PartialEq, PartialOrd)]
1239pub struct HitTest {
1240 pub regular_hit_test_nodes: BTreeMap<NodeId, HitTestItem>,
1241 pub scroll_hit_test_nodes: BTreeMap<NodeId, ScrollHitTestItem>,
1242}
1243
1244impl HitTest {
1245 pub fn empty() -> Self {
1246 Self {
1247 regular_hit_test_nodes: BTreeMap::new(),
1248 scroll_hit_test_nodes: BTreeMap::new(),
1249 }
1250 }
1251 pub fn is_empty(&self) -> bool {
1252 self.regular_hit_test_nodes.is_empty() && self.scroll_hit_test_nodes.is_empty()
1253 }
1254}
1255
1256#[derive(Debug, Clone, PartialEq, PartialOrd, Default)]
1258pub struct TextLayoutOptions {
1259 pub font_size_px: PixelValue,
1261 pub line_height: Option<f32>,
1263 pub letter_spacing: Option<PixelValue>,
1265 pub word_spacing: Option<PixelValue>,
1267 pub tab_width: Option<f32>,
1270 pub max_horizontal_width: Option<f32>,
1273 pub leading: Option<f32>,
1276 pub holes: Vec<LayoutRect>,
1281}
1282
1283#[derive(Debug, Clone, PartialEq, PartialOrd, Default)]
1286#[repr(C)]
1287pub struct ResolvedTextLayoutOptions {
1288 pub font_size_px: f32,
1290 pub line_height: OptionF32,
1292 pub letter_spacing: OptionF32,
1294 pub word_spacing: OptionF32,
1296 pub tab_width: OptionF32,
1299 pub max_horizontal_width: OptionF32,
1302 pub leading: OptionF32,
1305 pub holes: LogicalRectVec,
1310}
1311
1312impl_option!(
1313 ResolvedTextLayoutOptions,
1314 OptionResolvedTextLayoutOptions,
1315 copy = false,
1316 [Debug, Clone, PartialEq, PartialOrd]
1317);
1318
1319#[derive(Debug, Default, Copy, Clone, PartialEq, PartialOrd)]
1320#[repr(C)]
1321pub struct ResolvedOffsets {
1322 pub top: f32,
1323 pub left: f32,
1324 pub right: f32,
1325 pub bottom: f32,
1326}
1327
1328impl ResolvedOffsets {
1329 pub const fn zero() -> Self {
1330 Self {
1331 top: 0.0,
1332 left: 0.0,
1333 right: 0.0,
1334 bottom: 0.0,
1335 }
1336 }
1337 pub fn total_vertical(&self) -> f32 {
1338 self.top + self.bottom
1339 }
1340 pub fn total_horizontal(&self) -> f32 {
1341 self.left + self.right
1342 }
1343}
1344
1345#[derive(Debug, Clone, PartialEq, PartialOrd)]
1346pub struct PositionedRectangle {
1347 pub size: LogicalSize,
1349 pub position: PositionInfo,
1351 pub padding: ResolvedOffsets,
1353 pub margin: ResolvedOffsets,
1355 pub border_widths: ResolvedOffsets,
1357 pub box_shadow: StyleBoxShadowOffsets,
1359 pub box_sizing: LayoutBoxSizing,
1361 pub overflow_x: LayoutOverflow,
1363 pub overflow_y: LayoutOverflow,
1365 pub resolved_text_layout_options: Option<(ResolvedTextLayoutOptions, InlineTextLayout)>,
1369}
1370
1371impl Default for PositionedRectangle {
1372 fn default() -> Self {
1373 PositionedRectangle {
1374 size: LogicalSize::zero(),
1375 overflow_x: LayoutOverflow::default(),
1376 overflow_y: LayoutOverflow::default(),
1377 position: PositionInfo::Static(PositionInfoInner {
1378 x_offset: 0.0,
1379 y_offset: 0.0,
1380 static_x_offset: 0.0,
1381 static_y_offset: 0.0,
1382 }),
1383 padding: ResolvedOffsets::zero(),
1384 margin: ResolvedOffsets::zero(),
1385 border_widths: ResolvedOffsets::zero(),
1386 box_shadow: StyleBoxShadowOffsets::default(),
1387 box_sizing: LayoutBoxSizing::default(),
1388 resolved_text_layout_options: None,
1389 }
1390 }
1391}
1392
1393impl PositionedRectangle {
1394 #[inline]
1395 pub fn get_approximate_static_bounds(&self) -> LayoutRect {
1396 LayoutRect::new(self.get_static_offset(), self.get_content_size())
1397 }
1398
1399 #[inline]
1401 fn get_content_size(&self) -> LayoutSize {
1402 LayoutSize::new(
1403 libm::roundf(self.size.width) as isize,
1404 libm::roundf(self.size.height) as isize,
1405 )
1406 }
1407
1408 #[inline]
1409 fn get_logical_static_offset(&self) -> LogicalPosition {
1410 match self.position {
1411 PositionInfo::Static(p)
1412 | PositionInfo::Fixed(p)
1413 | PositionInfo::Absolute(p)
1414 | PositionInfo::Relative(p) => {
1415 LogicalPosition::new(p.static_x_offset, p.static_y_offset)
1416 }
1417 }
1418 }
1419
1420 #[inline]
1421 fn get_logical_relative_offset(&self) -> LogicalPosition {
1422 match self.position {
1423 PositionInfo::Static(p)
1424 | PositionInfo::Fixed(p)
1425 | PositionInfo::Absolute(p)
1426 | PositionInfo::Relative(p) => LogicalPosition::new(p.x_offset, p.y_offset),
1427 }
1428 }
1429
1430 #[inline]
1431 fn get_static_offset(&self) -> LayoutPoint {
1432 match self.position {
1433 PositionInfo::Static(p)
1434 | PositionInfo::Fixed(p)
1435 | PositionInfo::Absolute(p)
1436 | PositionInfo::Relative(p) => LayoutPoint::new(
1437 libm::roundf(p.static_x_offset) as isize,
1438 libm::roundf(p.static_y_offset) as isize,
1439 ),
1440 }
1441 }
1442
1443 #[inline]
1445 pub fn get_background_bounds(&self) -> (LogicalSize, PositionInfo) {
1446 use crate::ui_solver::PositionInfo::*;
1447
1448 let b_size = LogicalSize {
1449 width: self.size.width
1450 + self.padding.total_horizontal()
1451 + self.border_widths.total_horizontal(),
1452 height: self.size.height
1453 + self.padding.total_vertical()
1454 + self.border_widths.total_vertical(),
1455 };
1456
1457 let x_offset_add = 0.0 - self.padding.left - self.border_widths.left;
1458 let y_offset_add = 0.0 - self.padding.top - self.border_widths.top;
1459
1460 let b_position = match self.position {
1461 Static(PositionInfoInner {
1462 x_offset,
1463 y_offset,
1464 static_x_offset,
1465 static_y_offset,
1466 }) => Static(PositionInfoInner {
1467 x_offset: x_offset + x_offset_add,
1468 y_offset: y_offset + y_offset_add,
1469 static_x_offset,
1470 static_y_offset,
1471 }),
1472 Fixed(PositionInfoInner {
1473 x_offset,
1474 y_offset,
1475 static_x_offset,
1476 static_y_offset,
1477 }) => Fixed(PositionInfoInner {
1478 x_offset: x_offset + x_offset_add,
1479 y_offset: y_offset + y_offset_add,
1480 static_x_offset,
1481 static_y_offset,
1482 }),
1483 Relative(PositionInfoInner {
1484 x_offset,
1485 y_offset,
1486 static_x_offset,
1487 static_y_offset,
1488 }) => Relative(PositionInfoInner {
1489 x_offset: x_offset + x_offset_add,
1490 y_offset: y_offset + y_offset_add,
1491 static_x_offset,
1492 static_y_offset,
1493 }),
1494 Absolute(PositionInfoInner {
1495 x_offset,
1496 y_offset,
1497 static_x_offset,
1498 static_y_offset,
1499 }) => Absolute(PositionInfoInner {
1500 x_offset: x_offset + x_offset_add,
1501 y_offset: y_offset + y_offset_add,
1502 static_x_offset,
1503 static_y_offset,
1504 }),
1505 };
1506
1507 (b_size, b_position)
1508 }
1509
1510 #[inline]
1511 pub fn get_margin_box_width(&self) -> f32 {
1512 self.size.width
1513 + self.padding.total_horizontal()
1514 + self.border_widths.total_horizontal()
1515 + self.margin.total_horizontal()
1516 }
1517
1518 #[inline]
1519 pub fn get_margin_box_height(&self) -> f32 {
1520 self.size.height
1521 + self.padding.total_vertical()
1522 + self.border_widths.total_vertical()
1523 + self.margin.total_vertical()
1524 }
1525
1526 #[inline]
1527 pub fn get_left_leading(&self) -> f32 {
1528 self.margin.left + self.padding.left + self.border_widths.left
1529 }
1530
1531 #[inline]
1532 pub fn get_top_leading(&self) -> f32 {
1533 self.margin.top + self.padding.top + self.border_widths.top
1534 }
1535}
1536
1537#[derive(Debug, Default, Clone, PartialEq, PartialOrd)]
1538pub struct OverflowInfo {
1539 pub overflow_x: DirectionalOverflowInfo,
1540 pub overflow_y: DirectionalOverflowInfo,
1541}
1542
1543#[derive(Debug, Clone, PartialEq, PartialOrd)]
1548pub enum DirectionalOverflowInfo {
1549 Scroll { amount: Option<isize> },
1550 Auto { amount: Option<isize> },
1551 Hidden { amount: Option<isize> },
1552 Visible { amount: Option<isize> },
1553}
1554
1555impl Default for DirectionalOverflowInfo {
1556 fn default() -> DirectionalOverflowInfo {
1557 DirectionalOverflowInfo::Auto { amount: None }
1558 }
1559}
1560
1561impl DirectionalOverflowInfo {
1562 #[inline]
1563 pub fn get_amount(&self) -> Option<isize> {
1564 match self {
1565 DirectionalOverflowInfo::Scroll { amount: Some(s) }
1566 | DirectionalOverflowInfo::Auto { amount: Some(s) }
1567 | DirectionalOverflowInfo::Hidden { amount: Some(s) }
1568 | DirectionalOverflowInfo::Visible { amount: Some(s) } => Some(*s),
1569 _ => None,
1570 }
1571 }
1572
1573 #[inline]
1574 pub fn is_negative(&self) -> bool {
1575 match self {
1576 DirectionalOverflowInfo::Scroll { amount: Some(s) }
1577 | DirectionalOverflowInfo::Auto { amount: Some(s) }
1578 | DirectionalOverflowInfo::Hidden { amount: Some(s) }
1579 | DirectionalOverflowInfo::Visible { amount: Some(s) } => *s < 0_isize,
1580 _ => true, }
1582 }
1583
1584 #[inline]
1585 pub fn is_none(&self) -> bool {
1586 match self {
1587 DirectionalOverflowInfo::Scroll { amount: None }
1588 | DirectionalOverflowInfo::Auto { amount: None }
1589 | DirectionalOverflowInfo::Hidden { amount: None }
1590 | DirectionalOverflowInfo::Visible { amount: None } => true,
1591 _ => false,
1592 }
1593 }
1594}
1595
1596#[derive(Debug, Copy, Clone, PartialEq, PartialOrd)]
1597#[repr(C, u8)]
1598pub enum PositionInfo {
1599 Static(PositionInfoInner),
1600 Fixed(PositionInfoInner),
1601 Absolute(PositionInfoInner),
1602 Relative(PositionInfoInner),
1603}
1604
1605impl PositionInfo {
1606 pub fn translate_vertical(&mut self, offset_amount: f32) {
1609 match self {
1610 PositionInfo::Static(ref mut info)
1611 | PositionInfo::Absolute(ref mut info)
1612 | PositionInfo::Fixed(ref mut info)
1613 | PositionInfo::Relative(ref mut info) => {
1614 info.y_offset += offset_amount;
1615 info.static_y_offset += offset_amount;
1616 }
1617 }
1618 }
1619
1620 pub fn scale_for_dpi(&mut self, scale_factor: f32) {
1621 match self {
1622 PositionInfo::Static(p) => p.scale_for_dpi(scale_factor),
1623 PositionInfo::Fixed(p) => p.scale_for_dpi(scale_factor),
1624 PositionInfo::Absolute(p) => p.scale_for_dpi(scale_factor),
1625 PositionInfo::Relative(p) => p.scale_for_dpi(scale_factor),
1626 }
1627 }
1628}
1629
1630impl_option!(
1631 PositionInfo,
1632 OptionPositionInfo,
1633 [Debug, Copy, Clone, PartialEq, PartialOrd]
1634);
1635
1636#[derive(Debug, Copy, Clone, PartialEq, PartialOrd)]
1637#[repr(C)]
1638pub struct PositionInfoInner {
1639 pub x_offset: f32,
1640 pub y_offset: f32,
1641 pub static_x_offset: f32,
1642 pub static_y_offset: f32,
1643}
1644
1645impl PositionInfoInner {
1646 #[inline]
1647 pub const fn zero() -> Self {
1648 Self {
1649 x_offset: 0.0,
1650 y_offset: 0.0,
1651 static_x_offset: 0.0,
1652 static_y_offset: 0.0,
1653 }
1654 }
1655
1656 pub fn scale_for_dpi(&mut self, scale_factor: f32) {
1657 self.x_offset *= scale_factor;
1658 self.y_offset *= scale_factor;
1659 self.static_x_offset *= scale_factor;
1660 self.static_y_offset *= scale_factor;
1661 }
1662}
1663
1664impl PositionInfo {
1665 #[inline]
1666 pub fn is_positioned(&self) -> bool {
1667 match self {
1668 PositionInfo::Static(_) => false,
1669 PositionInfo::Fixed(_) => true,
1670 PositionInfo::Absolute(_) => true,
1671 PositionInfo::Relative(_) => true,
1672 }
1673 }
1674
1675 #[inline]
1676 pub fn get_relative_offset(&self) -> LogicalPosition {
1677 match self {
1678 PositionInfo::Static(p)
1679 | PositionInfo::Fixed(p)
1680 | PositionInfo::Absolute(p)
1681 | PositionInfo::Relative(p) => LogicalPosition {
1682 x: p.x_offset,
1683 y: p.y_offset,
1684 },
1685 }
1686 }
1687
1688 #[inline]
1689 pub fn get_static_offset(&self) -> LogicalPosition {
1690 match self {
1691 PositionInfo::Static(p)
1692 | PositionInfo::Fixed(p)
1693 | PositionInfo::Absolute(p)
1694 | PositionInfo::Relative(p) => LogicalPosition {
1695 x: p.static_x_offset,
1696 y: p.static_y_offset,
1697 },
1698 }
1699 }
1700}
1701
1702#[derive(Default, Debug, Copy, Clone, PartialEq, PartialOrd)]
1703pub struct StyleBoxShadowOffsets {
1704 pub left: Option<CssPropertyValue<StyleBoxShadow>>,
1705 pub right: Option<CssPropertyValue<StyleBoxShadow>>,
1706 pub top: Option<CssPropertyValue<StyleBoxShadow>>,
1707 pub bottom: Option<CssPropertyValue<StyleBoxShadow>>,
1708}
1709
1710#[derive(Debug, Copy, Clone)]
1717pub enum RotationMode {
1718 ForWebRender,
1719 ForHitTesting,
1720}
1721
1722#[derive(Debug, Copy, Clone, PartialEq, PartialOrd)]
1726#[repr(C)]
1727pub struct ComputedTransform3D {
1728 pub m: [[f32; 4]; 4],
1729}
1730
1731impl ComputedTransform3D {
1732 pub const IDENTITY: Self = Self {
1733 m: [
1734 [1.0, 0.0, 0.0, 0.0],
1735 [0.0, 1.0, 0.0, 0.0],
1736 [0.0, 0.0, 1.0, 0.0],
1737 [0.0, 0.0, 0.0, 1.0],
1738 ],
1739 };
1740
1741 pub const fn new(
1742 m11: f32,
1743 m12: f32,
1744 m13: f32,
1745 m14: f32,
1746 m21: f32,
1747 m22: f32,
1748 m23: f32,
1749 m24: f32,
1750 m31: f32,
1751 m32: f32,
1752 m33: f32,
1753 m34: f32,
1754 m41: f32,
1755 m42: f32,
1756 m43: f32,
1757 m44: f32,
1758 ) -> Self {
1759 Self {
1760 m: [
1761 [m11, m12, m13, m14],
1762 [m21, m22, m23, m24],
1763 [m31, m32, m33, m34],
1764 [m41, m42, m43, m44],
1765 ],
1766 }
1767 }
1768
1769 pub const fn new_2d(m11: f32, m12: f32, m21: f32, m22: f32, m41: f32, m42: f32) -> Self {
1770 Self::new(
1771 m11, m12, 0.0, 0.0, m21, m22, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, m41, m42, 0.0, 1.0,
1772 )
1773 }
1774
1775 pub fn inverse(&self) -> Self {
1777 let det = self.determinant();
1778
1779 let m = ComputedTransform3D::new(
1782 self.m[1][2] * self.m[2][3] * self.m[3][1] - self.m[1][3] * self.m[2][2] * self.m[3][1]
1783 + self.m[1][3] * self.m[2][1] * self.m[3][2]
1784 - self.m[1][1] * self.m[2][3] * self.m[3][2]
1785 - self.m[1][2] * self.m[2][1] * self.m[3][3]
1786 + self.m[1][1] * self.m[2][2] * self.m[3][3],
1787 self.m[0][3] * self.m[2][2] * self.m[3][1]
1788 - self.m[0][2] * self.m[2][3] * self.m[3][1]
1789 - self.m[0][3] * self.m[2][1] * self.m[3][2]
1790 + self.m[0][1] * self.m[2][3] * self.m[3][2]
1791 + self.m[0][2] * self.m[2][1] * self.m[3][3]
1792 - self.m[0][1] * self.m[2][2] * self.m[3][3],
1793 self.m[0][2] * self.m[1][3] * self.m[3][1] - self.m[0][3] * self.m[1][2] * self.m[3][1]
1794 + self.m[0][3] * self.m[1][1] * self.m[3][2]
1795 - self.m[0][1] * self.m[1][3] * self.m[3][2]
1796 - self.m[0][2] * self.m[1][1] * self.m[3][3]
1797 + self.m[0][1] * self.m[1][2] * self.m[3][3],
1798 self.m[0][3] * self.m[1][2] * self.m[2][1]
1799 - self.m[0][2] * self.m[1][3] * self.m[2][1]
1800 - self.m[0][3] * self.m[1][1] * self.m[2][2]
1801 + self.m[0][1] * self.m[1][3] * self.m[2][2]
1802 + self.m[0][2] * self.m[1][1] * self.m[2][3]
1803 - self.m[0][1] * self.m[1][2] * self.m[2][3],
1804 self.m[1][3] * self.m[2][2] * self.m[3][0]
1805 - self.m[1][2] * self.m[2][3] * self.m[3][0]
1806 - self.m[1][3] * self.m[2][0] * self.m[3][2]
1807 + self.m[1][0] * self.m[2][3] * self.m[3][2]
1808 + self.m[1][2] * self.m[2][0] * self.m[3][3]
1809 - self.m[1][0] * self.m[2][2] * self.m[3][3],
1810 self.m[0][2] * self.m[2][3] * self.m[3][0] - self.m[0][3] * self.m[2][2] * self.m[3][0]
1811 + self.m[0][3] * self.m[2][0] * self.m[3][2]
1812 - self.m[0][0] * self.m[2][3] * self.m[3][2]
1813 - self.m[0][2] * self.m[2][0] * self.m[3][3]
1814 + self.m[0][0] * self.m[2][2] * self.m[3][3],
1815 self.m[0][3] * self.m[1][2] * self.m[3][0]
1816 - self.m[0][2] * self.m[1][3] * self.m[3][0]
1817 - self.m[0][3] * self.m[1][0] * self.m[3][2]
1818 + self.m[0][0] * self.m[1][3] * self.m[3][2]
1819 + self.m[0][2] * self.m[1][0] * self.m[3][3]
1820 - self.m[0][0] * self.m[1][2] * self.m[3][3],
1821 self.m[0][2] * self.m[1][3] * self.m[2][0] - self.m[0][3] * self.m[1][2] * self.m[2][0]
1822 + self.m[0][3] * self.m[1][0] * self.m[2][2]
1823 - self.m[0][0] * self.m[1][3] * self.m[2][2]
1824 - self.m[0][2] * self.m[1][0] * self.m[2][3]
1825 + self.m[0][0] * self.m[1][2] * self.m[2][3],
1826 self.m[1][1] * self.m[2][3] * self.m[3][0] - self.m[1][3] * self.m[2][1] * self.m[3][0]
1827 + self.m[1][3] * self.m[2][0] * self.m[3][1]
1828 - self.m[1][0] * self.m[2][3] * self.m[3][1]
1829 - self.m[1][1] * self.m[2][0] * self.m[3][3]
1830 + self.m[1][0] * self.m[2][1] * self.m[3][3],
1831 self.m[0][3] * self.m[2][1] * self.m[3][0]
1832 - self.m[0][1] * self.m[2][3] * self.m[3][0]
1833 - self.m[0][3] * self.m[2][0] * self.m[3][1]
1834 + self.m[0][0] * self.m[2][3] * self.m[3][1]
1835 + self.m[0][1] * self.m[2][0] * self.m[3][3]
1836 - self.m[0][0] * self.m[2][1] * self.m[3][3],
1837 self.m[0][1] * self.m[1][3] * self.m[3][0] - self.m[0][3] * self.m[1][1] * self.m[3][0]
1838 + self.m[0][3] * self.m[1][0] * self.m[3][1]
1839 - self.m[0][0] * self.m[1][3] * self.m[3][1]
1840 - self.m[0][1] * self.m[1][0] * self.m[3][3]
1841 + self.m[0][0] * self.m[1][1] * self.m[3][3],
1842 self.m[0][3] * self.m[1][1] * self.m[2][0]
1843 - self.m[0][1] * self.m[1][3] * self.m[2][0]
1844 - self.m[0][3] * self.m[1][0] * self.m[2][1]
1845 + self.m[0][0] * self.m[1][3] * self.m[2][1]
1846 + self.m[0][1] * self.m[1][0] * self.m[2][3]
1847 - self.m[0][0] * self.m[1][1] * self.m[2][3],
1848 self.m[1][2] * self.m[2][1] * self.m[3][0]
1849 - self.m[1][1] * self.m[2][2] * self.m[3][0]
1850 - self.m[1][2] * self.m[2][0] * self.m[3][1]
1851 + self.m[1][0] * self.m[2][2] * self.m[3][1]
1852 + self.m[1][1] * self.m[2][0] * self.m[3][2]
1853 - self.m[1][0] * self.m[2][1] * self.m[3][2],
1854 self.m[0][1] * self.m[2][2] * self.m[3][0] - self.m[0][2] * self.m[2][1] * self.m[3][0]
1855 + self.m[0][2] * self.m[2][0] * self.m[3][1]
1856 - self.m[0][0] * self.m[2][2] * self.m[3][1]
1857 - self.m[0][1] * self.m[2][0] * self.m[3][2]
1858 + self.m[0][0] * self.m[2][1] * self.m[3][2],
1859 self.m[0][2] * self.m[1][1] * self.m[3][0]
1860 - self.m[0][1] * self.m[1][2] * self.m[3][0]
1861 - self.m[0][2] * self.m[1][0] * self.m[3][1]
1862 + self.m[0][0] * self.m[1][2] * self.m[3][1]
1863 + self.m[0][1] * self.m[1][0] * self.m[3][2]
1864 - self.m[0][0] * self.m[1][1] * self.m[3][2],
1865 self.m[0][1] * self.m[1][2] * self.m[2][0] - self.m[0][2] * self.m[1][1] * self.m[2][0]
1866 + self.m[0][2] * self.m[1][0] * self.m[2][1]
1867 - self.m[0][0] * self.m[1][2] * self.m[2][1]
1868 - self.m[0][1] * self.m[1][0] * self.m[2][2]
1869 + self.m[0][0] * self.m[1][1] * self.m[2][2],
1870 );
1871
1872 m.multiply_scalar(1.0 / det)
1873 }
1874
1875 fn determinant(&self) -> f32 {
1876 self.m[0][3] * self.m[1][2] * self.m[2][1] * self.m[3][0]
1877 - self.m[0][2] * self.m[1][3] * self.m[2][1] * self.m[3][0]
1878 - self.m[0][3] * self.m[1][1] * self.m[2][2] * self.m[3][0]
1879 + self.m[0][1] * self.m[1][3] * self.m[2][2] * self.m[3][0]
1880 + self.m[0][2] * self.m[1][1] * self.m[2][3] * self.m[3][0]
1881 - self.m[0][1] * self.m[1][2] * self.m[2][3] * self.m[3][0]
1882 - self.m[0][3] * self.m[1][2] * self.m[2][0] * self.m[3][1]
1883 + self.m[0][2] * self.m[1][3] * self.m[2][0] * self.m[3][1]
1884 + self.m[0][3] * self.m[1][0] * self.m[2][2] * self.m[3][1]
1885 - self.m[0][0] * self.m[1][3] * self.m[2][2] * self.m[3][1]
1886 - self.m[0][2] * self.m[1][0] * self.m[2][3] * self.m[3][1]
1887 + self.m[0][0] * self.m[1][2] * self.m[2][3] * self.m[3][1]
1888 + self.m[0][3] * self.m[1][1] * self.m[2][0] * self.m[3][2]
1889 - self.m[0][1] * self.m[1][3] * self.m[2][0] * self.m[3][2]
1890 - self.m[0][3] * self.m[1][0] * self.m[2][1] * self.m[3][2]
1891 + self.m[0][0] * self.m[1][3] * self.m[2][1] * self.m[3][2]
1892 + self.m[0][1] * self.m[1][0] * self.m[2][3] * self.m[3][2]
1893 - self.m[0][0] * self.m[1][1] * self.m[2][3] * self.m[3][2]
1894 - self.m[0][2] * self.m[1][1] * self.m[2][0] * self.m[3][3]
1895 + self.m[0][1] * self.m[1][2] * self.m[2][0] * self.m[3][3]
1896 + self.m[0][2] * self.m[1][0] * self.m[2][1] * self.m[3][3]
1897 - self.m[0][0] * self.m[1][2] * self.m[2][1] * self.m[3][3]
1898 - self.m[0][1] * self.m[1][0] * self.m[2][2] * self.m[3][3]
1899 + self.m[0][0] * self.m[1][1] * self.m[2][2] * self.m[3][3]
1900 }
1901
1902 fn multiply_scalar(&self, x: f32) -> Self {
1903 ComputedTransform3D::new(
1904 self.m[0][0] * x,
1905 self.m[0][1] * x,
1906 self.m[0][2] * x,
1907 self.m[0][3] * x,
1908 self.m[1][0] * x,
1909 self.m[1][1] * x,
1910 self.m[1][2] * x,
1911 self.m[1][3] * x,
1912 self.m[2][0] * x,
1913 self.m[2][1] * x,
1914 self.m[2][2] * x,
1915 self.m[2][3] * x,
1916 self.m[3][0] * x,
1917 self.m[3][1] * x,
1918 self.m[3][2] * x,
1919 self.m[3][3] * x,
1920 )
1921 }
1922
1923 pub fn from_style_transform_vec(
1925 t_vec: &[StyleTransform],
1926 transform_origin: &StyleTransformOrigin,
1927 percent_resolve_x: f32,
1928 percent_resolve_y: f32,
1929 rotation_mode: RotationMode,
1930 ) -> Self {
1931 let mut matrix = Self::IDENTITY;
1933 let use_avx =
1934 INITIALIZED.load(AtomicOrdering::SeqCst) && USE_AVX.load(AtomicOrdering::SeqCst);
1935 let use_sse = !use_avx
1936 && INITIALIZED.load(AtomicOrdering::SeqCst)
1937 && USE_SSE.load(AtomicOrdering::SeqCst);
1938
1939 if use_avx {
1940 for t in t_vec.iter() {
1941 #[cfg(target_arch = "x86_64")]
1942 unsafe {
1943 matrix = matrix.then_avx8(&Self::from_style_transform(
1944 t,
1945 transform_origin,
1946 percent_resolve_x,
1947 percent_resolve_y,
1948 rotation_mode,
1949 ));
1950 }
1951 }
1952 } else if use_sse {
1953 for t in t_vec.iter() {
1954 #[cfg(target_arch = "x86_64")]
1955 unsafe {
1956 matrix = matrix.then_sse(&Self::from_style_transform(
1957 t,
1958 transform_origin,
1959 percent_resolve_x,
1960 percent_resolve_y,
1961 rotation_mode,
1962 ));
1963 }
1964 }
1965 } else {
1966 for t in t_vec.iter() {
1968 matrix = matrix.then(&Self::from_style_transform(
1969 t,
1970 transform_origin,
1971 percent_resolve_x,
1972 percent_resolve_y,
1973 rotation_mode,
1974 ));
1975 }
1976 }
1977
1978 matrix
1979 }
1980
1981 pub fn from_style_transform(
1984 t: &StyleTransform,
1985 transform_origin: &StyleTransformOrigin,
1986 percent_resolve_x: f32,
1987 percent_resolve_y: f32,
1988 rotation_mode: RotationMode,
1989 ) -> Self {
1990 use azul_css::StyleTransform::*;
1991 match t {
1992 Matrix(mat2d) => {
1993 let a = mat2d.a.to_pixels(percent_resolve_x);
1994 let b = mat2d.b.to_pixels(percent_resolve_x);
1995 let c = mat2d.c.to_pixels(percent_resolve_x);
1996 let d = mat2d.d.to_pixels(percent_resolve_x);
1997 let tx = mat2d.tx.to_pixels(percent_resolve_x);
1998 let ty = mat2d.ty.to_pixels(percent_resolve_x);
1999
2000 Self::new_2d(a, b, c, d, tx, ty)
2001 }
2002 Matrix3D(mat3d) => {
2003 let m11 = mat3d.m11.to_pixels(percent_resolve_x);
2004 let m12 = mat3d.m12.to_pixels(percent_resolve_x);
2005 let m13 = mat3d.m13.to_pixels(percent_resolve_x);
2006 let m14 = mat3d.m14.to_pixels(percent_resolve_x);
2007 let m21 = mat3d.m21.to_pixels(percent_resolve_x);
2008 let m22 = mat3d.m22.to_pixels(percent_resolve_x);
2009 let m23 = mat3d.m23.to_pixels(percent_resolve_x);
2010 let m24 = mat3d.m24.to_pixels(percent_resolve_x);
2011 let m31 = mat3d.m31.to_pixels(percent_resolve_x);
2012 let m32 = mat3d.m32.to_pixels(percent_resolve_x);
2013 let m33 = mat3d.m33.to_pixels(percent_resolve_x);
2014 let m34 = mat3d.m34.to_pixels(percent_resolve_x);
2015 let m41 = mat3d.m41.to_pixels(percent_resolve_x);
2016 let m42 = mat3d.m42.to_pixels(percent_resolve_x);
2017 let m43 = mat3d.m43.to_pixels(percent_resolve_x);
2018 let m44 = mat3d.m44.to_pixels(percent_resolve_x);
2019
2020 Self::new(
2021 m11, m12, m13, m14, m21, m22, m23, m24, m31, m32, m33, m34, m41, m42, m43, m44,
2022 )
2023 }
2024 Translate(trans2d) => Self::new_translation(
2025 trans2d.x.to_pixels(percent_resolve_x),
2026 trans2d.y.to_pixels(percent_resolve_y),
2027 0.0,
2028 ),
2029 Translate3D(trans3d) => Self::new_translation(
2030 trans3d.x.to_pixels(percent_resolve_x),
2031 trans3d.y.to_pixels(percent_resolve_y),
2032 trans3d.z.to_pixels(percent_resolve_x), ),
2034 TranslateX(trans_x) => {
2035 Self::new_translation(trans_x.to_pixels(percent_resolve_x), 0.0, 0.0)
2036 }
2037 TranslateY(trans_y) => {
2038 Self::new_translation(0.0, trans_y.to_pixels(percent_resolve_y), 0.0)
2039 }
2040 TranslateZ(trans_z) => {
2041 Self::new_translation(0.0, 0.0, trans_z.to_pixels(percent_resolve_x))
2042 } Rotate3D(rot3d) => {
2044 let rotation_origin = (
2045 transform_origin.x.to_pixels(percent_resolve_x),
2046 transform_origin.y.to_pixels(percent_resolve_y),
2047 );
2048 Self::make_rotation(
2049 rotation_origin,
2050 rot3d.angle.to_degrees(),
2051 rot3d.x.normalized(),
2052 rot3d.y.normalized(),
2053 rot3d.z.normalized(),
2054 rotation_mode,
2055 )
2056 }
2057 RotateX(angle_x) => {
2058 let rotation_origin = (
2059 transform_origin.x.to_pixels(percent_resolve_x),
2060 transform_origin.y.to_pixels(percent_resolve_y),
2061 );
2062 Self::make_rotation(
2063 rotation_origin,
2064 angle_x.to_degrees(),
2065 1.0,
2066 0.0,
2067 0.0,
2068 rotation_mode,
2069 )
2070 }
2071 RotateY(angle_y) => {
2072 let rotation_origin = (
2073 transform_origin.x.to_pixels(percent_resolve_x),
2074 transform_origin.y.to_pixels(percent_resolve_y),
2075 );
2076 Self::make_rotation(
2077 rotation_origin,
2078 angle_y.to_degrees(),
2079 0.0,
2080 1.0,
2081 0.0,
2082 rotation_mode,
2083 )
2084 }
2085 Rotate(angle_z) | RotateZ(angle_z) => {
2086 let rotation_origin = (
2087 transform_origin.x.to_pixels(percent_resolve_x),
2088 transform_origin.y.to_pixels(percent_resolve_y),
2089 );
2090 Self::make_rotation(
2091 rotation_origin,
2092 angle_z.to_degrees(),
2093 0.0,
2094 0.0,
2095 1.0,
2096 rotation_mode,
2097 )
2098 }
2099 Scale(scale2d) => Self::new_scale(scale2d.x.normalized(), scale2d.y.normalized(), 1.0),
2100 Scale3D(scale3d) => Self::new_scale(
2101 scale3d.x.normalized(),
2102 scale3d.y.normalized(),
2103 scale3d.z.normalized(),
2104 ),
2105 ScaleX(scale_x) => Self::new_scale(scale_x.normalized(), 1.0, 1.0),
2106 ScaleY(scale_y) => Self::new_scale(1.0, scale_y.normalized(), 1.0),
2107 ScaleZ(scale_z) => Self::new_scale(1.0, 1.0, scale_z.normalized()),
2108 Skew(skew2d) => Self::new_skew(skew2d.x.normalized(), skew2d.y.normalized()),
2109 SkewX(skew_x) => Self::new_skew(skew_x.normalized(), 0.0),
2110 SkewY(skew_y) => Self::new_skew(0.0, skew_y.normalized()),
2111 Perspective(px) => Self::new_perspective(px.to_pixels(percent_resolve_x)),
2112 }
2113 }
2114
2115 #[inline]
2116 pub const fn new_scale(x: f32, y: f32, z: f32) -> Self {
2117 Self::new(
2118 x, 0.0, 0.0, 0.0, 0.0, y, 0.0, 0.0, 0.0, 0.0, z, 0.0, 0.0, 0.0, 0.0, 1.0,
2119 )
2120 }
2121
2122 #[inline]
2123 pub const fn new_translation(x: f32, y: f32, z: f32) -> Self {
2124 Self::new(
2125 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, x, y, z, 1.0,
2126 )
2127 }
2128
2129 #[inline]
2130 pub fn new_perspective(d: f32) -> Self {
2131 Self::new(
2132 1.0,
2133 0.0,
2134 0.0,
2135 0.0,
2136 0.0,
2137 1.0,
2138 0.0,
2139 0.0,
2140 0.0,
2141 0.0,
2142 1.0,
2143 -1.0 / d,
2144 0.0,
2145 0.0,
2146 0.0,
2147 1.0,
2148 )
2149 }
2150
2151 #[inline]
2154 pub fn new_rotation(x: f32, y: f32, z: f32, theta_radians: f32) -> Self {
2155 let xx = x * x;
2156 let yy = y * y;
2157 let zz = z * z;
2158
2159 let half_theta = theta_radians / 2.0;
2160 let sc = half_theta.sin() * half_theta.cos();
2161 let sq = half_theta.sin() * half_theta.sin();
2162
2163 Self::new(
2164 1.0 - 2.0 * (yy + zz) * sq,
2165 2.0 * (x * y * sq + z * sc),
2166 2.0 * (x * z * sq - y * sc),
2167 0.0,
2168 2.0 * (x * y * sq - z * sc),
2169 1.0 - 2.0 * (xx + zz) * sq,
2170 2.0 * (y * z * sq + x * sc),
2171 0.0,
2172 2.0 * (x * z * sq + y * sc),
2173 2.0 * (y * z * sq - x * sc),
2174 1.0 - 2.0 * (xx + yy) * sq,
2175 0.0,
2176 0.0,
2177 0.0,
2178 0.0,
2179 1.0,
2180 )
2181 }
2182
2183 #[inline]
2184 pub fn new_skew(alpha: f32, beta: f32) -> Self {
2185 let (sx, sy) = (beta.to_radians().tan(), alpha.to_radians().tan());
2186 Self::new(
2187 1.0, sx, 0.0, 0.0, sy, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0,
2188 )
2189 }
2190
2191 pub fn get_column_major(&self) -> Self {
2192 ComputedTransform3D::new(
2193 self.m[0][0],
2194 self.m[1][0],
2195 self.m[2][0],
2196 self.m[3][0],
2197 self.m[0][1],
2198 self.m[1][1],
2199 self.m[2][1],
2200 self.m[3][1],
2201 self.m[0][2],
2202 self.m[1][2],
2203 self.m[2][2],
2204 self.m[3][2],
2205 self.m[0][3],
2206 self.m[1][3],
2207 self.m[2][3],
2208 self.m[3][3],
2209 )
2210 }
2211
2212 #[must_use]
2214 pub fn transform_point2d(&self, p: LogicalPosition) -> Option<LogicalPosition> {
2215 let w =
2216 p.x.mul_add(self.m[0][3], p.y.mul_add(self.m[1][3], self.m[3][3]));
2217
2218 if !w.is_sign_positive() {
2219 return None;
2220 }
2221
2222 let x =
2223 p.x.mul_add(self.m[0][0], p.y.mul_add(self.m[1][0], self.m[3][0]));
2224 let y =
2225 p.x.mul_add(self.m[0][1], p.y.mul_add(self.m[1][1], self.m[3][1]));
2226
2227 Some(LogicalPosition { x: x / w, y: y / w })
2228 }
2229
2230 pub fn scale_for_dpi(&mut self, scale_factor: f32) {
2231 self.m[3][0] *= scale_factor;
2233 self.m[3][1] *= scale_factor;
2234 self.m[3][2] *= scale_factor;
2235 }
2236
2237 #[must_use]
2239 #[inline]
2240 pub fn then(&self, other: &Self) -> Self {
2241 Self::new(
2242 self.m[0][0].mul_add(
2243 other.m[0][0],
2244 self.m[0][1].mul_add(
2245 other.m[1][0],
2246 self.m[0][2].mul_add(other.m[2][0], self.m[0][3] * other.m[3][0]),
2247 ),
2248 ),
2249 self.m[0][0].mul_add(
2250 other.m[0][1],
2251 self.m[0][1].mul_add(
2252 other.m[1][1],
2253 self.m[0][2].mul_add(other.m[2][1], self.m[0][3] * other.m[3][1]),
2254 ),
2255 ),
2256 self.m[0][0].mul_add(
2257 other.m[0][2],
2258 self.m[0][1].mul_add(
2259 other.m[1][2],
2260 self.m[0][2].mul_add(other.m[2][2], self.m[0][3] * other.m[3][2]),
2261 ),
2262 ),
2263 self.m[0][0].mul_add(
2264 other.m[0][3],
2265 self.m[0][1].mul_add(
2266 other.m[1][3],
2267 self.m[0][2].mul_add(other.m[2][3], self.m[0][3] * other.m[3][3]),
2268 ),
2269 ),
2270 self.m[1][0].mul_add(
2271 other.m[0][0],
2272 self.m[1][1].mul_add(
2273 other.m[1][0],
2274 self.m[1][2].mul_add(other.m[2][0], self.m[1][3] * other.m[3][0]),
2275 ),
2276 ),
2277 self.m[1][0].mul_add(
2278 other.m[0][1],
2279 self.m[1][1].mul_add(
2280 other.m[1][1],
2281 self.m[1][2].mul_add(other.m[2][1], self.m[1][3] * other.m[3][1]),
2282 ),
2283 ),
2284 self.m[1][0].mul_add(
2285 other.m[0][2],
2286 self.m[1][1].mul_add(
2287 other.m[1][2],
2288 self.m[1][2].mul_add(other.m[2][2], self.m[1][3] * other.m[3][2]),
2289 ),
2290 ),
2291 self.m[1][0].mul_add(
2292 other.m[0][3],
2293 self.m[1][1].mul_add(
2294 other.m[1][3],
2295 self.m[1][2].mul_add(other.m[2][3], self.m[1][3] * other.m[3][3]),
2296 ),
2297 ),
2298 self.m[2][0].mul_add(
2299 other.m[0][0],
2300 self.m[2][1].mul_add(
2301 other.m[1][0],
2302 self.m[2][2].mul_add(other.m[2][0], self.m[2][3] * other.m[3][0]),
2303 ),
2304 ),
2305 self.m[2][0].mul_add(
2306 other.m[0][1],
2307 self.m[2][1].mul_add(
2308 other.m[1][1],
2309 self.m[2][2].mul_add(other.m[2][1], self.m[2][3] * other.m[3][1]),
2310 ),
2311 ),
2312 self.m[2][0].mul_add(
2313 other.m[0][2],
2314 self.m[2][1].mul_add(
2315 other.m[1][2],
2316 self.m[2][2].mul_add(other.m[2][2], self.m[2][3] * other.m[3][2]),
2317 ),
2318 ),
2319 self.m[2][0].mul_add(
2320 other.m[0][3],
2321 self.m[2][1].mul_add(
2322 other.m[1][3],
2323 self.m[2][2].mul_add(other.m[2][3], self.m[2][3] * other.m[3][3]),
2324 ),
2325 ),
2326 self.m[3][0].mul_add(
2327 other.m[0][0],
2328 self.m[3][1].mul_add(
2329 other.m[1][0],
2330 self.m[3][2].mul_add(other.m[2][0], self.m[3][3] * other.m[3][0]),
2331 ),
2332 ),
2333 self.m[3][0].mul_add(
2334 other.m[0][1],
2335 self.m[3][1].mul_add(
2336 other.m[1][1],
2337 self.m[3][2].mul_add(other.m[2][1], self.m[3][3] * other.m[3][1]),
2338 ),
2339 ),
2340 self.m[3][0].mul_add(
2341 other.m[0][2],
2342 self.m[3][1].mul_add(
2343 other.m[1][2],
2344 self.m[3][2].mul_add(other.m[2][2], self.m[3][3] * other.m[3][2]),
2345 ),
2346 ),
2347 self.m[3][0].mul_add(
2348 other.m[0][3],
2349 self.m[3][1].mul_add(
2350 other.m[1][3],
2351 self.m[3][2].mul_add(other.m[2][3], self.m[3][3] * other.m[3][3]),
2352 ),
2353 ),
2354 )
2355 }
2356
2357 #[cfg(target_arch = "x86_64")]
2362 #[inline]
2363 unsafe fn linear_combine_sse(a: [f32; 4], b: &ComputedTransform3D) -> [f32; 4] {
2364 use core::{
2365 arch::x86_64::{__m128, _mm_add_ps, _mm_mul_ps, _mm_shuffle_ps},
2366 mem,
2367 };
2368
2369 let a: __m128 = mem::transmute(a);
2370 let mut result = _mm_mul_ps(_mm_shuffle_ps(a, a, 0x00), mem::transmute(b.m[0]));
2371 result = _mm_add_ps(
2372 result,
2373 _mm_mul_ps(_mm_shuffle_ps(a, a, 0x55), mem::transmute(b.m[1])),
2374 );
2375 result = _mm_add_ps(
2376 result,
2377 _mm_mul_ps(_mm_shuffle_ps(a, a, 0xaa), mem::transmute(b.m[2])),
2378 );
2379 result = _mm_add_ps(
2380 result,
2381 _mm_mul_ps(_mm_shuffle_ps(a, a, 0xff), mem::transmute(b.m[3])),
2382 );
2383
2384 mem::transmute(result)
2385 }
2386
2387 #[cfg(target_arch = "x86_64")]
2388 #[inline]
2389 pub unsafe fn then_sse(&self, other: &Self) -> Self {
2390 Self {
2391 m: [
2392 Self::linear_combine_sse(self.m[0], other),
2393 Self::linear_combine_sse(self.m[1], other),
2394 Self::linear_combine_sse(self.m[2], other),
2395 Self::linear_combine_sse(self.m[3], other),
2396 ],
2397 }
2398 }
2399
2400 #[cfg(target_arch = "x86_64")]
2402 pub unsafe fn linear_combine_avx8(a01: __m256, b: &ComputedTransform3D) -> __m256 {
2403 use core::{
2404 arch::x86_64::{_mm256_add_ps, _mm256_broadcast_ps, _mm256_mul_ps, _mm256_shuffle_ps},
2405 mem,
2406 };
2407
2408 let mut result = _mm256_mul_ps(
2409 _mm256_shuffle_ps(a01, a01, 0x00),
2410 _mm256_broadcast_ps(mem::transmute(&b.m[0])),
2411 );
2412 result = _mm256_add_ps(
2413 result,
2414 _mm256_mul_ps(
2415 _mm256_shuffle_ps(a01, a01, 0x55),
2416 _mm256_broadcast_ps(mem::transmute(&b.m[1])),
2417 ),
2418 );
2419 result = _mm256_add_ps(
2420 result,
2421 _mm256_mul_ps(
2422 _mm256_shuffle_ps(a01, a01, 0xaa),
2423 _mm256_broadcast_ps(mem::transmute(&b.m[2])),
2424 ),
2425 );
2426 result = _mm256_add_ps(
2427 result,
2428 _mm256_mul_ps(
2429 _mm256_shuffle_ps(a01, a01, 0xff),
2430 _mm256_broadcast_ps(mem::transmute(&b.m[3])),
2431 ),
2432 );
2433 result
2434 }
2435
2436 #[cfg(target_arch = "x86_64")]
2437 #[inline]
2438 pub unsafe fn then_avx8(&self, other: &Self) -> Self {
2439 use core::{
2440 arch::x86_64::{_mm256_loadu_ps, _mm256_storeu_ps, _mm256_zeroupper},
2441 mem,
2442 };
2443
2444 _mm256_zeroupper();
2445
2446 let a01: __m256 = _mm256_loadu_ps(mem::transmute(&self.m[0][0]));
2447 let a23: __m256 = _mm256_loadu_ps(mem::transmute(&self.m[2][0]));
2448
2449 let out01x = Self::linear_combine_avx8(a01, other);
2450 let out23x = Self::linear_combine_avx8(a23, other);
2451
2452 let mut out = Self {
2453 m: [self.m[0], self.m[1], self.m[2], self.m[3]],
2454 };
2455
2456 _mm256_storeu_ps(mem::transmute(&mut out.m[0][0]), out01x);
2457 _mm256_storeu_ps(mem::transmute(&mut out.m[2][0]), out23x);
2458
2459 out
2460 }
2461
2462 #[inline]
2465 pub fn make_rotation(
2466 rotation_origin: (f32, f32),
2467 mut degrees: f32,
2468 axis_x: f32,
2469 axis_y: f32,
2470 axis_z: f32,
2471 rotation_mode: RotationMode,
2473 ) -> Self {
2474 degrees = match rotation_mode {
2475 RotationMode::ForWebRender => -degrees, RotationMode::ForHitTesting => degrees, };
2478
2479 let (origin_x, origin_y) = rotation_origin;
2480 let pre_transform = Self::new_translation(-origin_x, -origin_y, -0.0);
2481 let post_transform = Self::new_translation(origin_x, origin_y, 0.0);
2482 let theta = 2.0_f32 * core::f32::consts::PI - degrees.to_radians();
2483 let rotate_transform =
2484 Self::new_rotation(axis_x, axis_y, axis_z, theta).then(&Self::IDENTITY);
2485
2486 pre_transform.then(&rotate_transform).then(&post_transform)
2487 }
2488}