From 061ce9163f067d836a7a7e09d197d171d44b79e0 Mon Sep 17 00:00:00 2001 From: Lee Salzman Date: Fri, 31 Jul 2015 14:27:19 -0400 Subject: [PATCH] Bug 1185636 - Part 4 - Implement CSS border corners by splitting geometry instead of gradients with hard stops. r=mstange --- gfx/2d/Types.h | 8 + layout/base/nsCSSRenderingBorders.cpp | 572 +++++++++++++++++--------- layout/base/nsCSSRenderingBorders.h | 6 - 3 files changed, 394 insertions(+), 192 deletions(-) diff --git a/gfx/2d/Types.h b/gfx/2d/Types.h index 9606d59e78a..bc86f5a25ec 100644 --- a/gfx/2d/Types.h +++ b/gfx/2d/Types.h @@ -266,6 +266,14 @@ public: uint32_t(r * 255.0f) << 16 | uint32_t(a * 255.0f) << 24; } + bool operator==(const Color& aColor) const { + return r == aColor.r && g == aColor.g && b == aColor.b && a == aColor.a; + } + + bool operator!=(const Color& aColor) const { + return !(*this == aColor); + } + Float r, g, b, a; }; diff --git a/layout/base/nsCSSRenderingBorders.cpp b/layout/base/nsCSSRenderingBorders.cpp index ce932b17243..8fccd76a28a 100644 --- a/layout/base/nsCSSRenderingBorders.cpp +++ b/layout/base/nsCSSRenderingBorders.cpp @@ -1032,66 +1032,26 @@ bool IsVisible(int aStyle) return false; } -already_AddRefed -nsCSSBorderRenderer::CreateCornerGradient(mozilla::css::Corner aCorner, - nscolor aFirstColor, - nscolor aSecondColor, - DrawTarget *aDT, - Point &aPoint1, - Point &aPoint2) +struct twoFloats { - typedef struct { Float a, b; } twoFloats; + Float a, b; - const twoFloats gradientCoeff[4] = { { -1, +1 }, - { -1, -1 }, - { +1, -1 }, - { +1, +1 } }; + twoFloats operator*(const Size& aSize) const { + return { a * aSize.width, b * aSize.height }; + } - // Sides which form the 'width' and 'height' for the calculation of the angle - // for our gradient. - const int cornerWidth[4] = { 3, 1, 1, 3 }; - const int cornerHeight[4] = { 0, 0, 2, 2 }; + twoFloats operator*(Float aScale) const { + return { a * aScale, b * aScale }; + } - Point cornerOrigin = mOuterRect.AtCorner(aCorner); + twoFloats operator+(const Point& aPoint) const { + return { a + aPoint.x, b + aPoint.y }; + } - Point pat1, pat2; - pat1.x = cornerOrigin.x + - mBorderWidths[cornerHeight[aCorner]] * gradientCoeff[aCorner].a; - pat1.y = cornerOrigin.y + - mBorderWidths[cornerWidth[aCorner]] * gradientCoeff[aCorner].b; - pat2.x = cornerOrigin.x - - mBorderWidths[cornerHeight[aCorner]] * gradientCoeff[aCorner].a; - pat2.y = cornerOrigin.y - - mBorderWidths[cornerWidth[aCorner]] * gradientCoeff[aCorner].b; - - aPoint1 = Point(pat1.x, pat1.y); - aPoint2 = Point(pat2.x, pat2.y); - - Color firstColor = Color::FromABGR(aFirstColor); - Color secondColor = Color::FromABGR(aSecondColor); - - nsTArray rawStops(2); - rawStops.SetLength(2); - rawStops[0].color = firstColor; - rawStops[0].offset = 0.5; - rawStops[1].color = secondColor; - rawStops[1].offset = 0.5; - RefPtr gs = - gfxGradientCache::GetGradientStops(aDT, rawStops, ExtendMode::CLAMP); - if (!gs) { - // Having two corners, both with reversed color stops is pretty common - // for certain border types. Let's optimize it! - rawStops[0].color = secondColor; - rawStops[1].color = firstColor; - Point tmp = aPoint1; - aPoint1 = aPoint2; - aPoint2 = tmp; - gs = gfxGradientCache::GetOrCreateGradientStops(aDT, rawStops, ExtendMode::CLAMP); - } - return gs.forget(); -} - -typedef struct { Float a, b; } twoFloats; + operator Point() const { + return Point(a, b); + } +}; void nsCSSBorderRenderer::DrawSingleWidthSolidBorder() @@ -1105,12 +1065,8 @@ nsCSSBorderRenderer::DrawSingleWidthSolidBorder() { -0.5, 0 }, { 0, -0.5 } }; NS_FOR_CSS_SIDES(side) { - Point firstCorner = rect.CCWCorner(side); - firstCorner.x += cornerAdjusts[side].a; - firstCorner.y += cornerAdjusts[side].b; - Point secondCorner = rect.CWCorner(side); - secondCorner.x += cornerAdjusts[side].a; - secondCorner.y += cornerAdjusts[side].b; + Point firstCorner = rect.CCWCorner(side) + cornerAdjusts[side]; + Point secondCorner = rect.CWCorner(side) + cornerAdjusts[side]; ColorPattern color(ToDeviceColor(mBorderColors[side])); @@ -1118,11 +1074,308 @@ nsCSSBorderRenderer::DrawSingleWidthSolidBorder() } } +// Intersect a ray from the inner corner to the outer corner +// with the border radius, yielding the intersection point. +static Point +IntersectBorderRadius(const Point& aCenter, const Size& aRadius, + const Point& aInnerCorner, + const Point& aCornerDirection) +{ + Point toCorner = aCornerDirection; + // transform to-corner ray to unit-circle space + toCorner.x /= aRadius.width; + toCorner.y /= aRadius.height; + // normalize to-corner ray + Float cornerDist = toCorner.Length(); + if (cornerDist < 1.0e-6f) { + return aInnerCorner; + } + toCorner = toCorner / cornerDist; + // ray from inner corner to border radius center + Point toCenter = aCenter - aInnerCorner; + // transform to-center ray to unit-circle space + toCenter.x /= aRadius.width; + toCenter.y /= aRadius.height; + // compute offset of intersection with border radius unit circle + Float offset = toCenter.DotProduct(toCorner); + // compute discriminant to check for intersections + Float discrim = 1.0f - toCenter.DotProduct(toCenter) + offset * offset; + // choose farthest intersection + offset += sqrtf(std::max(discrim, 0.0f)); + // transform to-corner ray back out of unit-circle space + toCorner.x *= aRadius.width; + toCorner.y *= aRadius.height; + return aInnerCorner + toCorner * offset; +} + +// Calculate the split point and split angle for a border radius with +// differing sides. +static inline void +SplitBorderRadius(const Point& aCenter, const Size& aRadius, + const Point& aOuterCorner, const Point& aInnerCorner, + const twoFloats& aCornerMults, Float aStartAngle, + Point& aSplit, Float& aSplitAngle) +{ + Point cornerDir = aOuterCorner - aInnerCorner; + if (cornerDir.x == cornerDir.y && aRadius.IsSquare()) { + // optimize 45-degree intersection with circle since we can assume + // the circle center lies along the intersection edge + aSplit = aCenter - aCornerMults * (aRadius * Float(1.0f / M_SQRT2)); + aSplitAngle = aStartAngle + 0.5f * M_PI / 2.0f; + } else { + aSplit = IntersectBorderRadius(aCenter, aRadius, aInnerCorner, cornerDir); + aSplitAngle = + atan2f((aSplit.y - aCenter.y) / aRadius.height, + (aSplit.x - aCenter.x) / aRadius.width); + } +} + +// Compute the size of the skirt needed, given the color alphas +// of each corner side and the slope between them. +static void +ComputeCornerSkirtSize(Float aAlpha1, Float aAlpha2, + Float aSlopeY, Float aSlopeX, + Float& aSizeResult, Float& aSlopeResult) +{ + // If either side is (almost) invisible or there is no diagonal edge, + // then don't try to render a skirt. + if (aAlpha1 < 0.01f || aAlpha2 < 0.01f) { + return; + } + aSlopeX = fabs(aSlopeX); + aSlopeY = fabs(aSlopeY); + if (aSlopeX < 1.0e-6f || aSlopeY < 1.0e-6f) { + return; + } + + // If first and second color don't match, we need to split the corner in + // half. The diagonal edges created may not have full pixel coverage given + // anti-aliasing, so we need to compute a small subpixel skirt edge. This + // assumes each half has half coverage to start with, and that coverage + // increases as the skirt is pushed over, with the end result that we want + // to roughly preserve the alpha value along this edge. + // Given slope m, alphas a and A, use quadratic formula to solve for S in: + // a*(1 - 0.5*(1-S)*(1-mS))*(1 - 0.5*A) + 0.5*A = A + // yielding: + // S = ((1+m) - sqrt((1+m)*(1+m) + 4*m*(1 - A/(a*(1-0.5*A))))) / (2*m) + // and substitute k = (1+m)/(2*m): + // S = k - sqrt(k*k + (1 - A/(a*(1-0.5*A)))/m) + Float slope = aSlopeY / aSlopeX; + Float slopeScale = (1.0f + slope) / (2.0f * slope); + Float discrim = + slopeScale*slopeScale + + (1 - aAlpha2 / (aAlpha1 * (1.0f - 0.49f * aAlpha2))) / slope; + if (discrim >= 0) { + aSizeResult = slopeScale - sqrtf(discrim); + aSlopeResult = slope; + } +} + +// Draws a border radius with possibly different sides. +// A skirt is drawn underneath the corner intersection to hide possible +// seams when anti-aliased drawing is used. +// As an optimization, this tries to combine the drawing of the side itself +// with the drawing of the border radius where possible. +static void +DrawBorderRadius(DrawTarget* aDrawTarget, + const Point& aSideStart, Float aSideWidth, + mozilla::css::Corner c, + const Point& aOuterCorner, const Point& aInnerCorner, + const twoFloats& aCornerMultPrev, const twoFloats& aCornerMultNext, + const Size& aCornerDims, + const Size& aOuterRadius, const Size& aInnerRadius, + const Color& aFirstColor, const Color& aSecondColor, + Float aSkirtSize, Float aSkirtSlope) +{ + // Connect edge to outer arc start point + Point outerCornerStart = aOuterCorner + aCornerMultPrev * aCornerDims; + // Connect edge to outer arc end point + Point outerCornerEnd = aOuterCorner + aCornerMultNext * aCornerDims; + // Connect edge to inner arc start point + Point innerCornerStart = + outerCornerStart + + aCornerMultNext * (aCornerDims - aInnerRadius); + // Connect edge to inner arc end point + Point innerCornerEnd = + outerCornerEnd + + aCornerMultPrev * (aCornerDims - aInnerRadius); + + // Outer arc start point + Point outerArcStart = aOuterCorner + aCornerMultPrev * aOuterRadius; + // Outer arc end point + Point outerArcEnd = aOuterCorner + aCornerMultNext * aOuterRadius; + // Inner arc start point + Point innerArcStart = aInnerCorner + aCornerMultPrev * aInnerRadius; + // Inner arc end point + Point innerArcEnd = aInnerCorner + aCornerMultNext * aInnerRadius; + + // Outer radius center + Point outerCenter = aOuterCorner + (aCornerMultPrev + aCornerMultNext) * aOuterRadius; + // Inner radius center + Point innerCenter = aInnerCorner + (aCornerMultPrev + aCornerMultNext) * aInnerRadius; + + RefPtr builder; + RefPtr path; + + if (aFirstColor.a > 0) { + builder = aDrawTarget->CreatePathBuilder(); + // Combine stroke with corner if color matches. + if (aSideWidth > 0) { + builder->MoveTo(aSideStart + aCornerMultNext * aSideWidth); + builder->LineTo(aSideStart); + builder->LineTo(outerCornerStart); + } else { + builder->MoveTo(outerCornerStart); + } + } + + if (aFirstColor != aSecondColor) { + // Start and end angles of corner quadrant + Float startAngle = (c * M_PI) / 2.0f - M_PI, + endAngle = startAngle + M_PI / 2.0f, + outerSplitAngle, innerSplitAngle; + Point outerSplit, innerSplit; + + // Outer half-way point + SplitBorderRadius(outerCenter, aOuterRadius, aOuterCorner, aInnerCorner, + aCornerMultPrev + aCornerMultNext, startAngle, + outerSplit, outerSplitAngle); + // Inner half-way point + if (aInnerRadius.IsEmpty()) { + innerSplit = aInnerCorner; + innerSplitAngle = endAngle; + } else { + SplitBorderRadius(innerCenter, aInnerRadius, aOuterCorner, aInnerCorner, + aCornerMultPrev + aCornerMultNext, startAngle, + innerSplit, innerSplitAngle); + } + + // Draw first half with first color + if (aFirstColor.a > 0) { + AcuteArcToBezier(builder.get(), outerCenter, aOuterRadius, + outerArcStart, outerSplit, startAngle, outerSplitAngle); + // Draw skirt as part of first half + if (aSkirtSize > 0) { + builder->LineTo(outerSplit + aCornerMultNext * aSkirtSize); + builder->LineTo(innerSplit - aCornerMultPrev * (aSkirtSize * aSkirtSlope)); + } + AcuteArcToBezier(builder.get(), innerCenter, aInnerRadius, + innerSplit, innerArcStart, innerSplitAngle, startAngle); + if ((innerCornerStart - innerArcStart).DotProduct(aCornerMultPrev) > 0) { + builder->LineTo(innerCornerStart); + } + builder->Close(); + path = builder->Finish(); + aDrawTarget->Fill(path, ColorPattern(aFirstColor)); + } + + // Draw second half with second color + if (aSecondColor.a > 0) { + builder = aDrawTarget->CreatePathBuilder(); + builder->MoveTo(outerCornerEnd); + if ((innerArcEnd - innerCornerEnd).DotProduct(aCornerMultNext) < 0) { + builder->LineTo(innerCornerEnd); + } + AcuteArcToBezier(builder.get(), innerCenter, aInnerRadius, + innerArcEnd, innerSplit, endAngle, innerSplitAngle); + AcuteArcToBezier(builder.get(), outerCenter, aOuterRadius, + outerSplit, outerArcEnd, outerSplitAngle, endAngle); + builder->Close(); + path = builder->Finish(); + aDrawTarget->Fill(path, ColorPattern(aSecondColor)); + } + } else if (aFirstColor.a > 0) { + // Draw corner with single color + AcuteArcToBezier(builder.get(), outerCenter, aOuterRadius, + outerArcStart, outerArcEnd); + builder->LineTo(outerCornerEnd); + if ((innerArcEnd - innerCornerEnd).DotProduct(aCornerMultNext) < 0) { + builder->LineTo(innerCornerEnd); + } + AcuteArcToBezier(builder.get(), innerCenter, aInnerRadius, + innerArcEnd, innerArcStart, -kKappaFactor); + if ((innerCornerStart - innerArcStart).DotProduct(aCornerMultPrev) > 0) { + builder->LineTo(innerCornerStart); + } + builder->Close(); + path = builder->Finish(); + aDrawTarget->Fill(path, ColorPattern(aFirstColor)); + } +} + +// Draw a corner with possibly different sides. +// A skirt is drawn underneath the corner intersection to hide possible +// seams when anti-aliased drawing is used. +// As an optimization, this tries to combine the drawing of the side itself +// with the drawing of the corner where possible. +static void +DrawCorner(DrawTarget* aDrawTarget, + const Point& aSideStart, Float aSideWidth, + mozilla::css::Corner c, + const Point& aOuterCorner, const Point& aInnerCorner, + const twoFloats& aCornerMultPrev, const twoFloats& aCornerMultNext, + const Size& aCornerDims, + const Color& aFirstColor, const Color& aSecondColor, + Float aSkirtSize, Float aSkirtSlope) +{ + // Corner box start point + Point cornerStart = aOuterCorner + aCornerMultPrev * aCornerDims; + // Corner box end point + Point cornerEnd = aOuterCorner + aCornerMultNext * aCornerDims; + + RefPtr builder; + RefPtr path; + + if (aFirstColor.a > 0) { + builder = aDrawTarget->CreatePathBuilder(); + // Combine stroke with corner if color matches. + if (aSideWidth > 0) { + builder->MoveTo(aSideStart + aCornerMultNext * aSideWidth); + builder->LineTo(aSideStart); + } else { + builder->MoveTo(cornerStart); + } + } + + if (aFirstColor != aSecondColor) { + // Draw first half with first color + if (aFirstColor.a > 0) { + builder->LineTo(aOuterCorner); + // Draw skirt as part of first half + if (aSkirtSize > 0) { + builder->LineTo(aOuterCorner + aCornerMultNext * aSkirtSize); + builder->LineTo(aInnerCorner - aCornerMultPrev * (aSkirtSize * aSkirtSlope)); + } + builder->LineTo(aInnerCorner); + builder->Close(); + path = builder->Finish(); + aDrawTarget->Fill(path, ColorPattern(aFirstColor)); + } + + // Draw second half with second color + if (aSecondColor.a > 0) { + builder = aDrawTarget->CreatePathBuilder(); + builder->MoveTo(cornerEnd); + builder->LineTo(aInnerCorner); + builder->LineTo(aOuterCorner); + builder->Close(); + path = builder->Finish(); + aDrawTarget->Fill(path, ColorPattern(aSecondColor)); + } + } else if (aFirstColor.a > 0) { + // Draw corner with single color + builder->LineTo(aOuterCorner); + builder->LineTo(cornerEnd); + builder->LineTo(aInnerCorner); + builder->Close(); + path = builder->Finish(); + aDrawTarget->Fill(path, ColorPattern(aFirstColor)); + } +} + void nsCSSBorderRenderer::DrawNoCompositeColorSolidBorder() { - const Float alpha = 0.55191497064665766025f; - const twoFloats cornerMults[4] = { { -1, 0 }, { 0, -1 }, { +1, 0 }, @@ -1133,8 +1386,6 @@ nsCSSBorderRenderer::DrawNoCompositeColorSolidBorder() { 0, -0.5 }, { +0.5, 0 } }; - Point pc, pci, p0, p1, p2, p3, pd, p3i; - RectCornerRadii innerRadii; ComputeInnerRadii(mBorderRadii, mBorderWidths, &innerRadii); @@ -1142,11 +1393,16 @@ nsCSSBorderRenderer::DrawNoCompositeColorSolidBorder() strokeRect.Deflate(Margin(mBorderWidths[0] / 2.0, mBorderWidths[1] / 2.0, mBorderWidths[2] / 2.0, mBorderWidths[3] / 2.0)); - ColorPattern colorPat(Color(0, 0, 0, 0)); - LinearGradientPattern gradPat(Point(), Point(), nullptr); + NS_FOR_CSS_SIDES(i) { + // We now draw the current side and the CW corner following it. + // The CCW corner of this side was already drawn in the previous iteration. + // The side will either be drawn as an explicit stroke or combined + // with the drawing of the CW corner. + // If the next side does not have a matching color, then we split the + // corner into two halves, one of each side's color and draw both. + // Thus, the CCW corner of the next side will end up drawn here. - NS_FOR_CSS_CORNERS(i) { - // the corner index -- either 1 2 3 0 (cw) or 0 3 2 1 (ccw) + // the corner index -- either 1 2 3 0 (cw) or 0 3 2 1 (ccw) mozilla::css::Corner c = mozilla::css::Corner((i+1) % 4); mozilla::css::Corner prevCorner = mozilla::css::Corner(i); @@ -1157,122 +1413,72 @@ nsCSSBorderRenderer::DrawNoCompositeColorSolidBorder() int i2 = (i+2) % 4; int i3 = (i+3) % 4; - pc = mOuterRect.AtCorner(c); - pci = mInnerRect.AtCorner(c); - - nscolor firstColor, secondColor; - if (IsVisible(mBorderStyles[i]) && IsVisible(mBorderStyles[i1])) { - firstColor = mBorderColors[i]; - secondColor = mBorderColors[i1]; - } else if (IsVisible(mBorderStyles[i])) { - firstColor = mBorderColors[i]; - secondColor = mBorderColors[i]; + Float sideWidth = 0.0f; + Color firstColor, secondColor; + if (IsVisible(mBorderStyles[i]) && mBorderWidths[i]) { + // draw the side since it is visible + sideWidth = mBorderWidths[i]; + firstColor = ToDeviceColor(mBorderColors[i]); + // if the next side is visible, use its color for corner + secondColor = + IsVisible(mBorderStyles[i1]) && mBorderWidths[i1] ? + ToDeviceColor(mBorderColors[i1]) : + firstColor; + } else if (IsVisible(mBorderStyles[i1]) && mBorderWidths[i1]) { + // assign next side's color to both corner sides + firstColor = ToDeviceColor(mBorderColors[i1]); + secondColor = firstColor; } else { - firstColor = mBorderColors[i1]; - secondColor = mBorderColors[i1]; + // neither side is visible, so nothing to do + continue; } - RefPtr builder = mDrawTarget->CreatePathBuilder(); + Point outerCorner = mOuterRect.AtCorner(c); + Point innerCorner = mInnerRect.AtCorner(c); - Point strokeStart, strokeEnd; - - strokeStart.x = mOuterRect.AtCorner(prevCorner).x + - mBorderCornerDimensions[prevCorner].width * cornerMults[i2].a; - strokeStart.y = mOuterRect.AtCorner(prevCorner).y + - mBorderCornerDimensions[prevCorner].height * cornerMults[i2].b; - - strokeEnd.x = pc.x + mBorderCornerDimensions[c].width * cornerMults[i].a; - strokeEnd.y = pc.y + mBorderCornerDimensions[c].height * cornerMults[i].b; - - strokeStart.x += centerAdjusts[i].a * mBorderWidths[i]; - strokeStart.y += centerAdjusts[i].b * mBorderWidths[i]; - strokeEnd.x += centerAdjusts[i].a * mBorderWidths[i]; - strokeEnd.y += centerAdjusts[i].b * mBorderWidths[i]; - - builder->MoveTo(strokeStart); - builder->LineTo(strokeEnd); - RefPtr path = builder->Finish(); - mDrawTarget->Stroke(path, ColorPattern(ToDeviceColor(mBorderColors[i])), - StrokeOptions(mBorderWidths[i])); - builder = nullptr; - path = nullptr; - - Pattern *pattern; + // start and end points of border side stroke between corners + Point sideStart = + mOuterRect.AtCorner(prevCorner) + + cornerMults[i2] * mBorderCornerDimensions[prevCorner]; + Point sideEnd = outerCorner + cornerMults[i] * mBorderCornerDimensions[c]; + // if the side is inverted, don't draw it + if (-(sideEnd - sideStart).DotProduct(cornerMults[i]) <= 0) { + sideWidth = 0.0f; + } + Float skirtSize = 0.0f, skirtSlope = 0.0f; + // the sides don't match, so compute a skirt if (firstColor != secondColor) { - gradPat.mStops = CreateCornerGradient(c, firstColor, secondColor, - mDrawTarget, gradPat.mBegin, - gradPat.mEnd); - pattern = &gradPat; - } else { - colorPat.mColor = ToDeviceColor(firstColor); - pattern = &colorPat; + Point cornerDir = outerCorner - innerCorner; + ComputeCornerSkirtSize(firstColor.a, secondColor.a, + cornerDir.DotProduct(cornerMults[i]), + cornerDir.DotProduct(cornerMults[i3]), + skirtSize, skirtSlope); } - builder = mDrawTarget->CreatePathBuilder(); - - if (mBorderRadii[c].width > 0 && mBorderRadii[c].height > 0) { - p0.x = pc.x + cornerMults[i].a * mBorderRadii[c].width; - p0.y = pc.y + cornerMults[i].b * mBorderRadii[c].height; - - p3.x = pc.x + cornerMults[i3].a * mBorderRadii[c].width; - p3.y = pc.y + cornerMults[i3].b * mBorderRadii[c].height; - - p1.x = p0.x + alpha * cornerMults[i2].a * mBorderRadii[c].width; - p1.y = p0.y + alpha * cornerMults[i2].b * mBorderRadii[c].height; - - p2.x = p3.x - alpha * cornerMults[i3].a * mBorderRadii[c].width; - p2.y = p3.y - alpha * cornerMults[i3].b * mBorderRadii[c].height; - - Point cornerStart; - cornerStart.x = pc.x + cornerMults[i].a * mBorderCornerDimensions[c].width; - cornerStart.y = pc.y + cornerMults[i].b * mBorderCornerDimensions[c].height; - - builder->MoveTo(cornerStart); - builder->LineTo(p0); - - builder->BezierTo(p1, p2, p3); - - Point outerCornerEnd; - outerCornerEnd.x = pc.x + cornerMults[i3].a * mBorderCornerDimensions[c].width; - outerCornerEnd.y = pc.y + cornerMults[i3].b * mBorderCornerDimensions[c].height; - - builder->LineTo(outerCornerEnd); - - p0.x = pci.x + cornerMults[i].a * innerRadii[c].width; - p0.y = pci.y + cornerMults[i].b * innerRadii[c].height; - - p3i.x = pci.x + cornerMults[i3].a * innerRadii[c].width; - p3i.y = pci.y + cornerMults[i3].b * innerRadii[c].height; - - p1.x = p0.x + alpha * cornerMults[i2].a * innerRadii[c].width; - p1.y = p0.y + alpha * cornerMults[i2].b * innerRadii[c].height; - - p2.x = p3i.x - alpha * cornerMults[i3].a * innerRadii[c].width; - p2.y = p3i.y - alpha * cornerMults[i3].b * innerRadii[c].height; - builder->LineTo(p3i); - builder->BezierTo(p2, p1, p0); - builder->Close(); - path = builder->Finish(); - mDrawTarget->Fill(path, *pattern); - } else { - Point c1, c2, c3, c4; - - c1.x = pc.x + cornerMults[i].a * mBorderCornerDimensions[c].width; - c1.y = pc.y + cornerMults[i].b * mBorderCornerDimensions[c].height; - c2 = pc; - c3.x = pc.x + cornerMults[i3].a * mBorderCornerDimensions[c].width; - c3.y = pc.y + cornerMults[i3].b * mBorderCornerDimensions[c].height; - - builder->MoveTo(c1); - builder->LineTo(c2); - builder->LineTo(c3); - builder->LineTo(pci); - builder->Close(); - - path = builder->Finish(); - - mDrawTarget->Fill(path, *pattern); + if (!mBorderRadii[c].IsEmpty()) { + // the corner has a border radius + DrawBorderRadius(mDrawTarget, + sideStart, sideWidth, + c, outerCorner, innerCorner, + cornerMults[i], cornerMults[i3], + mBorderCornerDimensions[c], + mBorderRadii[c], innerRadii[c], + firstColor, secondColor, skirtSize, skirtSlope); + } else if (!mBorderCornerDimensions[c].IsEmpty()) { + // a corner with no border radius + DrawCorner(mDrawTarget, + sideStart, sideWidth, + c, outerCorner, innerCorner, + cornerMults[i], cornerMults[i3], + mBorderCornerDimensions[c], + firstColor, secondColor, skirtSize, skirtSlope); + } else if (sideWidth > 0 && firstColor.a > 0) { + // if there is no corner, then stroke the border side separately + mDrawTarget->StrokeLine(sideStart + centerAdjusts[i] * sideWidth, + sideEnd + centerAdjusts[i] * sideWidth, + ColorPattern(firstColor), + StrokeOptions(sideWidth)); } } } @@ -1294,12 +1500,8 @@ nsCSSBorderRenderer::DrawRectangularCompositeColors() NS_FOR_CSS_SIDES(side) { int sideNext = (side + 1) % 4; - Point firstCorner = rect.CCWCorner(side); - firstCorner.x += cornerAdjusts[side].a; - firstCorner.y += cornerAdjusts[side].b; - Point secondCorner = rect.CWCorner(side); - secondCorner.x -= cornerAdjusts[side].a; - secondCorner.y -= cornerAdjusts[side].b; + Point firstCorner = rect.CCWCorner(side) + cornerAdjusts[side]; + Point secondCorner = rect.CWCorner(side) - cornerAdjusts[side]; Color currentColor = Color::FromABGR( currentColors[side] ? currentColors[side]->mColor @@ -1308,9 +1510,7 @@ nsCSSBorderRenderer::DrawRectangularCompositeColors() mDrawTarget->StrokeLine(firstCorner, secondCorner, ColorPattern(ToDeviceColor(currentColor))); - Point cornerTopLeft = rect.CWCorner(side); - cornerTopLeft.x -= 0.5; - cornerTopLeft.y -= 0.5; + Point cornerTopLeft = rect.CWCorner(side) - Point(0.5, 0.5); Color nextColor = Color::FromABGR( currentColors[sideNext] ? currentColors[sideNext]->mColor : mBorderColors[sideNext]); diff --git a/layout/base/nsCSSRenderingBorders.h b/layout/base/nsCSSRenderingBorders.h index 03b2118d9ee..355baa3fd78 100644 --- a/layout/base/nsCSSRenderingBorders.h +++ b/layout/base/nsCSSRenderingBorders.h @@ -211,12 +211,6 @@ private: // with no color effect. bool AllBordersSolid(bool *aHasCompositeColors); - // Azure variant of CreateCornerGradient. - already_AddRefed - CreateCornerGradient(mozilla::css::Corner aCorner, nscolor aFirstColor, - nscolor aSecondColor, mozilla::gfx::DrawTarget *aDT, - mozilla::gfx::Point &aPoint1, mozilla::gfx::Point &aPoint2); - // Draw a solid color border that is uniformly the same width. void DrawSingleWidthSolidBorder();