Bug 1573649 - Implement feOffset SVG filter primitive in WebRender r=nical
authorConnor Brewster <cbrewster@mozilla.com>
Wed, 14 Aug 2019 20:14:25 +0000
changeset 488178 d596e6b5be6a5a68eb459dbb9e098e359b25ccce
parent 488177 2d3159992a617c16ec8472479c46a4e3ab4dc147
child 488179 9e513ae4747fad12001ef627e6f273624a3feff4
push id36437
push userncsoregi@mozilla.com
push dateThu, 15 Aug 2019 19:33:18 +0000
treeherdermozilla-central@44aac6fc3352 [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
@@ -1471,16 +1471,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
@@ -53,8 +53,9 @@ platform(linux,mac) == draw_calls(8) col
 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
 == backdrop-filter-basic.yaml backdrop-filter-basic-ref.yaml
 platform(linux,mac) == backdrop-filter-perspective.yaml backdrop-filter-perspective.png
+platform(linux,max) == svg-filter-offset.yaml svg-filter-offset-ref.yaml
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 {