Bug 1573649 - Implement feOffset SVG filter primitive in WebRender r=nical
☠☠ backed out by c9f306364c7d ☠ ☠
authorConnor Brewster <cbrewster@mozilla.com>
Wed, 14 Aug 2019 14:37:05 +0000
changeset 487959 71b7ef6e0ed8b224e2715f894887587b1a80b34e
parent 487958 186c61bb0efffb6a8c23752b69675cd922bde64f
child 487960 bd44d2dd0c02c1e2ab10e73695ee3ef1da971aa6
push id36434
push usercbrindusan@mozilla.com
push dateThu, 15 Aug 2019 09:44:30 +0000
treeherdermozilla-central@144fbfb409b7 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersnical
bugs1573649
milestone70.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1573649 - Implement feOffset SVG filter primitive in WebRender r=nical Differential Revision: https://phabricator.services.mozilla.com/D41840
gfx/wr/webrender/src/picture.rs
gfx/wr/webrender/src/prim_store/picture.rs
gfx/wr/webrender/src/render_task.rs
gfx/wr/webrender_api/src/display_item.rs
gfx/wr/wrench/reftests/filters/reftest.list
gfx/wr/wrench/reftests/filters/svg-filter-offset-ref.yaml
gfx/wr/wrench/reftests/filters/svg-filter-offset.yaml
gfx/wr/wrench/src/yaml_frame_writer.rs
gfx/wr/wrench/src/yaml_helper.rs
--- a/gfx/wr/webrender/src/picture.rs
+++ b/gfx/wr/webrender/src/picture.rs
@@ -1959,16 +1959,20 @@ impl PictureCompositeMode {
                         FilterPrimitiveKind::Identity(ref primitive) =>
                             primitive.input.to_index(cur_index).map(|index| output_rects[index]).unwrap_or(picture_rect),
                         FilterPrimitiveKind::Opacity(ref primitive) =>
                             primitive.input.to_index(cur_index).map(|index| output_rects[index]).unwrap_or(picture_rect),
                         FilterPrimitiveKind::ColorMatrix(ref primitive) =>
                             primitive.input.to_index(cur_index).map(|index| output_rects[index]).unwrap_or(picture_rect),
                         FilterPrimitiveKind::ComponentTransfer(ref primitive) =>
                             primitive.input.to_index(cur_index).map(|index| output_rects[index]).unwrap_or(picture_rect),
+                        FilterPrimitiveKind::Offset(ref primitive) => {
+                            let input_rect = primitive.input.to_index(cur_index).map(|index| output_rects[index]).unwrap_or(picture_rect);
+                            input_rect.translate(primitive.offset * Scale::new(1.0))
+                        },
 
                         FilterPrimitiveKind::Flood(..) => picture_rect,
                     };
                     output_rects.push(output_rect);
                     result_rect = result_rect.union(&output_rect);
                 }
             }
             _ => {},
--- a/gfx/wr/webrender/src/prim_store/picture.rs
+++ b/gfx/wr/webrender/src/prim_store/picture.rs
@@ -26,16 +26,17 @@ pub enum FilterPrimitiveKey {
     Identity(ColorSpace, FilterPrimitiveInput),
     Flood(ColorSpace, ColorU),
     Blend(ColorSpace, MixBlendMode, FilterPrimitiveInput, FilterPrimitiveInput),
     Blur(ColorSpace, Au, FilterPrimitiveInput),
     Opacity(ColorSpace, Au, FilterPrimitiveInput),
     ColorMatrix(ColorSpace, [Au; 20], FilterPrimitiveInput),
     DropShadow(ColorSpace, (VectorKey, Au, ColorU), FilterPrimitiveInput),
     ComponentTransfer(ColorSpace, FilterPrimitiveInput, Vec<SFilterData>),
+    Offset(ColorSpace, FilterPrimitiveInput, VectorKey),
 }
 
 /// Represents a hashable description of how a picture primitive
 /// will be composited into its parent.
 #[cfg_attr(feature = "capture", derive(Serialize))]
 #[cfg_attr(feature = "replay", derive(Deserialize))]
 #[derive(Debug, Clone, MallocSizeOf, PartialEq, Hash, Eq)]
 pub enum PictureCompositeKey {
@@ -170,16 +171,18 @@ impl From<Option<PictureCompositeMode>> 
                                     Au::from_f32_px(drop_shadow.shadow.blur_radius),
                                     drop_shadow.shadow.color.into(),
                                 ),
                                 drop_shadow.input,
                             )
                         }
                         FilterPrimitiveKind::ComponentTransfer(component_transfer) =>
                             FilterPrimitiveKey::ComponentTransfer(primitive.color_space, component_transfer.input, filter_data.clone()),
