tor-browser

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

commit fc6f1cb437ff6188b95f248cac91278db55cd0f4
parent e82e1453748b162494795ee283ac56da853cb5a6
Author: Nicolas Silva <nical@fastmail.com>
Date:   Fri, 12 Dec 2025 14:44:32 +0000

Bug 1998913 - Part 4 - Move SVG filter code into its own module. r=gfx-reviewers,gw

Differential Revision: https://phabricator.services.mozilla.com/D275951

Diffstat:
Mgfx/wr/webrender/src/internal_types.rs | 553+------------------------------------------------------------------------------
Mgfx/wr/webrender/src/lib.rs | 1+
Mgfx/wr/webrender/src/picture.rs | 297++-----------------------------------------------------------------------------
Mgfx/wr/webrender/src/prim_store/picture.rs | 4++--
Mgfx/wr/webrender/src/render_target.rs | 3++-
Mgfx/wr/webrender/src/render_task.rs | 3++-
Mgfx/wr/webrender/src/scene_building.rs | 3++-
Mgfx/wr/webrender/src/surface.rs | 3++-
Agfx/wr/webrender/src/svg_filter.rs | 839+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
9 files changed, 858 insertions(+), 848 deletions(-)

diff --git a/gfx/wr/webrender/src/internal_types.rs b/gfx/wr/webrender/src/internal_types.rs @@ -3,7 +3,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use api::{ColorF, DocumentId, ExternalImageId, PrimitiveFlags, Parameter, RenderReasons}; -use api::{ImageFormat, NotificationRequest, Shadow, FilterOpGraphPictureBufferId, FilterOpGraphPictureReference, FilterOpGraphNode, FilterOp, ImageBufferKind}; +use api::{ImageFormat, NotificationRequest, Shadow, FilterOp, ImageBufferKind}; use api::{FramePublishId, TextureCacheCategory}; use api::units::*; use crate::render_api::DebugCommand; @@ -15,7 +15,7 @@ use crate::frame_builder::Frame; use crate::profiler::TransactionProfile; use crate::spatial_tree::SpatialNodeIndex; use crate::prim_store::PrimitiveInstanceIndex; -use crate::filterdata::FilterDataHandle; +use crate::svg_filter::{FilterGraphNode, FilterGraphOp, FilterGraphPictureReference}; use rustc_hash::FxHasher; use plane_split::BspSplitter; use smallvec::SmallVec; @@ -215,555 +215,6 @@ pub struct PlaneSplitterIndex(pub usize); /// An arbitrary number which we assume opacity is invisible below. const OPACITY_EPSILON: f32 = 0.001; -#[derive(Clone, Copy, Debug)] -#[cfg_attr(feature = "capture", derive(Serialize))] -#[cfg_attr(feature = "replay", derive(Deserialize))] -pub struct FilterGraphPictureReference { - /// Id of the picture in question in a namespace unique to this filter DAG, - /// some are special values like - /// FilterPrimitiveDescription::kPrimitiveIndexSourceGraphic. - pub buffer_id: FilterOpGraphPictureBufferId, - /// Set by wrap_prim_with_filters to the subregion of the input node, may - /// also have been offset for feDropShadow or feOffset - pub subregion: LayoutRect, - /// During scene build this is the offset to apply to the input subregion - /// for feOffset, which can be optimized away by pushing its offset and - /// subregion crop to downstream nodes. This is always zero in render tasks - /// where it has already been applied to subregion by that point. Not used - /// in get_coverage_svgfe because source_padding/target_padding represent - /// the offset there. - pub offset: LayoutVector2D, - /// Equal to the inflate value of the referenced buffer, or 0 - pub inflate: i16, - /// Padding on each side to represent how this input is read relative to the - /// node's output subregion, this represents what the operation needs to - /// read from ths input, which may be blurred or offset. - pub source_padding: LayoutRect, - /// Padding on each side to represent how this input affects the node's - /// subregion, this can be used to calculate target subregion based on - /// SourceGraphic subregion. This is usually equal to source_padding except - /// offset in the opposite direction, inflates typically do the same thing - /// to both types of padding. - pub target_padding: LayoutRect, -} - -impl From<FilterOpGraphPictureReference> for FilterGraphPictureReference { - fn from(pic: FilterOpGraphPictureReference) -> Self { - FilterGraphPictureReference{ - buffer_id: pic.buffer_id, - // All of these are set by wrap_prim_with_filters - subregion: LayoutRect::zero(), - offset: LayoutVector2D::zero(), - inflate: 0, - source_padding: LayoutRect::zero(), - target_padding: LayoutRect::zero(), - } - } -} - -pub const SVGFE_CONVOLVE_DIAMETER_LIMIT: usize = 5; -pub const SVGFE_CONVOLVE_VALUES_LIMIT: usize = SVGFE_CONVOLVE_DIAMETER_LIMIT * - SVGFE_CONVOLVE_DIAMETER_LIMIT; - -#[derive(Clone, Debug)] -#[cfg_attr(feature = "capture", derive(Serialize))] -#[cfg_attr(feature = "replay", derive(Deserialize))] -pub enum FilterGraphOp { - /// Filter that copies the SourceGraphic image into the specified subregion, - /// This is intentionally the only way to get SourceGraphic into the graph, - /// as the filter region must be applied before it is used. - /// parameters: FilterOpGraphNode - /// SVG filter semantics - no inputs, no linear - SVGFESourceGraphic, - /// Filter that copies the SourceAlpha image into the specified subregion, - /// This is intentionally the only way to get SourceAlpha into the graph, - /// as the filter region must be applied before it is used. - /// parameters: FilterOpGraphNode - /// SVG filter semantics - no inputs, no linear - SVGFESourceAlpha, - /// Filter that does no transformation of the colors, used to implement a - /// few things like SVGFEOffset, and this is the default value in - /// impl_default_for_enums. - /// parameters: FilterGraphNode - /// SVG filter semantics - selectable input with offset - SVGFEIdentity, - /// represents CSS opacity property as a graph node like the rest of the - /// SVGFE* filters - /// parameters: FilterGraphNode - /// SVG filter semantics - selectable input(s), selectable between linear - /// (default) and sRGB color space for calculations - SVGFEOpacity{valuebinding: api::PropertyBinding<f32>, value: f32}, - /// convert a color image to an alpha channel - internal use; generated by - /// SVGFilterInstance::GetOrCreateSourceAlphaIndex(). - SVGFEToAlpha, - /// combine 2 images with SVG_FEBLEND_MODE_DARKEN - /// parameters: FilterGraphNode - /// SVG filter semantics - selectable input(s), selectable between linear - /// (default) and sRGB color space for calculations - /// Spec: https://www.w3.org/TR/filter-effects-1/#feBlendElement - SVGFEBlendDarken, - /// combine 2 images with SVG_FEBLEND_MODE_LIGHTEN - /// parameters: FilterGraphNode - /// SVG filter semantics - selectable input(s), selectable between linear - /// (default) and sRGB color space for calculations - /// Spec: https://www.w3.org/TR/filter-effects-1/#feBlendElement - SVGFEBlendLighten, - /// combine 2 images with SVG_FEBLEND_MODE_MULTIPLY - /// parameters: FilterGraphNode - /// SVG filter semantics - selectable input(s), selectable between linear - /// (default) and sRGB color space for calculations - /// Spec: https://www.w3.org/TR/filter-effects-1/#feBlendElement - SVGFEBlendMultiply, - /// combine 2 images with SVG_FEBLEND_MODE_NORMAL - /// parameters: FilterGraphNode - /// SVG filter semantics - selectable input(s), selectable between linear - /// (default) and sRGB color space for calculations - /// Spec: https://www.w3.org/TR/filter-effects-1/#feBlendElement - SVGFEBlendNormal, - /// combine 2 images with SVG_FEBLEND_MODE_SCREEN - /// parameters: FilterGraphNode - /// SVG filter semantics - selectable input(s), selectable between linear - /// (default) and sRGB color space for calculations - /// Spec: https://www.w3.org/TR/filter-effects-1/#feBlendElement - SVGFEBlendScreen, - /// combine 2 images with SVG_FEBLEND_MODE_OVERLAY - /// parameters: FilterOpGraphNode - /// SVG filter semantics - selectable input(s), selectable between linear - /// (default) and sRGB color space for calculations - /// Source: https://developer.mozilla.org/en-US/docs/Web/CSS/mix-blend-mode - SVGFEBlendOverlay, - /// combine 2 images with SVG_FEBLEND_MODE_COLOR_DODGE - /// parameters: FilterOpGraphNode - /// SVG filter semantics - selectable input(s), selectable between linear - /// (default) and sRGB color space for calculations - /// Source: https://developer.mozilla.org/en-US/docs/Web/CSS/mix-blend-mode - SVGFEBlendColorDodge, - /// combine 2 images with SVG_FEBLEND_MODE_COLOR_BURN - /// parameters: FilterOpGraphNode - /// SVG filter semantics - selectable input(s), selectable between linear - /// (default) and sRGB color space for calculations - /// Source: https://developer.mozilla.org/en-US/docs/Web/CSS/mix-blend-mode - SVGFEBlendColorBurn, - /// combine 2 images with SVG_FEBLEND_MODE_HARD_LIGHT - /// parameters: FilterOpGraphNode - /// SVG filter semantics - selectable input(s), selectable between linear - /// (default) and sRGB color space for calculations - /// Source: https://developer.mozilla.org/en-US/docs/Web/CSS/mix-blend-mode - SVGFEBlendHardLight, - /// combine 2 images with SVG_FEBLEND_MODE_SOFT_LIGHT - /// parameters: FilterOpGraphNode - /// SVG filter semantics - selectable input(s), selectable between linear - /// (default) and sRGB color space for calculations - /// Source: https://developer.mozilla.org/en-US/docs/Web/CSS/mix-blend-mode - SVGFEBlendSoftLight, - /// combine 2 images with SVG_FEBLEND_MODE_DIFFERENCE - /// parameters: FilterOpGraphNode - /// SVG filter semantics - selectable input(s), selectable between linear - /// (default) and sRGB color space for calculations - /// Source: https://developer.mozilla.org/en-US/docs/Web/CSS/mix-blend-mode - SVGFEBlendDifference, - /// combine 2 images with SVG_FEBLEND_MODE_EXCLUSION - /// parameters: FilterOpGraphNode - /// SVG filter semantics - selectable input(s), selectable between linear - /// (default) and sRGB color space for calculations - /// Source: https://developer.mozilla.org/en-US/docs/Web/CSS/mix-blend-mode - SVGFEBlendExclusion, - /// combine 2 images with SVG_FEBLEND_MODE_HUE - /// parameters: FilterOpGraphNode - /// SVG filter semantics - selectable input(s), selectable between linear - /// (default) and sRGB color space for calculations - /// Source: https://developer.mozilla.org/en-US/docs/Web/CSS/mix-blend-mode - SVGFEBlendHue, - /// combine 2 images with SVG_FEBLEND_MODE_SATURATION - /// parameters: FilterOpGraphNode - /// SVG filter semantics - selectable input(s), selectable between linear - /// (default) and sRGB color space for calculations - /// Source: https://developer.mozilla.org/en-US/docs/Web/CSS/mix-blend-mode - SVGFEBlendSaturation, - /// combine 2 images with SVG_FEBLEND_MODE_COLOR - /// parameters: FilterOpGraphNode - /// SVG filter semantics - selectable input(s), selectable between linear - /// (default) and sRGB color space for calculations - /// Source: https://developer.mozilla.org/en-US/docs/Web/CSS/mix-blend-mode - SVGFEBlendColor, - /// combine 2 images with SVG_FEBLEND_MODE_LUMINOSITY - /// parameters: FilterOpGraphNode - /// SVG filter semantics - selectable input(s), selectable between linear - /// (default) and sRGB color space for calculations - /// Source: https://developer.mozilla.org/en-US/docs/Web/CSS/mix-blend-mode - SVGFEBlendLuminosity, - /// transform colors of image through 5x4 color matrix (transposed for - /// efficiency) - /// parameters: FilterGraphNode, matrix[5][4] - /// SVG filter semantics - selectable input(s), selectable between linear - /// (default) and sRGB color space for calculations - /// Spec: https://www.w3.org/TR/filter-effects-1/#feColorMatrixElement - SVGFEColorMatrix{values: [f32; 20]}, - /// transform colors of image through configurable gradients with component - /// swizzle - /// parameters: FilterGraphNode - /// SVG filter semantics - selectable input(s), selectable between linear - /// (default) and sRGB color space for calculations - /// Spec: https://www.w3.org/TR/filter-effects-1/#feComponentTransferElement - SVGFEComponentTransfer, - /// Processed version of SVGFEComponentTransfer with the FilterData - /// replaced by an interned handle, this is made in wrap_prim_with_filters. - /// Aside from the interned handle, creates_pixels indicates if the transfer - /// parameters will probably fill the entire subregion with non-zero alpha. - SVGFEComponentTransferInterned{handle: FilterDataHandle, creates_pixels: bool}, - /// composite 2 images with chosen composite mode with parameters for that - /// mode - /// parameters: FilterGraphNode, k1, k2, k3, k4 - /// SVG filter semantics - selectable input(s), selectable between linear - /// (default) and sRGB color space for calculations - /// Spec: https://www.w3.org/TR/filter-effects-1/#feCompositeElement - SVGFECompositeArithmetic{k1: f32, k2: f32, k3: f32, k4: f32}, - /// composite 2 images with chosen composite mode with parameters for that - /// mode - /// parameters: FilterGraphNode - /// SVG filter semantics - selectable input(s), selectable between linear - /// (default) and sRGB color space for calculations - /// Spec: https://www.w3.org/TR/filter-effects-1/#feCompositeElement - SVGFECompositeATop, - /// composite 2 images with chosen composite mode with parameters for that - /// mode - /// parameters: FilterGraphNode - /// SVG filter semantics - selectable input(s), selectable between linear - /// (default) and sRGB color space for calculations - /// Spec: https://www.w3.org/TR/filter-effects-1/#feCompositeElement - SVGFECompositeIn, - /// composite 2 images with chosen composite mode with parameters for that - /// mode - /// parameters: FilterOpGraphNode - /// SVG filter semantics - selectable input(s), selectable between linear - /// (default) and sRGB color space for calculations - /// Docs: https://developer.mozilla.org/en-US/docs/Web/SVG/Element/feComposite - SVGFECompositeLighter, - /// composite 2 images with chosen composite mode with parameters for that - /// mode - /// parameters: FilterGraphNode - /// SVG filter semantics - selectable input(s), selectable between linear - /// (default) and sRGB color space for calculations - /// Spec: https://www.w3.org/TR/filter-effects-1/#feCompositeElement - SVGFECompositeOut, - /// composite 2 images with chosen composite mode with parameters for that - /// mode - /// parameters: FilterGraphNode - /// SVG filter semantics - selectable input(s), selectable between linear - /// (default) and sRGB color space for calculations - /// Spec: https://www.w3.org/TR/filter-effects-1/#feCompositeElement - SVGFECompositeOver, - /// composite 2 images with chosen composite mode with parameters for that - /// mode - /// parameters: FilterGraphNode - /// SVG filter semantics - selectable input(s), selectable between linear - /// (default) and sRGB color space for calculations - /// Spec: https://www.w3.org/TR/filter-effects-1/#feCompositeElement - SVGFECompositeXOR, - /// transform image through convolution matrix of up to 25 values (spec - /// allows more but for performance reasons we do not) - /// parameters: FilterGraphNode, orderX, orderY, kernelValues[25], divisor, - /// bias, targetX, targetY, kernelUnitLengthX, kernelUnitLengthY, - /// preserveAlpha - /// SVG filter semantics - selectable input(s), selectable between linear - /// (default) and sRGB color space for calculations - /// Spec: https://www.w3.org/TR/filter-effects-1/#feConvolveMatrixElement - SVGFEConvolveMatrixEdgeModeDuplicate{order_x: i32, order_y: i32, - kernel: [f32; SVGFE_CONVOLVE_VALUES_LIMIT], divisor: f32, bias: f32, - target_x: i32, target_y: i32, kernel_unit_length_x: f32, - kernel_unit_length_y: f32, preserve_alpha: i32}, - /// transform image through convolution matrix of up to 25 values (spec - /// allows more but for performance reasons we do not) - /// parameters: FilterGraphNode, orderX, orderY, kernelValues[25], divisor, - /// bias, targetX, targetY, kernelUnitLengthX, kernelUnitLengthY, - /// preserveAlpha - /// SVG filter semantics - selectable input(s), selectable between linear - /// (default) and sRGB color space for calculations - /// Spec: https://www.w3.org/TR/filter-effects-1/#feConvolveMatrixElement - SVGFEConvolveMatrixEdgeModeNone{order_x: i32, order_y: i32, - kernel: [f32; SVGFE_CONVOLVE_VALUES_LIMIT], divisor: f32, bias: f32, - target_x: i32, target_y: i32, kernel_unit_length_x: f32, - kernel_unit_length_y: f32, preserve_alpha: i32}, - /// transform image through convolution matrix of up to 25 values (spec - /// allows more but for performance reasons we do not) - /// parameters: FilterGraphNode, orderX, orderY, kernelValues[25], divisor, - /// bias, targetX, targetY, kernelUnitLengthX, kernelUnitLengthY, - /// preserveAlpha - /// SVG filter semantics - selectable input(s), selectable between linear - /// (default) and sRGB color space for calculations - /// Spec: https://www.w3.org/TR/filter-effects-1/#feConvolveMatrixElement - SVGFEConvolveMatrixEdgeModeWrap{order_x: i32, order_y: i32, - kernel: [f32; SVGFE_CONVOLVE_VALUES_LIMIT], divisor: f32, bias: f32, - target_x: i32, target_y: i32, kernel_unit_length_x: f32, - kernel_unit_length_y: f32, preserve_alpha: i32}, - /// calculate lighting based on heightmap image with provided values for a - /// distant light source with specified direction - /// parameters: FilterGraphNode, surfaceScale, diffuseConstant, - /// kernelUnitLengthX, kernelUnitLengthY, azimuth, elevation - /// SVG filter semantics - selectable input(s), selectable between linear - /// (default) and sRGB color space for calculations - /// Spec: https://www.w3.org/TR/filter-effects-1/#InterfaceSVGFEDiffuseLightingElement - /// https://www.w3.org/TR/filter-effects-1/#InterfaceSVGFEDistantLightElement - SVGFEDiffuseLightingDistant{surface_scale: f32, diffuse_constant: f32, - kernel_unit_length_x: f32, kernel_unit_length_y: f32, azimuth: f32, - elevation: f32}, - /// calculate lighting based on heightmap image with provided values for a - /// point light source at specified location - /// parameters: FilterGraphNode, surfaceScale, diffuseConstant, - /// kernelUnitLengthX, kernelUnitLengthY, x, y, z - /// SVG filter semantics - selectable input(s), selectable between linear - /// (default) and sRGB color space for calculations - /// Spec: https://www.w3.org/TR/filter-effects-1/#InterfaceSVGFEDiffuseLightingElement - /// https://www.w3.org/TR/filter-effects-1/#InterfaceSVGFEPointLightElement - SVGFEDiffuseLightingPoint{surface_scale: f32, diffuse_constant: f32, - kernel_unit_length_x: f32, kernel_unit_length_y: f32, x: f32, y: f32, - z: f32}, - /// calculate lighting based on heightmap image with provided values for a - /// spot light source at specified location pointing at specified target - /// location with specified hotspot sharpness and cone angle - /// parameters: FilterGraphNode, surfaceScale, diffuseConstant, - /// kernelUnitLengthX, kernelUnitLengthY, x, y, z, pointsAtX, pointsAtY, - /// pointsAtZ, specularExponent, limitingConeAngle - /// SVG filter semantics - selectable input(s), selectable between linear - /// (default) and sRGB color space for calculations - /// Spec: https://www.w3.org/TR/filter-effects-1/#InterfaceSVGFEDiffuseLightingElement - /// https://www.w3.org/TR/filter-effects-1/#InterfaceSVGFESpotLightElement - SVGFEDiffuseLightingSpot{surface_scale: f32, diffuse_constant: f32, - kernel_unit_length_x: f32, kernel_unit_length_y: f32, x: f32, y: f32, - z: f32, points_at_x: f32, points_at_y: f32, points_at_z: f32, - cone_exponent: f32, limiting_cone_angle: f32}, - /// calculate a distorted version of first input image using offset values - /// from second input image at specified intensity - /// parameters: FilterGraphNode, scale, xChannelSelector, yChannelSelector - /// SVG filter semantics - selectable input(s), selectable between linear - /// (default) and sRGB color space for calculations - /// Spec: https://www.w3.org/TR/filter-effects-1/#InterfaceSVGFEDisplacementMapElement - SVGFEDisplacementMap{scale: f32, x_channel_selector: u32, - y_channel_selector: u32}, - /// create and merge a dropshadow version of the specified image's alpha - /// channel with specified offset and blur radius - /// parameters: FilterGraphNode, flood_color, flood_opacity, dx, dy, - /// stdDeviationX, stdDeviationY - /// SVG filter semantics - selectable input(s), selectable between linear - /// (default) and sRGB color space for calculations - /// Spec: https://www.w3.org/TR/filter-effects-1/#InterfaceSVGFEDropShadowElement - SVGFEDropShadow{color: ColorF, dx: f32, dy: f32, std_deviation_x: f32, - std_deviation_y: f32}, - /// synthesize a new image of specified size containing a solid color - /// parameters: FilterGraphNode, color - /// SVG filter semantics - selectable input(s), selectable between linear - /// (default) and sRGB color space for calculations - /// Spec: https://www.w3.org/TR/filter-effects-1/#InterfaceSVGFEFloodElement - SVGFEFlood{color: ColorF}, - /// create a blurred version of the input image - /// parameters: FilterGraphNode, stdDeviationX, stdDeviationY - /// SVG filter semantics - selectable input(s), selectable between linear - /// (default) and sRGB color space for calculations - /// Spec: https://www.w3.org/TR/filter-effects-1/#InterfaceSVGFEGaussianBlurElement - SVGFEGaussianBlur{std_deviation_x: f32, std_deviation_y: f32}, - /// synthesize a new image based on a url (i.e. blob image source) - /// parameters: FilterGraphNode, - /// samplingFilter (see SamplingFilter in Types.h), transform - /// SVG filter semantics - selectable input(s), selectable between linear - /// (default) and sRGB color space for calculations - /// Spec: https://www.w3.org/TR/filter-effects-1/#InterfaceSVGFEImageElement - SVGFEImage{sampling_filter: u32, matrix: [f32; 6]}, - /// create a new image based on the input image with the contour stretched - /// outward (dilate operator) - /// parameters: FilterGraphNode, radiusX, radiusY - /// SVG filter semantics - selectable input(s), selectable between linear - /// (default) and sRGB color space for calculations - /// Spec: https://www.w3.org/TR/filter-effects-1/#InterfaceSVGFEMorphologyElement - SVGFEMorphologyDilate{radius_x: f32, radius_y: f32}, - /// create a new image based on the input image with the contour shrunken - /// inward (erode operator) - /// parameters: FilterGraphNode, radiusX, radiusY - /// SVG filter semantics - selectable input(s), selectable between linear - /// (default) and sRGB color space for calculations - /// Spec: https://www.w3.org/TR/filter-effects-1/#InterfaceSVGFEMorphologyElement - SVGFEMorphologyErode{radius_x: f32, radius_y: f32}, - /// calculate lighting based on heightmap image with provided values for a - /// distant light source with specified direction - /// parameters: FilerData, surfaceScale, specularConstant, specularExponent, - /// kernelUnitLengthX, kernelUnitLengthY, azimuth, elevation - /// SVG filter semantics - selectable input(s), selectable between linear - /// (default) and sRGB color space for calculations - /// Spec: https://www.w3.org/TR/filter-effects-1/#InterfaceSVGFESpecularLightingElement - /// https://www.w3.org/TR/filter-effects-1/#InterfaceSVGFEDistantLightElement - SVGFESpecularLightingDistant{surface_scale: f32, specular_constant: f32, - specular_exponent: f32, kernel_unit_length_x: f32, - kernel_unit_length_y: f32, azimuth: f32, elevation: f32}, - /// calculate lighting based on heightmap image with provided values for a - /// point light source at specified location - /// parameters: FilterGraphNode, surfaceScale, specularConstant, - /// specularExponent, kernelUnitLengthX, kernelUnitLengthY, x, y, z - /// SVG filter semantics - selectable input(s), selectable between linear - /// (default) and sRGB color space for calculations - /// Spec: https://www.w3.org/TR/filter-effects-1/#InterfaceSVGFESpecularLightingElement - /// https://www.w3.org/TR/filter-effects-1/#InterfaceSVGFEPointLightElement - SVGFESpecularLightingPoint{surface_scale: f32, specular_constant: f32, - specular_exponent: f32, kernel_unit_length_x: f32, - kernel_unit_length_y: f32, x: f32, y: f32, z: f32}, - /// calculate lighting based on heightmap image with provided values for a - /// spot light source at specified location pointing at specified target - /// location with specified hotspot sharpness and cone angle - /// parameters: FilterGraphNode, surfaceScale, specularConstant, - /// specularExponent, kernelUnitLengthX, kernelUnitLengthY, x, y, z, - /// pointsAtX, pointsAtY, pointsAtZ, specularExponent, limitingConeAngle - /// SVG filter semantics - selectable input(s), selectable between linear - /// (default) and sRGB color space for calculations - /// Spec: https://www.w3.org/TR/filter-effects-1/#InterfaceSVGFESpecularLightingElement - /// https://www.w3.org/TR/filter-effects-1/#InterfaceSVGFESpotLightElement - SVGFESpecularLightingSpot{surface_scale: f32, specular_constant: f32, - specular_exponent: f32, kernel_unit_length_x: f32, - kernel_unit_length_y: f32, x: f32, y: f32, z: f32, points_at_x: f32, - points_at_y: f32, points_at_z: f32, cone_exponent: f32, - limiting_cone_angle: f32}, - /// create a new image based on the input image, repeated throughout the - /// output rectangle - /// parameters: FilterGraphNode - /// SVG filter semantics - selectable input(s), selectable between linear - /// (default) and sRGB color space for calculations - /// Spec: https://www.w3.org/TR/filter-effects-1/#InterfaceSVGFETileElement - SVGFETile, - /// synthesize a new image based on Fractal Noise (Perlin) with the chosen - /// stitching mode - /// parameters: FilterGraphNode, baseFrequencyX, baseFrequencyY, numOctaves, - /// seed - /// SVG filter semantics - selectable input(s), selectable between linear - /// (default) and sRGB color space for calculations - /// Spec: https://www.w3.org/TR/filter-effects-1/#InterfaceSVGFETurbulenceElement - SVGFETurbulenceWithFractalNoiseWithNoStitching{base_frequency_x: f32, - base_frequency_y: f32, num_octaves: u32, seed: u32}, - /// synthesize a new image based on Fractal Noise (Perlin) with the chosen - /// stitching mode - /// parameters: FilterGraphNode, baseFrequencyX, baseFrequencyY, numOctaves, - /// seed - /// SVG filter semantics - selectable input(s), selectable between linear - /// (default) and sRGB color space for calculations - /// Spec: https://www.w3.org/TR/filter-effects-1/#InterfaceSVGFETurbulenceElement - SVGFETurbulenceWithFractalNoiseWithStitching{base_frequency_x: f32, - base_frequency_y: f32, num_octaves: u32, seed: u32}, - /// synthesize a new image based on Turbulence Noise (offset vectors) - /// parameters: FilterGraphNode, baseFrequencyX, baseFrequencyY, numOctaves, - /// seed - /// SVG filter semantics - selectable input(s), selectable between linear - /// (default) and sRGB color space for calculations - /// Spec: https://www.w3.org/TR/filter-effects-1/#InterfaceSVGFETurbulenceElement - SVGFETurbulenceWithTurbulenceNoiseWithNoStitching{base_frequency_x: f32, - base_frequency_y: f32, num_octaves: u32, seed: u32}, - /// synthesize a new image based on Turbulence Noise (offset vectors) - /// parameters: FilterGraphNode, baseFrequencyX, baseFrequencyY, numOctaves, - /// seed - /// SVG filter semantics - selectable input(s), selectable between linear - /// (default) and sRGB color space for calculations - /// Spec: https://www.w3.org/TR/filter-effects-1/#InterfaceSVGFETurbulenceElement - SVGFETurbulenceWithTurbulenceNoiseWithStitching{base_frequency_x: f32, - base_frequency_y: f32, num_octaves: u32, seed: u32}, -} - -impl FilterGraphOp { - pub fn kind(&self) -> &'static str { - match *self { - FilterGraphOp::SVGFEBlendColor => "SVGFEBlendColor", - FilterGraphOp::SVGFEBlendColorBurn => "SVGFEBlendColorBurn", - FilterGraphOp::SVGFEBlendColorDodge => "SVGFEBlendColorDodge", - FilterGraphOp::SVGFEBlendDarken => "SVGFEBlendDarken", - FilterGraphOp::SVGFEBlendDifference => "SVGFEBlendDifference", - FilterGraphOp::SVGFEBlendExclusion => "SVGFEBlendExclusion", - FilterGraphOp::SVGFEBlendHardLight => "SVGFEBlendHardLight", - FilterGraphOp::SVGFEBlendHue => "SVGFEBlendHue", - FilterGraphOp::SVGFEBlendLighten => "SVGFEBlendLighten", - FilterGraphOp::SVGFEBlendLuminosity => "SVGFEBlendLuminosity", - FilterGraphOp::SVGFEBlendMultiply => "SVGFEBlendMultiply", - FilterGraphOp::SVGFEBlendNormal => "SVGFEBlendNormal", - FilterGraphOp::SVGFEBlendOverlay => "SVGFEBlendOverlay", - FilterGraphOp::SVGFEBlendSaturation => "SVGFEBlendSaturation", - FilterGraphOp::SVGFEBlendScreen => "SVGFEBlendScreen", - FilterGraphOp::SVGFEBlendSoftLight => "SVGFEBlendSoftLight", - FilterGraphOp::SVGFEColorMatrix{..} => "SVGFEColorMatrix", - FilterGraphOp::SVGFEComponentTransfer => "SVGFEComponentTransfer", - FilterGraphOp::SVGFEComponentTransferInterned{..} => "SVGFEComponentTransferInterned", - FilterGraphOp::SVGFECompositeArithmetic{..} => "SVGFECompositeArithmetic", - FilterGraphOp::SVGFECompositeATop => "SVGFECompositeATop", - FilterGraphOp::SVGFECompositeIn => "SVGFECompositeIn", - FilterGraphOp::SVGFECompositeLighter => "SVGFECompositeLighter", - FilterGraphOp::SVGFECompositeOut => "SVGFECompositeOut", - FilterGraphOp::SVGFECompositeOver => "SVGFECompositeOver", - FilterGraphOp::SVGFECompositeXOR => "SVGFECompositeXOR", - FilterGraphOp::SVGFEConvolveMatrixEdgeModeDuplicate{..} => "SVGFEConvolveMatrixEdgeModeDuplicate", - FilterGraphOp::SVGFEConvolveMatrixEdgeModeNone{..} => "SVGFEConvolveMatrixEdgeModeNone", - FilterGraphOp::SVGFEConvolveMatrixEdgeModeWrap{..} => "SVGFEConvolveMatrixEdgeModeWrap", - FilterGraphOp::SVGFEDiffuseLightingDistant{..} => "SVGFEDiffuseLightingDistant", - FilterGraphOp::SVGFEDiffuseLightingPoint{..} => "SVGFEDiffuseLightingPoint", - FilterGraphOp::SVGFEDiffuseLightingSpot{..} => "SVGFEDiffuseLightingSpot", - FilterGraphOp::SVGFEDisplacementMap{..} => "SVGFEDisplacementMap", - FilterGraphOp::SVGFEDropShadow{..} => "SVGFEDropShadow", - FilterGraphOp::SVGFEFlood{..} => "SVGFEFlood", - FilterGraphOp::SVGFEGaussianBlur{..} => "SVGFEGaussianBlur", - FilterGraphOp::SVGFEIdentity => "SVGFEIdentity", - FilterGraphOp::SVGFEImage{..} => "SVGFEImage", - FilterGraphOp::SVGFEMorphologyDilate{..} => "SVGFEMorphologyDilate", - FilterGraphOp::SVGFEMorphologyErode{..} => "SVGFEMorphologyErode", - FilterGraphOp::SVGFEOpacity{..} => "SVGFEOpacity", - FilterGraphOp::SVGFESourceAlpha => "SVGFESourceAlpha", - FilterGraphOp::SVGFESourceGraphic => "SVGFESourceGraphic", - FilterGraphOp::SVGFESpecularLightingDistant{..} => "SVGFESpecularLightingDistant", - FilterGraphOp::SVGFESpecularLightingPoint{..} => "SVGFESpecularLightingPoint", - FilterGraphOp::SVGFESpecularLightingSpot{..} => "SVGFESpecularLightingSpot", - FilterGraphOp::SVGFETile => "SVGFETile", - FilterGraphOp::SVGFEToAlpha => "SVGFEToAlpha", - FilterGraphOp::SVGFETurbulenceWithFractalNoiseWithNoStitching{..} => "SVGFETurbulenceWithFractalNoiseWithNoStitching", - FilterGraphOp::SVGFETurbulenceWithFractalNoiseWithStitching{..} => "SVGFETurbulenceWithFractalNoiseWithStitching", - FilterGraphOp::SVGFETurbulenceWithTurbulenceNoiseWithNoStitching{..} => "SVGFETurbulenceWithTurbulenceNoiseWithNoStitching", - FilterGraphOp::SVGFETurbulenceWithTurbulenceNoiseWithStitching{..} => "SVGFETurbulenceWithTurbulenceNoiseWithStitching", - } - } -} - -#[derive(Clone, Debug)] -#[cfg_attr(feature = "capture", derive(Serialize))] -#[cfg_attr(feature = "replay", derive(Deserialize))] -pub struct FilterGraphNode { - /// Indicates this graph node was marked as necessary by the DAG optimizer - pub kept_by_optimizer: bool, - /// true if color_interpolation_filter == LinearRgb; shader will convert - /// sRGB texture pixel colors on load and convert back on store, for correct - /// interpolation - pub linear: bool, - /// padding for output rect if we need a border to get correct clamping, or - /// to account for larger final subregion than source rect (see bug 1869672) - pub inflate: i16, - /// virtualized picture input bindings, these refer to other filter outputs - /// by number within the graph, usually there is one element - pub inputs: Vec<FilterGraphPictureReference>, - /// clipping rect for filter node output - pub subregion: LayoutRect, -} - -impl From<FilterOpGraphNode> for FilterGraphNode { - fn from(node: FilterOpGraphNode) -> Self { - let mut inputs: Vec<FilterGraphPictureReference> = Vec::new(); - if node.input.buffer_id != FilterOpGraphPictureBufferId::None { - inputs.push(node.input.into()); - } - if node.input2.buffer_id != FilterOpGraphPictureBufferId::None { - inputs.push(node.input2.into()); - } - // If the op used by this node is a feMerge, it will add more inputs - // after this invocation. - FilterGraphNode{ - linear: node.linear, - inputs, - subregion: node.subregion, - // These are computed later in scene_building - kept_by_optimizer: true, - inflate: 0, - } - } -} - - /// Equivalent to api::FilterOp with added internal information #[derive(Clone, Debug)] #[cfg_attr(feature = "capture", derive(Serialize))] diff --git a/gfx/wr/webrender/src/lib.rs b/gfx/wr/webrender/src/lib.rs @@ -140,6 +140,7 @@ mod rectangle_occlusion; mod picture_textures; mod frame_allocator; mod bump_allocator; +mod svg_filter; /// pub mod intern; diff --git a/gfx/wr/webrender/src/picture.rs b/gfx/wr/webrender/src/picture.rs @@ -94,25 +94,23 @@ //! blend the overlay tile (this is not always optimal right now, but will be //! improved as a follow up). -use api::{MixBlendMode, PremultipliedColorF, SVGFE_GRAPH_MAX}; -use api::{FilterOpGraphPictureBufferId, RasterSpace}; +use api::{MixBlendMode, PremultipliedColorF, RasterSpace}; use api::{DebugFlags, ColorF, PrimitiveFlags, SnapshotInfo}; use api::units::*; use crate::prim_store::image::AdjustedImageSource; use crate::{command_buffer::PrimitiveCommand, render_task_graph::RenderTaskGraphBuilder, renderer::GpuBufferBuilderF}; use crate::box_shadow::BLUR_SAMPLE_SCALE; use crate::clip::{ClipNodeId, ClipTreeBuilder}; -use crate::profiler::add_text_marker; use crate::spatial_tree::{SpatialTree, CoordinateSpaceMapping, SpatialNodeIndex, VisibleFace}; use crate::composite::{tile_kind, CompositeState, CompositeTileSurface, CompositorKind, NativeTileId}; use crate::composite::{CompositeTileDescriptor, CompositeTile}; use crate::debug_colors; use euclid::{vec3, Scale, Vector2D, Box2D}; -use crate::internal_types::{FastHashMap, PlaneSplitter, FilterGraphOp, FilterGraphNode, Filter}; +use crate::internal_types::{FastHashMap, PlaneSplitter, Filter}; use crate::internal_types::{PlaneSplitterIndex, PlaneSplitAnchor, TextureSource}; use crate::frame_builder::{FrameBuildingContext, FrameBuildingState, PictureState, PictureContext}; use crate::gpu_types::{BlurEdgeMode, BrushSegmentGpuData, ImageBrushPrimitiveData}; - +use crate::svg_filter::{FilterGraphOp, FilterGraphNode, get_coverage_source_svgfe, get_coverage_target_svgfe}; use plane_split::{Clipper, Polygon}; use crate::prim_store::{PictureIndex, PrimitiveInstance, PrimitiveInstanceKind}; use crate::prim_store::PrimitiveScratchBuffer; @@ -142,7 +140,6 @@ use crate::tile_cache::{BackdropKind, BackdropSurface, Tile}; use crate::tile_cache::{TileKey, SubSliceIndex}; use crate::invalidation::InvalidationReason; use crate::tile_cache::MAX_SURFACE_SIZE; -use core::time::Duration; pub use crate::invalidation::DirtyRegion; pub use crate::invalidation::dependency::ImageDependency; @@ -344,7 +341,7 @@ impl PictureCompositeMode { // Return prim_subregion for use in get_local_prim_rect, which // is the polygon size. // This must match surface_rects.unclipped_local. - self.get_coverage_target_svgfe(filters, surface_rect.cast_unit()) + get_coverage_target_svgfe(filters, surface_rect.cast_unit()) } _ => { surface_rect @@ -397,8 +394,8 @@ impl PictureCompositeMode { PictureCompositeMode::SVGFEGraph(ref filters) => { // surface_rect may be for source or target, so invalidate based // on both interpretations - let target_subregion = self.get_coverage_source_svgfe(filters, surface_rect.cast()); - let source_subregion = self.get_coverage_target_svgfe(filters, surface_rect.cast()); + let target_subregion = get_coverage_source_svgfe(filters, surface_rect.cast()); + let source_subregion = get_coverage_target_svgfe(filters, surface_rect.cast()); target_subregion.union(&source_subregion) } _ => { @@ -436,288 +433,6 @@ impl PictureCompositeMode { PictureCompositeMode::Filter(Filter::SVGGraphNode(..)) => "Filter::SVGGraphNode", } } - - /// Here we transform source rect to target rect for SVGFEGraph by walking - /// the whole graph and propagating subregions based on the provided - /// invalidation rect, and we want it to be a tight fit so we don't waste - /// time applying multiple filters to pixels that do not contribute to the - /// invalidated rect. - /// - /// The interesting parts of the handling of SVG filters are: - /// * scene_building.rs : wrap_prim_with_filters - /// * picture.rs : get_coverage_target_svgfe (you are here) - /// * picture.rs : get_coverage_source_svgfe - /// * render_task.rs : new_svg_filter_graph - /// * render_target.rs : add_svg_filter_node_instances - pub fn get_coverage_target_svgfe( - &self, - filters: &[(FilterGraphNode, FilterGraphOp)], - surface_rect: LayoutRect, - ) -> LayoutRect { - - // The value of BUFFER_LIMIT here must be the same as in - // scene_building.rs, or we'll hit asserts here. - const BUFFER_LIMIT: usize = SVGFE_GRAPH_MAX; - - // We need to evaluate the subregions based on the proposed - // SourceGraphic rect as it isn't known at scene build time. - let mut subregion_by_buffer_id: [LayoutRect; BUFFER_LIMIT] = [LayoutRect::zero(); BUFFER_LIMIT]; - for (id, (node, op)) in filters.iter().enumerate() { - let full_subregion = node.subregion; - let mut used_subregion = LayoutRect::zero(); - for input in &node.inputs { - match input.buffer_id { - FilterOpGraphPictureBufferId::BufferId(id) => { - assert!((id as usize) < BUFFER_LIMIT, "BUFFER_LIMIT must be the same in frame building and scene building"); - // This id lookup should always succeed. - let input_subregion = subregion_by_buffer_id[id as usize]; - // Now add the padding that transforms from - // source to target, this was determined during - // scene build based on the operation. - let input_subregion = - LayoutRect::new( - LayoutPoint::new( - input_subregion.min.x + input.target_padding.min.x, - input_subregion.min.y + input.target_padding.min.y, - ), - LayoutPoint::new( - input_subregion.max.x + input.target_padding.max.x, - input_subregion.max.y + input.target_padding.max.y, - ), - ); - used_subregion = used_subregion - .union(&input_subregion); - } - FilterOpGraphPictureBufferId::None => { - panic!("Unsupported BufferId type"); - } - } - } - // We can clip the used subregion to the node subregion - used_subregion = used_subregion - .intersection(&full_subregion) - .unwrap_or(LayoutRect::zero()); - match op { - FilterGraphOp::SVGFEBlendColor => {} - FilterGraphOp::SVGFEBlendColorBurn => {} - FilterGraphOp::SVGFEBlendColorDodge => {} - FilterGraphOp::SVGFEBlendDarken => {} - FilterGraphOp::SVGFEBlendDifference => {} - FilterGraphOp::SVGFEBlendExclusion => {} - FilterGraphOp::SVGFEBlendHardLight => {} - FilterGraphOp::SVGFEBlendHue => {} - FilterGraphOp::SVGFEBlendLighten => {} - FilterGraphOp::SVGFEBlendLuminosity => {} - FilterGraphOp::SVGFEBlendMultiply => {} - FilterGraphOp::SVGFEBlendNormal => {} - FilterGraphOp::SVGFEBlendOverlay => {} - FilterGraphOp::SVGFEBlendSaturation => {} - FilterGraphOp::SVGFEBlendScreen => {} - FilterGraphOp::SVGFEBlendSoftLight => {} - FilterGraphOp::SVGFEColorMatrix { values } => { - if values[19] > 0.0 { - // Manipulating alpha offset can easily create new - // pixels outside of input subregions - used_subregion = full_subregion; - add_text_marker( - "SVGFEColorMatrix", - "SVGFEColorMatrix with non-zero alpha offset, using full subregion", - Duration::from_millis(1)); - } - } - FilterGraphOp::SVGFEComponentTransfer => unreachable!(), - FilterGraphOp::SVGFEComponentTransferInterned{handle: _, creates_pixels} => { - // Check if the value of alpha[0] is modified, if so - // the whole subregion is used because it will be - // creating new pixels outside of input subregions - if *creates_pixels { - used_subregion = full_subregion; - add_text_marker( - "SVGFEComponentTransfer", - "SVGFEComponentTransfer with non-zero minimum alpha, using full subregion", - Duration::from_millis(1)); - } - } - FilterGraphOp::SVGFECompositeArithmetic { k1, k2, k3, k4 } => { - // Optimization opportunity - some inputs may be - // smaller subregions due to the way the math works, - // k1 is the intersection of the two inputs, k2 is - // the first input only, k3 is the second input - // only, and k4 changes the whole subregion. - // - // See logic for SVG_FECOMPOSITE_OPERATOR_ARITHMETIC - // in FilterSupport.cpp - // - // We can at least ignore the entire node if - // everything is zero. - if *k1 <= 0.0 && - *k2 <= 0.0 && - *k3 <= 0.0 { - used_subregion = LayoutRect::zero(); - } - // Check if alpha is added to pixels as it means it - // can fill pixels outside input subregions - if *k4 > 0.0 { - used_subregion = full_subregion; - add_text_marker( - "SVGFECompositeArithmetic", - "SVGFECompositeArithmetic with non-zero offset, using full subregion", - Duration::from_millis(1)); - } - } - FilterGraphOp::SVGFECompositeATop => {} - FilterGraphOp::SVGFECompositeIn => {} - FilterGraphOp::SVGFECompositeLighter => {} - FilterGraphOp::SVGFECompositeOut => {} - FilterGraphOp::SVGFECompositeOver => {} - FilterGraphOp::SVGFECompositeXOR => {} - FilterGraphOp::SVGFEConvolveMatrixEdgeModeDuplicate{..} => {} - FilterGraphOp::SVGFEConvolveMatrixEdgeModeNone{..} => {} - FilterGraphOp::SVGFEConvolveMatrixEdgeModeWrap{..} => {} - FilterGraphOp::SVGFEDiffuseLightingDistant{..} => {} - FilterGraphOp::SVGFEDiffuseLightingPoint{..} => {} - FilterGraphOp::SVGFEDiffuseLightingSpot{..} => {} - FilterGraphOp::SVGFEDisplacementMap{..} => {} - FilterGraphOp::SVGFEDropShadow{..} => {} - FilterGraphOp::SVGFEFlood { color } => { - // Subregion needs to be set to the full node - // subregion for fills (unless the fill is a no-op) - if color.a > 0.0 { - used_subregion = full_subregion; - } - } - FilterGraphOp::SVGFEGaussianBlur{..} => {} - FilterGraphOp::SVGFEIdentity => {} - FilterGraphOp::SVGFEImage { sampling_filter: _sampling_filter, matrix: _matrix } => { - // TODO: calculate the actual subregion - used_subregion = full_subregion; - } - FilterGraphOp::SVGFEMorphologyDilate{..} => {} - FilterGraphOp::SVGFEMorphologyErode{..} => {} - FilterGraphOp::SVGFEOpacity { valuebinding: _valuebinding, value } => { - // If fully transparent, we can ignore this node - if *value <= 0.0 { - used_subregion = LayoutRect::zero(); - } - } - FilterGraphOp::SVGFESourceAlpha | - FilterGraphOp::SVGFESourceGraphic => { - used_subregion = surface_rect; - } - FilterGraphOp::SVGFESpecularLightingDistant{..} => {} - FilterGraphOp::SVGFESpecularLightingPoint{..} => {} - FilterGraphOp::SVGFESpecularLightingSpot{..} => {} - FilterGraphOp::SVGFETile => { - // feTile fills the entire output with - // source pixels, so it's effectively a flood. - used_subregion = full_subregion; - } - FilterGraphOp::SVGFEToAlpha => {} - FilterGraphOp::SVGFETurbulenceWithFractalNoiseWithNoStitching{..} | - FilterGraphOp::SVGFETurbulenceWithFractalNoiseWithStitching{..} | - FilterGraphOp::SVGFETurbulenceWithTurbulenceNoiseWithNoStitching{..} | - FilterGraphOp::SVGFETurbulenceWithTurbulenceNoiseWithStitching{..} => { - // Turbulence produces pixel values throughout the - // node subregion. - used_subregion = full_subregion; - } - } - // Store the subregion so later nodes can refer back - // to this and propagate rects properly - assert!((id as usize) < BUFFER_LIMIT, "BUFFER_LIMIT must be the same in frame building and scene building"); - subregion_by_buffer_id[id] = used_subregion; - } - subregion_by_buffer_id[filters.len() - 1] - } - - /// Here we transform target rect to source rect for SVGFEGraph by walking - /// the whole graph and propagating subregions based on the provided - /// invalidation rect, and we want it to be a tight fit so we don't waste - /// time applying multiple filters to pixels that do not contribute to the - /// invalidated rect. - /// - /// The interesting parts of the handling of SVG filters are: - /// * scene_building.rs : wrap_prim_with_filters - /// * picture.rs : get_coverage_target_svgfe - /// * picture.rs : get_coverage_source_svgfe (you are here) - /// * render_task.rs : new_svg_filter_graph - /// * render_target.rs : add_svg_filter_node_instances - pub fn get_coverage_source_svgfe( - &self, - filters: &[(FilterGraphNode, FilterGraphOp)], - surface_rect: LayoutRect, - ) -> LayoutRect { - - // The value of BUFFER_LIMIT here must be the same as in - // scene_building.rs, or we'll hit asserts here. - const BUFFER_LIMIT: usize = SVGFE_GRAPH_MAX; - - // We're solving the source rect from target rect (e.g. due - // to invalidation of a region, we need to know how much of - // SourceGraphic is needed to draw that region accurately), - // so we need to walk the DAG in reverse and accumulate the source - // subregion for each input onto the referenced node, which can then - // propagate that to its inputs when it is iterated. - let mut source_subregion = LayoutRect::zero(); - let mut subregion_by_buffer_id: [LayoutRect; BUFFER_LIMIT] = - [LayoutRect::zero(); BUFFER_LIMIT]; - let final_buffer_id = filters.len() - 1; - assert!(final_buffer_id < BUFFER_LIMIT, "BUFFER_LIMIT must be the same in frame building and scene building"); - subregion_by_buffer_id[final_buffer_id] = surface_rect; - for (node_buffer_id, (node, op)) in filters.iter().enumerate().rev() { - // This is the subregion this node outputs, we can clip - // the inputs based on source_padding relative to this, - // and accumulate a new subregion for them. - assert!(node_buffer_id < BUFFER_LIMIT, "BUFFER_LIMIT must be the same in frame building and scene building"); - let full_subregion = node.subregion; - let mut used_subregion = - subregion_by_buffer_id[node_buffer_id]; - // We can clip the propagated subregion to the node subregion before - // we add source_padding for each input and propogate to them - used_subregion = used_subregion - .intersection(&full_subregion) - .unwrap_or(LayoutRect::zero()); - if !used_subregion.is_empty() { - for input in &node.inputs { - let input_subregion = LayoutRect::new( - LayoutPoint::new( - used_subregion.min.x + input.source_padding.min.x, - used_subregion.min.y + input.source_padding.min.y, - ), - LayoutPoint::new( - used_subregion.max.x + input.source_padding.max.x, - used_subregion.max.y + input.source_padding.max.y, - ), - ); - match input.buffer_id { - FilterOpGraphPictureBufferId::BufferId(id) => { - // Add the used area to the input, later when - // the referneced node is iterated as a node it - // will propagate the used bounds. - subregion_by_buffer_id[id as usize] = - subregion_by_buffer_id[id as usize] - .union(&input_subregion); - } - FilterOpGraphPictureBufferId::None => {} - } - } - } - // If this is the SourceGraphic or SourceAlpha, we now have the - // source subregion we're looking for. If both exist in the - // same graph, we need to combine them, so don't merely replace. - match op { - FilterGraphOp::SVGFESourceAlpha | - FilterGraphOp::SVGFESourceGraphic => { - source_subregion = source_subregion.union(&used_subregion); - } - _ => {} - } - } - - // Note that this can be zero if SourceGraphic/SourceAlpha is not used - // in this graph. - source_subregion - } } /// Enum value describing the place of a picture in a 3D context. diff --git a/gfx/wr/webrender/src/prim_store/picture.rs b/gfx/wr/webrender/src/prim_store/picture.rs @@ -11,8 +11,8 @@ use crate::scene_building::IsVisible; use crate::gpu_types::BlurEdgeMode; use crate::intern::ItemUid; use crate::intern::{Internable, InternDebug, Handle as InternHandle}; -use crate::internal_types::{LayoutPrimitiveInfo, FilterGraphPictureReference, - FilterGraphOp, FilterGraphNode, SVGFE_CONVOLVE_VALUES_LIMIT, Filter}; +use crate::internal_types::{LayoutPrimitiveInfo, Filter}; +use crate::svg_filter::{FilterGraphPictureReference, FilterGraphOp, FilterGraphNode, SVGFE_CONVOLVE_VALUES_LIMIT}; use crate::picture::PictureCompositeMode; use crate::prim_store::{ PrimitiveInstanceKind, PrimitiveStore, VectorKey, diff --git a/gfx/wr/webrender/src/render_target.rs b/gfx/wr/webrender/src/render_target.rs @@ -16,7 +16,8 @@ use crate::frame_builder::FrameGlobalResources; use crate::gpu_types::{BorderInstance, SVGFEFilterInstance, BlurDirection, BlurInstance, PrimitiveHeaders, ScalingInstance}; use crate::gpu_types::{TransformPalette, ZBufferIdGenerator, MaskInstance, ClipSpace, BlurEdgeMode}; use crate::gpu_types::{ZBufferId, QuadSegment, PrimitiveInstanceData, TransformPaletteId}; -use crate::internal_types::{CacheTextureId, FastHashMap, FilterGraphOp, FrameAllocator, FrameMemory, FrameVec, TextureSource}; +use crate::internal_types::{CacheTextureId, FastHashMap, FrameAllocator, FrameMemory, FrameVec, TextureSource}; +use crate::svg_filter::FilterGraphOp; use crate::picture::{SurfaceInfo, ResolvedSurfaceTexture}; use crate::tile_cache::{SliceId, TileCacheInstance}; use crate::quad; diff --git a/gfx/wr/webrender/src/render_task.rs b/gfx/wr/webrender/src/render_task.rs @@ -14,7 +14,8 @@ use crate::profiler::{add_text_marker}; use crate::spatial_tree::SpatialNodeIndex; use crate::frame_builder::FrameBuilderConfig; use crate::gpu_types::{BorderInstance, UvRectKind, TransformPaletteId, BlurEdgeMode}; -use crate::internal_types::{CacheTextureId, FastHashMap, FilterGraphNode, FilterGraphOp, FilterGraphPictureReference, SVGFE_CONVOLVE_VALUES_LIMIT, TextureSource, Swizzle}; +use crate::internal_types::{CacheTextureId, FastHashMap, TextureSource, Swizzle}; +use crate::svg_filter::{FilterGraphNode, FilterGraphOp, FilterGraphPictureReference, SVGFE_CONVOLVE_VALUES_LIMIT}; use crate::picture::ResolvedSurfaceTexture; use crate::tile_cache::MAX_SURFACE_SIZE; use crate::prim_store::ClipData; diff --git a/gfx/wr/webrender/src/scene_building.rs b/gfx/wr/webrender/src/scene_building.rs @@ -60,7 +60,8 @@ use crate::frame_builder::FrameBuilderConfig; use glyph_rasterizer::{FontInstance, SharedFontResources}; use crate::hit_test::HitTestingScene; use crate::intern::Interner; -use crate::internal_types::{FastHashMap, LayoutPrimitiveInfo, Filter, FilterGraphNode, FilterGraphOp, FilterGraphPictureReference, PlaneSplitterIndex, PipelineInstanceId}; +use crate::internal_types::{FastHashMap, LayoutPrimitiveInfo, Filter, PlaneSplitterIndex, PipelineInstanceId}; +use crate::svg_filter::{FilterGraphNode, FilterGraphOp, FilterGraphPictureReference}; use crate::picture::{Picture3DContext, PictureCompositeMode, PicturePrimitive}; use crate::picture::{BlitReason, OrderedPictureChild, PrimitiveList, SurfaceInfo, PictureFlags}; use crate::picture_graph::PictureGraph; diff --git a/gfx/wr/webrender/src/surface.rs b/gfx/wr/webrender/src/surface.rs @@ -12,6 +12,7 @@ use crate::command_buffer::{CommandBufferBuilderKind, CommandBufferList, Command use crate::gpu_types::UvRectKind; use crate::internal_types::{FastHashMap, Filter}; use crate::picture::PictureCompositeMode; +use crate::svg_filter::get_coverage_source_svgfe; use crate::tile_cache::{TileKey, SubSliceIndex, MAX_COMPOSITOR_SURFACES}; use crate::prim_store::PictureIndex; use crate::profiler; @@ -970,7 +971,7 @@ pub fn get_surface_rects( // to produce to satisfy the invalidation rect, then clip it by the // original primitive rect because we have no reason to produce any // out of bounds pixels; they would just be blank anyway. - let source_potential_subregion = composite_mode.get_coverage_source_svgfe( + let source_potential_subregion = get_coverage_source_svgfe( filters, visible_subregion.cast_unit()); let source_subregion = source_potential_subregion diff --git a/gfx/wr/webrender/src/svg_filter.rs b/gfx/wr/webrender/src/svg_filter.rs @@ -0,0 +1,839 @@ +/* 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 api::{FilterOpGraphPictureReference, FilterOpGraphNode, ColorF}; +use api::SVGFE_GRAPH_MAX; +use api::units::*; +use api::FilterOpGraphPictureBufferId; +use crate::profiler::add_text_marker; +use crate::filterdata::FilterDataHandle; +use core::time::Duration; + +#[derive(Clone, Copy, Debug)] +#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "replay", derive(Deserialize))] +pub struct FilterGraphPictureReference { + /// Id of the picture in question in a namespace unique to this filter DAG, + /// some are special values like + /// FilterPrimitiveDescription::kPrimitiveIndexSourceGraphic. + pub buffer_id: FilterOpGraphPictureBufferId, + /// Set by wrap_prim_with_filters to the subregion of the input node, may + /// also have been offset for feDropShadow or feOffset + pub subregion: LayoutRect, + /// During scene build this is the offset to apply to the input subregion + /// for feOffset, which can be optimized away by pushing its offset and + /// subregion crop to downstream nodes. This is always zero in render tasks + /// where it has already been applied to subregion by that point. Not used + /// in get_coverage_svgfe because source_padding/target_padding represent + /// the offset there. + pub offset: LayoutVector2D, + /// Equal to the inflate value of the referenced buffer, or 0 + pub inflate: i16, + /// Padding on each side to represent how this input is read relative to the + /// node's output subregion, this represents what the operation needs to + /// read from ths input, which may be blurred or offset. + pub source_padding: LayoutRect, + /// Padding on each side to represent how this input affects the node's + /// subregion, this can be used to calculate target subregion based on + /// SourceGraphic subregion. This is usually equal to source_padding except + /// offset in the opposite direction, inflates typically do the same thing + /// to both types of padding. + pub target_padding: LayoutRect, +} + +impl From<FilterOpGraphPictureReference> for FilterGraphPictureReference { + fn from(pic: FilterOpGraphPictureReference) -> Self { + FilterGraphPictureReference{ + buffer_id: pic.buffer_id, + // All of these are set by wrap_prim_with_filters + subregion: LayoutRect::zero(), + offset: LayoutVector2D::zero(), + inflate: 0, + source_padding: LayoutRect::zero(), + target_padding: LayoutRect::zero(), + } + } +} + +pub const SVGFE_CONVOLVE_DIAMETER_LIMIT: usize = 5; +pub const SVGFE_CONVOLVE_VALUES_LIMIT: usize = SVGFE_CONVOLVE_DIAMETER_LIMIT * + SVGFE_CONVOLVE_DIAMETER_LIMIT; + +#[derive(Clone, Debug)] +#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "replay", derive(Deserialize))] +pub enum FilterGraphOp { + /// Filter that copies the SourceGraphic image into the specified subregion, + /// This is intentionally the only way to get SourceGraphic into the graph, + /// as the filter region must be applied before it is used. + /// parameters: FilterOpGraphNode + /// SVG filter semantics - no inputs, no linear + SVGFESourceGraphic, + /// Filter that copies the SourceAlpha image into the specified subregion, + /// This is intentionally the only way to get SourceAlpha into the graph, + /// as the filter region must be applied before it is used. + /// parameters: FilterOpGraphNode + /// SVG filter semantics - no inputs, no linear + SVGFESourceAlpha, + /// Filter that does no transformation of the colors, used to implement a + /// few things like SVGFEOffset, and this is the default value in + /// impl_default_for_enums. + /// parameters: FilterGraphNode + /// SVG filter semantics - selectable input with offset + SVGFEIdentity, + /// represents CSS opacity property as a graph node like the rest of the + /// SVGFE* filters + /// parameters: FilterGraphNode + /// SVG filter semantics - selectable input(s), selectable between linear + /// (default) and sRGB color space for calculations + SVGFEOpacity{valuebinding: api::PropertyBinding<f32>, value: f32}, + /// convert a color image to an alpha channel - internal use; generated by + /// SVGFilterInstance::GetOrCreateSourceAlphaIndex(). + SVGFEToAlpha, + /// combine 2 images with SVG_FEBLEND_MODE_DARKEN + /// parameters: FilterGraphNode + /// SVG filter semantics - selectable input(s), selectable between linear + /// (default) and sRGB color space for calculations + /// Spec: https://www.w3.org/TR/filter-effects-1/#feBlendElement + SVGFEBlendDarken, + /// combine 2 images with SVG_FEBLEND_MODE_LIGHTEN + /// parameters: FilterGraphNode + /// SVG filter semantics - selectable input(s), selectable between linear + /// (default) and sRGB color space for calculations + /// Spec: https://www.w3.org/TR/filter-effects-1/#feBlendElement + SVGFEBlendLighten, + /// combine 2 images with SVG_FEBLEND_MODE_MULTIPLY + /// parameters: FilterGraphNode + /// SVG filter semantics - selectable input(s), selectable between linear + /// (default) and sRGB color space for calculations + /// Spec: https://www.w3.org/TR/filter-effects-1/#feBlendElement + SVGFEBlendMultiply, + /// combine 2 images with SVG_FEBLEND_MODE_NORMAL + /// parameters: FilterGraphNode + /// SVG filter semantics - selectable input(s), selectable between linear + /// (default) and sRGB color space for calculations + /// Spec: https://www.w3.org/TR/filter-effects-1/#feBlendElement + SVGFEBlendNormal, + /// combine 2 images with SVG_FEBLEND_MODE_SCREEN + /// parameters: FilterGraphNode + /// SVG filter semantics - selectable input(s), selectable between linear + /// (default) and sRGB color space for calculations + /// Spec: https://www.w3.org/TR/filter-effects-1/#feBlendElement + SVGFEBlendScreen, + /// combine 2 images with SVG_FEBLEND_MODE_OVERLAY + /// parameters: FilterOpGraphNode + /// SVG filter semantics - selectable input(s), selectable between linear + /// (default) and sRGB color space for calculations + /// Source: https://developer.mozilla.org/en-US/docs/Web/CSS/mix-blend-mode + SVGFEBlendOverlay, + /// combine 2 images with SVG_FEBLEND_MODE_COLOR_DODGE + /// parameters: FilterOpGraphNode + /// SVG filter semantics - selectable input(s), selectable between linear + /// (default) and sRGB color space for calculations + /// Source: https://developer.mozilla.org/en-US/docs/Web/CSS/mix-blend-mode + SVGFEBlendColorDodge, + /// combine 2 images with SVG_FEBLEND_MODE_COLOR_BURN + /// parameters: FilterOpGraphNode + /// SVG filter semantics - selectable input(s), selectable between linear + /// (default) and sRGB color space for calculations + /// Source: https://developer.mozilla.org/en-US/docs/Web/CSS/mix-blend-mode + SVGFEBlendColorBurn, + /// combine 2 images with SVG_FEBLEND_MODE_HARD_LIGHT + /// parameters: FilterOpGraphNode + /// SVG filter semantics - selectable input(s), selectable between linear + /// (default) and sRGB color space for calculations + /// Source: https://developer.mozilla.org/en-US/docs/Web/CSS/mix-blend-mode + SVGFEBlendHardLight, + /// combine 2 images with SVG_FEBLEND_MODE_SOFT_LIGHT + /// parameters: FilterOpGraphNode + /// SVG filter semantics - selectable input(s), selectable between linear + /// (default) and sRGB color space for calculations + /// Source: https://developer.mozilla.org/en-US/docs/Web/CSS/mix-blend-mode + SVGFEBlendSoftLight, + /// combine 2 images with SVG_FEBLEND_MODE_DIFFERENCE + /// parameters: FilterOpGraphNode + /// SVG filter semantics - selectable input(s), selectable between linear + /// (default) and sRGB color space for calculations + /// Source: https://developer.mozilla.org/en-US/docs/Web/CSS/mix-blend-mode + SVGFEBlendDifference, + /// combine 2 images with SVG_FEBLEND_MODE_EXCLUSION + /// parameters: FilterOpGraphNode + /// SVG filter semantics - selectable input(s), selectable between linear + /// (default) and sRGB color space for calculations + /// Source: https://developer.mozilla.org/en-US/docs/Web/CSS/mix-blend-mode + SVGFEBlendExclusion, + /// combine 2 images with SVG_FEBLEND_MODE_HUE + /// parameters: FilterOpGraphNode + /// SVG filter semantics - selectable input(s), selectable between linear + /// (default) and sRGB color space for calculations + /// Source: https://developer.mozilla.org/en-US/docs/Web/CSS/mix-blend-mode + SVGFEBlendHue, + /// combine 2 images with SVG_FEBLEND_MODE_SATURATION + /// parameters: FilterOpGraphNode + /// SVG filter semantics - selectable input(s), selectable between linear + /// (default) and sRGB color space for calculations + /// Source: https://developer.mozilla.org/en-US/docs/Web/CSS/mix-blend-mode + SVGFEBlendSaturation, + /// combine 2 images with SVG_FEBLEND_MODE_COLOR + /// parameters: FilterOpGraphNode + /// SVG filter semantics - selectable input(s), selectable between linear + /// (default) and sRGB color space for calculations + /// Source: https://developer.mozilla.org/en-US/docs/Web/CSS/mix-blend-mode + SVGFEBlendColor, + /// combine 2 images with SVG_FEBLEND_MODE_LUMINOSITY + /// parameters: FilterOpGraphNode + /// SVG filter semantics - selectable input(s), selectable between linear + /// (default) and sRGB color space for calculations + /// Source: https://developer.mozilla.org/en-US/docs/Web/CSS/mix-blend-mode + SVGFEBlendLuminosity, + /// transform colors of image through 5x4 color matrix (transposed for + /// efficiency) + /// parameters: FilterGraphNode, matrix[5][4] + /// SVG filter semantics - selectable input(s), selectable between linear + /// (default) and sRGB color space for calculations + /// Spec: https://www.w3.org/TR/filter-effects-1/#feColorMatrixElement + SVGFEColorMatrix{values: [f32; 20]}, + /// transform colors of image through configurable gradients with component + /// swizzle + /// parameters: FilterGraphNode + /// SVG filter semantics - selectable input(s), selectable between linear + /// (default) and sRGB color space for calculations + /// Spec: https://www.w3.org/TR/filter-effects-1/#feComponentTransferElement + SVGFEComponentTransfer, + /// Processed version of SVGFEComponentTransfer with the FilterData + /// replaced by an interned handle, this is made in wrap_prim_with_filters. + /// Aside from the interned handle, creates_pixels indicates if the transfer + /// parameters will probably fill the entire subregion with non-zero alpha. + SVGFEComponentTransferInterned{handle: FilterDataHandle, creates_pixels: bool}, + /// composite 2 images with chosen composite mode with parameters for that + /// mode + /// parameters: FilterGraphNode, k1, k2, k3, k4 + /// SVG filter semantics - selectable input(s), selectable between linear + /// (default) and sRGB color space for calculations + /// Spec: https://www.w3.org/TR/filter-effects-1/#feCompositeElement + SVGFECompositeArithmetic{k1: f32, k2: f32, k3: f32, k4: f32}, + /// composite 2 images with chosen composite mode with parameters for that + /// mode + /// parameters: FilterGraphNode + /// SVG filter semantics - selectable input(s), selectable between linear + /// (default) and sRGB color space for calculations + /// Spec: https://www.w3.org/TR/filter-effects-1/#feCompositeElement + SVGFECompositeATop, + /// composite 2 images with chosen composite mode with parameters for that + /// mode + /// parameters: FilterGraphNode + /// SVG filter semantics - selectable input(s), selectable between linear + /// (default) and sRGB color space for calculations + /// Spec: https://www.w3.org/TR/filter-effects-1/#feCompositeElement + SVGFECompositeIn, + /// composite 2 images with chosen composite mode with parameters for that + /// mode + /// parameters: FilterOpGraphNode + /// SVG filter semantics - selectable input(s), selectable between linear + /// (default) and sRGB color space for calculations + /// Docs: https://developer.mozilla.org/en-US/docs/Web/SVG/Element/feComposite + SVGFECompositeLighter, + /// composite 2 images with chosen composite mode with parameters for that + /// mode + /// parameters: FilterGraphNode + /// SVG filter semantics - selectable input(s), selectable between linear + /// (default) and sRGB color space for calculations + /// Spec: https://www.w3.org/TR/filter-effects-1/#feCompositeElement + SVGFECompositeOut, + /// composite 2 images with chosen composite mode with parameters for that + /// mode + /// parameters: FilterGraphNode + /// SVG filter semantics - selectable input(s), selectable between linear + /// (default) and sRGB color space for calculations + /// Spec: https://www.w3.org/TR/filter-effects-1/#feCompositeElement + SVGFECompositeOver, + /// composite 2 images with chosen composite mode with parameters for that + /// mode + /// parameters: FilterGraphNode + /// SVG filter semantics - selectable input(s), selectable between linear + /// (default) and sRGB color space for calculations + /// Spec: https://www.w3.org/TR/filter-effects-1/#feCompositeElement + SVGFECompositeXOR, + /// transform image through convolution matrix of up to 25 values (spec + /// allows more but for performance reasons we do not) + /// parameters: FilterGraphNode, orderX, orderY, kernelValues[25], divisor, + /// bias, targetX, targetY, kernelUnitLengthX, kernelUnitLengthY, + /// preserveAlpha + /// SVG filter semantics - selectable input(s), selectable between linear + /// (default) and sRGB color space for calculations + /// Spec: https://www.w3.org/TR/filter-effects-1/#feConvolveMatrixElement + SVGFEConvolveMatrixEdgeModeDuplicate{order_x: i32, order_y: i32, + kernel: [f32; SVGFE_CONVOLVE_VALUES_LIMIT], divisor: f32, bias: f32, + target_x: i32, target_y: i32, kernel_unit_length_x: f32, + kernel_unit_length_y: f32, preserve_alpha: i32}, + /// transform image through convolution matrix of up to 25 values (spec + /// allows more but for performance reasons we do not) + /// parameters: FilterGraphNode, orderX, orderY, kernelValues[25], divisor, + /// bias, targetX, targetY, kernelUnitLengthX, kernelUnitLengthY, + /// preserveAlpha + /// SVG filter semantics - selectable input(s), selectable between linear + /// (default) and sRGB color space for calculations + /// Spec: https://www.w3.org/TR/filter-effects-1/#feConvolveMatrixElement + SVGFEConvolveMatrixEdgeModeNone{order_x: i32, order_y: i32, + kernel: [f32; SVGFE_CONVOLVE_VALUES_LIMIT], divisor: f32, bias: f32, + target_x: i32, target_y: i32, kernel_unit_length_x: f32, + kernel_unit_length_y: f32, preserve_alpha: i32}, + /// transform image through convolution matrix of up to 25 values (spec + /// allows more but for performance reasons we do not) + /// parameters: FilterGraphNode, orderX, orderY, kernelValues[25], divisor, + /// bias, targetX, targetY, kernelUnitLengthX, kernelUnitLengthY, + /// preserveAlpha + /// SVG filter semantics - selectable input(s), selectable between linear + /// (default) and sRGB color space for calculations + /// Spec: https://www.w3.org/TR/filter-effects-1/#feConvolveMatrixElement + SVGFEConvolveMatrixEdgeModeWrap{order_x: i32, order_y: i32, + kernel: [f32; SVGFE_CONVOLVE_VALUES_LIMIT], divisor: f32, bias: f32, + target_x: i32, target_y: i32, kernel_unit_length_x: f32, + kernel_unit_length_y: f32, preserve_alpha: i32}, + /// calculate lighting based on heightmap image with provided values for a + /// distant light source with specified direction + /// parameters: FilterGraphNode, surfaceScale, diffuseConstant, + /// kernelUnitLengthX, kernelUnitLengthY, azimuth, elevation + /// SVG filter semantics - selectable input(s), selectable between linear + /// (default) and sRGB color space for calculations + /// Spec: https://www.w3.org/TR/filter-effects-1/#InterfaceSVGFEDiffuseLightingElement + /// https://www.w3.org/TR/filter-effects-1/#InterfaceSVGFEDistantLightElement + SVGFEDiffuseLightingDistant{surface_scale: f32, diffuse_constant: f32, + kernel_unit_length_x: f32, kernel_unit_length_y: f32, azimuth: f32, + elevation: f32}, + /// calculate lighting based on heightmap image with provided values for a + /// point light source at specified location + /// parameters: FilterGraphNode, surfaceScale, diffuseConstant, + /// kernelUnitLengthX, kernelUnitLengthY, x, y, z + /// SVG filter semantics - selectable input(s), selectable between linear + /// (default) and sRGB color space for calculations + /// Spec: https://www.w3.org/TR/filter-effects-1/#InterfaceSVGFEDiffuseLightingElement + /// https://www.w3.org/TR/filter-effects-1/#InterfaceSVGFEPointLightElement + SVGFEDiffuseLightingPoint{surface_scale: f32, diffuse_constant: f32, + kernel_unit_length_x: f32, kernel_unit_length_y: f32, x: f32, y: f32, + z: f32}, + /// calculate lighting based on heightmap image with provided values for a + /// spot light source at specified location pointing at specified target + /// location with specified hotspot sharpness and cone angle + /// parameters: FilterGraphNode, surfaceScale, diffuseConstant, + /// kernelUnitLengthX, kernelUnitLengthY, x, y, z, pointsAtX, pointsAtY, + /// pointsAtZ, specularExponent, limitingConeAngle + /// SVG filter semantics - selectable input(s), selectable between linear + /// (default) and sRGB color space for calculations + /// Spec: https://www.w3.org/TR/filter-effects-1/#InterfaceSVGFEDiffuseLightingElement + /// https://www.w3.org/TR/filter-effects-1/#InterfaceSVGFESpotLightElement + SVGFEDiffuseLightingSpot{surface_scale: f32, diffuse_constant: f32, + kernel_unit_length_x: f32, kernel_unit_length_y: f32, x: f32, y: f32, + z: f32, points_at_x: f32, points_at_y: f32, points_at_z: f32, + cone_exponent: f32, limiting_cone_angle: f32}, + /// calculate a distorted version of first input image using offset values + /// from second input image at specified intensity + /// parameters: FilterGraphNode, scale, xChannelSelector, yChannelSelector + /// SVG filter semantics - selectable input(s), selectable between linear + /// (default) and sRGB color space for calculations + /// Spec: https://www.w3.org/TR/filter-effects-1/#InterfaceSVGFEDisplacementMapElement + SVGFEDisplacementMap{scale: f32, x_channel_selector: u32, + y_channel_selector: u32}, + /// create and merge a dropshadow version of the specified image's alpha + /// channel with specified offset and blur radius + /// parameters: FilterGraphNode, flood_color, flood_opacity, dx, dy, + /// stdDeviationX, stdDeviationY + /// SVG filter semantics - selectable input(s), selectable between linear + /// (default) and sRGB color space for calculations + /// Spec: https://www.w3.org/TR/filter-effects-1/#InterfaceSVGFEDropShadowElement + SVGFEDropShadow{color: ColorF, dx: f32, dy: f32, std_deviation_x: f32, + std_deviation_y: f32}, + /// synthesize a new image of specified size containing a solid color + /// parameters: FilterGraphNode, color + /// SVG filter semantics - selectable input(s), selectable between linear + /// (default) and sRGB color space for calculations + /// Spec: https://www.w3.org/TR/filter-effects-1/#InterfaceSVGFEFloodElement + SVGFEFlood{color: ColorF}, + /// create a blurred version of the input image + /// parameters: FilterGraphNode, stdDeviationX, stdDeviationY + /// SVG filter semantics - selectable input(s), selectable between linear + /// (default) and sRGB color space for calculations + /// Spec: https://www.w3.org/TR/filter-effects-1/#InterfaceSVGFEGaussianBlurElement + SVGFEGaussianBlur{std_deviation_x: f32, std_deviation_y: f32}, + /// synthesize a new image based on a url (i.e. blob image source) + /// parameters: FilterGraphNode, + /// samplingFilter (see SamplingFilter in Types.h), transform + /// SVG filter semantics - selectable input(s), selectable between linear + /// (default) and sRGB color space for calculations + /// Spec: https://www.w3.org/TR/filter-effects-1/#InterfaceSVGFEImageElement + SVGFEImage{sampling_filter: u32, matrix: [f32; 6]}, + /// create a new image based on the input image with the contour stretched + /// outward (dilate operator) + /// parameters: FilterGraphNode, radiusX, radiusY + /// SVG filter semantics - selectable input(s), selectable between linear + /// (default) and sRGB color space for calculations + /// Spec: https://www.w3.org/TR/filter-effects-1/#InterfaceSVGFEMorphologyElement + SVGFEMorphologyDilate{radius_x: f32, radius_y: f32}, + /// create a new image based on the input image with the contour shrunken + /// inward (erode operator) + /// parameters: FilterGraphNode, radiusX, radiusY + /// SVG filter semantics - selectable input(s), selectable between linear + /// (default) and sRGB color space for calculations + /// Spec: https://www.w3.org/TR/filter-effects-1/#InterfaceSVGFEMorphologyElement + SVGFEMorphologyErode{radius_x: f32, radius_y: f32}, + /// calculate lighting based on heightmap image with provided values for a + /// distant light source with specified direction + /// parameters: FilerData, surfaceScale, specularConstant, specularExponent, + /// kernelUnitLengthX, kernelUnitLengthY, azimuth, elevation + /// SVG filter semantics - selectable input(s), selectable between linear + /// (default) and sRGB color space for calculations + /// Spec: https://www.w3.org/TR/filter-effects-1/#InterfaceSVGFESpecularLightingElement + /// https://www.w3.org/TR/filter-effects-1/#InterfaceSVGFEDistantLightElement + SVGFESpecularLightingDistant{surface_scale: f32, specular_constant: f32, + specular_exponent: f32, kernel_unit_length_x: f32, + kernel_unit_length_y: f32, azimuth: f32, elevation: f32}, + /// calculate lighting based on heightmap image with provided values for a + /// point light source at specified location + /// parameters: FilterGraphNode, surfaceScale, specularConstant, + /// specularExponent, kernelUnitLengthX, kernelUnitLengthY, x, y, z + /// SVG filter semantics - selectable input(s), selectable between linear + /// (default) and sRGB color space for calculations + /// Spec: https://www.w3.org/TR/filter-effects-1/#InterfaceSVGFESpecularLightingElement + /// https://www.w3.org/TR/filter-effects-1/#InterfaceSVGFEPointLightElement + SVGFESpecularLightingPoint{surface_scale: f32, specular_constant: f32, + specular_exponent: f32, kernel_unit_length_x: f32, + kernel_unit_length_y: f32, x: f32, y: f32, z: f32}, + /// calculate lighting based on heightmap image with provided values for a + /// spot light source at specified location pointing at specified target + /// location with specified hotspot sharpness and cone angle + /// parameters: FilterGraphNode, surfaceScale, specularConstant, + /// specularExponent, kernelUnitLengthX, kernelUnitLengthY, x, y, z, + /// pointsAtX, pointsAtY, pointsAtZ, specularExponent, limitingConeAngle + /// SVG filter semantics - selectable input(s), selectable between linear + /// (default) and sRGB color space for calculations + /// Spec: https://www.w3.org/TR/filter-effects-1/#InterfaceSVGFESpecularLightingElement + /// https://www.w3.org/TR/filter-effects-1/#InterfaceSVGFESpotLightElement + SVGFESpecularLightingSpot{surface_scale: f32, specular_constant: f32, + specular_exponent: f32, kernel_unit_length_x: f32, + kernel_unit_length_y: f32, x: f32, y: f32, z: f32, points_at_x: f32, + points_at_y: f32, points_at_z: f32, cone_exponent: f32, + limiting_cone_angle: f32}, + /// create a new image based on the input image, repeated throughout the + /// output rectangle + /// parameters: FilterGraphNode + /// SVG filter semantics - selectable input(s), selectable between linear + /// (default) and sRGB color space for calculations + /// Spec: https://www.w3.org/TR/filter-effects-1/#InterfaceSVGFETileElement + SVGFETile, + /// synthesize a new image based on Fractal Noise (Perlin) with the chosen + /// stitching mode + /// parameters: FilterGraphNode, baseFrequencyX, baseFrequencyY, numOctaves, + /// seed + /// SVG filter semantics - selectable input(s), selectable between linear + /// (default) and sRGB color space for calculations + /// Spec: https://www.w3.org/TR/filter-effects-1/#InterfaceSVGFETurbulenceElement + SVGFETurbulenceWithFractalNoiseWithNoStitching{base_frequency_x: f32, + base_frequency_y: f32, num_octaves: u32, seed: u32}, + /// synthesize a new image based on Fractal Noise (Perlin) with the chosen + /// stitching mode + /// parameters: FilterGraphNode, baseFrequencyX, baseFrequencyY, numOctaves, + /// seed + /// SVG filter semantics - selectable input(s), selectable between linear + /// (default) and sRGB color space for calculations + /// Spec: https://www.w3.org/TR/filter-effects-1/#InterfaceSVGFETurbulenceElement + SVGFETurbulenceWithFractalNoiseWithStitching{base_frequency_x: f32, + base_frequency_y: f32, num_octaves: u32, seed: u32}, + /// synthesize a new image based on Turbulence Noise (offset vectors) + /// parameters: FilterGraphNode, baseFrequencyX, baseFrequencyY, numOctaves, + /// seed + /// SVG filter semantics - selectable input(s), selectable between linear + /// (default) and sRGB color space for calculations + /// Spec: https://www.w3.org/TR/filter-effects-1/#InterfaceSVGFETurbulenceElement + SVGFETurbulenceWithTurbulenceNoiseWithNoStitching{base_frequency_x: f32, + base_frequency_y: f32, num_octaves: u32, seed: u32}, + /// synthesize a new image based on Turbulence Noise (offset vectors) + /// parameters: FilterGraphNode, baseFrequencyX, baseFrequencyY, numOctaves, + /// seed + /// SVG filter semantics - selectable input(s), selectable between linear + /// (default) and sRGB color space for calculations + /// Spec: https://www.w3.org/TR/filter-effects-1/#InterfaceSVGFETurbulenceElement + SVGFETurbulenceWithTurbulenceNoiseWithStitching{base_frequency_x: f32, + base_frequency_y: f32, num_octaves: u32, seed: u32}, +} + +impl FilterGraphOp { + pub fn kind(&self) -> &'static str { + match *self { + FilterGraphOp::SVGFEBlendColor => "SVGFEBlendColor", + FilterGraphOp::SVGFEBlendColorBurn => "SVGFEBlendColorBurn", + FilterGraphOp::SVGFEBlendColorDodge => "SVGFEBlendColorDodge", + FilterGraphOp::SVGFEBlendDarken => "SVGFEBlendDarken", + FilterGraphOp::SVGFEBlendDifference => "SVGFEBlendDifference", + FilterGraphOp::SVGFEBlendExclusion => "SVGFEBlendExclusion", + FilterGraphOp::SVGFEBlendHardLight => "SVGFEBlendHardLight", + FilterGraphOp::SVGFEBlendHue => "SVGFEBlendHue", + FilterGraphOp::SVGFEBlendLighten => "SVGFEBlendLighten", + FilterGraphOp::SVGFEBlendLuminosity => "SVGFEBlendLuminosity", + FilterGraphOp::SVGFEBlendMultiply => "SVGFEBlendMultiply", + FilterGraphOp::SVGFEBlendNormal => "SVGFEBlendNormal", + FilterGraphOp::SVGFEBlendOverlay => "SVGFEBlendOverlay", + FilterGraphOp::SVGFEBlendSaturation => "SVGFEBlendSaturation", + FilterGraphOp::SVGFEBlendScreen => "SVGFEBlendScreen", + FilterGraphOp::SVGFEBlendSoftLight => "SVGFEBlendSoftLight", + FilterGraphOp::SVGFEColorMatrix{..} => "SVGFEColorMatrix", + FilterGraphOp::SVGFEComponentTransfer => "SVGFEComponentTransfer", + FilterGraphOp::SVGFEComponentTransferInterned{..} => "SVGFEComponentTransferInterned", + FilterGraphOp::SVGFECompositeArithmetic{..} => "SVGFECompositeArithmetic", + FilterGraphOp::SVGFECompositeATop => "SVGFECompositeATop", + FilterGraphOp::SVGFECompositeIn => "SVGFECompositeIn", + FilterGraphOp::SVGFECompositeLighter => "SVGFECompositeLighter", + FilterGraphOp::SVGFECompositeOut => "SVGFECompositeOut", + FilterGraphOp::SVGFECompositeOver => "SVGFECompositeOver", + FilterGraphOp::SVGFECompositeXOR => "SVGFECompositeXOR", + FilterGraphOp::SVGFEConvolveMatrixEdgeModeDuplicate{..} => "SVGFEConvolveMatrixEdgeModeDuplicate", + FilterGraphOp::SVGFEConvolveMatrixEdgeModeNone{..} => "SVGFEConvolveMatrixEdgeModeNone", + FilterGraphOp::SVGFEConvolveMatrixEdgeModeWrap{..} => "SVGFEConvolveMatrixEdgeModeWrap", + FilterGraphOp::SVGFEDiffuseLightingDistant{..} => "SVGFEDiffuseLightingDistant", + FilterGraphOp::SVGFEDiffuseLightingPoint{..} => "SVGFEDiffuseLightingPoint", + FilterGraphOp::SVGFEDiffuseLightingSpot{..} => "SVGFEDiffuseLightingSpot", + FilterGraphOp::SVGFEDisplacementMap{..} => "SVGFEDisplacementMap", + FilterGraphOp::SVGFEDropShadow{..} => "SVGFEDropShadow", + FilterGraphOp::SVGFEFlood{..} => "SVGFEFlood", + FilterGraphOp::SVGFEGaussianBlur{..} => "SVGFEGaussianBlur", + FilterGraphOp::SVGFEIdentity => "SVGFEIdentity", + FilterGraphOp::SVGFEImage{..} => "SVGFEImage", + FilterGraphOp::SVGFEMorphologyDilate{..} => "SVGFEMorphologyDilate", + FilterGraphOp::SVGFEMorphologyErode{..} => "SVGFEMorphologyErode", + FilterGraphOp::SVGFEOpacity{..} => "SVGFEOpacity", + FilterGraphOp::SVGFESourceAlpha => "SVGFESourceAlpha", + FilterGraphOp::SVGFESourceGraphic => "SVGFESourceGraphic", + FilterGraphOp::SVGFESpecularLightingDistant{..} => "SVGFESpecularLightingDistant", + FilterGraphOp::SVGFESpecularLightingPoint{..} => "SVGFESpecularLightingPoint", + FilterGraphOp::SVGFESpecularLightingSpot{..} => "SVGFESpecularLightingSpot", + FilterGraphOp::SVGFETile => "SVGFETile", + FilterGraphOp::SVGFEToAlpha => "SVGFEToAlpha", + FilterGraphOp::SVGFETurbulenceWithFractalNoiseWithNoStitching{..} => "SVGFETurbulenceWithFractalNoiseWithNoStitching", + FilterGraphOp::SVGFETurbulenceWithFractalNoiseWithStitching{..} => "SVGFETurbulenceWithFractalNoiseWithStitching", + FilterGraphOp::SVGFETurbulenceWithTurbulenceNoiseWithNoStitching{..} => "SVGFETurbulenceWithTurbulenceNoiseWithNoStitching", + FilterGraphOp::SVGFETurbulenceWithTurbulenceNoiseWithStitching{..} => "SVGFETurbulenceWithTurbulenceNoiseWithStitching", + } + } +} + +#[derive(Clone, Debug)] +#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "replay", derive(Deserialize))] +pub struct FilterGraphNode { + /// Indicates this graph node was marked as necessary by the DAG optimizer + pub kept_by_optimizer: bool, + /// true if color_interpolation_filter == LinearRgb; shader will convert + /// sRGB texture pixel colors on load and convert back on store, for correct + /// interpolation + pub linear: bool, + /// padding for output rect if we need a border to get correct clamping, or + /// to account for larger final subregion than source rect (see bug 1869672) + pub inflate: i16, + /// virtualized picture input bindings, these refer to other filter outputs + /// by number within the graph, usually there is one element + pub inputs: Vec<FilterGraphPictureReference>, + /// clipping rect for filter node output + pub subregion: LayoutRect, +} + +impl From<FilterOpGraphNode> for FilterGraphNode { + fn from(node: FilterOpGraphNode) -> Self { + let mut inputs: Vec<FilterGraphPictureReference> = Vec::new(); + if node.input.buffer_id != FilterOpGraphPictureBufferId::None { + inputs.push(node.input.into()); + } + if node.input2.buffer_id != FilterOpGraphPictureBufferId::None { + inputs.push(node.input2.into()); + } + // If the op used by this node is a feMerge, it will add more inputs + // after this invocation. + FilterGraphNode{ + linear: node.linear, + inputs, + subregion: node.subregion, + // These are computed later in scene_building + kept_by_optimizer: true, + inflate: 0, + } + } +} + +/// Here we transform source rect to target rect for SVGFEGraph by walking +/// the whole graph and propagating subregions based on the provided +/// invalidation rect, and we want it to be a tight fit so we don't waste +/// time applying multiple filters to pixels that do not contribute to the +/// invalidated rect. +/// +/// The interesting parts of the handling of SVG filters are: +/// * scene_building.rs : wrap_prim_with_filters +/// * picture.rs : get_coverage_target_svgfe (you are here) +/// * picture.rs : get_coverage_source_svgfe +/// * render_task.rs : new_svg_filter_graph +/// * render_target.rs : add_svg_filter_node_instances +pub fn get_coverage_target_svgfe( + filters: &[(FilterGraphNode, FilterGraphOp)], + surface_rect: LayoutRect, +) -> LayoutRect { + + // The value of BUFFER_LIMIT here must be the same as in + // scene_building.rs, or we'll hit asserts here. + const BUFFER_LIMIT: usize = SVGFE_GRAPH_MAX; + + // We need to evaluate the subregions based on the proposed + // SourceGraphic rect as it isn't known at scene build time. + let mut subregion_by_buffer_id: [LayoutRect; BUFFER_LIMIT] = [LayoutRect::zero(); BUFFER_LIMIT]; + for (id, (node, op)) in filters.iter().enumerate() { + let full_subregion = node.subregion; + let mut used_subregion = LayoutRect::zero(); + for input in &node.inputs { + match input.buffer_id { + FilterOpGraphPictureBufferId::BufferId(id) => { + assert!((id as usize) < BUFFER_LIMIT, "BUFFER_LIMIT must be the same in frame building and scene building"); + // This id lookup should always succeed. + let input_subregion = subregion_by_buffer_id[id as usize]; + // Now add the padding that transforms from + // source to target, this was determined during + // scene build based on the operation. + let input_subregion = + LayoutRect::new( + LayoutPoint::new( + input_subregion.min.x + input.target_padding.min.x, + input_subregion.min.y + input.target_padding.min.y, + ), + LayoutPoint::new( + input_subregion.max.x + input.target_padding.max.x, + input_subregion.max.y + input.target_padding.max.y, + ), + ); + used_subregion = used_subregion + .union(&input_subregion); + } + FilterOpGraphPictureBufferId::None => { + panic!("Unsupported BufferId type"); + } + } + } + // We can clip the used subregion to the node subregion + used_subregion = used_subregion + .intersection(&full_subregion) + .unwrap_or(LayoutRect::zero()); + match op { + FilterGraphOp::SVGFEBlendColor => {} + FilterGraphOp::SVGFEBlendColorBurn => {} + FilterGraphOp::SVGFEBlendColorDodge => {} + FilterGraphOp::SVGFEBlendDarken => {} + FilterGraphOp::SVGFEBlendDifference => {} + FilterGraphOp::SVGFEBlendExclusion => {} + FilterGraphOp::SVGFEBlendHardLight => {} + FilterGraphOp::SVGFEBlendHue => {} + FilterGraphOp::SVGFEBlendLighten => {} + FilterGraphOp::SVGFEBlendLuminosity => {} + FilterGraphOp::SVGFEBlendMultiply => {} + FilterGraphOp::SVGFEBlendNormal => {} + FilterGraphOp::SVGFEBlendOverlay => {} + FilterGraphOp::SVGFEBlendSaturation => {} + FilterGraphOp::SVGFEBlendScreen => {} + FilterGraphOp::SVGFEBlendSoftLight => {} + FilterGraphOp::SVGFEColorMatrix { values } => { + if values[19] > 0.0 { + // Manipulating alpha offset can easily create new + // pixels outside of input subregions + used_subregion = full_subregion; + add_text_marker( + "SVGFEColorMatrix", + "SVGFEColorMatrix with non-zero alpha offset, using full subregion", + Duration::from_millis(1)); + } + } + FilterGraphOp::SVGFEComponentTransfer => unreachable!(), + FilterGraphOp::SVGFEComponentTransferInterned{handle: _, creates_pixels} => { + // Check if the value of alpha[0] is modified, if so + // the whole subregion is used because it will be + // creating new pixels outside of input subregions + if *creates_pixels { + used_subregion = full_subregion; + add_text_marker( + "SVGFEComponentTransfer", + "SVGFEComponentTransfer with non-zero minimum alpha, using full subregion", + Duration::from_millis(1)); + } + } + FilterGraphOp::SVGFECompositeArithmetic { k1, k2, k3, k4 } => { + // Optimization opportunity - some inputs may be + // smaller subregions due to the way the math works, + // k1 is the intersection of the two inputs, k2 is + // the first input only, k3 is the second input + // only, and k4 changes the whole subregion. + // + // See logic for SVG_FECOMPOSITE_OPERATOR_ARITHMETIC + // in FilterSupport.cpp + // + // We can at least ignore the entire node if + // everything is zero. + if *k1 <= 0.0 && + *k2 <= 0.0 && + *k3 <= 0.0 { + used_subregion = LayoutRect::zero(); + } + // Check if alpha is added to pixels as it means it + // can fill pixels outside input subregions + if *k4 > 0.0 { + used_subregion = full_subregion; + add_text_marker( + "SVGFECompositeArithmetic", + "SVGFECompositeArithmetic with non-zero offset, using full subregion", + Duration::from_millis(1)); + } + } + FilterGraphOp::SVGFECompositeATop => {} + FilterGraphOp::SVGFECompositeIn => {} + FilterGraphOp::SVGFECompositeLighter => {} + FilterGraphOp::SVGFECompositeOut => {} + FilterGraphOp::SVGFECompositeOver => {} + FilterGraphOp::SVGFECompositeXOR => {} + FilterGraphOp::SVGFEConvolveMatrixEdgeModeDuplicate{..} => {} + FilterGraphOp::SVGFEConvolveMatrixEdgeModeNone{..} => {} + FilterGraphOp::SVGFEConvolveMatrixEdgeModeWrap{..} => {} + FilterGraphOp::SVGFEDiffuseLightingDistant{..} => {} + FilterGraphOp::SVGFEDiffuseLightingPoint{..} => {} + FilterGraphOp::SVGFEDiffuseLightingSpot{..} => {} + FilterGraphOp::SVGFEDisplacementMap{..} => {} + FilterGraphOp::SVGFEDropShadow{..} => {} + FilterGraphOp::SVGFEFlood { color } => { + // Subregion needs to be set to the full node + // subregion for fills (unless the fill is a no-op) + if color.a > 0.0 { + used_subregion = full_subregion; + } + } + FilterGraphOp::SVGFEGaussianBlur{..} => {} + FilterGraphOp::SVGFEIdentity => {} + FilterGraphOp::SVGFEImage { sampling_filter: _sampling_filter, matrix: _matrix } => { + // TODO: calculate the actual subregion + used_subregion = full_subregion; + } + FilterGraphOp::SVGFEMorphologyDilate{..} => {} + FilterGraphOp::SVGFEMorphologyErode{..} => {} + FilterGraphOp::SVGFEOpacity { valuebinding: _valuebinding, value } => { + // If fully transparent, we can ignore this node + if *value <= 0.0 { + used_subregion = LayoutRect::zero(); + } + } + FilterGraphOp::SVGFESourceAlpha | + FilterGraphOp::SVGFESourceGraphic => { + used_subregion = surface_rect; + } + FilterGraphOp::SVGFESpecularLightingDistant{..} => {} + FilterGraphOp::SVGFESpecularLightingPoint{..} => {} + FilterGraphOp::SVGFESpecularLightingSpot{..} => {} + FilterGraphOp::SVGFETile => { + // feTile fills the entire output with + // source pixels, so it's effectively a flood. + used_subregion = full_subregion; + } + FilterGraphOp::SVGFEToAlpha => {} + FilterGraphOp::SVGFETurbulenceWithFractalNoiseWithNoStitching{..} | + FilterGraphOp::SVGFETurbulenceWithFractalNoiseWithStitching{..} | + FilterGraphOp::SVGFETurbulenceWithTurbulenceNoiseWithNoStitching{..} | + FilterGraphOp::SVGFETurbulenceWithTurbulenceNoiseWithStitching{..} => { + // Turbulence produces pixel values throughout the + // node subregion. + used_subregion = full_subregion; + } + } + // Store the subregion so later nodes can refer back + // to this and propagate rects properly + assert!((id as usize) < BUFFER_LIMIT, "BUFFER_LIMIT must be the same in frame building and scene building"); + subregion_by_buffer_id[id] = used_subregion; + } + subregion_by_buffer_id[filters.len() - 1] +} + +/// Here we transform target rect to source rect for SVGFEGraph by walking +/// the whole graph and propagating subregions based on the provided +/// invalidation rect, and we want it to be a tight fit so we don't waste +/// time applying multiple filters to pixels that do not contribute to the +/// invalidated rect. +/// +/// The interesting parts of the handling of SVG filters are: +/// * scene_building.rs : wrap_prim_with_filters +/// * picture.rs : get_coverage_target_svgfe +/// * picture.rs : get_coverage_source_svgfe (you are here) +/// * render_task.rs : new_svg_filter_graph +/// * render_target.rs : add_svg_filter_node_instances +pub fn get_coverage_source_svgfe( + filters: &[(FilterGraphNode, FilterGraphOp)], + surface_rect: LayoutRect, +) -> LayoutRect { + + // The value of BUFFER_LIMIT here must be the same as in + // scene_building.rs, or we'll hit asserts here. + const BUFFER_LIMIT: usize = SVGFE_GRAPH_MAX; + + // We're solving the source rect from target rect (e.g. due + // to invalidation of a region, we need to know how much of + // SourceGraphic is needed to draw that region accurately), + // so we need to walk the DAG in reverse and accumulate the source + // subregion for each input onto the referenced node, which can then + // propagate that to its inputs when it is iterated. + let mut source_subregion = LayoutRect::zero(); + let mut subregion_by_buffer_id: [LayoutRect; BUFFER_LIMIT] = + [LayoutRect::zero(); BUFFER_LIMIT]; + let final_buffer_id = filters.len() - 1; + assert!(final_buffer_id < BUFFER_LIMIT, "BUFFER_LIMIT must be the same in frame building and scene building"); + subregion_by_buffer_id[final_buffer_id] = surface_rect; + for (node_buffer_id, (node, op)) in filters.iter().enumerate().rev() { + // This is the subregion this node outputs, we can clip + // the inputs based on source_padding relative to this, + // and accumulate a new subregion for them. + assert!(node_buffer_id < BUFFER_LIMIT, "BUFFER_LIMIT must be the same in frame building and scene building"); + let full_subregion = node.subregion; + let mut used_subregion = + subregion_by_buffer_id[node_buffer_id]; + // We can clip the propagated subregion to the node subregion before + // we add source_padding for each input and propogate to them + used_subregion = used_subregion + .intersection(&full_subregion) + .unwrap_or(LayoutRect::zero()); + if !used_subregion.is_empty() { + for input in &node.inputs { + let input_subregion = LayoutRect::new( + LayoutPoint::new( + used_subregion.min.x + input.source_padding.min.x, + used_subregion.min.y + input.source_padding.min.y, + ), + LayoutPoint::new( + used_subregion.max.x + input.source_padding.max.x, + used_subregion.max.y + input.source_padding.max.y, + ), + ); + match input.buffer_id { + FilterOpGraphPictureBufferId::BufferId(id) => { + // Add the used area to the input, later when + // the referneced node is iterated as a node it + // will propagate the used bounds. + subregion_by_buffer_id[id as usize] = + subregion_by_buffer_id[id as usize] + .union(&input_subregion); + } + FilterOpGraphPictureBufferId::None => {} + } + } + } + // If this is the SourceGraphic or SourceAlpha, we now have the + // source subregion we're looking for. If both exist in the + // same graph, we need to combine them, so don't merely replace. + match op { + FilterGraphOp::SVGFESourceAlpha | + FilterGraphOp::SVGFESourceGraphic => { + source_subregion = source_subregion.union(&used_subregion); + } + _ => {} + } + } + + // Note that this can be zero if SourceGraphic/SourceAlpha is not used + // in this graph. + source_subregion +}