Bug 1299741 part 11 - Implement interpolation between numeric color and currentcolor. r?birtles draft
authorXidorn Quan <me@upsuper.org>
Thu, 01 Sep 2016 23:54:02 +1000
changeset 408692 da11a166a6b2d0f1bd9d358b49efc13ec7a41d0e
parent 408691 bc841990feccf9987dcb89e8f2984336f086d62c
child 408693 edb134cdc566fb7faed99b1216df022d479ff02c
push id28271
push userxquan@mozilla.com
push dateThu, 01 Sep 2016 14:23:32 +0000
reviewersbirtles
bugs1299741
milestone51.0a1
Bug 1299741 part 11 - Implement interpolation between numeric color and currentcolor. r?birtles MozReview-Commit-ID: CAQmTpCznaa
gfx/src/nsColor.h
layout/style/StyleAnimationValue.cpp
--- a/gfx/src/nsColor.h
+++ b/gfx/src/nsColor.h
@@ -29,16 +29,22 @@ typedef uint32_t nscolor;
   ((nscolor) (((_a) << 24) | ((_b)<<16) | ((_g)<<8) | (_r)))
 
 // Extract color components from nscolor
 #define NS_GET_R(_rgba) ((uint8_t) ((_rgba) & 0xff))
 #define NS_GET_G(_rgba) ((uint8_t) (((_rgba) >> 8) & 0xff))
 #define NS_GET_B(_rgba) ((uint8_t) (((_rgba) >> 16) & 0xff))
 #define NS_GET_A(_rgba) ((uint8_t) (((_rgba) >> 24) & 0xff))
 
