style: Add computed value ZeroToOneNumber.

Correctly handle clamping to 1 behavior of grayscale(),
invert(), opacity() and sepia().

Differential Revision: https://phabricator.services.mozilla.com/D35509
This commit is contained in:
violet 2019-06-23 23:01:47 +00:00 committed by Emilio Cobos Álvarez
parent 9939c1ee07
commit 2fba62aba9
No known key found for this signature in database
GPG key ID: E1152D0994E4BF8A
6 changed files with 132 additions and 73 deletions

View file

@ -19,8 +19,8 @@ pub type AnimatedSimpleShadow = GenericSimpleShadow<Color, Length, Length>;
/// An animated value for a single `filter`. /// An animated value for a single `filter`.
#[cfg(feature = "gecko")] #[cfg(feature = "gecko")]
pub type AnimatedFilter = GenericFilter<Angle, Number, Length, AnimatedSimpleShadow, ComputedUrl>; pub type AnimatedFilter = GenericFilter<Angle, Number, Number, Length, AnimatedSimpleShadow, ComputedUrl>;
/// An animated value for a single `filter`. /// An animated value for a single `filter`.
#[cfg(not(feature = "gecko"))] #[cfg(not(feature = "gecko"))]
pub type AnimatedFilter = GenericFilter<Angle, Number, Length, Impossible, Impossible>; pub type AnimatedFilter = GenericFilter<Angle, Number, Number, Length, Impossible, Impossible>;

View file

@ -8,7 +8,7 @@ use crate::values::computed::color::Color;
use crate::values::computed::length::{Length, NonNegativeLength}; use crate::values::computed::length::{Length, NonNegativeLength};
#[cfg(feature = "gecko")] #[cfg(feature = "gecko")]
use crate::values::computed::url::ComputedUrl; use crate::values::computed::url::ComputedUrl;
use crate::values::computed::{Angle, NonNegativeNumber}; use crate::values::computed::{Angle, ZeroToOneNumber, NonNegativeNumber};
use crate::values::generics::effects::BoxShadow as GenericBoxShadow; use crate::values::generics::effects::BoxShadow as GenericBoxShadow;
use crate::values::generics::effects::Filter as GenericFilter; use crate::values::generics::effects::Filter as GenericFilter;
use crate::values::generics::effects::SimpleShadow as GenericSimpleShadow; use crate::values::generics::effects::SimpleShadow as GenericSimpleShadow;
@ -21,12 +21,12 @@ pub type BoxShadow = GenericBoxShadow<Color, Length, NonNegativeLength, Length>;
/// A computed value for a single `filter`. /// A computed value for a single `filter`.
#[cfg(feature = "gecko")] #[cfg(feature = "gecko")]
pub type Filter = pub type Filter =
GenericFilter<Angle, NonNegativeNumber, NonNegativeLength, SimpleShadow, ComputedUrl>; GenericFilter<Angle, NonNegativeNumber, ZeroToOneNumber, NonNegativeLength, SimpleShadow, ComputedUrl>;
/// A computed value for a single `filter`. /// A computed value for a single `filter`.
#[cfg(feature = "servo")] #[cfg(feature = "servo")]
pub type Filter = pub type Filter =
GenericFilter<Angle, NonNegativeNumber, NonNegativeLength, Impossible, Impossible>; GenericFilter<Angle, NonNegativeNumber, ZeroToOneNumber, NonNegativeLength, Impossible, Impossible>;
/// A computed value for the `drop-shadow()` filter. /// A computed value for the `drop-shadow()` filter.
pub type SimpleShadow = GenericSimpleShadow<Color, Length, NonNegativeLength>; pub type SimpleShadow = GenericSimpleShadow<Color, Length, NonNegativeLength>;

View file

