Bug 1299741 part 3 - Add LinearBlendColors function for linear blending two colors. r=mstange
MozReview-Commit-ID: KVzV2DxXRqu
--- a/gfx/src/nsColor.cpp
+++ b/gfx/src/nsColor.cpp
@@ -1,15 +1,16 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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/. */
#include "mozilla/ArrayUtils.h" // for ArrayLength
#include "mozilla/mozalloc.h" // for operator delete, etc
+#include "mozilla/MathAlgorithms.h"
#include "nsColor.h"
#include <sys/types.h> // for int32_t
#include "nsColorNames.h" // for nsColorNames
#include "nsDebug.h" // for NS_ASSERTION, etc
#include "nsStaticNameTable.h"
#include "nsString.h" // for nsAutoCString, nsString, etc
#include "nscore.h" // for nsAString, etc
@@ -251,16 +252,69 @@ NS_ComposeColors(nscolor aBG, nscolor aF
}
MOZ_BLEND(r, NS_GET_R(aBG), NS_GET_R(aFG), blendAlpha);
MOZ_BLEND(g, NS_GET_G(aBG), NS_GET_G(aFG), blendAlpha);
MOZ_BLEND(b, NS_GET_B(aBG), NS_GET_B(aFG), blendAlpha);
return NS_RGBA(r, g, b, a);
}
+namespace mozilla {
+
+static uint32_t
+BlendColorComponent(uint32_t aBg, uint32_t aFg, uint32_t aFgAlpha)
+{
+ return RoundingDivideBy255(aBg * (255 - aFgAlpha) + aFg * aFgAlpha);
+}
+
+nscolor
+LinearBlendColors(nscolor aBg, nscolor aFg, uint_fast8_t 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 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 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) {
+ return NS_RGBA(0, 0, 0, 0);
+ }
+
+ 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));
+}
+
+} // namespace mozilla
+
// Functions to convert from HSL color space to RGB color space.
// This is the algorithm described in the CSS3 specification
// helper
static float
HSL_HueToRGB(float m1, float m2, float h)
{
if (h < 0.0f)
--- a/gfx/src/nsColor.h
+++ b/gfx/src/nsColor.h
@@ -72,16 +72,32 @@ enum class nsHexColorType : uint8_t {
// This accepts the number of digits specified by aType.
bool
NS_HexToRGBA(const nsAString& aBuf, nsHexColorType aType, nscolor* aResult);
// Compose one NS_RGB color onto another. The result is what
// you get if you draw aFG on top of aBG with operator OVER.
nscolor NS_ComposeColors(nscolor aBG, nscolor aFG);
+namespace mozilla {
+
+inline uint32_t RoundingDivideBy255(uint32_t n)
+{
+ // There is an approximate alternative: ((n << 8) + n + 32896) >> 16
+ // But that is actually slower than this simple expression on a modern
+ // machine with a modern compiler.
+ return (n + 127) / 255;
+}
+
+// Blend one RGBA color with another based on a given ratio.
+// It is a linear interpolation on each channel with alpha premultipled.
+nscolor LinearBlendColors(nscolor aBg, nscolor aFg, uint_fast8_t aFgRatio);
+
+} // namespace mozilla
+
// Translate a hex string to a color. Return true if it parses ok,
// otherwise return false.
// This version accepts 1 to 9 digits (missing digits are 0)
bool NS_LooseHexToRGB(const nsString& aBuf, nscolor* aResult);
// There is no function to translate a color to a hex string, because
// the hex-string syntax does not support transparency.