+inline nscolor
+NS_ColorWithAlpha(nscolor aColor, uint_fast8_t aAlpha)
+{
+  return (aColor & 0x00ffffff) | (aAlpha << 24);
+}
+
 template<typename T>
 inline uint_fast8_t NS_ClampColor(T aColor)
 {
   if (aColor >= 255) {
     return 255;
   }
   if (aColor <= 0) {
     return 0;
--- a/layout/style/StyleAnimationValue.cpp
+++ b/layout/style/StyleAnimationValue.cpp
@@ -21,16 +21,17 @@
 #include "nsStyleSet.h"
 #include "nsComputedDOMStyle.h"
 #include "nsCSSParser.h"
 #include "nsCSSPseudoElements.h"
 #include "mozilla/css/Declaration.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/FloatingPoint.h"
 #include "mozilla/Likely.h"
+#include "mozilla/gfx/Tools.h"
 #include "gfxMatrix.h"
 #include "gfxQuaternion.h"
 #include "nsIDocument.h"
 #include "nsIFrame.h"
 #include "gfx2DGlue.h"
 
 using namespace mozilla;
 using namespace mozilla::gfx;
@@ -63,16 +64,25 @@ GetCommonUnit(nsCSSPropertyID aProperty,
          aFirstUnit == StyleAnimationValue::eUnit_Percent ||
          aFirstUnit == StyleAnimationValue::eUnit_Calc) &&
         (aSecondUnit == StyleAnimationValue::eUnit_Coord ||
          aSecondUnit == StyleAnimationValue::eUnit_Percent ||
          aSecondUnit == StyleAnimationValue::eUnit_Calc)) {
       // We can use calc() as the common unit.
       return StyleAnimationValue::eUnit_Calc;
     }
+    if ((aFirstUnit == StyleAnimationValue::eUnit_Color ||
+         aFirstUnit == StyleAnimationValue::eUnit_CurrentColor ||
+         aFirstUnit == StyleAnimationValue::eUnit_ComplexColor) &&
+        (aSecondUnit == StyleAnimationValue::eUnit_Color ||
+         aSecondUnit == StyleAnimationValue::eUnit_CurrentColor ||
+         aSecondUnit == StyleAnimationValue::eUnit_ComplexColor)) {
+      // We can use complex color as the common unit.
+      return StyleAnimationValue::eUnit_ComplexColor;
+    }
     return StyleAnimationValue::eUnit_Null;
   }
   return aFirstUnit;
 }
 
 static
 nsCSSUnit
 GetCommonUnit(nsCSSPropertyID aProperty,
@@ -293,16 +303,30 @@ FragmentOrURLToURLValue(FragmentOrURL* a
   RefPtr<nsStringBuffer> uriStringBuffer = nsCSSValue::BufferFromString(path);
   RefPtr<mozilla::css::URLValue> result =
     new mozilla::css::URLValue(aUrl->GetSourceURL(), uriStringBuffer,
                                aDoc->GetDocumentURI(), aDoc->NodePrincipal());
 
   return result.forget();
 }
 
+static StyleComplexColor
+ExtractComplexColorValue(const StyleAnimationValue& aValue)
+{
+  if (aValue.GetUnit() == StyleAnimationValue::eUnit_Color) {
+    return StyleComplexColor::FromColor(aValue.GetColorValue());
+  }
+  if (aValue.GetUnit() == StyleAnimationValue::eUnit_CurrentColor) {
+    return StyleComplexColor::CurrentColor();
+  }
+  MOZ_ASSERT(aValue.GetUnit() == StyleAnimationValue::eUnit_ComplexColor,
+             "unexpected unit");
+  return aValue.GetComplexColorValue();
+}
+
 // Like nsStyleCoord::CalcValue, but with length in float pixels instead
 // of nscoord.
 struct PixelCalcValue
 {
   float mLength, mPercent;
   bool mHasPercent;
 };
 
@@ -530,17 +554,16 @@ StyleAnimationValue::ComputeDistance(nsC
 
   switch (commonUnit) {
     case eUnit_Null:
     case eUnit_Auto:
     case eUnit_None:
     case eUnit_Normal:
     case eUnit_UnparsedString:
     case eUnit_URL:
-    case eUnit_CurrentColor:
     case eUnit_DiscreteCSSValue:
       return false;
 
     case eUnit_Enumerated:
       switch (aProperty) {
         case eCSSProperty_font_stretch: {
           // just like eUnit_Integer.
           int32_t startInt = aStartValue.GetIntValue();
@@ -589,16 +612,51 @@ StyleAnimationValue::ComputeDistance(nsC
       aDistance = Abs(double(endFloat) - double(startFloat));
       return true;
     }
     case eUnit_Color: {
       aDistance = ComputeColorDistance(aStartValue.GetColorValue(),
                                        aEndValue.GetColorValue());
       return true;
     }
+    case eUnit_CurrentColor: {
+      aDistance = 0;
+      return true;
+    }
+    case eUnit_ComplexColor: {
+      StyleComplexColor color1 = ExtractComplexColorValue(aStartValue);
+      StyleComplexColor color2 = ExtractComplexColorValue(aEndValue);
+
+      // Common cases that interpolating between a color and a currentcolor.
+      if (color1.IsNumericColor() && color2.IsCurrentColor()) {
+        double dist = ComputeColorDistance(color1.mColor, NS_RGBA(0, 0, 0, 0));
+        aDistance = sqrt(dist * dist + 1);
+        return true;
+      }
+      if (color1.IsCurrentColor() && color2.IsNumericColor()) {
+        double dist = ComputeColorDistance(NS_RGBA(0, 0, 0, 0), color2.mColor);
+        aDistance = sqrt(dist * dist + 1);
+        return true;
+      }
+
+      // XXX Would we ever get here?
+      // Compute distance based on colors premultipled with
+      // the non-foreground ratio.
+      auto alpha1 = FastDivideBy255(NS_GET_A(color1.mColor) *
+                                    (255 - color1.mForegroundRatio));
+      auto alpha2 = FastDivideBy255(NS_GET_A(color2.mColor) *
+                                    (255 - color2.mForegroundRatio));
+      double dist =
+        ComputeColorDistance(NS_ColorWithAlpha(color1.mColor, alpha1),
+                             NS_ColorWithAlpha(color2.mColor, alpha2));
+      double diffRatio = (1.0 / 255.0) * color1.mForegroundRatio -
+                         (1.0 / 255.0) * color2.mForegroundRatio;
+      aDistance = sqrt(dist * dist + diffRatio * diffRatio);
+      return true;
+    }
     case eUnit_Calc: {
       PixelCalcValue v1 = ExtractCalcValue(aStartValue);
       PixelCalcValue v2 = ExtractCalcValue(aEndValue);
       float difflen = v2.mLength - v1.mLength;
       float diffpct = v2.mPercent - v1.mPercent;
       aDistance = sqrt(difflen * difflen + diffpct * diffpct);
       return true;
     }
@@ -2319,17 +2377,16 @@ StyleAnimationValue::AddWeighted(nsCSSPr
 
   switch (commonUnit) {
     case eUnit_Null:
     case eUnit_Auto:
     case eUnit_None:
     case eUnit_Normal:
     case eUnit_UnparsedString:
     case eUnit_URL:
-    case eUnit_CurrentColor:
     case eUnit_DiscreteCSSValue:
       return false;
 
     case eUnit_Enumerated:
       switch (aProperty) {
         case eCSSProperty_font_stretch: {
           // Animate just like eUnit_Integer.
           int32_t result = floor(aCoeff1 * double(aValue1.GetIntValue()) +
@@ -2402,16 +2459,57 @@ StyleAnimationValue::AddWeighted(nsCSSPr
     }
     case eUnit_Color: {
       nscolor color1 = aValue1.GetColorValue();
       nscolor color2 = aValue2.GetColorValue();
       aResultValue.SetColorValue(AddWeightedColors(aCoeff1, color1,
                                                    aCoeff2, color2));
       return true;
     }
+    case eUnit_CurrentColor: {
+      aResultValue.SetCurrentColorValue();
+      return true;
+    }
+    case eUnit_ComplexColor: {
+      StyleComplexColor color1 = ExtractComplexColorValue(aValue1);
+      StyleComplexColor color2 = ExtractComplexColorValue(aValue2);
+      StyleComplexColor result;
+      // Common cases that interpolating between a color and a currentcolor.
+      if (color1.IsNumericColor() && color2.IsCurrentColor()) {
+        result.mColor = color1.mColor;
+        result.mForegroundRatio = NS_ClampColor(aCoeff2 * 255);
+      } else if (color1.IsCurrentColor() && color2.IsNumericColor()) {
+        result.mColor = color2.mColor;
+        result.mForegroundRatio = NS_ClampColor(aCoeff1 * 255);
+      } else {
+        // XXX Would we ever get here?
+        uint32_t ratio1 = 255 - color1.mForegroundRatio;
+        uint32_t ratio2 = 255 - color2.mForegroundRatio;
+        // Premultipled alpha channel with the non-foreground ratio.
+        auto alpha1 = FastDivideBy255(NS_GET_A(color1.mColor) * ratio1);
+        auto alpha2 = FastDivideBy255(NS_GET_A(color2.mColor) * ratio2);
+        nscolor resultColor = AddWeightedColors(
+          aCoeff1, NS_ColorWithAlpha(color1.mColor, alpha1),
+          aCoeff2, NS_ColorWithAlpha(color2.mColor, alpha2));
+        auto resultRatio = NS_ClampColor(color1.mForegroundRatio * aCoeff1 +
+                                         color2.mForegroundRatio * aCoeff2);
+        if (resultRatio == 255) {
+          result = StyleComplexColor::CurrentColor();
+        } else {
+          auto resultAlpha = NS_GET_A(resultColor) * 255 / (255 - resultRatio);
+          if (resultAlpha > 255) {
+            resultAlpha = 255;
+          }
+          result.mColor = NS_ColorWithAlpha(resultColor, resultAlpha);
+          result.mForegroundRatio = resultRatio;
+        }
+      }
+      aResultValue.SetComplexColorValue(result);
+      return true;
+    }
     case eUnit_Calc: {
       PixelCalcValue v1 = ExtractCalcValue(aValue1);
       PixelCalcValue v2 = ExtractCalcValue(aValue2);
       double len = aCoeff1 * v1.mLength + aCoeff2 * v2.mLength;
       double pct = aCoeff1 * v1.mPercent + aCoeff2 * v2.mPercent;
       bool hasPct = (aCoeff1 != 0.0 && v1.mHasPercent) ||
                       (aCoeff2 != 0.0 && v2.mHasPercent);
       nsCSSValue *val = new nsCSSValue();