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`.
#[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`.
#[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};
#[cfg(feature = "gecko")]
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::Filter as GenericFilter;
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`.
#[cfg(feature = "gecko")]
pub type Filter =
GenericFilter<Angle, NonNegativeNumber, NonNegativeLength, SimpleShadow, ComputedUrl>;
GenericFilter<Angle, NonNegativeNumber, ZeroToOneNumber, NonNegativeLength, SimpleShadow, ComputedUrl>;
/// A computed value for a single `filter`.
#[cfg(feature = "servo")]
pub type Filter =
GenericFilter<Angle, NonNegativeNumber, NonNegativeLength, Impossible, Impossible>;
GenericFilter<Angle, NonNegativeNumber, ZeroToOneNumber, NonNegativeLength, Impossible, Impossible>;
/// A computed value for the `drop-shadow()` filter.
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::{TrackList as GenericTrackList, TrackSize as GenericTrackSize};
use super::generics::transform::IsParallelTo;
use super::generics::{self, GreaterThanOrEqualToOne, NonNegative};
use super::generics::{self, GreaterThanOrEqualToOne, NonNegative, ZeroToOne};
use super::specified;
use super::{CSSFloat, CSSInteger};
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.
pub type GreaterThanOrEqualToOneNumber = GreaterThanOrEqualToOne<CSSFloat>;

View file

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

View file

@ -227,6 +227,28 @@ impl<T: Zero> Zero for NonNegative<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
#[allow(missing_docs)]
#[derive(

View file

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