diff options
author | Xidorn Quan <me@upsuper.org> | 2017-06-08 10:42:31 +1000 |
---|---|---|
committer | Xidorn Quan <me@upsuper.org> | 2017-06-08 12:59:26 +1000 |
commit | c62935577a36ea98c9d625747cc3e4387ee9941e (patch) | |
tree | 4e716caa58a6ff080eff1007554ba6ca1ee50f94 | |
parent | 742c45f8599272fd77cd342ca81621daa76366b6 (diff) | |
download | servo-c62935577a36ea98c9d625747cc3e4387ee9941e.tar.gz servo-c62935577a36ea98c9d625747cc3e4387ee9941e.zip |
Add separate computed Color value.
20 files changed, 317 insertions, 145 deletions
diff --git a/Cargo.lock b/Cargo.lock index 38414c4669a..217bc8c7dec 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1419,7 +1419,6 @@ dependencies = [ "atomic_refcell 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "canvas_traits 0.0.1", - "cssparser 0.13.7 (registry+https://github.com/rust-lang/crates.io-index)", "euclid 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)", "fnv 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", "gfx 0.0.1", diff --git a/components/layout/Cargo.toml b/components/layout/Cargo.toml index 763ac1288a4..e9679887da6 100644 --- a/components/layout/Cargo.toml +++ b/components/layout/Cargo.toml @@ -14,7 +14,6 @@ app_units = "0.4.1" atomic_refcell = "0.1" bitflags = "0.7" canvas_traits = {path = "../canvas_traits"} -cssparser = "0.13.7" euclid = "0.13" fnv = "1.0" gfx = {path = "../gfx"} diff --git a/components/layout/lib.rs b/components/layout/lib.rs index 65923c10554..ca85d83e9d1 100644 --- a/components/layout/lib.rs +++ b/components/layout/lib.rs @@ -16,7 +16,6 @@ extern crate atomic_refcell; extern crate bitflags; extern crate canvas_traits; extern crate core; -extern crate cssparser; extern crate euclid; extern crate fnv; extern crate gfx; diff --git a/components/layout/table_cell.rs b/components/layout/table_cell.rs index 8c54297d4b5..47f74891488 100644 --- a/components/layout/table_cell.rs +++ b/components/layout/table_cell.rs @@ -9,7 +9,6 @@ use app_units::Au; use block::{BlockFlow, ISizeAndMarginsComputer, MarginsMayCollapseFlag}; use context::LayoutContext; -use cssparser::Color; use display_list_builder::{BlockFlowDisplayListBuilding, BorderPaintingMode, DisplayListBuildState}; use euclid::{Point2D, Rect, SideOffsets2D, Size2D}; use flow::{self, Flow, FlowClass, IS_ABSOLUTELY_POSITIONED, OpaqueFlow}; @@ -22,6 +21,7 @@ use std::fmt; use style::computed_values::{border_collapse, border_top_style, vertical_align}; use style::logical_geometry::{LogicalMargin, LogicalRect, LogicalSize, WritingMode}; use style::properties::ServoComputedValues; +use style::values::computed::Color; use table::InternalTable; use table_row::{CollapsedBorder, CollapsedBorderProvenance}; diff --git a/components/layout/table_row.rs b/components/layout/table_row.rs index 8d074f59c52..2fb61a336de 100644 --- a/components/layout/table_row.rs +++ b/components/layout/table_row.rs @@ -9,7 +9,6 @@ use app_units::Au; use block::{BlockFlow, ISizeAndMarginsComputer}; use context::LayoutContext; -use cssparser::{Color, RGBA}; use display_list_builder::{BlockFlowDisplayListBuilding, BorderPaintingMode, DisplayListBuildState}; use euclid::Point2D; use flow::{self, EarlyAbsolutePositionInfo, Flow, FlowClass, ImmutableFlowUtils, OpaqueFlow}; @@ -26,7 +25,7 @@ use style::computed_values::{border_collapse, border_spacing, border_top_style}; use style::logical_geometry::{LogicalSize, PhysicalSide, WritingMode}; use style::properties::ServoComputedValues; use style::servo::restyle_damage::{REFLOW, REFLOW_OUT_OF_FLOW}; -use style::values::computed::LengthOrPercentageOrAuto; +use style::values::computed::{Color, LengthOrPercentageOrAuto}; use table::{ColumnComputedInlineSize, ColumnIntrinsicInlineSize, InternalTable, VecExt}; use table_cell::{CollapsedBordersForCell, TableCellFlow}; @@ -606,7 +605,7 @@ impl CollapsedBorder { CollapsedBorder { style: border_top_style::T::none, width: Au(0), - color: Color::RGBA(RGBA::transparent()), + color: Color::transparent(), provenance: CollapsedBorderProvenance::FromTable, } } diff --git a/components/style/gecko_bindings/sugar/ns_css_shadow_item.rs b/components/style/gecko_bindings/sugar/ns_css_shadow_item.rs index 003da073b23..fe818d71330 100644 --- a/components/style/gecko_bindings/sugar/ns_css_shadow_item.rs +++ b/components/style/gecko_bindings/sugar/ns_css_shadow_item.rs @@ -5,10 +5,9 @@ //! Rust helpers for Gecko's `nsCSSShadowItem`. use app_units::Au; -use cssparser::Color; use gecko::values::{convert_rgba_to_nscolor, convert_nscolor_to_rgba}; use gecko_bindings::structs::nsCSSShadowItem; -use values::computed::Shadow; +use values::computed::{Color, Shadow}; impl nsCSSShadowItem { /// Set this item to the given shadow value. @@ -18,14 +17,14 @@ impl nsCSSShadowItem { self.mRadius = other.blur_radius.0; self.mSpread = other.spread_radius.0; self.mInset = other.inset; - self.mColor = match other.color { - Color::RGBA(rgba) => { - self.mHasColor = true; - convert_rgba_to_nscolor(&rgba) - }, + if other.color.is_currentcolor() { // TODO handle currentColor // https://bugzilla.mozilla.org/show_bug.cgi?id=760345 - Color::CurrentColor => 0, + self.mHasColor = false; + self.mColor = 0; + } else { + self.mHasColor = true; + self.mColor = convert_rgba_to_nscolor(&other.color.color); } } @@ -37,7 +36,7 @@ impl nsCSSShadowItem { blur_radius: Au(self.mRadius), spread_radius: Au(self.mSpread), inset: self.mInset, - color: Color::RGBA(convert_nscolor_to_rgba(self.mColor)), + color: Color::rgba(convert_nscolor_to_rgba(self.mColor)), } } } diff --git a/components/style/gecko_bindings/sugar/style_complex_color.rs b/components/style/gecko_bindings/sugar/style_complex_color.rs index 47d9046e874..6b166384aed 100644 --- a/components/style/gecko_bindings/sugar/style_complex_color.rs +++ b/components/style/gecko_bindings/sugar/style_complex_color.rs @@ -4,10 +4,10 @@ //! Rust helpers to interact with Gecko's StyleComplexColor. -use cssparser; use gecko::values::{convert_nscolor_to_rgba, convert_rgba_to_nscolor}; use gecko_bindings::structs::{nscolor, StyleComplexColor}; use values; +use values::computed::Color as ComputedColor; impl From<nscolor> for StyleComplexColor { fn from(other: nscolor) -> Self { @@ -39,13 +39,12 @@ impl StyleComplexColor { } } -impl From<cssparser::Color> for StyleComplexColor { - fn from(other: cssparser::Color) -> Self { - use cssparser::Color; - - match other { - Color::RGBA(rgba) => convert_rgba_to_nscolor(&rgba).into(), - Color::CurrentColor => StyleComplexColor::current_color(), +impl From<ComputedColor> for StyleComplexColor { + fn from(other: ComputedColor) -> Self { + StyleComplexColor { + mColor: convert_rgba_to_nscolor(&other.color).into(), + mForegroundRatio: other.foreground_ratio, + mIsAuto: false, } } } @@ -62,17 +61,12 @@ impl From<StyleComplexColor> for values::computed::ColorOrAuto { } } -impl From<StyleComplexColor> for cssparser::Color { +impl From<StyleComplexColor> for ComputedColor { fn from(other: StyleComplexColor) -> Self { - use cssparser::Color; - - if other.mForegroundRatio == 0 { - Color::RGBA(convert_nscolor_to_rgba(other.mColor)) - } else if other.mForegroundRatio == 255 { - Color::CurrentColor - } else { - // FIXME #13546 handle interpolation values - Color::CurrentColor + debug_assert!(!other.mIsAuto); + ComputedColor { + color: convert_nscolor_to_rgba(other.mColor), + foreground_ratio: other.mForegroundRatio, } } } diff --git a/components/style/properties/helpers/animated_properties.mako.rs b/components/style/properties/helpers/animated_properties.mako.rs index ce3b406d4f4..41501e5ddd8 100644 --- a/components/style/properties/helpers/animated_properties.mako.rs +++ b/components/style/properties/helpers/animated_properties.mako.rs @@ -7,7 +7,7 @@ <% from data import SYSTEM_FONT_LONGHANDS %> use app_units::Au; -use cssparser::{Color as CSSParserColor, Parser, RGBA, serialize_identifier}; +use cssparser::{Parser, RGBA, serialize_identifier}; use euclid::{Point2D, Size2D}; #[cfg(feature = "gecko")] use gecko_bindings::bindings::RawServoAnimationValueMap; #[cfg(feature = "gecko")] use gecko_bindings::structs::RawGeckoGfxMatrix4x4; @@ -38,7 +38,7 @@ use values::CSSFloat; use values::{Auto, Either}; use values::computed::{Angle, LengthOrPercentageOrAuto, LengthOrPercentageOrNone}; use values::computed::{BorderCornerRadius, ClipRect}; -use values::computed::{CalcLengthOrPercentage, Context, ComputedValueAsSpecified}; +use values::computed::{CalcLengthOrPercentage, Color, Context, ComputedValueAsSpecified}; use values::computed::{LengthOrPercentage, MaxLength, MozLength, Shadow, ToComputedValue}; use values::generics::{SVGPaint, SVGPaintKind}; use values::generics::border::BorderCornerRadius as GenericBorderCornerRadius; @@ -2721,38 +2721,19 @@ impl Animatable for IntermediateRGBA { } } -impl From<Either<CSSParserColor, Auto>> for Either<IntermediateColor, Auto> { - fn from(from: Either<CSSParserColor, Auto>) -> Either<IntermediateColor, Auto> { +impl From<Either<Color, Auto>> for Either<IntermediateColor, Auto> { + fn from(from: Either<Color, Auto>) -> Either<IntermediateColor, Auto> { match from { - Either::First(from) => - match from { - CSSParserColor::RGBA(color) => - Either::First(IntermediateColor::IntermediateRGBA( - IntermediateRGBA::new(color.red_f32(), - color.green_f32(), - color.blue_f32(), - color.alpha_f32()))), - CSSParserColor::CurrentColor => - Either::First(IntermediateColor::CurrentColor), - }, + Either::First(from) => Either::First(from.into()), Either::Second(Auto) => Either::Second(Auto), } } } -impl From<Either<IntermediateColor, Auto>> for Either<CSSParserColor, Auto> { - fn from(from: Either<IntermediateColor, Auto>) -> Either<CSSParserColor, Auto> { +impl From<Either<IntermediateColor, Auto>> for Either<Color, Auto> { + fn from(from: Either<IntermediateColor, Auto>) -> Either<Color, Auto> { match from { - Either::First(from) => - match from { - IntermediateColor::IntermediateRGBA(color) => - Either::First(CSSParserColor::RGBA(RGBA::from_floats(color.red, - color.green, - color.blue, - color.alpha))), - IntermediateColor::CurrentColor => - Either::First(CSSParserColor::CurrentColor), - }, + Either::First(from) => Either::First(from.into()), Either::Second(Auto) => Either::Second(Auto), } } @@ -2761,22 +2742,89 @@ impl From<Either<IntermediateColor, Auto>> for Either<CSSParserColor, Auto> { #[derive(Copy, Clone, Debug, PartialEq)] #[cfg_attr(feature = "servo", derive(HeapSizeOf))] #[allow(missing_docs)] -pub enum IntermediateColor { - CurrentColor, - IntermediateRGBA(IntermediateRGBA), +pub struct IntermediateColor { + color: IntermediateRGBA, + foreground_ratio: f32, +} + +impl IntermediateColor { + fn currentcolor() -> Self { + IntermediateColor { + color: IntermediateRGBA::transparent(), + foreground_ratio: 1., + } + } + + fn transparent() -> Self { + IntermediateColor { + color: IntermediateRGBA::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) -> IntermediateRGBA { + IntermediateRGBA { + alpha: self.color.alpha * (1. - self.foreground_ratio), + .. self.color + } + } } impl Animatable for IntermediateColor { #[inline] fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> { - match (*self, *other) { - (IntermediateColor::IntermediateRGBA(ref this), - IntermediateColor::IntermediateRGBA(ref other)) => { - this.add_weighted(other, self_portion, other_portion) - .map(IntermediateColor::IntermediateRGBA) + // 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(IntermediateColor::currentcolor()) + } else { + Ok(IntermediateColor { + color: self.color.add_weighted(&other.color, self_portion, other_portion)?, + foreground_ratio: self.foreground_ratio, + }) } - // FIXME: Bug 1345709: Implement currentColor animations. - _ => Err(()), + } else if self.is_currentcolor() && other.is_numeric() { + Ok(IntermediateColor { + color: other.color, + foreground_ratio: self_portion as f32, + }) + } else if self.is_numeric() && other.is_currentcolor() { + Ok(IntermediateColor { + 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(IntermediateColor { + color: IntermediateRGBA { + alpha: alpha, + .. color + }, + foreground_ratio: foreground_ratio, + }) } } @@ -2787,37 +2835,41 @@ impl Animatable for IntermediateColor { #[inline] fn compute_squared_distance(&self, other: &Self) -> Result<f64, ()> { - match (*self, *other) { - (IntermediateColor::IntermediateRGBA(ref this), IntermediateColor::IntermediateRGBA(ref other)) => { - this.compute_squared_distance(other) - }, - _ => Ok(0.0), + // All comments in add_weighted also applies here. + if self.foreground_ratio == other.foreground_ratio { + if self.is_currentcolor() { + Ok(0.) + } else { + self.color.compute_squared_distance(&other.color) + } + } else if self.is_currentcolor() && other.is_numeric() { + Ok(IntermediateRGBA::transparent().compute_squared_distance(&other.color)? + 1.) + } else if self.is_numeric() && other.is_currentcolor() { + Ok(self.color.compute_squared_distance(&IntermediateRGBA::transparent())? + 1.) + } else { + let self_color = self.effective_intermediate_rgba(); + let other_color = other.effective_intermediate_rgba(); + let dist = self_color.compute_squared_distance(&other_color)?; + let ratio_diff = (self.foreground_ratio - other.foreground_ratio) as f64; + Ok(dist + ratio_diff * ratio_diff) } } } -impl From<CSSParserColor> for IntermediateColor { - fn from(color: CSSParserColor) -> IntermediateColor { - match color { - CSSParserColor::RGBA(color) => - IntermediateColor::IntermediateRGBA(IntermediateRGBA::new(color.red_f32(), - color.green_f32(), - color.blue_f32(), - color.alpha_f32())), - CSSParserColor::CurrentColor => IntermediateColor::CurrentColor, +impl From<Color> for IntermediateColor { + fn from(color: Color) -> IntermediateColor { + IntermediateColor { + color: color.color.into(), + foreground_ratio: color.foreground_ratio as f32 * (1. / 255.), } } } -impl From<IntermediateColor> for CSSParserColor { - fn from(color: IntermediateColor) -> CSSParserColor { - match color { - IntermediateColor::IntermediateRGBA(color) => - CSSParserColor::RGBA(RGBA::from_floats(color.red, - color.green, - color.blue, - color.alpha)), - IntermediateColor::CurrentColor => CSSParserColor::CurrentColor, +impl From<IntermediateColor> for Color { + fn from(color: IntermediateColor) -> Color { + Color { + color: color.color.into(), + foreground_ratio: (color.foreground_ratio * 255.).round() as u8, } } } @@ -3024,7 +3076,7 @@ impl Animatable for IntermediateShadowList { offset_y: Au(0), blur_radius: Au(0), spread_radius: Au(0), - color: IntermediateColor::IntermediateRGBA(IntermediateRGBA::transparent()), + color: IntermediateColor::transparent(), inset: false, }; diff --git a/components/style/properties/longhand/background.mako.rs b/components/style/properties/longhand/background.mako.rs index 3b2a2e6760f..9b799c220b9 100644 --- a/components/style/properties/longhand/background.mako.rs +++ b/components/style/properties/longhand/background.mako.rs @@ -7,7 +7,7 @@ <% data.new_style_struct("Background", inherited=False) %> ${helpers.predefined_type("background-color", "Color", - "::cssparser::Color::RGBA(::cssparser::RGBA::transparent())", + "computed_value::T::transparent()", initial_specified_value="SpecifiedValue::transparent()", spec="https://drafts.csswg.org/css-backgrounds/#background-color", animation_value_type="IntermediateColor", diff --git a/components/style/properties/longhand/border.mako.rs b/components/style/properties/longhand/border.mako.rs index 8c0bf0aa9f2..0fd7196baf5 100644 --- a/components/style/properties/longhand/border.mako.rs +++ b/components/style/properties/longhand/border.mako.rs @@ -17,7 +17,7 @@ %> % for side in ALL_SIDES: ${helpers.predefined_type("border-%s-color" % side[0], "Color", - "::cssparser::Color::CurrentColor", + "computed_value::T::currentcolor()", alias=maybe_moz_logical_alias(product, side, "-moz-border-%s-color"), spec=maybe_logical_spec(side, "color"), animation_value_type="IntermediateColor", diff --git a/components/style/properties/longhand/color.mako.rs b/components/style/properties/longhand/color.mako.rs index bcdd97dedb0..fe5e411801e 100644 --- a/components/style/properties/longhand/color.mako.rs +++ b/components/style/properties/longhand/color.mako.rs @@ -12,7 +12,7 @@ animation_value_type="IntermediateRGBA" ignored_when_colors_disabled="True" spec="https://drafts.csswg.org/css-color/#color"> - use cssparser::{Color as CSSParserColor, RGBA}; + use cssparser::RGBA; use values::specified::{AllowQuirks, Color}; impl ToComputedValue for SpecifiedValue { @@ -20,11 +20,8 @@ #[inline] fn to_computed_value(&self, context: &Context) -> computed_value::T { - match self.0.to_computed_value(context) { - CSSParserColor::RGBA(rgba) => rgba, - CSSParserColor::CurrentColor => - context.inherited_style.get_color().clone_color(), - } + self.0.to_computed_value(context) + .to_rgba(context.inherited_style.get_color().clone_color()) } #[inline] diff --git a/components/style/properties/longhand/column.mako.rs b/components/style/properties/longhand/column.mako.rs index 56d481b719d..440e5d94d81 100644 --- a/components/style/properties/longhand/column.mako.rs +++ b/components/style/properties/longhand/column.mako.rs @@ -52,7 +52,7 @@ ${helpers.predefined_type("column-rule-width", // https://drafts.csswg.org/css-multicol-1/#crc ${helpers.predefined_type("column-rule-color", "Color", - "::cssparser::Color::CurrentColor", + "computed_value::T::currentcolor()", initial_specified_value="specified::Color::currentcolor()", products="gecko", animation_value_type="IntermediateColor", extra_prefixes="moz", need_clone=True, ignored_when_colors_disabled=True, diff --git a/components/style/properties/longhand/inherited_text.mako.rs b/components/style/properties/longhand/inherited_text.mako.rs index a8f055663c2..649caa5cece 100644 --- a/components/style/properties/longhand/inherited_text.mako.rs +++ b/components/style/properties/longhand/inherited_text.mako.rs @@ -686,7 +686,7 @@ ${helpers.predefined_type("word-spacing", </%helpers:longhand> ${helpers.predefined_type("text-emphasis-color", "Color", - "::cssparser::Color::CurrentColor", + "computed_value::T::currentcolor()", initial_specified_value="specified::Color::currentcolor()", products="gecko", animation_value_type="IntermediateColor", need_clone=True, ignored_when_colors_disabled=True, @@ -705,14 +705,14 @@ ${helpers.predefined_type( // https://compat.spec.whatwg.org ${helpers.predefined_type( "-webkit-text-fill-color", "Color", - "CSSParserColor::CurrentColor", + "computed_value::T::currentcolor()", products="gecko", animation_value_type="IntermediateColor", need_clone=True, ignored_when_colors_disabled=True, spec="https://compat.spec.whatwg.org/#the-webkit-text-fill-color")} ${helpers.predefined_type( "-webkit-text-stroke-color", "Color", - "CSSParserColor::CurrentColor", + "computed_value::T::currentcolor()", initial_specified_value="specified::Color::currentcolor()", products="gecko", animation_value_type="IntermediateColor", need_clone=True, ignored_when_colors_disabled=True, diff --git a/components/style/properties/longhand/outline.mako.rs b/components/style/properties/longhand/outline.mako.rs index ec08fa43972..03ec187135e 100644 --- a/components/style/properties/longhand/outline.mako.rs +++ b/components/style/properties/longhand/outline.mako.rs @@ -10,7 +10,7 @@ additional_methods=[Method("outline_has_nonzero_width", "bool")]) %> // TODO(pcwalton): `invert` -${helpers.predefined_type("outline-color", "Color", "computed::Color::CurrentColor", +${helpers.predefined_type("outline-color", "Color", "computed_value::T::currentcolor()", initial_specified_value="specified::Color::currentcolor()", animation_value_type="IntermediateColor", need_clone=True, ignored_when_colors_disabled=True, diff --git a/components/style/properties/longhand/text.mako.rs b/components/style/properties/longhand/text.mako.rs index c4bbe26c0e5..08113b228f2 100644 --- a/components/style/properties/longhand/text.mako.rs +++ b/components/style/properties/longhand/text.mako.rs @@ -279,7 +279,7 @@ ${helpers.single_keyword("text-decoration-style", ${helpers.predefined_type( "text-decoration-color", "Color", - "computed::Color::CurrentColor", + "computed_value::T::currentcolor()", initial_specified_value="specified::Color::currentcolor()", products="gecko", animation_value_type="IntermediateColor", diff --git a/components/style/properties/properties.mako.rs b/components/style/properties/properties.mako.rs index d9d95a82024..9e8420fa471 100644 --- a/components/style/properties/properties.mako.rs +++ b/components/style/properties/properties.mako.rs @@ -18,7 +18,7 @@ use std::ops::Deref; use stylearc::{Arc, UniqueArc}; use app_units::Au; -#[cfg(feature = "servo")] use cssparser::{Color as CSSParserColor, RGBA}; +#[cfg(feature = "servo")] use cssparser::RGBA; use cssparser::{Parser, TokenSerializationType, serialize_identifier}; use error_reporting::ParseErrorReporter; #[cfg(feature = "servo")] use euclid::side_offsets::SideOffsets2D; @@ -1937,11 +1937,8 @@ impl ComputedValues { /// Usage example: /// let top_color = style.resolve_color(style.Border.border_top_color); #[inline] - pub fn resolve_color(&self, color: CSSParserColor) -> RGBA { - match color { - CSSParserColor::RGBA(rgba) => rgba, - CSSParserColor::CurrentColor => self.get_color().color, - } + pub fn resolve_color(&self, color: computed::Color) -> RGBA { + color.to_rgba(self.get_color().color) } /// Get the logical computed inline size. diff --git a/components/style/values/computed/color.rs b/components/style/values/computed/color.rs index b77f55d0453..8c9a863b2c5 100644 --- a/components/style/values/computed/color.rs +++ b/components/style/values/computed/color.rs @@ -4,7 +4,141 @@ //! Computed color values. -use cssparser::RGBA; +use cssparser::{Color as CSSParserColor, RGBA}; +use std::fmt; +use style_traits::ToCss; + +/// This struct represents a combined color from a numeric color and +/// the current foreground color (currentcolor keyword). +/// Conceptually, the formula is "color * (1 - p) + currentcolor * p" +/// where p is foreground_ratio. +#[derive(Clone, Copy, Debug)] +#[cfg_attr(feature = "servo", derive(HeapSizeOf))] +pub struct Color { + /// RGBA color. + pub color: RGBA, + /// The ratio of currentcolor in complex color. + pub foreground_ratio: u8, +} + +fn blend_color_component(bg: u8, fg: u8, fg_alpha: u8) -> u8 { + let bg_ratio = (u8::max_value() - fg_alpha) as u32; + let fg_ratio = fg_alpha as u32; + let color = bg as u32 * bg_ratio + fg as u32 * fg_ratio; + // Rounding divide the number by 255 + ((color + 127) / 255) as u8 +} + +impl Color { + /// Returns a numeric color representing the given RGBA value. + pub fn rgba(rgba: RGBA) -> Color { + Color { + color: rgba, + foreground_ratio: 0, + } + } + + /// Returns a complex color value representing transparent. + pub fn transparent() -> Color { + Color::rgba(RGBA::transparent()) + } + + /// Returns a complex color value representing currentcolor. + pub fn currentcolor() -> Color { + Color { + color: RGBA::transparent(), + foreground_ratio: u8::max_value(), + } + } + + /// Whether it is a numeric color (no currentcolor component). + pub fn is_numeric(&self) -> bool { + self.foreground_ratio == 0 + } + + /// Whether it is a currentcolor value (no numeric color component). + pub fn is_currentcolor(&self) -> bool { + self.foreground_ratio == u8::max_value() + } + + /// Combine this complex color with the given foreground color into + /// a numeric RGBA color. It currently uses linear blending. + pub fn to_rgba(&self, fg_color: RGBA) -> RGBA { + // Common cases that the complex color is either pure numeric + // color or pure currentcolor. + if self.is_numeric() { + return self.color; + } + if self.is_currentcolor() { + return fg_color.clone(); + } + // Common case that alpha channel is equal (usually both are opaque). + let fg_ratio = self.foreground_ratio; + if self.color.alpha == fg_color.alpha { + let r = blend_color_component(self.color.red, fg_color.red, fg_ratio); + let g = blend_color_component(self.color.green, fg_color.green, fg_ratio); + let b = blend_color_component(self.color.blue, fg_color.blue, fg_ratio); + return RGBA::new(r, g, b, fg_color.alpha); + } + + // For the more complicated case that the alpha value differs, + // we use the following formula to compute the components: + // alpha = self_alpha * (1 - fg_ratio) + fg_alpha * fg_ratio + // color = (self_color * self_alpha * (1 - fg_ratio) + + // fg_color * fg_alpha * fg_ratio) / alpha + + let p1 = (1. / 255.) * (255 - fg_ratio) as f32; + let a1 = self.color.alpha_f32(); + let r1 = a1 * self.color.red_f32(); + let g1 = a1 * self.color.green_f32(); + let b1 = a1 * self.color.blue_f32(); + + let p2 = 1. - p1; + let a2 = fg_color.alpha_f32(); + let r2 = a2 * fg_color.red_f32(); + let g2 = a2 * fg_color.green_f32(); + let b2 = a2 * fg_color.blue_f32(); + + let a = p1 * a1 + p2 * a2; + if a == 0.0 { + return RGBA::transparent(); + } + + let inverse_a = 1. / a; + let r = (p1 * r1 + p2 * r2) * inverse_a; + let g = (p1 * g1 + p2 * g2) * inverse_a; + let b = (p1 * b1 + p2 * b2) * inverse_a; + return RGBA::from_floats(r, g, b, a); + } +} + +impl PartialEq for Color { + fn eq(&self, other: &Color) -> bool { + self.foreground_ratio == other.foreground_ratio && + (self.is_currentcolor() || self.color == other.color) + } +} + +impl From<RGBA> for Color { + fn from(color: RGBA) -> Color { + Color { + color: color, + foreground_ratio: 0, + } + } +} + +impl ToCss for Color { + fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + if self.is_numeric() { + self.color.to_css(dest) + } else if self.is_currentcolor() { + CSSParserColor::CurrentColor.to_css(dest) + } else { + Ok(()) + } + } +} /// Computed value type for the specified RGBAColor. pub type RGBAColor = RGBA; diff --git a/components/style/values/computed/mod.rs b/components/style/values/computed/mod.rs index 50ad4bbb82d..ff682255d99 100644 --- a/components/style/values/computed/mod.rs +++ b/components/style/values/computed/mod.rs @@ -23,12 +23,11 @@ use super::generics::grid::TrackList as GenericTrackList; use super::specified; pub use app_units::Au; -pub use cssparser::Color; pub use properties::animated_properties::TransitionProperty; pub use self::background::BackgroundSize; pub use self::border::{BorderImageSlice, BorderImageWidth, BorderImageSideWidth}; pub use self::border::{BorderRadius, BorderCornerRadius}; -pub use self::color::RGBAColor; +pub use self::color::{Color, RGBAColor}; pub use self::image::{Gradient, GradientItem, ImageLayer, LineDirection, Image, ImageRect}; #[cfg(feature = "gecko")] pub use self::gecko::ScrollSnapPoint; diff --git a/components/style/values/specified/color.rs b/components/style/values/specified/color.rs index d18bd574479..9fb5e849c5c 100644 --- a/components/style/values/specified/color.rs +++ b/components/style/values/specified/color.rs @@ -15,7 +15,7 @@ use std::fmt; use std::io::Write; use style_traits::ToCss; use super::AllowQuirks; -use values::computed::{Context, ToComputedValue}; +use values::computed::{Color as ComputedColor, Context, ToComputedValue}; /// Specified color value #[derive(Clone, PartialEq, Debug)] @@ -30,6 +30,8 @@ pub enum Color { /// Authored representation authored: Option<Box<str>>, }, + /// A complex color value from computed value + Complex(ComputedColor), /// A system color #[cfg(feature = "gecko")] @@ -103,6 +105,7 @@ impl ToCss for Color { Color::CurrentColor => CSSParserColor::CurrentColor.to_css(dest), Color::Numeric { authored: Some(ref authored), .. } => dest.write_str(authored), Color::Numeric { parsed: ref rgba, .. } => rgba.to_css(dest), + Color::Complex(_) => Ok(()), #[cfg(feature = "gecko")] Color::System(system) => system.to_css(dest), #[cfg(feature = "gecko")] @@ -232,25 +235,27 @@ impl Color { } #[cfg(feature = "gecko")] -fn to_rgba(color: nscolor) -> CSSParserColor { +fn convert_nscolor_to_computedcolor(color: nscolor) -> ComputedColor { use gecko::values::convert_nscolor_to_rgba; - CSSParserColor::RGBA(convert_nscolor_to_rgba(color)) + ComputedColor::rgba(convert_nscolor_to_rgba(color)) } impl ToComputedValue for Color { - type ComputedValue = CSSParserColor; + type ComputedValue = ComputedColor; - fn to_computed_value(&self, _context: &Context) -> CSSParserColor { + fn to_computed_value(&self, _context: &Context) -> ComputedColor { match *self { - Color::CurrentColor => CSSParserColor::CurrentColor, - Color::Numeric { ref parsed, .. } => CSSParserColor::RGBA(*parsed), + Color::CurrentColor => ComputedColor::currentcolor(), + Color::Numeric { ref parsed, .. } => ComputedColor::rgba(*parsed), + Color::Complex(ref complex) => *complex, #[cfg(feature = "gecko")] - Color::System(system) => to_rgba(system.to_computed_value(_context)), + Color::System(system) => + convert_nscolor_to_computedcolor(system.to_computed_value(_context)), #[cfg(feature = "gecko")] Color::Special(special) => { use self::gecko::SpecialColorKeyword as Keyword; let pres_context = unsafe { &*_context.device.pres_context }; - to_rgba(match special { + convert_nscolor_to_computedcolor(match special { Keyword::MozDefaultColor => pres_context.mDefaultColor, Keyword::MozDefaultBackgroundColor => pres_context.mBackgroundColor, Keyword::MozHyperlinktext => pres_context.mLinkColor, @@ -270,21 +275,24 @@ impl ToComputedValue for Color { if let Some(body) = body { let wrap = GeckoElement(body); let borrow = wrap.borrow_data(); - CSSParserColor::RGBA(borrow.as_ref().unwrap() - .styles().primary.values() - .get_color() - .clone_color()) + ComputedColor::rgba(borrow.as_ref().unwrap() + .styles().primary.values() + .get_color() + .clone_color()) } else { - to_rgba(pres_context.mDefaultColor) + convert_nscolor_to_computedcolor(pres_context.mDefaultColor) } }, } } - fn from_computed_value(computed: &CSSParserColor) -> Self { - match *computed { - CSSParserColor::RGBA(rgba) => Color::rgba(rgba), - CSSParserColor::CurrentColor => Color::currentcolor(), + fn from_computed_value(computed: &ComputedColor) -> Self { + if computed.is_numeric() { + Color::rgba(computed.color) + } else if computed.is_currentcolor() { + Color::currentcolor() + } else { + Color::Complex(*computed) } } } @@ -313,10 +321,8 @@ impl ToComputedValue for RGBAColor { type ComputedValue = RGBA; fn to_computed_value(&self, context: &Context) -> RGBA { - match self.0.to_computed_value(context) { - CSSParserColor::RGBA(rgba) => rgba, - CSSParserColor::CurrentColor => context.style.get_color().clone_color(), - } + self.0.to_computed_value(context) + .to_rgba(context.style.get_color().clone_color()) } fn from_computed_value(computed: &RGBA) -> Self { diff --git a/components/style/values/specified/mod.rs b/components/style/values/specified/mod.rs index c0f9c6c96aa..2ea37db841f 100644 --- a/components/style/values/specified/mod.rs +++ b/components/style/values/specified/mod.rs @@ -8,7 +8,7 @@ use Namespace; use context::QuirksMode; -use cssparser::{self, Parser, Token, serialize_identifier}; +use cssparser::{Parser, Token, serialize_identifier}; use parser::{ParserContext, Parse}; use self::grid::TrackSizeOrRepeat; use self::url::SpecifiedUrl; @@ -695,10 +695,8 @@ impl ToComputedValue for Shadow { offset_y: self.offset_y.to_computed_value(context), blur_radius: self.blur_radius.to_computed_value(context), spread_radius: self.spread_radius.to_computed_value(context), - color: self.color - .as_ref() - .map(|color| color.to_computed_value(context)) - .unwrap_or(cssparser::Color::CurrentColor), + color: self.color.as_ref().unwrap_or(&Color::CurrentColor) + .to_computed_value(context), inset: self.inset, } } |