Skip to content

Commit

Permalink
Make filter property animatable.
Browse files Browse the repository at this point in the history
  • Loading branch information
mantaroh committed Jun 19, 2017
1 parent 0ca9a01 commit 87e580a
Show file tree
Hide file tree
Showing 3 changed files with 274 additions and 33 deletions.
106 changes: 76 additions & 30 deletions components/style/properties/gecko.mako.rs
Expand Up @@ -3434,6 +3434,15 @@ fn static_assert() {
}
}

<%
# This array is several filter function which has percentage or
# number value for function of clone / set.
# The setting / cloning process of other function(e.g. Blur / HueRotate) is
# different from these function. So this array don't include such function.
FILTER_FUNCTIONS = [ 'Brightness', 'Contrast', 'Grayscale', 'Invert',
'Opacity', 'Saturate', 'Sepia' ]
%>

pub fn set_filter(&mut self, v: longhands::filter::computed_value::T) {
use properties::longhands::filter::computed_value::Filter::*;
use gecko_bindings::structs::nsCSSShadowArray;
Expand All @@ -3460,35 +3469,20 @@ fn static_assert() {
debug_assert!(v.filters.len() == self.gecko.mFilters.len());

for (servo, gecko_filter) in v.filters.into_iter().zip(self.gecko.mFilters.iter_mut()) {
//TODO: URL, drop-shadow
match servo {
Blur(len) => fill_filter(NS_STYLE_FILTER_BLUR,
CoordDataValue::Coord(len.0),
gecko_filter),
Brightness(factor) => fill_filter(NS_STYLE_FILTER_BRIGHTNESS,
CoordDataValue::Factor(factor),
gecko_filter),
Contrast(factor) => fill_filter(NS_STYLE_FILTER_CONTRAST,
CoordDataValue::Factor(factor),
gecko_filter),
Grayscale(factor) => fill_filter(NS_STYLE_FILTER_GRAYSCALE,
CoordDataValue::Factor(factor),
gecko_filter),
HueRotate(angle) => fill_filter(NS_STYLE_FILTER_HUE_ROTATE,
CoordDataValue::from(angle),
gecko_filter),
Invert(factor) => fill_filter(NS_STYLE_FILTER_INVERT,
CoordDataValue::Factor(factor),
gecko_filter),
Opacity(factor) => fill_filter(NS_STYLE_FILTER_OPACITY,
CoordDataValue::Factor(factor),
gecko_filter),
Saturate(factor) => fill_filter(NS_STYLE_FILTER_SATURATE,
CoordDataValue::Factor(factor),
gecko_filter),
Sepia(factor) => fill_filter(NS_STYLE_FILTER_SEPIA,
CoordDataValue::Factor(factor),
gecko_filter),
% for func in FILTER_FUNCTIONS:
${func}(factor) => fill_filter(NS_STYLE_FILTER_${func.upper()},
CoordDataValue::Factor(factor),
gecko_filter),
% endfor
Blur(length) => fill_filter(NS_STYLE_FILTER_BLUR,
CoordDataValue::Coord(length.0),
gecko_filter),

HueRotate(angle) => fill_filter(NS_STYLE_FILTER_HUE_ROTATE,
CoordDataValue::from(angle),
gecko_filter),

DropShadow(shadow) => {
gecko_filter.mType = NS_STYLE_FILTER_DROP_SHADOW;

Expand All @@ -3504,12 +3498,12 @@ fn static_assert() {

let mut gecko_shadow = init_shadow(gecko_filter);
gecko_shadow.mArray[0].set_from_shadow(shadow);
}
},
Url(ref url) => {
unsafe {
bindings::Gecko_nsStyleFilter_SetURLValue(gecko_filter, url.for_ffi());
}
}
},
}
}
}
Expand All @@ -3519,6 +3513,58 @@ fn static_assert() {
Gecko_CopyFiltersFrom(&other.gecko as *const _ as *mut _, &mut self.gecko);
}
}

