tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

display_item.rs (94028B)


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