+                        FilterPrimitiveKind::Offset(info) =>
+                            FilterPrimitiveKey::Offset(primitive.color_space, info.input, info.offset.into()),
                     }
                 }).collect())
             }
             Some(PictureCompositeMode::Blit(_)) |
             Some(PictureCompositeMode::TileCache { .. }) |
             None => {
                 PictureCompositeKey::Identity
             }
--- a/gfx/wr/webrender/src/render_task.rs
+++ b/gfx/wr/webrender/src/render_task.rs
@@ -1462,16 +1462,36 @@ impl RenderTask {
                             vec![input_task_id],
                             content_size,
                             uv_rect_kind,
                             SvgFilterInfo::ComponentTransfer(filter_data.clone()),
                         );
                         render_tasks.add(task)
                     }
                 }
+                FilterPrimitiveKind::Offset(ref info) => {
+                    let input_task_id = get_task_input(
+                        &info.input,
+                        filter_primitives,
+                        render_tasks,
+                        cur_index,
+                        &outputs,
+                        original_task_id,
+                        primitive.color_space
+                    );
+
+                    let offset = info.offset * LayoutToWorldScale::new(1.0) * device_pixel_scale;
+                    let offset_task = RenderTask::new_svg_filter_primitive(
+                        vec![input_task_id],
+                        content_size,
+                        uv_rect_kind,
+                        SvgFilterInfo::Offset(offset),
+                    );
+                    render_tasks.add(offset_task)
+                }
             };
             outputs.push(render_task_id);
         }
 
         // The output of a filter is the output of the last primitive in the chain.
         let mut render_task_id = *outputs.last().unwrap();
 
         // Convert to sRGB if needed
--- a/gfx/wr/webrender_api/src/display_item.rs
+++ b/gfx/wr/webrender_api/src/display_item.rs
@@ -832,31 +832,39 @@ pub struct ComponentTransferPrimitive {
 }
 
 #[repr(C)]
 #[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)]
 pub struct IdentityPrimitive {
     pub input: FilterPrimitiveInput,
 }
 
+#[repr(C)]
+#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)]
+pub struct OffsetPrimitive {
+    pub input: FilterPrimitiveInput,
+    pub offset: LayoutVector2D,
+}
+
 /// See: https://github.com/eqrion/cbindgen/issues/9
 /// cbindgen:derive-eq=false
 #[repr(C)]
 #[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize, PeekPoke)]
 pub enum FilterPrimitiveKind {
     Identity(IdentityPrimitive),
     Blend(BlendPrimitive),
     Flood(FloodPrimitive),
     Blur(BlurPrimitive),
     // TODO: Support animated opacity?
     Opacity(OpacityPrimitive),
     /// cbindgen:derive-eq=false
     ColorMatrix(ColorMatrixPrimitive),
     DropShadow(DropShadowPrimitive),
     ComponentTransfer(ComponentTransferPrimitive),
+    Offset(OffsetPrimitive),
 }
 
 impl Default for FilterPrimitiveKind {
     fn default() -> Self {
         FilterPrimitiveKind::Identity(IdentityPrimitive::default())
     }
 }
 
@@ -867,16 +875,17 @@ impl FilterPrimitiveKind {
             FilterPrimitiveKind::Blur(blur) => blur.sanitize(),
             FilterPrimitiveKind::Opacity(opacity) => opacity.sanitize(),
             FilterPrimitiveKind::DropShadow(drop_shadow) => drop_shadow.sanitize(),
 
             // No sanitization needed.
             FilterPrimitiveKind::Identity(..) |
             FilterPrimitiveKind::Blend(..) |
             FilterPrimitiveKind::ColorMatrix(..) |
+            FilterPrimitiveKind::Offset(..) |
             // Component transfer's filter data is sanitized separately.
             FilterPrimitiveKind::ComponentTransfer(..) => {}
         }
     }
 }
 
 /// SVG Filter Primitive.
 /// See: https://github.com/eqrion/cbindgen/issues/9