pub fn clone_filter(&self) -> longhands::filter::computed_value::T {
use properties::longhands::filter::computed_value::Filter::*;
use values::specified::url::SpecifiedUrl;
use gecko_bindings::structs::NS_STYLE_FILTER_BLUR;
use gecko_bindings::structs::NS_STYLE_FILTER_BRIGHTNESS;
use gecko_bindings::structs::NS_STYLE_FILTER_CONTRAST;
use gecko_bindings::structs::NS_STYLE_FILTER_GRAYSCALE;
use gecko_bindings::structs::NS_STYLE_FILTER_INVERT;
use gecko_bindings::structs::NS_STYLE_FILTER_OPACITY;
use gecko_bindings::structs::NS_STYLE_FILTER_SATURATE;
use gecko_bindings::structs::NS_STYLE_FILTER_SEPIA;
use gecko_bindings::structs::NS_STYLE_FILTER_HUE_ROTATE;
use gecko_bindings::structs::NS_STYLE_FILTER_DROP_SHADOW;
use gecko_bindings::structs::NS_STYLE_FILTER_URL;

let mut filters = Vec::new();
for filter in self.gecko.mFilters.iter(){
match filter.mType {
% for func in FILTER_FUNCTIONS:
NS_STYLE_FILTER_${func.upper()} => {
filters.push(${func}(
GeckoStyleCoordConvertible::from_gecko_style_coord(
&filter.mFilterParameter).unwrap()));
},
% endfor
NS_STYLE_FILTER_BLUR => {
filters.push(Blur(Au::from_gecko_style_coord(
&filter.mFilterParameter).unwrap()));
},
NS_STYLE_FILTER_HUE_ROTATE => {
filters.push(HueRotate(
GeckoStyleCoordConvertible::from_gecko_style_coord(
&filter.mFilterParameter).unwrap()));
},
NS_STYLE_FILTER_DROP_SHADOW => {
filters.push(unsafe {
DropShadow((**filter.__bindgen_anon_1.mDropShadow.as_ref()).mArray[0].to_shadow())
});
},
NS_STYLE_FILTER_URL => {
filters.push(unsafe {
(Url(SpecifiedUrl::from_url_value_data(
&(**filter.__bindgen_anon_1.mURL.as_ref())._base).unwrap()))
});
}
_ => {},
}
}
longhands::filter::computed_value::T::new(filters)
}

</%self:impl_trait>

<%self:impl_trait style_struct_name="InheritedBox"
Expand Down
198 changes: 197 additions & 1 deletion components/style/properties/helpers/animated_properties.mako.rs
Expand Up @@ -14,9 +14,12 @@ use euclid::{Point2D, Size2D};
#[cfg(feature = "gecko")] use gecko_bindings::structs::nsCSSPropertyID;
#[cfg(feature = "gecko")] use gecko_bindings::sugar::ownership::{HasFFI, HasSimpleFFI};
#[cfg(feature = "gecko")] use gecko_string_cache::Atom;
#[cfg(feature = "gecko")] use gecko::url::SpecifiedUrl;
use properties::{CSSWideKeyword, PropertyDeclaration};
use properties::longhands;
use properties::longhands::background_size::computed_value::T as BackgroundSizeList;
use properties::longhands::filter::computed_value::Filter;
use properties::longhands::filter::computed_value::T as Filters;
use properties::longhands::font_weight::computed_value::T as FontWeight;
use properties::longhands::font_stretch::computed_value::T as FontStretch;
use properties::longhands::text_shadow::computed_value::T as TextShadowList;
Expand Down Expand Up @@ -3016,7 +3019,7 @@ impl Animatable for IntermediateSVGPaintKind {
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
#[allow(missing_docs)]
/// Intermediate type for box-shadow and text-shadow.
/// The difference between normal shadow type is that this type uses
/// The difference from normal shadow type is that this type uses
/// IntermediateColor instead of ParserColor.
pub struct IntermediateShadow {
pub offset_x: Au,
Expand Down Expand Up @@ -3192,3 +3195,196 @@ impl Animatable for IntermediateShadowList {
Ok(IntermediateShadowList(result))
}
}

/// Intermediate type for filter property.
/// The difference from normal filter type is that this structure uses
/// IntermediateColor into DropShadow's value.
pub type IntermediateFilters = Vec<IntermediateFilter>;

#[derive(Clone, PartialEq, Debug)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
#[allow(missing_docs)]
pub enum IntermediateFilter {
Blur(Au),
Brightness(CSSFloat),
Contrast(CSSFloat),
Grayscale(CSSFloat),
HueRotate(Angle),
Invert(CSSFloat),
Opacity(CSSFloat),
Saturate(CSSFloat),
Sepia(CSSFloat),
% if product == "gecko":
DropShadow(IntermediateShadow),
Url(SpecifiedUrl),
% endif
}

impl From<Filters> for IntermediateFilters {
fn from(filters: Filters) -> IntermediateFilters {
filters.filters.into_iter().map(|f| f.into()).collect()
}
}

impl From<IntermediateFilters> for Filters {
fn from(filters: IntermediateFilters) -> Filters {
Filters::new(filters.into_iter().map(|f| f.into()).collect())
}
}

<%
FILTER_FUNCTIONS = [ 'Blur', 'Brightness', 'Contrast', 'Grayscale',
'HueRotate', 'Invert', 'Opacity', 'Saturate',
'Sepia' ]
%>

impl From<Filter> for IntermediateFilter {
fn from(filter: Filter) -> IntermediateFilter {
use properties::longhands::filter::computed_value::Filter::*;
match filter {
% for func in FILTER_FUNCTIONS:
${func}(val) => IntermediateFilter::${func}(val),
% endfor
% if product == "gecko":
DropShadow(shadow) => {
IntermediateFilter::DropShadow(shadow.into())
},
Url(ref url) => {
IntermediateFilter::Url(url.clone())
},
% endif
}
}
}

impl From<IntermediateFilter> for Filter {
fn from(filter: IntermediateFilter) -> Filter {
match filter {
% for func in FILTER_FUNCTIONS:
IntermediateFilter::${func}(val) => Filter::${func}(val),
% endfor
% if product == "gecko":
IntermediateFilter::DropShadow(shadow) => {
Filter::DropShadow(shadow.into())
},
IntermediateFilter::Url(ref url) => {
Filter::Url(url.clone())
},
% endif
}
}
}

/// https://drafts.fxtf.org/filters/#animation-of-filters
fn add_weighted_filter_function_impl(from: &IntermediateFilter,
to: &IntermediateFilter,
self_portion: f64,
other_portion: f64)
-> Result<IntermediateFilter, ()> {
match (from, to) {
% for func in [ 'Blur', 'HueRotate' ]:
(&IntermediateFilter::${func}(from_value),
&IntermediateFilter::${func}(to_value)) => {
Ok(IntermediateFilter::${func}(
try!(from_value.add_weighted(&to_value,
self_portion,
other_portion))))
},
% endfor
% for func in [ 'Grayscale', 'Invert', 'Sepia' ]:
(&IntermediateFilter::${func}(from_value),
&IntermediateFilter::${func}(to_value)) => {
Ok(IntermediateFilter::${func}(try!(
add_weighted_with_initial_val(&from_value,
&to_value,
self_portion,
other_portion,
&0.0))))
},
% endfor
% for func in [ 'Brightness', 'Contrast', 'Opacity', 'Saturate' ]:
(&IntermediateFilter::${func}(from_value),
&IntermediateFilter::${func}(to_value)) => {
Ok(IntermediateFilter::${func}(try!(
add_weighted_with_initial_val(&from_value,
&to_value,
self_portion,
other_portion,
&1.0))))
},
% endfor
% if product == "gecko":
(&IntermediateFilter::DropShadow(from_value),
&IntermediateFilter::DropShadow(to_value)) => {
Ok(IntermediateFilter::DropShadow(try!(
from_value.add_weighted(&to_value,
self_portion,
other_portion))))
},
(&IntermediateFilter::Url(_),
&IntermediateFilter::Url(_)) => {
Err(())
},
% endif
_ => {
// If specified the different filter functions,
// we will need to interpolate as discreate.
Err(())
},
}
}

/// https://drafts.fxtf.org/filters/#animation-of-filters
fn add_weighted_filter_function(from: Option<<&IntermediateFilter>,
to: Option<<&IntermediateFilter>,
self_portion: f64,
other_portion: f64) -> Result<IntermediateFilter, ()> {
match (from, to) {
(Some(f), Some(t)) => {
add_weighted_filter_function_impl(f, t, self_portion, other_portion)
},
(Some(f), None) => {
add_weighted_filter_function_impl(f, f, self_portion, 0.0)
},
(None, Some(t)) => {
add_weighted_filter_function_impl(t, t, other_portion, 0.0)
},
_ => { Err(()) }
}
}


impl Animatable for IntermediateFilters {
#[inline]
fn add_weighted(&self, other: &Self,
self_portion: f64, other_portion: f64) -> Result<Self, ()> {
let mut filters: IntermediateFilters = Vec::new();
let mut from_iter = self.iter();
let mut to_iter = (&other).iter();

let mut from = from_iter.next();
let mut to = to_iter.next();
while (from,to) != (None, None) {
filters.push(try!(add_weighted_filter_function(from,
to,
self_portion,
other_portion)));
if from != None {
from = from_iter.next();
}
if to != None {
to = to_iter.next();
}
}

Ok(filters)
}

fn add(&self, other: &Self) -> Result<Self, ()> {
let from_list = &self;
let to_list = &other;
let filters: IntermediateFilters =
vec![&from_list[..], &to_list[..]].concat();
Ok(filters)
}
}
3 changes: 1 addition & 2 deletions components/style/properties/longhand/effects.mako.rs
Expand Up @@ -41,8 +41,7 @@ ${helpers.predefined_type("clip",
allow_quirks=True,
spec="https://drafts.fxtf.org/css-masking/#clip-property")}

// FIXME: This prop should be animatable
<%helpers:longhand name="filter" animation_value_type="none" extra_prefixes="webkit"
<%helpers:longhand name="filter" animation_value_type="IntermediateFilters" extra_prefixes="webkit"
flags="CREATES_STACKING_CONTEXT FIXPOS_CB"
spec="https://drafts.fxtf.org/filters/#propdef-filter">
//pub use self::computed_value::T as SpecifiedValue;
Expand Down

0 comments on commit 87e580a

Please sign in to comment.