@ -10,7 +10,7 @@ use super::generics::grid::GridTemplateComponent as GenericGridTemplateComponent
use super::generics::grid::{GridLine as GenericGridLine, TrackBreadth as GenericTrackBreadth}; use super::generics::grid::{GridLine as GenericGridLine, TrackBreadth as GenericTrackBreadth};
use super::generics::grid::{TrackList as GenericTrackList, TrackSize as GenericTrackSize}; use super::generics::grid::{TrackList as GenericTrackList, TrackSize as GenericTrackSize};
use super::generics::transform::IsParallelTo; use super::generics::transform::IsParallelTo;
use super::generics::{self, GreaterThanOrEqualToOne, NonNegative}; use super::generics::{self, GreaterThanOrEqualToOne, NonNegative, ZeroToOne};
use super::specified; use super::specified;
use super::{CSSFloat, CSSInteger}; use super::{CSSFloat, CSSInteger};
use crate::context::QuirksMode; use crate::context::QuirksMode;
@ -519,6 +519,30 @@ impl From<NonNegativeNumber> for CSSFloat {
} }
} }
/// A wrapper of Number, but the value between 0 and 1
pub type ZeroToOneNumber = ZeroToOne<CSSFloat>;
impl ToAnimatedValue for ZeroToOneNumber {
type AnimatedValue = CSSFloat;
#[inline]
fn to_animated_value(self) -> Self::AnimatedValue {
self.0
}
#[inline]
fn from_animated_value(animated: Self::AnimatedValue) -> Self {
Self(animated.max(0.).min(1.))
}
}
impl From<CSSFloat> for ZeroToOneNumber {
#[inline]
fn from(number: CSSFloat) -> Self {
Self(number)
}
}
/// A wrapper of Number, but the value >= 1. /// A wrapper of Number, but the value >= 1.
pub type GreaterThanOrEqualToOneNumber = GreaterThanOrEqualToOne<CSSFloat>; pub type GreaterThanOrEqualToOneNumber = GreaterThanOrEqualToOne<CSSFloat>;

View file

@ -52,34 +52,34 @@ pub use self::GenericBoxShadow as BoxShadow;
ToShmem, ToShmem,
)] )]
#[repr(C, u8)] #[repr(C, u8)]
pub enum GenericFilter<Angle, Factor, Length, Shadow, U> { pub enum GenericFilter<Angle, NonNegativeFactor, ZeroToOneFactor, Length, Shadow, U> {
/// `blur(<length>)` /// `blur(<length>)`
#[css(function)] #[css(function)]
Blur(Length), Blur(Length),
/// `brightness(<factor>)` /// `brightness(<factor>)`
#[css(function)] #[css(function)]
Brightness(Factor), Brightness(NonNegativeFactor),
/// `contrast(<factor>)` /// `contrast(<factor>)`
#[css(function)] #[css(function)]
Contrast(Factor), Contrast(NonNegativeFactor),
/// `grayscale(<factor>)` /// `grayscale(<factor>)`
#[css(function)] #[css(function)]
Grayscale(Factor), Grayscale(ZeroToOneFactor),
/// `hue-rotate(<angle>)` /// `hue-rotate(<angle>)`
#[css(function)] #[css(function)]
HueRotate(Angle), HueRotate(Angle),
/// `invert(<factor>)` /// `invert(<factor>)`
#[css(function)] #[css(function)]
Invert(Factor), Invert(ZeroToOneFactor),
/// `opacity(<factor>)` /// `opacity(<factor>)`
#[css(function)] #[css(function)]
Opacity(Factor), Opacity(ZeroToOneFactor),
/// `saturate(<factor>)` /// `saturate(<factor>)`
#[css(function)] #[css(function)]
Saturate(Factor), Saturate(NonNegativeFactor),
/// `sepia(<factor>)` /// `sepia(<factor>)`
#[css(function)] #[css(function)]
Sepia(Factor), Sepia(ZeroToOneFactor),
/// `drop-shadow(...)` /// `drop-shadow(...)`
#[css(function)] #[css(function)]
DropShadow(Shadow), DropShadow(Shadow),

View file

@ -227,6 +227,28 @@ impl<T: Zero> Zero for NonNegative<T> {
)] )]
pub struct GreaterThanOrEqualToOne<T>(pub T); pub struct GreaterThanOrEqualToOne<T>(pub T);
/// A wrapper of values between zero and one.
#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
#[derive(
Animate,
Clone,
ComputeSquaredDistance,
Copy,
Debug,
Hash,
MallocSizeOf,
PartialEq,
PartialOrd,
SpecifiedValueInfo,
ToAnimatedZero,
ToComputedValue,
ToCss,
ToResolvedValue,
ToShmem,
)]
#[repr(transparent)]
pub struct ZeroToOne<T>(pub T);
/// A clip rect for clip and image-region /// A clip rect for clip and image-region
#[allow(missing_docs)] #[allow(missing_docs)]
#[derive( #[derive(

View file

