azul_webrender_api/
display_item.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 http://mozilla.org/MPL/2.0/. */
4
5use euclid::{SideOffsets2D, Angle};
6use peek_poke::PeekPoke;
7use std::ops::Not;
8// local imports
9use crate::font;
10use crate::{PipelineId, PropertyBinding};
11use crate::color::ColorF;
12use crate::image::{ColorDepth, ImageKey};
13use crate::units::*;
14use std::hash::{Hash, Hasher};
15
16// ******************************************************************
17// * NOTE: some of these structs have an "IMPLICIT" comment.        *
18// * This indicates that the BuiltDisplayList will have serialized  *
19// * a list of values nearby that this item consumes. The traversal *
20// * iterator should handle finding these. DebugDisplayItem should  *
21// * make them explicit.                                            *
22// ******************************************************************
23
24/// A tag that can be used to identify items during hit testing. If the tag
25/// is missing then the item doesn't take part in hit testing at all. This
26/// is composed of two numbers. In Servo, the first is an identifier while the
27/// second is used to select the cursor that should be used during mouse
28/// movement. In Gecko, the first is a scrollframe identifier, while the second
29/// is used to store various flags that APZ needs to properly process input
30/// events.
31pub type ItemTag = (u64, u16);
32
33/// An identifier used to refer to previously sent display items. Currently it
34/// refers to individual display items, but this may change later.
35pub type ItemKey = u16;
36
37bitflags! {
38    #[repr(C)]
39    #[derive(Deserialize, MallocSizeOf, Serialize, PeekPoke)]
40    pub struct PrimitiveFlags: u8 {
41        /// The CSS backface-visibility property (yes, it can be really granular)
42        const IS_BACKFACE_VISIBLE = 1 << 0;
43        /// If set, this primitive represents a scroll bar container
44        const IS_SCROLLBAR_CONTAINER = 1 << 1;
45        /// If set, this primitive represents a scroll bar thumb
46        const IS_SCROLLBAR_THUMB = 1 << 2;
47        /// This is used as a performance hint - this primitive may be promoted to a native
48        /// compositor surface under certain (implementation specific) conditions. This
49        /// is typically used for large videos, and canvas elements.
50        const PREFER_COMPOSITOR_SURFACE = 1 << 3;
51        /// If set, this primitive can be passed directly to the compositor via its
52        /// ExternalImageId, and the compositor will use the native image directly.
53        /// Used as a further extension on top of PREFER_COMPOSITOR_SURFACE.
54        const SUPPORTS_EXTERNAL_COMPOSITOR_SURFACE = 1 << 4;
55    }
56}
57
58impl Default for PrimitiveFlags {
59    fn default() -> Self {
60        PrimitiveFlags::IS_BACKFACE_VISIBLE
61    }
62}
63
64/// A grouping of fields a lot of display items need, just to avoid
65/// repeating these over and over in this file.
66#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)]
67pub struct CommonItemProperties {
68    /// Bounds of the display item to clip to. Many items are logically
69    /// infinite, and rely on this clip_rect to define their bounds
70    /// (solid colors, background-images, gradients, etc).
71    pub clip_rect: LayoutRect,
72    /// Additional clips
73    pub clip_id: ClipId,
74    /// The coordinate-space the item is in (yes, it can be really granular)
75    pub spatial_id: SpatialId,
76    /// Various flags describing properties of this primitive.
77    pub flags: PrimitiveFlags,
78}
79
80impl CommonItemProperties {
81    /// Convenience for tests.
82    pub fn new(
83        clip_rect: LayoutRect,
84        space_and_clip: SpaceAndClipInfo,
85    ) -> Self {
86        Self {
87            clip_rect,
88            spatial_id: space_and_clip.spatial_id,
89            clip_id: space_and_clip.clip_id,
90            flags: PrimitiveFlags::default(),
91        }
92    }
93}
94
95/// Per-primitive information about the nodes in the clip tree and
96/// the spatial tree that the primitive belongs to.
97///
98/// Note: this is a separate struct from `PrimitiveInfo` because
99/// it needs indirectional mapping during the DL flattening phase,
100/// turning into `ScrollNodeAndClipChain`.
101#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)]
102pub struct SpaceAndClipInfo {
103    pub spatial_id: SpatialId,
104    pub clip_id: ClipId,
105}
106
107impl SpaceAndClipInfo {
108    /// Create a new space/clip info associated with the root
109    /// scroll frame.
110    pub fn root_scroll(pipeline_id: PipelineId) -> Self {
111        SpaceAndClipInfo {
112            spatial_id: SpatialId::root_scroll_node(pipeline_id),
113            clip_id: ClipId::root(pipeline_id),
114        }
115    }
116}
117
118#[repr(u8)]
119#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize, PeekPoke)]
120pub enum DisplayItem {
121    // These are the "real content" display items
122    Rectangle(RectangleDisplayItem),
123    ClearRectangle(ClearRectangleDisplayItem),
124    HitTest(HitTestDisplayItem),
125    Text(TextDisplayItem),
126    Line(LineDisplayItem),
127    Border(BorderDisplayItem),
128    BoxShadow(BoxShadowDisplayItem),
129    PushShadow(PushShadowDisplayItem),
130    Gradient(GradientDisplayItem),
131    RadialGradient(RadialGradientDisplayItem),
132    ConicGradient(ConicGradientDisplayItem),
133    Image(ImageDisplayItem),
134    RepeatingImage(RepeatingImageDisplayItem),
135    YuvImage(YuvImageDisplayItem),
136    BackdropFilter(BackdropFilterDisplayItem),
137
138    // Clips
139    RectClip(RectClipDisplayItem),
140    RoundedRectClip(RoundedRectClipDisplayItem),
141    ImageMaskClip(ImageMaskClipDisplayItem),
142    ClipChain(ClipChainItem),
143
144    // Spaces and Frames that content can be scoped under.
145    ScrollFrame(ScrollFrameDisplayItem),
146    StickyFrame(StickyFrameDisplayItem),
147    Iframe(IframeDisplayItem),
148    PushReferenceFrame(ReferenceFrameDisplayListItem),
149    PushStackingContext(PushStackingContextDisplayItem),
150
151    // These marker items indicate an array of data follows, to be used for the
152    // next non-marker item.
153    SetGradientStops,
154    SetFilterOps,
155    SetFilterData,
156    SetFilterPrimitives,
157    SetPoints,
158
159    // These marker items terminate a scope introduced by a previous item.
160    PopReferenceFrame,
161    PopStackingContext,
162    PopAllShadows,
163
164    ReuseItems(ItemKey),
165    RetainedItems(ItemKey),
166}
167
168/// This is a "complete" version of the DisplayItem, with all implicit trailing
169/// arrays included, for debug serialization (captures).
170#[cfg(any(feature = "serialize", feature = "deserialize"))]
171#[cfg_attr(feature = "serialize", derive(Serialize))]
172#[cfg_attr(feature = "deserialize", derive(Deserialize))]
173pub enum DebugDisplayItem {
174    Rectangle(RectangleDisplayItem),
175    ClearRectangle(ClearRectangleDisplayItem),
176    HitTest(HitTestDisplayItem),
177    Text(TextDisplayItem, Vec<font::GlyphInstance>),
178    Line(LineDisplayItem),
179    Border(BorderDisplayItem),
180    BoxShadow(BoxShadowDisplayItem),
181    PushShadow(PushShadowDisplayItem),
182    Gradient(GradientDisplayItem),
183    RadialGradient(RadialGradientDisplayItem),
184    ConicGradient(ConicGradientDisplayItem),
185    Image(ImageDisplayItem),
186    RepeatingImage(RepeatingImageDisplayItem),
187    YuvImage(YuvImageDisplayItem),
188    BackdropFilter(BackdropFilterDisplayItem),
189
190    ImageMaskClip(ImageMaskClipDisplayItem),
191    RoundedRectClip(RoundedRectClipDisplayItem),
192    RectClip(RectClipDisplayItem),
193    ClipChain(ClipChainItem, Vec<ClipId>),
194
195    ScrollFrame(ScrollFrameDisplayItem),
196    StickyFrame(StickyFrameDisplayItem),
197    Iframe(IframeDisplayItem),
198    PushReferenceFrame(ReferenceFrameDisplayListItem),
199    PushStackingContext(PushStackingContextDisplayItem),
200
201    SetGradientStops(Vec<GradientStop>),
202    SetFilterOps(Vec<FilterOp>),
203    SetFilterData(FilterData),
204    SetFilterPrimitives(Vec<FilterPrimitive>),
205    SetPoints(Vec<LayoutPoint>),
206
207    PopReferenceFrame,
208    PopStackingContext,
209    PopAllShadows,
210}
211
212#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)]
213pub struct ImageMaskClipDisplayItem {
214    pub id: ClipId,
215    pub parent_space_and_clip: SpaceAndClipInfo,
216    pub image_mask: ImageMask,
217    pub fill_rule: FillRule,
218} // IMPLICIT points: Vec<LayoutPoint>
219
220#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)]
221pub struct RectClipDisplayItem {
222    pub id: ClipId,
223    pub parent_space_and_clip: SpaceAndClipInfo,
224    pub clip_rect: LayoutRect,
225}
226
227#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)]
228pub struct RoundedRectClipDisplayItem {
229    pub id: ClipId,
230    pub parent_space_and_clip: SpaceAndClipInfo,
231    pub clip: ComplexClipRegion,
232}
233
234#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)]
235pub struct ClipDisplayItem {
236    pub id: ClipId,
237    pub parent_space_and_clip: SpaceAndClipInfo,
238    pub clip_rect: LayoutRect,
239} // IMPLICIT: complex_clips: Vec<ComplexClipRegion>
240
241/// The minimum and maximum allowable offset for a sticky frame in a single dimension.
242#[repr(C)]
243#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)]
244pub struct StickyOffsetBounds {
245    /// The minimum offset for this frame, typically a negative value, which specifies how
246    /// far in the negative direction the sticky frame can offset its contents in this
247    /// dimension.
248    pub min: f32,
249
250    /// The maximum offset for this frame, typically a positive value, which specifies how
251    /// far in the positive direction the sticky frame can offset its contents in this
252    /// dimension.
253    pub max: f32,
254}
255
256impl StickyOffsetBounds {
257    pub fn new(min: f32, max: f32) -> StickyOffsetBounds {
258        StickyOffsetBounds { min, max }
259    }
260}
261
262#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)]
263pub struct StickyFrameDisplayItem {
264    pub id: SpatialId,
265    pub parent_spatial_id: SpatialId,
266    pub bounds: LayoutRect,
267
268    /// The margins that should be maintained between the edge of the parent viewport and this
269    /// sticky frame. A margin of None indicates that the sticky frame should not stick at all
270    /// to that particular edge of the viewport.
271    pub margins: SideOffsets2D<Option<f32>, LayoutPixel>,
272
273    /// The minimum and maximum vertical offsets for this sticky frame. Ignoring these constraints,
274    /// the sticky frame will continue to stick to the edge of the viewport as its original
275    /// position is scrolled out of view. Constraints specify a maximum and minimum offset from the
276    /// original position relative to non-sticky content within the same scrolling frame.
277    pub vertical_offset_bounds: StickyOffsetBounds,
278
279    /// The minimum and maximum horizontal offsets for this sticky frame. Ignoring these constraints,
280    /// the sticky frame will continue to stick to the edge of the viewport as its original
281    /// position is scrolled out of view. Constraints specify a maximum and minimum offset from the
282    /// original position relative to non-sticky content within the same scrolling frame.
283    pub horizontal_offset_bounds: StickyOffsetBounds,
284
285    /// The amount of offset that has already been applied to the sticky frame. A positive y
286    /// component this field means that a top-sticky item was in a scrollframe that has been
287    /// scrolled down, such that the sticky item's position needed to be offset downwards by
288    /// `previously_applied_offset.y`. A negative y component corresponds to the upward offset
289    /// applied due to bottom-stickiness. The x-axis works analogously.
290    pub previously_applied_offset: LayoutVector2D,
291}
292
293#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize, PeekPoke)]
294pub enum ScrollSensitivity {
295    ScriptAndInputEvents,
296    Script,
297}
298
299#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)]
300pub struct ScrollFrameDisplayItem {
301    /// The id of the clip this scroll frame creates
302    pub clip_id: ClipId,
303    /// The id of the space this scroll frame creates
304    pub scroll_frame_id: SpatialId,
305    /// The size of the contents this contains (so the backend knows how far it can scroll).
306    // FIXME: this can *probably* just be a size? Origin seems to just get thrown out.
307    pub content_rect: LayoutRect,
308    pub clip_rect: LayoutRect,
309    pub parent_space_and_clip: SpaceAndClipInfo,
310    pub external_id: ExternalScrollId,
311    pub scroll_sensitivity: ScrollSensitivity,
312    /// The amount this scrollframe has already been scrolled by, in the caller.
313    /// This means that all the display items that are inside the scrollframe
314    /// will have their coordinates shifted by this amount, and this offset
315    /// should be added to those display item coordinates in order to get a
316    /// normalized value that is consistent across display lists.
317    pub external_scroll_offset: LayoutVector2D,
318}
319
320/// A solid or an animating color to draw (may not actually be a rectangle due to complex clips)
321#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)]
322pub struct RectangleDisplayItem {
323    pub common: CommonItemProperties,
324    pub bounds: LayoutRect,
325    pub color: PropertyBinding<ColorF>,
326}
327
328/// Clears all colors from the area, making it possible to cut holes in the window.
329/// (useful for things like the macos frosted-glass effect).
330#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)]
331pub struct ClearRectangleDisplayItem {
332    pub common: CommonItemProperties,
333    pub bounds: LayoutRect,
334}
335
336/// A minimal hit-testable item for the parent browser's convenience, and is
337/// slimmer than a RectangleDisplayItem (no color). The existence of this as a
338/// distinct item also makes it easier to inspect/debug display items.
339#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)]
340pub struct HitTestDisplayItem {
341    pub common: CommonItemProperties,
342    pub tag: ItemTag,
343}
344
345#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)]
346pub struct LineDisplayItem {
347    pub common: CommonItemProperties,
348    /// We need a separate rect from common.clip_rect to encode cute
349    /// tricks that firefox does to make a series of text-decorations seamlessly
350    /// line up -- snapping the decorations to a multiple of their period, and
351    /// then clipping them to their "proper" area. This rect is that "logical"
352    /// snapped area that may be clipped to the right size by the clip_rect.
353    pub area: LayoutRect,
354    /// Whether the rect is interpretted as vertical or horizontal
355    pub orientation: LineOrientation,
356    /// This could potentially be implied from area, but we currently prefer
357    /// that this is the responsibility of the layout engine. Value irrelevant
358    /// for non-wavy lines.
359    // FIXME: this was done before we could use tagged unions in enums, but now
360    // it should just be part of LineStyle::Wavy.
361    pub wavy_line_thickness: f32,
362    pub color: ColorF,
363    pub style: LineStyle,
364}
365
366#[repr(u8)]
367#[derive(Clone, Copy, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize, Eq, Hash, PeekPoke)]
368pub enum LineOrientation {
369    Vertical,
370    Horizontal,
371}
372
373#[repr(u8)]
374#[derive(Clone, Copy, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize, Eq, Hash, PeekPoke)]
375pub enum LineStyle {
376    Solid,
377    Dotted,
378    Dashed,
379    Wavy,
380}
381
382#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)]
383pub struct TextDisplayItem {
384    pub common: CommonItemProperties,
385    /// The area all the glyphs should be found in. Strictly speaking this isn't
386    /// necessarily needed, but layout engines should already "know" this, and we
387    /// use it cull and size things quickly before glyph layout is done. Currently
388    /// the glyphs *can* be outside these bounds, but that should imply they
389    /// can be cut off.
390    // FIXME: these are currently sometimes ignored to keep some old wrench tests
391    // working, but we should really just fix the tests!
392    pub bounds: LayoutRect,
393    pub font_key: font::FontInstanceKey,
394    pub color: ColorF,
395    pub glyph_options: Option<font::GlyphOptions>,
396} // IMPLICIT: glyphs: Vec<font::GlyphInstance>
397
398#[derive(Clone, Copy, Debug, Default, Deserialize, MallocSizeOf, PartialEq, Serialize, PeekPoke)]
399pub struct NormalBorder {
400    pub left: BorderSide,
401    pub right: BorderSide,
402    pub top: BorderSide,
403    pub bottom: BorderSide,
404    pub radius: BorderRadius,
405    /// Whether to apply anti-aliasing on the border corners.
406    ///
407    /// Note that for this to be `false` and work, this requires the borders to
408    /// be solid, and no border-radius.
409    pub do_aa: bool,
410}
411
412impl NormalBorder {
413    fn can_disable_antialiasing(&self) -> bool {
414        fn is_valid(style: BorderStyle) -> bool {
415            style == BorderStyle::Solid || style == BorderStyle::None
416        }
417
418        self.radius.is_zero() &&
419            is_valid(self.top.style) &&
420            is_valid(self.left.style) &&
421            is_valid(self.bottom.style) &&
422            is_valid(self.right.style)
423    }
424
425    /// Normalizes a border so that we don't render disallowed stuff, like inset
426    /// borders that are less than two pixels wide.
427    #[inline]
428    pub fn normalize(&mut self, widths: &LayoutSideOffsets) {
429        debug_assert!(
430            self.do_aa || self.can_disable_antialiasing(),
431            "Unexpected disabled-antialiasing in a border, likely won't work or will be ignored"
432        );
433
434        #[inline]
435        fn renders_small_border_solid(style: BorderStyle) -> bool {
436            match style {
437                BorderStyle::Groove |
438                BorderStyle::Ridge => true,
439                _ => false,
440            }
441        }
442
443        let normalize_side = |side: &mut BorderSide, width: f32| {
444            if renders_small_border_solid(side.style) && width < 2. {
445                side.style = BorderStyle::Solid;
446            }
447        };
448
449        normalize_side(&mut self.left, widths.left);
450        normalize_side(&mut self.right, widths.right);
451        normalize_side(&mut self.top, widths.top);
452        normalize_side(&mut self.bottom, widths.bottom);
453    }
454}
455
456#[repr(u8)]
457#[derive(Debug, Copy, Clone, MallocSizeOf, PartialEq, Serialize, Deserialize, Eq, Hash, PeekPoke)]
458pub enum RepeatMode {
459    Stretch,
460    Repeat,
461    Round,
462    Space,
463}
464
465#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize, PeekPoke)]
466pub enum NinePatchBorderSource {
467    Image(ImageKey),
468    Gradient(Gradient),
469    RadialGradient(RadialGradient),
470    ConicGradient(ConicGradient),
471}
472
473#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)]
474pub struct NinePatchBorder {
475    /// Describes what to use as the 9-patch source image. If this is an image,
476    /// it will be stretched to fill the size given by width x height.
477    pub source: NinePatchBorderSource,
478
479    /// The width of the 9-part image.
480    pub width: i32,
481
482    /// The height of the 9-part image.
483    pub height: i32,
484
485    /// Distances from each edge where the image should be sliced up. These
486    /// values are in 9-part-image space (the same space as width and height),
487    /// and the resulting image parts will be used to fill the corresponding
488    /// parts of the border as given by the border widths. This can lead to
489    /// stretching.
490    /// Slices can be overlapping. In that case, the same pixels from the
491    /// 9-part image will show up in multiple parts of the resulting border.
492    pub slice: DeviceIntSideOffsets,
493
494    /// Controls whether the center of the 9 patch image is rendered or
495    /// ignored. The center is never rendered if the slices are overlapping.
496    pub fill: bool,
497
498    /// Determines what happens if the horizontal side parts of the 9-part
499    /// image have a different size than the horizontal parts of the border.
500    pub repeat_horizontal: RepeatMode,
501
502    /// Determines what happens if the vertical side parts of the 9-part
503    /// image have a different size than the vertical parts of the border.
504    pub repeat_vertical: RepeatMode,
505
506    /// The outset for the border.
507    /// TODO(mrobinson): This should be removed and handled by the client.
508    pub outset: LayoutSideOffsets, // TODO: what unit is this in?
509}
510
511#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize, PeekPoke)]
512pub enum BorderDetails {
513    Normal(NormalBorder),
514    NinePatch(NinePatchBorder),
515}
516
517#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)]
518pub struct BorderDisplayItem {
519    pub common: CommonItemProperties,
520    pub bounds: LayoutRect,
521    pub widths: LayoutSideOffsets,
522    pub details: BorderDetails,
523}
524
525#[repr(C)]
526#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize, PeekPoke)]
527pub enum BorderRadiusKind {
528    Uniform,
529    NonUniform,
530}
531
532#[repr(C)]
533#[derive(Clone, Copy, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize, PeekPoke)]
534pub struct BorderRadius {
535    pub top_left: LayoutSize,
536    pub top_right: LayoutSize,
537    pub bottom_left: LayoutSize,
538    pub bottom_right: LayoutSize,
539}
540
541impl Default for BorderRadius {
542    fn default() -> Self {
543        BorderRadius {
544            top_left: LayoutSize::zero(),
545            top_right: LayoutSize::zero(),
546            bottom_left: LayoutSize::zero(),
547            bottom_right: LayoutSize::zero(),
548        }
549    }
550}
551
552#[repr(C)]
553#[derive(Clone, Copy, Debug, Default, Deserialize, MallocSizeOf, PartialEq, Serialize, PeekPoke)]
554pub struct BorderSide {
555    pub color: ColorF,
556    pub style: BorderStyle,
557}
558
559#[repr(u32)]
560#[derive(Clone, Copy, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize, Hash, Eq, PeekPoke)]
561pub enum BorderStyle {
562    None = 0,
563    Solid = 1,
564    Double = 2,
565    Dotted = 3,
566    Dashed = 4,
567    Hidden = 5,
568    Groove = 6,
569    Ridge = 7,
570    Inset = 8,
571    Outset = 9,
572}
573
574impl BorderStyle {
575    pub fn is_hidden(self) -> bool {
576        self == BorderStyle::Hidden || self == BorderStyle::None
577    }
578}
579
580#[repr(u8)]
581#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize, PeekPoke)]
582pub enum BoxShadowClipMode {
583    Outset = 0,
584    Inset = 1,
585}
586
587#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)]
588pub struct BoxShadowDisplayItem {
589    pub common: CommonItemProperties,
590    pub box_bounds: LayoutRect,
591    pub offset: LayoutVector2D,
592    pub color: ColorF,
593    pub blur_radius: f32,
594    pub spread_radius: f32,
595    pub border_radius: BorderRadius,
596    pub clip_mode: BoxShadowClipMode,
597}
598
599#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)]
600pub struct PushShadowDisplayItem {
601    pub space_and_clip: SpaceAndClipInfo,
602    pub shadow: Shadow,
603    pub should_inflate: bool,
604}
605
606#[repr(C)]
607#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)]
608pub struct Shadow {
609    pub offset: LayoutVector2D,
610    pub color: ColorF,
611    pub blur_radius: f32,
612}
613
614#[repr(u8)]
615#[derive(Debug, Copy, Clone, Hash, Eq, MallocSizeOf, PartialEq, Serialize, Deserialize, Ord, PartialOrd, PeekPoke)]
616pub enum ExtendMode {
617    Clamp,
618    Repeat,
619}
620
621#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)]
622pub struct Gradient {
623    pub start_point: LayoutPoint,
624    pub end_point: LayoutPoint,
625    pub extend_mode: ExtendMode,
626} // IMPLICIT: stops: Vec<GradientStop>
627
628impl Gradient {
629    pub fn is_valid(&self) -> bool {
630        self.start_point.x.is_finite() &&
631            self.start_point.y.is_finite() &&
632            self.end_point.x.is_finite() &&
633            self.end_point.y.is_finite()
634    }
635}
636
637/// The area
638#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)]
639pub struct GradientDisplayItem {
640    /// NOTE: common.clip_rect is the area the gradient covers
641    pub common: CommonItemProperties,
642    /// The area to tile the gradient over (first tile starts at origin of this rect)
643    // FIXME: this should ideally just be `tile_origin` here, with the clip_rect
644    // defining the bounds of the item. Needs non-trivial backend changes.
645    pub bounds: LayoutRect,
646    /// How big a tile of the of the gradient should be (common case: bounds.size)
647    pub tile_size: LayoutSize,
648    /// The space between tiles of the gradient (common case: 0)
649    pub tile_spacing: LayoutSize,
650    pub gradient: Gradient,
651}
652
653#[repr(C)]
654#[derive(Clone, Copy, Debug, Default, Deserialize, MallocSizeOf, PartialEq, Serialize, PeekPoke)]
655pub struct GradientStop {
656    pub offset: f32,
657    pub color: ColorF,
658}
659
660#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)]
661pub struct RadialGradient {
662    pub center: LayoutPoint,
663    pub radius: LayoutSize,
664    pub start_offset: f32,
665    pub end_offset: f32,
666    pub extend_mode: ExtendMode,
667} // IMPLICIT stops: Vec<GradientStop>
668
669impl RadialGradient {
670    pub fn is_valid(&self) -> bool {
671        self.center.x.is_finite() &&
672            self.center.y.is_finite() &&
673            self.start_offset.is_finite() &&
674            self.end_offset.is_finite()
675    }
676}
677
678#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)]
679pub struct ConicGradient {
680    pub center: LayoutPoint,
681    pub angle: f32,
682    pub start_offset: f32,
683    pub end_offset: f32,
684    pub extend_mode: ExtendMode,
685} // IMPLICIT stops: Vec<GradientStop>
686
687impl ConicGradient {
688    pub fn is_valid(&self) -> bool {
689        self.center.x.is_finite() &&
690            self.center.y.is_finite() &&
691            self.angle.is_finite() &&
692            self.start_offset.is_finite() &&
693            self.end_offset.is_finite()
694    }
695}
696
697/// Just an abstraction for bundling up a bunch of clips into a "super clip".
698#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)]
699pub struct ClipChainItem {
700    pub id: ClipChainId,
701    pub parent: Option<ClipChainId>,
702} // IMPLICIT clip_ids: Vec<ClipId>
703
704#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)]
705pub struct RadialGradientDisplayItem {
706    pub common: CommonItemProperties,
707    /// The area to tile the gradient over (first tile starts at origin of this rect)
708    // FIXME: this should ideally just be `tile_origin` here, with the clip_rect
709    // defining the bounds of the item. Needs non-trivial backend changes.
710    pub bounds: LayoutRect,
711    pub gradient: RadialGradient,
712    pub tile_size: LayoutSize,
713    pub tile_spacing: LayoutSize,
714}
715
716#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)]
717pub struct ConicGradientDisplayItem {
718    pub common: CommonItemProperties,
719    /// The area to tile the gradient over (first tile starts at origin of this rect)
720    // FIXME: this should ideally just be `tile_origin` here, with the clip_rect
721    // defining the bounds of the item. Needs non-trivial backend changes.
722    pub bounds: LayoutRect,
723    pub gradient: ConicGradient,
724    pub tile_size: LayoutSize,
725    pub tile_spacing: LayoutSize,
726}
727
728/// Renders a filtered region of its backdrop
729#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)]
730pub struct BackdropFilterDisplayItem {
731    pub common: CommonItemProperties,
732}
733// IMPLICIT: filters: Vec<FilterOp>, filter_datas: Vec<FilterData>, filter_primitives: Vec<FilterPrimitive>
734
735#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)]
736pub struct ReferenceFrameDisplayListItem {
737    pub origin: LayoutPoint,
738    pub parent_spatial_id: SpatialId,
739    pub reference_frame: ReferenceFrame,
740}
741
742#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize, PeekPoke)]
743pub enum ReferenceFrameKind {
744    /// A normal transform matrix, may contain perspective (the CSS transform property)
745    Transform {
746        /// Optionally marks the transform as only ever having a simple 2D scale or translation,
747        /// allowing for optimizations.
748        is_2d_scale_translation: bool,
749        /// Marks that the transform should be snapped. Used for transforms which animate in
750        /// response to scrolling, eg for zooming or dynamic toolbar fixed-positioning.
751        should_snap: bool,
752    },
753    /// A perspective transform, that optionally scrolls relative to a specific scroll node
754    Perspective {
755        scrolling_relative_to: Option<ExternalScrollId>,
756    }
757}
758
759#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize, PeekPoke)]
760pub enum Rotation {
761    Degree0,
762    Degree90,
763    Degree180,
764    Degree270,
765}
766
767impl Rotation {
768    pub fn to_matrix(
769        &self,
770        size: LayoutSize,
771    ) -> LayoutTransform {
772        let (shift_center_to_origin, angle) = match self {
773            Rotation::Degree0 => {
774              (LayoutTransform::translation(-size.width / 2., -size.height / 2., 0.), Angle::degrees(0.))
775            },
776            Rotation::Degree90 => {
777              (LayoutTransform::translation(-size.height / 2., -size.width / 2., 0.), Angle::degrees(90.))
778            },
779            Rotation::Degree180 => {
780              (LayoutTransform::translation(-size.width / 2., -size.height / 2., 0.), Angle::degrees(180.))
781            },
782            Rotation::Degree270 => {
783              (LayoutTransform::translation(-size.height / 2., -size.width / 2., 0.), Angle::degrees(270.))
784            },
785        };
786        let shift_origin_to_center = LayoutTransform::translation(size.width / 2., size.height / 2., 0.);
787
788        shift_center_to_origin
789            .then(&LayoutTransform::rotation(0., 0., 1.0, angle))
790            .then(&shift_origin_to_center)
791    }
792}
793
794#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize, PeekPoke)]
795pub enum ReferenceTransformBinding {
796    /// Standard reference frame which contains a precomputed transform.
797    Static {
798        binding: PropertyBinding<LayoutTransform>,
799    },
800    /// Computed reference frame which dynamically calculates the transform
801    /// based on the given parameters. The reference is the content size of
802    /// the parent iframe, which is affected by snapping.
803    Computed {
804        scale_from: Option<LayoutSize>,
805        vertical_flip: bool,
806        rotation: Rotation,
807    },
808}
809
810impl Default for ReferenceTransformBinding {
811    fn default() -> Self {
812        ReferenceTransformBinding::Static {
813            binding: Default::default(),
814        }
815    }
816}
817
818#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)]
819pub struct ReferenceFrame {
820    pub kind: ReferenceFrameKind,
821    pub transform_style: TransformStyle,
822    /// The transform matrix, either the perspective matrix or the transform
823    /// matrix.
824    pub transform: ReferenceTransformBinding,
825    pub id: SpatialId,
826}
827
828#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)]
829pub struct PushStackingContextDisplayItem {
830    pub origin: LayoutPoint,
831    pub spatial_id: SpatialId,
832    pub prim_flags: PrimitiveFlags,
833    pub stacking_context: StackingContext,
834}
835
836#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)]
837pub struct StackingContext {
838    pub transform_style: TransformStyle,
839    pub mix_blend_mode: MixBlendMode,
840    pub clip_id: Option<ClipId>,
841    pub raster_space: RasterSpace,
842    pub flags: StackingContextFlags,
843}
844// IMPLICIT: filters: Vec<FilterOp>, filter_datas: Vec<FilterData>, filter_primitives: Vec<FilterPrimitive>
845
846#[repr(u8)]
847#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize, PeekPoke)]
848pub enum TransformStyle {
849    Flat = 0,
850    Preserve3D = 1,
851}
852
853/// Configure whether the contents of a stacking context
854/// should be rasterized in local space or screen space.
855/// Local space rasterized pictures are typically used
856/// when we want to cache the output, and performance is
857/// important. Note that this is a performance hint only,
858/// which WR may choose to ignore.
859#[derive(Clone, Copy, Debug, Deserialize, PartialEq, MallocSizeOf, Serialize, PeekPoke)]
860#[repr(u8)]
861pub enum RasterSpace {
862    // Rasterize in local-space, applying supplied scale to primitives.
863    // Best performance, but lower quality.
864    Local(f32),
865
866    // Rasterize the picture in screen-space, including rotation / skew etc in
867    // the rasterized element. Best quality, but slower performance. Note that
868    // any stacking context with a perspective transform will be rasterized
869    // in local-space, even if this is set.
870    Screen,
871}
872
873impl RasterSpace {
874    pub fn local_scale(self) -> Option<f32> {
875        match self {
876            RasterSpace::Local(scale) => Some(scale),
877            RasterSpace::Screen => None,
878        }
879    }
880}
881
882impl Eq for RasterSpace {}
883
884impl Hash for RasterSpace {
885    fn hash<H: Hasher>(&self, state: &mut H) {
886        match self {
887            RasterSpace::Screen => {
888                0.hash(state);
889            }
890            RasterSpace::Local(scale) => {
891                // Note: this is inconsistent with the Eq impl for -0.0 (don't care).
892                1.hash(state);
893                scale.to_bits().hash(state);
894            }
895        }
896    }
897}
898
899bitflags! {
900    #[repr(C)]
901    #[derive(Deserialize, MallocSizeOf, Serialize, PeekPoke)]
902    pub struct StackingContextFlags: u8 {
903        /// If true, this stacking context represents a backdrop root, per the CSS
904        /// filter-effects specification (see https://drafts.fxtf.org/filter-effects-2/#BackdropRoot).
905        const IS_BACKDROP_ROOT = 1 << 0;
906        /// If true, this stacking context is a blend container than contains
907        /// mix-blend-mode children (and should thus be isolated).
908        const IS_BLEND_CONTAINER = 1 << 1;
909    }
910}
911
912impl Default for StackingContextFlags {
913    fn default() -> Self {
914        StackingContextFlags::empty()
915    }
916}
917
918#[repr(u8)]
919#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize, PeekPoke)]
920pub enum MixBlendMode {
921    Normal = 0,
922    Multiply = 1,
923    Screen = 2,
924    Overlay = 3,
925    Darken = 4,
926    Lighten = 5,
927    ColorDodge = 6,
928    ColorBurn = 7,
929    HardLight = 8,
930    SoftLight = 9,
931    Difference = 10,
932    Exclusion = 11,
933    Hue = 12,
934    Saturation = 13,
935    Color = 14,
936    Luminosity = 15,
937}
938
939#[repr(C)]
940#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize, PeekPoke)]
941pub enum ColorSpace {
942    Srgb,
943    LinearRgb,
944}
945
946/// Available composite operoations for the composite filter primitive
947#[repr(C)]
948#[derive(Clone, Copy, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize, PeekPoke)]
949pub enum CompositeOperator {
950    Over,
951    In,
952    Atop,
953    Out,
954    Xor,
955    Lighter,
956    Arithmetic([f32; 4]),
957}
958
959impl CompositeOperator {
960    // This must stay in sync with the composite operator defines in cs_svg_filter.glsl
961    pub fn as_int(&self) -> u32 {
962        match self {
963            CompositeOperator::Over => 0,
964            CompositeOperator::In => 1,
965            CompositeOperator::Out => 2,
966            CompositeOperator::Atop => 3,
967            CompositeOperator::Xor => 4,
968            CompositeOperator::Lighter => 5,
969            CompositeOperator::Arithmetic(..) => 6,
970        }
971    }
972}
973
974/// An input to a SVG filter primitive.
975#[repr(C)]
976#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize, PeekPoke)]
977pub enum FilterPrimitiveInput {
978    /// The input is the original graphic that the filter is being applied to.
979    Original,
980    /// The input is the output of the previous filter primitive in the filter primitive chain.
981    Previous,
982    /// The input is the output of the filter primitive at the given index in the filter primitive chain.
983    OutputOfPrimitiveIndex(usize),
984}
985
986impl FilterPrimitiveInput {
987    /// Gets the index of the input.
988    /// Returns `None` if the source graphic is the input.
989    pub fn to_index(self, cur_index: usize) -> Option<usize> {
990        match self {
991            FilterPrimitiveInput::Previous if cur_index > 0 => Some(cur_index - 1),
992            FilterPrimitiveInput::OutputOfPrimitiveIndex(index) => Some(index),
993            _ => None,
994        }
995    }
996}
997
998#[repr(C)]
999#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)]
1000pub struct BlendPrimitive {
1001    pub input1: FilterPrimitiveInput,
1002    pub input2: FilterPrimitiveInput,
1003    pub mode: MixBlendMode,
1004}
1005
1006#[repr(C)]
1007#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)]
1008pub struct FloodPrimitive {
1009    pub color: ColorF,
1010}
1011
1012impl FloodPrimitive {
1013    pub fn sanitize(&mut self) {
1014        self.color.r = self.color.r.min(1.0).max(0.0);
1015        self.color.g = self.color.g.min(1.0).max(0.0);
1016        self.color.b = self.color.b.min(1.0).max(0.0);
1017        self.color.a = self.color.a.min(1.0).max(0.0);
1018    }
1019}
1020
1021#[repr(C)]
1022#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)]
1023pub struct BlurPrimitive {
1024    pub input: FilterPrimitiveInput,
1025    pub width: f32,
1026    pub height: f32,
1027}
1028
1029#[repr(C)]
1030#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)]
1031pub struct OpacityPrimitive {
1032    pub input: FilterPrimitiveInput,
1033    pub opacity: f32,
1034}
1035
1036impl OpacityPrimitive {
1037    pub fn sanitize(&mut self) {
1038        self.opacity = self.opacity.min(1.0).max(0.0);
1039    }
1040}
1041
1042/// cbindgen:derive-eq=false
1043#[repr(C)]
1044#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)]
1045pub struct ColorMatrixPrimitive {
1046    pub input: FilterPrimitiveInput,
1047    pub matrix: [f32; 20],
1048}
1049
1050#[repr(C)]
1051#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)]
1052pub struct DropShadowPrimitive {
1053    pub input: FilterPrimitiveInput,
1054    pub shadow: Shadow,
1055}
1056
1057#[repr(C)]
1058#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)]
1059pub struct ComponentTransferPrimitive {
1060    pub input: FilterPrimitiveInput,
1061    // Component transfer data is stored in FilterData.
1062}
1063
1064#[repr(C)]
1065#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)]
1066pub struct IdentityPrimitive {
1067    pub input: FilterPrimitiveInput,
1068}
1069
1070#[repr(C)]
1071#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)]
1072pub struct OffsetPrimitive {
1073    pub input: FilterPrimitiveInput,
1074    pub offset: LayoutVector2D,
1075}
1076
1077#[repr(C)]
1078#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)]
1079pub struct CompositePrimitive {
1080    pub input1: FilterPrimitiveInput,
1081    pub input2: FilterPrimitiveInput,
1082    pub operator: CompositeOperator,
1083}
1084
1085/// See: https://github.com/eqrion/cbindgen/issues/9
1086/// cbindgen:derive-eq=false
1087#[repr(C)]
1088#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize, PeekPoke)]
1089pub enum FilterPrimitiveKind {
1090    Identity(IdentityPrimitive),
1091    Blend(BlendPrimitive),
1092    Flood(FloodPrimitive),
1093    Blur(BlurPrimitive),
1094    // TODO: Support animated opacity?
1095    Opacity(OpacityPrimitive),
1096    /// cbindgen:derive-eq=false
1097    ColorMatrix(ColorMatrixPrimitive),
1098    DropShadow(DropShadowPrimitive),
1099    ComponentTransfer(ComponentTransferPrimitive),
1100    Offset(OffsetPrimitive),
1101    Composite(CompositePrimitive),
1102}
1103
1104impl Default for FilterPrimitiveKind {
1105    fn default() -> Self {
1106        FilterPrimitiveKind::Identity(IdentityPrimitive::default())
1107    }
1108}
1109
1110impl FilterPrimitiveKind {
1111    pub fn sanitize(&mut self) {
1112        match self {
1113            FilterPrimitiveKind::Flood(flood) => flood.sanitize(),
1114            FilterPrimitiveKind::Opacity(opacity) => opacity.sanitize(),
1115
1116            // No sanitization needed.
1117            FilterPrimitiveKind::Identity(..) |
1118            FilterPrimitiveKind::Blend(..) |
1119            FilterPrimitiveKind::ColorMatrix(..) |
1120            FilterPrimitiveKind::Offset(..) |
1121            FilterPrimitiveKind::Composite(..) |
1122            FilterPrimitiveKind::Blur(..) |
1123            FilterPrimitiveKind::DropShadow(..) |
1124            // Component transfer's filter data is sanitized separately.
1125            FilterPrimitiveKind::ComponentTransfer(..) => {}
1126        }
1127    }
1128}
1129
1130/// SVG Filter Primitive.
1131/// See: https://github.com/eqrion/cbindgen/issues/9
1132/// cbindgen:derive-eq=false
1133#[repr(C)]
1134#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)]
1135pub struct FilterPrimitive {
1136    pub kind: FilterPrimitiveKind,
1137    pub color_space: ColorSpace,
1138}
1139
1140impl FilterPrimitive {
1141    pub fn sanitize(&mut self) {
1142        self.kind.sanitize();
1143    }
1144}
1145
1146/// CSS filter.
1147#[repr(C)]
1148#[derive(Clone, Copy, Debug, PartialEq, Deserialize, Serialize, PeekPoke)]
1149pub enum FilterOp {
1150    /// Filter that does no transformation of the colors, needed for
1151    /// debug purposes only.
1152    Identity,
1153    Blur(f32, f32),
1154    Brightness(f32),
1155    Contrast(f32),
1156    Grayscale(f32),
1157    HueRotate(f32),
1158    Invert(f32),
1159    Opacity(PropertyBinding<f32>, f32),
1160    Saturate(f32),
1161    Sepia(f32),
1162    DropShadow(Shadow),
1163    ColorMatrix([f32; 20]),
1164    SrgbToLinear,
1165    LinearToSrgb,
1166    ComponentTransfer,
1167    Flood(ColorF),
1168}
1169
1170#[repr(u8)]
1171#[derive(Clone, Copy, Debug, PartialEq, Deserialize, Serialize, PeekPoke)]
1172pub enum ComponentTransferFuncType {
1173  Identity = 0,
1174  Table = 1,
1175  Discrete = 2,
1176  Linear = 3,
1177  Gamma = 4,
1178}
1179
1180#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
1181pub struct FilterData {
1182    pub func_r_type: ComponentTransferFuncType,
1183    pub r_values: Vec<f32>,
1184    pub func_g_type: ComponentTransferFuncType,
1185    pub g_values: Vec<f32>,
1186    pub func_b_type: ComponentTransferFuncType,
1187    pub b_values: Vec<f32>,
1188    pub func_a_type: ComponentTransferFuncType,
1189    pub a_values: Vec<f32>,
1190}
1191
1192fn sanitize_func_type(
1193    func_type: ComponentTransferFuncType,
1194    values: &[f32],
1195) -> ComponentTransferFuncType {
1196    if values.is_empty() {
1197        return ComponentTransferFuncType::Identity;
1198    }
1199    if values.len() < 2 && func_type == ComponentTransferFuncType::Linear {
1200        return ComponentTransferFuncType::Identity;
1201    }
1202    if values.len() < 3 && func_type == ComponentTransferFuncType::Gamma {
1203        return ComponentTransferFuncType::Identity;
1204    }
1205    func_type
1206}
1207
1208fn sanitize_values(
1209    func_type: ComponentTransferFuncType,
1210    values: &[f32],
1211) -> bool {
1212    if values.len() < 2 && func_type == ComponentTransferFuncType::Linear {
1213        return false;
1214    }
1215    if values.len() < 3 && func_type == ComponentTransferFuncType::Gamma {
1216        return false;
1217    }
1218    true
1219}
1220
1221impl FilterData {
1222    /// Ensure that the number of values matches up with the function type.
1223    pub fn sanitize(&self) -> FilterData {
1224        FilterData {
1225            func_r_type: sanitize_func_type(self.func_r_type, &self.r_values),
1226            r_values:
1227                    if sanitize_values(self.func_r_type, &self.r_values) {
1228                        self.r_values.clone()
1229                    } else {
1230                        Vec::new()
1231                    },
1232            func_g_type: sanitize_func_type(self.func_g_type, &self.g_values),
1233            g_values:
1234                    if sanitize_values(self.func_g_type, &self.g_values) {
1235                        self.g_values.clone()
1236                    } else {
1237                        Vec::new()
1238                    },
1239
1240            func_b_type: sanitize_func_type(self.func_b_type, &self.b_values),
1241            b_values:
1242                    if sanitize_values(self.func_b_type, &self.b_values) {
1243                        self.b_values.clone()
1244                    } else {
1245                        Vec::new()
1246                    },
1247
1248            func_a_type: sanitize_func_type(self.func_a_type, &self.a_values),
1249            a_values:
1250                    if sanitize_values(self.func_a_type, &self.a_values) {
1251                        self.a_values.clone()
1252                    } else {
1253                        Vec::new()
1254                    },
1255
1256        }
1257    }
1258
1259    pub fn is_identity(&self) -> bool {
1260        self.func_r_type == ComponentTransferFuncType::Identity &&
1261        self.func_g_type == ComponentTransferFuncType::Identity &&
1262        self.func_b_type == ComponentTransferFuncType::Identity &&
1263        self.func_a_type == ComponentTransferFuncType::Identity
1264    }
1265}
1266
1267#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)]
1268pub struct IframeDisplayItem {
1269    pub bounds: LayoutRect,
1270    pub clip_rect: LayoutRect,
1271    pub space_and_clip: SpaceAndClipInfo,
1272    pub pipeline_id: PipelineId,
1273    pub ignore_missing_pipeline: bool,
1274}
1275
1276/// This describes an image that fills the specified area. It stretches or shrinks
1277/// the image as necessary. While RepeatingImageDisplayItem could otherwise provide
1278/// a superset of the functionality, it has been problematic inferring the desired
1279/// repetition properties when snapping changes the size of the primitive.
1280#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)]
1281pub struct ImageDisplayItem {
1282    pub common: CommonItemProperties,
1283    /// The area to tile the image over (first tile starts at origin of this rect)
1284    // FIXME: this should ideally just be `tile_origin` here, with the clip_rect
1285    // defining the bounds of the item. Needs non-trivial backend changes.
1286    pub bounds: LayoutRect,
1287    pub image_key: ImageKey,
1288    pub image_rendering: ImageRendering,
1289    pub alpha_type: AlphaType,
1290    /// A hack used by gecko to color a simple bitmap font used for tofu glyphs
1291    pub color: ColorF,
1292}
1293
1294/// This describes a background-image and its tiling. It repeats in a grid to fill
1295/// the specified area.
1296#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)]
1297pub struct RepeatingImageDisplayItem {
1298    pub common: CommonItemProperties,
1299    /// The area to tile the image over (first tile starts at origin of this rect)
1300    // FIXME: this should ideally just be `tile_origin` here, with the clip_rect
1301    // defining the bounds of the item. Needs non-trivial backend changes.
1302    pub bounds: LayoutRect,
1303    /// How large to make a single tile of the image (common case: bounds.size)
1304    pub stretch_size: LayoutSize,
1305    /// The space between tiles (common case: 0)
1306    pub tile_spacing: LayoutSize,
1307    pub image_key: ImageKey,
1308    pub image_rendering: ImageRendering,
1309    pub alpha_type: AlphaType,
1310    /// A hack used by gecko to color a simple bitmap font used for tofu glyphs
1311    pub color: ColorF,
1312}
1313
1314#[repr(u8)]
1315#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize, PeekPoke)]
1316pub enum ImageRendering {
1317    Auto = 0,
1318    CrispEdges = 1,
1319    Pixelated = 2,
1320}
1321
1322#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize, PeekPoke)]
1323pub enum AlphaType {
1324    Alpha = 0,
1325    PremultipliedAlpha = 1,
1326}
1327
1328#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)]
1329pub struct YuvImageDisplayItem {
1330    pub common: CommonItemProperties,
1331    pub bounds: LayoutRect,
1332    pub yuv_data: YuvData,
1333    pub color_depth: ColorDepth,
1334    pub color_space: YuvColorSpace,
1335    pub color_range: ColorRange,
1336    pub image_rendering: ImageRendering,
1337}
1338
1339#[repr(u8)]
1340#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize, PeekPoke)]
1341pub enum YuvColorSpace {
1342    Rec601 = 0,
1343    Rec709 = 1,
1344    Rec2020 = 2,
1345    Identity = 3, // aka GBR as per ISO/IEC 23091-2:2019
1346}
1347
1348#[repr(u8)]
1349#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize, PeekPoke)]
1350pub enum ColorRange {
1351    Limited = 0,
1352    Full = 1,
1353}
1354
1355#[repr(u8)]
1356#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize, PeekPoke)]
1357pub enum YuvRangedColorSpace {
1358    Rec601Narrow = 0,
1359    Rec601Full = 1,
1360    Rec709Narrow = 2,
1361    Rec709Full = 3,
1362    Rec2020Narrow = 4,
1363    Rec2020Full = 5,
1364    GbrIdentity = 6,
1365}
1366
1367impl YuvColorSpace {
1368    pub fn with_range(self, range: ColorRange) -> YuvRangedColorSpace {
1369        match self {
1370            YuvColorSpace::Identity => YuvRangedColorSpace::GbrIdentity,
1371            YuvColorSpace::Rec601 => {
1372                match range {
1373                    ColorRange::Limited => YuvRangedColorSpace::Rec601Narrow,
1374                    ColorRange::Full => YuvRangedColorSpace::Rec601Full,
1375                }
1376            }
1377            YuvColorSpace::Rec709 => {
1378                match range {
1379                    ColorRange::Limited => YuvRangedColorSpace::Rec709Narrow,
1380                    ColorRange::Full => YuvRangedColorSpace::Rec709Full,
1381                }
1382            }
1383            YuvColorSpace::Rec2020 => {
1384                match range {
1385                    ColorRange::Limited => YuvRangedColorSpace::Rec2020Narrow,
1386                    ColorRange::Full => YuvRangedColorSpace::Rec2020Full,
1387                }
1388            }
1389        }
1390    }
1391}
1392
1393#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize, PeekPoke)]
1394pub enum YuvData {
1395    NV12(ImageKey, ImageKey), // (Y channel, CbCr interleaved channel)
1396    PlanarYCbCr(ImageKey, ImageKey, ImageKey), // (Y channel, Cb channel, Cr Channel)
1397    InterleavedYCbCr(ImageKey), // (YCbCr interleaved channel)
1398}
1399
1400impl YuvData {
1401    pub fn get_format(&self) -> YuvFormat {
1402        match *self {
1403            YuvData::NV12(..) => YuvFormat::NV12,
1404            YuvData::PlanarYCbCr(..) => YuvFormat::PlanarYCbCr,
1405            YuvData::InterleavedYCbCr(..) => YuvFormat::InterleavedYCbCr,
1406        }
1407    }
1408}
1409
1410#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize, PeekPoke)]
1411pub enum YuvFormat {
1412    NV12 = 0,
1413    PlanarYCbCr = 1,
1414    InterleavedYCbCr = 2,
1415}
1416
1417impl YuvFormat {
1418    pub fn get_plane_num(self) -> usize {
1419        match self {
1420            YuvFormat::NV12 => 2,
1421            YuvFormat::PlanarYCbCr => 3,
1422            YuvFormat::InterleavedYCbCr => 1,
1423        }
1424    }
1425}
1426
1427#[repr(C)]
1428#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)]
1429pub struct ImageMask {
1430    pub image: ImageKey,
1431    pub rect: LayoutRect,
1432    pub repeat: bool,
1433}
1434
1435impl ImageMask {
1436    /// Get a local clipping rect contributed by this mask.
1437    pub fn get_local_clip_rect(&self) -> Option<LayoutRect> {
1438        if self.repeat {
1439            None
1440        } else {
1441            Some(self.rect)
1442        }
1443    }
1444}
1445
1446#[repr(C)]
1447#[derive(Copy, Clone, Debug, MallocSizeOf, PartialEq, Serialize, Deserialize, Eq, Hash, PeekPoke)]
1448pub enum ClipMode {
1449    Clip,    // Pixels inside the region are visible.
1450    ClipOut, // Pixels outside the region are visible.
1451}
1452
1453impl Not for ClipMode {
1454    type Output = ClipMode;
1455
1456    fn not(self) -> ClipMode {
1457        match self {
1458            ClipMode::Clip => ClipMode::ClipOut,
1459            ClipMode::ClipOut => ClipMode::Clip,
1460        }
1461    }
1462}
1463
1464#[repr(C)]
1465#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)]
1466pub struct ComplexClipRegion {
1467    /// The boundaries of the rectangle.
1468    pub rect: LayoutRect,
1469    /// Border radii of this rectangle.
1470    pub radii: BorderRadius,
1471    /// Whether we are clipping inside or outside
1472    /// the region.
1473    pub mode: ClipMode,
1474}
1475
1476impl BorderRadius {
1477    pub fn zero() -> BorderRadius {
1478        BorderRadius {
1479            top_left: LayoutSize::new(0.0, 0.0),
1480            top_right: LayoutSize::new(0.0, 0.0),
1481            bottom_left: LayoutSize::new(0.0, 0.0),
1482            bottom_right: LayoutSize::new(0.0, 0.0),
1483        }
1484    }
1485
1486    pub fn uniform(radius: f32) -> BorderRadius {
1487        BorderRadius {
1488            top_left: LayoutSize::new(radius, radius),
1489            top_right: LayoutSize::new(radius, radius),
1490            bottom_left: LayoutSize::new(radius, radius),
1491            bottom_right: LayoutSize::new(radius, radius),
1492        }
1493    }
1494
1495    pub fn uniform_size(radius: LayoutSize) -> BorderRadius {
1496        BorderRadius {
1497            top_left: radius,
1498            top_right: radius,
1499            bottom_left: radius,
1500            bottom_right: radius,
1501        }
1502    }
1503
1504    pub fn is_uniform(&self) -> Option<f32> {
1505        match self.is_uniform_size() {
1506            Some(radius) if radius.width == radius.height => Some(radius.width),
1507            _ => None,
1508        }
1509    }
1510
1511    pub fn is_uniform_size(&self) -> Option<LayoutSize> {
1512        let uniform_radius = self.top_left;
1513        if self.top_right == uniform_radius && self.bottom_left == uniform_radius &&
1514            self.bottom_right == uniform_radius
1515        {
1516            Some(uniform_radius)
1517        } else {
1518            None
1519        }
1520    }
1521
1522    /// Return whether, in each corner, the radius in *either* direction is zero.
1523    /// This means that none of the corners are rounded.
1524    pub fn is_zero(&self) -> bool {
1525        let corner_is_zero = |corner: &LayoutSize| corner.width == 0.0 || corner.height == 0.0;
1526        corner_is_zero(&self.top_left) &&
1527        corner_is_zero(&self.top_right) &&
1528        corner_is_zero(&self.bottom_right) &&
1529        corner_is_zero(&self.bottom_left)
1530    }
1531}
1532
1533impl ComplexClipRegion {
1534    /// Create a new complex clip region.
1535    pub fn new(
1536        rect: LayoutRect,
1537        radii: BorderRadius,
1538        mode: ClipMode,
1539    ) -> Self {
1540        ComplexClipRegion { rect, radii, mode }
1541    }
1542}
1543
1544impl ComplexClipRegion {
1545    /// Get a local clipping rect contributed by this clip region.
1546    pub fn get_local_clip_rect(&self) -> Option<LayoutRect> {
1547        match self.mode {
1548            ClipMode::Clip => {
1549                Some(self.rect)
1550            }
1551            ClipMode::ClipOut => {
1552                None
1553            }
1554        }
1555    }
1556}
1557
1558pub const POLYGON_CLIP_VERTEX_MAX: usize = 16;
1559
1560#[repr(u8)]
1561#[derive(Clone, Copy, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize, Eq, Hash, PeekPoke)]
1562pub enum FillRule {
1563    Nonzero = 0x1, // Behaves as the SVG fill-rule definition for nonzero.
1564    Evenodd = 0x2, // Behaves as the SVG fill-rule definition for evenodd.
1565}
1566
1567impl From<u8> for FillRule {
1568    fn from(fill_rule: u8) -> Self {
1569        match fill_rule {
1570            0x1 => FillRule::Nonzero,
1571            0x2 => FillRule::Evenodd,
1572            _ => panic!("Unexpected FillRule value."),
1573        }
1574    }
1575}
1576
1577impl From<FillRule> for u8 {
1578    fn from(fill_rule: FillRule) -> Self {
1579        match fill_rule {
1580            FillRule::Nonzero => 0x1,
1581            FillRule::Evenodd => 0x2,
1582        }
1583    }
1584}
1585
1586#[derive(Clone, Copy, Debug, Default, Deserialize, Eq, Hash, PartialEq, Serialize, PeekPoke)]
1587pub struct ClipChainId(pub u64, pub PipelineId);
1588
1589/// A reference to a clipping node defining how an item is clipped.
1590#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize, PeekPoke)]
1591pub enum ClipId {
1592    Clip(usize, PipelineId),
1593    ClipChain(ClipChainId),
1594}
1595
1596const ROOT_CLIP_ID: usize = 0;
1597
1598impl ClipId {
1599    /// Return the root clip ID - effectively doing no clipping.
1600    pub fn root(pipeline_id: PipelineId) -> Self {
1601        ClipId::Clip(ROOT_CLIP_ID, pipeline_id)
1602    }
1603
1604    /// Return an invalid clip ID - needed in places where we carry
1605    /// one but need to not attempt to use it.
1606    pub fn invalid() -> Self {
1607        ClipId::Clip(!0, PipelineId::dummy())
1608    }
1609
1610    pub fn pipeline_id(&self) -> PipelineId {
1611        match *self {
1612            ClipId::Clip(_, pipeline_id) |
1613            ClipId::ClipChain(ClipChainId(_, pipeline_id)) => pipeline_id,
1614        }
1615    }
1616
1617    pub fn is_root(&self) -> bool {
1618        match *self {
1619            ClipId::Clip(id, _) => id == ROOT_CLIP_ID,
1620            ClipId::ClipChain(_) => false,
1621        }
1622    }
1623
1624    pub fn is_valid(&self) -> bool {
1625        match *self {
1626            ClipId::Clip(id, _) => id != !0,
1627            _ => true,
1628        }
1629    }
1630}
1631
1632/// A reference to a spatial node defining item positioning.
1633#[derive(Clone, Copy, Debug, Default, Deserialize, Eq, Hash, PartialEq, Serialize, PeekPoke)]
1634pub struct SpatialId(pub usize, PipelineId);
1635
1636const ROOT_REFERENCE_FRAME_SPATIAL_ID: usize = 0;
1637const ROOT_SCROLL_NODE_SPATIAL_ID: usize = 1;
1638
1639impl SpatialId {
1640    pub fn new(spatial_node_index: usize, pipeline_id: PipelineId) -> Self {
1641        SpatialId(spatial_node_index, pipeline_id)
1642    }
1643
1644    pub fn root_reference_frame(pipeline_id: PipelineId) -> Self {
1645        SpatialId(ROOT_REFERENCE_FRAME_SPATIAL_ID, pipeline_id)
1646    }
1647
1648    pub fn root_scroll_node(pipeline_id: PipelineId) -> Self {
1649        SpatialId(ROOT_SCROLL_NODE_SPATIAL_ID, pipeline_id)
1650    }
1651
1652    pub fn pipeline_id(&self) -> PipelineId {
1653        self.1
1654    }
1655
1656    pub fn is_root_reference_frame(&self) -> bool {
1657        self.0 == ROOT_REFERENCE_FRAME_SPATIAL_ID
1658    }
1659
1660    pub fn is_root_scroll_node(&self) -> bool {
1661        self.0 == ROOT_SCROLL_NODE_SPATIAL_ID
1662    }
1663}
1664
1665/// An external identifier that uniquely identifies a scroll frame independent of its ClipId, which
1666/// may change from frame to frame. This should be unique within a pipeline. WebRender makes no
1667/// attempt to ensure uniqueness. The zero value is reserved for use by the root scroll node of
1668/// every pipeline, which always has an external id.
1669///
1670/// When setting display lists with the `preserve_frame_state` this id is used to preserve scroll
1671/// offsets between different sets of SpatialNodes which are ScrollFrames.
1672#[derive(Clone, Copy, Debug, Default, Deserialize, Eq, Hash, PartialEq, Serialize, PeekPoke)]
1673#[repr(C)]
1674pub struct ExternalScrollId(pub u64, pub PipelineId);
1675
1676impl ExternalScrollId {
1677    pub fn pipeline_id(&self) -> PipelineId {
1678        self.1
1679    }
1680
1681    pub fn is_root(&self) -> bool {
1682        self.0 == 0
1683    }
1684}
1685
1686impl DisplayItem {
1687    pub fn debug_name(&self) -> &'static str {
1688        match *self {
1689            DisplayItem::Border(..) => "border",
1690            DisplayItem::BoxShadow(..) => "box_shadow",
1691            DisplayItem::ClearRectangle(..) => "clear_rectangle",
1692            DisplayItem::HitTest(..) => "hit_test",
1693            DisplayItem::RectClip(..) => "rect_clip",
1694            DisplayItem::RoundedRectClip(..) => "rounded_rect_clip",
1695            DisplayItem::ImageMaskClip(..) => "image_mask_clip",
1696            DisplayItem::ClipChain(..) => "clip_chain",
1697            DisplayItem::ConicGradient(..) => "conic_gradient",
1698            DisplayItem::Gradient(..) => "gradient",
1699            DisplayItem::Iframe(..) => "iframe",
1700            DisplayItem::Image(..) => "image",
1701            DisplayItem::RepeatingImage(..) => "repeating_image",
1702            DisplayItem::Line(..) => "line",
1703            DisplayItem::PopAllShadows => "pop_all_shadows",
1704            DisplayItem::PopReferenceFrame => "pop_reference_frame",
1705            DisplayItem::PopStackingContext => "pop_stacking_context",
1706            DisplayItem::PushShadow(..) => "push_shadow",
1707            DisplayItem::PushReferenceFrame(..) => "push_reference_frame",
1708            DisplayItem::PushStackingContext(..) => "push_stacking_context",
1709            DisplayItem::SetFilterOps => "set_filter_ops",
1710            DisplayItem::SetFilterData => "set_filter_data",
1711            DisplayItem::SetFilterPrimitives => "set_filter_primitives",
1712            DisplayItem::SetPoints => "set_points",
1713            DisplayItem::RadialGradient(..) => "radial_gradient",
1714            DisplayItem::Rectangle(..) => "rectangle",
1715            DisplayItem::ScrollFrame(..) => "scroll_frame",
1716            DisplayItem::SetGradientStops => "set_gradient_stops",
1717            DisplayItem::ReuseItems(..) => "reuse_item",
1718            DisplayItem::RetainedItems(..) => "retained_items",
1719            DisplayItem::StickyFrame(..) => "sticky_frame",
1720            DisplayItem::Text(..) => "text",
1721            DisplayItem::YuvImage(..) => "yuv_image",
1722            DisplayItem::BackdropFilter(..) => "backdrop_filter",
1723        }
1724    }
1725}
1726
1727macro_rules! impl_default_for_enums {
1728    ($($enum:ident => $init:expr ),+) => {
1729        $(impl Default for $enum {
1730            #[allow(unused_imports)]
1731            fn default() -> Self {
1732                use $enum::*;
1733                $init
1734            }
1735        })*
1736    }
1737}
1738
1739impl_default_for_enums! {
1740    DisplayItem => PopStackingContext,
1741    ScrollSensitivity => ScriptAndInputEvents,
1742    LineOrientation => Vertical,
1743    LineStyle => Solid,
1744    RepeatMode => Stretch,
1745    NinePatchBorderSource => Image(ImageKey::default()),
1746    BorderDetails => Normal(NormalBorder::default()),
1747    BorderRadiusKind => Uniform,
1748    BorderStyle => None,
1749    BoxShadowClipMode => Outset,
1750    ExtendMode => Clamp,
1751    FilterOp => Identity,
1752    ComponentTransferFuncType => Identity,
1753    ClipMode => Clip,
1754    FillRule => Nonzero,
1755    ClipId => ClipId::invalid(),
1756    ReferenceFrameKind => Transform {
1757        is_2d_scale_translation: false,
1758        should_snap: false,
1759    },
1760    Rotation => Degree0,
1761    TransformStyle => Flat,
1762    RasterSpace => Local(f32::default()),
1763    MixBlendMode => Normal,
1764    ImageRendering => Auto,
1765    AlphaType => Alpha,
1766    YuvColorSpace => Rec601,
1767    YuvRangedColorSpace => Rec601Narrow,
1768    ColorRange => Limited,
1769    YuvData => NV12(ImageKey::default(), ImageKey::default()),
1770    YuvFormat => NV12,
1771    FilterPrimitiveInput => Original,
1772    ColorSpace => Srgb,
1773    CompositeOperator => Over
1774}