diff options
Diffstat (limited to 'gfx/wr/webrender_api/src/display_item.rs')
-rw-r--r-- | gfx/wr/webrender_api/src/display_item.rs | 1669 |
1 files changed, 1669 insertions, 0 deletions
diff --git a/gfx/wr/webrender_api/src/display_item.rs b/gfx/wr/webrender_api/src/display_item.rs new file mode 100644 index 0000000000..1ba51f9401 --- /dev/null +++ b/gfx/wr/webrender_api/src/display_item.rs @@ -0,0 +1,1669 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +use euclid::{SideOffsets2D, Angle}; +use peek_poke::PeekPoke; +use std::ops::Not; +// local imports +use crate::font; +use crate::{PipelineId, PropertyBinding}; +use crate::color::ColorF; +use crate::image::{ColorDepth, ImageKey}; +use crate::units::*; +use std::hash::{Hash, Hasher}; + +// ****************************************************************** +// * NOTE: some of these structs have an "IMPLICIT" comment. * +// * This indicates that the BuiltDisplayList will have serialized * +// * a list of values nearby that this item consumes. The traversal * +// * iterator should handle finding these. DebugDisplayItem should * +// * make them explicit. * +// ****************************************************************** + +/// A tag that can be used to identify items during hit testing. If the tag +/// is missing then the item doesn't take part in hit testing at all. This +/// is composed of two numbers. In Servo, the first is an identifier while the +/// second is used to select the cursor that should be used during mouse +/// movement. In Gecko, the first is a scrollframe identifier, while the second +/// is used to store various flags that APZ needs to properly process input +/// events. +pub type ItemTag = (u64, u16); + +/// An identifier used to refer to previously sent display items. Currently it +/// refers to individual display items, but this may change later. +pub type ItemKey = u16; + +bitflags! { + #[repr(C)] + #[derive(Deserialize, MallocSizeOf, Serialize, PeekPoke)] + pub struct PrimitiveFlags: u8 { + /// The CSS backface-visibility property (yes, it can be really granular) + const IS_BACKFACE_VISIBLE = 1 << 0; + /// If set, this primitive represents a scroll bar container + const IS_SCROLLBAR_CONTAINER = 1 << 1; + /// If set, this primitive represents a scroll bar thumb + const IS_SCROLLBAR_THUMB = 1 << 2; + /// This is used as a performance hint - this primitive may be promoted to a native + /// compositor surface under certain (implementation specific) conditions. This + /// is typically used for large videos, and canvas elements. + const PREFER_COMPOSITOR_SURFACE = 1 << 3; + /// If set, this primitive can be passed directly to the compositor via its + /// ExternalImageId, and the compositor will use the native image directly. + /// Used as a further extension on top of PREFER_COMPOSITOR_SURFACE. + const SUPPORTS_EXTERNAL_COMPOSITOR_SURFACE = 1 << 4; + } +} + +impl Default for PrimitiveFlags { + fn default() -> Self { + PrimitiveFlags::IS_BACKFACE_VISIBLE + } +} + +/// A grouping of fields a lot of display items need, just to avoid +/// repeating these over and over in this file. +#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)] +pub struct CommonItemProperties { + /// Bounds of the display item to clip to. Many items are logically + /// infinite, and rely on this clip_rect to define their bounds + /// (solid colors, background-images, gradients, etc). + pub clip_rect: LayoutRect, + /// Additional clips + pub clip_id: ClipId, + /// The coordinate-space the item is in (yes, it can be really granular) + pub spatial_id: SpatialId, + /// Various flags describing properties of this primitive. + pub flags: PrimitiveFlags, +} + +impl CommonItemProperties { + /// Convenience for tests. + pub fn new( + clip_rect: LayoutRect, + space_and_clip: SpaceAndClipInfo, + ) -> Self { + Self { + clip_rect, + spatial_id: space_and_clip.spatial_id, + clip_id: space_and_clip.clip_id, + flags: PrimitiveFlags::default(), + } + } +} + +/// Per-primitive information about the nodes in the clip tree and +/// the spatial tree that the primitive belongs to. +/// +/// Note: this is a separate struct from `PrimitiveInfo` because +/// it needs indirectional mapping during the DL flattening phase, +/// turning into `ScrollNodeAndClipChain`. +#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)] +pub struct SpaceAndClipInfo { + pub spatial_id: SpatialId, + pub clip_id: ClipId, +} + +impl SpaceAndClipInfo { + /// Create a new space/clip info associated with the root + /// scroll frame. + pub fn root_scroll(pipeline_id: PipelineId) -> Self { + SpaceAndClipInfo { + spatial_id: SpatialId::root_scroll_node(pipeline_id), + clip_id: ClipId::root(pipeline_id), + } + } +} + +#[repr(u8)] +#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize, PeekPoke)] +pub enum DisplayItem { + // These are the "real content" display items + Rectangle(RectangleDisplayItem), + ClearRectangle(ClearRectangleDisplayItem), + HitTest(HitTestDisplayItem), + Text(TextDisplayItem), + Line(LineDisplayItem), + Border(BorderDisplayItem), + BoxShadow(BoxShadowDisplayItem), + PushShadow(PushShadowDisplayItem), + Gradient(GradientDisplayItem), + RadialGradient(RadialGradientDisplayItem), + ConicGradient(ConicGradientDisplayItem), + Image(ImageDisplayItem), + RepeatingImage(RepeatingImageDisplayItem), + YuvImage(YuvImageDisplayItem), + BackdropFilter(BackdropFilterDisplayItem), + + // Clips + RectClip(RectClipDisplayItem), + RoundedRectClip(RoundedRectClipDisplayItem), + ImageMaskClip(ImageMaskClipDisplayItem), + Clip(ClipDisplayItem), + ClipChain(ClipChainItem), + + // Spaces and Frames that content can be scoped under. + ScrollFrame(ScrollFrameDisplayItem), + StickyFrame(StickyFrameDisplayItem), + Iframe(IframeDisplayItem), + PushReferenceFrame(ReferenceFrameDisplayListItem), + PushStackingContext(PushStackingContextDisplayItem), + + // These marker items indicate an array of data follows, to be used for the + // next non-marker item. + SetGradientStops, + SetFilterOps, + SetFilterData, + SetFilterPrimitives, + + // These marker items terminate a scope introduced by a previous item. + PopReferenceFrame, + PopStackingContext, + PopAllShadows, + + ReuseItems(ItemKey), + RetainedItems(ItemKey), +} + +/// This is a "complete" version of the DisplayItem, with all implicit trailing +/// arrays included, for debug serialization (captures). +#[cfg(any(feature = "serialize", feature = "deserialize"))] +#[cfg_attr(feature = "serialize", derive(Serialize))] +#[cfg_attr(feature = "deserialize", derive(Deserialize))] +pub enum DebugDisplayItem { + Rectangle(RectangleDisplayItem), + ClearRectangle(ClearRectangleDisplayItem), + HitTest(HitTestDisplayItem), + Text(TextDisplayItem, Vec<font::GlyphInstance>), + Line(LineDisplayItem), + Border(BorderDisplayItem), + BoxShadow(BoxShadowDisplayItem), + PushShadow(PushShadowDisplayItem), + Gradient(GradientDisplayItem), + RadialGradient(RadialGradientDisplayItem), + ConicGradient(ConicGradientDisplayItem), + Image(ImageDisplayItem), + RepeatingImage(RepeatingImageDisplayItem), + YuvImage(YuvImageDisplayItem), + BackdropFilter(BackdropFilterDisplayItem), + + ImageMaskClip(ImageMaskClipDisplayItem), + RoundedRectClip(RoundedRectClipDisplayItem), + RectClip(RectClipDisplayItem), + Clip(ClipDisplayItem, Vec<ComplexClipRegion>), + ClipChain(ClipChainItem, Vec<ClipId>), + + ScrollFrame(ScrollFrameDisplayItem), + StickyFrame(StickyFrameDisplayItem), + Iframe(IframeDisplayItem), + PushReferenceFrame(ReferenceFrameDisplayListItem), + PushStackingContext(PushStackingContextDisplayItem), + + SetGradientStops(Vec<GradientStop>), + SetFilterOps(Vec<FilterOp>), + SetFilterData(FilterData), + SetFilterPrimitives(Vec<FilterPrimitive>), + + PopReferenceFrame, + PopStackingContext, + PopAllShadows, +} + +#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)] +pub struct ImageMaskClipDisplayItem { + pub id: ClipId, + pub parent_space_and_clip: SpaceAndClipInfo, + pub image_mask: ImageMask, +} + +#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)] +pub struct RectClipDisplayItem { + pub id: ClipId, + pub parent_space_and_clip: SpaceAndClipInfo, + pub clip_rect: LayoutRect, +} + +#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)] +pub struct RoundedRectClipDisplayItem { + pub id: ClipId, + pub parent_space_and_clip: SpaceAndClipInfo, + pub clip: ComplexClipRegion, +} + +#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)] +pub struct ClipDisplayItem { + pub id: ClipId, + pub parent_space_and_clip: SpaceAndClipInfo, + pub clip_rect: LayoutRect, +} // IMPLICIT: complex_clips: Vec<ComplexClipRegion> + +/// The minimum and maximum allowable offset for a sticky frame in a single dimension. +#[repr(C)] +#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)] +pub struct StickyOffsetBounds { + /// The minimum offset for this frame, typically a negative value, which specifies how + /// far in the negative direction the sticky frame can offset its contents in this + /// dimension. + pub min: f32, + + /// The maximum offset for this frame, typically a positive value, which specifies how + /// far in the positive direction the sticky frame can offset its contents in this + /// dimension. + pub max: f32, +} + +impl StickyOffsetBounds { + pub fn new(min: f32, max: f32) -> StickyOffsetBounds { + StickyOffsetBounds { min, max } + } +} + +#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)] +pub struct StickyFrameDisplayItem { + pub id: SpatialId, + pub parent_spatial_id: SpatialId, + pub bounds: LayoutRect, + + /// The margins that should be maintained between the edge of the parent viewport and this + /// sticky frame. A margin of None indicates that the sticky frame should not stick at all + /// to that particular edge of the viewport. + pub margins: SideOffsets2D<Option<f32>, LayoutPixel>, + + /// The minimum and maximum vertical offsets for this sticky frame. Ignoring these constraints, + /// the sticky frame will continue to stick to the edge of the viewport as its original + /// position is scrolled out of view. Constraints specify a maximum and minimum offset from the + /// original position relative to non-sticky content within the same scrolling frame. + pub vertical_offset_bounds: StickyOffsetBounds, + + /// The minimum and maximum horizontal offsets for this sticky frame. Ignoring these constraints, + /// the sticky frame will continue to stick to the edge of the viewport as its original + /// position is scrolled out of view. Constraints specify a maximum and minimum offset from the + /// original position relative to non-sticky content within the same scrolling frame. + pub horizontal_offset_bounds: StickyOffsetBounds, + + /// The amount of offset that has already been applied to the sticky frame. A positive y + /// component this field means that a top-sticky item was in a scrollframe that has been + /// scrolled down, such that the sticky item's position needed to be offset downwards by + /// `previously_applied_offset.y`. A negative y component corresponds to the upward offset + /// applied due to bottom-stickiness. The x-axis works analogously. + pub previously_applied_offset: LayoutVector2D, +} + +#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize, PeekPoke)] +pub enum ScrollSensitivity { + ScriptAndInputEvents, + Script, +} + +#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)] +pub struct ScrollFrameDisplayItem { + /// The id of the clip this scroll frame creates + pub clip_id: ClipId, + /// The id of the space this scroll frame creates + pub scroll_frame_id: SpatialId, + /// The size of the contents this contains (so the backend knows how far it can scroll). + // FIXME: this can *probably* just be a size? Origin seems to just get thrown out. + pub content_rect: LayoutRect, + pub clip_rect: LayoutRect, + pub parent_space_and_clip: SpaceAndClipInfo, + pub external_id: ExternalScrollId, + pub scroll_sensitivity: ScrollSensitivity, + /// The amount this scrollframe has already been scrolled by, in the caller. + /// This means that all the display items that are inside the scrollframe + /// will have their coordinates shifted by this amount, and this offset + /// should be added to those display item coordinates in order to get a + /// normalized value that is consistent across display lists. + pub external_scroll_offset: LayoutVector2D, +} + +/// A solid or an animating color to draw (may not actually be a rectangle due to complex clips) +#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)] +pub struct RectangleDisplayItem { + pub common: CommonItemProperties, + pub bounds: LayoutRect, + pub color: PropertyBinding<ColorF>, +} + +/// Clears all colors from the area, making it possible to cut holes in the window. +/// (useful for things like the macos frosted-glass effect). +#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)] +pub struct ClearRectangleDisplayItem { + pub common: CommonItemProperties, + pub bounds: LayoutRect, +} + +/// A minimal hit-testable item for the parent browser's convenience, and is +/// slimmer than a RectangleDisplayItem (no color). The existence of this as a +/// distinct item also makes it easier to inspect/debug display items. +#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)] +pub struct HitTestDisplayItem { + pub common: CommonItemProperties, + pub tag: ItemTag, +} + +#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)] +pub struct LineDisplayItem { + pub common: CommonItemProperties, + /// We need a separate rect from common.clip_rect to encode cute + /// tricks that firefox does to make a series of text-decorations seamlessly + /// line up -- snapping the decorations to a multiple of their period, and + /// then clipping them to their "proper" area. This rect is that "logical" + /// snapped area that may be clipped to the right size by the clip_rect. + pub area: LayoutRect, + /// Whether the rect is interpretted as vertical or horizontal + pub orientation: LineOrientation, + /// This could potentially be implied from area, but we currently prefer + /// that this is the responsibility of the layout engine. Value irrelevant + /// for non-wavy lines. + // FIXME: this was done before we could use tagged unions in enums, but now + // it should just be part of LineStyle::Wavy. + pub wavy_line_thickness: f32, + pub color: ColorF, + pub style: LineStyle, +} + +#[repr(u8)] +#[derive(Clone, Copy, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize, Eq, Hash, PeekPoke)] +pub enum LineOrientation { + Vertical, + Horizontal, +} + +#[repr(u8)] +#[derive(Clone, Copy, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize, Eq, Hash, PeekPoke)] +pub enum LineStyle { + Solid, + Dotted, + Dashed, + Wavy, +} + +#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)] +pub struct TextDisplayItem { + pub common: CommonItemProperties, + /// The area all the glyphs should be found in. Strictly speaking this isn't + /// necessarily needed, but layout engines should already "know" this, and we + /// use it cull and size things quickly before glyph layout is done. Currently + /// the glyphs *can* be outside these bounds, but that should imply they + /// can be cut off. + // FIXME: these are currently sometimes ignored to keep some old wrench tests + // working, but we should really just fix the tests! + pub bounds: LayoutRect, + pub font_key: font::FontInstanceKey, + pub color: ColorF, + pub glyph_options: Option<font::GlyphOptions>, +} // IMPLICIT: glyphs: Vec<font::GlyphInstance> + +#[derive(Clone, Copy, Debug, Default, Deserialize, MallocSizeOf, PartialEq, Serialize, PeekPoke)] +pub struct NormalBorder { + pub left: BorderSide, + pub right: BorderSide, + pub top: BorderSide, + pub bottom: BorderSide, + pub radius: BorderRadius, + /// Whether to apply anti-aliasing on the border corners. + /// + /// Note that for this to be `false` and work, this requires the borders to + /// be solid, and no border-radius. + pub do_aa: bool, +} + +impl NormalBorder { + fn can_disable_antialiasing(&self) -> bool { + fn is_valid(style: BorderStyle) -> bool { + style == BorderStyle::Solid || style == BorderStyle::None + } + + self.radius.is_zero() && + is_valid(self.top.style) && + is_valid(self.left.style) && + is_valid(self.bottom.style) && + is_valid(self.right.style) + } + + /// Normalizes a border so that we don't render disallowed stuff, like inset + /// borders that are less than two pixels wide. + #[inline] + pub fn normalize(&mut self, widths: &LayoutSideOffsets) { + debug_assert!( + self.do_aa || self.can_disable_antialiasing(), + "Unexpected disabled-antialiasing in a border, likely won't work or will be ignored" + ); + + #[inline] + fn renders_small_border_solid(style: BorderStyle) -> bool { + match style { + BorderStyle::Groove | + BorderStyle::Ridge => true, + _ => false, + } + } + + let normalize_side = |side: &mut BorderSide, width: f32| { + if renders_small_border_solid(side.style) && width < 2. { + side.style = BorderStyle::Solid; + } + }; + + normalize_side(&mut self.left, widths.left); + normalize_side(&mut self.right, widths.right); + normalize_side(&mut self.top, widths.top); + normalize_side(&mut self.bottom, widths.bottom); + } +} + +#[repr(u8)] +#[derive(Debug, Copy, Clone, MallocSizeOf, PartialEq, Serialize, Deserialize, Eq, Hash, PeekPoke)] +pub enum RepeatMode { + Stretch, + Repeat, + Round, + Space, +} + +#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize, PeekPoke)] +pub enum NinePatchBorderSource { + Image(ImageKey), + Gradient(Gradient), + RadialGradient(RadialGradient), + ConicGradient(ConicGradient), +} + +#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)] +pub struct NinePatchBorder { + /// Describes what to use as the 9-patch source image. If this is an image, + /// it will be stretched to fill the size given by width x height. + pub source: NinePatchBorderSource, + + /// The width of the 9-part image. + pub width: i32, + + /// The height of the 9-part image. + pub height: i32, + + /// Distances from each edge where the image should be sliced up. These + /// values are in 9-part-image space (the same space as width and height), + /// and the resulting image parts will be used to fill the corresponding + /// parts of the border as given by the border widths. This can lead to + /// stretching. + /// Slices can be overlapping. In that case, the same pixels from the + /// 9-part image will show up in multiple parts of the resulting border. + pub slice: DeviceIntSideOffsets, + + /// Controls whether the center of the 9 patch image is rendered or + /// ignored. The center is never rendered if the slices are overlapping. + pub fill: bool, + + /// Determines what happens if the horizontal side parts of the 9-part + /// image have a different size than the horizontal parts of the border. + pub repeat_horizontal: RepeatMode, + + /// Determines what happens if the vertical side parts of the 9-part + /// image have a different size than the vertical parts of the border. + pub repeat_vertical: RepeatMode, + + /// The outset for the border. + /// TODO(mrobinson): This should be removed and handled by the client. + pub outset: LayoutSideOffsets, // TODO: what unit is this in? +} + +#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize, PeekPoke)] +pub enum BorderDetails { + Normal(NormalBorder), + NinePatch(NinePatchBorder), +} + +#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)] +pub struct BorderDisplayItem { + pub common: CommonItemProperties, + pub bounds: LayoutRect, + pub widths: LayoutSideOffsets, + pub details: BorderDetails, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize, PeekPoke)] +pub enum BorderRadiusKind { + Uniform, + NonUniform, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize, PeekPoke)] +pub struct BorderRadius { + pub top_left: LayoutSize, + pub top_right: LayoutSize, + pub bottom_left: LayoutSize, + pub bottom_right: LayoutSize, +} + +impl Default for BorderRadius { + fn default() -> Self { + BorderRadius { + top_left: LayoutSize::zero(), + top_right: LayoutSize::zero(), + bottom_left: LayoutSize::zero(), + bottom_right: LayoutSize::zero(), + } + } +} + +#[repr(C)] +#[derive(Clone, Copy, Debug, Default, Deserialize, MallocSizeOf, PartialEq, Serialize, PeekPoke)] +pub struct BorderSide { + pub color: ColorF, + pub style: BorderStyle, +} + +#[repr(u32)] +#[derive(Clone, Copy, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize, Hash, Eq, PeekPoke)] +pub enum BorderStyle { + None = 0, + Solid = 1, + Double = 2, + Dotted = 3, + Dashed = 4, + Hidden = 5, + Groove = 6, + Ridge = 7, + Inset = 8, + Outset = 9, +} + +impl BorderStyle { + pub fn is_hidden(self) -> bool { + self == BorderStyle::Hidden || self == BorderStyle::None + } +} + +#[repr(u8)] +#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize, PeekPoke)] +pub enum BoxShadowClipMode { + Outset = 0, + Inset = 1, +} + +#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)] +pub struct BoxShadowDisplayItem { + pub common: CommonItemProperties, + pub box_bounds: LayoutRect, + pub offset: LayoutVector2D, + pub color: ColorF, + pub blur_radius: f32, + pub spread_radius: f32, + pub border_radius: BorderRadius, + pub clip_mode: BoxShadowClipMode, +} + +#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)] +pub struct PushShadowDisplayItem { + pub space_and_clip: SpaceAndClipInfo, + pub shadow: Shadow, + pub should_inflate: bool, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)] +pub struct Shadow { + pub offset: LayoutVector2D, + pub color: ColorF, + pub blur_radius: f32, +} + +#[repr(u8)] +#[derive(Debug, Copy, Clone, Hash, Eq, MallocSizeOf, PartialEq, Serialize, Deserialize, Ord, PartialOrd, PeekPoke)] +pub enum ExtendMode { + Clamp, + Repeat, +} + +#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)] +pub struct Gradient { + pub start_point: LayoutPoint, + pub end_point: LayoutPoint, + pub extend_mode: ExtendMode, +} // IMPLICIT: stops: Vec<GradientStop> + +/// The area +#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)] +pub struct GradientDisplayItem { + /// NOTE: common.clip_rect is the area the gradient covers + pub common: CommonItemProperties, + /// The area to tile the gradient over (first tile starts at origin of this rect) + // FIXME: this should ideally just be `tile_origin` here, with the clip_rect + // defining the bounds of the item. Needs non-trivial backend changes. + pub bounds: LayoutRect, + /// How big a tile of the of the gradient should be (common case: bounds.size) + pub tile_size: LayoutSize, + /// The space between tiles of the gradient (common case: 0) + pub tile_spacing: LayoutSize, + pub gradient: Gradient, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug, Default, Deserialize, MallocSizeOf, PartialEq, Serialize, PeekPoke)] +pub struct GradientStop { + pub offset: f32, + pub color: ColorF, +} + +#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)] +pub struct RadialGradient { + pub center: LayoutPoint, + pub radius: LayoutSize, + pub start_offset: f32, + pub end_offset: f32, + pub extend_mode: ExtendMode, +} // IMPLICIT stops: Vec<GradientStop> + +#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)] +pub struct ConicGradient { + pub center: LayoutPoint, + pub angle: f32, + pub start_offset: f32, + pub end_offset: f32, + pub extend_mode: ExtendMode, +} // IMPLICIT stops: Vec<GradientStop> + +/// Just an abstraction for bundling up a bunch of clips into a "super clip". +#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)] +pub struct ClipChainItem { + pub id: ClipChainId, + pub parent: Option<ClipChainId>, +} // IMPLICIT clip_ids: Vec<ClipId> + +#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)] +pub struct RadialGradientDisplayItem { + pub common: CommonItemProperties, + /// The area to tile the gradient over (first tile starts at origin of this rect) + // FIXME: this should ideally just be `tile_origin` here, with the clip_rect + // defining the bounds of the item. Needs non-trivial backend changes. + pub bounds: LayoutRect, + pub gradient: RadialGradient, + pub tile_size: LayoutSize, + pub tile_spacing: LayoutSize, +} + +#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)] +pub struct ConicGradientDisplayItem { + pub common: CommonItemProperties, + /// The area to tile the gradient over (first tile starts at origin of this rect) + // FIXME: this should ideally just be `tile_origin` here, with the clip_rect + // defining the bounds of the item. Needs non-trivial backend changes. + pub bounds: LayoutRect, + pub gradient: ConicGradient, + pub tile_size: LayoutSize, + pub tile_spacing: LayoutSize, +} + +/// Renders a filtered region of its backdrop +#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)] +pub struct BackdropFilterDisplayItem { + pub common: CommonItemProperties, +} +// IMPLICIT: filters: Vec<FilterOp>, filter_datas: Vec<FilterData>, filter_primitives: Vec<FilterPrimitive> + +#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)] +pub struct ReferenceFrameDisplayListItem { + pub origin: LayoutPoint, + pub parent_spatial_id: SpatialId, + pub reference_frame: ReferenceFrame, +} + +#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize, PeekPoke)] +pub enum ReferenceFrameKind { + /// Zoom reference frames must be a scale + translation only + Zoom, + /// A normal transform matrix, may contain perspective (the CSS transform property) + Transform, + /// A perspective transform, that optionally scrolls relative to a specific scroll node + Perspective { + scrolling_relative_to: Option<ExternalScrollId>, + } +} + +#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize, PeekPoke)] +pub enum Rotation { + Degree0, + Degree90, + Degree180, + Degree270, +} + +impl Rotation { + pub fn to_matrix( + &self, + size: LayoutSize, + ) -> LayoutTransform { + let (shift_center_to_origin, angle) = match self { + Rotation::Degree0 => { + (LayoutTransform::translation(-size.width / 2., -size.height / 2., 0.), Angle::degrees(0.)) + }, + Rotation::Degree90 => { + (LayoutTransform::translation(-size.height / 2., -size.width / 2., 0.), Angle::degrees(90.)) + }, + Rotation::Degree180 => { + (LayoutTransform::translation(-size.width / 2., -size.height / 2., 0.), Angle::degrees(180.)) + }, + Rotation::Degree270 => { + (LayoutTransform::translation(-size.height / 2., -size.width / 2., 0.), Angle::degrees(270.)) + }, + }; + let shift_origin_to_center = LayoutTransform::translation(size.width / 2., size.height / 2., 0.); + + shift_center_to_origin + .then(&LayoutTransform::rotation(0., 0., 1.0, angle)) + .then(&shift_origin_to_center) + } +} + +#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize, PeekPoke)] +pub enum ReferenceTransformBinding { + /// Standard reference frame which contains a precomputed transform. + Static { + binding: PropertyBinding<LayoutTransform>, + }, + /// Computed reference frame which dynamically calculates the transform + /// based on the given parameters. The reference is the content size of + /// the parent iframe, which is affected by snapping. + Computed { + scale_from: Option<LayoutSize>, + vertical_flip: bool, + rotation: Rotation, + }, +} + +impl Default for ReferenceTransformBinding { + fn default() -> Self { + ReferenceTransformBinding::Static { + binding: Default::default(), + } + } +} + +#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)] +pub struct ReferenceFrame { + pub kind: ReferenceFrameKind, + pub transform_style: TransformStyle, + /// The transform matrix, either the perspective matrix or the transform + /// matrix. + pub transform: ReferenceTransformBinding, + pub id: SpatialId, +} + +#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)] +pub struct PushStackingContextDisplayItem { + pub origin: LayoutPoint, + pub spatial_id: SpatialId, + pub prim_flags: PrimitiveFlags, + pub stacking_context: StackingContext, +} + +#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)] +pub struct StackingContext { + pub transform_style: TransformStyle, + pub mix_blend_mode: MixBlendMode, + pub clip_id: Option<ClipId>, + pub raster_space: RasterSpace, + pub flags: StackingContextFlags, +} +// IMPLICIT: filters: Vec<FilterOp>, filter_datas: Vec<FilterData>, filter_primitives: Vec<FilterPrimitive> + +#[repr(u8)] +#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize, PeekPoke)] +pub enum TransformStyle { + Flat = 0, + Preserve3D = 1, +} + +/// Configure whether the contents of a stacking context +/// should be rasterized in local space or screen space. +/// Local space rasterized pictures are typically used +/// when we want to cache the output, and performance is +/// important. Note that this is a performance hint only, +/// which WR may choose to ignore. +#[derive(Clone, Copy, Debug, Deserialize, PartialEq, MallocSizeOf, Serialize, PeekPoke)] +#[repr(u8)] +pub enum RasterSpace { + // Rasterize in local-space, applying supplied scale to primitives. + // Best performance, but lower quality. + Local(f32), + + // Rasterize the picture in screen-space, including rotation / skew etc in + // the rasterized element. Best quality, but slower performance. Note that + // any stacking context with a perspective transform will be rasterized + // in local-space, even if this is set. + Screen, +} + +impl RasterSpace { + pub fn local_scale(self) -> Option<f32> { + match self { + RasterSpace::Local(scale) => Some(scale), + RasterSpace::Screen => None, + } + } +} + +impl Eq for RasterSpace {} + +impl Hash for RasterSpace { + fn hash<H: Hasher>(&self, state: &mut H) { + match self { + RasterSpace::Screen => { + 0.hash(state); + } + RasterSpace::Local(scale) => { + // Note: this is inconsistent with the Eq impl for -0.0 (don't care). + 1.hash(state); + scale.to_bits().hash(state); + } + } + } +} + +bitflags! { + #[repr(C)] + #[derive(Deserialize, MallocSizeOf, Serialize, PeekPoke)] + pub struct StackingContextFlags: u8 { + /// If true, this stacking context represents a backdrop root, per the CSS + /// filter-effects specification (see https://drafts.fxtf.org/filter-effects-2/#BackdropRoot). + const IS_BACKDROP_ROOT = 1 << 0; + /// If true, this stacking context is a blend container than contains + /// mix-blend-mode children (and should thus be isolated). + const IS_BLEND_CONTAINER = 1 << 1; + } +} + +impl Default for StackingContextFlags { + fn default() -> Self { + StackingContextFlags::empty() + } +} + +#[repr(u8)] +#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize, PeekPoke)] +pub enum MixBlendMode { + Normal = 0, + Multiply = 1, + Screen = 2, + Overlay = 3, + Darken = 4, + Lighten = 5, + ColorDodge = 6, + ColorBurn = 7, + HardLight = 8, + SoftLight = 9, + Difference = 10, + Exclusion = 11, + Hue = 12, + Saturation = 13, + Color = 14, + Luminosity = 15, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize, PeekPoke)] +pub enum ColorSpace { + Srgb, + LinearRgb, +} + +/// Available composite operoations for the composite filter primitive +#[repr(C)] +#[derive(Clone, Copy, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize, PeekPoke)] +pub enum CompositeOperator { + Over, + In, + Atop, + Out, + Xor, + Lighter, + Arithmetic([f32; 4]), +} + +impl CompositeOperator { + // This must stay in sync with the composite operator defines in cs_svg_filter.glsl + pub fn as_int(&self) -> u32 { + match self { + CompositeOperator::Over => 0, + CompositeOperator::In => 1, + CompositeOperator::Out => 2, + CompositeOperator::Atop => 3, + CompositeOperator::Xor => 4, + CompositeOperator::Lighter => 5, + CompositeOperator::Arithmetic(..) => 6, + } + } +} + +/// An input to a SVG filter primitive. +#[repr(C)] +#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize, PeekPoke)] +pub enum FilterPrimitiveInput { + /// The input is the original graphic that the filter is being applied to. + Original, + /// The input is the output of the previous filter primitive in the filter primitive chain. + Previous, + /// The input is the output of the filter primitive at the given index in the filter primitive chain. + OutputOfPrimitiveIndex(usize), +} + +impl FilterPrimitiveInput { + /// Gets the index of the input. + /// Returns `None` if the source graphic is the input. + pub fn to_index(self, cur_index: usize) -> Option<usize> { + match self { + FilterPrimitiveInput::Previous if cur_index > 0 => Some(cur_index - 1), + FilterPrimitiveInput::OutputOfPrimitiveIndex(index) => Some(index), + _ => None, + } + } +} + +#[repr(C)] +#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)] +pub struct BlendPrimitive { + pub input1: FilterPrimitiveInput, + pub input2: FilterPrimitiveInput, + pub mode: MixBlendMode, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)] +pub struct FloodPrimitive { + pub color: ColorF, +} + +impl FloodPrimitive { + pub fn sanitize(&mut self) { + self.color.r = self.color.r.min(1.0).max(0.0); + self.color.g = self.color.g.min(1.0).max(0.0); + self.color.b = self.color.b.min(1.0).max(0.0); + self.color.a = self.color.a.min(1.0).max(0.0); + } +} + +#[repr(C)] +#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)] +pub struct BlurPrimitive { + pub input: FilterPrimitiveInput, + pub width: f32, + pub height: f32, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)] +pub struct OpacityPrimitive { + pub input: FilterPrimitiveInput, + pub opacity: f32, +} + +impl OpacityPrimitive { + pub fn sanitize(&mut self) { + self.opacity = self.opacity.min(1.0).max(0.0); + } +} + +/// cbindgen:derive-eq=false +#[repr(C)] +#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)] +pub struct ColorMatrixPrimitive { + pub input: FilterPrimitiveInput, + pub matrix: [f32; 20], +} + +#[repr(C)] +#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)] +pub struct DropShadowPrimitive { + pub input: FilterPrimitiveInput, + pub shadow: Shadow, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)] +pub struct ComponentTransferPrimitive { + pub input: FilterPrimitiveInput, + // Component transfer data is stored in FilterData. +} + +#[repr(C)] +#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)] +pub struct IdentityPrimitive { + pub input: FilterPrimitiveInput, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)] +pub struct OffsetPrimitive { + pub input: FilterPrimitiveInput, + pub offset: LayoutVector2D, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)] +pub struct CompositePrimitive { + pub input1: FilterPrimitiveInput, + pub input2: FilterPrimitiveInput, + pub operator: CompositeOperator, +} + +/// See: https://github.com/eqrion/cbindgen/issues/9 +/// cbindgen:derive-eq=false +#[repr(C)] +#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize, PeekPoke)] +pub enum FilterPrimitiveKind { + Identity(IdentityPrimitive), + Blend(BlendPrimitive), + Flood(FloodPrimitive), + Blur(BlurPrimitive), + // TODO: Support animated opacity? + Opacity(OpacityPrimitive), + /// cbindgen:derive-eq=false + ColorMatrix(ColorMatrixPrimitive), + DropShadow(DropShadowPrimitive), + ComponentTransfer(ComponentTransferPrimitive), + Offset(OffsetPrimitive), + Composite(CompositePrimitive), +} + +impl Default for FilterPrimitiveKind { + fn default() -> Self { + FilterPrimitiveKind::Identity(IdentityPrimitive::default()) + } +} + +impl FilterPrimitiveKind { + pub fn sanitize(&mut self) { + match self { + FilterPrimitiveKind::Flood(flood) => flood.sanitize(), + FilterPrimitiveKind::Opacity(opacity) => opacity.sanitize(), + + // No sanitization needed. + FilterPrimitiveKind::Identity(..) | + FilterPrimitiveKind::Blend(..) | + FilterPrimitiveKind::ColorMatrix(..) | + FilterPrimitiveKind::Offset(..) | + FilterPrimitiveKind::Composite(..) | + FilterPrimitiveKind::Blur(..) | + FilterPrimitiveKind::DropShadow(..) | + // Component transfer's filter data is sanitized separately. + FilterPrimitiveKind::ComponentTransfer(..) => {} + } + } +} + +/// SVG Filter Primitive. +/// See: https://github.com/eqrion/cbindgen/issues/9 +/// cbindgen:derive-eq=false +#[repr(C)] +#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)] +pub struct FilterPrimitive { + pub kind: FilterPrimitiveKind, + pub color_space: ColorSpace, +} + +impl FilterPrimitive { + pub fn sanitize(&mut self) { + self.kind.sanitize(); + } +} + +/// CSS filter. +#[repr(C)] +#[derive(Clone, Copy, Debug, PartialEq, Deserialize, Serialize, PeekPoke)] +pub enum FilterOp { + /// Filter that does no transformation of the colors, needed for + /// debug purposes only. + Identity, + Blur(f32, f32), + Brightness(f32), + Contrast(f32), + Grayscale(f32), + HueRotate(f32), + Invert(f32), + Opacity(PropertyBinding<f32>, f32), + Saturate(f32), + Sepia(f32), + DropShadow(Shadow), + ColorMatrix([f32; 20]), + SrgbToLinear, + LinearToSrgb, + ComponentTransfer, + Flood(ColorF), +} + +#[repr(u8)] +#[derive(Clone, Copy, Debug, PartialEq, Deserialize, Serialize, PeekPoke)] +pub enum ComponentTransferFuncType { + Identity = 0, + Table = 1, + Discrete = 2, + Linear = 3, + Gamma = 4, +} + +#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] +pub struct FilterData { + pub func_r_type: ComponentTransferFuncType, + pub r_values: Vec<f32>, + pub func_g_type: ComponentTransferFuncType, + pub g_values: Vec<f32>, + pub func_b_type: ComponentTransferFuncType, + pub b_values: Vec<f32>, + pub func_a_type: ComponentTransferFuncType, + pub a_values: Vec<f32>, +} + +fn sanitize_func_type( + func_type: ComponentTransferFuncType, + values: &[f32], +) -> ComponentTransferFuncType { + if values.is_empty() { + return ComponentTransferFuncType::Identity; + } + if values.len() < 2 && func_type == ComponentTransferFuncType::Linear { + return ComponentTransferFuncType::Identity; + } + if values.len() < 3 && func_type == ComponentTransferFuncType::Gamma { + return ComponentTransferFuncType::Identity; + } + func_type +} + +fn sanitize_values( + func_type: ComponentTransferFuncType, + values: &[f32], +) -> bool { + if values.len() < 2 && func_type == ComponentTransferFuncType::Linear { + return false; + } + if values.len() < 3 && func_type == ComponentTransferFuncType::Gamma { + return false; + } + true +} + +impl FilterData { + /// Ensure that the number of values matches up with the function type. + pub fn sanitize(&self) -> FilterData { + FilterData { + func_r_type: sanitize_func_type(self.func_r_type, &self.r_values), + r_values: + if sanitize_values(self.func_r_type, &self.r_values) { + self.r_values.clone() + } else { + Vec::new() + }, + func_g_type: sanitize_func_type(self.func_g_type, &self.g_values), + g_values: + if sanitize_values(self.func_g_type, &self.g_values) { + self.g_values.clone() + } else { + Vec::new() + }, + + func_b_type: sanitize_func_type(self.func_b_type, &self.b_values), + b_values: + if sanitize_values(self.func_b_type, &self.b_values) { + self.b_values.clone() + } else { + Vec::new() + }, + + func_a_type: sanitize_func_type(self.func_a_type, &self.a_values), + a_values: + if sanitize_values(self.func_a_type, &self.a_values) { + self.a_values.clone() + } else { + Vec::new() + }, + + } + } + + pub fn is_identity(&self) -> bool { + self.func_r_type == ComponentTransferFuncType::Identity && + self.func_g_type == ComponentTransferFuncType::Identity && + self.func_b_type == ComponentTransferFuncType::Identity && + self.func_a_type == ComponentTransferFuncType::Identity + } +} + +#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)] +pub struct IframeDisplayItem { + pub bounds: LayoutRect, + pub clip_rect: LayoutRect, + pub space_and_clip: SpaceAndClipInfo, + pub pipeline_id: PipelineId, + pub ignore_missing_pipeline: bool, +} + +/// This describes an image that fills the specified area. It stretches or shrinks +/// the image as necessary. While RepeatingImageDisplayItem could otherwise provide +/// a superset of the functionality, it has been problematic inferring the desired +/// repetition properties when snapping changes the size of the primitive. +#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)] +pub struct ImageDisplayItem { + pub common: CommonItemProperties, + /// The area to tile the image over (first tile starts at origin of this rect) + // FIXME: this should ideally just be `tile_origin` here, with the clip_rect + // defining the bounds of the item. Needs non-trivial backend changes. + pub bounds: LayoutRect, + pub image_key: ImageKey, + pub image_rendering: ImageRendering, + pub alpha_type: AlphaType, + /// A hack used by gecko to color a simple bitmap font used for tofu glyphs + pub color: ColorF, +} + +/// This describes a background-image and its tiling. It repeats in a grid to fill +/// the specified area. +#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)] +pub struct RepeatingImageDisplayItem { + pub common: CommonItemProperties, + /// The area to tile the image over (first tile starts at origin of this rect) + // FIXME: this should ideally just be `tile_origin` here, with the clip_rect + // defining the bounds of the item. Needs non-trivial backend changes. + pub bounds: LayoutRect, + /// How large to make a single tile of the image (common case: bounds.size) + pub stretch_size: LayoutSize, + /// The space between tiles (common case: 0) + pub tile_spacing: LayoutSize, + pub image_key: ImageKey, + pub image_rendering: ImageRendering, + pub alpha_type: AlphaType, + /// A hack used by gecko to color a simple bitmap font used for tofu glyphs + pub color: ColorF, +} + +#[repr(u8)] +#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize, PeekPoke)] +pub enum ImageRendering { + Auto = 0, + CrispEdges = 1, + Pixelated = 2, +} + +#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize, PeekPoke)] +pub enum AlphaType { + Alpha = 0, + PremultipliedAlpha = 1, +} + +#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)] +pub struct YuvImageDisplayItem { + pub common: CommonItemProperties, + pub bounds: LayoutRect, + pub yuv_data: YuvData, + pub color_depth: ColorDepth, + pub color_space: YuvColorSpace, + pub color_range: ColorRange, + pub image_rendering: ImageRendering, +} + +#[repr(u8)] +#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize, PeekPoke)] +pub enum YuvColorSpace { + Rec601 = 0, + Rec709 = 1, + Rec2020 = 2, + Identity = 3, // aka RGB as per ISO/IEC 23091-2:2019 +} + +#[repr(u8)] +#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize, PeekPoke)] +pub enum ColorRange { + Limited = 0, + Full = 1, +} + +#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize, PeekPoke)] +pub enum YuvData { + NV12(ImageKey, ImageKey), // (Y channel, CbCr interleaved channel) + PlanarYCbCr(ImageKey, ImageKey, ImageKey), // (Y channel, Cb channel, Cr Channel) + InterleavedYCbCr(ImageKey), // (YCbCr interleaved channel) +} + +impl YuvData { + pub fn get_format(&self) -> YuvFormat { + match *self { + YuvData::NV12(..) => YuvFormat::NV12, + YuvData::PlanarYCbCr(..) => YuvFormat::PlanarYCbCr, + YuvData::InterleavedYCbCr(..) => YuvFormat::InterleavedYCbCr, + } + } +} + +#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize, PeekPoke)] +pub enum YuvFormat { + NV12 = 0, + PlanarYCbCr = 1, + InterleavedYCbCr = 2, +} + +impl YuvFormat { + pub fn get_plane_num(self) -> usize { + match self { + YuvFormat::NV12 => 2, + YuvFormat::PlanarYCbCr => 3, + YuvFormat::InterleavedYCbCr => 1, + } + } +} + +#[repr(C)] +#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)] +pub struct ImageMask { + pub image: ImageKey, + pub rect: LayoutRect, + pub repeat: bool, +} + +impl ImageMask { + /// Get a local clipping rect contributed by this mask. + pub fn get_local_clip_rect(&self) -> Option<LayoutRect> { + if self.repeat { + None + } else { + Some(self.rect) + } + } +} + +#[repr(C)] +#[derive(Copy, Clone, Debug, MallocSizeOf, PartialEq, Serialize, Deserialize, Eq, Hash, PeekPoke)] +pub enum ClipMode { + Clip, // Pixels inside the region are visible. + ClipOut, // Pixels outside the region are visible. +} + +impl Not for ClipMode { + type Output = ClipMode; + + fn not(self) -> ClipMode { + match self { + ClipMode::Clip => ClipMode::ClipOut, + ClipMode::ClipOut => ClipMode::Clip, + } + } +} + +#[repr(C)] +#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)] +pub struct ComplexClipRegion { + /// The boundaries of the rectangle. + pub rect: LayoutRect, + /// Border radii of this rectangle. + pub radii: BorderRadius, + /// Whether we are clipping inside or outside + /// the region. + pub mode: ClipMode, +} + +impl BorderRadius { + pub fn zero() -> BorderRadius { + BorderRadius { + top_left: LayoutSize::new(0.0, 0.0), + top_right: LayoutSize::new(0.0, 0.0), + bottom_left: LayoutSize::new(0.0, 0.0), + bottom_right: LayoutSize::new(0.0, 0.0), + } + } + + pub fn uniform(radius: f32) -> BorderRadius { + BorderRadius { + top_left: LayoutSize::new(radius, radius), + top_right: LayoutSize::new(radius, radius), + bottom_left: LayoutSize::new(radius, radius), + bottom_right: LayoutSize::new(radius, radius), + } + } + + pub fn uniform_size(radius: LayoutSize) -> BorderRadius { + BorderRadius { + top_left: radius, + top_right: radius, + bottom_left: radius, + bottom_right: radius, + } + } + + pub fn is_uniform(&self) -> Option<f32> { + match self.is_uniform_size() { + Some(radius) if radius.width == radius.height => Some(radius.width), + _ => None, + } + } + + pub fn is_uniform_size(&self) -> Option<LayoutSize> { + let uniform_radius = self.top_left; + if self.top_right == uniform_radius && self.bottom_left == uniform_radius && + self.bottom_right == uniform_radius + { + Some(uniform_radius) + } else { + None + } + } + + /// Return whether, in each corner, the radius in *either* direction is zero. + /// This means that none of the corners are rounded. + pub fn is_zero(&self) -> bool { + let corner_is_zero = |corner: &LayoutSize| corner.width == 0.0 || corner.height == 0.0; + corner_is_zero(&self.top_left) && + corner_is_zero(&self.top_right) && + corner_is_zero(&self.bottom_right) && + corner_is_zero(&self.bottom_left) + } +} + +impl ComplexClipRegion { + /// Create a new complex clip region. + pub fn new( + rect: LayoutRect, + radii: BorderRadius, + mode: ClipMode, + ) -> Self { + ComplexClipRegion { rect, radii, mode } + } +} + +impl ComplexClipRegion { + /// Get a local clipping rect contributed by this clip region. + pub fn get_local_clip_rect(&self) -> Option<LayoutRect> { + match self.mode { + ClipMode::Clip => { + Some(self.rect) + } + ClipMode::ClipOut => { + None + } + } + } +} + +#[derive(Clone, Copy, Debug, Default, Deserialize, Eq, Hash, PartialEq, Serialize, PeekPoke)] +pub struct ClipChainId(pub u64, pub PipelineId); + +/// A reference to a clipping node defining how an item is clipped. +#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize, PeekPoke)] +pub enum ClipId { + Clip(usize, PipelineId), + ClipChain(ClipChainId), +} + +const ROOT_CLIP_ID: usize = 0; + +impl ClipId { + /// Return the root clip ID - effectively doing no clipping. + pub fn root(pipeline_id: PipelineId) -> Self { + ClipId::Clip(ROOT_CLIP_ID, pipeline_id) + } + + /// Return an invalid clip ID - needed in places where we carry + /// one but need to not attempt to use it. + pub fn invalid() -> Self { + ClipId::Clip(!0, PipelineId::dummy()) + } + + pub fn pipeline_id(&self) -> PipelineId { + match *self { + ClipId::Clip(_, pipeline_id) | + ClipId::ClipChain(ClipChainId(_, pipeline_id)) => pipeline_id, + } + } + + pub fn is_root(&self) -> bool { + match *self { + ClipId::Clip(id, _) => id == ROOT_CLIP_ID, + ClipId::ClipChain(_) => false, + } + } + + pub fn is_valid(&self) -> bool { + match *self { + ClipId::Clip(id, _) => id != !0, + _ => true, + } + } +} + +/// A reference to a spatial node defining item positioning. +#[derive(Clone, Copy, Debug, Default, Deserialize, Eq, Hash, PartialEq, Serialize, PeekPoke)] +pub struct SpatialId(pub usize, PipelineId); + +const ROOT_REFERENCE_FRAME_SPATIAL_ID: usize = 0; +const ROOT_SCROLL_NODE_SPATIAL_ID: usize = 1; + +impl SpatialId { + pub fn new(spatial_node_index: usize, pipeline_id: PipelineId) -> Self { + SpatialId(spatial_node_index, pipeline_id) + } + + pub fn root_reference_frame(pipeline_id: PipelineId) -> Self { + SpatialId(ROOT_REFERENCE_FRAME_SPATIAL_ID, pipeline_id) + } + + pub fn root_scroll_node(pipeline_id: PipelineId) -> Self { + SpatialId(ROOT_SCROLL_NODE_SPATIAL_ID, pipeline_id) + } + + pub fn pipeline_id(&self) -> PipelineId { + self.1 + } + + pub fn is_root_reference_frame(&self) -> bool { + self.0 == ROOT_REFERENCE_FRAME_SPATIAL_ID + } + + pub fn is_root_scroll_node(&self) -> bool { + self.0 == ROOT_SCROLL_NODE_SPATIAL_ID + } +} + +/// An external identifier that uniquely identifies a scroll frame independent of its ClipId, which +/// may change from frame to frame. This should be unique within a pipeline. WebRender makes no +/// attempt to ensure uniqueness. The zero value is reserved for use by the root scroll node of +/// every pipeline, which always has an external id. +/// +/// When setting display lists with the `preserve_frame_state` this id is used to preserve scroll +/// offsets between different sets of SpatialNodes which are ScrollFrames. +#[derive(Clone, Copy, Debug, Default, Deserialize, Eq, Hash, PartialEq, Serialize, PeekPoke)] +#[repr(C)] +pub struct ExternalScrollId(pub u64, pub PipelineId); + +impl ExternalScrollId { + pub fn pipeline_id(&self) -> PipelineId { + self.1 + } + + pub fn is_root(&self) -> bool { + self.0 == 0 + } +} + +impl DisplayItem { + pub fn debug_name(&self) -> &'static str { + match *self { + DisplayItem::Border(..) => "border", + DisplayItem::BoxShadow(..) => "box_shadow", + DisplayItem::ClearRectangle(..) => "clear_rectangle", + DisplayItem::HitTest(..) => "hit_test", + DisplayItem::RectClip(..) => "rect_clip", + DisplayItem::RoundedRectClip(..) => "rounded_rect_clip", + DisplayItem::ImageMaskClip(..) => "image_mask_clip", + DisplayItem::Clip(..) => "clip", + DisplayItem::ClipChain(..) => "clip_chain", + DisplayItem::ConicGradient(..) => "conic_gradient", + DisplayItem::Gradient(..) => "gradient", + DisplayItem::Iframe(..) => "iframe", + DisplayItem::Image(..) => "image", + DisplayItem::RepeatingImage(..) => "repeating_image", + DisplayItem::Line(..) => "line", + DisplayItem::PopAllShadows => "pop_all_shadows", + DisplayItem::PopReferenceFrame => "pop_reference_frame", + DisplayItem::PopStackingContext => "pop_stacking_context", + DisplayItem::PushShadow(..) => "push_shadow", + DisplayItem::PushReferenceFrame(..) => "push_reference_frame", + DisplayItem::PushStackingContext(..) => "push_stacking_context", + DisplayItem::SetFilterOps => "set_filter_ops", + DisplayItem::SetFilterData => "set_filter_data", + DisplayItem::SetFilterPrimitives => "set_filter_primitives", + DisplayItem::RadialGradient(..) => "radial_gradient", + DisplayItem::Rectangle(..) => "rectangle", + DisplayItem::ScrollFrame(..) => "scroll_frame", + DisplayItem::SetGradientStops => "set_gradient_stops", + DisplayItem::ReuseItems(..) => "reuse_item", + DisplayItem::RetainedItems(..) => "retained_items", + DisplayItem::StickyFrame(..) => "sticky_frame", + DisplayItem::Text(..) => "text", + DisplayItem::YuvImage(..) => "yuv_image", + DisplayItem::BackdropFilter(..) => "backdrop_filter", + } + } +} + +macro_rules! impl_default_for_enums { + ($($enum:ident => $init:expr ),+) => { + $(impl Default for $enum { + #[allow(unused_imports)] + fn default() -> Self { + use $enum::*; + $init + } + })* + } +} + +impl_default_for_enums! { + DisplayItem => PopStackingContext, + ScrollSensitivity => ScriptAndInputEvents, + LineOrientation => Vertical, + LineStyle => Solid, + RepeatMode => Stretch, + NinePatchBorderSource => Image(ImageKey::default()), + BorderDetails => Normal(NormalBorder::default()), + BorderRadiusKind => Uniform, + BorderStyle => None, + BoxShadowClipMode => Outset, + ExtendMode => Clamp, + FilterOp => Identity, + ComponentTransferFuncType => Identity, + ClipMode => Clip, + ClipId => ClipId::invalid(), + ReferenceFrameKind => Transform, + Rotation => Degree0, + TransformStyle => Flat, + RasterSpace => Local(f32::default()), + MixBlendMode => Normal, + ImageRendering => Auto, + AlphaType => Alpha, + YuvColorSpace => Rec601, + ColorRange => Limited, + YuvData => NV12(ImageKey::default(), ImageKey::default()), + YuvFormat => NV12, + FilterPrimitiveInput => Original, + ColorSpace => Srgb, + CompositeOperator => Over +} |