@ -7,6 +7,7 @@
use crate::parser::{Parse, ParserContext}; use crate::parser::{Parse, ParserContext};
use crate::values::computed::effects::BoxShadow as ComputedBoxShadow; use crate::values::computed::effects::BoxShadow as ComputedBoxShadow;
use crate::values::computed::effects::SimpleShadow as ComputedSimpleShadow; use crate::values::computed::effects::SimpleShadow as ComputedSimpleShadow;
use crate::values::computed::ZeroToOneNumber as ComputedZeroToOneNumber;
use crate::values::computed::NonNegativeNumber as ComputedNonNegativeNumber; use crate::values::computed::NonNegativeNumber as ComputedNonNegativeNumber;
use crate::values::computed::{Context, ToComputedValue}; use crate::values::computed::{Context, ToComputedValue};
use crate::values::generics::effects::BoxShadow as GenericBoxShadow; use crate::values::generics::effects::BoxShadow as GenericBoxShadow;
@ -31,58 +32,44 @@ pub type BoxShadow =
/// A specified value for a single `filter`. /// A specified value for a single `filter`.
#[cfg(feature = "gecko")] #[cfg(feature = "gecko")]
pub type SpecifiedFilter = pub type SpecifiedFilter =
GenericFilter<Angle, Factor, NonNegativeLength, SimpleShadow, SpecifiedUrl>; GenericFilter<Angle, NonNegativeFactor, ZeroToOneFactor, NonNegativeLength, SimpleShadow, SpecifiedUrl>;
/// A specified value for a single `filter`. /// A specified value for a single `filter`.
#[cfg(feature = "servo")] #[cfg(feature = "servo")]
pub type SpecifiedFilter = GenericFilter<Angle, Factor, NonNegativeLength, Impossible, Impossible>; pub type SpecifiedFilter =
GenericFilter<Angle, NonNegativeFactor, ZeroToOneFactor, NonNegativeLength, Impossible, Impossible>;
pub use self::SpecifiedFilter as Filter; pub use self::SpecifiedFilter as Filter;
/// A value for the `<factor>` parts in `Filter`. /// A value for the `<factor>` parts in `Filter`.
#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)] #[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)]
pub struct Factor(NumberOrPercentage); pub struct NonNegativeFactor(NumberOrPercentage);
impl Factor { /// A value for the `<factor>` parts in `Filter` which clamps to one.
/// Parse this factor but clamp to one if the value is over 100%. #[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)]
#[inline] pub struct ZeroToOneFactor(NumberOrPercentage);
pub fn parse_with_clamping_to_one<'i, 't>(
context: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<Self, ParseError<'i>> {
Factor::parse(context, input).map(|v| v.clamp_to_one())
}
/// Clamp the value to 1 if the value is over 100%. /// Clamp the value to 1 if the value is over 100%.
#[inline] #[inline]
fn clamp_to_one(self) -> Self { fn clamp_to_one(number: NumberOrPercentage) -> NumberOrPercentage {
match self.0 { match number {
NumberOrPercentage::Percentage(percent) => { NumberOrPercentage::Percentage(percent) => {
Factor(NumberOrPercentage::Percentage(percent.clamp_to_hundred())) NumberOrPercentage::Percentage(percent.clamp_to_hundred())
},
NumberOrPercentage::Number(number) => {
Factor(NumberOrPercentage::Number(number.clamp_to_one()))
}, },
NumberOrPercentage::Number(number) => NumberOrPercentage::Number(number.clamp_to_one()),
} }
} }
macro_rules! factor_impl_common {
($ty:ty, $computed_ty:ty) => {
impl $ty {
fn one() -> Self { fn one() -> Self {
Factor(NumberOrPercentage::Number(Number::new(1.0))) Self(NumberOrPercentage::Number(Number::new(1.)))
} }
}
impl Parse for Factor {
#[inline]
fn parse<'i, 't>(
context: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<Self, ParseError<'i>> {
NumberOrPercentage::parse_non_negative(context, input).map(Factor)
} }
}
impl ToComputedValue for Factor { impl ToComputedValue for $ty {
type ComputedValue = ComputedNonNegativeNumber; type ComputedValue = $computed_ty;
#[inline] #[inline]
fn to_computed_value(&self, context: &Context) -> Self::ComputedValue { fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
@ -95,10 +82,36 @@ impl ToComputedValue for Factor {
#[inline] #[inline]
fn from_computed_value(computed: &Self::ComputedValue) -> Self { fn from_computed_value(computed: &Self::ComputedValue) -> Self {
Factor(NumberOrPercentage::Number( Self(NumberOrPercentage::Number(
ToComputedValue::from_computed_value(&computed.0), ToComputedValue::from_computed_value(&computed.0),
)) ))
} }
}
};
}
factor_impl_common!(NonNegativeFactor, ComputedNonNegativeNumber);
factor_impl_common!(ZeroToOneFactor, ComputedZeroToOneNumber);
impl Parse for NonNegativeFactor {
#[inline]
fn parse<'i, 't>(
context: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<Self, ParseError<'i>> {
NumberOrPercentage::parse_non_negative(context, input).map(Self)
}
}
impl Parse for ZeroToOneFactor {
#[inline]
fn parse<'i, 't>(
context: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<Self, ParseError<'i>> {
NumberOrPercentage::parse_non_negative(context, input)
.map(clamp_to_one)
.map(Self)
}
} }
/// A specified value for the `drop-shadow()` filter. /// A specified value for the `drop-shadow()` filter.
@ -221,19 +234,19 @@ impl Parse for Filter {
.unwrap_or(Zero::zero()), .unwrap_or(Zero::zero()),
)), )),
"brightness" => Ok(GenericFilter::Brightness( "brightness" => Ok(GenericFilter::Brightness(
i.try(|i| Factor::parse(context, i)) i.try(|i| NonNegativeFactor::parse(context, i))
.unwrap_or(Factor::one()), .unwrap_or(NonNegativeFactor::one()),
)), )),
"contrast" => Ok(GenericFilter::Contrast( "contrast" => Ok(GenericFilter::Contrast(
i.try(|i| Factor::parse(context, i)) i.try(|i| NonNegativeFactor::parse(context, i))
.unwrap_or(Factor::one()), .unwrap_or(NonNegativeFactor::one()),
)), )),
"grayscale" => { "grayscale" => {
// Values of amount over 100% are allowed but UAs must clamp the values to 1. // Values of amount over 100% are allowed but UAs must clamp the values to 1.
// https://drafts.fxtf.org/filter-effects/#funcdef-filter-grayscale // https://drafts.fxtf.org/filter-effects/#funcdef-filter-grayscale
Ok(GenericFilter::Grayscale( Ok(GenericFilter::Grayscale(
i.try(|i| Factor::parse_with_clamping_to_one(context, i)) i.try(|i| ZeroToOneFactor::parse(context, i))
.unwrap_or(Factor::one()), .unwrap_or(ZeroToOneFactor::one()),
)) ))
}, },
"hue-rotate" => { "hue-rotate" => {
@ -248,28 +261,28 @@ impl Parse for Filter {
// Values of amount over 100% are allowed but UAs must clamp the values to 1. // Values of amount over 100% are allowed but UAs must clamp the values to 1.
// https://drafts.fxtf.org/filter-effects/#funcdef-filter-invert // https://drafts.fxtf.org/filter-effects/#funcdef-filter-invert
Ok(GenericFilter::Invert( Ok(GenericFilter::Invert(
i.try(|i| Factor::parse_with_clamping_to_one(context, i)) i.try(|i| ZeroToOneFactor::parse(context, i))
.unwrap_or(Factor::one()), .unwrap_or(ZeroToOneFactor::one()),
)) ))
}, },
"opacity" => { "opacity" => {
// Values of amount over 100% are allowed but UAs must clamp the values to 1. // Values of amount over 100% are allowed but UAs must clamp the values to 1.
// https://drafts.fxtf.org/filter-effects/#funcdef-filter-opacity // https://drafts.fxtf.org/filter-effects/#funcdef-filter-opacity
Ok(GenericFilter::Opacity( Ok(GenericFilter::Opacity(
i.try(|i| Factor::parse_with_clamping_to_one(context, i)) i.try(|i| ZeroToOneFactor::parse(context, i))
.unwrap_or(Factor::one()), .unwrap_or(ZeroToOneFactor::one()),
)) ))
}, },
"saturate" => Ok(GenericFilter::Saturate( "saturate" => Ok(GenericFilter::Saturate(
i.try(|i| Factor::parse(context, i)) i.try(|i| NonNegativeFactor::parse(context, i))
.unwrap_or(Factor::one()), .unwrap_or(NonNegativeFactor::one()),
)), )),
"sepia" => { "sepia" => {
// Values of amount over 100% are allowed but UAs must clamp the values to 1. // Values of amount over 100% are allowed but UAs must clamp the values to 1.
// https://drafts.fxtf.org/filter-effects/#funcdef-filter-sepia // https://drafts.fxtf.org/filter-effects/#funcdef-filter-sepia
Ok(GenericFilter::Sepia( Ok(GenericFilter::Sepia(
i.try(|i| Factor::parse_with_clamping_to_one(context, i)) i.try(|i| ZeroToOneFactor::parse(context, i))
.unwrap_or(Factor::one()), .unwrap_or(ZeroToOneFactor::one()),
)) ))
}, },
"drop-shadow" => Ok(GenericFilter::DropShadow(Parse::parse(context, i)?)), "drop-shadow" => Ok(GenericFilter::DropShadow(Parse::parse(context, i)?)),