mirror of
https://github.com/servo/servo.git
synced 2025-08-27 08:08:19 +01:00
Introduce style::values::animated::color
Starting to remove stuff from style::properties::animated_properties for real.
This commit is contained in:
parent
60c44b072c
commit
49204e2c72
14 changed files with 377 additions and 317 deletions
214
components/style/values/animated/color.rs
Normal file
214
components/style/values/animated/color.rs
Normal file
|
@ -0,0 +1,214 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
//! Animated types for CSS colors.
|
||||
|
||||
use properties::animated_properties::Animatable;
|
||||
use values::animated::ToAnimatedZero;
|
||||
use values::distance::{ComputeSquaredDistance, SquaredDistance};
|
||||
|
||||
/// An animated RGBA color.
|
||||
///
|
||||
/// Unlike in computed values, each component value may exceed the
|
||||
/// range `[0.0, 1.0]`.
|
||||
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub struct RGBA {
|
||||
/// The red component.
|
||||
pub red: f32,
|
||||
/// The green component.
|
||||
pub green: f32,
|
||||
/// The blue component.
|
||||
pub blue: f32,
|
||||
/// The alpha component.
|
||||
pub alpha: f32,
|
||||
}
|
||||
|
||||
impl RGBA {
|
||||
/// Returns a transparent color.
|
||||
#[inline]
|
||||
pub fn transparent() -> Self {
|
||||
Self::new(0., 0., 0., 0.)
|
||||
}
|
||||
|
||||
/// Returns a new color.
|
||||
#[inline]
|
||||
pub fn new(red: f32, green: f32, blue: f32, alpha: f32) -> Self {
|
||||
RGBA { red: red, green: green, blue: blue, alpha: alpha }
|
||||
}
|
||||
}
|
||||
|
||||
/// Unlike Animatable for computed colors, we don't clamp any component values.
|
||||
///
|
||||
/// FIXME(nox): Why do computed colors even implement Animatable?
|
||||
impl Animatable for RGBA {
|
||||
#[inline]
|
||||
fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
|
||||
let mut alpha = self.alpha.add_weighted(&other.alpha, self_portion, other_portion)?;
|
||||
if alpha <= 0. {
|
||||
// Ideally we should return color value that only alpha component is
|
||||
// 0, but this is what current gecko does.
|
||||
return Ok(RGBA::transparent());
|
||||
}
|
||||
|
||||
alpha = alpha.min(1.);
|
||||
let red = (self.red * self.alpha).add_weighted(
|
||||
&(other.red * other.alpha), self_portion, other_portion
|
||||
)? * 1. / alpha;
|
||||
let green = (self.green * self.alpha).add_weighted(
|
||||
&(other.green * other.alpha), self_portion, other_portion
|
||||
)? * 1. / alpha;
|
||||
let blue = (self.blue * self.alpha).add_weighted(
|
||||
&(other.blue * other.alpha), self_portion, other_portion
|
||||
)? * 1. / alpha;
|
||||
|
||||
Ok(RGBA::new(red, green, blue, alpha))
|
||||
}
|
||||
}
|
||||
|
||||
impl ComputeSquaredDistance for RGBA {
|
||||
#[inline]
|
||||
fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
|
||||
let start = [ self.alpha, self.red * self.alpha, self.green * self.alpha, self.blue * self.alpha ];
|
||||
let end = [ other.alpha, other.red * other.alpha, other.green * other.alpha, other.blue * other.alpha ];
|
||||
start.iter().zip(&end).map(|(this, other)| this.compute_squared_distance(other)).sum()
|
||||
}
|
||||
}
|
||||
|
||||
impl ToAnimatedZero for RGBA {
|
||||
#[inline]
|
||||
fn to_animated_zero(&self) -> Result<Self, ()> {
|
||||
Ok(RGBA::transparent())
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(missing_docs)]
|
||||
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub struct Color {
|
||||
pub color: RGBA,
|
||||
pub foreground_ratio: f32,
|
||||
}
|
||||
|
||||
impl Color {
|
||||
fn currentcolor() -> Self {
|
||||
Color {
|
||||
color: RGBA::transparent(),
|
||||
foreground_ratio: 1.,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a transparent intermediate color.
|
||||
pub fn transparent() -> Self {
|
||||
Color {
|
||||
color: RGBA::transparent(),
|
||||
foreground_ratio: 0.,
|
||||
}
|
||||
}
|
||||
|
||||
fn is_currentcolor(&self) -> bool {
|
||||
self.foreground_ratio >= 1.
|
||||
}
|
||||
|
||||
fn is_numeric(&self) -> bool {
|
||||
self.foreground_ratio <= 0.
|
||||
}
|
||||
|
||||
fn effective_intermediate_rgba(&self) -> RGBA {
|
||||
RGBA {
|
||||
alpha: self.color.alpha * (1. - self.foreground_ratio),
|
||||
.. self.color
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Animatable for Color {
|
||||
#[inline]
|
||||
fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
|
||||
// Common cases are interpolating between two numeric colors,
|
||||
// two currentcolors, and a numeric color and a currentcolor.
|
||||
//
|
||||
// Note: this algorithm assumes self_portion + other_portion
|
||||
// equals to one, so it may be broken for additive operation.
|
||||
// To properly support additive color interpolation, we would
|
||||
// need two ratio fields in computed color types.
|
||||
if self.foreground_ratio == other.foreground_ratio {
|
||||
if self.is_currentcolor() {
|
||||
Ok(Color::currentcolor())
|
||||
} else {
|
||||
Ok(Color {
|
||||
color: self.color.add_weighted(&other.color, self_portion, other_portion)?,
|
||||
foreground_ratio: self.foreground_ratio,
|
||||
})
|
||||
}
|
||||
} else if self.is_currentcolor() && other.is_numeric() {
|
||||
Ok(Color {
|
||||
color: other.color,
|
||||
foreground_ratio: self_portion as f32,
|
||||
})
|
||||
} else if self.is_numeric() && other.is_currentcolor() {
|
||||
Ok(Color {
|
||||
color: self.color,
|
||||
foreground_ratio: other_portion as f32,
|
||||
})
|
||||
} else {
|
||||
// For interpolating between two complex colors, we need to
|
||||
// generate colors with effective alpha value.
|
||||
let self_color = self.effective_intermediate_rgba();
|
||||
let other_color = other.effective_intermediate_rgba();
|
||||
let color = self_color.add_weighted(&other_color, self_portion, other_portion)?;
|
||||
// Then we compute the final foreground ratio, and derive
|
||||
// the final alpha value from the effective alpha value.
|
||||
let foreground_ratio = self.foreground_ratio
|
||||
.add_weighted(&other.foreground_ratio, self_portion, other_portion)?;
|
||||
let alpha = color.alpha / (1. - foreground_ratio);
|
||||
Ok(Color {
|
||||
color: RGBA {
|
||||
alpha: alpha,
|
||||
.. color
|
||||
},
|
||||
foreground_ratio: foreground_ratio,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ComputeSquaredDistance for Color {
|
||||
#[inline]
|
||||
fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
|
||||
// All comments in add_weighted also applies here.
|
||||
if self.foreground_ratio == other.foreground_ratio {
|
||||
if self.is_currentcolor() {
|
||||
Ok(SquaredDistance::Value(0.))
|
||||
} else {
|
||||
self.color.compute_squared_distance(&other.color)
|
||||
}
|
||||
} else if self.is_currentcolor() && other.is_numeric() {
|
||||
Ok(
|
||||
RGBA::transparent().compute_squared_distance(&other.color)? +
|
||||
SquaredDistance::Value(1.),
|
||||
)
|
||||
} else if self.is_numeric() && other.is_currentcolor() {
|
||||
Ok(
|
||||
self.color.compute_squared_distance(&RGBA::transparent())? +
|
||||
SquaredDistance::Value(1.),
|
||||
)
|
||||
} else {
|
||||
let self_color = self.effective_intermediate_rgba();
|
||||
let other_color = other.effective_intermediate_rgba();
|
||||
Ok(
|
||||
self_color.compute_squared_distance(&other_color)? +
|
||||
self.foreground_ratio.compute_squared_distance(&other.foreground_ratio)?,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ToAnimatedZero for Color {
|
||||
#[inline]
|
||||
fn to_animated_zero(&self) -> Result<Self, ()> {
|
||||
/// FIXME(nox): This does not look correct to me.
|
||||
Err(())
|
||||
}
|
||||
}
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
//! Animated types for CSS values related to effects.
|
||||
|
||||
use properties::animated_properties::{Animatable, IntermediateColor};
|
||||
use properties::animated_properties::Animatable;
|
||||
use properties::longhands::box_shadow::computed_value::T as ComputedBoxShadowList;
|
||||
use properties::longhands::filter::computed_value::T as ComputedFilterList;
|
||||
use properties::longhands::text_shadow::computed_value::T as ComputedTextShadowList;
|
||||
|
@ -12,6 +12,7 @@ use std::cmp;
|
|||
#[cfg(not(feature = "gecko"))]
|
||||
use values::Impossible;
|
||||
use values::animated::{ToAnimatedValue, ToAnimatedZero};
|
||||
use values::animated::color::Color;
|
||||
use values::computed::{Angle, NonNegativeNumber};
|
||||
use values::computed::length::{Length, NonNegativeLength};
|
||||
use values::distance::{ComputeSquaredDistance, SquaredDistance};
|
||||
|
@ -33,7 +34,7 @@ pub type TextShadowList = ShadowList<SimpleShadow>;
|
|||
pub struct ShadowList<Shadow>(Vec<Shadow>);
|
||||
|
||||
/// An animated value for a single `box-shadow`.
|
||||
pub type BoxShadow = GenericBoxShadow<IntermediateColor, Length, NonNegativeLength, Length>;
|
||||
pub type BoxShadow = GenericBoxShadow<Color, Length, NonNegativeLength, Length>;
|
||||
|
||||
/// An animated value for the `filter` property.
|
||||
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
||||
|
@ -49,7 +50,7 @@ pub type Filter = GenericFilter<Angle, NonNegativeNumber, NonNegativeLength, Sim
|
|||
pub type Filter = GenericFilter<Angle, NonNegativeNumber, NonNegativeLength, Impossible>;
|
||||
|
||||
/// An animated value for the `drop-shadow()` filter.
|
||||
pub type SimpleShadow = GenericSimpleShadow<IntermediateColor, Length, NonNegativeLength>;
|
||||
pub type SimpleShadow = GenericSimpleShadow<Color, Length, NonNegativeLength>;
|
||||
|
||||
impl ToAnimatedValue for ComputedBoxShadowList {
|
||||
type AnimatedValue = BoxShadowList;
|
||||
|
@ -231,7 +232,7 @@ impl ToAnimatedZero for SimpleShadow {
|
|||
#[inline]
|
||||
fn to_animated_zero(&self) -> Result<Self, ()> {
|
||||
Ok(SimpleShadow {
|
||||
color: IntermediateColor::transparent(),
|
||||
color: Color::transparent(),
|
||||
horizontal: self.horizontal.to_animated_zero()?,
|
||||
vertical: self.vertical.to_animated_zero()?,
|
||||
blur: self.blur.to_animated_zero()?,
|
||||
|
|
|
@ -24,6 +24,7 @@ use values::computed::NonNegativeNumber as ComputedNonNegativeNumber;
|
|||
use values::computed::PositiveInteger as ComputedPositiveInteger;
|
||||
use values::specified::url::SpecifiedUrl;
|
||||
|
||||
pub mod color;
|
||||
pub mod effects;
|
||||
|
||||
/// Conversion between computed values and intermediate values for animations.
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue