mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-10 11:55:49 +00:00
servo: Merge #17288 - Make filter property animatable (from mantaroh:filter); r=hiro
<!-- Please describe your changes on the following line: --> This is a PR for https://bugzilla.mozilla.org/show_bug.cgi?id=1362897 --- <!-- Thank you for contributing to Servo! Please replace each `[ ]` by `[X]` when the step is complete, and replace `__` with appropriate data: --> - [X] `./mach build -d` does not report any errors - [X] `./mach test-tidy` does not report any errors <!-- Either: --> There are tests for these changes, a test case will be landed in web-platform-tests in https://bugzilla.mozilla.org/show_bug.cgi?id=1362897 <!-- Also, please make sure that "Allow edits from maintainers" checkbox is checked, so that we can help you if you get stuck somewhere along the way.--> <!-- Pull requests that do not address these steps are welcome, but they will require additional verification as part of the review process. --> Source-Repo: https://github.com/servo/servo Source-Revision: e41b7d06b473544755902be2c1334a7622c84940 --HG-- extra : subtree_source : https%3A//hg.mozilla.org/projects/converted-servo-linear extra : subtree_revision : 2262798313c19fcbff756b6b1aa95482b547ad1d
This commit is contained in:
parent
3157086820
commit
0cda7f5772
@ -5,6 +5,7 @@
|
||||
//! Common handling for the specified value CSS url() values.
|
||||
|
||||
use gecko_bindings::structs::{ServoBundledURI, URLExtraData};
|
||||
use gecko_bindings::structs::mozilla::css::URLValueData;
|
||||
use gecko_bindings::structs::root::mozilla::css::ImageValue;
|
||||
use gecko_bindings::sugar::refptr::RefPtr;
|
||||
use parser::ParserContext;
|
||||
@ -51,6 +52,16 @@ impl SpecifiedUrl {
|
||||
false
|
||||
}
|
||||
|
||||
/// Convert from URLValueData to SpecifiedUrl.
|
||||
pub unsafe fn from_url_value_data(url: &URLValueData)
|
||||
-> Result<SpecifiedUrl, ()> {
|
||||
Ok(SpecifiedUrl {
|
||||
serialization: Arc::new(url.mString.to_string()),
|
||||
extra_data: url.mExtraData.to_safe(),
|
||||
image_value: None,
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns true if this URL looks like a fragment.
|
||||
/// See https://drafts.csswg.org/css-values/#local-urls
|
||||
pub fn is_fragment(&self) -> bool {
|
||||
|
@ -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;
|
||||
@ -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;
|
||||
|
||||
@ -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());
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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"
|
||||
|
@ -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;
|
||||
@ -936,9 +939,20 @@ impl Animatable for i32 {
|
||||
impl Animatable for Angle {
|
||||
#[inline]
|
||||
fn add_weighted(&self, other: &Angle, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
|
||||
self.radians()
|
||||
.add_weighted(&other.radians(), self_portion, other_portion)
|
||||
.map(Angle::from_radians)
|
||||
match (*self, *other) {
|
||||
% for angle_type in [ 'Degree', 'Gradian', 'Turn' ]:
|
||||
(Angle::${angle_type}(val1), Angle::${angle_type}(val2)) => {
|
||||
Ok(Angle::${angle_type}(
|
||||
try!(val1.add_weighted(&val2, self_portion, other_portion))
|
||||
))
|
||||
}
|
||||
% endfor
|
||||
_ => {
|
||||
self.radians()
|
||||
.add_weighted(&other.radians(), self_portion, other_portion)
|
||||
.map(Angle::from_radians)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -3005,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,
|
||||
@ -3181,3 +3195,257 @@ 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(()) }
|
||||
}
|
||||
}
|
||||
|
||||
fn compute_filter_square_distance(from: &IntermediateFilter,
|
||||
to: &IntermediateFilter)
|
||||
-> Result<f64, ()> {
|
||||
match (from, to) {
|
||||
% for func in FILTER_FUNCTIONS :
|
||||
(&IntermediateFilter::${func}(f),
|
||||
&IntermediateFilter::${func}(t)) => {
|
||||
Ok(try!(f.compute_squared_distance(&t)))
|
||||
},
|
||||
% endfor
|
||||
% if product == "gecko":
|
||||
(&IntermediateFilter::DropShadow(f),
|
||||
&IntermediateFilter::DropShadow(t)) => {
|
||||
Ok(try!(f.compute_squared_distance(&t)))
|
||||
},
|
||||
% endif
|
||||
_ => {
|
||||
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)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn compute_distance(&self, other: &Self) -> Result<f64, ()> {
|
||||
self.compute_squared_distance(other).map(|sd| sd.sqrt())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn compute_squared_distance(&self, other: &Self) -> Result<f64, ()> {
|
||||
let mut square_distance: f64 = 0.0;
|
||||
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) {
|
||||
let current_square_distance: f64 ;
|
||||
if from == None {
|
||||
let none = try!(add_weighted_filter_function(to, to, 0.0, 0.0));
|
||||
current_square_distance =
|
||||
compute_filter_square_distance(&none, &(to.unwrap())).unwrap();
|
||||
|
||||
to = to_iter.next();
|
||||
} else if to == None {
|
||||
let none = try!(add_weighted_filter_function(from, from, 0.0, 0.0));
|
||||
current_square_distance =
|
||||
compute_filter_square_distance(&none, &(from.unwrap())).unwrap();
|
||||
|
||||
from = from_iter.next();
|
||||
} else {
|
||||
current_square_distance =
|
||||
compute_filter_square_distance(&(from.unwrap()),
|
||||
&(to.unwrap())).unwrap();
|
||||
|
||||
from = from_iter.next();
|
||||
to = to_iter.next();
|
||||
}
|
||||
square_distance += current_square_distance;
|
||||
}
|
||||
Ok(square_distance.sqrt())
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user