Bug 1555483 - Part 1: Add SVG filter primitive display item. r=gw

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

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Connor Brewster 2019-07-10 22:36:25 +00:00
Родитель 75c7f2f802
Коммит 3efce98819
9 изменённых файлов: 174 добавлений и 15 удалений

Просмотреть файл

@ -71,6 +71,7 @@ impl App {
true,
&filters,
&[],
&[]
);
let space_and_clip = SpaceAndClipInfo {

Просмотреть файл

@ -5,7 +5,7 @@
use api::{AlphaType, BorderDetails, BorderDisplayItem, BuiltDisplayListIter};
use api::{ClipId, ColorF, CommonItemProperties, ComplexClipRegion, RasterSpace};
use api::{DisplayItem, DisplayItemRef, ExtendMode, ExternalScrollId};
use api::{FilterOp, FontInstanceKey, GlyphInstance, GlyphOptions, GradientStop};
use api::{FilterOp, FilterPrimitive, FontInstanceKey, GlyphInstance, GlyphOptions, GradientStop};
use api::{IframeDisplayItem, ImageKey, ImageRendering, ItemRange, ColorDepth};
use api::{LineOrientation, LineStyle, NinePatchBorderSource, PipelineId};
use api::{PropertyBinding, ReferenceFrame, ReferenceFrameKind, ScrollFrameDisplayItem, ScrollSensitivity};
@ -796,6 +796,7 @@ impl<'a> DisplayListFlattener<'a> {
origin: LayoutPoint,
filters: ItemRange<FilterOp>,
filter_datas: &[TempFilterData],
filter_primitives: ItemRange<FilterPrimitive>,
is_backface_visible: bool,
apply_pipeline_clip: bool,
) {
@ -809,6 +810,7 @@ impl<'a> DisplayListFlattener<'a> {
CompositeOps::new(
stacking_context.filter_ops_for_compositing(filters),
stacking_context.filter_datas_for_compositing(filter_datas),
stacking_context.filter_primitives_for_compositing(filter_primitives),
stacking_context.mix_blend_mode_for_compositing(),
)
};
@ -1180,6 +1182,7 @@ impl<'a> DisplayListFlattener<'a> {
info.origin,
item.filters(),
item.filter_datas(),
item.filter_primitives(),
info.is_backface_visible,
apply_pipeline_clip,
);
@ -1309,9 +1312,10 @@ impl<'a> DisplayListFlattener<'a> {
}
// Do nothing; these are dummy items for the display list parser
DisplayItem::SetGradientStops => {}
DisplayItem::SetFilterOps => {}
DisplayItem::SetFilterData => {}
DisplayItem::SetGradientStops |
DisplayItem::SetFilterOps |
DisplayItem::SetFilterData |
DisplayItem::SetFilterPrimitives => {}
DisplayItem::PopReferenceFrame |
DisplayItem::PopStackingContext => {

Просмотреть файл

@ -3,7 +3,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use api::{BuiltDisplayList, ColorF, DynamicProperties, Epoch};
use api::{FilterOp, TempFilterData, FilterData, ComponentTransferFuncType};
use api::{FilterOp, TempFilterData, FilterData, FilterPrimitive, ComponentTransferFuncType};
use api::{PipelineId, PropertyBinding, PropertyBindingId, ItemRange, MixBlendMode, StackingContext};
use api::units::{LayoutSize, LayoutTransform};
use crate::internal_types::{FastHashMap, Filter};
@ -206,6 +206,10 @@ pub trait StackingContextHelpers {
&self,
input_filter_datas: &[TempFilterData],
) -> Vec<FilterData>;
fn filter_primitives_for_compositing(
&self,
input_filter_primitives: ItemRange<FilterPrimitive>,
) -> Vec<FilterPrimitive>;
}
impl StackingContextHelpers for StackingContext {
@ -254,4 +258,15 @@ impl StackingContextHelpers for StackingContext {
}
filter_datas
}
fn filter_primitives_for_compositing(
&self,
input_filter_primitives: ItemRange<FilterPrimitive>,
) -> Vec<FilterPrimitive> {
// Resolve these in the flattener?
// TODO(gw): Now that we resolve these later on,
// we could probably make it a bit
// more efficient than cloning these here.
input_filter_primitives.iter().map(|primitive| primitive.into()).collect()
}
}

Просмотреть файл

@ -3,7 +3,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use api::{ColorF, BorderStyle, MixBlendMode, PipelineId, PremultipliedColorF};
use api::{DocumentLayer, FilterData, ImageFormat, LineOrientation};
use api::{DocumentLayer, FilterData, FilterPrimitive, ImageFormat, LineOrientation};
use api::units::*;
#[cfg(feature = "pathfinder")]
use api::FontRenderMode;
@ -1332,18 +1332,23 @@ pub struct CompositeOps {
// Requires only a single texture as input (e.g. most filters)
pub filters: Vec<Filter>,
pub filter_datas: Vec<FilterData>,
pub filter_primitives: Vec<FilterPrimitive>,
// Requires two source textures (e.g. mix-blend-mode)
pub mix_blend_mode: Option<MixBlendMode>,
}
impl CompositeOps {
pub fn new(filters: Vec<Filter>,
filter_datas: Vec<FilterData>,
mix_blend_mode: Option<MixBlendMode>) -> Self {
pub fn new(
filters: Vec<Filter>,
filter_datas: Vec<FilterData>,
filter_primitives: Vec<FilterPrimitive>,
mix_blend_mode: Option<MixBlendMode>
) -> Self {
CompositeOps {
filters,
filter_datas,
filter_primitives,
mix_blend_mode,
}
}

Просмотреть файл

@ -121,6 +121,7 @@ pub enum DisplayItem {
SetGradientStops,
SetFilterOps,
SetFilterData,
SetFilterPrimitives,
// These marker items terminate a scope introduced by a previous item.
PopReferenceFrame,
@ -159,6 +160,7 @@ pub enum DebugDisplayItem {
SetGradientStops(Vec<GradientStop>),
SetFilterOps(Vec<FilterOp>),
SetFilterData(FilterData),
SetFilterPrimitives(Vec<FilterPrimitive>),
PopReferenceFrame,
PopStackingContext,
@ -638,7 +640,7 @@ pub struct StackingContext {
pub raster_space: RasterSpace,
/// True if picture caching should be used on this stacking context.
pub cache_tiles: bool,
} // IMPLICIT: filters: Vec<FilterOp>, filter_datas: Vec<FilterData>
} // IMPLICIT: filters: Vec<FilterOp>, filter_datas: Vec<FilterData>, filter_primitives: Vec<FilterPrimitive>
#[repr(u8)]
#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
@ -697,6 +699,34 @@ pub enum MixBlendMode {
Luminosity = 15,
}
/// An input to a SVG filter primitive.
#[repr(C)]
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
pub enum FilterPrimitiveInput {
/// The input is the original graphic that the filter is being applied to.
Original,
/// The input is the output of the previous filter primitive in the filter primitive chain.
Previous,
/// The input is the output of the filter primitive at the given index in the filter primitive chain.
OutputOfPrimitiveIndex(usize),
}
#[repr(C)]
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
pub struct BlendPrimitive {
pub input1: FilterPrimitiveInput,
pub input2: FilterPrimitiveInput,
pub mode: MixBlendMode,
}
/// SVG Filter Primitive.
#[repr(C)]
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
pub enum FilterPrimitive {
Blend(BlendPrimitive),
}
/// CSS filter.
#[repr(C)]
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
pub enum FilterOp {
@ -1162,6 +1192,7 @@ impl DisplayItem {
DisplayItem::PushStackingContext(..) => "push_stacking_context",
DisplayItem::SetFilterOps => "set_filter_ops",
DisplayItem::SetFilterData => "set_filter_data",
DisplayItem::SetFilterPrimitives => "set_filter_primitives",
DisplayItem::RadialGradient(..) => "radial_gradient",
DisplayItem::Rectangle(..) => "rectangle",
DisplayItem::ScrollFrame(..) => "scroll_frame",

Просмотреть файл

@ -131,6 +131,7 @@ pub struct BuiltDisplayListIter<'a> {
cur_glyphs: ItemRange<'a, GlyphInstance>,
cur_filters: ItemRange<'a, di::FilterOp>,
cur_filter_data: Vec<TempFilterData<'a>>,
cur_filter_primitives: ItemRange<'a, di::FilterPrimitive>,
cur_clip_chain_items: ItemRange<'a, di::ClipId>,
cur_complex_clip: ItemRange<'a, di::ComplexClipRegion>,
peeking: Peek,
@ -297,6 +298,7 @@ impl<'a> BuiltDisplayListIter<'a> {
cur_glyphs: ItemRange::default(),
cur_filters: ItemRange::default(),
cur_filter_data: Vec::new(),
cur_filter_primitives: ItemRange::default(),
cur_clip_chain_items: ItemRange::default(),
cur_complex_clip: ItemRange::default(),
peeking: Peek::NotPeeking,
@ -335,7 +337,8 @@ impl<'a> BuiltDisplayListIter<'a> {
match self.cur_item {
SetGradientStops |
SetFilterOps |
SetFilterData => {
SetFilterData |
SetFilterPrimitives => {
// These are marker items for populating other display items, don't yield them.
continue;
}
@ -391,6 +394,10 @@ impl<'a> BuiltDisplayListIter<'a> {
self.debug_stats.log_slice("set_filter_data.b_values", &data.b_values);
self.debug_stats.log_slice("set_filter_data.a_values", &data.a_values);
}
SetFilterPrimitives => {
self.cur_filter_primitives = skip_slice::<di::FilterPrimitive>(&mut self.data);
self.debug_stats.log_slice("set_filter_primitives.primitives", &self.cur_filter_primitives);
}
ClipChain(_) => {
self.cur_clip_chain_items = skip_slice::<di::ClipId>(&mut self.data);
self.debug_stats.log_slice("clip_chain.clip_ids", &self.cur_clip_chain_items);
@ -502,6 +509,10 @@ impl<'a, 'b> DisplayItemRef<'a, 'b> {
&self.iter.cur_filter_data
}
pub fn filter_primitives(&self) -> ItemRange<di::FilterPrimitive> {
self.iter.cur_filter_primitives
}
pub fn clip_chain_items(&self) -> ItemRange<di::ClipId> {
self.iter.cur_clip_chain_items
}
@ -602,6 +613,9 @@ impl Serialize for BuiltDisplayList {
a_values: temp_filter_data.a_values.iter().collect(),
})
},
Real::SetFilterPrimitives => Debug::SetFilterPrimitives(
item.iter.cur_filter_primitives.iter().collect()
),
Real::SetGradientStops => Debug::SetGradientStops(
item.iter.cur_stops.iter().collect()
),
@ -700,6 +714,10 @@ impl<'de> Deserialize<'de> for BuiltDisplayList {
DisplayListBuilder::push_iter_impl(&mut temp, filter_data.a_values);
Real::SetFilterData
},
Debug::SetFilterPrimitives(filter_primitives) => {
DisplayListBuilder::push_iter_impl(&mut temp, filter_primitives);
Real::SetFilterPrimitives
}
Debug::SetGradientStops(stops) => {
DisplayListBuilder::push_iter_impl(&mut temp, stops);
Real::SetGradientStops
@ -1425,6 +1443,7 @@ impl DisplayListBuilder {
mix_blend_mode: di::MixBlendMode,
filters: &[di::FilterOp],
filter_datas: &[di::FilterData],
filter_primitives: &[di::FilterPrimitive],
raster_space: di::RasterSpace,
cache_tiles: bool,
) {
@ -1445,6 +1464,11 @@ impl DisplayListBuilder {
self.push_iter(&filter_data.a_values);
}
if !filter_primitives.is_empty() {
self.push_item(&di::DisplayItem::SetFilterPrimitives);
self.push_iter(filter_primitives);
}
let item = di::DisplayItem::PushStackingContext(di::PushStackingContextDisplayItem {
origin,
spatial_id,
@ -1468,7 +1492,7 @@ impl DisplayListBuilder {
spatial_id: di::SpatialId,
is_backface_visible: bool,
) {
self.push_simple_stacking_context_with_filters(origin, spatial_id, is_backface_visible, &[], &[]);
self.push_simple_stacking_context_with_filters(origin, spatial_id, is_backface_visible, &[], &[], &[]);
}
/// Helper for examples/ code.
@ -1479,6 +1503,7 @@ impl DisplayListBuilder {
is_backface_visible: bool,
filters: &[di::FilterOp],
filter_datas: &[di::FilterData],
filter_primitives: &[di::FilterPrimitive],
) {
self.push_stacking_context(
origin,
@ -1489,6 +1514,7 @@ impl DisplayListBuilder {
di::MixBlendMode::Normal,
filters,
filter_datas,
filter_primitives,
di::RasterSpace::Screen,
/* cache_tiles = */ false,
);

Просмотреть файл

@ -1881,6 +1881,7 @@ impl YamlFrameReader {
let filters = yaml["filters"].as_vec_filter_op().unwrap_or(vec![]);
let filter_datas = yaml["filter-datas"].as_vec_filter_data().unwrap_or(vec![]);
let filter_primitives = yaml["filter-primitives"].as_vec_filter_primitive().unwrap_or(vec![]);
dl.push_stacking_context(
bounds.origin,
@ -1891,6 +1892,7 @@ impl YamlFrameReader {
mix_blend_mode,
&filters,
&filter_datas,
&filter_primitives,
raster_space,
cache_tiles,
);

Просмотреть файл

@ -72,6 +72,14 @@ fn color_to_string(value: ColorF) -> String {
}
}
fn filter_input_to_string(input: FilterPrimitiveInput) -> String {
match input {
FilterPrimitiveInput::Original => "original".into(),
FilterPrimitiveInput::Previous => "previous".into(),
FilterPrimitiveInput::OutputOfPrimitiveIndex(index) => index.to_string(),
}
}
fn color_node(parent: &mut Table, key: &str, value: ColorF) {
yaml_node(parent, key, Yaml::String(color_to_string(value)));
}
@ -253,6 +261,7 @@ fn write_stacking_context(
properties: &SceneProperties,
filter_iter: impl IntoIterator<Item = FilterOp>,
filter_data_iter: &[TempFilterData],
filter_primitive_iter: impl IntoIterator<Item = FilterPrimitive>,
) {
enum_node(parent, "transform-style", sc.transform_style);
@ -349,6 +358,23 @@ fn write_stacking_context(
}
yaml_node(parent, "filter-datas", Yaml::Array(filter_datas));
// filter primitives
let mut filter_primitives = vec![];
for filter_primitive in filter_primitive_iter {
let mut table = new_table();
match filter_primitive {
FilterPrimitive::Blend(blend_primitive) => {
yaml_node(&mut table, "type", Yaml::String("blend".into()));
yaml_node(&mut table, "in1", Yaml::String(filter_input_to_string(blend_primitive.input1)));
yaml_node(&mut table, "in2", Yaml::String(filter_input_to_string(blend_primitive.input2)));
enum_node(&mut table, "mode", blend_primitive.mode);
}
}
filter_primitives.push(Yaml::Hash(table));
}
yaml_node(parent, "filter-primitives", Yaml::Array(filter_primitives));
}
#[cfg(target_os = "macos")]
@ -1165,6 +1191,7 @@ impl YamlFrameWriter {
&scene.properties,
base.filters(),
base.filter_datas(),
base.filter_primitives(),
);
let mut sub_iter = base.sub_iter();
@ -1277,9 +1304,11 @@ impl YamlFrameWriter {
DisplayItem::PopReferenceFrame |
DisplayItem::PopStackingContext => return,
DisplayItem::SetGradientStops => panic!("dummy item yielded?"),
DisplayItem::SetFilterOps => panic!("dummy item yielded?"),
DisplayItem::SetFilterData => panic!("dummy item yielded?"),
DisplayItem::SetGradientStops |
DisplayItem::SetFilterOps |
DisplayItem::SetFilterData |
DisplayItem::SetFilterPrimitives => panic!("dummy item yielded?"),
DisplayItem::PushShadow(item) => {
str_node(&mut v, "type", "shadow");
vector_node(&mut v, "offset", &item.shadow.offset);

Просмотреть файл

@ -38,6 +38,9 @@ pub trait YamlHelper {
fn as_vec_filter_op(&self) -> Option<Vec<FilterOp>>;
fn as_filter_data(&self) -> Option<FilterData>;
fn as_vec_filter_data(&self) -> Option<Vec<FilterData>>;
fn as_filter_input(&self) -> Option<FilterPrimitiveInput>;
fn as_filter_primitive(&self) -> Option<FilterPrimitive>;
fn as_vec_filter_primitive(&self) -> Option<Vec<FilterPrimitive>>;
}
fn string_to_color(color: &str) -> Option<ColorF> {
@ -674,6 +677,24 @@ impl YamlHelper for Yaml {
None
}
fn as_filter_input(&self) -> Option<FilterPrimitiveInput> {
if let Some(input) = self.as_str() {
match input {
"original" => Some(FilterPrimitiveInput::Original),
"previous" => Some(FilterPrimitiveInput::Previous),
_ => None,
}
} else if let Some(index) = self.as_i64() {
if index >= 0 {
Some(FilterPrimitiveInput::OutputOfPrimitiveIndex(index as usize))
} else {
panic!("Filter input index cannot be negative");
}
} else {
panic!("Invalid filter input");
}
}
fn as_vec_filter_data(&self) -> Option<Vec<FilterData>> {
if let Some(v) = self.as_vec() {
Some(v.iter().map(|x| x.as_filter_data().unwrap()).collect())
@ -681,4 +702,29 @@ impl YamlHelper for Yaml {
self.as_filter_data().map(|data| vec![data])
}
}
fn as_filter_primitive(&self) -> Option<FilterPrimitive> {
if let Some(filter_type) = self["type"].as_str() {
match filter_type {
"blend" => {
Some(FilterPrimitive::Blend(BlendPrimitive {
input1: self["in1"].as_filter_input().unwrap(),
input2: self["in2"].as_filter_input().unwrap(),
mode: self["blend-mode"].as_mix_blend_mode().unwrap(),
}))
}
_ => None,
}
} else {
None
}
}
fn as_vec_filter_primitive(&self) -> Option<Vec<FilterPrimitive>> {
if let Some(v) = self.as_vec() {
Some(v.iter().map(|x| x.as_filter_primitive().unwrap()).collect())
} else {
self.as_filter_primitive().map(|data| vec![data])
}
}
}