--- a/gfx/wr/wrench/reftests/filters/reftest.list
+++ b/gfx/wr/wrench/reftests/filters/reftest.list
@@ -51,10 +51,11 @@ skip_on(android) == svg-filter-blend.yam
 skip_on(android,device) == svg-filter-color-matrix.yaml filter-color-matrix-ref.yaml  # fails on Pixel2
 platform(linux,mac) == draw_calls(8) color_targets(8) alpha_targets(0) svg-filter-blur.yaml filter-blur.png # Extra draw call is due to render task graph workaround
 platform(linux,mac) == svg-filter-drop-shadow.yaml svg-filter-drop-shadow.png
 == fuzzy(1,10000) svg-srgb-to-linear.yaml srgb-to-linear-ref.yaml
 platform(linux,mac) == fuzzy(4,28250) svg-filter-drop-shadow-rotate.yaml svg-filter-drop-shadow-rotate-ref.yaml
 platform(linux,mac) == svg-filter-blur-transforms.yaml svg-filter-blur-transforms.png
 platform(linux,mac) == svg-filter-drop-shadow-on-viewport-edge.yaml svg-filter-drop-shadow-on-viewport-edge.png
 platform(linux,mac) == svg-filter-drop-shadow-perspective.yaml svg-filter-drop-shadow-perspective.png
+== svg-filter-offset.yaml svg-filter-offset-ref.yaml
 == backdrop-filter-basic.yaml backdrop-filter-basic-ref.yaml
 platform(linux,mac) == backdrop-filter-perspective.yaml backdrop-filter-perspective.png
new file mode 100644
--- /dev/null
+++ b/gfx/wr/wrench/reftests/filters/svg-filter-offset-ref.yaml
@@ -0,0 +1,11 @@
+# Tests the SVG offset filter primitive
+# An offset filter should have the same effect as changing the origin of the rectangle.
+---
+root:
+  items:
+    - type: stacking-context
+      bounds: 0 0 0 0
+      items:
+      - type: rect
+        bounds: 20 20 100 100
+        color: red
new file mode 100644
--- /dev/null
+++ b/gfx/wr/wrench/reftests/filters/svg-filter-offset.yaml
@@ -0,0 +1,15 @@
+# Tests the SVG offset filter primitive
+# An offset filter should have the same effect as changing the origin of the rectangle.
+---
+root:
+  items:
+    - type: stacking-context
+      bounds: 0 0 0 0
+      filter-primitives:
+      - type: offset
+        offset: 10 10
+        in: original
+      items:
+      - type: rect
+        bounds: 10 10 100 100
+        color: red
--- a/gfx/wr/wrench/src/yaml_frame_writer.rs
+++ b/gfx/wr/wrench/src/yaml_frame_writer.rs
@@ -394,16 +394,21 @@ fn write_filter_primitives(
                 vector_node(&mut table, "offset", &drop_shadow_primitive.shadow.offset);
                 color_node(&mut table, "color", drop_shadow_primitive.shadow.color);
                 f32_node(&mut table, "radius", drop_shadow_primitive.shadow.blur_radius);
             }
             FilterPrimitiveKind::ComponentTransfer(component_transfer_primitive) => {
                 yaml_node(&mut table, "type", Yaml::String("component-transfer".into()));
                 filter_input_node(&mut table, "in", component_transfer_primitive.input);
             }
+            FilterPrimitiveKind::Offset(info) => {
+                yaml_node(&mut table, "type", Yaml::String("offset".into()));
+                filter_input_node(&mut table, "in", info.input);
+                vector_node(&mut table, "offset", &info.offset);
+            }
         }
         enum_node(&mut table, "color-space", filter_primitive.color_space);
         filter_primitives.push(Yaml::Hash(table));
     }
 
     yaml_node(parent, name, Yaml::Array(filter_primitives));
 }
 
--- a/gfx/wr/wrench/src/yaml_helper.rs
+++ b/gfx/wr/wrench/src/yaml_helper.rs
@@ -764,16 +764,22 @@ impl YamlHelper for Yaml {
                         }
                     })
                 }
                 "component-transfer" => {
                     FilterPrimitiveKind::ComponentTransfer(ComponentTransferPrimitive {
                         input: self["in"].as_filter_input().unwrap(),
                     })
                 }
+                "offset" => {
+                    FilterPrimitiveKind::Offset(OffsetPrimitive {
+                        input: self["in"].as_filter_input().unwrap(),
+                        offset: self["offset"].as_vector().unwrap(),
+                    })
+                }
                 _ => return None,
             };
 
             Some(FilterPrimitive {
                 kind,
                 color_space: self["color-space"].as_color_space().unwrap_or(ColorSpace::LinearRgb),
             })
         } else {