--- a/layout/style/StyleAnimationValue.cpp
+++ b/layout/style/StyleAnimationValue.cpp
@@ -5,16 +5,17 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/* Utilities for animation of computed style values */
#include "mozilla/ArrayUtils.h"
#include "mozilla/MathAlgorithms.h"
#include "mozilla/RuleNodeCacheConditions.h"
#include "mozilla/StyleAnimationValue.h"
+#include "mozilla/Tuple.h"
#include "mozilla/UniquePtr.h"
#include "nsStyleTransformMatrix.h"
#include "nsAutoPtr.h"
#include "nsCOMArray.h"
#include "nsIStyleRule.h"
#include "mozilla/css/StyleRule.h"
#include "nsString.h"
#include "nsStyleContext.h"
@@ -1157,82 +1158,122 @@ AddCSSValuePercentNumber(const uint32_t
// aCoeff2 is 0, then we'll return the value halfway between 1 and
// aValue1, rather than the value halfway between 0 and aValue1.
// Note that we do something similar in AddTransformScale().
float result = (n1 - aInitialVal) * aCoeff1 + (n2 - aInitialVal) * aCoeff2;
aResult.SetFloatValue(RestrictValue(aValueRestrictions, result + aInitialVal),
eCSSUnit_Number);
}
+// Returns Tuple(Red, Green, Blue, Alpha).
+static Tuple<double, double, double, double>
+GetPremultipliedColor(const nsCSSValue& aValue)
+{
+ // PercentageRGBColor and PercentageRGBAColor component value might be
+ // greater than 1.0 in case when the color value is accumulated, so we
+ // can't use nsCSSValue::GetColorValue() here because that function
+ // clamps its values.
+ if (aValue.GetUnit() == eCSSUnit_PercentageRGBColor ||
+ aValue.GetUnit() == eCSSUnit_PercentageRGBAColor) {
+ nsCSSValueFloatColor* floatColor = aValue.GetFloatColorValue();
+ double alpha = floatColor->Alpha();
+ return MakeTuple(
+ floatColor->Comp1() * 255.0 * alpha,
+ floatColor->Comp2() * 255.0 * alpha,
+ floatColor->Comp3() * 255.0 * alpha,
+ alpha);
+ }
+
+ nscolor color = aValue.GetColorValue();
+ double alpha = NS_GET_A(color) * (1.0 / 255.0);
+ return MakeTuple(
+ NS_GET_R(color) * alpha,
+ NS_GET_G(color) * alpha,
+ NS_GET_B(color) * alpha,
+ alpha);
+}
+
+enum class ColorAdditionType {
+ Clamped, // Clamp each color channel after adding.
+ Unclamped // Do not clamp color channels after adding.
+};
+
+// |aAdditionType| should be Clamped in case of interpolation or SMIL
+// animation (e.g. 'by' attribute). For now, Unclamped is only for
+// accumulation.
static void
AddWeightedColors(double aCoeff1, const nsCSSValue& aValue1,
double aCoeff2, const nsCSSValue& aValue2,
+ ColorAdditionType aAdditionType,
nsCSSValue& aResult)
{
MOZ_ASSERT(aValue1.IsNumericColorUnit() && aValue2.IsNumericColorUnit(),
"The unit should be color");
- nscolor color1 = aValue1.GetColorValue();
- nscolor color2 = aValue2.GetColorValue();
// FIXME (spec): The CSS transitions spec doesn't say whether
// colors are premultiplied, but things work better when they are,
// so use premultiplication. Spec issue is still open per
// http://lists.w3.org/Archives/Public/www-style/2009Jul/0050.html
// To save some math, scale the alpha down to a 0-1 scale, but
// leave the color components on a 0-255 scale.
- double A1 = NS_GET_A(color1) * (1.0 / 255.0);
- double R1 = NS_GET_R(color1) * A1;
- double G1 = NS_GET_G(color1) * A1;
- double B1 = NS_GET_B(color1) * A1;
- double A2 = NS_GET_A(color2) * (1.0 / 255.0);
- double R2 = NS_GET_R(color2) * A2;
- double G2 = NS_GET_G(color2) * A2;
- double B2 = NS_GET_B(color2) * A2;
+
+ double R1, G1, B1, A1;
+ Tie(R1, G1, B1, A1) = GetPremultipliedColor(aValue1);
+ double R2, G2, B2, A2;
+ Tie(R2, G2, B2, A2) = GetPremultipliedColor(aValue2);
double Aresf = (A1 * aCoeff1 + A2 * aCoeff2);
if (Aresf <= 0.0) {
aResult.SetColorValue(NS_RGBA(0, 0, 0, 0));
return;
}
if (Aresf > 1.0) {
Aresf = 1.0;
}
double factor = 1.0 / Aresf;
- uint8_t Ares = NSToIntRound(Aresf * 255.0);
- uint8_t Rres = ClampColor((R1 * aCoeff1 + R2 * aCoeff2) * factor);
- uint8_t Gres = ClampColor((G1 * aCoeff1 + G2 * aCoeff2) * factor);
- uint8_t Bres = ClampColor((B1 * aCoeff1 + B2 * aCoeff2) * factor);
- aResult.SetColorValue(NS_RGBA(Rres, Gres, Bres, Ares));
+ double Rres = (R1 * aCoeff1 + R2 * aCoeff2) * factor;
+ double Gres = (G1 * aCoeff1 + G2 * aCoeff2) * factor;
+ double Bres = (B1 * aCoeff1 + B2 * aCoeff2) * factor;
+
+ if (aAdditionType == ColorAdditionType::Clamped) {
+ aResult.SetColorValue(
+ NS_RGBA(ClampColor(Rres), ClampColor(Gres), ClampColor(Bres),
+ NSToIntRound(Aresf * 255.0)));
+ return;
+ }
+
+ Rres = Rres * (1.0 / 255.0);
+ Gres = Gres * (1.0 / 255.0);
+ Bres = Bres * (1.0 / 255.0);
+
+ aResult.SetFloatColorValue(Rres, Gres, Bres, Aresf,
+ eCSSUnit_PercentageRGBAColor);
}
// Multiplies |aValue| color by |aDilutionRation| with premultiplication.
// The result is stored in |aResult|.
// (The logic here should pretty closely match AddWeightedColors()' logic.)
static void
DiluteColor(const nsCSSValue& aValue, double aDilutionRatio,
nsCSSValue& aResult)
{
MOZ_ASSERT(aValue.IsNumericColorUnit(), "The unit should be color");
MOZ_ASSERT(aDilutionRatio >= 0.0 && aDilutionRatio <= 1.0,
"Dilution ratio should be in [0, 1]");
- nscolor color = aValue.GetColorValue();
- double A = NS_GET_A(color) * (1.0 / 255.0);
+ // Premultiplication
+ double R, G, B, A;
+ Tie(R, G, B, A) = GetPremultipliedColor(aValue);
double Aresf = A * aDilutionRatio;
if (Aresf <= 0.0) {
aResult.SetColorValue(NS_RGBA(0, 0, 0, 0));
return;
}
- // Premultiplication
- double R = NS_GET_R(color) * A;
- double G = NS_GET_G(color) * A;
- double B = NS_GET_B(color) * A;
-
double factor = 1.0 / Aresf;
aResult.SetColorValue(
NS_RGBA(ClampColor(R * aDilutionRatio * factor),
ClampColor(G * aDilutionRatio * factor),
ClampColor(B * aDilutionRatio * factor),
NSToIntRound(Aresf * 255.0)));
}
@@ -1256,28 +1297,33 @@ AddShadowItems(double aCoeff1, const nsC
// blur radius must be nonnegative
(i == 2) ? CSS_PROPERTY_VALUE_NONNEGATIVE : 0);
}
const nsCSSValue& color1 = array1->Item(4);
const nsCSSValue& color2 = array2->Item(4);
const nsCSSValue& inset1 = array1->Item(5);
const nsCSSValue& inset2 = array2->Item(5);
- if (color1.GetUnit() != color2.GetUnit() ||
+ if ((color1.GetUnit() != color2.GetUnit() &&
+ (!color1.IsNumericColorUnit() || !color2.IsNumericColorUnit())) ||
inset1.GetUnit() != inset2.GetUnit()) {
// We don't know how to animate between color and no-color, or
// between inset and not-inset.
+ // NOTE: In case when both colors' units are eCSSUnit_Null, that means
+ // neither color value was specified, so we can interpolate.
return false;
}
if (color1.GetUnit() != eCSSUnit_Null) {
if (aCoeff2 == 0.0 && aCoeff1 != 1.0) {
DiluteColor(color1, aCoeff1, resultArray->Item(4));
} else {
- AddWeightedColors(aCoeff1, color1, aCoeff2, color2, resultArray->Item(4));
+ AddWeightedColors(aCoeff1, color1, aCoeff2, color2,
+ ColorAdditionType::Clamped,
+ resultArray->Item(4));
}
}
MOZ_ASSERT(inset1 == inset2, "should match");
resultArray->Item(5) = inset1;
nsCSSValueList *resultItem = new nsCSSValueList;
resultItem->mValue.SetArrayValue(resultArray, eCSSUnit_Array);
@@ -2421,17 +2467,19 @@ StyleAnimationValue::AddWeighted(nsCSSPr
// We are using AddWeighted() with a zero aCoeff2 for colors to
// pretend AddWeighted() against transparent color, i.e. rgba(0, 0, 0, 0).
// But unpremultiplication in AddWeightedColors() does not work well
// for such cases, so we use another function named DiluteColor() which
// has a similar logic to AddWeightedColors().
if (aCoeff2 == 0.0) {
DiluteColor(*value1, aCoeff1, *resultColor);
} else {
- AddWeightedColors(aCoeff1, *value1, aCoeff2, *value2, *resultColor);
+ AddWeightedColors(aCoeff1, *value1, aCoeff2, *value2,
+ ColorAdditionType::Clamped,
+ *resultColor);
}
aResultValue.SetAndAdoptCSSValueValue(resultColor.release(), eUnit_Color);
return true;
}
case eUnit_Calc: {
PixelCalcValue v1 = ExtractCalcValue(aValue1);
PixelCalcValue v2 = ExtractCalcValue(aValue2);
double len = aCoeff1 * v1.mLength + aCoeff2 * v2.mLength;
@@ -2812,19 +2860,30 @@ StyleAnimationValue::Accumulate(nsCSSPro
StyleAnimationValue& aDest,
const StyleAnimationValue& aValueToAccumulate,
uint64_t aCount)
{
Unit commonUnit =
GetCommonUnit(aProperty, aDest.GetUnit(), aValueToAccumulate.GetUnit());
switch (commonUnit) {
// FIXME: implement them!
- //case eUnit_Color:
//case eUnit_Shadow:
//case eUnit_Filter:
+ case eUnit_Color: {
+ auto resultColor = MakeUnique<nsCSSValue>();
+ AddWeightedColors(1.0,
+ *aDest.GetCSSValueValue(),
+ aCount,
+ *aValueToAccumulate.GetCSSValueValue(),
+ ColorAdditionType::Unclamped,
+ *resultColor);
+
+ aDest.SetAndAdoptCSSValueValue(resultColor.release(), eUnit_Color);
+ return true;
+ }
default:
return Add(aProperty, aDest, aValueToAccumulate, aCount);
}
MOZ_ASSERT_UNREACHABLE("Can't accumulate using the given common unit");
return false;
}
already_AddRefed<css::StyleRule>