Skip to main content

layout/
geom.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5use std::convert::From;
6use std::fmt;
7use std::ops::{Add, AddAssign, Neg, Sub, SubAssign};
8use std::sync::atomic::{AtomicI32, Ordering};
9
10use app_units::Au;
11use euclid::{Point2D, Size2D};
12use malloc_size_of_derive::MallocSizeOf;
13use style::Zero;
14use style::logical_geometry::{BlockFlowDirection, InlineBaseDirection, WritingMode};
15use style::values::computed::{CSSPixelLength, LengthPercentage};
16use style::values::generics::length::GenericLengthPercentageOrAuto as AutoOr;
17use style_traits::CSSPixel;
18
19use crate::ContainingBlock;
20use crate::sizing::Size;
21
22pub type PhysicalPoint<U> = euclid::Point2D<U, CSSPixel>;
23pub type PhysicalSize<U> = euclid::Size2D<U, CSSPixel>;
24pub type PhysicalVec<U> = euclid::Vector2D<U, CSSPixel>;
25pub type PhysicalRect<U> = euclid::Rect<U, CSSPixel>;
26pub type PhysicalSides<U> = euclid::SideOffsets2D<U, CSSPixel>;
27pub type AuOrAuto = AutoOr<Au>;
28pub type LengthPercentageOrAuto<'a> = AutoOr<&'a LengthPercentage>;
29
30#[derive(Clone, Copy, MallocSizeOf, PartialEq)]
31pub struct LogicalVec2<T> {
32    pub inline: T,
33    pub block: T,
34}
35
36#[derive(Clone, Copy, MallocSizeOf)]
37pub struct LogicalRect<T> {
38    pub start_corner: LogicalVec2<T>,
39    pub size: LogicalVec2<T>,
40}
41
42#[derive(Clone, Copy, Debug, Default)]
43pub struct LogicalSides<T> {
44    pub inline_start: T,
45    pub inline_end: T,
46    pub block_start: T,
47    pub block_end: T,
48}
49
50#[derive(Clone, Copy, Debug, Default, MallocSizeOf, PartialEq)]
51pub(crate) struct LogicalSides1D<T> {
52    pub start: T,
53    pub end: T,
54}
55
56impl<T> LogicalSides1D<T> {
57    pub(crate) fn map<U>(&self, f: impl Fn(&T) -> U) -> LogicalSides1D<U> {
58        LogicalSides1D {
59            start: f(&self.start),
60            end: f(&self.end),
61        }
62    }
63}
64
65impl LogicalSides1D<AutoOr<&LengthPercentage>> {
66    pub(crate) fn percentages_relative_to(&self, basis: Au) -> LogicalSides1D<AutoOr<Au>> {
67        self.map(|value| value.map(|length_percentage| length_percentage.to_used_value(basis)))
68    }
69}
70
71impl<T: fmt::Debug> fmt::Debug for LogicalVec2<T> {
72    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
73        // Not using f.debug_struct on purpose here, to keep {:?} output somewhat compact
74        f.write_str("Vec2 { i: ")?;
75        self.inline.fmt(f)?;
76        f.write_str(", b: ")?;
77        self.block.fmt(f)?;
78        f.write_str(" }")
79    }
80}
81
82impl<T: Default> Default for LogicalVec2<T> {
83    fn default() -> Self {
84        Self {
85            inline: T::default(),
86            block: T::default(),
87        }
88    }
89}
90
91impl<T: Copy> From<T> for LogicalVec2<T> {
92    fn from(value: T) -> Self {
93        Self {
94            inline: value,
95            block: value,
96        }
97    }
98}
99
100impl<T> LogicalVec2<T> {
101    pub fn map_inline_and_block_axes<U>(
102        &self,
103        inline_f: impl FnOnce(&T) -> U,
104        block_f: impl FnOnce(&T) -> U,
105    ) -> LogicalVec2<U> {
106        LogicalVec2 {
107            inline: inline_f(&self.inline),
108            block: block_f(&self.block),
109        }
110    }
111}
112
113impl<T: Clone> LogicalVec2<Size<T>> {
114    pub fn map_inline_and_block_sizes<U>(
115        &self,
116        inline_f: impl FnOnce(T) -> U,
117        block_f: impl FnOnce(T) -> U,
118    ) -> LogicalVec2<Size<U>> {
119        self.map_inline_and_block_axes(|size| size.map(inline_f), |size| size.map(block_f))
120    }
121}
122
123impl<T: Clone> LogicalVec2<T> {
124    pub fn from_physical_size(physical_size: &PhysicalSize<T>, mode: WritingMode) -> Self {
125        // https://drafts.csswg.org/css-writing-modes/#logical-to-physical
126        let (i, b) = if mode.is_horizontal() {
127            (&physical_size.width, &physical_size.height)
128        } else {
129            (&physical_size.height, &physical_size.width)
130        };
131        LogicalVec2 {
132            inline: i.clone(),
133            block: b.clone(),
134        }
135    }
136
137    pub fn map<U>(&self, f: impl Fn(&T) -> U) -> LogicalVec2<U> {
138        LogicalVec2 {
139            inline: f(&self.inline),
140            block: f(&self.block),
141        }
142    }
143
144    pub(crate) fn map_with<U, V>(
145        &self,
146        other: &LogicalVec2<U>,
147        f: impl Fn(&T, &U) -> V,
148    ) -> LogicalVec2<V> {
149        LogicalVec2 {
150            inline: f(&self.inline, &other.inline),
151            block: f(&self.block, &other.block),
152        }
153    }
154}
155
156impl<T: Add<Output = T> + Copy> Add<LogicalVec2<T>> for LogicalVec2<T> {
157    type Output = LogicalVec2<T>;
158    fn add(self, other: Self) -> Self::Output {
159        LogicalVec2 {
160            inline: self.inline + other.inline,
161            block: self.block + other.block,
162        }
163    }
164}
165
166impl<T: Sub<Output = T> + Copy> Sub<LogicalVec2<T>> for LogicalVec2<T> {
167    type Output = LogicalVec2<T>;
168    fn sub(self, other: Self) -> Self::Output {
169        LogicalVec2 {
170            inline: self.inline - other.inline,
171            block: self.block - other.block,
172        }
173    }
174}
175
176impl<T: AddAssign<T> + Copy> AddAssign<LogicalVec2<T>> for LogicalVec2<T> {
177    fn add_assign(&mut self, other: LogicalVec2<T>) {
178        self.inline += other.inline;
179        self.block += other.block;
180    }
181}
182
183impl<T: SubAssign<T> + Copy> SubAssign<LogicalVec2<T>> for LogicalVec2<T> {
184    fn sub_assign(&mut self, other: LogicalVec2<T>) {
185        self.inline -= other.inline;
186        self.block -= other.block;
187    }
188}
189
190impl<T: Neg<Output = T> + Copy> Neg for LogicalVec2<T> {
191    type Output = LogicalVec2<T>;
192    fn neg(self) -> Self::Output {
193        Self {
194            inline: -self.inline,
195            block: -self.block,
196        }
197    }
198}
199
200impl<T: Zero> LogicalVec2<T> {
201    pub fn zero() -> Self {
202        Self {
203            inline: T::zero(),
204            block: T::zero(),
205        }
206    }
207}
208
209impl<T: Clone> LogicalVec2<AutoOr<T>> {
210    pub fn auto_is(&self, f: impl Fn() -> T) -> LogicalVec2<T> {
211        self.map(|t| t.auto_is(&f))
212    }
213}
214
215impl<T: Zero> LogicalRect<T> {
216    pub fn zero() -> Self {
217        Self {
218            start_corner: LogicalVec2::zero(),
219            size: LogicalVec2::zero(),
220        }
221    }
222}
223
224impl fmt::Debug for LogicalRect<Au> {
225    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
226        write!(
227            f,
228            "Rect(i{}×b{} @ (i{},b{}))",
229            self.size.inline.to_f32_px(),
230            self.size.block.to_f32_px(),
231            self.start_corner.inline.to_f32_px(),
232            self.start_corner.block.to_f32_px(),
233        )
234    }
235}
236
237impl<T: Clone> LogicalVec2<T> {
238    pub fn to_physical_size(&self, mode: WritingMode) -> PhysicalSize<T> {
239        // https://drafts.csswg.org/css-writing-modes/#logical-to-physical
240        let (x, y) = if mode.is_horizontal() {
241            (&self.inline, &self.block)
242        } else {
243            (&self.block, &self.inline)
244        };
245        PhysicalSize::new(x.clone(), y.clone())
246    }
247}
248
249impl<T: Copy + Neg<Output = T>> LogicalVec2<T> {
250    pub fn to_physical_vector(&self, mode: WritingMode) -> PhysicalVec<T> {
251        if mode.is_horizontal() {
252            if mode.is_bidi_ltr() {
253                PhysicalVec::new(self.inline, self.block)
254            } else {
255                PhysicalVec::new(-self.inline, self.block)
256            }
257        } else if mode.is_inline_tb() {
258            PhysicalVec::new(self.block, self.inline)
259        } else {
260            PhysicalVec::new(-self.block, self.inline)
261        }
262    }
263}
264
265impl<T: Clone> LogicalSides<T> {
266    pub fn from_physical(sides: &PhysicalSides<T>, mode: WritingMode) -> Self {
267        // https://drafts.csswg.org/css-writing-modes/#logical-to-physical
268        let block_flow = mode.block_flow_direction();
269        let (bs, be) = match mode.block_flow_direction() {
270            BlockFlowDirection::TopToBottom => (&sides.top, &sides.bottom),
271            BlockFlowDirection::RightToLeft => (&sides.right, &sides.left),
272            BlockFlowDirection::LeftToRight => (&sides.left, &sides.right),
273        };
274        use BlockFlowDirection::TopToBottom;
275        let (is, ie) = match (block_flow, mode.inline_base_direction()) {
276            (TopToBottom, InlineBaseDirection::LeftToRight) => (&sides.left, &sides.right),
277            (TopToBottom, InlineBaseDirection::RightToLeft) => (&sides.right, &sides.left),
278            (_, InlineBaseDirection::LeftToRight) => (&sides.top, &sides.bottom),
279            (_, InlineBaseDirection::RightToLeft) => (&sides.bottom, &sides.top),
280        };
281        LogicalSides {
282            inline_start: is.clone(),
283            inline_end: ie.clone(),
284            block_start: bs.clone(),
285            block_end: be.clone(),
286        }
287    }
288}
289
290impl<T> LogicalSides<T> {
291    pub fn map<U>(&self, f: impl Fn(&T) -> U) -> LogicalSides<U> {
292        LogicalSides {
293            inline_start: f(&self.inline_start),
294            inline_end: f(&self.inline_end),
295            block_start: f(&self.block_start),
296            block_end: f(&self.block_end),
297        }
298    }
299
300    pub fn map_inline_and_block_axes<U>(
301        &self,
302        inline_f: impl Fn(&T) -> U,
303        block_f: impl Fn(&T) -> U,
304    ) -> LogicalSides<U> {
305        LogicalSides {
306            inline_start: inline_f(&self.inline_start),
307            inline_end: inline_f(&self.inline_end),
308            block_start: block_f(&self.block_start),
309            block_end: block_f(&self.block_end),
310        }
311    }
312
313    pub fn inline_sum(&self) -> T::Output
314    where
315        T: Add + Copy,
316    {
317        self.inline_start + self.inline_end
318    }
319
320    pub fn block_sum(&self) -> T::Output
321    where
322        T: Add + Copy,
323    {
324        self.block_start + self.block_end
325    }
326
327    pub fn sum(&self) -> LogicalVec2<T::Output>
328    where
329        T: Add + Copy,
330    {
331        LogicalVec2 {
332            inline: self.inline_sum(),
333            block: self.block_sum(),
334        }
335    }
336
337    pub fn to_physical(&self, mode: WritingMode) -> PhysicalSides<T>
338    where
339        T: Clone,
340    {
341        let top;
342        let right;
343        let bottom;
344        let left;
345        if mode.is_vertical() {
346            if mode.is_vertical_lr() {
347                left = self.block_start.clone();
348                right = self.block_end.clone();
349            } else {
350                right = self.block_start.clone();
351                left = self.block_end.clone();
352            }
353
354            if mode.is_inline_tb() {
355                top = self.inline_start.clone();
356                bottom = self.inline_end.clone();
357            } else {
358                bottom = self.inline_start.clone();
359                top = self.inline_end.clone();
360            }
361        } else {
362            top = self.block_start.clone();
363            bottom = self.block_end.clone();
364            if mode.is_bidi_ltr() {
365                left = self.inline_start.clone();
366                right = self.inline_end.clone();
367            } else {
368                right = self.inline_start.clone();
369                left = self.inline_end.clone();
370            }
371        }
372        PhysicalSides::new(top, right, bottom, left)
373    }
374}
375
376impl<T: Copy> LogicalSides<T> {
377    pub fn start_offset(&self) -> LogicalVec2<T> {
378        LogicalVec2 {
379            inline: self.inline_start,
380            block: self.block_start,
381        }
382    }
383
384    #[inline]
385    pub(crate) fn inline_sides(&self) -> LogicalSides1D<T> {
386        LogicalSides1D::new(self.inline_start, self.inline_end)
387    }
388
389    #[inline]
390    pub(crate) fn block_sides(&self) -> LogicalSides1D<T> {
391        LogicalSides1D::new(self.block_start, self.block_end)
392    }
393}
394
395impl LogicalSides<LengthPercentage> {
396    pub fn percentages_relative_to(&self, basis: Au) -> LogicalSides<Au> {
397        self.map(|value| value.to_used_value(basis))
398    }
399}
400
401impl LogicalSides<LengthPercentageOrAuto<'_>> {
402    pub fn percentages_relative_to(&self, basis: Au) -> LogicalSides<AuOrAuto> {
403        self.map(|value| value.map(|value| value.to_used_value(basis)))
404    }
405}
406
407impl<T: Clone> LogicalSides<AutoOr<T>> {
408    pub fn auto_is(&self, f: impl Fn() -> T) -> LogicalSides<T> {
409        self.map(|s| s.auto_is(&f))
410    }
411}
412
413impl<T: Add<Output = T> + Copy> Add<LogicalSides<T>> for LogicalSides<T> {
414    type Output = LogicalSides<T>;
415
416    fn add(self, other: Self) -> Self::Output {
417        LogicalSides {
418            inline_start: self.inline_start + other.inline_start,
419            inline_end: self.inline_end + other.inline_end,
420            block_start: self.block_start + other.block_start,
421            block_end: self.block_end + other.block_end,
422        }
423    }
424}
425
426impl<T: Sub<Output = T> + Copy> Sub<LogicalSides<T>> for LogicalSides<T> {
427    type Output = LogicalSides<T>;
428
429    fn sub(self, other: Self) -> Self::Output {
430        LogicalSides {
431            inline_start: self.inline_start - other.inline_start,
432            inline_end: self.inline_end - other.inline_end,
433            block_start: self.block_start - other.block_start,
434            block_end: self.block_end - other.block_end,
435        }
436    }
437}
438
439impl<T: Neg<Output = T> + Copy> Neg for LogicalSides<T> {
440    type Output = LogicalSides<T>;
441    fn neg(self) -> Self::Output {
442        Self {
443            inline_start: -self.inline_start,
444            inline_end: -self.inline_end,
445            block_start: -self.block_start,
446            block_end: -self.block_end,
447        }
448    }
449}
450
451impl<T: Zero> LogicalSides<T> {
452    pub(crate) fn zero() -> LogicalSides<T> {
453        Self {
454            inline_start: T::zero(),
455            inline_end: T::zero(),
456            block_start: T::zero(),
457            block_end: T::zero(),
458        }
459    }
460}
461
462impl From<LogicalSides<CSSPixelLength>> for LogicalSides<Au> {
463    fn from(value: LogicalSides<CSSPixelLength>) -> Self {
464        Self {
465            inline_start: value.inline_start.into(),
466            inline_end: value.inline_end.into(),
467            block_start: value.block_start.into(),
468            block_end: value.block_end.into(),
469        }
470    }
471}
472
473impl From<LogicalSides<Au>> for LogicalSides<CSSPixelLength> {
474    fn from(value: LogicalSides<Au>) -> Self {
475        Self {
476            inline_start: value.inline_start.into(),
477            inline_end: value.inline_end.into(),
478            block_start: value.block_start.into(),
479            block_end: value.block_end.into(),
480        }
481    }
482}
483
484impl<T> LogicalSides1D<T> {
485    #[inline]
486    pub(crate) fn new(start: T, end: T) -> Self {
487        Self { start, end }
488    }
489}
490
491impl<T> LogicalSides1D<AutoOr<T>> {
492    #[inline]
493    pub(crate) fn either_specified(&self) -> bool {
494        !self.start.is_auto() || !self.end.is_auto()
495    }
496
497    #[inline]
498    pub(crate) fn either_auto(&self) -> bool {
499        self.start.is_auto() || self.end.is_auto()
500    }
501}
502
503impl<T> LogicalRect<T> {
504    pub fn max_inline_position(&self) -> T
505    where
506        T: Add<Output = T> + Copy,
507    {
508        self.start_corner.inline + self.size.inline
509    }
510
511    pub fn max_block_position(&self) -> T
512    where
513        T: Add<Output = T> + Copy,
514    {
515        self.start_corner.block + self.size.block
516    }
517
518    pub fn inflate(&self, sides: &LogicalSides<T>) -> Self
519    where
520        T: Add<Output = T> + Copy,
521        T: Sub<Output = T> + Copy,
522    {
523        Self {
524            start_corner: LogicalVec2 {
525                inline: self.start_corner.inline - sides.inline_start,
526                block: self.start_corner.block - sides.block_start,
527            },
528            size: LogicalVec2 {
529                inline: self.size.inline + sides.inline_sum(),
530                block: self.size.block + sides.block_sum(),
531            },
532        }
533    }
534
535    pub fn deflate(&self, sides: &LogicalSides<T>) -> Self
536    where
537        T: Add<Output = T> + Copy,
538        T: Sub<Output = T> + Copy,
539    {
540        LogicalRect {
541            start_corner: LogicalVec2 {
542                inline: self.start_corner.inline + sides.inline_start,
543                block: self.start_corner.block + sides.block_start,
544            },
545            size: LogicalVec2 {
546                inline: self.size.inline - sides.inline_sum(),
547                block: self.size.block - sides.block_sum(),
548            },
549        }
550    }
551}
552
553impl LogicalRect<Au> {
554    pub(crate) fn as_physical(
555        &self,
556        containing_block: Option<&ContainingBlock<'_>>,
557    ) -> PhysicalRect<Au> {
558        let mode = containing_block.map_or_else(WritingMode::horizontal_tb, |containing_block| {
559            containing_block.style.writing_mode
560        });
561        let (x, y, width, height) = if mode.is_vertical() {
562            // TODO: Bottom-to-top writing modes are not supported.
563            (
564                self.start_corner.block,
565                self.start_corner.inline,
566                self.size.block,
567                self.size.inline,
568            )
569        } else {
570            let y = self.start_corner.block;
571            let x = match containing_block {
572                Some(containing_block) if !mode.is_bidi_ltr() => {
573                    containing_block.size.inline - self.max_inline_position()
574                },
575                _ => self.start_corner.inline,
576            };
577            (x, y, self.size.inline, self.size.block)
578        };
579
580        PhysicalRect::new(PhysicalPoint::new(x, y), PhysicalSize::new(width, height))
581    }
582}
583
584impl From<LogicalVec2<CSSPixelLength>> for LogicalVec2<Au> {
585    fn from(value: LogicalVec2<CSSPixelLength>) -> Self {
586        LogicalVec2 {
587            inline: value.inline.into(),
588            block: value.block.into(),
589        }
590    }
591}
592
593impl From<LogicalVec2<Au>> for LogicalVec2<CSSPixelLength> {
594    fn from(value: LogicalVec2<Au>) -> Self {
595        LogicalVec2 {
596            inline: value.inline.into(),
597            block: value.block.into(),
598        }
599    }
600}
601
602impl From<LogicalRect<Au>> for LogicalRect<CSSPixelLength> {
603    fn from(value: LogicalRect<Au>) -> Self {
604        LogicalRect {
605            start_corner: value.start_corner.into(),
606            size: value.size.into(),
607        }
608    }
609}
610
611impl From<LogicalRect<CSSPixelLength>> for LogicalRect<Au> {
612    fn from(value: LogicalRect<CSSPixelLength>) -> Self {
613        LogicalRect {
614            start_corner: value.start_corner.into(),
615            size: value.size.into(),
616        }
617    }
618}
619
620pub(crate) trait ToLogical<Unit, LogicalType> {
621    fn to_logical(&self, writing_mode: WritingMode) -> LogicalType;
622}
623
624impl<Unit: Copy> ToLogical<Unit, LogicalVec2<Unit>> for PhysicalSize<Unit> {
625    fn to_logical(&self, writing_mode: WritingMode) -> LogicalVec2<Unit> {
626        LogicalVec2::from_physical_size(self, writing_mode)
627    }
628}
629
630impl<Unit: Copy> ToLogical<Unit, LogicalSides<Unit>> for PhysicalSides<Unit> {
631    fn to_logical(&self, writing_mode: WritingMode) -> LogicalSides<Unit> {
632        LogicalSides::from_physical(self, writing_mode)
633    }
634}
635
636pub(crate) trait ToLogicalWithContainingBlock<LogicalType> {
637    fn to_logical(&self, containing_block: &ContainingBlock) -> LogicalType;
638}
639
640impl ToLogicalWithContainingBlock<LogicalVec2<Au>> for PhysicalPoint<Au> {
641    fn to_logical(&self, containing_block: &ContainingBlock) -> LogicalVec2<Au> {
642        let writing_mode = containing_block.style.writing_mode;
643        // TODO: Bottom-to-top and right-to-left vertical writing modes are not supported yet.
644        if writing_mode.is_vertical() {
645            LogicalVec2 {
646                inline: self.y,
647                block: self.x,
648            }
649        } else {
650            LogicalVec2 {
651                inline: if writing_mode.is_bidi_ltr() {
652                    self.x
653                } else {
654                    containing_block.size.inline - self.x
655                },
656                block: self.y,
657            }
658        }
659    }
660}
661
662impl ToLogicalWithContainingBlock<LogicalRect<Au>> for PhysicalRect<Au> {
663    fn to_logical(&self, containing_block: &ContainingBlock) -> LogicalRect<Au> {
664        let inline_start;
665        let block_start;
666        let inline;
667        let block;
668
669        let writing_mode = containing_block.style.writing_mode;
670        if writing_mode.is_vertical() {
671            // TODO: Bottom-to-top and right-to-left vertical writing modes are not supported yet.
672            inline = self.size.height;
673            block = self.size.width;
674            block_start = self.origin.x;
675            inline_start = self.origin.y;
676        } else {
677            inline = self.size.width;
678            block = self.size.height;
679            block_start = self.origin.y;
680            if writing_mode.is_bidi_ltr() {
681                inline_start = self.origin.x;
682            } else {
683                inline_start = containing_block.size.inline - (self.origin.x + self.size.width);
684            }
685        }
686        LogicalRect {
687            start_corner: LogicalVec2 {
688                inline: inline_start,
689                block: block_start,
690            },
691            size: LogicalVec2 { inline, block },
692        }
693    }
694}
695
696/// A `PhysicalRect<Au>` with shared mutablitiy
697///
698/// It is based on `AtomicI32` but is not atomic itself: there is no protection against tearing:
699/// If multiple threads are calling `set()`, the rectangle may end up with some fields from one call
700/// and some fields from another call.
701///
702/// Compared to `AtomicRefCell<PhysicalRect<Au>>`:
703///
704/// * Both are meant for uses where despite shared ownership, no concurrency is expected in practice.
705/// * If concurrent access does unexpectedly happen `AtomicRefCell` will panic,
706///   whereas `SyncPhysicalRectAu` can tear and result in invalid/inconsistent data.
707/// * `SyncPhysicalRectAu` has no space overhead, `AtomicRefCell` stores an `AtomicUsize` borrow flag
708/// * `SyncPhysicalRectAu` uses relaxed atomic access for each field separately
709#[derive(MallocSizeOf, Default)]
710pub(crate) struct SyncPhysicalRectAu(PhysicalRect<AtomicI32>);
711
712impl SyncPhysicalRectAu {
713    #[inline]
714    pub(crate) fn new(rect: PhysicalRect<Au>) -> Self {
715        Self(PhysicalRect::new(
716            PhysicalPoint::new(rect.origin.x.0.into(), rect.origin.y.0.into()),
717            PhysicalSize::new(rect.size.width.0.into(), rect.size.height.0.into()),
718        ))
719    }
720
721    pub(crate) fn get(&self) -> PhysicalRect<Au> {
722        PhysicalRect::new(self.origin(), self.size())
723    }
724
725    pub(crate) fn set(&self, new_rect: PhysicalRect<Au>) {
726        self.set_origin(new_rect.origin);
727        self.set_size(new_rect.size);
728    }
729
730    #[inline]
731    pub(crate) fn origin(&self) -> PhysicalPoint<Au> {
732        Point2D::new(
733            // Bypass clamping in `Au::new`: the value originally came from another `Au`
734            // so it’s expected to already be in range
735            Au(self.0.origin.x.load(Ordering::Relaxed)),
736            Au(self.0.origin.y.load(Ordering::Relaxed)),
737        )
738    }
739
740    #[inline]
741    pub(crate) fn size(&self) -> PhysicalSize<Au> {
742        Size2D::new(
743            // Bypass clamping in `Au::new`: the value originally came from another `Au`
744            // so it’s expected to already be in range
745            Au(self.0.size.width.load(Ordering::Relaxed)),
746            Au(self.0.size.height.load(Ordering::Relaxed)),
747        )
748    }
749
750    #[inline]
751    pub(crate) fn set_origin(&self, new_origin: PhysicalPoint<Au>) {
752        let origin = &self.0.origin;
753        origin.x.store(new_origin.x.0, Ordering::Relaxed);
754        origin.y.store(new_origin.y.0, Ordering::Relaxed);
755    }
756
757    #[inline]
758    pub(crate) fn set_size(&self, new_size: PhysicalSize<Au>) {
759        let size = &self.0.size;
760        size.width.store(new_size.width.0, Ordering::Relaxed);
761        size.height.store(new_size.height.0, Ordering::Relaxed);
762    }
763
764    #[inline]
765    pub(crate) fn translate(&self, offset: PhysicalSize<Au>) {
766        // This code explicitly does not use `AtomicI32::fetch_add`, as we rely on Au's
767        // overflow detection to clamp the resulting value between `MAX_AU` and `MIN_AU`.
768        let new_origin = self.origin() + offset;
769        self.set_origin(new_origin);
770    }
771}
772
773impl fmt::Debug for SyncPhysicalRectAu {
774    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
775        self.get().fmt(f)
776    }
777}