Bug 1505871. Write component transfer filter data into the webrender display list bitstream. r=jrmuizel
☠☠ backed out by 3c030119c0dc ☠ ☠
authorTimothy Nikkel <tnikkel@gmail.com>
Mon, 25 Feb 2019 19:20:27 -0600
changeset 461101 1899600a79855a892c9ec75216db20ad5e2b0c2e
parent 461100 f9578d20e54ec0c0ffebe29d78946352cc9ae438
child 461102 0fd8742fa66223b490880d6b759066d1e5532148
push id35618
push usershindli@mozilla.com
push dateTue, 26 Feb 2019 16:54:44 +0000
treeherdermozilla-central@d326a9d5f77b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjrmuizel
bugs1505871
milestone67.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 1505871. Write component transfer filter data into the webrender display list bitstream. r=jrmuizel The format for stacking contexts in the built display list goes from PushStackingContext item push_iter of Vec<FilterOp> to SetFilterOps item push_iter of Vec<FilterOp> 1st SetFilterData item push_iter of array of func types push_iter funcR values push_iter funcG values push_iter funcB values push_iter funcA values . . . nth SetFilterData item push_iter of array of func types push_iter funcR values push_iter funcG values push_iter funcB values push_iter funcA values PushStackingContext item We need separate a SetFilterData item for each filter because we can't push_iter a variable sized thing. When we iterate over the built display list to flatten it we work similarly to how gradients work with a SetGradientStops item before the actual gradient item. So when we see SetFilterOps or SetFilterData we use them to fill out values on the built display list iterator but don't those items return them to the iterator user and instead continue iterating until we hit the PushStackingContext item, at which point to the iterator consumer it appears as those the FilterOps and FilterDatas were on the PushStackingContext item. (This part is trickier too since we need a TempFilterData type that just holds ItemRange's until we get the actual bytes later.) Do we need to clear cur_filters and cur_filter_data at some point to prevent them from getting ready by items for which they do not apply?
gfx/webrender_bindings/src/bindings.rs
gfx/wr/examples/animation.rs
gfx/wr/webrender/src/display_list_flattener.rs
gfx/wr/webrender/src/render_backend.rs
gfx/wr/webrender/src/scene.rs
gfx/wr/webrender_api/src/display_item.rs
gfx/wr/webrender_api/src/display_list.rs
--- a/gfx/webrender_bindings/src/bindings.rs
+++ b/gfx/webrender_bindings/src/bindings.rs
@@ -1976,16 +1976,30 @@ pub extern "C" fn wr_dp_push_stacking_co
 ) -> WrSpatialId {
     debug_assert!(unsafe { !is_in_render_thread() });
 
     let c_filters = make_slice(filters, filter_count);
     let mut filters : Vec<FilterOp> = c_filters.iter().map(|c_filter| {
                                                            *c_filter
     }).collect();
 
+    let c_filter_datas = make_slice(filter_datas, filter_datas_count);
+    let r_filter_datas : Vec<FilterData> = c_filter_datas.iter().map(|c_filter_data| {
+        FilterData {
+            func_r_type: c_filter_data.funcR_type,
+            r_values: make_slice(c_filter_data.R_values, c_filter_data.R_values_count).to_vec(),
+            func_g_type: c_filter_data.funcG_type,
+            g_values: make_slice(c_filter_data.G_values, c_filter_data.G_values_count).to_vec(),
+            func_b_type: c_filter_data.funcB_type,
+            b_values: make_slice(c_filter_data.B_values, c_filter_data.B_values_count).to_vec(),
+            func_a_type: c_filter_data.funcA_type,
+            a_values: make_slice(c_filter_data.A_values, c_filter_data.A_values_count).to_vec(),
+        }
+    }).collect();
+
     let transform_ref = unsafe { transform.as_ref() };
     let mut transform_binding = match transform_ref {
         Some(t) => Some(PropertyBinding::Value(t.clone())),
         None => None,
     };
 
     let opacity_ref = unsafe { params.opacity.as_ref() };
     let mut has_opacity_animation = false;
@@ -2063,16 +2077,17 @@ pub extern "C" fn wr_dp_push_stacking_co
     state.frame_builder
          .dl_builder
          .push_stacking_context(&prim_info,
                                 wr_spatial_id,
                                 wr_clip_id,
                                 params.transform_style,
                                 params.mix_blend_mode,
                                 &filters,
+                                &r_filter_datas,
                                 glyph_raster_space,
                                 params.cache_tiles);
 
     result
 }
 
 #[no_mangle]
 pub extern "C" fn wr_dp_pop_stacking_context(state: &mut WrState,
--- a/gfx/wr/examples/animation.rs
+++ b/gfx/wr/examples/animation.rs
@@ -62,16 +62,17 @@ impl App {
             PropertyBinding::Binding(property_key, LayoutTransform::identity()),
             ReferenceFrameKind::Transform,
         );
 
         builder.push_simple_stacking_context_with_filters(
             &LayoutPrimitiveInfo::new(LayoutRect::zero()),
             spatial_id,
             &filters,
+            &[],
         );
 
         let space_and_clip = SpaceAndClipInfo {
             spatial_id,
             clip_id: ClipId::root(pipeline_id),
         };
         let clip_bounds = LayoutRect::new(LayoutPoint::zero(), bounds.size);
         let complex_clip = ComplexClipRegion {
--- a/gfx/wr/webrender/src/display_list_flattener.rs
+++ b/gfx/wr/webrender/src/display_list_flattener.rs
@@ -6,17 +6,17 @@ use api::{AlphaType, BorderDetails, Bord
 use api::{ClipId, ColorF, ComplexClipRegion, DeviceIntPoint, DeviceIntRect, DeviceIntSize};
 use api::{DisplayItemRef, ExtendMode, ExternalScrollId, AuHelpers};
 use api::{FilterOp, FontInstanceKey, GlyphInstance, GlyphOptions, RasterSpace, GradientStop};
 use api::{IframeDisplayItem, ImageKey, ImageRendering, ItemRange, LayoutPoint, ColorDepth};
 use api::{LayoutPrimitiveInfo, LayoutRect, LayoutSize, LayoutTransform, LayoutVector2D};
 use api::{LineOrientation, LineStyle, NinePatchBorderSource, PipelineId};
 use api::{PropertyBinding, ReferenceFrame, ReferenceFrameKind, ScrollFrameDisplayItem, ScrollSensitivity};
 use api::{Shadow, SpaceAndClipInfo, SpatialId, SpecificDisplayItem, StackingContext, StickyFrameDisplayItem, TexelRect};
-use api::{ClipMode, TransformStyle, YuvColorSpace, YuvData};
+use api::{ClipMode, TransformStyle, YuvColorSpace, YuvData, TempFilterData};
 use app_units::Au;
 use clip::{ClipChainId, ClipRegion, ClipItemKey, ClipStore};
 use clip_scroll_tree::{ROOT_SPATIAL_NODE_INDEX, ClipScrollTree, SpatialNodeIndex};
 use frame_builder::{ChasePrimitive, FrameBuilder, FrameBuilderConfig};
 use glyph_rasterizer::FontInstance;
 use hit_test::{HitTestingItem, HitTestingRun};
 use image::simplify_repeated_primitive;
 use intern::{Handle, Internable, InternDebug};
@@ -587,16 +587,17 @@ impl<'a> DisplayListFlattener<'a> {
     fn flatten_stacking_context(
         &mut self,
         traversal: &mut BuiltDisplayListIter<'a>,
         pipeline_id: PipelineId,
         stacking_context: &StackingContext,
         spatial_node_index: SpatialNodeIndex,
         origin: LayoutPoint,
         filters: ItemRange<FilterOp>,
+        filter_datas: &[TempFilterData],
         reference_frame_relative_offset: &LayoutVector2D,
         is_backface_visible: bool,
         apply_pipeline_clip: bool,
     ) {
         // Avoid doing unnecessary work for empty stacking contexts.
         if traversal.current_stacking_context_empty() {
             traversal.skip_current_stacking_context();
             return;
@@ -875,16 +876,17 @@ impl<'a> DisplayListFlattener<'a> {
                 let mut subtraversal = item.sub_iter();
                 self.flatten_stacking_context(
                     &mut subtraversal,
                     pipeline_id,
                     &info.stacking_context,
                     clip_and_scroll.spatial_node_index,
                     item.rect().origin,
                     item.filters(),
+                    item.filter_datas(),
                     &reference_frame_relative_offset,
                     prim_info.is_backface_visible,
                     apply_pipeline_clip,
                 );
                 return Some(subtraversal);
             }
             SpecificDisplayItem::PushReferenceFrame(ref info) => {
                 let mut subtraversal = item.sub_iter();
@@ -1001,16 +1003,18 @@ impl<'a> DisplayListFlattener<'a> {
                     info,
                     clip_and_scroll.spatial_node_index,
                     &reference_frame_relative_offset,
                 );
             }
 
             // Do nothing; these are dummy items for the display list parser
             SpecificDisplayItem::SetGradientStops => {}
+            SpecificDisplayItem::SetFilterOps => {}
+            SpecificDisplayItem::SetFilterData => {}
 
             SpecificDisplayItem::PopReferenceFrame |
             SpecificDisplayItem::PopStackingContext => {
                 unreachable!("Should have returned in parent method.")
             }
             SpecificDisplayItem::PushShadow(shadow) => {
                 self.push_shadow(shadow, clip_and_scroll);
             }
--- a/gfx/wr/webrender/src/render_backend.rs
+++ b/gfx/wr/webrender/src/render_backend.rs
@@ -1588,16 +1588,18 @@ impl ToDebugString for SpecificDisplayIt
             SpecificDisplayItem::Image(..) => String::from("image"),
             SpecificDisplayItem::Line(..) => String::from("line"),
             SpecificDisplayItem::PopAllShadows => String::from("pop_all_shadows"),
             SpecificDisplayItem::PopReferenceFrame => String::from("pop_reference_frame"),
             SpecificDisplayItem::PopStackingContext => String::from("pop_stacking_context"),
             SpecificDisplayItem::PushShadow(..) => String::from("push_shadow"),
             SpecificDisplayItem::PushReferenceFrame(..) => String::from("push_reference_frame"),
             SpecificDisplayItem::PushStackingContext(..) => String::from("push_stacking_context"),
+            SpecificDisplayItem::SetFilterOps => String::from("set_filter_ops"),
+            SpecificDisplayItem::SetFilterData => String::from("set_filter_data"),
             SpecificDisplayItem::RadialGradient(..) => String::from("radial_gradient"),
             SpecificDisplayItem::Rectangle(..) => String::from("rectangle"),
             SpecificDisplayItem::ScrollFrame(..) => String::from("scroll_frame"),
             SpecificDisplayItem::SetGradientStops => String::from("set_gradient_stops"),
             SpecificDisplayItem::StickyFrame(..) => String::from("sticky_frame"),
             SpecificDisplayItem::Text(..) => String::from("text"),
             SpecificDisplayItem::YuvImage(..) => String::from("yuv_image"),
             SpecificDisplayItem::PushCacheMarker(..) => String::from("push_cache_marker"),
--- a/gfx/wr/webrender/src/scene.rs
+++ b/gfx/wr/webrender/src/scene.rs
@@ -1,15 +1,15 @@
 /* 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::{BuiltDisplayList, ColorF, DynamicProperties, Epoch, LayoutSize};
-use api::{FilterOp, LayoutTransform, PipelineId, PropertyBinding, PropertyBindingId};
-use api::{ItemRange, MixBlendMode, StackingContext};
+use api::{FilterOp, TempFilterData, FilterData, ComponentTransferFuncType, LayoutTransform};
+use api::{PipelineId, PropertyBinding, PropertyBindingId, ItemRange, MixBlendMode, StackingContext};
 use internal_types::FastHashMap;
 use std::sync::Arc;
 
 /// Stores a map of the animated property bindings for the current display list. These
 /// can be used to animate the transform and/or opacity of a display list without
 /// re-submitting the display list itself.
 #[cfg_attr(feature = "capture", derive(Serialize))]
 #[cfg_attr(feature = "replay", derive(Deserialize))]
@@ -220,17 +220,18 @@ impl FilterOpHelpers for FilterOp {
             FilterOp::Grayscale(..) |
             FilterOp::HueRotate(..) |
             FilterOp::Invert(..) |
             FilterOp::Saturate(..) |
             FilterOp::Sepia(..) |
             FilterOp::DropShadow(..) |
             FilterOp::ColorMatrix(..) |
             FilterOp::SrgbToLinear |
-            FilterOp::LinearToSrgb  => true,
+            FilterOp::LinearToSrgb |
+            FilterOp::ComponentTransfer  => true,
             FilterOp::Opacity(_, amount) => {
                 amount > OPACITY_EPSILON
             }
         }
     }
 
     fn is_noop(&self) -> bool {
         match *self {
@@ -249,28 +250,35 @@ impl FilterOpHelpers for FilterOp {
             },
             FilterOp::ColorMatrix(matrix) => {
                 matrix == [1.0, 0.0, 0.0, 0.0,
                            0.0, 1.0, 0.0, 0.0,
                            0.0, 0.0, 1.0, 0.0,
                            0.0, 0.0, 0.0, 1.0,
                            0.0, 0.0, 0.0, 0.0]
             }
-            FilterOp::SrgbToLinear | FilterOp::LinearToSrgb => false,
+            FilterOp::SrgbToLinear |
+            FilterOp::LinearToSrgb |
+            FilterOp::ComponentTransfer => false,
         }
     }
 }
 
 pub trait StackingContextHelpers {
     fn mix_blend_mode_for_compositing(&self) -> Option<MixBlendMode>;
     fn filter_ops_for_compositing(
         &self,
         display_list: &BuiltDisplayList,
         input_filters: ItemRange<FilterOp>,
     ) -> Vec<FilterOp>;
+    fn filter_datas_for_compositing(
+        &self,
+        display_list: &BuiltDisplayList,
+        input_filter_datas: &[TempFilterData],
+    ) -> Vec<FilterData>;
 }
 
 impl StackingContextHelpers for StackingContext {
     fn mix_blend_mode_for_compositing(&self) -> Option<MixBlendMode> {
         match self.mix_blend_mode {
             MixBlendMode::Normal => None,
             _ => Some(self.mix_blend_mode),
         }
@@ -285,9 +293,35 @@ impl StackingContextHelpers for Stacking
         //           we could probably make it a bit
         //           more efficient than cloning these here.
         let mut filters = vec![];
         for filter in display_list.get(input_filters) {
             filters.push(filter);
         }
         filters
     }
+
+    fn filter_datas_for_compositing(
+        &self,
+        display_list: &BuiltDisplayList,
+        input_filter_datas: &[TempFilterData],
+    ) -> Vec<FilterData> {
+        // TODO(gw): Now that we resolve these later on,
+        //           we could probably make it a bit
+        //           more efficient than cloning these here.
+        let mut filter_datas = vec![];
+        for temp_filter_data in input_filter_datas {
+            let func_types : Vec<ComponentTransferFuncType> = display_list.get(temp_filter_data.func_types).collect();
+            debug_assert!(func_types.len() == 4);
+            filter_datas.push( FilterData {
+                func_r_type: func_types[0],
+                r_values: display_list.get(temp_filter_data.r_values).collect(),
+                func_g_type: func_types[1],
+                g_values: display_list.get(temp_filter_data.g_values).collect(),
+                func_b_type: func_types[2],
+                b_values: display_list.get(temp_filter_data.b_values).collect(),
+                func_a_type: func_types[3],
+                a_values: display_list.get(temp_filter_data.a_values).collect(),
+            });
+        }
+        filter_datas
+    }
 }
--- a/gfx/wr/webrender_api/src/display_item.rs
+++ b/gfx/wr/webrender_api/src/display_item.rs
@@ -122,16 +122,18 @@ pub enum SpecificDisplayItem {
     PopReferenceFrame,
     PushStackingContext(PushStackingContextDisplayItem),
     PopStackingContext,
     SetGradientStops,
     PushShadow(Shadow),
     PopAllShadows,
     PushCacheMarker(CacheMarkerDisplayItem),
     PopCacheMarker,
+    SetFilterOps,
+    SetFilterData,
 }
 
 /// This is a "complete" version of the DI specifics,
 /// containing the auxiliary data within the corresponding
 /// enumeration variants, to be used for debug serialization.
 #[cfg(any(feature = "serialize", feature = "deserialize"))]
 #[cfg_attr(feature = "serialize", derive(Serialize))]
 #[cfg_attr(feature = "deserialize", derive(Deserialize))]
@@ -148,23 +150,25 @@ pub enum CompletelySpecificDisplayItem {
     YuvImage(YuvImageDisplayItem),
     Border(BorderDisplayItem),
     BoxShadow(BoxShadowDisplayItem),
     Gradient(GradientDisplayItem),
     RadialGradient(RadialGradientDisplayItem),
     Iframe(IframeDisplayItem),
     PushReferenceFrame(ReferenceFrameDisplayListItem),
     PopReferenceFrame,
-    PushStackingContext(PushStackingContextDisplayItem, Vec<FilterOp>),
+    PushStackingContext(PushStackingContextDisplayItem),
     PopStackingContext,
     SetGradientStops(Vec<GradientStop>),
     PushShadow(Shadow),
     PopAllShadows,
     PushCacheMarker(CacheMarkerDisplayItem),
     PopCacheMarker,
+    SetFilterOps(Vec<FilterOp>),
+    SetFilterData(FilterData),
 }
 
 #[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
 pub struct ClipDisplayItem {
     pub id: ClipId,
     pub image_mask: Option<ImageMask>,
 }
 
@@ -557,18 +561,17 @@ pub struct PushStackingContextDisplayIte
 #[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
 pub struct StackingContext {
     pub transform_style: TransformStyle,
     pub mix_blend_mode: MixBlendMode,
     pub clip_id: Option<ClipId>,
     pub raster_space: RasterSpace,
     /// True if picture caching should be used on this stacking context.
     pub cache_tiles: bool,
-} // IMPLICIT: filters: Vec<FilterOp>
-
+} // IMPLICIT: filters: Vec<FilterOp>, filter_datas: Vec<FilterData>
 
 #[repr(u32)]
 #[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
 pub enum TransformStyle {
     Flat = 0,
     Preserve3D = 1,
 }
 
@@ -636,16 +639,17 @@ pub enum FilterOp {
     Invert(f32),
     Opacity(PropertyBinding<f32>, f32),
     Saturate(f32),
     Sepia(f32),
     DropShadow(LayoutVector2D, f32, ColorF),
     ColorMatrix([f32; 20]),
     SrgbToLinear,
     LinearToSrgb,
+    ComponentTransfer,
 }
 
 impl FilterOp {
     /// Ensure that the parameters for a filter operation
     /// are sensible.
     pub fn sanitize(self) -> FilterOp {
         match self {
             FilterOp::Blur(radius) => {
@@ -656,16 +660,38 @@ impl FilterOp {
                 let radius = radius.min(MAX_BLUR_RADIUS);
                 FilterOp::DropShadow(offset, radius, color)
             }
             filter => filter,
         }
     }
 }
 
+#[repr(u8)]
+#[derive(Clone, Copy, Debug, PartialEq, Deserialize, Serialize)]
+pub enum ComponentTransferFuncType {
+  Identity = 0,
+  Table = 1,
+  Discrete = 2,
+  Linear = 3,
+  Gamma = 4,
+}
+
+#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
+pub struct FilterData {
+    pub func_r_type: ComponentTransferFuncType,
+    pub r_values: Vec<f32>,
+    pub func_g_type: ComponentTransferFuncType,
+    pub g_values: Vec<f32>,
+    pub func_b_type: ComponentTransferFuncType,
+    pub b_values: Vec<f32>,
+    pub func_a_type: ComponentTransferFuncType,
+    pub a_values: Vec<f32>,
+}
+
 #[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
 pub struct IframeDisplayItem {
     pub pipeline_id: PipelineId,
     pub ignore_missing_pipeline: bool,
 }
 
 #[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
 pub struct ImageDisplayItem {
--- a/gfx/wr/webrender_api/src/display_list.rs
+++ b/gfx/wr/webrender_api/src/display_list.rs
@@ -56,16 +56,24 @@ impl<T> Default for ItemRange<T> {
 
 impl<T> ItemRange<T> {
     pub fn is_empty(&self) -> bool {
         // Nothing more than space for a length (0).
         self.length <= mem::size_of::<u64>()
     }
 }
 
+pub struct TempFilterData {
+    pub func_types: ItemRange<di::ComponentTransferFuncType>,
+    pub r_values: ItemRange<f32>,
+    pub g_values: ItemRange<f32>,
+    pub b_values: ItemRange<f32>,
+    pub a_values: ItemRange<f32>,
+}
+
 /// A display list.
 #[derive(Clone, Default)]
 pub struct BuiltDisplayList {
     /// Serde encoded bytes. Mostly DisplayItems, but some mixed in slices.
     data: Vec<u8>,
     descriptor: BuiltDisplayListDescriptor,
 }
 
@@ -90,16 +98,17 @@ pub struct BuiltDisplayListDescriptor {
 
 pub struct BuiltDisplayListIter<'a> {
     list: &'a BuiltDisplayList,
     data: &'a [u8],
     cur_item: di::DisplayItem,
     cur_stops: ItemRange<di::GradientStop>,
     cur_glyphs: ItemRange<GlyphInstance>,
     cur_filters: ItemRange<di::FilterOp>,
+    cur_filter_data: Vec<TempFilterData>,
     cur_clip_chain_items: ItemRange<di::ClipId>,
     cur_complex_clip: (ItemRange<di::ComplexClipRegion>, usize),
     peeking: Peek,
 }
 
 pub struct DisplayItemRef<'a: 'b, 'b> {
     iter: &'b BuiltDisplayListIter<'a>,
 }
@@ -210,16 +219,17 @@ impl<'a> BuiltDisplayListIter<'a> {
                 // Dummy data, will be overwritten by `next`
                 item: di::SpecificDisplayItem::PopStackingContext,
                 layout: di::LayoutPrimitiveInfo::new(LayoutRect::zero()),
                 space_and_clip: di::SpaceAndClipInfo::root_scroll(PipelineId::dummy())
             },
             cur_stops: ItemRange::default(),
             cur_glyphs: ItemRange::default(),
             cur_filters: ItemRange::default(),
+            cur_filter_data: Vec::new(),
             cur_clip_chain_items: ItemRange::default(),
             cur_complex_clip: (ItemRange::default(), 0),
             peeking: Peek::NotPeeking,
         }
     }
 
     pub fn display_list(&self) -> &'a BuiltDisplayList {
         self.list
@@ -245,16 +255,25 @@ impl<'a> BuiltDisplayListIter<'a> {
         self.cur_clip_chain_items = ItemRange::default();
 
         loop {
             self.next_raw()?;
             if let SetGradientStops = self.cur_item.item {
                 // SetGradientStops is a dummy item that most consumers should ignore
                 continue;
             }
+            if let SetFilterOps = self.cur_item.item {
+                // SetFilterOps is a dummy item that most consumers should ignore
+                continue;
+            }
+            if let SetFilterData = self.cur_item.item {
+                // SetFilterData is a dummy item that most consumers should ignore
+                continue;
+            }
+
             break;
         }
 
         Some(self.as_ref())
     }
 
     /// Gets the next display item, even if it's a dummy. Also doesn't handle peeking
     /// and may leave irrelevant ranges live (so a Clip may have GradientStops if
@@ -271,24 +290,35 @@ impl<'a> BuiltDisplayListIter<'a> {
             bincode::deserialize_in_place(reader, &mut self.cur_item)
                 .expect("MEH: malicious process?");
         }
 
         match self.cur_item.item {
             SetGradientStops => {
                 self.cur_stops = skip_slice::<di::GradientStop>(self.list, &mut self.data).0;
             }
+            SetFilterOps => {
+                self.cur_filters = skip_slice::<di::FilterOp>(self.list, &mut self.data).0;
+            }
+            SetFilterData => {
+                self.cur_filter_data.push(TempFilterData {
+                    func_types: skip_slice::<di::ComponentTransferFuncType>(self.list, &mut self.data).0,
+                    r_values: skip_slice::<f32>(self.list, &mut self.data).0,
+                    g_values: skip_slice::<f32>(self.list, &mut self.data).0,
+                    b_values: skip_slice::<f32>(self.list, &mut self.data).0,
+                    a_values: skip_slice::<f32>(self.list, &mut self.data).0,
+                });
+            }
             ClipChain(_) => {
                 self.cur_clip_chain_items = skip_slice::<di::ClipId>(self.list, &mut self.data).0;
             }
             Clip(_) | ScrollFrame(_) => {
                 self.cur_complex_clip = self.skip_slice::<di::ComplexClipRegion>()
             }
             Text(_) => self.cur_glyphs = self.skip_slice::<GlyphInstance>().0,
-            PushStackingContext(_) => self.cur_filters = self.skip_slice::<di::FilterOp>().0,
             _ => { /* do nothing */ }
         }
 
         Some(self.as_ref())
     }
 
     fn skip_slice<T: for<'de> Deserialize<'de>>(&mut self) -> (ItemRange<T>, usize) {
         skip_slice::<T>(self.list, &mut self.data)
@@ -384,16 +414,20 @@ impl<'a, 'b> DisplayItemRef<'a, 'b> {
     pub fn glyphs(&self) -> ItemRange<GlyphInstance> {
         self.iter.cur_glyphs
     }
 
     pub fn filters(&self) -> ItemRange<di::FilterOp> {
         self.iter.cur_filters
     }
 
+    pub fn filter_datas(&self) -> &Vec<TempFilterData> {
+        &self.iter.cur_filter_data
+    }
+
     pub fn clip_chain_items(&self) -> ItemRange<di::ClipId> {
         self.iter.cur_clip_chain_items
     }
 
     pub fn display_list(&self) -> &BuiltDisplayList {
         self.iter.display_list()
     }
 
@@ -482,21 +516,39 @@ impl Serialize for BuiltDisplayList {
                     di::SpecificDisplayItem::YuvImage(v) => YuvImage(v),
                     di::SpecificDisplayItem::Border(v) => Border(v),
                     di::SpecificDisplayItem::BoxShadow(v) => BoxShadow(v),
                     di::SpecificDisplayItem::Gradient(v) => Gradient(v),
                     di::SpecificDisplayItem::RadialGradient(v) => RadialGradient(v),
                     di::SpecificDisplayItem::Iframe(v) => Iframe(v),
                     di::SpecificDisplayItem::PushReferenceFrame(v) => PushReferenceFrame(v),
                     di::SpecificDisplayItem::PopReferenceFrame => PopReferenceFrame,
-                    di::SpecificDisplayItem::PushStackingContext(v) => PushStackingContext(
-                        v,
+                    di::SpecificDisplayItem::PushStackingContext(v) => PushStackingContext(v),
+                    di::SpecificDisplayItem::PopStackingContext => PopStackingContext,
+                    di::SpecificDisplayItem::SetFilterOps => SetFilterOps(
                         item.iter.list.get(item.iter.cur_filters).collect()
                     ),
-                    di::SpecificDisplayItem::PopStackingContext => PopStackingContext,
+                    di::SpecificDisplayItem::SetFilterData => {
+                        debug_assert!(!item.iter.cur_filter_data.is_empty());
+                        let temp_filter_data = &item.iter.cur_filter_data[item.iter.cur_filter_data.len()-1];
+
+                        let func_types: Vec<di::ComponentTransferFuncType> =
+                            item.iter.list.get(temp_filter_data.func_types).collect();
+                        debug_assert!(func_types.len() == 4);
+                        SetFilterData(di::FilterData {
+                            func_r_type: func_types[0],
+                            r_values: item.iter.list.get(temp_filter_data.r_values).collect(),
+                            func_g_type: func_types[1],
+                            g_values: item.iter.list.get(temp_filter_data.g_values).collect(),
+                            func_b_type: func_types[2],
+                            b_values: item.iter.list.get(temp_filter_data.b_values).collect(),
+                            func_a_type: func_types[3],
+                            a_values: item.iter.list.get(temp_filter_data.a_values).collect(),
+                        })
+                    },
                     di::SpecificDisplayItem::SetGradientStops => SetGradientStops(
                         item.iter.list.get(item.iter.cur_stops).collect()
                     ),
                     di::SpecificDisplayItem::PushShadow(v) => PushShadow(v),
                     di::SpecificDisplayItem::PopAllShadows => PopAllShadows,
                     di::SpecificDisplayItem::PushCacheMarker(m) => PushCacheMarker(m),
                     di::SpecificDisplayItem::PopCacheMarker => PopCacheMarker,
                 },
@@ -569,19 +621,35 @@ impl<'de> Deserialize<'de> for BuiltDisp
                         total_clip_nodes += 1;
                         di::SpecificDisplayItem::Iframe(specific_item)
                     }
                     PushReferenceFrame(v) => {
                         total_spatial_nodes += 1;
                         di::SpecificDisplayItem::PushReferenceFrame(v)
                     }
                     PopReferenceFrame => di::SpecificDisplayItem::PopReferenceFrame,
-                    PushStackingContext(specific_item, filters) => {
+                    PushStackingContext(specific_item) => {
+                        di::SpecificDisplayItem::PushStackingContext(specific_item)
+                    },
+                    SetFilterOps(filters) => {
                         DisplayListBuilder::push_iter_impl(&mut temp, filters);
-                        di::SpecificDisplayItem::PushStackingContext(specific_item)
+                        di::SpecificDisplayItem::SetFilterOps
+                    },
+                    SetFilterData(filter_data) => {
+                        let func_types: Vec<di::ComponentTransferFuncType> =
+                            [filter_data.func_r_type,
+                             filter_data.func_g_type,
+                             filter_data.func_b_type,
+                             filter_data.func_a_type].to_vec();
+                        DisplayListBuilder::push_iter_impl(&mut temp, func_types);
+                        DisplayListBuilder::push_iter_impl(&mut temp, filter_data.r_values);
+                        DisplayListBuilder::push_iter_impl(&mut temp, filter_data.g_values);
+                        DisplayListBuilder::push_iter_impl(&mut temp, filter_data.b_values);
+                        DisplayListBuilder::push_iter_impl(&mut temp, filter_data.a_values);
+                        di::SpecificDisplayItem::SetFilterData
                     },
                     PopStackingContext => di::SpecificDisplayItem::PopStackingContext,
                     SetGradientStops(stops) => {
                         DisplayListBuilder::push_iter_impl(&mut temp, stops);
                         di::SpecificDisplayItem::SetGradientStops
                     },
                     PushShadow(specific_item) => di::SpecificDisplayItem::PushShadow(specific_item),
                     PopAllShadows => di::SpecificDisplayItem::PopAllShadows,
@@ -1294,59 +1362,76 @@ impl DisplayListBuilder {
     pub fn push_stacking_context(
         &mut self,
         layout: &di::LayoutPrimitiveInfo,
         spatial_id: di::SpatialId,
         clip_id: Option<di::ClipId>,
         transform_style: di::TransformStyle,
         mix_blend_mode: di::MixBlendMode,
         filters: &[di::FilterOp],
+        filter_datas: &[di::FilterData],
         raster_space: di::RasterSpace,
         cache_tiles: bool,
     ) {
+        self.push_new_empty_item(&di::SpecificDisplayItem::SetFilterOps);
+        self.push_iter(filters);
+
+        for filter_data in filter_datas {
+            let func_types = [
+                filter_data.func_r_type, filter_data.func_g_type,
+                filter_data.func_b_type, filter_data.func_a_type];
+            self.push_new_empty_item(&di::SpecificDisplayItem::SetFilterData);
+            self.push_iter(&func_types);
+            self.push_iter(&filter_data.r_values);
+            self.push_iter(&filter_data.g_values);
+            self.push_iter(&filter_data.b_values);
+            self.push_iter(&filter_data.a_values);
+        }
+
         let item = di::SpecificDisplayItem::PushStackingContext(di::PushStackingContextDisplayItem {
             stacking_context: di::StackingContext {
                 transform_style,
                 mix_blend_mode,
                 clip_id,
                 raster_space,
                 cache_tiles,
             },
         });
 
         self.push_item(&item, layout, &di::SpaceAndClipInfo {
             spatial_id,
             clip_id: di::ClipId::invalid(),
         });
-        self.push_iter(filters);
     }
 
     /// Helper for examples/ code.
     pub fn push_simple_stacking_context(
         &mut self,
         layout: &di::LayoutPrimitiveInfo,
         spatial_id: di::SpatialId,
     ) {
-        self.push_simple_stacking_context_with_filters(layout, spatial_id, &[]);
+        self.push_simple_stacking_context_with_filters(layout, spatial_id, &[], &[]);
     }
 
     /// Helper for examples/ code.
     pub fn push_simple_stacking_context_with_filters(
         &mut self,
         layout: &di::LayoutPrimitiveInfo,
         spatial_id: di::SpatialId,
         filters: &[di::FilterOp],
+        filter_datas: &[di::FilterData],
     ) {
         self.push_stacking_context(
             layout,
             spatial_id,
             None,
             di::TransformStyle::Flat,
             di::MixBlendMode::Normal,
             filters,
+            filter_datas,
             di::RasterSpace::Screen,
             /* cache_tiles = */ false,
         );
     }
 
     pub fn pop_stacking_context(&mut self) {
         self.push_new_empty_item(&di::SpecificDisplayItem::PopStackingContext);
     }