зеркало из https://github.com/mozilla/gecko-dev.git
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:
Родитель
75c7f2f802
Коммит
3efce98819
|
@ -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])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче