mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-12 21:05:36 +00:00
Bug 591600 - CSS gradients should work on premultiplied colors. r=mstange
--HG-- extra : rebase_source : 24d38e88d6efc8ed34960c3d3602b18f5bcd2c95
This commit is contained in:
parent
49428da1fa
commit
2729085dd0
@ -2137,6 +2137,12 @@ ComputeRadialGradientLine(nsPresContext* aPresContext,
|
|||||||
*aLineEnd = *aLineStart + gfxPoint(radiusX*cos(-angle), radiusY*sin(-angle));
|
*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
|
// Returns aFrac*aC2 + (1 - aFrac)*C1. The interpolation is done
|
||||||
// in unpremultiplied space, which is what SVG gradients and cairo
|
// in unpremultiplied space, which is what SVG gradients and cairo
|
||||||
// gradients expect.
|
// gradients expect.
|
||||||
@ -2220,6 +2226,77 @@ RectIsBeyondLinearGradientEdge(const gfxRect& aRect,
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static gfxRGBA
|
||||||
|
Premultiply(const gfxRGBA& aColor)
|
||||||
|
{
|
||||||
|
gfxFloat a = aColor.a;
|
||||||
|
return gfxRGBA(aColor.r * a, aColor.g * a, aColor.b * a, a);
|
||||||
|
}
|
||||||
|
|
||||||
|
static gfxRGBA
|
||||||
|
Unpremultiply(const gfxRGBA& aColor)
|
||||||
|
{
|
||||||
|
gfxFloat a = aColor.a;
|
||||||
|
return (a > 0.0) ? gfxRGBA(aColor.r / a, aColor.g / a, aColor.b / a, a) : aColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gfxRGBA
|
||||||
|
TransparentColor(gfxRGBA 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) {
|
||||||
|
gfxRGBA premulLeftColor = Premultiply(leftStop.mColor);
|
||||||
|
gfxRGBA premulRightColor = Premultiply(rightStop.mColor);
|
||||||
|
// Calculate how many extra steps. We do a step per 10% transparency.
|
||||||
|
size_t stepCount = NSToIntFloor(fabs(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),
|
||||||
|
Unpremultiply(InterpolateColor(premulLeftColor, premulRightColor, frac)));
|
||||||
|
aStops.InsertElementAt(x, newStop);
|
||||||
|
x++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
nsCSSRendering::PaintGradient(nsPresContext* aPresContext,
|
nsCSSRendering::PaintGradient(nsPresContext* aPresContext,
|
||||||
nsRenderingContext& aRenderingContext,
|
nsRenderingContext& aRenderingContext,
|
||||||
@ -2514,6 +2591,8 @@ nsCSSRendering::PaintGradient(nsPresContext* aPresContext,
|
|||||||
stops.AppendElement(ColorStop(firstStop, lastColor));
|
stops.AppendElement(ColorStop(firstStop, lastColor));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ResolvePremultipliedAlpha(stops);
|
||||||
|
|
||||||
bool isRepeat = aGradient->mRepeating || forceRepeatToCoverTiles;
|
bool isRepeat = aGradient->mRepeating || forceRepeatToCoverTiles;
|
||||||
|
|
||||||
// Now set normalized color stops in pattern.
|
// Now set normalized color stops in pattern.
|
||||||
|
1
layout/reftests/css-gradients/linear-premul-ref.html
Normal file
1
layout/reftests/css-gradients/linear-premul-ref.html
Normal file
@ -0,0 +1 @@
|
|||||||
|
<div style="background: linear-gradient(to right, rgba(255, 0, 0, .9) 0% , rgba(255, 0, 0, 0) 50%, rgba(0, 0, 255, 0) 50%, rgba(0, 0, 255, 1) 100%) no-repeat; width: 300px; height: 300px;"><br></div>
|
1
layout/reftests/css-gradients/linear-premul.html
Normal file
1
layout/reftests/css-gradients/linear-premul.html
Normal file
@ -0,0 +1 @@
|
|||||||
|
<div style="background: linear-gradient(to right, rgba(255, 0, 0, .9) 0% , rgba(0, 255, 0, 0) 50%, rgba(0, 0, 255, 1) 100%) no-repeat; width: 300px; height: 300px;"><br></div>
|
1
layout/reftests/css-gradients/radial-premul-ref.html
Normal file
1
layout/reftests/css-gradients/radial-premul-ref.html
Normal file
@ -0,0 +1 @@
|
|||||||
|
<div style="background: radial-gradient(rgba(255, 0, 0, .9) 0% , rgba(255, 0, 0, 0) 50%, rgba(0, 0, 255, 0) 50%, rgba(0, 0, 255, 1) 100%) no-repeat; width: 300px; height: 300px;"><br></div>
|
1
layout/reftests/css-gradients/radial-premul.html
Normal file
1
layout/reftests/css-gradients/radial-premul.html
Normal file
@ -0,0 +1 @@
|
|||||||
|
<div style="background: radial-gradient(rgba(255, 0, 0, .9) 0% , rgba(0, 255, 0, 0) 50%, rgba(0, 0, 255, 1) 100%) no-repeat; width: 300px; height: 300px;"><br></div>
|
@ -16,6 +16,7 @@ fuzzy-if(!contentSameGfxBackendAsCanvas,4,92400) fuzzy-if(azureSkiaGL,2,143400)
|
|||||||
== linear-diagonal-4a.html linear-diagonal-4-ref.html
|
== linear-diagonal-4a.html linear-diagonal-4-ref.html
|
||||||
== linear-diagonal-4b.html linear-diagonal-4-ref.html
|
== linear-diagonal-4b.html linear-diagonal-4-ref.html
|
||||||
== linear-diagonal-4c.html linear-diagonal-4-ref.html
|
== linear-diagonal-4c.html linear-diagonal-4-ref.html
|
||||||
|
== linear-premul.html linear-premul-ref.html
|
||||||
|
|
||||||
# these tests uses a similar gradient over different bounds. It's perfectly
|
# these tests uses a similar gradient over different bounds. It's perfectly
|
||||||
# reasonable to expect implementations to give slightly different results
|
# reasonable to expect implementations to give slightly different results
|
||||||
@ -91,6 +92,7 @@ fuzzy-if(Android,17,13320) == radial-shape-farthest-side-1c.html radial-shape-fa
|
|||||||
== radial-zero-length-1h.html radial-zero-length-1-ref.html
|
== radial-zero-length-1h.html radial-zero-length-1-ref.html
|
||||||
== radial-zero-length-1i.html radial-zero-length-1-ref.html
|
== radial-zero-length-1i.html radial-zero-length-1-ref.html
|
||||||
== radial-zero-length-1j.html radial-zero-length-1-ref.html
|
== radial-zero-length-1j.html radial-zero-length-1-ref.html
|
||||||
|
== radial-premul.html radial-premul-ref.html
|
||||||
== repeated-final-stop-1.html repeated-final-stop-1-ref.html
|
== repeated-final-stop-1.html repeated-final-stop-1-ref.html
|
||||||
== repeating-linear-1a.html repeating-linear-1-ref.html
|
== repeating-linear-1a.html repeating-linear-1-ref.html
|
||||||
== repeating-linear-1b.html repeating-linear-1-ref.html
|
== repeating-linear-1b.html repeating-linear-1-ref.html
|
||||||
|
Loading…
Reference in New Issue
Block a user