Add separate computed Color value. r?birtles,manishearth
draft
Add separate computed Color value. r?birtles,manishearth
MozReview-Commit-ID: 9hRcmGkhu4d
--- a/servo/Cargo.lock
+++ b/servo/Cargo.lock
@@ -1413,17 +1413,16 @@ source = "registry+https://github.com/ru
[[package]]
name = "layout"
version = "0.0.1"
dependencies = [
"app_units 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
"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",
"gfx_traits 0.0.1",
"heapsize 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"html5ever 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)",
"ipc-channel 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.23 (registry+https://github.com/rust-lang/crates.io-index)",
--- a/servo/components/layout/Cargo.toml
+++ b/servo/components/layout/Cargo.toml
@@ -9,17 +9,16 @@ publish = false
name = "layout"
path = "lib.rs"
[dependencies]
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"}
gfx_traits = {path = "../gfx_traits"}
heapsize = "0.4"
html5ever = "0.17"
ipc-channel = "0.7"
libc = "0.2"
--- a/servo/components/layout/lib.rs
+++ b/servo/components/layout/lib.rs
@@ -11,17 +11,16 @@
#![feature(step_by)]
extern crate app_units;
extern crate atomic_refcell;
#[macro_use]
extern crate bitflags;
extern crate canvas_traits;
extern crate core;
-extern crate cssparser;
extern crate euclid;
extern crate fnv;
extern crate gfx;
extern crate gfx_traits;
extern crate heapsize;
#[macro_use] extern crate html5ever;
extern crate ipc_channel;
extern crate libc;
--- a/servo/components/layout/table_cell.rs
+++ b/servo/components/layout/table_cell.rs
@@ -4,29 +4,29 @@
//! CSS table formatting contexts.
#![deny(unsafe_code)]
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};
use fragment::{Fragment, FragmentBorderBoxIterator, Overflow};
use gfx_traits::print_tree::PrintTree;
use layout_debug;
use model::MaybeAuto;
use script_layout_interface::wrapper_traits::ThreadSafeLayoutNode;
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};
/// A table formatting context.
#[derive(Serialize)]
pub struct TableCellFlow {
/// Data common to all block flows.
pub block_flow: BlockFlow,
--- a/servo/components/layout/table_row.rs
+++ b/servo/components/layout/table_row.rs
@@ -4,34 +4,33 @@
//! CSS table formatting contexts.
#![deny(unsafe_code)]
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};
use flow_list::MutFlowListIterator;
use fragment::{Fragment, FragmentBorderBoxIterator, Overflow};
use gfx_traits::print_tree::PrintTree;
use layout_debug;
use model::MaybeAuto;
use serde::{Serialize, Serializer};
use std::cmp::max;
use std::fmt;
use std::iter::{Enumerate, IntoIterator, Peekable};
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};
/// A single row of a table.
pub struct TableRowFlow {
/// Fields common to all block flows.
pub block_flow: BlockFlow,
@@ -601,17 +600,17 @@ pub enum CollapsedBorderProvenance {
}
impl CollapsedBorder {
/// Creates a collapsible border style for no border.
pub fn new() -> CollapsedBorder {
CollapsedBorder {
style: border_top_style::T::none,
width: Au(0),
- color: Color::RGBA(RGBA::transparent()),
+ color: Color::transparent(),
provenance: CollapsedBorderProvenance::FromTable,
}
}
/// Creates a collapsed border from the block-start border described in the given CSS style
/// object.
fn top(css_style: &ServoComputedValues, provenance: CollapsedBorderProvenance)
-> CollapsedBorder {
--- a/servo/components/style/gecko_bindings/sugar/ns_css_shadow_item.rs
+++ b/servo/components/style/gecko_bindings/sugar/ns_css_shadow_item.rs
@@ -1,43 +1,42 @@
/* 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/. */
//! 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.
pub fn set_from_shadow(&mut self, other: Shadow) {
self.mXOffset = other.offset_x.0;
self.mYOffset = other.offset_y.0;
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);
}
}
/// Generate shadow value from this shadow item.
pub fn to_shadow(&self) -> Shadow {
Shadow {
offset_x: Au(self.mXOffset),
offset_y: Au(self.mYOffset),
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)),
}
}
}
--- a/servo/components/style/gecko_bindings/sugar/style_complex_color.rs
+++ b/servo/components/style/gecko_bindings/sugar/style_complex_color.rs
@@ -1,18 +1,18 @@
/* 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/. */
//! 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 {
StyleComplexColor {
mColor: other,
mForegroundRatio: 0,
mIsAuto: false,
}
@@ -34,45 +34,39 @@ impl StyleComplexColor {
StyleComplexColor {
mColor: 0,
mForegroundRatio: 255,
mIsAuto: true,
}
}
}
-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,
}
}
}
impl From<StyleComplexColor> for values::computed::ColorOrAuto {
fn from(color: StyleComplexColor) -> Self {
use values::{Auto, Either};
if color.mIsAuto {
return Either::Second(Auto);
}
Either::First(color.into())
}
}
-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,
}
}
}
--- a/servo/components/style/properties/helpers/animated_properties.mako.rs
+++ b/servo/components/style/properties/helpers/animated_properties.mako.rs
@@ -2,17 +2,17 @@
* 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/. */
<%namespace name="helpers" file="/helpers.mako.rs" />
<% 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::nsCSSPropertyID;
#[cfg(feature = "gecko")] use gecko_bindings::sugar::ownership::{HasFFI, HasSimpleFFI};
#[cfg(feature = "gecko")] use gecko_string_cache::Atom;
use properties::{CSSWideKeyword, PropertyDeclaration};
use properties::longhands;
use properties::longhands::background_size::computed_value::T as BackgroundSizeList;
@@ -32,17 +32,17 @@ use std::cmp;
#[cfg(feature = "gecko")] use std::collections::HashMap;
use std::fmt;
use style_traits::ToCss;
use super::ComputedValues;
use values::CSSFloat;
use values::{Auto, Either};
use values::computed::{Angle, LengthOrPercentageOrAuto, LengthOrPercentageOrNone};
use values::computed::{BorderCornerRadius, ClipRect};
-use values::computed::{CalcLengthOrPercentage, Context, LengthOrPercentage};
+use values::computed::{CalcLengthOrPercentage, Color, Context, LengthOrPercentage};
use values::computed::{MaxLength, MozLength, Shadow};
use values::computed::ToComputedValue;
use values::generics::{SVGPaint, SVGPaintKind};
use values::generics::border::BorderCornerRadius as GenericBorderCornerRadius;
use values::generics::position as generic_position;
/// A given transition property, that is either `All`, or an animatable
@@ -2616,113 +2616,165 @@ impl Animatable for IntermediateRGBA {
.fold(0.0f64, |n, (&a, &b)| {
let diff = (a - b) as f64;
n + diff * diff
});
Ok(diff)
}
}
-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),
}
}
}
#[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,
+ })
}
}
#[inline]
fn compute_distance(&self, other: &Self) -> Result<f64, ()> {
self.compute_squared_distance(other).map(|sq| sq.sqrt())
}
#[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,
}
}
}
/// Animatable SVGPaint
pub type IntermediateSVGPaint = SVGPaint<IntermediateRGBA>;
/// Animatable SVGPaintKind
pub type IntermediateSVGPaintKind = SVGPaintKind<IntermediateRGBA>;
@@ -2919,17 +2971,17 @@ impl Animatable for IntermediateShadowLi
#[inline]
fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
// The inset value must change
let mut zero = IntermediateShadow {
offset_x: Au(0),
offset_y: Au(0),
blur_radius: Au(0),
spread_radius: Au(0),
- color: IntermediateColor::IntermediateRGBA(IntermediateRGBA::transparent()),
+ color: IntermediateColor::transparent(),
inset: false,
};
let max_len = cmp::max(self.0.len(), other.0.len());
let mut result = if max_len > 1 {
SmallVec::from_vec(Vec::with_capacity(max_len))
} else {
--- a/servo/components/style/properties/longhand/background.mako.rs
+++ b/servo/components/style/properties/longhand/background.mako.rs
@@ -2,17 +2,17 @@
* 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/. */
<%namespace name="helpers" file="/helpers.mako.rs" />
<% 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",
ignored_when_colors_disabled=True,
allow_quirks=True)}
${helpers.predefined_type("background-image", "ImageLayer",
initial_value="Either::First(None_)",
--- a/servo/components/style/properties/longhand/border.mako.rs
+++ b/servo/components/style/properties/longhand/border.mako.rs
@@ -12,17 +12,17 @@
def maybe_logical_spec(side, kind):
if side[1]: # if it is logical
return "https://drafts.csswg.org/css-logical-props/#propdef-border-%s-%s" % (side[0], kind)
else:
return "https://drafts.csswg.org/css-backgrounds/#border-%s-%s" % (side[0], kind)
%>
% 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",
logical=side[1],
allow_quirks=not side[1],
ignored_when_colors_disabled=True)}
${helpers.predefined_type("border-%s-style" % side[0], "BorderStyle",
--- a/servo/components/style/properties/longhand/color.mako.rs
+++ b/servo/components/style/properties/longhand/color.mako.rs
@@ -7,29 +7,26 @@
<% data.new_style_struct("Color", inherited=True) %>
<% from data import to_rust_ident %>
<%helpers:longhand name="color" need_clone="True"
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 {
type ComputedValue = computed_value::T;
#[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]
fn from_computed_value(computed: &computed_value::T) -> Self {
SpecifiedValue(Color::rgba(*computed).into())
}
}
--- a/servo/components/style/properties/longhand/column.mako.rs
+++ b/servo/components/style/properties/longhand/column.mako.rs
@@ -47,17 +47,17 @@
computed_type="::app_units::Au",
products="gecko",
spec="https://drafts.csswg.org/css-multicol/#propdef-column-rule-width",
animation_value_type="ComputedValue",
extra_prefixes="moz")}
// 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,
spec="https://drafts.csswg.org/css-multicol/#propdef-column-rule-color")}
${helpers.single_keyword("column-span", "none all",
products="gecko", animation_value_type="discrete",
spec="https://drafts.csswg.org/css-multicol/#propdef-column-span")}
--- a/servo/components/style/properties/longhand/inherited_text.mako.rs
+++ b/servo/components/style/properties/longhand/inherited_text.mako.rs
@@ -681,17 +681,17 @@
};
SpecifiedValue(horiz, vert)
}
}
% endif
</%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,
spec="https://drafts.csswg.org/css-text-decor/#propdef-text-emphasis-color")}
${helpers.predefined_type(
"-moz-tab-size", "LengthOrNumber",
@@ -700,24 +700,24 @@
products="gecko", animation_value_type="none",
spec="https://drafts.csswg.org/css-text-3/#tab-size-property")}
// CSS Compatibility
// 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,
spec="https://compat.spec.whatwg.org/#the-webkit-text-stroke-color")}
${helpers.predefined_type("-webkit-text-stroke-width",
"BorderSideWidth",
"Au::from_px(0)",
--- a/servo/components/style/properties/longhand/outline.mako.rs
+++ b/servo/components/style/properties/longhand/outline.mako.rs
@@ -5,17 +5,17 @@
<%namespace name="helpers" file="/helpers.mako.rs" />
<% from data import Method %>
<% data.new_style_struct("Outline",
inherited=False,
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,
spec="https://drafts.csswg.org/css-ui/#propdef-outline-color")}
<%helpers:longhand name="outline-style" need_clone="True" animation_value_type="none"
spec="https://drafts.csswg.org/css-ui/#propdef-outline-style">
use values::specified::BorderStyle;
--- a/servo/components/style/properties/longhand/text.mako.rs
+++ b/servo/components/style/properties/longhand/text.mako.rs
@@ -274,17 +274,17 @@
${helpers.single_keyword("text-decoration-style",
"solid double dotted dashed wavy -moz-none",
products="gecko",
animation_value_type="discrete",
spec="https://drafts.csswg.org/css-text-decor/#propdef-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",
ignored_when_colors_disabled=True,
spec="https://drafts.csswg.org/css-text-decor/#propdef-text-decoration-color")}
<%helpers:longhand name="initial-letter"
animation_value_type="none"
--- a/servo/components/style/properties/properties.mako.rs
+++ b/servo/components/style/properties/properties.mako.rs
@@ -13,17 +13,17 @@
use std::borrow::Cow;
use std::collections::HashSet;
use std::fmt;
use std::mem;
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;
use computed_values;
use context::QuirksMode;
use font_metrics::FontMetricsProvider;
#[cfg(feature = "gecko")] use gecko_bindings::bindings;
#[cfg(feature = "gecko")] use gecko_bindings::structs::{self, nsCSSPropertyID};
@@ -1947,21 +1947,18 @@ impl ComputedValues {
/// Resolves the currentColor keyword.
///
/// Any color value from computed values (except for the 'color' property
/// itself) should go through this method.
///
/// 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.
#[inline]
pub fn content_inline_size(&self) -> computed::LengthOrPercentageOrAuto {
let position_style = self.get_position();
if self.writing_mode.is_vertical() {
position_style.height
--- a/servo/components/style/values/computed/color.rs
+++ b/servo/components/style/values/computed/color.rs
@@ -1,10 +1,144 @@
/* 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/. */
//! 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;
--- a/servo/components/style/values/computed/mod.rs
+++ b/servo/components/style/values/computed/mod.rs
@@ -18,21 +18,20 @@ use std::f32::consts::PI;
use std::fmt;
use style_traits::ToCss;
use super::{CSSFloat, CSSInteger, RGBA};
use super::generics::grid::{TrackBreadth as GenericTrackBreadth, TrackSize as GenericTrackSize};
use super::generics::grid::TrackList as GenericTrackList;
use super::specified;
pub use app_units::Au;
-pub use cssparser::Color;
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};
pub use self::rect::LengthOrNumberRect;
pub use super::{Auto, Either, None_};
#[cfg(feature = "gecko")]
pub use super::specified::{AlignItems, AlignJustifyContent, AlignJustifySelf, JustifyItems};
pub use super::specified::{BorderStyle, Percentage, UrlOrNone};
pub use super::generics::grid::GridLine;
pub use super::specified::url::SpecifiedUrl;
--- a/servo/components/style/values/specified/color.rs
+++ b/servo/components/style/values/specified/color.rs
@@ -10,31 +10,33 @@ use gecko_bindings::structs::nscolor;
use itoa;
use parser::{ParserContext, Parse};
#[cfg(feature = "gecko")]
use properties::longhands::color::SystemColor;
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)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub enum Color {
/// The 'currentColor' keyword
CurrentColor,
/// A specific RGBA color
Numeric {
/// Parsed RGBA color
parsed: RGBA,
/// Authored representation
authored: Option<Box<str>>,
},
+ /// A complex color value from computed value
+ Complex(ComputedColor),
/// A system color
#[cfg(feature = "gecko")]
System(SystemColor),
/// A special color keyword value used in Gecko
#[cfg(feature = "gecko")]
Special(gecko::SpecialColorKeyword),
/// Quirksmode-only rule for inheriting color from the body
@@ -92,16 +94,17 @@ impl Parse for Color {
}
impl ToCss for Color {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
match *self {
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")]
Color::Special(special) => special.to_css(dest),
#[cfg(feature = "gecko")]
Color::InheritFromBodyQuirk => Ok(()),
}
}
@@ -221,35 +224,37 @@ impl Color {
match *self {
Color::Numeric { ref parsed, .. } => parsed.alpha != 0,
_ => true,
}
}
}
#[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,
Keyword::MozActiveHyperlinktext => pres_context.mActiveLinkColor,
Keyword::MozVisitedHyperlinktext => pres_context.mVisitedLinkColor,
})
}
#[cfg(feature = "gecko")]
@@ -259,31 +264,34 @@ impl ToComputedValue for Color {
use gecko_bindings::bindings::Gecko_GetBody;
let pres_context = unsafe { &*_context.device.pres_context };
let body = unsafe {
Gecko_GetBody(pres_context)
};
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)
}
}
}
/// Specified color value, but resolved to just RGBA for computed value
/// with value from color property at the same context.
#[derive(Clone, PartialEq, Debug)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
@@ -302,20 +310,18 @@ impl ToCss for RGBAColor {
self.0.to_css(dest)
}
}
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 {
RGBAColor(Color::rgba(*computed))
}
}
impl From<Color> for RGBAColor {
--- a/servo/components/style/values/specified/mod.rs
+++ b/servo/components/style/values/specified/mod.rs
@@ -3,17 +3,17 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
//! Specified values.
//!
//! TODO(emilio): Enhance docs.
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;
use std::ascii::AsciiExt;
use std::f32;
use std::fmt;
use style_traits::ToCss;
use style_traits::values::specified::AllowedNumericType;
@@ -685,20 +685,18 @@ impl ToComputedValue for Shadow {
#[inline]
fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
ComputedShadow {
offset_x: self.offset_x.to_computed_value(context),
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,
}
}
#[inline]
fn from_computed_value(computed: &ComputedShadow) -> Self {
Shadow {
offset_x: ToComputedValue::from_computed_value(&computed.offset_x),