CSS: Stop using premultiplied color space for gradients.

This commit is contained in:
wolfbeast 2017-06-15 21:46:53 +02:00 committed by Roy Tam
commit 21defafa0b
6 changed files with 4 additions and 90 deletions

View file

@ -2268,14 +2268,8 @@ ComputeRadialGradientLine(nsPresContext* aPresContext,
*aLineEnd = *aLineStart + gfxPoint(radiusX*cos(-angle), radiusY*sin(-angle));
}
static float Interpolate(float aF1, float aF2, float aFrac)
{
return aF1 + aFrac * (aF2 - aF1);
}
// Returns aFrac*aC2 + (1 - aFrac)*C1. The interpolation is done
// in unpremultiplied space, which is what SVG gradients and cairo
// in RGBA color space, which is what SVG gradients and cairo
// gradients expect.
static Color
InterpolateColor(const Color& aC1, const Color& aC2, float aFrac)
@ -2435,79 +2429,6 @@ static void ResolveMidpoints(nsTArray<ColorStop>& stops)
}
}
static Color
Premultiply(const Color& aColor)
{
gfx::Float a = aColor.a;
return Color(aColor.r * a, aColor.g * a, aColor.b * a, a);
}
static Color
Unpremultiply(const Color& aColor)
{
gfx::Float a = aColor.a;
return (a > 0.f)
? Color(aColor.r / a, aColor.g / a, aColor.b / a, a)
: aColor;
}
static Color
TransparentColor(Color aColor) {
aColor.a = 0;
return aColor;
}
// Adjusts and adds color stops in such a way that drawing the gradient with
// unpremultiplied interpolation looks nearly the same as if it were drawn with
// premultiplied interpolation.
static const float kAlphaIncrementPerGradientStep = 0.1f;
static void
ResolvePremultipliedAlpha(nsTArray<ColorStop>& aStops)
{
for (size_t x = 1; x < aStops.Length(); x++) {
const ColorStop leftStop = aStops[x - 1];
const ColorStop rightStop = aStops[x];
// if the left and right stop have the same alpha value, we don't need
// to do anything
if (leftStop.mColor.a == rightStop.mColor.a) {
continue;
}
// Is the stop on the left 100% transparent? If so, have it adopt the color
// of the right stop
if (leftStop.mColor.a == 0) {
aStops[x - 1].mColor = TransparentColor(rightStop.mColor);
continue;
}
// Is the stop on the right completely transparent?
// If so, duplicate it and assign it the color on the left.
if (rightStop.mColor.a == 0) {
ColorStop newStop = rightStop;
newStop.mColor = TransparentColor(leftStop.mColor);
aStops.InsertElementAt(x, newStop);
x++;
continue;
}
// Now handle cases where one or both of the stops are partially transparent.
if (leftStop.mColor.a != 1.0f || rightStop.mColor.a != 1.0f) {
Color premulLeftColor = Premultiply(leftStop.mColor);
Color premulRightColor = Premultiply(rightStop.mColor);
// Calculate how many extra steps. We do a step per 10% transparency.
size_t stepCount = NSToIntFloor(fabsf(leftStop.mColor.a - rightStop.mColor.a) / kAlphaIncrementPerGradientStep);
for (size_t y = 1; y < stepCount; y++) {
float frac = static_cast<float>(y) / stepCount;
ColorStop newStop(Interpolate(leftStop.mPosition, rightStop.mPosition, frac), false,
Unpremultiply(InterpolateColor(premulLeftColor, premulRightColor, frac)));
aStops.InsertElementAt(x, newStop);
x++;
}
}
}
}
static ColorStop
InterpolateColorStop(const ColorStop& aFirst, const ColorStop& aSecond,
double aPosition, const Color& aDefault)
@ -2522,9 +2443,9 @@ InterpolateColorStop(const ColorStop& aFirst, const ColorStop& aSecond,
}
return ColorStop(aPosition, false,
Unpremultiply(InterpolateColor(Premultiply(aFirst.mColor),
Premultiply(aSecond.mColor),
(aPosition - aFirst.mPosition) / delta)));
InterpolateColor(aFirst.mColor,
aSecond.mColor,
(aPosition - aFirst.mPosition) / delta));
}
// Clamp and extend the given ColorStop array in-place to fit exactly into the
@ -2924,7 +2845,6 @@ nsCSSRendering::PaintGradient(nsPresContext* aPresContext,
}
ResolveMidpoints(stops);
ResolvePremultipliedAlpha(stops);
bool isRepeat = aGradient->mRepeating || forceRepeatToCoverTiles;