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, SnapshotImageKey};
10use crate::{APZScrollGeneration, HasScrollLinkedEffect, PipelineId, PropertyBinding};
11use crate::serde::{Serialize, Deserialize};
12use crate::color::ColorF;
13use crate::image::{ColorDepth, ImageKey};
14use crate::units::*;
15use std::hash::{Hash, Hasher};
16
17// ******************************************************************
18// * NOTE: some of these structs have an "IMPLICIT" comment.        *
19// * This indicates that the BuiltDisplayList will have serialized  *
20// * a list of values nearby that this item consumes. The traversal *
21// * iterator should handle finding these. DebugDisplayItem should  *
22// * make them explicit.                                            *
23// ******************************************************************
24
25/// A tag that can be used to identify items during hit testing. If the tag
26/// is missing then the item doesn't take part in hit testing at all. This
27/// is composed of two numbers. In Servo, the first is an identifier while the
28/// second is used to select the cursor that should be used during mouse
29/// movement. In Gecko, the first is a scrollframe identifier, while the second
30/// is used to store various flags that APZ needs to properly process input
31/// events.
32pub type ItemTag = (u64, u16);
33
34/// An identifier used to refer to previously sent display items. Currently it
35/// refers to individual display items, but this may change later.
36pub type ItemKey = u16;
37
38#[repr(C)]
39#[derive(Copy, PartialEq, Eq, Clone, PartialOrd, Ord, Hash, Deserialize, MallocSizeOf, Serialize, PeekPoke)]
40pub struct PrimitiveFlags(u8);
41
42bitflags! {
43    impl PrimitiveFlags: u8 {
44        /// The CSS backface-visibility property (yes, it can be really granular)
45        const IS_BACKFACE_VISIBLE = 1 << 0;
46        /// If set, this primitive represents a scroll bar container
47        const IS_SCROLLBAR_CONTAINER = 1 << 1;
48        /// This is used as a performance hint - this primitive may be promoted to a native
49        /// compositor surface under certain (implementation specific) conditions. This
50        /// is typically used for large videos, and canvas elements.
51        const PREFER_COMPOSITOR_SURFACE = 1 << 2;
52        /// If set, this primitive can be passed directly to the compositor via its
53        /// ExternalImageId, and the compositor will use the native image directly.
54        /// Used as a further extension on top of PREFER_COMPOSITOR_SURFACE.
55        const SUPPORTS_EXTERNAL_COMPOSITOR_SURFACE = 1 << 3;
56        /// This flags disables snapping and forces anti-aliasing even if the primitive is axis-aligned.
57        const ANTIALISED = 1 << 4;
58        /// If true, this primitive is used as a background for checkerboarding
59        const CHECKERBOARD_BACKGROUND = 1 << 5;
60    }
61}
62
63impl core::fmt::Debug for PrimitiveFlags {
64    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
65        if self.is_empty() {
66            write!(f, "{:#x}", Self::empty().bits())
67        } else {
68            bitflags::parser::to_writer(self, f)
69        }
70    }
71}
72
73impl Default for PrimitiveFlags {
74    fn default() -> Self {
75        PrimitiveFlags::IS_BACKFACE_VISIBLE
76    }
77}
78
79/// A grouping of fields a lot of display items need, just to avoid
80/// repeating these over and over in this file.
81#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)]
82pub struct CommonItemProperties {
83    /// Bounds of the display item to clip to. Many items are logically
84    /// infinite, and rely on this clip_rect to define their bounds
85    /// (solid colors, background-images, gradients, etc).
86    pub clip_rect: LayoutRect,
87    /// Additional clips
88    pub clip_chain_id: ClipChainId,
89    /// The coordinate-space the item is in (yes, it can be really granular)
90    pub spatial_id: SpatialId,
91    /// Various flags describing properties of this primitive.
92    pub flags: PrimitiveFlags,
93}
94
95impl CommonItemProperties {
96    /// Convenience for tests.
97    pub fn new(
98        clip_rect: LayoutRect,
99        space_and_clip: SpaceAndClipInfo,
100    ) -> Self {
101        Self {
102            clip_rect,
103            spatial_id: space_and_clip.spatial_id,
104            clip_chain_id: space_and_clip.clip_chain_id,
105            flags: PrimitiveFlags::default(),
106        }
107    }
108}
109
110/// Per-primitive information about the nodes in the clip tree and
111/// the spatial tree that the primitive belongs to.
112///
113/// Note: this is a separate struct from `PrimitiveInfo` because
114/// it needs indirectional mapping during the DL flattening phase,
115/// turning into `ScrollNodeAndClipChain`.
116#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)]
117pub struct SpaceAndClipInfo {
118    pub spatial_id: SpatialId,
119    pub clip_chain_id: ClipChainId,
120}
121
122impl SpaceAndClipInfo {
123    /// Create a new space/clip info associated with the root
124    /// scroll frame.
125    pub fn root_scroll(pipeline_id: PipelineId) -> Self {
126        SpaceAndClipInfo {
127            spatial_id: SpatialId::root_scroll_node(pipeline_id),
128            clip_chain_id: ClipChainId::INVALID,
129        }
130    }
131}
132
133/// Defines a caller provided key that is unique for a given spatial node, and is stable across
134/// display lists. WR uses this to determine which spatial nodes are added / removed for a new
135/// display list. The content itself is arbitrary and opaque to WR, the only thing that matters
136/// is that it's unique and stable between display lists.
137#[repr(C)]
138#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize, PeekPoke, Default, Eq, Hash)]
139pub struct SpatialTreeItemKey {
140    key0: u64,
141    key1: u64,
142}
143
144impl SpatialTreeItemKey {
145    pub fn new(key0: u64, key1: u64) -> Self {
146        SpatialTreeItemKey {
147            key0,
148            key1,
149        }
150    }
151}
152
153#[repr(u8)]
154#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize, PeekPoke)]
155pub enum SpatialTreeItem {
156    ScrollFrame(ScrollFrameDescriptor),
157    ReferenceFrame(ReferenceFrameDescriptor),
158    StickyFrame(StickyFrameDescriptor),
159    Invalid,
160}
161
162#[repr(u8)]
163#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize, PeekPoke)]
164pub enum DisplayItem {
165    // These are the "real content" display items
166    Rectangle(RectangleDisplayItem),
167    ClearRectangle(ClearRectangleDisplayItem),
168    HitTest(HitTestDisplayItem),
169    Text(TextDisplayItem),
170    Line(LineDisplayItem),
171    Border(BorderDisplayItem),
172    BoxShadow(BoxShadowDisplayItem),
173    PushShadow(PushShadowDisplayItem),
174    Gradient(GradientDisplayItem),
175    RadialGradient(RadialGradientDisplayItem),
176    ConicGradient(ConicGradientDisplayItem),
177    Image(ImageDisplayItem),
178    RepeatingImage(RepeatingImageDisplayItem),
179    YuvImage(YuvImageDisplayItem),
180    BackdropFilter(BackdropFilterDisplayItem),
181
182    // Clips
183    RectClip(RectClipDisplayItem),
184    RoundedRectClip(RoundedRectClipDisplayItem),
185    ImageMaskClip(ImageMaskClipDisplayItem),
186    ClipChain(ClipChainItem),
187
188    // Spaces and Frames that content can be scoped under.
189    Iframe(IframeDisplayItem),
190    PushReferenceFrame(ReferenceFrameDisplayListItem),
191    PushStackingContext(PushStackingContextDisplayItem),
192
193    // These marker items indicate an array of data follows, to be used for the
194    // next non-marker item.
195    SetGradientStops,
196    SetFilterOps,
197    SetFilterData,
198    SetFilterPrimitives,
199    SetPoints,
200
201    // These marker items terminate a scope introduced by a previous item.
202    PopReferenceFrame,
203    PopStackingContext,
204    PopAllShadows,
205
206    ReuseItems(ItemKey),
207    RetainedItems(ItemKey),
208
209    // For debugging purposes.
210    DebugMarker(u32),
211}
212
213/// This is a "complete" version of the DisplayItem, with all implicit trailing
214/// arrays included, for debug serialization (captures).
215#[cfg(any(feature = "serialize", feature = "deserialize"))]
216#[cfg_attr(feature = "serialize", derive(Serialize))]
217#[cfg_attr(feature = "deserialize", derive(Deserialize))]
218pub enum DebugDisplayItem {
219    Rectangle(RectangleDisplayItem),
220    ClearRectangle(ClearRectangleDisplayItem),
221    HitTest(HitTestDisplayItem),
222    Text(TextDisplayItem, Vec<font::GlyphInstance>),
223    Line(LineDisplayItem),
224    Border(BorderDisplayItem),
225    BoxShadow(BoxShadowDisplayItem),
226    PushShadow(PushShadowDisplayItem),
227    Gradient(GradientDisplayItem),
228    RadialGradient(RadialGradientDisplayItem),
229    ConicGradient(ConicGradientDisplayItem),
230    Image(ImageDisplayItem),
231    RepeatingImage(RepeatingImageDisplayItem),
232    YuvImage(YuvImageDisplayItem),
233    BackdropFilter(BackdropFilterDisplayItem),
234
235    ImageMaskClip(ImageMaskClipDisplayItem),
236    RoundedRectClip(RoundedRectClipDisplayItem),
237    RectClip(RectClipDisplayItem),
238    ClipChain(ClipChainItem, Vec<ClipId>),
239
240    Iframe(IframeDisplayItem),
241    PushReferenceFrame(ReferenceFrameDisplayListItem),
242    PushStackingContext(PushStackingContextDisplayItem),
243
244    SetGradientStops(Vec<GradientStop>),
245    SetFilterOps(Vec<FilterOp>),
246    SetFilterData(FilterData),
247    SetFilterPrimitives(Vec<FilterPrimitive>),
248    SetPoints(Vec<LayoutPoint>),
249
250    PopReferenceFrame,
251    PopStackingContext,
252    PopAllShadows,
253
254    DebugMarker(u32)
255}
256
257#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)]
258pub struct ImageMaskClipDisplayItem {
259    pub id: ClipId,
260    pub spatial_id: SpatialId,
261    pub image_mask: ImageMask,
262    pub fill_rule: FillRule,
263} // IMPLICIT points: Vec<LayoutPoint>
264
265#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)]
266pub struct RectClipDisplayItem {
267    pub id: ClipId,
268    pub spatial_id: SpatialId,
269    pub clip_rect: LayoutRect,
270}
271
272#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)]
273pub struct RoundedRectClipDisplayItem {
274    pub id: ClipId,
275    pub spatial_id: SpatialId,
276    pub clip: ComplexClipRegion,
277}
278
279/// The minimum and maximum allowable offset for a sticky frame in a single dimension.
280#[repr(C)]
281#[derive(Clone, Copy, Debug, Default, Deserialize, MallocSizeOf, PartialEq, Serialize, PeekPoke)]
282pub struct StickyOffsetBounds {
283    /// The minimum offset for this frame, typically a negative value, which specifies how
284    /// far in the negative direction the sticky frame can offset its contents in this
285    /// dimension.
286    pub min: f32,
287
288    /// The maximum offset for this frame, typically a positive value, which specifies how
289    /// far in the positive direction the sticky frame can offset its contents in this
290    /// dimension.
291    pub max: f32,
292}
293
294impl StickyOffsetBounds {
295    pub fn new(min: f32, max: f32) -> StickyOffsetBounds {
296        StickyOffsetBounds { min, max }
297    }
298}
299
300#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)]
301pub struct StickyFrameDescriptor {
302    pub id: SpatialId,
303    pub parent_spatial_id: SpatialId,
304    pub bounds: LayoutRect,
305
306    /// The margins that should be maintained between the edge of the parent viewport and this
307    /// sticky frame. A margin of None indicates that the sticky frame should not stick at all
308    /// to that particular edge of the viewport.
309    pub margins: SideOffsets2D<Option<f32>, LayoutPixel>,
310
311    /// The minimum and maximum vertical offsets for this sticky frame. Ignoring these constraints,
312    /// the sticky frame will continue to stick to the edge of the viewport as its original
313    /// position is scrolled out of view. Constraints specify a maximum and minimum offset from the
314    /// original position relative to non-sticky content within the same scrolling frame.
315    pub vertical_offset_bounds: StickyOffsetBounds,
316
317    /// The minimum and maximum horizontal offsets for this sticky frame. Ignoring these constraints,
318    /// the sticky frame will continue to stick to the edge of the viewport as its original
319    /// position is scrolled out of view. Constraints specify a maximum and minimum offset from the
320    /// original position relative to non-sticky content within the same scrolling frame.
321    pub horizontal_offset_bounds: StickyOffsetBounds,
322
323    /// The amount of offset that has already been applied to the sticky frame. A positive y
324    /// component this field means that a top-sticky item was in a scrollframe that has been
325    /// scrolled down, such that the sticky item's position needed to be offset downwards by
326    /// `previously_applied_offset.y`. A negative y component corresponds to the upward offset
327    /// applied due to bottom-stickiness. The x-axis works analogously.
328    pub previously_applied_offset: LayoutVector2D,
329
330    /// A unique (per-pipeline) key for this spatial that is stable across display lists.
331    pub key: SpatialTreeItemKey,
332
333    /// A property binding that we use to store an animation ID for APZ
334    pub transform: Option<PropertyBinding<LayoutTransform>>,
335}
336
337#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)]
338pub struct ScrollFrameDescriptor {
339    /// The id of the space this scroll frame creates
340    pub scroll_frame_id: SpatialId,
341    /// The size of the contents this contains (so the backend knows how far it can scroll).
342    // FIXME: this can *probably* just be a size? Origin seems to just get thrown out.
343    pub content_rect: LayoutRect,
344    pub frame_rect: LayoutRect,
345    pub parent_space: SpatialId,
346    pub external_id: ExternalScrollId,
347    /// The amount this scrollframe has already been scrolled by, in the caller.
348    /// This means that all the display items that are inside the scrollframe
349    /// will have their coordinates shifted by this amount, and this offset
350    /// should be added to those display item coordinates in order to get a
351    /// normalized value that is consistent across display lists.
352    pub external_scroll_offset: LayoutVector2D,
353    /// The generation of the external_scroll_offset.
354    pub scroll_offset_generation: APZScrollGeneration,
355    /// Whether this scrollframe document has any scroll-linked effect or not.
356    pub has_scroll_linked_effect: HasScrollLinkedEffect,
357    /// A unique (per-pipeline) key for this spatial that is stable across display lists.
358    pub key: SpatialTreeItemKey,
359}
360
361/// A solid or an animating color to draw (may not actually be a rectangle due to complex clips)
362#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)]
363pub struct RectangleDisplayItem {
364    pub common: CommonItemProperties,
365    pub bounds: LayoutRect,
366    pub color: PropertyBinding<ColorF>,
367}
368
369/// Clears all colors from the area, making it possible to cut holes in the window.
370/// (useful for things like the macos frosted-glass effect).
371#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)]
372pub struct ClearRectangleDisplayItem {
373    pub common: CommonItemProperties,
374    pub bounds: LayoutRect,
375}
376
377/// A minimal hit-testable item for the parent browser's convenience, and is
378/// slimmer than a RectangleDisplayItem (no color). The existence of this as a
379/// distinct item also makes it easier to inspect/debug display items.
380#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)]
381pub struct HitTestDisplayItem {
382    pub rect: LayoutRect,
383    pub clip_chain_id: ClipChainId,
384    pub spatial_id: SpatialId,
385    pub flags: PrimitiveFlags,
386    pub tag: ItemTag,
387}
388
389#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)]
390pub struct LineDisplayItem {
391    pub common: CommonItemProperties,
392    /// We need a separate rect from common.clip_rect to encode cute
393    /// tricks that firefox does to make a series of text-decorations seamlessly
394    /// line up -- snapping the decorations to a multiple of their period, and
395    /// then clipping them to their "proper" area. This rect is that "logical"
396    /// snapped area that may be clipped to the right size by the clip_rect.
397    pub area: LayoutRect,
398    /// Whether the rect is interpretted as vertical or horizontal
399    pub orientation: LineOrientation,
400    /// This could potentially be implied from area, but we currently prefer
401    /// that this is the responsibility of the layout engine. Value irrelevant
402    /// for non-wavy lines.
403    // FIXME: this was done before we could use tagged unions in enums, but now
404    // it should just be part of LineStyle::Wavy.
405    pub wavy_line_thickness: f32,
406    pub color: ColorF,
407    pub style: LineStyle,
408}
409
410#[repr(u8)]
411#[derive(Clone, Copy, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize, Eq, Hash, PeekPoke)]
412pub enum LineOrientation {
413    Vertical,
414    Horizontal,
415}
416
417#[repr(u8)]
418#[derive(Clone, Copy, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize, Eq, Hash, PeekPoke)]
419pub enum LineStyle {
420    Solid,
421    Dotted,
422    Dashed,
423    Wavy,
424}
425
426#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)]
427pub struct TextDisplayItem {
428    pub common: CommonItemProperties,
429    /// The area all the glyphs should be found in. Strictly speaking this isn't
430    /// necessarily needed, but layout engines should already "know" this, and we
431    /// use it cull and size things quickly before glyph layout is done. Currently
432    /// the glyphs *can* be outside these bounds, but that should imply they
433    /// can be cut off.
434    // FIXME: these are currently sometimes ignored to keep some old wrench tests
435    // working, but we should really just fix the tests!
436    pub bounds: LayoutRect,
437    pub font_key: font::FontInstanceKey,
438    pub color: ColorF,
439    pub glyph_options: Option<font::GlyphOptions>,
440    pub ref_frame_offset: LayoutVector2D,
441} // IMPLICIT: glyphs: Vec<font::GlyphInstance>
442
443#[derive(Clone, Copy, Debug, Default, Deserialize, MallocSizeOf, PartialEq, Serialize, PeekPoke)]
444pub struct NormalBorder {
445    pub left: BorderSide,
446    pub right: BorderSide,
447    pub top: BorderSide,
448    pub bottom: BorderSide,
449    pub radius: BorderRadius,
450    /// Whether to apply anti-aliasing on the border corners.
451    ///
452    /// Note that for this to be `false` and work, this requires the borders to
453    /// be solid, and no border-radius.
454    pub do_aa: bool,
455}
456
457impl NormalBorder {
458    fn can_disable_antialiasing(&self) -> bool {
459        fn is_valid(style: BorderStyle) -> bool {
460            style == BorderStyle::Solid || style == BorderStyle::None
461        }
462
463        self.radius.is_zero() &&
464            is_valid(self.top.style) &&
465            is_valid(self.left.style) &&
466            is_valid(self.bottom.style) &&
467            is_valid(self.right.style)
468    }
469
470    /// Normalizes a border so that we don't render disallowed stuff, like inset
471    /// borders that are less than two pixels wide.
472    #[inline]
473    pub fn normalize(&mut self, widths: &LayoutSideOffsets) {
474        debug_assert!(
475            self.do_aa || self.can_disable_antialiasing(),
476            "Unexpected disabled-antialiasing in a border, likely won't work or will be ignored"
477        );
478
479        #[inline]
480        fn renders_small_border_solid(style: BorderStyle) -> bool {
481            match style {
482                BorderStyle::Groove |
483                BorderStyle::Ridge => true,
484                _ => false,
485            }
486        }
487
488        let normalize_side = |side: &mut BorderSide, width: f32| {
489            if renders_small_border_solid(side.style) && width < 2. {
490                side.style = BorderStyle::Solid;
491            }
492        };
493
494        normalize_side(&mut self.left, widths.left);
495        normalize_side(&mut self.right, widths.right);
496        normalize_side(&mut self.top, widths.top);
497        normalize_side(&mut self.bottom, widths.bottom);
498    }
499}
500
501#[repr(u8)]
502#[derive(Debug, Copy, Clone, MallocSizeOf, PartialEq, Serialize, Deserialize, Eq, Hash, PeekPoke)]
503pub enum RepeatMode {
504    Stretch,
505    Repeat,
506    Round,
507    Space,
508}
509
510#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize, PeekPoke)]
511pub enum NinePatchBorderSource {
512    Image(ImageKey, ImageRendering),
513    Gradient(Gradient),
514    RadialGradient(RadialGradient),
515    ConicGradient(ConicGradient),
516}
517
518#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)]
519pub struct NinePatchBorder {
520    /// Describes what to use as the 9-patch source image. If this is an image,
521    /// it will be stretched to fill the size given by width x height.
522    pub source: NinePatchBorderSource,
523
524    /// The width of the 9-part image.
525    pub width: i32,
526
527    /// The height of the 9-part image.
528    pub height: i32,
529
530    /// Distances from each edge where the image should be sliced up. These
531    /// values are in 9-part-image space (the same space as width and height),
532    /// and the resulting image parts will be used to fill the corresponding
533    /// parts of the border as given by the border widths. This can lead to
534    /// stretching.
535    /// Slices can be overlapping. In that case, the same pixels from the
536    /// 9-part image will show up in multiple parts of the resulting border.
537    pub slice: DeviceIntSideOffsets,
538
539    /// Controls whether the center of the 9 patch image is rendered or
540    /// ignored. The center is never rendered if the slices are overlapping.
541    pub fill: bool,
542
543    /// Determines what happens if the horizontal side parts of the 9-part
544    /// image have a different size than the horizontal parts of the border.
545    pub repeat_horizontal: RepeatMode,
546
547    /// Determines what happens if the vertical side parts of the 9-part
548    /// image have a different size than the vertical parts of the border.
549    pub repeat_vertical: RepeatMode,
550}
551
552#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize, PeekPoke)]
553pub enum BorderDetails {
554    Normal(NormalBorder),
555    NinePatch(NinePatchBorder),
556}
557
558#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)]
559pub struct BorderDisplayItem {
560    pub common: CommonItemProperties,
561    pub bounds: LayoutRect,
562    pub widths: LayoutSideOffsets,
563    pub details: BorderDetails,
564}
565
566#[repr(C)]
567#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize, PeekPoke)]
568pub enum BorderRadiusKind {
569    Uniform,
570    NonUniform,
571}
572
573#[repr(C)]
574#[derive(Clone, Copy, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize, PeekPoke)]
575pub struct BorderRadius {
576    pub top_left: LayoutSize,
577    pub top_right: LayoutSize,
578    pub bottom_left: LayoutSize,
579    pub bottom_right: LayoutSize,
580}
581
582impl Default for BorderRadius {
583    fn default() -> Self {
584        BorderRadius {
585            top_left: LayoutSize::zero(),
586            top_right: LayoutSize::zero(),
587            bottom_left: LayoutSize::zero(),
588            bottom_right: LayoutSize::zero(),
589        }
590    }
591}
592
593#[repr(C)]
594#[derive(Clone, Copy, Debug, Default, Deserialize, MallocSizeOf, PartialEq, Serialize, PeekPoke)]
595pub struct BorderSide {
596    pub color: ColorF,
597    pub style: BorderStyle,
598}
599
600#[repr(u32)]
601#[derive(Clone, Copy, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize, Hash, Eq, PeekPoke)]
602pub enum BorderStyle {
603    None = 0,
604    Solid = 1,
605    Double = 2,
606    Dotted = 3,
607    Dashed = 4,
608    Hidden = 5,
609    Groove = 6,
610    Ridge = 7,
611    Inset = 8,
612    Outset = 9,
613}
614
615impl BorderStyle {
616    pub fn is_hidden(self) -> bool {
617        self == BorderStyle::Hidden || self == BorderStyle::None
618    }
619}
620
621#[repr(u8)]
622#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize, PeekPoke)]
623pub enum BoxShadowClipMode {
624    Outset = 0,
625    Inset = 1,
626}
627
628#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)]
629pub struct BoxShadowDisplayItem {
630    pub common: CommonItemProperties,
631    pub box_bounds: LayoutRect,
632    pub offset: LayoutVector2D,
633    pub color: ColorF,
634    pub blur_radius: f32,
635    pub spread_radius: f32,
636    pub border_radius: BorderRadius,
637    pub clip_mode: BoxShadowClipMode,
638}
639
640#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)]
641pub struct PushShadowDisplayItem {
642    pub space_and_clip: SpaceAndClipInfo,
643    pub shadow: Shadow,
644    pub should_inflate: bool,
645}
646
647#[repr(C)]
648#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)]
649pub struct Shadow {
650    pub offset: LayoutVector2D,
651    pub color: ColorF,
652    pub blur_radius: f32,
653}
654
655#[repr(u8)]
656#[derive(Debug, Copy, Clone, Hash, Eq, MallocSizeOf, PartialEq, Serialize, Deserialize, Ord, PartialOrd, PeekPoke)]
657pub enum ExtendMode {
658    Clamp,
659    Repeat,
660}
661
662#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)]
663pub struct Gradient {
664    pub start_point: LayoutPoint,
665    pub end_point: LayoutPoint,
666    pub extend_mode: ExtendMode,
667} // IMPLICIT: stops: Vec<GradientStop>
668
669impl Gradient {
670    pub fn is_valid(&self) -> bool {
671        self.start_point.x.is_finite() &&
672            self.start_point.y.is_finite() &&
673            self.end_point.x.is_finite() &&
674            self.end_point.y.is_finite()
675    }
676}
677
678/// The area
679#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)]
680pub struct GradientDisplayItem {
681    /// NOTE: common.clip_rect is the area the gradient covers
682    pub common: CommonItemProperties,
683    /// The area to tile the gradient over (first tile starts at origin of this rect)
684    // FIXME: this should ideally just be `tile_origin` here, with the clip_rect
685    // defining the bounds of the item. Needs non-trivial backend changes.
686    pub bounds: LayoutRect,
687    /// How big a tile of the of the gradient should be (common case: bounds.size)
688    pub tile_size: LayoutSize,
689    /// The space between tiles of the gradient (common case: 0)
690    pub tile_spacing: LayoutSize,
691    pub gradient: Gradient,
692}
693
694#[repr(C)]
695#[derive(Clone, Copy, Debug, Default, Deserialize, MallocSizeOf, PartialEq, Serialize, PeekPoke)]
696pub struct GradientStop {
697    pub offset: f32,
698    pub color: ColorF,
699}
700
701#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)]
702pub struct RadialGradient {
703    pub center: LayoutPoint,
704    pub radius: LayoutSize,
705    pub start_offset: f32,
706    pub end_offset: f32,
707    pub extend_mode: ExtendMode,
708} // IMPLICIT stops: Vec<GradientStop>
709
710impl RadialGradient {
711    pub fn is_valid(&self) -> bool {
712        self.center.x.is_finite() &&
713            self.center.y.is_finite() &&
714            self.start_offset.is_finite() &&
715            self.end_offset.is_finite()
716    }
717}
718
719#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)]
720pub struct ConicGradient {
721    pub center: LayoutPoint,
722    pub angle: f32,
723    pub start_offset: f32,
724    pub end_offset: f32,
725    pub extend_mode: ExtendMode,
726} // IMPLICIT stops: Vec<GradientStop>
727
728impl ConicGradient {
729    pub fn is_valid(&self) -> bool {
730        self.center.x.is_finite() &&
731            self.center.y.is_finite() &&
732            self.angle.is_finite() &&
733            self.start_offset.is_finite() &&
734            self.end_offset.is_finite()
735    }
736}
737
738/// Just an abstraction for bundling up a bunch of clips into a "super clip".
739#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)]
740pub struct ClipChainItem {
741    pub id: ClipChainId,
742    pub parent: Option<ClipChainId>,
743} // IMPLICIT clip_ids: Vec<ClipId>
744
745#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)]
746pub struct RadialGradientDisplayItem {
747    pub common: CommonItemProperties,
748    /// The area to tile the gradient over (first tile starts at origin of this rect)
749    // FIXME: this should ideally just be `tile_origin` here, with the clip_rect
750    // defining the bounds of the item. Needs non-trivial backend changes.
751    pub bounds: LayoutRect,
752    pub gradient: RadialGradient,
753    pub tile_size: LayoutSize,
754    pub tile_spacing: LayoutSize,
755}
756
757#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)]
758pub struct ConicGradientDisplayItem {
759    pub common: CommonItemProperties,
760    /// The area to tile the gradient over (first tile starts at origin of this rect)
761    // FIXME: this should ideally just be `tile_origin` here, with the clip_rect
762    // defining the bounds of the item. Needs non-trivial backend changes.
763    pub bounds: LayoutRect,
764    pub gradient: ConicGradient,
765    pub tile_size: LayoutSize,
766    pub tile_spacing: LayoutSize,
767}
768
769/// Renders a filtered region of its backdrop
770#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)]
771pub struct BackdropFilterDisplayItem {
772    pub common: CommonItemProperties,
773}
774// IMPLICIT: filters: Vec<FilterOp>, filter_datas: Vec<FilterData>, filter_primitives: Vec<FilterPrimitive>
775
776#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)]
777pub struct ReferenceFrameDisplayListItem {
778}
779
780#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)]
781pub struct ReferenceFrameDescriptor {
782    pub origin: LayoutPoint,
783    pub parent_spatial_id: SpatialId,
784    pub reference_frame: ReferenceFrame,
785}
786
787#[derive(Clone, Copy, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize, PeekPoke)]
788pub enum ReferenceFrameKind {
789    /// A normal transform matrix, may contain perspective (the CSS transform property)
790    Transform {
791        /// Optionally marks the transform as only ever having a simple 2D scale or translation,
792        /// allowing for optimizations.
793        is_2d_scale_translation: bool,
794        /// Marks that the transform should be snapped. Used for transforms which animate in
795        /// response to scrolling, eg for zooming or dynamic toolbar fixed-positioning.
796        should_snap: bool,
797        /// Marks the transform being a part of the CSS stacking context that also has
798        /// a perspective. In this case, backface visibility takes this perspective into
799        /// account.
800        paired_with_perspective: bool,
801    },
802    /// A perspective transform, that optionally scrolls relative to a specific scroll node
803    Perspective {
804        scrolling_relative_to: Option<ExternalScrollId>,
805    }
806}
807
808#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize, PeekPoke)]
809pub enum Rotation {
810    Degree0,
811    Degree90,
812    Degree180,
813    Degree270,
814}
815
816impl Rotation {
817    pub fn to_matrix(
818        &self,
819        size: LayoutSize,
820    ) -> LayoutTransform {
821        let (shift_center_to_origin, angle) = match self {
822            Rotation::Degree0 => {
823              (LayoutTransform::translation(-size.width / 2., -size.height / 2., 0.), Angle::degrees(0.))
824            },
825            Rotation::Degree90 => {
826              (LayoutTransform::translation(-size.height / 2., -size.width / 2., 0.), Angle::degrees(90.))
827            },
828            Rotation::Degree180 => {
829              (LayoutTransform::translation(-size.width / 2., -size.height / 2., 0.), Angle::degrees(180.))
830            },
831            Rotation::Degree270 => {
832              (LayoutTransform::translation(-size.height / 2., -size.width / 2., 0.), Angle::degrees(270.))
833            },
834        };
835        let shift_origin_to_center = LayoutTransform::translation(size.width / 2., size.height / 2., 0.);
836
837        shift_center_to_origin
838            .then(&LayoutTransform::rotation(0., 0., 1.0, angle))
839            .then(&shift_origin_to_center)
840    }
841}
842
843#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize, PeekPoke)]
844pub enum ReferenceTransformBinding {
845    /// Standard reference frame which contains a precomputed transform.
846    Static {
847        binding: PropertyBinding<LayoutTransform>,
848    },
849    /// Computed reference frame which dynamically calculates the transform
850    /// based on the given parameters. The reference is the content size of
851    /// the parent iframe, which is affected by snapping.
852    ///
853    /// This is used when a transform depends on the layout size of an
854    /// element, otherwise the difference between the unsnapped size
855    /// used in the transform, and the snapped size calculated during scene
856    /// building can cause seaming.
857    Computed {
858        scale_from: Option<LayoutSize>,
859        vertical_flip: bool,
860        rotation: Rotation,
861    },
862}
863
864impl Default for ReferenceTransformBinding {
865    fn default() -> Self {
866        ReferenceTransformBinding::Static {
867            binding: Default::default(),
868        }
869    }
870}
871
872#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)]
873pub struct ReferenceFrame {
874    pub kind: ReferenceFrameKind,
875    pub transform_style: TransformStyle,
876    /// The transform matrix, either the perspective matrix or the transform
877    /// matrix.
878    pub transform: ReferenceTransformBinding,
879    pub id: SpatialId,
880    /// A unique (per-pipeline) key for this spatial that is stable across display lists.
881    pub key: SpatialTreeItemKey,
882}
883
884/// If passed in a stacking context display item, inform WebRender that
885/// the contents of the stacking context should be retained into a texture
886/// and associated to an image key.
887///
888/// Image display items can then display the cached snapshot using the
889/// same image key.
890///
891/// The flow for creating/using/deleting snapshots is the same as with
892/// regular images:
893///  - The image key must have been created with `Transaction::add_snapshot_image`.
894///  - The current scene must not contain references to the snapshot when
895///    `Transaction::delete_snapshot_image` is called.
896#[repr(C)]
897#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)]
898pub struct SnapshotInfo {
899    /// The image key to associate the snapshot with.
900    pub key: SnapshotImageKey,
901    /// The bounds of the snapshot in local space.
902    ///
903    /// This rectangle is relative to the same coordinate space as the
904    /// child items of the stacking context.
905    pub area: LayoutRect,
906    /// If true, detach the stacking context from the scene and only
907    /// render it into the snapshot.
908    /// If false, the stacking context rendered in the frame normally
909    /// in addition to being cached into the snapshot.
910    pub detached: bool,
911}
912
913#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)]
914pub struct PushStackingContextDisplayItem {
915    pub origin: LayoutPoint,
916    pub spatial_id: SpatialId,
917    pub snapshot: Option<SnapshotInfo>,
918    pub prim_flags: PrimitiveFlags,
919    pub ref_frame_offset: LayoutVector2D,
920    pub stacking_context: StackingContext,
921}
922
923#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)]
924pub struct StackingContext {
925    pub transform_style: TransformStyle,
926    pub mix_blend_mode: MixBlendMode,
927    pub clip_chain_id: Option<ClipChainId>,
928    pub raster_space: RasterSpace,
929    pub flags: StackingContextFlags,
930}
931// IMPLICIT: filters: Vec<FilterOp>, filter_datas: Vec<FilterData>, filter_primitives: Vec<FilterPrimitive>
932
933#[repr(u8)]
934#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize, PeekPoke)]
935pub enum TransformStyle {
936    Flat = 0,
937    Preserve3D = 1,
938}
939
940/// Configure whether the contents of a stacking context
941/// should be rasterized in local space or screen space.
942/// Local space rasterized pictures are typically used
943/// when we want to cache the output, and performance is
944/// important. Note that this is a performance hint only,
945/// which WR may choose to ignore.
946#[derive(Clone, Copy, Debug, Deserialize, PartialEq, MallocSizeOf, Serialize, PeekPoke)]
947#[repr(u8)]
948pub enum RasterSpace {
949    // Rasterize in local-space, applying supplied scale to primitives.
950    // Best performance, but lower quality.
951    Local(f32),
952
953    // Rasterize the picture in screen-space, including rotation / skew etc in
954    // the rasterized element. Best quality, but slower performance. Note that
955    // any stacking context with a perspective transform will be rasterized
956    // in local-space, even if this is set.
957    Screen,
958}
959
960impl RasterSpace {
961    pub fn local_scale(self) -> Option<f32> {
962        match self {
963            RasterSpace::Local(scale) => Some(scale),
964            RasterSpace::Screen => None,
965        }
966    }
967}
968
969impl Eq for RasterSpace {}
970
971impl Hash for RasterSpace {
972    fn hash<H: Hasher>(&self, state: &mut H) {
973        match self {
974            RasterSpace::Screen => {
975                0.hash(state);
976            }
977            RasterSpace::Local(scale) => {
978                // Note: this is inconsistent with the Eq impl for -0.0 (don't care).
979                1.hash(state);
980                scale.to_bits().hash(state);
981            }
982        }
983    }
984}
985
986#[repr(C)]
987#[derive(Copy, PartialEq, Eq, Clone, PartialOrd, Ord, Hash, Deserialize, MallocSizeOf, Serialize, PeekPoke)]
988pub struct StackingContextFlags(u8);
989
990bitflags! {
991    impl StackingContextFlags: u8 {
992        /// If true, this stacking context is a blend container than contains
993        /// mix-blend-mode children (and should thus be isolated).
994        const IS_BLEND_CONTAINER = 1 << 0;
995        /// If true, this stacking context is a wrapper around a backdrop-filter (e.g. for
996        /// a clip-mask). This is needed to allow the correct selection of a backdrop root
997        /// since a clip-mask stacking context creates a parent surface.
998        const WRAPS_BACKDROP_FILTER = 1 << 1;
999        /// If true, this stacking context must be isolated from parent by a surface.
1000        const FORCED_ISOLATION = 1 << 2;
1001    }
1002}
1003
1004impl core::fmt::Debug for StackingContextFlags {
1005    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
1006        if self.is_empty() {
1007            write!(f, "{:#x}", Self::empty().bits())
1008        } else {
1009            bitflags::parser::to_writer(self, f)
1010        }
1011    }
1012}
1013
1014impl Default for StackingContextFlags {
1015    fn default() -> Self {
1016        StackingContextFlags::empty()
1017    }
1018}
1019
1020#[repr(u8)]
1021#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize, PeekPoke)]
1022pub enum MixBlendMode {
1023    Normal = 0,
1024    Multiply = 1,
1025    Screen = 2,
1026    Overlay = 3,
1027    Darken = 4,
1028    Lighten = 5,
1029    ColorDodge = 6,
1030    ColorBurn = 7,
1031    HardLight = 8,
1032    SoftLight = 9,
1033    Difference = 10,
1034    Exclusion = 11,
1035    Hue = 12,
1036    Saturation = 13,
1037    Color = 14,
1038    Luminosity = 15,
1039    PlusLighter = 16,
1040}
1041
1042#[repr(C)]
1043#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize, PeekPoke)]
1044pub enum ColorSpace {
1045    Srgb,
1046    LinearRgb,
1047}
1048
1049/// Available composite operoations for the composite filter primitive
1050#[repr(C)]
1051#[derive(Clone, Copy, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize, PeekPoke)]
1052pub enum CompositeOperator {
1053    Over,
1054    In,
1055    Atop,
1056    Out,
1057    Xor,
1058    Lighter,
1059    Arithmetic([f32; 4]),
1060}
1061
1062impl CompositeOperator {
1063    // This must stay in sync with the composite operator defines in cs_svg_filter.glsl
1064    pub fn as_int(&self) -> u32 {
1065        match self {
1066            CompositeOperator::Over => 0,
1067            CompositeOperator::In => 1,
1068            CompositeOperator::Out => 2,
1069            CompositeOperator::Atop => 3,
1070            CompositeOperator::Xor => 4,
1071            CompositeOperator::Lighter => 5,
1072            CompositeOperator::Arithmetic(..) => 6,
1073        }
1074    }
1075}
1076
1077/// An input to a SVG filter primitive.
1078#[repr(C)]
1079#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize, PeekPoke)]
1080pub enum FilterPrimitiveInput {
1081    /// The input is the original graphic that the filter is being applied to.
1082    Original,
1083    /// The input is the output of the previous filter primitive in the filter primitive chain.
1084    Previous,
1085    /// The input is the output of the filter primitive at the given index in the filter primitive chain.
1086    OutputOfPrimitiveIndex(usize),
1087}
1088
1089impl FilterPrimitiveInput {
1090    /// Gets the index of the input.
1091    /// Returns `None` if the source graphic is the input.
1092    pub fn to_index(self, cur_index: usize) -> Option<usize> {
1093        match self {
1094            FilterPrimitiveInput::Previous if cur_index > 0 => Some(cur_index - 1),
1095            FilterPrimitiveInput::OutputOfPrimitiveIndex(index) => Some(index),
1096            _ => None,
1097        }
1098    }
1099}
1100
1101#[repr(C)]
1102#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)]
1103pub struct BlendPrimitive {
1104    pub input1: FilterPrimitiveInput,
1105    pub input2: FilterPrimitiveInput,
1106    pub mode: MixBlendMode,
1107}
1108
1109#[repr(C)]
1110#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)]
1111pub struct FloodPrimitive {
1112    pub color: ColorF,
1113}
1114
1115impl FloodPrimitive {
1116    pub fn sanitize(&mut self) {
1117        self.color.r = self.color.r.clamp(0.0, 1.0);
1118        self.color.g = self.color.g.clamp(0.0, 1.0);
1119        self.color.b = self.color.b.clamp(0.0, 1.0);
1120        self.color.a = self.color.a.clamp(0.0, 1.0);
1121    }
1122}
1123
1124#[repr(C)]
1125#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)]
1126pub struct BlurPrimitive {
1127    pub input: FilterPrimitiveInput,
1128    pub width: f32,
1129    pub height: f32,
1130}
1131
1132#[repr(C)]
1133#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)]
1134pub struct OpacityPrimitive {
1135    pub input: FilterPrimitiveInput,
1136    pub opacity: f32,
1137}
1138
1139impl OpacityPrimitive {
1140    pub fn sanitize(&mut self) {
1141        self.opacity = self.opacity.clamp(0.0, 1.0);
1142    }
1143}
1144
1145/// cbindgen:derive-eq=false
1146#[repr(C)]
1147#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)]
1148pub struct ColorMatrixPrimitive {
1149    pub input: FilterPrimitiveInput,
1150    pub matrix: [f32; 20],
1151}
1152
1153#[repr(C)]
1154#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)]
1155pub struct DropShadowPrimitive {
1156    pub input: FilterPrimitiveInput,
1157    pub shadow: Shadow,
1158}
1159
1160#[repr(C)]
1161#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)]
1162pub struct ComponentTransferPrimitive {
1163    pub input: FilterPrimitiveInput,
1164    // Component transfer data is stored in FilterData.
1165}
1166
1167#[repr(C)]
1168#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)]
1169pub struct IdentityPrimitive {
1170    pub input: FilterPrimitiveInput,
1171}
1172
1173#[repr(C)]
1174#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)]
1175pub struct OffsetPrimitive {
1176    pub input: FilterPrimitiveInput,
1177    pub offset: LayoutVector2D,
1178}
1179
1180#[repr(C)]
1181#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)]
1182pub struct CompositePrimitive {
1183    pub input1: FilterPrimitiveInput,
1184    pub input2: FilterPrimitiveInput,
1185    pub operator: CompositeOperator,
1186}
1187
1188/// See: https://github.com/eqrion/cbindgen/issues/9
1189/// cbindgen:derive-eq=false
1190#[repr(C)]
1191#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize, PeekPoke)]
1192pub enum FilterPrimitiveKind {
1193    Identity(IdentityPrimitive),
1194    Blend(BlendPrimitive),
1195    Flood(FloodPrimitive),
1196    Blur(BlurPrimitive),
1197    // TODO: Support animated opacity?
1198    Opacity(OpacityPrimitive),
1199    /// cbindgen:derive-eq=false
1200    ColorMatrix(ColorMatrixPrimitive),
1201    DropShadow(DropShadowPrimitive),
1202    ComponentTransfer(ComponentTransferPrimitive),
1203    Offset(OffsetPrimitive),
1204    Composite(CompositePrimitive),
1205}
1206
1207impl Default for FilterPrimitiveKind {
1208    fn default() -> Self {
1209        FilterPrimitiveKind::Identity(IdentityPrimitive::default())
1210    }
1211}
1212
1213impl FilterPrimitiveKind {
1214    pub fn sanitize(&mut self) {
1215        match self {
1216            FilterPrimitiveKind::Flood(flood) => flood.sanitize(),
1217            FilterPrimitiveKind::Opacity(opacity) => opacity.sanitize(),
1218
1219            // No sanitization needed.
1220            FilterPrimitiveKind::Identity(..) |
1221            FilterPrimitiveKind::Blend(..) |
1222            FilterPrimitiveKind::ColorMatrix(..) |
1223            FilterPrimitiveKind::Offset(..) |
1224            FilterPrimitiveKind::Composite(..) |
1225            FilterPrimitiveKind::Blur(..) |
1226            FilterPrimitiveKind::DropShadow(..) |
1227            // Component transfer's filter data is sanitized separately.
1228            FilterPrimitiveKind::ComponentTransfer(..) => {}
1229        }
1230    }
1231}
1232
1233/// SVG Filter Primitive.
1234/// See: https://github.com/eqrion/cbindgen/issues/9
1235/// cbindgen:derive-eq=false
1236#[repr(C)]
1237#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)]
1238pub struct FilterPrimitive {
1239    pub kind: FilterPrimitiveKind,
1240    pub color_space: ColorSpace,
1241}
1242
1243impl FilterPrimitive {
1244    pub fn sanitize(&mut self) {
1245        self.kind.sanitize();
1246    }
1247}
1248
1249#[repr(C)]
1250#[derive(Clone, Copy, Debug, Default, Deserialize, Eq, PartialEq, Serialize, PeekPoke)]
1251pub enum FilterOpGraphPictureBufferId {
1252    #[default]
1253    /// empty slot in feMerge inputs
1254    None,
1255    /// reference to another (earlier) node in filter graph
1256    BufferId(i16),
1257}
1258
1259#[repr(C)]
1260#[derive(Clone, Copy, Debug, Default, Deserialize, Serialize, PeekPoke)]
1261pub struct FilterOpGraphPictureReference {
1262    /// Id of the picture in question in a namespace unique to this filter DAG
1263    pub buffer_id: FilterOpGraphPictureBufferId,
1264}
1265
1266#[repr(C)]
1267#[derive(Clone, Copy, Debug, Default, Deserialize, Serialize, PeekPoke)]
1268pub struct FilterOpGraphNode {
1269    /// True if color_interpolation_filter == LinearRgb; shader will convert
1270    /// sRGB texture pixel colors on load and convert back on store, for correct
1271    /// interpolation
1272    pub linear: bool,
1273    /// virtualized picture input binding 1 (i.e. texture source), typically
1274    /// this is used, but certain filters do not use it
1275    pub input: FilterOpGraphPictureReference,
1276    /// virtualized picture input binding 2 (i.e. texture sources), only certain
1277    /// filters use this
1278    pub input2: FilterOpGraphPictureReference,
1279    /// rect this node will render into, in filter space
1280    pub subregion: LayoutRect,
1281}
1282
1283/// Maximum number of SVGFE filters in one graph, this is constant size to avoid
1284/// allocating anything, and the SVG spec allows us to drop all filters on an
1285/// item if the graph is excessively complex - a graph this large will never be
1286/// a good user experience, performance-wise.
1287pub const SVGFE_GRAPH_MAX: usize = 256;
1288
1289#[repr(C)]
1290#[derive(Clone, Copy, Debug, Deserialize, Serialize, PeekPoke)]
1291pub enum FilterOp {
1292    /// Filter that does no transformation of the colors, needed for
1293    /// debug purposes, and is the default value in impl_default_for_enums.
1294    /// parameters: none
1295    /// CSS filter semantics - operates on previous picture, uses sRGB space (non-linear)
1296    Identity,
1297    /// apply blur effect
1298    /// parameters: stdDeviationX, stdDeviationY
1299    /// CSS filter semantics - operates on previous picture, uses sRGB space (non-linear)
1300    Blur(f32, f32),
1301    /// apply brightness effect
1302    /// parameters: amount
1303    /// CSS filter semantics - operates on previous picture, uses sRGB space (non-linear)
1304    Brightness(f32),
1305    /// apply contrast effect
1306    /// parameters: amount
1307    /// CSS filter semantics - operates on previous picture, uses sRGB space (non-linear)
1308    Contrast(f32),
1309    /// fade image toward greyscale version of image
1310    /// parameters: amount
1311    /// CSS filter semantics - operates on previous picture, uses sRGB space (non-linear)
1312    Grayscale(f32),
1313    /// fade image toward hue-rotated version of image (rotate RGB around color wheel)
1314    /// parameters: angle
1315    /// CSS filter semantics - operates on previous picture, uses sRGB space (non-linear)
1316    HueRotate(f32),
1317    /// fade image toward inverted image (1 - RGB)
1318    /// parameters: amount
1319    /// CSS filter semantics - operates on previous picture, uses sRGB space (non-linear)
1320    Invert(f32),
1321    /// multiplies color and alpha by opacity
1322    /// parameters: amount
1323    /// CSS filter semantics - operates on previous picture, uses sRGB space (non-linear)
1324    Opacity(PropertyBinding<f32>, f32),
1325    /// multiply saturation of colors
1326    /// parameters: amount
1327    /// CSS filter semantics - operates on previous picture, uses sRGB space (non-linear)
1328    Saturate(f32),
1329    /// fade image toward sepia tone version of image
1330    /// parameters: amount
1331    /// CSS filter semantics - operates on previous picture, uses sRGB space (non-linear)
1332    Sepia(f32),
1333    /// add drop shadow version of image to the image
1334    /// parameters: shadow
1335    /// CSS filter semantics - operates on previous picture, uses sRGB space (non-linear)
1336    DropShadow(Shadow),
1337    /// transform color and alpha in image through 4x5 color matrix (transposed for efficiency)
1338    /// parameters: matrix[5][4]
1339    /// CSS filter semantics - operates on previous picture, uses sRGB space (non-linear)
1340    ColorMatrix([f32; 20]),
1341    /// internal use - convert sRGB input to linear output
1342    /// parameters: none
1343    /// CSS filter semantics - operates on previous picture, uses sRGB space (non-linear)
1344    SrgbToLinear,
1345    /// internal use - convert linear input to sRGB output
1346    /// parameters: none
1347    /// CSS filter semantics - operates on previous picture, uses sRGB space (non-linear)
1348    LinearToSrgb,
1349    /// remap RGBA with color gradients and component swizzle
1350    /// parameters: FilterData
1351    /// CSS filter semantics - operates on previous picture, uses sRGB space (non-linear)
1352    ComponentTransfer,
1353    /// replace image with a solid color
1354    /// NOTE: UNUSED; Gecko never produces this filter
1355    /// parameters: color
1356    /// CSS filter semantics - operates on previous picture,uses sRGB space (non-linear)
1357    Flood(ColorF),
1358    /// Filter that copies the SourceGraphic image into the specified subregion,
1359    /// This is intentionally the only way to get SourceGraphic into the graph,
1360    /// as the filter region must be applied before it is used.
1361    /// parameters: FilterOpGraphNode
1362    /// SVG filter semantics - no inputs, no linear
1363    SVGFESourceGraphic{node: FilterOpGraphNode},
1364    /// Filter that copies the SourceAlpha image into the specified subregion,
1365    /// This is intentionally the only way to get SourceGraphic into the graph,
1366    /// as the filter region must be applied before it is used.
1367    /// parameters: FilterOpGraphNode
1368    /// SVG filter semantics - no inputs, no linear
1369    SVGFESourceAlpha{node: FilterOpGraphNode},
1370    /// Filter that does no transformation of the colors, used for subregion
1371    /// cropping only.
1372    SVGFEIdentity{node: FilterOpGraphNode},
1373    /// represents CSS opacity property as a graph node like the rest of the SVGFE* filters
1374    /// parameters: FilterOpGraphNode
1375    /// SVG filter semantics - selectable input(s), selectable between linear
1376    /// (default) and sRGB color space for calculations
1377    SVGFEOpacity{node: FilterOpGraphNode, valuebinding: PropertyBinding<f32>, value: f32},
1378    /// convert a color image to an alpha channel - internal use; generated by
1379    /// SVGFilterInstance::GetOrCreateSourceAlphaIndex().
1380    SVGFEToAlpha{node: FilterOpGraphNode},
1381    /// combine 2 images with SVG_FEBLEND_MODE_DARKEN
1382    /// parameters: FilterOpGraphNode
1383    /// SVG filter semantics - selectable input(s), selectable between linear
1384    /// (default) and sRGB color space for calculations
1385    /// Spec: https://www.w3.org/TR/filter-effects-1/#feBlendElement
1386    SVGFEBlendDarken{node: FilterOpGraphNode},
1387    /// combine 2 images with SVG_FEBLEND_MODE_LIGHTEN
1388    /// parameters: FilterOpGraphNode
1389    /// SVG filter semantics - selectable input(s), selectable between linear
1390    /// (default) and sRGB color space for calculations
1391    /// Spec: https://www.w3.org/TR/filter-effects-1/#feBlendElement
1392    SVGFEBlendLighten{node: FilterOpGraphNode},
1393    /// combine 2 images with SVG_FEBLEND_MODE_MULTIPLY
1394    /// parameters: FilterOpGraphNode
1395    /// SVG filter semantics - selectable input(s), selectable between linear
1396    /// (default) and sRGB color space for calculations
1397    /// Spec: https://www.w3.org/TR/filter-effects-1/#feBlendElement
1398    SVGFEBlendMultiply{node: FilterOpGraphNode},
1399    /// combine 2 images with SVG_FEBLEND_MODE_NORMAL
1400    /// parameters: FilterOpGraphNode
1401    /// SVG filter semantics - selectable input(s), selectable between linear
1402    /// (default) and sRGB color space for calculations
1403    /// Spec: https://www.w3.org/TR/filter-effects-1/#feBlendElement
1404    SVGFEBlendNormal{node: FilterOpGraphNode},
1405    /// combine 2 images with SVG_FEBLEND_MODE_SCREEN
1406    /// parameters: FilterOpGraphNode
1407    /// SVG filter semantics - selectable input(s), selectable between linear
1408    /// (default) and sRGB color space for calculations
1409    /// Spec: https://www.w3.org/TR/filter-effects-1/#feBlendElement
1410    SVGFEBlendScreen{node: FilterOpGraphNode},
1411    /// combine 2 images with SVG_FEBLEND_MODE_OVERLAY
1412    /// parameters: FilterOpGraphNode
1413    /// SVG filter semantics - selectable input(s), selectable between linear
1414    /// (default) and sRGB color space for calculations
1415    /// Source: https://developer.mozilla.org/en-US/docs/Web/CSS/mix-blend-mode
1416    SVGFEBlendOverlay{node: FilterOpGraphNode},
1417    /// combine 2 images with SVG_FEBLEND_MODE_COLOR_DODGE
1418    /// parameters: FilterOpGraphNode
1419    /// SVG filter semantics - selectable input(s), selectable between linear
1420    /// (default) and sRGB color space for calculations
1421    /// Source: https://developer.mozilla.org/en-US/docs/Web/CSS/mix-blend-mode
1422    SVGFEBlendColorDodge{node: FilterOpGraphNode},
1423    /// combine 2 images with SVG_FEBLEND_MODE_COLOR_BURN
1424    /// parameters: FilterOpGraphNode
1425    /// SVG filter semantics - selectable input(s), selectable between linear
1426    /// (default) and sRGB color space for calculations
1427    /// Source: https://developer.mozilla.org/en-US/docs/Web/CSS/mix-blend-mode
1428    SVGFEBlendColorBurn{node: FilterOpGraphNode},
1429    /// combine 2 images with SVG_FEBLEND_MODE_HARD_LIGHT
1430    /// parameters: FilterOpGraphNode
1431    /// SVG filter semantics - selectable input(s), selectable between linear
1432    /// (default) and sRGB color space for calculations
1433    /// Source: https://developer.mozilla.org/en-US/docs/Web/CSS/mix-blend-mode
1434    SVGFEBlendHardLight{node: FilterOpGraphNode},
1435    /// combine 2 images with SVG_FEBLEND_MODE_SOFT_LIGHT
1436    /// parameters: FilterOpGraphNode
1437    /// SVG filter semantics - selectable input(s), selectable between linear
1438    /// (default) and sRGB color space for calculations
1439    /// Source: https://developer.mozilla.org/en-US/docs/Web/CSS/mix-blend-mode
1440    SVGFEBlendSoftLight{node: FilterOpGraphNode},
1441    /// combine 2 images with SVG_FEBLEND_MODE_DIFFERENCE
1442    /// parameters: FilterOpGraphNode
1443    /// SVG filter semantics - selectable input(s), selectable between linear
1444    /// (default) and sRGB color space for calculations
1445    /// Source: https://developer.mozilla.org/en-US/docs/Web/CSS/mix-blend-mode
1446    SVGFEBlendDifference{node: FilterOpGraphNode},
1447    /// combine 2 images with SVG_FEBLEND_MODE_EXCLUSION
1448    /// parameters: FilterOpGraphNode
1449    /// SVG filter semantics - selectable input(s), selectable between linear
1450    /// (default) and sRGB color space for calculations
1451    /// Source: https://developer.mozilla.org/en-US/docs/Web/CSS/mix-blend-mode
1452    SVGFEBlendExclusion{node: FilterOpGraphNode},
1453    /// combine 2 images with SVG_FEBLEND_MODE_HUE
1454    /// parameters: FilterOpGraphNode
1455    /// SVG filter semantics - selectable input(s), selectable between linear
1456    /// (default) and sRGB color space for calculations
1457    /// Source: https://developer.mozilla.org/en-US/docs/Web/CSS/mix-blend-mode
1458    SVGFEBlendHue{node: FilterOpGraphNode},
1459    /// combine 2 images with SVG_FEBLEND_MODE_SATURATION
1460    /// parameters: FilterOpGraphNode
1461    /// SVG filter semantics - selectable input(s), selectable between linear
1462    /// (default) and sRGB color space for calculations
1463    /// Source: https://developer.mozilla.org/en-US/docs/Web/CSS/mix-blend-mode
1464    SVGFEBlendSaturation{node: FilterOpGraphNode},
1465    /// combine 2 images with SVG_FEBLEND_MODE_COLOR
1466    /// parameters: FilterOpGraphNode
1467    /// SVG filter semantics - selectable input(s), selectable between linear
1468    /// (default) and sRGB color space for calculations
1469    /// Source: https://developer.mozilla.org/en-US/docs/Web/CSS/mix-blend-mode
1470    SVGFEBlendColor{node: FilterOpGraphNode},
1471    /// combine 2 images with SVG_FEBLEND_MODE_LUMINOSITY
1472    /// parameters: FilterOpGraphNode
1473    /// SVG filter semantics - selectable input(s), selectable between linear
1474    /// (default) and sRGB color space for calculations
1475    /// Source: https://developer.mozilla.org/en-US/docs/Web/CSS/mix-blend-mode
1476    SVGFEBlendLuminosity{node: FilterOpGraphNode},
1477    /// transform colors of image through 5x4 color matrix (transposed for efficiency)
1478    /// parameters: FilterOpGraphNode, matrix[5][4]
1479    /// SVG filter semantics - selectable input(s), selectable between linear
1480    /// (default) and sRGB color space for calculations
1481    /// Spec: https://www.w3.org/TR/filter-effects-1/#feColorMatrixElement
1482    SVGFEColorMatrix{node: FilterOpGraphNode, values: [f32; 20]},
1483    /// transform colors of image through configurable gradients with component swizzle
1484    /// parameters: FilterOpGraphNode, FilterData
1485    /// SVG filter semantics - selectable input(s), selectable between linear
1486    /// (default) and sRGB color space for calculations
1487    /// Spec: https://www.w3.org/TR/filter-effects-1/#feComponentTransferElement
1488    SVGFEComponentTransfer{node: FilterOpGraphNode},
1489    /// composite 2 images with chosen composite mode with parameters for that mode
1490    /// parameters: FilterOpGraphNode, k1, k2, k3, k4
1491    /// SVG filter semantics - selectable input(s), selectable between linear
1492    /// (default) and sRGB color space for calculations
1493    /// Spec: https://www.w3.org/TR/filter-effects-1/#feCompositeElement
1494    SVGFECompositeArithmetic{node: FilterOpGraphNode, k1: f32, k2: f32, k3: f32,
1495        k4: f32},
1496    /// composite 2 images with chosen composite mode with parameters for that mode
1497    /// parameters: FilterOpGraphNode
1498    /// SVG filter semantics - selectable input(s), selectable between linear
1499    /// (default) and sRGB color space for calculations
1500    /// Spec: https://www.w3.org/TR/filter-effects-1/#feCompositeElement
1501    SVGFECompositeATop{node: FilterOpGraphNode},
1502    /// composite 2 images with chosen composite mode with parameters for that mode
1503    /// parameters: FilterOpGraphNode
1504    /// SVG filter semantics - selectable input(s), selectable between linear
1505    /// (default) and sRGB color space for calculations
1506    /// Spec: https://www.w3.org/TR/filter-effects-1/#feCompositeElement
1507    SVGFECompositeIn{node: FilterOpGraphNode},
1508    /// composite 2 images with chosen composite mode with parameters for that mode
1509    /// parameters: FilterOpGraphNode
1510    /// SVG filter semantics - selectable input(s), selectable between linear
1511    /// (default) and sRGB color space for calculations
1512    /// Docs: https://developer.mozilla.org/en-US/docs/Web/SVG/Element/feComposite
1513    SVGFECompositeLighter{node: FilterOpGraphNode},
1514    /// composite 2 images with chosen composite mode with parameters for that mode
1515    /// parameters: FilterOpGraphNode
1516    /// SVG filter semantics - selectable input(s), selectable between linear
1517    /// (default) and sRGB color space for calculations
1518    /// Spec: https://www.w3.org/TR/filter-effects-1/#feCompositeElement
1519    SVGFECompositeOut{node: FilterOpGraphNode},
1520    /// composite 2 images with chosen composite mode with parameters for that mode
1521    /// parameters: FilterOpGraphNode
1522    /// SVG filter semantics - selectable input(s), selectable between linear
1523    /// (default) and sRGB color space for calculations
1524    /// Spec: https://www.w3.org/TR/filter-effects-1/#feCompositeElement
1525    SVGFECompositeOver{node: FilterOpGraphNode},
1526    /// composite 2 images with chosen composite mode with parameters for that mode
1527    /// parameters: FilterOpGraphNode
1528    /// SVG filter semantics - selectable input(s), selectable between linear
1529    /// (default) and sRGB color space for calculations
1530    /// Spec: https://www.w3.org/TR/filter-effects-1/#feCompositeElement
1531    SVGFECompositeXOR{node: FilterOpGraphNode},
1532    /// transform image through convolution matrix of up to 25 values (spec
1533    /// allows more but for performance reasons we do not)
1534    /// parameters: FilterOpGraphNode, orderX, orderY, kernelValues[25],
1535    ///  divisor, bias, targetX, targetY, kernelUnitLengthX, kernelUnitLengthY,
1536    ///  preserveAlpha
1537    /// SVG filter semantics - selectable input(s), selectable between linear
1538    /// (default) and sRGB color space for calculations
1539    /// Spec: https://www.w3.org/TR/filter-effects-1/#feConvolveMatrixElement
1540    SVGFEConvolveMatrixEdgeModeDuplicate{node: FilterOpGraphNode, order_x: i32,
1541        order_y: i32, kernel: [f32; 25], divisor: f32, bias: f32, target_x: i32,
1542        target_y: i32, kernel_unit_length_x: f32, kernel_unit_length_y: f32,
1543        preserve_alpha: i32},
1544    /// transform image through convolution matrix of up to 25 values (spec
1545    /// allows more but for performance reasons we do not)
1546    /// parameters: FilterOpGraphNode, orderX, orderY, kernelValues[25],
1547    ///  divisor, bias, targetX, targetY, kernelUnitLengthX, kernelUnitLengthY,
1548    ///  preserveAlpha
1549    /// SVG filter semantics - selectable input(s), selectable between linear
1550    /// (default) and sRGB color space for calculations
1551    /// Spec: https://www.w3.org/TR/filter-effects-1/#feConvolveMatrixElement
1552    SVGFEConvolveMatrixEdgeModeNone{node: FilterOpGraphNode, order_x: i32,
1553        order_y: i32, kernel: [f32; 25], divisor: f32, bias: f32, target_x: i32,
1554        target_y: i32, kernel_unit_length_x: f32, kernel_unit_length_y: f32,
1555        preserve_alpha: i32},
1556    /// transform image through convolution matrix of up to 25 values (spec
1557    /// allows more but for performance reasons we do not)
1558    /// parameters: FilterOpGraphNode, orderX, orderY, kernelValues[25],
1559    ///  divisor, bias, targetX, targetY, kernelUnitLengthX, kernelUnitLengthY,
1560    /// preserveAlpha
1561    /// SVG filter semantics - selectable input(s), selectable between linear
1562    /// (default) and sRGB color space for calculations
1563    /// Spec: https://www.w3.org/TR/filter-effects-1/#feConvolveMatrixElement
1564    SVGFEConvolveMatrixEdgeModeWrap{node: FilterOpGraphNode, order_x: i32,
1565        order_y: i32, kernel: [f32; 25], divisor: f32, bias: f32, target_x: i32,
1566        target_y: i32, kernel_unit_length_x: f32, kernel_unit_length_y: f32,
1567        preserve_alpha: i32},
1568    /// calculate lighting based on heightmap image with provided values for a
1569    /// distant light source with specified direction
1570    /// parameters: FilterOpGraphNode, surfaceScale, diffuseConstant,
1571    ///  kernelUnitLengthX, kernelUnitLengthY, azimuth, elevation
1572    /// SVG filter semantics - selectable input(s), selectable between linear
1573    /// (default) and sRGB color space for calculations
1574    /// Spec: https://www.w3.org/TR/filter-effects-1/#InterfaceSVGFEDiffuseLightingElement
1575    ///  https://www.w3.org/TR/filter-effects-1/#InterfaceSVGFEDistantLightElement
1576    SVGFEDiffuseLightingDistant{node: FilterOpGraphNode, surface_scale: f32,
1577        diffuse_constant: f32, kernel_unit_length_x: f32,
1578        kernel_unit_length_y: f32, azimuth: f32, elevation: f32},
1579    /// calculate lighting based on heightmap image with provided values for a
1580    /// point light source at specified location
1581    /// parameters: FilterOpGraphNode, surfaceScale, diffuseConstant,
1582    ///  kernelUnitLengthX, kernelUnitLengthY, x, y, z
1583    /// SVG filter semantics - selectable input(s), selectable between linear
1584    /// (default) and sRGB color space for calculations
1585    /// Spec: https://www.w3.org/TR/filter-effects-1/#InterfaceSVGFEDiffuseLightingElement
1586    ///  https://www.w3.org/TR/filter-effects-1/#InterfaceSVGFEPointLightElement
1587    SVGFEDiffuseLightingPoint{node: FilterOpGraphNode, surface_scale: f32,
1588        diffuse_constant: f32, kernel_unit_length_x: f32,
1589        kernel_unit_length_y: f32, x: f32, y: f32, z: f32},
1590    /// calculate lighting based on heightmap image with provided values for a
1591    /// spot light source at specified location pointing at specified target
1592    /// location with specified hotspot sharpness and cone angle
1593    /// parameters: FilterOpGraphNode, surfaceScale, diffuseConstant,
1594    ///  kernelUnitLengthX, kernelUnitLengthY, x, y, z, pointsAtX, pointsAtY,
1595    ///  pointsAtZ, specularExponent, limitingConeAngle
1596    /// SVG filter semantics - selectable input(s), selectable between linear
1597    /// (default) and sRGB color space for calculations
1598    /// Spec: https://www.w3.org/TR/filter-effects-1/#InterfaceSVGFEDiffuseLightingElement
1599    /// https://www.w3.org/TR/filter-effects-1/#InterfaceSVGFESpotLightElement
1600    SVGFEDiffuseLightingSpot{node: FilterOpGraphNode, surface_scale: f32,
1601        diffuse_constant: f32, kernel_unit_length_x: f32,
1602        kernel_unit_length_y: f32, x: f32, y: f32, z: f32, points_at_x: f32,
1603        points_at_y: f32, points_at_z: f32, cone_exponent: f32,
1604        limiting_cone_angle: f32},
1605    /// calculate a distorted version of first input image using offset values
1606    /// from second input image at specified intensity
1607    /// parameters: FilterOpGraphNode, scale, xChannelSelector, yChannelSelector
1608    /// SVG filter semantics - selectable input(s), selectable between linear
1609    /// (default) and sRGB color space for calculations
1610    /// Spec: https://www.w3.org/TR/filter-effects-1/#InterfaceSVGFEDisplacementMapElement
1611    SVGFEDisplacementMap{node: FilterOpGraphNode, scale: f32,
1612        x_channel_selector: u32, y_channel_selector: u32},
1613    /// create and merge a dropshadow version of the specified image's alpha
1614    /// channel with specified offset and blur radius
1615    /// parameters: FilterOpGraphNode, flood_color, flood_opacity, dx, dy,
1616    ///  stdDeviationX, stdDeviationY
1617    /// SVG filter semantics - selectable input(s), selectable between linear
1618    /// (default) and sRGB color space for calculations
1619    /// Spec: https://www.w3.org/TR/filter-effects-1/#InterfaceSVGFEDropShadowElement
1620    SVGFEDropShadow{node: FilterOpGraphNode, color: ColorF, dx: f32, dy: f32,
1621        std_deviation_x: f32, std_deviation_y: f32},
1622    /// synthesize a new image of specified size containing a solid color
1623    /// parameters: FilterOpGraphNode, color
1624    /// SVG filter semantics - selectable input(s), selectable between linear
1625    /// (default) and sRGB color space for calculations
1626    /// Spec: https://www.w3.org/TR/filter-effects-1/#InterfaceSVGFEFloodElement
1627    SVGFEFlood{node: FilterOpGraphNode, color: ColorF},
1628    /// create a blurred version of the input image
1629    /// parameters: FilterOpGraphNode, stdDeviationX, stdDeviationY
1630    /// SVG filter semantics - selectable input(s), selectable between linear
1631    /// (default) and sRGB color space for calculations
1632    /// Spec: https://www.w3.org/TR/filter-effects-1/#InterfaceSVGFEGaussianBlurElement
1633    SVGFEGaussianBlur{node: FilterOpGraphNode, std_deviation_x: f32, std_deviation_y: f32},
1634    /// synthesize a new image based on a url (i.e. blob image source)
1635    /// parameters: FilterOpGraphNode, sampling_filter (see SamplingFilter in Types.h), transform
1636    /// SVG filter semantics - selectable input(s), selectable between linear
1637    /// (default) and sRGB color space for calculations
1638    /// Spec: https://www.w3.org/TR/filter-effects-1/#InterfaceSVGFEImageElement
1639    SVGFEImage{node: FilterOpGraphNode, sampling_filter: u32, matrix: [f32; 6]},
1640    /// create a new image based on the input image with the contour stretched
1641    /// outward (dilate operator)
1642    /// parameters: FilterOpGraphNode, radiusX, radiusY
1643    /// SVG filter semantics - selectable input(s), selectable between linear
1644    /// (default) and sRGB color space for calculations
1645    /// Spec: https://www.w3.org/TR/filter-effects-1/#InterfaceSVGFEMorphologyElement
1646    SVGFEMorphologyDilate{node: FilterOpGraphNode, radius_x: f32, radius_y: f32},
1647    /// create a new image based on the input image with the contour shrunken
1648    /// inward (erode operator)
1649    /// parameters: FilterOpGraphNode, radiusX, radiusY
1650    /// SVG filter semantics - selectable input(s), selectable between linear
1651    /// (default) and sRGB color space for calculations
1652    /// Spec: https://www.w3.org/TR/filter-effects-1/#InterfaceSVGFEMorphologyElement
1653    SVGFEMorphologyErode{node: FilterOpGraphNode, radius_x: f32, radius_y: f32},
1654    /// create a new image that is a scrolled version of the input image, this
1655    /// is basically a no-op as we support offset in the graph node
1656    /// parameters: FilterOpGraphNode
1657    /// SVG filter semantics - selectable input(s), selectable between linear
1658    /// (default) and sRGB color space for calculations
1659    /// Spec: https://www.w3.org/TR/filter-effects-1/#InterfaceSVGFEOffsetElement
1660    SVGFEOffset{node: FilterOpGraphNode, offset_x: f32, offset_y: f32},
1661    /// calculate lighting based on heightmap image with provided values for a
1662    /// distant light source with specified direction
1663    /// parameters: FilerData, surfaceScale, specularConstant, specularExponent,
1664    ///  kernelUnitLengthX, kernelUnitLengthY, azimuth, elevation
1665    /// SVG filter semantics - selectable input(s), selectable between linear
1666    /// (default) and sRGB color space for calculations
1667    /// Spec: https://www.w3.org/TR/filter-effects-1/#InterfaceSVGFESpecularLightingElement
1668    /// https://www.w3.org/TR/filter-effects-1/#InterfaceSVGFEDistantLightElement
1669    SVGFESpecularLightingDistant{node: FilterOpGraphNode, surface_scale: f32,
1670        specular_constant: f32, specular_exponent: f32,
1671        kernel_unit_length_x: f32, kernel_unit_length_y: f32, azimuth: f32,
1672        elevation: f32},
1673    /// calculate lighting based on heightmap image with provided values for a
1674    /// point light source at specified location
1675    /// parameters: FilterOpGraphNode, surfaceScale, specularConstant,
1676    ///  specularExponent, kernelUnitLengthX, kernelUnitLengthY, x, y, z
1677    /// SVG filter semantics - selectable input(s), selectable between linear
1678    /// (default) and sRGB color space for calculations
1679    /// Spec: https://www.w3.org/TR/filter-effects-1/#InterfaceSVGFESpecularLightingElement
1680    ///  https://www.w3.org/TR/filter-effects-1/#InterfaceSVGFEPointLightElement
1681    SVGFESpecularLightingPoint{node: FilterOpGraphNode, surface_scale: f32,
1682        specular_constant: f32, specular_exponent: f32,
1683        kernel_unit_length_x: f32, kernel_unit_length_y: f32, x: f32, y: f32,
1684        z: f32},
1685    /// calculate lighting based on heightmap image with provided values for a
1686    /// spot light source at specified location pointing at specified target
1687    /// location with specified hotspot sharpness and cone angle
1688    /// parameters: FilterOpGraphNode, surfaceScale, specularConstant,
1689    ///  specularExponent, kernelUnitLengthX, kernelUnitLengthY, x, y, z,
1690    ///  pointsAtX, pointsAtY, pointsAtZ, specularExponent, limitingConeAngle
1691    /// SVG filter semantics - selectable input(s), selectable between linear
1692    /// (default) and sRGB color space for calculations
1693    /// Spec: https://www.w3.org/TR/filter-effects-1/#InterfaceSVGFESpecularLightingElement
1694    ///  https://www.w3.org/TR/filter-effects-1/#InterfaceSVGFESpotLightElement
1695    SVGFESpecularLightingSpot{node: FilterOpGraphNode, surface_scale: f32,
1696        specular_constant: f32, specular_exponent: f32,
1697        kernel_unit_length_x: f32, kernel_unit_length_y: f32, x: f32, y: f32,
1698        z: f32, points_at_x: f32, points_at_y: f32, points_at_z: f32,
1699        cone_exponent: f32, limiting_cone_angle: f32},
1700    /// create a new image based on the input image, repeated throughout the
1701    /// output rectangle
1702    /// parameters: FilterOpGraphNode
1703    /// SVG filter semantics - selectable input(s), selectable between linear
1704    /// (default) and sRGB color space for calculations
1705    /// Spec: https://www.w3.org/TR/filter-effects-1/#InterfaceSVGFETileElement
1706    SVGFETile{node: FilterOpGraphNode},
1707    /// synthesize a new image based on Fractal Noise (Perlin) with the chosen
1708    /// stitching mode
1709    /// parameters: FilterOpGraphNode, baseFrequencyX, baseFrequencyY,
1710    ///  numOctaves, seed
1711    /// SVG filter semantics - selectable input(s), selectable between linear
1712    /// (default) and sRGB color space for calculations
1713    /// Spec: https://www.w3.org/TR/filter-effects-1/#InterfaceSVGFETurbulenceElement
1714    SVGFETurbulenceWithFractalNoiseWithNoStitching{node: FilterOpGraphNode,
1715        base_frequency_x: f32, base_frequency_y: f32, num_octaves: u32,
1716        seed: u32},
1717    /// synthesize a new image based on Fractal Noise (Perlin) with the chosen
1718    /// stitching mode
1719    /// parameters: FilterOpGraphNode, baseFrequencyX, baseFrequencyY,
1720    ///  numOctaves, seed
1721    /// SVG filter semantics - selectable input(s), selectable between linear
1722    /// (default) and sRGB color space for calculations
1723    /// Spec: https://www.w3.org/TR/filter-effects-1/#InterfaceSVGFETurbulenceElement
1724    SVGFETurbulenceWithFractalNoiseWithStitching{node: FilterOpGraphNode,
1725        base_frequency_x: f32, base_frequency_y: f32, num_octaves: u32,
1726        seed: u32},
1727    /// synthesize a new image based on Turbulence Noise (offset vectors)
1728    /// parameters: FilterOpGraphNode, baseFrequencyX, baseFrequencyY,
1729    ///  numOctaves, seed
1730    /// SVG filter semantics - selectable input(s), selectable between linear
1731    /// (default) and sRGB color space for calculations
1732    /// Spec: https://www.w3.org/TR/filter-effects-1/#InterfaceSVGFETurbulenceElement
1733    SVGFETurbulenceWithTurbulenceNoiseWithNoStitching{node: FilterOpGraphNode,
1734        base_frequency_x: f32, base_frequency_y: f32, num_octaves: u32,
1735        seed: u32},
1736    /// synthesize a new image based on Turbulence Noise (offset vectors)
1737    /// parameters: FilterOpGraphNode, baseFrequencyX, baseFrequencyY,
1738    ///  numOctaves, seed
1739    /// SVG filter semantics - selectable input(s), selectable between linear
1740    /// (default) and sRGB color space for calculations
1741    /// Spec: https://www.w3.org/TR/filter-effects-1/#InterfaceSVGFETurbulenceElement
1742    SVGFETurbulenceWithTurbulenceNoiseWithStitching{node: FilterOpGraphNode,
1743        base_frequency_x: f32, base_frequency_y: f32, num_octaves: u32, seed: u32},
1744}
1745
1746#[repr(u8)]
1747#[derive(Clone, Copy, Debug, PartialEq, Deserialize, Serialize, PeekPoke)]
1748pub enum ComponentTransferFuncType {
1749  Identity = 0,
1750  Table = 1,
1751  Discrete = 2,
1752  Linear = 3,
1753  Gamma = 4,
1754}
1755
1756#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
1757pub struct FilterData {
1758    /// ComponentTransfer / SVGFEComponentTransfer
1759    pub func_r_type: ComponentTransferFuncType,
1760    pub r_values: Vec<f32>,
1761    pub func_g_type: ComponentTransferFuncType,
1762    pub g_values: Vec<f32>,
1763    pub func_b_type: ComponentTransferFuncType,
1764    pub b_values: Vec<f32>,
1765    pub func_a_type: ComponentTransferFuncType,
1766    pub a_values: Vec<f32>,
1767}
1768
1769fn sanitize_func_type(
1770    func_type: ComponentTransferFuncType,
1771    values: &[f32],
1772) -> ComponentTransferFuncType {
1773    if values.is_empty() {
1774        return ComponentTransferFuncType::Identity;
1775    }
1776    if values.len() < 2 && func_type == ComponentTransferFuncType::Linear {
1777        return ComponentTransferFuncType::Identity;
1778    }
1779    if values.len() < 3 && func_type == ComponentTransferFuncType::Gamma {
1780        return ComponentTransferFuncType::Identity;
1781    }
1782    func_type
1783}
1784
1785fn sanitize_values(
1786    func_type: ComponentTransferFuncType,
1787    values: &[f32],
1788) -> bool {
1789    if values.len() < 2 && func_type == ComponentTransferFuncType::Linear {
1790        return false;
1791    }
1792    if values.len() < 3 && func_type == ComponentTransferFuncType::Gamma {
1793        return false;
1794    }
1795    true
1796}
1797
1798impl FilterData {
1799    /// Ensure that the number of values matches up with the function type.
1800    pub fn sanitize(&self) -> FilterData {
1801        FilterData {
1802            func_r_type: sanitize_func_type(self.func_r_type, &self.r_values),
1803            r_values:
1804                    if sanitize_values(self.func_r_type, &self.r_values) {
1805                        self.r_values.clone()
1806                    } else {
1807                        Vec::new()
1808                    },
1809            func_g_type: sanitize_func_type(self.func_g_type, &self.g_values),
1810            g_values:
1811                    if sanitize_values(self.func_g_type, &self.g_values) {
1812                        self.g_values.clone()
1813                    } else {
1814                        Vec::new()
1815                    },
1816
1817            func_b_type: sanitize_func_type(self.func_b_type, &self.b_values),
1818            b_values:
1819                    if sanitize_values(self.func_b_type, &self.b_values) {
1820                        self.b_values.clone()
1821                    } else {
1822                        Vec::new()
1823                    },
1824
1825            func_a_type: sanitize_func_type(self.func_a_type, &self.a_values),
1826            a_values:
1827                    if sanitize_values(self.func_a_type, &self.a_values) {
1828                        self.a_values.clone()
1829                    } else {
1830                        Vec::new()
1831                    },
1832
1833        }
1834    }
1835
1836    pub fn is_identity(&self) -> bool {
1837        self.func_r_type == ComponentTransferFuncType::Identity &&
1838        self.func_g_type == ComponentTransferFuncType::Identity &&
1839        self.func_b_type == ComponentTransferFuncType::Identity &&
1840        self.func_a_type == ComponentTransferFuncType::Identity
1841    }
1842}
1843
1844#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)]
1845pub struct IframeDisplayItem {
1846    pub bounds: LayoutRect,
1847    pub clip_rect: LayoutRect,
1848    pub space_and_clip: SpaceAndClipInfo,
1849    pub pipeline_id: PipelineId,
1850    pub ignore_missing_pipeline: bool,
1851}
1852
1853/// This describes an image that fills the specified area. It stretches or shrinks
1854/// the image as necessary. While RepeatingImageDisplayItem could otherwise provide
1855/// a superset of the functionality, it has been problematic inferring the desired
1856/// repetition properties when snapping changes the size of the primitive.
1857#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)]
1858pub struct ImageDisplayItem {
1859    pub common: CommonItemProperties,
1860    /// The area to tile the image over (first tile starts at origin of this rect)
1861    // FIXME: this should ideally just be `tile_origin` here, with the clip_rect
1862    // defining the bounds of the item. Needs non-trivial backend changes.
1863    pub bounds: LayoutRect,
1864    pub image_key: ImageKey,
1865    pub image_rendering: ImageRendering,
1866    pub alpha_type: AlphaType,
1867    /// A hack used by gecko to color a simple bitmap font used for tofu glyphs
1868    pub color: ColorF,
1869}
1870
1871/// This describes a background-image and its tiling. It repeats in a grid to fill
1872/// the specified area.
1873#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)]
1874pub struct RepeatingImageDisplayItem {
1875    pub common: CommonItemProperties,
1876    /// The area to tile the image over (first tile starts at origin of this rect)
1877    // FIXME: this should ideally just be `tile_origin` here, with the clip_rect
1878    // defining the bounds of the item. Needs non-trivial backend changes.
1879    pub bounds: LayoutRect,
1880    /// How large to make a single tile of the image (common case: bounds.size)
1881    pub stretch_size: LayoutSize,
1882    /// The space between tiles (common case: 0)
1883    pub tile_spacing: LayoutSize,
1884    pub image_key: ImageKey,
1885    pub image_rendering: ImageRendering,
1886    pub alpha_type: AlphaType,
1887    /// A hack used by gecko to color a simple bitmap font used for tofu glyphs
1888    pub color: ColorF,
1889}
1890
1891#[repr(u8)]
1892#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize, PeekPoke)]
1893pub enum ImageRendering {
1894    Auto = 0,
1895    CrispEdges = 1,
1896    Pixelated = 2,
1897}
1898
1899#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize, PeekPoke)]
1900pub enum AlphaType {
1901    Alpha = 0,
1902    PremultipliedAlpha = 1,
1903}
1904
1905#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)]
1906pub struct YuvImageDisplayItem {
1907    pub common: CommonItemProperties,
1908    pub bounds: LayoutRect,
1909    pub yuv_data: YuvData,
1910    pub color_depth: ColorDepth,
1911    pub color_space: YuvColorSpace,
1912    pub color_range: ColorRange,
1913    pub image_rendering: ImageRendering,
1914}
1915
1916#[repr(u8)]
1917#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize, PeekPoke)]
1918pub enum YuvColorSpace {
1919    Rec601 = 0,
1920    Rec709 = 1,
1921    Rec2020 = 2,
1922    Identity = 3, // aka GBR as per ISO/IEC 23091-2:2019
1923}
1924
1925#[repr(u8)]
1926#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize, PeekPoke)]
1927pub enum ColorRange {
1928    Limited = 0,
1929    Full = 1,
1930}
1931
1932#[repr(u8)]
1933#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize, PeekPoke)]
1934pub enum YuvRangedColorSpace {
1935    Rec601Narrow = 0,
1936    Rec601Full = 1,
1937    Rec709Narrow = 2,
1938    Rec709Full = 3,
1939    Rec2020Narrow = 4,
1940    Rec2020Full = 5,
1941    GbrIdentity = 6,
1942}
1943
1944impl YuvColorSpace {
1945    pub fn with_range(self, range: ColorRange) -> YuvRangedColorSpace {
1946        match self {
1947            YuvColorSpace::Identity => YuvRangedColorSpace::GbrIdentity,
1948            YuvColorSpace::Rec601 => {
1949                match range {
1950                    ColorRange::Limited => YuvRangedColorSpace::Rec601Narrow,
1951                    ColorRange::Full => YuvRangedColorSpace::Rec601Full,
1952                }
1953            }
1954            YuvColorSpace::Rec709 => {
1955                match range {
1956                    ColorRange::Limited => YuvRangedColorSpace::Rec709Narrow,
1957                    ColorRange::Full => YuvRangedColorSpace::Rec709Full,
1958                }
1959            }
1960            YuvColorSpace::Rec2020 => {
1961                match range {
1962                    ColorRange::Limited => YuvRangedColorSpace::Rec2020Narrow,
1963                    ColorRange::Full => YuvRangedColorSpace::Rec2020Full,
1964                }
1965            }
1966        }
1967    }
1968}
1969
1970#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize, PeekPoke)]
1971pub enum YuvData {
1972    NV12(ImageKey, ImageKey), // (Y channel, CbCr interleaved channel)
1973    P010(ImageKey, ImageKey), // (Y channel, CbCr interleaved channel)
1974    NV16(ImageKey, ImageKey), // (Y channel, CbCr interleaved channel)
1975    PlanarYCbCr(ImageKey, ImageKey, ImageKey), // (Y channel, Cb channel, Cr Channel)
1976    InterleavedYCbCr(ImageKey), // (YCbCr interleaved channel)
1977}
1978
1979impl YuvData {
1980    pub fn get_format(&self) -> YuvFormat {
1981        match *self {
1982            YuvData::NV12(..) => YuvFormat::NV12,
1983            YuvData::P010(..) => YuvFormat::P010,
1984            YuvData::NV16(..) => YuvFormat::NV16,
1985            YuvData::PlanarYCbCr(..) => YuvFormat::PlanarYCbCr,
1986            YuvData::InterleavedYCbCr(..) => YuvFormat::InterleavedYCbCr,
1987        }
1988    }
1989}
1990
1991#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize, PeekPoke)]
1992pub enum YuvFormat {
1993    // These enum values need to be kept in sync with yuv.glsl.
1994    NV12 = 0,
1995    P010 = 1,
1996    NV16 = 2,
1997    PlanarYCbCr = 3,
1998    InterleavedYCbCr = 4,
1999}
2000
2001impl YuvFormat {
2002    pub fn get_plane_num(self) -> usize {
2003        match self {
2004            YuvFormat::NV12 | YuvFormat::P010 | YuvFormat::NV16 => 2,
2005            YuvFormat::PlanarYCbCr => 3,
2006            YuvFormat::InterleavedYCbCr => 1,
2007        }
2008    }
2009}
2010
2011#[repr(C)]
2012#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)]
2013pub struct ImageMask {
2014    pub image: ImageKey,
2015    pub rect: LayoutRect,
2016}
2017
2018impl ImageMask {
2019    /// Get a local clipping rect contributed by this mask.
2020    pub fn get_local_clip_rect(&self) -> Option<LayoutRect> {
2021        Some(self.rect)
2022    }
2023}
2024
2025#[repr(C)]
2026#[derive(Copy, Clone, Debug, MallocSizeOf, PartialEq, Serialize, Deserialize, Eq, Hash, PeekPoke)]
2027pub enum ClipMode {
2028    Clip,    // Pixels inside the region are visible.
2029    ClipOut, // Pixels outside the region are visible.
2030}
2031
2032impl Not for ClipMode {
2033    type Output = ClipMode;
2034
2035    fn not(self) -> ClipMode {
2036        match self {
2037            ClipMode::Clip => ClipMode::ClipOut,
2038            ClipMode::ClipOut => ClipMode::Clip,
2039        }
2040    }
2041}
2042
2043#[repr(C)]
2044#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)]
2045pub struct ComplexClipRegion {
2046    /// The boundaries of the rectangle.
2047    pub rect: LayoutRect,
2048    /// Border radii of this rectangle.
2049    pub radii: BorderRadius,
2050    /// Whether we are clipping inside or outside
2051    /// the region.
2052    pub mode: ClipMode,
2053}
2054
2055impl BorderRadius {
2056    pub fn zero() -> BorderRadius {
2057        BorderRadius {
2058            top_left: LayoutSize::new(0.0, 0.0),
2059            top_right: LayoutSize::new(0.0, 0.0),
2060            bottom_left: LayoutSize::new(0.0, 0.0),
2061            bottom_right: LayoutSize::new(0.0, 0.0),
2062        }
2063    }
2064
2065    pub fn uniform(radius: f32) -> BorderRadius {
2066        BorderRadius {
2067            top_left: LayoutSize::new(radius, radius),
2068            top_right: LayoutSize::new(radius, radius),
2069            bottom_left: LayoutSize::new(radius, radius),
2070            bottom_right: LayoutSize::new(radius, radius),
2071        }
2072    }
2073
2074    pub fn uniform_size(radius: LayoutSize) -> BorderRadius {
2075        BorderRadius {
2076            top_left: radius,
2077            top_right: radius,
2078            bottom_left: radius,
2079            bottom_right: radius,
2080        }
2081    }
2082
2083    pub fn all_sides_uniform(&self) -> bool {
2084        let corner_is_uniform = |corner: &LayoutSize| corner.width == corner.height;
2085        corner_is_uniform(&self.top_left) &&
2086        corner_is_uniform(&self.top_right) &&
2087        corner_is_uniform(&self.bottom_right) &&
2088        corner_is_uniform(&self.bottom_left)
2089    }
2090
2091    pub fn can_use_fast_path_in(&self, rect: &LayoutRect) -> bool {
2092        if !self.all_sides_uniform() {
2093            // The fast path needs uniform sides.
2094            return false;
2095        }
2096        // The shader code that evaluates the rounded corners in the fast path relies on each
2097        // corner fitting into their quadrant of the quad. In other words the radius cannot
2098        // exceed half of the length of the sides they are on. That necessarily holds if all the
2099        // radii are the same.
2100        let tl = self.top_left.width;
2101        if tl == self.bottom_right.width && tl == self.top_right.width && tl == self.bottom_left.width {
2102            return true;
2103        }
2104        let half_size = rect.size() * 0.5;
2105        let fits = |v: f32| v <= half_size.width && v <= half_size.height;
2106        fits(tl) && fits(self.bottom_right.width) && fits(self.top_right.width) && fits(self.bottom_left.width)
2107    }
2108
2109    /// Return whether, in each corner, the radius in *either* direction is zero.
2110    /// This means that none of the corners are rounded.
2111    pub fn is_zero(&self) -> bool {
2112        let corner_is_zero = |corner: &LayoutSize| corner.width == 0.0 || corner.height == 0.0;
2113        corner_is_zero(&self.top_left) &&
2114        corner_is_zero(&self.top_right) &&
2115        corner_is_zero(&self.bottom_right) &&
2116        corner_is_zero(&self.bottom_left)
2117    }
2118}
2119
2120impl ComplexClipRegion {
2121    /// Create a new complex clip region.
2122    pub fn new(
2123        rect: LayoutRect,
2124        radii: BorderRadius,
2125        mode: ClipMode,
2126    ) -> Self {
2127        ComplexClipRegion { rect, radii, mode }
2128    }
2129}
2130
2131impl ComplexClipRegion {
2132    /// Get a local clipping rect contributed by this clip region.
2133    pub fn get_local_clip_rect(&self) -> Option<LayoutRect> {
2134        match self.mode {
2135            ClipMode::Clip => {
2136                Some(self.rect)
2137            }
2138            ClipMode::ClipOut => {
2139                None
2140            }
2141        }
2142    }
2143}
2144
2145pub const POLYGON_CLIP_VERTEX_MAX: usize = 32;
2146
2147#[repr(u8)]
2148#[derive(Clone, Copy, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize, Eq, Hash, PeekPoke)]
2149pub enum FillRule {
2150    Nonzero = 0x1, // Behaves as the SVG fill-rule definition for nonzero.
2151    Evenodd = 0x2, // Behaves as the SVG fill-rule definition for evenodd.
2152}
2153
2154impl From<u8> for FillRule {
2155    fn from(fill_rule: u8) -> Self {
2156        match fill_rule {
2157            0x1 => FillRule::Nonzero,
2158            0x2 => FillRule::Evenodd,
2159            _ => panic!("Unexpected FillRule value."),
2160        }
2161    }
2162}
2163
2164impl From<FillRule> for u8 {
2165    fn from(fill_rule: FillRule) -> Self {
2166        match fill_rule {
2167            FillRule::Nonzero => 0x1,
2168            FillRule::Evenodd => 0x2,
2169        }
2170    }
2171}
2172
2173#[derive(Clone, Copy, Debug, Default, Deserialize, Eq, Hash, PartialEq, Serialize, PeekPoke)]
2174pub struct ClipChainId(pub u64, pub PipelineId);
2175
2176impl ClipChainId {
2177    pub const INVALID: Self = ClipChainId(!0, PipelineId::INVALID);
2178}
2179
2180/// A reference to a clipping node defining how an item is clipped.
2181#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize, PeekPoke)]
2182pub struct ClipId(pub usize, pub PipelineId);
2183
2184impl Default for ClipId {
2185    fn default() -> Self {
2186        ClipId::invalid()
2187    }
2188}
2189
2190const ROOT_CLIP_ID: usize = 0;
2191
2192impl ClipId {
2193    /// Return the root clip ID - effectively doing no clipping.
2194    pub fn root(pipeline_id: PipelineId) -> Self {
2195        ClipId(ROOT_CLIP_ID, pipeline_id)
2196    }
2197
2198    /// Return an invalid clip ID - needed in places where we carry
2199    /// one but need to not attempt to use it.
2200    pub fn invalid() -> Self {
2201        ClipId(!0, PipelineId::dummy())
2202    }
2203
2204    pub fn pipeline_id(&self) -> PipelineId {
2205        match *self {
2206            ClipId(_, pipeline_id) => pipeline_id,
2207        }
2208    }
2209
2210    pub fn is_root(&self) -> bool {
2211        match *self {
2212            ClipId(id, _) => id == ROOT_CLIP_ID,
2213        }
2214    }
2215
2216    pub fn is_valid(&self) -> bool {
2217        match *self {
2218            ClipId(id, _) => id != !0,
2219        }
2220    }
2221}
2222
2223/// A reference to a spatial node defining item positioning.
2224#[derive(Clone, Copy, Debug, Default, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize, PeekPoke)]
2225pub struct SpatialId(pub usize, PipelineId);
2226
2227const ROOT_REFERENCE_FRAME_SPATIAL_ID: usize = 0;
2228const ROOT_SCROLL_NODE_SPATIAL_ID: usize = 1;
2229
2230impl SpatialId {
2231    pub fn new(spatial_node_index: usize, pipeline_id: PipelineId) -> Self {
2232        SpatialId(spatial_node_index, pipeline_id)
2233    }
2234
2235    pub fn root_reference_frame(pipeline_id: PipelineId) -> Self {
2236        SpatialId(ROOT_REFERENCE_FRAME_SPATIAL_ID, pipeline_id)
2237    }
2238
2239    pub fn root_scroll_node(pipeline_id: PipelineId) -> Self {
2240        SpatialId(ROOT_SCROLL_NODE_SPATIAL_ID, pipeline_id)
2241    }
2242
2243    pub fn pipeline_id(&self) -> PipelineId {
2244        self.1
2245    }
2246}
2247
2248/// An external identifier that uniquely identifies a scroll frame independent of its ClipId, which
2249/// may change from frame to frame. This should be unique within a pipeline. WebRender makes no
2250/// attempt to ensure uniqueness. The zero value is reserved for use by the root scroll node of
2251/// every pipeline, which always has an external id.
2252///
2253/// When setting display lists with the `preserve_frame_state` this id is used to preserve scroll
2254/// offsets between different sets of SpatialNodes which are ScrollFrames.
2255#[derive(Clone, Copy, Debug, Default, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize, PeekPoke)]
2256#[repr(C)]
2257pub struct ExternalScrollId(pub u64, pub PipelineId);
2258
2259impl ExternalScrollId {
2260    pub fn pipeline_id(&self) -> PipelineId {
2261        self.1
2262    }
2263
2264    pub fn is_root(&self) -> bool {
2265        self.0 == 0
2266    }
2267}
2268
2269impl DisplayItem {
2270    pub fn debug_name(&self) -> &'static str {
2271        match *self {
2272            DisplayItem::Border(..) => "border",
2273            DisplayItem::BoxShadow(..) => "box_shadow",
2274            DisplayItem::ClearRectangle(..) => "clear_rectangle",
2275            DisplayItem::HitTest(..) => "hit_test",
2276            DisplayItem::RectClip(..) => "rect_clip",
2277            DisplayItem::RoundedRectClip(..) => "rounded_rect_clip",
2278            DisplayItem::ImageMaskClip(..) => "image_mask_clip",
2279            DisplayItem::ClipChain(..) => "clip_chain",
2280            DisplayItem::ConicGradient(..) => "conic_gradient",
2281            DisplayItem::Gradient(..) => "gradient",
2282            DisplayItem::Iframe(..) => "iframe",
2283            DisplayItem::Image(..) => "image",
2284            DisplayItem::RepeatingImage(..) => "repeating_image",
2285            DisplayItem::Line(..) => "line",
2286            DisplayItem::PopAllShadows => "pop_all_shadows",
2287            DisplayItem::PopReferenceFrame => "pop_reference_frame",
2288            DisplayItem::PopStackingContext => "pop_stacking_context",
2289            DisplayItem::PushShadow(..) => "push_shadow",
2290            DisplayItem::PushReferenceFrame(..) => "push_reference_frame",
2291            DisplayItem::PushStackingContext(..) => "push_stacking_context",
2292            DisplayItem::SetFilterOps => "set_filter_ops",
2293            DisplayItem::SetFilterData => "set_filter_data",
2294            DisplayItem::SetFilterPrimitives => "set_filter_primitives",
2295            DisplayItem::SetPoints => "set_points",
2296            DisplayItem::RadialGradient(..) => "radial_gradient",
2297            DisplayItem::Rectangle(..) => "rectangle",
2298            DisplayItem::SetGradientStops => "set_gradient_stops",
2299            DisplayItem::ReuseItems(..) => "reuse_item",
2300            DisplayItem::RetainedItems(..) => "retained_items",
2301            DisplayItem::Text(..) => "text",
2302            DisplayItem::YuvImage(..) => "yuv_image",
2303            DisplayItem::BackdropFilter(..) => "backdrop_filter",
2304            DisplayItem::DebugMarker(..) => "debug",
2305        }
2306    }
2307}
2308
2309macro_rules! impl_default_for_enums {
2310    ($($enum:ident => $init:expr ),+) => {
2311        $(impl Default for $enum {
2312            #[allow(unused_imports)]
2313            fn default() -> Self {
2314                use $enum::*;
2315                $init
2316            }
2317        })*
2318    }
2319}
2320
2321impl_default_for_enums! {
2322    DisplayItem => PopStackingContext,
2323    LineOrientation => Vertical,
2324    LineStyle => Solid,
2325    RepeatMode => Stretch,
2326    NinePatchBorderSource => Image(ImageKey::default(), ImageRendering::Auto),
2327    BorderDetails => Normal(NormalBorder::default()),
2328    BorderRadiusKind => Uniform,
2329    BorderStyle => None,
2330    BoxShadowClipMode => Outset,
2331    ExtendMode => Clamp,
2332    FilterOp => Identity,
2333    ComponentTransferFuncType => Identity,
2334    ClipMode => Clip,
2335    FillRule => Nonzero,
2336    ReferenceFrameKind => Transform {
2337        is_2d_scale_translation: false,
2338        should_snap: false,
2339        paired_with_perspective: false,
2340    },
2341    Rotation => Degree0,
2342    TransformStyle => Flat,
2343    RasterSpace => Local(f32::default()),
2344    MixBlendMode => Normal,
2345    ImageRendering => Auto,
2346    AlphaType => Alpha,
2347    YuvColorSpace => Rec601,
2348    YuvRangedColorSpace => Rec601Narrow,
2349    ColorRange => Limited,
2350    YuvData => NV12(ImageKey::default(), ImageKey::default()),
2351    YuvFormat => NV12,
2352    FilterPrimitiveInput => Original,
2353    ColorSpace => Srgb,
2354    CompositeOperator => Over
2355}