--- a/layout/painting/nsCSSRendering.cpp
+++ b/layout/painting/nsCSSRendering.cpp
@@ -2160,20 +2160,17 @@ IsOpaqueBorderEdge(const nsStyleBorder&
// even if we did, checking whether the relevant tile is fully
// opaque would be too much work.
if (aBorder.mBorderImageSource.GetType() != eStyleImageType_Null)
return false;
StyleComplexColor color = aBorder.mBorderColor[aSide];
// We don't know the foreground color here, so if it's being used
// we must assume it might be transparent.
- if (!color.IsNumericColor()) {
- return false;
- }
- return NS_GET_A(color.mColor) == 255;
+ return !color.MaybeTransparent();
}
/**
* Returns true if all border edges are either missing or opaque.
*/
static bool
IsOpaqueBorder(const nsStyleBorder& aBorder)
{
--- a/layout/style/ServoBindings.toml
+++ b/layout/style/ServoBindings.toml
@@ -149,16 +149,17 @@ rusty-enums = [
"mozilla::HalfCorner",
"mozilla::StyleDisplay",
"mozilla::StyleFloatEdge",
"mozilla::StyleShapeRadius",
"mozilla::StyleWindowDragging",
"mozilla::StyleOrient",
"mozilla::StyleBoxSizing",
"mozilla::StyleClear",
+ "mozilla::StyleComplexColor_Tag",
"mozilla::StyleFloat",
"mozilla::StyleUserModify",
"mozilla::StyleUserInput",
"mozilla::StyleBoxDirection",
"mozilla::StyleTextJustify",
"mozilla::StyleHyphens",
"mozilla::StyleFillRule",
"mozilla::StyleShapeSourceType",
--- a/layout/style/StyleComplexColor.cpp
+++ b/layout/style/StyleComplexColor.cpp
@@ -8,70 +8,74 @@
#include "mozilla/ComputedStyle.h"
#include "mozilla/ComputedStyleInlines.h"
#include "nsIFrame.h"
#include "nsStyleStruct.h"
using namespace mozilla;
-static uint32_t
-BlendColorComponent(uint32_t aBg, uint32_t aFg, uint32_t aFgAlpha)
-{
- return RoundingDivideBy255(aBg * (255 - aFgAlpha) + aFg * aFgAlpha);
-}
-
-// Blend one RGBA color with another based on a given ratio.
-// It is a linear interpolation on each channel with alpha premultipled.
+// Blend one RGBA color with another based on a given ratios.
+// It is a linear combination of each channel with alpha premultipled.
static nscolor
-LinearBlendColors(nscolor aBg, nscolor aFg, uint_fast8_t aFgRatio)
+LinearBlendColors(nscolor aBg, float aBgRatio, nscolor aFg, float aFgRatio)
{
- // Common case that either pure background or pure foreground
- if (aFgRatio == 0) {
- return aBg;
- }
- if (aFgRatio == 255) {
- return aFg;
- }
- // Common case that alpha channel is equal (usually both are opaque)
- if (NS_GET_A(aBg) == NS_GET_A(aFg)) {
- auto r = BlendColorComponent(NS_GET_R(aBg), NS_GET_R(aFg), aFgRatio);
- auto g = BlendColorComponent(NS_GET_G(aBg), NS_GET_G(aFg), aFgRatio);
- auto b = BlendColorComponent(NS_GET_B(aBg), NS_GET_B(aFg), aFgRatio);
- return NS_RGBA(r, g, b, NS_GET_A(aFg));
- }
-
constexpr float kFactor = 1.0f / 255.0f;
- float p1 = kFactor * (255 - aFgRatio);
+ float p1 = aBgRatio;
float a1 = kFactor * NS_GET_A(aBg);
float r1 = a1 * NS_GET_R(aBg);
float g1 = a1 * NS_GET_G(aBg);
float b1 = a1 * NS_GET_B(aBg);
- float p2 = 1.0f - p1;
+ float p2 = aFgRatio;
float a2 = kFactor * NS_GET_A(aFg);
float r2 = a2 * NS_GET_R(aFg);
float g2 = a2 * NS_GET_G(aFg);
float b2 = a2 * NS_GET_B(aFg);
float a = p1 * a1 + p2 * a2;
- if (a == 0.0) {
+ if (a <= 0.f) {
return NS_RGBA(0, 0, 0, 0);
}
+ if (a > 1.f) {
+ a = 1.f;
+ }
+
auto r = ClampColor((p1 * r1 + p2 * r2) / a);
auto g = ClampColor((p1 * g1 + p2 * g2) / a);
auto b = ClampColor((p1 * b1 + p2 * b2) / a);
return NS_RGBA(r, g, b, NSToIntRound(a * 255));
}
+bool
+StyleComplexColor::MaybeTransparent() const {
+ // We know that the color is opaque when it's a numeric color with
+ // alpha == 255.
+ // TODO(djg): Should we extend this to check Complex with bgRatio =
+ // 0, and fgRatio * alpha >= 255?
+ return mTag != eNumeric || NS_GET_A(mColor) != 255;
+}
+
nscolor
StyleComplexColor::CalcColor(mozilla::ComputedStyle* aStyle) const {
+ // Common case that is numeric color, which is pure background, we
+ // can skip resolving StyleColor().
+ if (mTag == eNumeric) {
+ return mColor;
+ }
+
MOZ_ASSERT(aStyle);
- auto foregroundColor = aStyle->StyleColor()->mColor;
- return LinearBlendColors(mColor, foregroundColor, mForegroundRatio);
+ auto fgColor = aStyle->StyleColor()->mColor;
+
+ if (mTag == eComplex) {
+ return LinearBlendColors(mColor, mBgRatio, fgColor, mFgRatio);
+ }
+
+ // eForeground and eAuto return the currentcolor.
+ return fgColor;
}
nscolor
StyleComplexColor::CalcColor(const nsIFrame* aFrame) const {
return CalcColor(aFrame->Style());
}
--- a/layout/style/StyleComplexColor.h
+++ b/layout/style/StyleComplexColor.h
@@ -15,64 +15,111 @@ class nsIFrame;
namespace mozilla {
class ComputedStyle;
/**
* 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 mForegroundRatio. See mozilla::LinearBlendColors for
- * the actual algorithm.
+ * Conceptually, the formula is "color * q + currentcolor * p"
+ * where p is mFgRatio and q is mBgRatio.
*
* It can also represent an "auto" value, which is valid for some
- * properties. See comment of mIsAuto.
+ * properties. See comment of `Tag::eAuto`.
*/
-struct StyleComplexColor
+class StyleComplexColor final
{
- nscolor mColor;
- uint8_t mForegroundRatio;
- // Whether the complex color represents a computed-value time auto
- // value. This is a flag indicating that this value should not be
- // interpolatable with other colors. When this flag is set, other
- // fields represent a currentcolor. Properties can decide whether
- // that should be used.
- bool mIsAuto;
-
+public:
static StyleComplexColor FromColor(nscolor aColor) {
- return {aColor, 0, false};
+ return {aColor, 0, eNumeric};
}
static StyleComplexColor CurrentColor() {
- return {NS_RGBA(0, 0, 0, 0), 255, false};
+ return {NS_RGBA(0, 0, 0, 0), 1, eForeground};
}
static StyleComplexColor Auto() {
- return {NS_RGBA(0, 0, 0, 0), 255, true};
+ return {NS_RGBA(0, 0, 0, 0), 1, eAuto};
}
- bool IsNumericColor() const { return mForegroundRatio == 0; }
- bool IsCurrentColor() const { return mForegroundRatio == 255; }
+ bool IsAuto() const { return mTag == eAuto; }
+ bool IsCurrentColor() const { return mTag == eForeground; }
bool operator==(const StyleComplexColor& aOther) const {
- return mForegroundRatio == aOther.mForegroundRatio &&
- (IsCurrentColor() || mColor == aOther.mColor) &&
- mIsAuto == aOther.mIsAuto;
+ if (mTag != aOther.mTag) {
+ return false;
+ }
+
+ switch (mTag) {
+ case eAuto:
+ case eForeground:
+ return true;
+ case eNumeric:
+ return mColor == aOther.mColor;
+ case eComplex:
+ return (mBgRatio == aOther.mBgRatio &&
+ mFgRatio == aOther.mFgRatio &&
+ mColor == aOther.mColor);
+ default:
+ MOZ_ASSERT_UNREACHABLE("Unexpected StyleComplexColor type.");
+ return false;
+ }
}
+
bool operator!=(const StyleComplexColor& aOther) const {
return !(*this == aOther);
}
/**
+ * Is it possible that this StyleComplexColor is transparent?
+ */
+ bool MaybeTransparent() const;
+
+ /**
* Compute the color for this StyleComplexColor, taking into account
* the foreground color from aStyle.
*/
nscolor CalcColor(mozilla::ComputedStyle* aStyle) const;
/**
* Compute the color for this StyleComplexColor, taking into account
* the foreground color from aFrame's ComputedStyle.
*/
nscolor CalcColor(const nsIFrame* aFrame) const;
+
+private:
+ enum Tag : uint8_t {
+ // This represents a computed-value time auto value. This
+ // indicates that this value should not be interpolatable with
+ // other colors. Other fields represent a currentcolor and
+ // properties can decide whether that should be used.
+ eAuto,
+ // This represents a numeric color; no currentcolor component.
+ eNumeric,
+ // This represents the current foreground color, currentcolor; no
+ // numeric color component.
+ eForeground,
+ // This represents a linear combination of numeric color and the
+ // foreground color: "mColor * mBgRatio + currentcolor *
+ // mFgRatio".
+ eComplex,
+ };
+
+ StyleComplexColor(nscolor aColor,
+ float aFgRatio,
+ Tag aTag)
+ : mColor(aColor)
+ , mBgRatio(1.f - aFgRatio)
+ , mFgRatio(aFgRatio)
+ , mTag(aTag)
+ {
+ MOZ_ASSERT(mTag != eNumeric || aFgRatio == 0.);
+ MOZ_ASSERT(!(mTag == eAuto || mTag == eForeground) || aFgRatio == 1.);
+ }
+
+ nscolor mColor;
+ float mBgRatio;
+ float mFgRatio;
+ Tag mTag;
};
-}
+} // namespace mozilla
#endif // mozilla_StyleComplexColor_h_
--- a/layout/style/nsComputedDOMStyle.cpp
+++ b/layout/style/nsComputedDOMStyle.cpp
@@ -1180,17 +1180,17 @@ nsComputedDOMStyle::SetValueFromComplexC
SetToRGBAColor(aValue, aColor.CalcColor(mComputedStyle));
}
void
nsComputedDOMStyle::SetValueForWidgetColor(nsROCSSPrimitiveValue* aValue,
const StyleComplexColor& aColor,
uint8_t aWidgetType)
{
- if (!aColor.mIsAuto) {
+ if (!aColor.IsAuto()) {
SetToRGBAColor(aValue, aColor.CalcColor(mComputedStyle));
return;
}
nsPresContext* presContext = mPresShell->GetPresContext();
MOZ_ASSERT(presContext);
if (nsContentUtils::ShouldResistFingerprinting(presContext->GetDocShell())) {
// Return transparent when resisting fingerprinting.
SetToRGBAColor(aValue, NS_RGBA(0, 0, 0, 0));
--- a/layout/style/nsStyleStruct.cpp
+++ b/layout/style/nsStyleStruct.cpp
@@ -4256,30 +4256,37 @@ nsStyleContent::CalcDifference(const nsS
return nsChangeHint(0);
}
// --------------------
// nsStyleTextReset
//
nsStyleTextReset::nsStyleTextReset(const nsPresContext* aContext)
- : mTextDecorationLine(NS_STYLE_TEXT_DECORATION_LINE_NONE)
+ : mTextOverflow()
+ , mTextDecorationLine(NS_STYLE_TEXT_DECORATION_LINE_NONE)
, mTextDecorationStyle(NS_STYLE_TEXT_DECORATION_STYLE_SOLID)
, mUnicodeBidi(NS_STYLE_UNICODE_BIDI_NORMAL)
, mInitialLetterSink(0)
, mInitialLetterSize(0.0f)
, mTextDecorationColor(StyleComplexColor::CurrentColor())
{
MOZ_COUNT_CTOR(nsStyleTextReset);
}
nsStyleTextReset::nsStyleTextReset(const nsStyleTextReset& aSource)
+ : mTextOverflow(aSource.mTextOverflow)
+ , mTextDecorationLine(aSource.mTextDecorationLine)
+ , mTextDecorationStyle(aSource.mTextDecorationStyle)
+ , mUnicodeBidi(aSource.mUnicodeBidi)
+ , mInitialLetterSink(aSource.mInitialLetterSink)
+ , mInitialLetterSize(aSource.mInitialLetterSize)
+ , mTextDecorationColor(aSource.mTextDecorationColor)
{
MOZ_COUNT_CTOR(nsStyleTextReset);
- *this = aSource;
}
nsStyleTextReset::~nsStyleTextReset()
{
MOZ_COUNT_DTOR(nsStyleTextReset);
}
nsChangeHint
--- a/layout/style/nsStyleStruct.h
+++ b/layout/style/nsStyleStruct.h
@@ -2856,17 +2856,17 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsSt
mozilla::StyleComplexColor mScrollbarFaceColor; // [inherited]
mozilla::StyleComplexColor mScrollbarTrackColor; // [inherited]
inline uint8_t GetEffectivePointerEvents(nsIFrame* aFrame) const;
bool HasCustomScrollbars() const
{
- return !mScrollbarFaceColor.mIsAuto || !mScrollbarTrackColor.mIsAuto;
+ return !mScrollbarFaceColor.IsAuto() || !mScrollbarTrackColor.IsAuto();
}
};
struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsStyleXUL
{
explicit nsStyleXUL(const nsPresContext* aContext);
nsStyleXUL(const nsStyleXUL& aSource);
~nsStyleXUL();
--- a/servo/components/style/gecko_bindings/sugar/style_complex_color.rs
+++ b/servo/components/style/gecko_bindings/sugar/style_complex_color.rs
@@ -1,80 +1,111 @@
/* 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 gecko::values::{convert_nscolor_to_rgba, convert_rgba_to_nscolor};
-use gecko_bindings::structs::{nscolor, StyleComplexColor};
+use gecko_bindings::structs::StyleComplexColor;
+use gecko_bindings::structs::StyleComplexColor_Tag as Tag;
use values::{Auto, Either};
-use values::computed::Color as ComputedColor;
+use values::computed::{Color as ComputedColor, RGBAColor as ComputedRGBA};
+use values::computed::ComplexColorRatios;
use values::computed::ui::ColorOrAuto;
-impl From<nscolor> for StyleComplexColor {
- fn from(other: nscolor) -> Self {
- StyleComplexColor {
- mColor: other,
- mForegroundRatio: 0,
- mIsAuto: false,
- }
- }
-}
-
impl StyleComplexColor {
/// Create a `StyleComplexColor` value that represents `currentColor`.
pub fn current_color() -> Self {
StyleComplexColor {
mColor: 0,
- mForegroundRatio: 255,
- mIsAuto: false,
+ mBgRatio: 0.,
+ mFgRatio: 1.,
+ mTag: Tag::eForeground,
}
}
/// Create a `StyleComplexColor` value that represents `auto`.
pub fn auto() -> Self {
StyleComplexColor {
mColor: 0,
- mForegroundRatio: 255,
- mIsAuto: true,
+ mBgRatio: 0.,
+ mFgRatio: 1.,
+ mTag: Tag::eAuto,
+ }
+ }
+}
+
+impl From<ComputedRGBA> for StyleComplexColor {
+ fn from(other: ComputedRGBA) -> Self {
+ StyleComplexColor {
+ mColor: convert_rgba_to_nscolor(&other),
+ mBgRatio: 1.,
+ mFgRatio: 0.,
+ mTag: Tag::eNumeric,
}
}
}
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,
+ match other {
+ ComputedColor::Numeric(color) => color.into(),
+ ComputedColor::Foreground => Self::current_color(),
+ ComputedColor::Complex(color, ratios) => {
+ debug_assert!(ratios != ComplexColorRatios::NUMERIC);
+ debug_assert!(ratios != ComplexColorRatios::FOREGROUND);
+ StyleComplexColor {
+ mColor: convert_rgba_to_nscolor(&color).into(),
+ mBgRatio: ratios.bg,
+ mFgRatio: ratios.fg,
+ mTag: Tag::eComplex,
+ }
+ }
}
}
}
impl From<StyleComplexColor> for ComputedColor {
fn from(other: StyleComplexColor) -> Self {
- debug_assert!(!other.mIsAuto);
- ComputedColor {
- color: convert_nscolor_to_rgba(other.mColor),
- foreground_ratio: other.mForegroundRatio,
+ match other.mTag {
+ Tag::eNumeric => {
+ debug_assert!(other.mBgRatio == 1. && other.mFgRatio == 0.);
+ ComputedColor::Numeric(convert_nscolor_to_rgba(other.mColor))
+ }
+ Tag::eForeground => {
+ debug_assert!(other.mBgRatio == 0. && other.mFgRatio == 1.);
+ ComputedColor::Foreground
+ }
+ Tag::eComplex => {
+ debug_assert!(other.mBgRatio != 1. || other.mFgRatio != 0.);
+ debug_assert!(other.mBgRatio != 0. || other.mFgRatio != 1.);
+ ComputedColor::Complex(
+ convert_nscolor_to_rgba(other.mColor),
+ ComplexColorRatios {
+ bg: other.mBgRatio,
+ fg: other.mFgRatio,
+ },
+ )
+ }
+ Tag::eAuto => unreachable!("Unsupport StyleComplexColor with tag eAuto"),
}
}
}
impl From<ColorOrAuto> for StyleComplexColor {
fn from(other: ColorOrAuto) -> Self {
match other {
Either::First(color) => color.into(),
Either::Second(_) => StyleComplexColor::auto(),
}
}
}
impl From<StyleComplexColor> for ColorOrAuto {
fn from(other: StyleComplexColor) -> Self {
- if !other.mIsAuto {
+ if other.mTag != Tag::eAuto {
Either::First(other.into())
} else {
Either::Second(Auto)
}
}
}
--- a/servo/components/style/values/animated/color.rs
+++ b/servo/components/style/values/animated/color.rs
@@ -1,16 +1,17 @@
/* 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 values::animated::{Animate, Procedure, ToAnimatedZero};
use values::distance::{ComputeSquaredDistance, SquaredDistance};
+use values::computed::ComplexColorRatios;
/// An animated RGBA color.
///
/// Unlike in computed values, each component value may exceed the
/// range `[0.0, 1.0]`.
#[cfg_attr(feature = "servo", derive(MallocSizeOf))]
#[derive(Clone, Copy, Debug, PartialEq, ToAnimatedZero)]
pub struct RGBA {
@@ -86,133 +87,157 @@ impl ComputeSquaredDistance for RGBA {
start
.iter()
.zip(&end)
.map(|(this, other)| this.compute_squared_distance(other))
.sum()
}
}
+impl Animate for ComplexColorRatios {
+ #[inline]
+ fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
+ let bg = self.bg.animate(&other.bg, procedure)?;
+ let fg = self.fg.animate(&other.fg, procedure)?;
+
+ Ok(ComplexColorRatios { bg, fg })
+ }
+}
+
#[allow(missing_docs)]
#[cfg_attr(feature = "servo", derive(MallocSizeOf))]
#[derive(Clone, Copy, Debug, PartialEq)]
-pub struct Color {
- pub color: RGBA,
- pub foreground_ratio: f32,
+pub enum Color {
+ Numeric(RGBA),
+ Foreground,
+ Complex(RGBA, ComplexColorRatios),
}
impl Color {
fn currentcolor() -> Self {
- Color {
- color: RGBA::transparent(),
- foreground_ratio: 1.,
- }
+ Color::Foreground
}
/// Returns a transparent intermediate color.
pub fn transparent() -> Self {
- Color {
- color: RGBA::transparent(),
- foreground_ratio: 0.,
+ Color::Numeric(RGBA::transparent())
+ }
+
+ fn effective_intermediate_rgba(&self) -> RGBA {
+ match *self {
+ Color::Numeric(color) => color,
+ Color::Foreground => RGBA::transparent(),
+ Color::Complex(color, ratios) => RGBA {
+ alpha: color.alpha * ratios.bg,
+ ..color.clone()
+ },
}
}
- 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
+ fn effective_ratios(&self) -> ComplexColorRatios {
+ match *self {
+ Color::Numeric(..) => ComplexColorRatios::NUMERIC,
+ Color::Foreground => ComplexColorRatios::FOREGROUND,
+ Color::Complex(.., ratios) => ratios,
}
}
}
impl Animate for Color {
#[inline]
fn animate(&self, other: &Self, procedure: Procedure) -> 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.
let (this_weight, other_weight) = procedure.weights();
- if self.foreground_ratio == other.foreground_ratio {
- if self.is_currentcolor() {
- Ok(Color::currentcolor())
- } else {
- Ok(Color {
- color: self.color.animate(&other.color, procedure)?,
- foreground_ratio: self.foreground_ratio,
- })
+
+ Ok(match (*self, *other, procedure) {
+ // Any interpolation of currentColor with currentColor returns currentColor.
+ (Color::Foreground, Color::Foreground, Procedure::Interpolate { .. }) => {
+ Color::currentcolor()
+ }
+ // Animating two numeric colors.
+ (Color::Numeric(c1), Color::Numeric(c2), _) => {
+ Color::Numeric(c1.animate(&c2, procedure)?)
}
- } else if self.is_currentcolor() && other.is_numeric() {
- Ok(Color {
- color: other.color,
- foreground_ratio: this_weight as f32,
- })
- } else if self.is_numeric() && other.is_currentcolor() {
- Ok(Color {
- color: self.color,
- foreground_ratio: other_weight 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.animate(&other_color, procedure)?;
- // Then we compute the final foreground ratio, and derive
- // the final alpha value from the effective alpha value.
- let foreground_ratio = self.foreground_ratio
- .animate(&other.foreground_ratio, procedure)?;
- let alpha = color.alpha / (1. - foreground_ratio);
- Ok(Color {
- color: RGBA {
- alpha: alpha,
- ..color
+ // Combinations of numeric color and currentColor
+ (Color::Foreground, Color::Numeric(color), _) => Color::Complex(
+ color,
+ ComplexColorRatios {
+ bg: other_weight as f32,
+ fg: this_weight as f32,
+ },
+ ),
+ (Color::Numeric(color), Color::Foreground, _) => Color::Complex(
+ color,
+ ComplexColorRatios {
+ bg: this_weight as f32,
+ fg: other_weight as f32,
+ },
+ ),
+
+ // Any other animation of currentColor with currentColor is complex.
+ (Color::Foreground, Color::Foreground, _) => Color::Complex(
+ RGBA::transparent(),
+ ComplexColorRatios {
+ bg: 0.,
+ fg: (this_weight + other_weight) as f32,
},
- foreground_ratio: foreground_ratio,
- })
- }
+ ),
+
+ // Defer to complex calculations
+ _ => {
+ // 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.animate(&other_color, procedure)?;
+ // Then we compute the final background ratio, and derive
+ // the final alpha value from the effective alpha value.
+ let self_ratios = self.effective_ratios();
+ let other_ratios = other.effective_ratios();
+ let ratios = self_ratios.animate(&other_ratios, procedure)?;
+ let alpha = color.alpha / ratios.bg;
+ let color = RGBA { alpha, ..color };
+
+ if ratios == ComplexColorRatios::NUMERIC {
+ Color::Numeric(color)
+ } else if ratios == ComplexColorRatios::FOREGROUND {
+ Color::Foreground
+ } else {
+ Color::Complex(color, ratios)
+ }
+ }
+ })
}
}
impl ComputeSquaredDistance for Color {
#[inline]
fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
// All comments from the Animate impl also applies here.
- if self.foreground_ratio == other.foreground_ratio {
- if self.is_currentcolor() {
- Ok(SquaredDistance::from_sqrt(0.))
- } else {
- self.color.compute_squared_distance(&other.color)
+ Ok(match (*self, *other) {
+ (Color::Foreground, Color::Foreground) => SquaredDistance::from_sqrt(0.),
+ (Color::Numeric(c1), Color::Numeric(c2)) => c1.compute_squared_distance(&c2)?,
+ (Color::Foreground, Color::Numeric(color))
+ | (Color::Numeric(color), Color::Foreground) => {
+ // `computed_squared_distance` is symmetic.
+ color.compute_squared_distance(&RGBA::transparent())?
+ + SquaredDistance::from_sqrt(1.)
}
- } else if self.is_currentcolor() && other.is_numeric() {
- Ok(
- RGBA::transparent().compute_squared_distance(&other.color)? +
- SquaredDistance::from_sqrt(1.),
- )
- } else if self.is_numeric() && other.is_currentcolor() {
- Ok(self.color.compute_squared_distance(&RGBA::transparent())? +
- SquaredDistance::from_sqrt(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)?)
- }
+ (_, _) => {
+ let self_color = self.effective_intermediate_rgba();
+ let other_color = other.effective_intermediate_rgba();
+ let self_ratios = self.effective_ratios();
+ let other_ratios = other.effective_ratios();
+
+ self_color.compute_squared_distance(&other_color)?
+ + self_ratios.bg.compute_squared_distance(&other_ratios.bg)?
+ + self_ratios.fg.compute_squared_distance(&other_ratios.fg)?
+ }
+ })
}
}
impl ToAnimatedZero for Color {
#[inline]
fn to_animated_zero(&self) -> Result<Self, ()> {
// FIXME(nox): This does not look correct to me.
Err(())
--- a/servo/components/style/values/computed/color.rs
+++ b/servo/components/style/values/computed/color.rs
@@ -5,174 +5,164 @@
//! Computed color values.
use cssparser::{Color as CSSParserColor, RGBA};
use std::fmt;
use style_traits::{CssWriter, ToCss};
use values::animated::ToAnimatedValue;
use values::animated::color::{Color as AnimatedColor, RGBA as AnimatedRGBA};
-/// 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, MallocSizeOf)]
-pub struct Color {
- /// RGBA color.
- pub color: RGBA,
+/// Ratios representing the contribution of color and currentcolor to
+/// the final color value.
+#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq)]
+pub struct ComplexColorRatios {
+ /// Numeric color contribution.
+ pub bg: f32,
+ /// Foreground color, aka currentcolor, contribution.
+ pub fg: f32,
+}
- /// The ratio of currentcolor in complex color.
- pub foreground_ratio: u8,
+impl ComplexColorRatios {
+ /// Ratios representing pure numeric color.
+ pub const NUMERIC: ComplexColorRatios = ComplexColorRatios { bg: 1., fg: 0. };
+ /// Ratios representing pure foreground color.
+ pub const FOREGROUND: ComplexColorRatios = ComplexColorRatios { bg: 0., fg: 1. };
+}
+
+/// This enum represents a combined color from a numeric color and
+/// the current foreground color (currentColor keyword).
+#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq)]
+pub enum Color {
+ /// Numeric RGBA color.
+ Numeric(RGBA),
+
+ /// The current foreground color.
+ Foreground,
+
+ /// A linear combination of numeric color and currentColor.
+ /// The formula is: `color * bg_ratio + currentColor * fg_ratio`.
+ Complex(RGBA, ComplexColorRatios),
}
/// Computed value type for the specified RGBAColor.
pub type RGBAColor = RGBA;
/// The computed value of the `color` property.
pub type ColorPropertyValue = RGBA;
impl Color {
/// Returns a numeric color representing the given RGBA value.
- pub fn rgba(rgba: RGBA) -> Color {
- Color {
- color: rgba,
- foreground_ratio: 0,
- }
+ pub fn rgba(color: RGBA) -> Color {
+ Color::Numeric(color)
}
/// 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(),
- }
+ Color::Foreground
}
/// Whether it is a numeric color (no currentcolor component).
pub fn is_numeric(&self) -> bool {
- self.foreground_ratio == 0
+ matches!(*self, Color::Numeric { .. })
}
/// Whether it is a currentcolor value (no numeric color component).
pub fn is_currentcolor(&self) -> bool {
- self.foreground_ratio == u8::max_value()
+ matches!(*self, Color::Foreground)
}
/// 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();
- }
-
- 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
- }
-
- // 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);
- }
+ let (color, ratios) = match *self {
+ // Common cases that the complex color is either pure numeric
+ // color or pure currentcolor.
+ Color::Numeric(color) => return color,
+ Color::Foreground => return fg_color,
+ Color::Complex(color, ratios) => (color, ratios),
+ };
// 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) +
+ // alpha = self_alpha * bg_ratio + fg_alpha * fg_ratio
+ // color = (self_color * self_alpha * bg_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 p1 = ratios.bg;
+ let a1 = color.alpha_f32();
+ let r1 = a1 * color.red_f32();
+ let g1 = a1 * color.green_f32();
+ let b1 = a1 * color.blue_f32();
- let p2 = 1. - p1;
+ let p2 = ratios.fg;
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 {
+ if a <= 0. {
return RGBA::transparent();
}
+ let a = f32::min(a, 1.);
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,
- }
+ Color::Numeric(color)
}
}
impl ToCss for Color {
fn to_css<W>(&self, dest: &mut CssWriter<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(())
+ match *self {
+ Color::Numeric(color) => color.to_css(dest),
+ Color::Foreground => CSSParserColor::CurrentColor.to_css(dest),
+ _ => Ok(()),
}
}
}
impl ToAnimatedValue for Color {
type AnimatedValue = AnimatedColor;
#[inline]
fn to_animated_value(self) -> Self::AnimatedValue {
- AnimatedColor {
- color: self.color.to_animated_value(),
- foreground_ratio: self.foreground_ratio as f32 * (1. / 255.),
+ match self {
+ Color::Numeric(color) => AnimatedColor::Numeric(color.to_animated_value()),
+ Color::Foreground => AnimatedColor::Foreground,
+ Color::Complex(color, ratios) => {
+ AnimatedColor::Complex(color.to_animated_value(), ratios)
+ }
}
}
#[inline]
fn from_animated_value(animated: Self::AnimatedValue) -> Self {
- Color {
- color: RGBA::from_animated_value(animated.color),
- foreground_ratio: (animated.foreground_ratio * 255.).round() as u8,
+ match animated {
+ AnimatedColor::Numeric(color) => Color::Numeric(RGBA::from_animated_value(color)),
+ AnimatedColor::Foreground => Color::Foreground,
+ AnimatedColor::Complex(color, ratios) => {
+ Color::Complex(RGBA::from_animated_value(color), ratios)
+ }
}
}
}
impl ToAnimatedValue for RGBA {
type AnimatedValue = AnimatedRGBA;
#[inline]
--- a/servo/components/style/values/computed/mod.rs
+++ b/servo/components/style/values/computed/mod.rs
@@ -40,17 +40,17 @@ pub use self::border::{BorderImageRepeat
pub use self::border::{BorderCornerRadius, BorderRadius, BorderSpacing};
pub use self::font::{FontSize, FontSizeAdjust, FontStretch, FontSynthesis, FontVariantAlternates, FontWeight};
pub use self::font::{FontFamily, FontLanguageOverride, FontStyle, FontVariantEastAsian, FontVariationSettings};
pub use self::font::{FontFeatureSettings, FontVariantLigatures, FontVariantNumeric};
pub use self::font::{MozScriptLevel, MozScriptMinSize, MozScriptSizeMultiplier, XLang, XTextZoom};
pub use self::box_::{AnimationIterationCount, AnimationName, Contain, Display, TransitionProperty};
pub use self::box_::{OverflowClipBox, OverscrollBehavior, Perspective};
pub use self::box_::{ScrollSnapType, TouchAction, VerticalAlign, WillChange};
-pub use self::color::{Color, ColorPropertyValue, RGBAColor};
+pub use self::color::{Color, ColorPropertyValue, ComplexColorRatios, RGBAColor};
pub use self::column::ColumnCount;
pub use self::counters::{Content, ContentItem, CounterIncrement, CounterReset};
pub use self::effects::{BoxShadow, Filter, SimpleShadow};
pub use self::flex::FlexBasis;
pub use self::image::{Gradient, GradientItem, Image, ImageLayer, LineDirection, MozImageRect};
pub use self::inherited_box::{ImageOrientation, Orientation};
#[cfg(feature = "gecko")]
pub use self::gecko::ScrollSnapPoint;
--- a/servo/components/style/values/specified/color.rs
+++ b/servo/components/style/values/specified/color.rs
@@ -83,21 +83,21 @@ impl<'a, 'b: 'a, 'i: 'a> ::cssparser::Co
let angle = Angle::parse_dimension(value, unit, /* from_calc = */ false);
let degrees = match angle {
Ok(angle) => angle.degrees(),
Err(()) => return Err(location.new_unexpected_token_error(token.clone())),
};
Ok(AngleOrNumber::Angle { degrees })
- },
+ }
Token::Number { value, .. } => Ok(AngleOrNumber::Number { value }),
Token::Function(ref name) if name.eq_ignore_ascii_case("calc") => {
input.parse_nested_block(|i| CalcNode::parse_angle_or_number(self.0, i))
- },
+ }
t => return Err(location.new_unexpected_token_error(t)),
}
}
fn parse_percentage<'t>(&self, input: &mut Parser<'i, 't>) -> Result<f32, ParseError<'i>> {
use values::specified::Percentage;
Ok(Percentage::parse(self.0, input)?.get())
@@ -114,20 +114,20 @@ impl<'a, 'b: 'a, 'i: 'a> ::cssparser::Co
input: &mut Parser<'i, 't>,
) -> Result<NumberOrPercentage, ParseError<'i>> {
let location = input.current_source_location();
match input.next()?.clone() {
Token::Number { value, .. } => Ok(NumberOrPercentage::Number { value }),
Token::Percentage { unit_value, .. } => {
Ok(NumberOrPercentage::Percentage { unit_value })
- },
+ }
Token::Function(ref name) if name.eq_ignore_ascii_case("calc") => {
input.parse_nested_block(|i| CalcNode::parse_number_or_percentage(self.0, i))
- },
+ }
t => return Err(location.new_unexpected_token_error(t)),
}
}
}
impl Parse for Color {
fn parse<'i, 't>(
context: &ParserContext,
@@ -163,20 +163,20 @@ impl Parse for Color {
}
}
match e.kind {
ParseErrorKind::Basic(BasicParseErrorKind::UnexpectedToken(t)) => {
Err(e.location.new_custom_error(StyleParseErrorKind::ValueError(
ValueParseErrorKind::InvalidColor(t),
)))
- },
+ }
_ => Err(e),
}
- },
+ }
}
}
}
impl ToCss for Color {
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
where
W: Write,
@@ -270,20 +270,20 @@ impl Color {
..
} => (integer, Some(unit)),
Token::Ident(ref ident) => {
if ident.len() != 3 && ident.len() != 6 {
return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError));
}
return parse_hash_color(ident.as_bytes())
.map_err(|()| location.new_custom_error(StyleParseErrorKind::UnspecifiedError));
- },
+ }
ref t => {
return Err(location.new_unexpected_token_error(t.clone()));
- },
+ }
};
if value < 0 {
return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError));
}
let length = if value <= 9 {
1
} else if value <= 99 {
2
@@ -353,47 +353,45 @@ impl Color {
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")]
Color::InheritFromBodyQuirk => {
_context.map(|context| ComputedColor::rgba(context.device().body_text_color()))
- },
+ }
}
}
}
impl ToComputedValue for Color {
type ComputedValue = ComputedColor;
fn to_computed_value(&self, context: &Context) -> ComputedColor {
let result = self.to_computed_color(Some(context)).unwrap();
- if result.foreground_ratio != 0 {
+ if !result.is_numeric() {
if let Some(longhand) = context.for_non_inherited_property {
if longhand.stores_complex_colors_lossily() {
context.rule_cache_conditions.borrow_mut().set_uncacheable();
}
}
}
result
}
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)
+ match *computed {
+ ComputedColor::Numeric(color) => Color::rgba(color),
+ ComputedColor::Foreground => Color::currentcolor(),
+ ComputedColor::Complex(..) => 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, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss)]
pub struct RGBAColor(pub Color);
--- a/widget/windows/nsNativeThemeWin.cpp
+++ b/widget/windows/nsNativeThemeWin.cpp
@@ -4131,34 +4131,34 @@ GetOpaqueBackgroundColor(ComputedStyle*
return NS_ComposeColors(NS_RGB(255, 255, 255), color);
}
static nscolor
GetScrollbarFaceColor(ComputedStyle* aStyle)
{
StyleComplexColor complexColor =
aStyle->StyleUserInterface()->mScrollbarFaceColor;
- if (complexColor.mIsAuto) {
+ if (complexColor.IsAuto()) {
return GetScrollbarFaceColorForAuto();
}
nscolor color = complexColor.CalcColor(aStyle);
if (NS_GET_A(color) == 255) {
return color;
}
nscolor bgColor = GetOpaqueBackgroundColor(aStyle);
return NS_ComposeColors(bgColor, color);
}
static nscolor
GetScrollbarTrackColor(ComputedStyle* aStyle)
{
StyleComplexColor complexColor =
aStyle->StyleUserInterface()->mScrollbarTrackColor;
nscolor color;
- if (complexColor.mIsAuto) {
+ if (complexColor.IsAuto()) {
color = GetScrollbarTrackColorForAuto(aStyle);
} else {
color = complexColor.CalcColor(aStyle);
}
if (NS_GET_A(color) == 255) {
return color;
}
nscolor bgColor = GetOpaqueBackgroundColor(aStyle);
@@ -4258,18 +4258,18 @@ GetScrollbarArrowColor(nscolor aTrackCol
nsresult
nsNativeThemeWin::DrawCustomScrollbarPart(gfxContext* aContext,
nsIFrame* aFrame,
ComputedStyle* aStyle,
uint8_t aWidgetType,
const nsRect& aRect,
const nsRect& aClipRect)
{
- MOZ_ASSERT(!aStyle->StyleUserInterface()->mScrollbarFaceColor.mIsAuto ||
- !aStyle->StyleUserInterface()->mScrollbarTrackColor.mIsAuto);
+ MOZ_ASSERT(!aStyle->StyleUserInterface()->mScrollbarFaceColor.IsAuto() ||
+ !aStyle->StyleUserInterface()->mScrollbarTrackColor.IsAuto());
gfxRect tr(aRect.X(), aRect.Y(), aRect.Width(), aRect.Height()),
dr(aClipRect.X(), aClipRect.Y(),
aClipRect.Width(), aClipRect.Height());
nscolor trackColor = GetScrollbarTrackColor(aStyle);
HBRUSH dcBrush = (HBRUSH) GetStockObject(DC_BRUSH);