webrender_api/
display_item.rs

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