Bug 459148 - use thebes primitives for SVG rounded rects diff --git a/content/svg/content/src/nsSVGRectElement.cpp b/content/svg/content/src/nsSVGRectElement.cpp --- a/content/svg/content/src/nsSVGRectElement.cpp +++ b/content/svg/content/src/nsSVGRectElement.cpp @@ -205,42 +205,12 @@ nsSVGRectElement::ConstructPath(gfxConte /* However, we may now have made rx > width/2 or else ry > height/2. (If this is the case, we know we must be giving rx and ry the same value.) */ if (rx > halfWidth) rx = ry = halfWidth; else if (ry > halfHeight) rx = ry = halfHeight; - // Conversion factor used for ellipse to bezier conversion. - // Gives radial error of 0.0273% in circular case. - // See comp.graphics.algorithms FAQ 4.04 - const float magic = 4*(sqrt(2.)-1)/3; - const float magic_x = magic*rx; - const float magic_y = magic*ry; - - aCtx->MoveTo(gfxPoint(x + rx, y)); - aCtx->LineTo(gfxPoint(x + width - rx, y)); - - aCtx->CurveTo(gfxPoint(x + width - rx + magic_x, y), - gfxPoint(x + width, y + ry - magic_y), - gfxPoint(x + width, y + ry)); - - aCtx->LineTo(gfxPoint(x + width, y + height - ry)); - - aCtx->CurveTo(gfxPoint(x + width, y + height - ry + magic_y), - gfxPoint(x + width - rx + magic_x, y+height), - gfxPoint(x + width - rx, y + height)); - - aCtx->LineTo(gfxPoint(x + rx, y + height)); - - aCtx->CurveTo(gfxPoint(x + rx - magic_x, y + height), - gfxPoint(x, y + height - ry + magic_y), - gfxPoint(x, y + height - ry)); - - aCtx->LineTo(gfxPoint(x, y + ry)); - - aCtx->CurveTo(gfxPoint(x, y + ry - magic_y), - gfxPoint(x + rx - magic_x, y), - gfxPoint(x + rx, y)); - - aCtx->ClosePath(); + gfxSize corner(rx, ry); + aCtx->RoundedRectangle(gfxRect(x, y, width, height), + gfxCornerSizes(corner, corner, corner, corner)); } diff --git a/gfx/thebes/src/gfxContext.cpp b/gfx/thebes/src/gfxContext.cpp --- a/gfx/thebes/src/gfxContext.cpp +++ b/gfx/thebes/src/gfxContext.cpp @@ -812,37 +812,73 @@ gfxContext::RoundedRectangle(const gfxRe // *** 2 // ** // * // * // 3 // * // * // - // Where 0, 1, 2, 3 are the control points of the Bezier curve for the corner, - // and C is the actual corner point. - // - // For details about representing an elliptical arc as a cubic Bezier curve, - // see http://www.spaceroots.org/documents/ellipse/elliptical-arc.pdf + // Where 0, 1, 2, 3 are the control points of the Bezier curve for + // the corner, and C is the actual corner point. // // At the start of the loop, the current point is assumed to be // the point adjacent to the top left corner on the top // horizontal. Note that corner indices start at the top left and // continue clockwise, whereas in our loop i = 0 refers to the top // right corner. // - // When going CCW, the control points are swapped, and the first corner - // that's drawn is the top left (along with the top segment). - - // This is (sqrt(7) - 1) / 3; this ends up falling out of the equations - // given in the above paper -- it's the value of alpha at the end of section - // 3.4.1 when n2 and n1 are 90 degrees apart. For the various corners, the - // axes the sign of this value changes, or it might be 0 -- it's multiplied by - // the appropriate multiplier from the list before using. - const gfxFloat alpha = 0.54858377035486361; + // When going CCW, the control points are swapped, and the first + // corner that's drawn is the top left (along with the top segment). + // + // There is considerable latitude in how one chooses the four + // control points for a Bezier curve approximation to an ellipse. + // For the overall path to be continuous and show no corner at the + // endpoints of the arc, points 0 and 3 must be at the ends of the + // straight segments of the rectangle; points 0, 1, and C must be + // collinear; and points 3, 2, and C must also be collinear. This + // leaves only two free parameters: the ratio of the line segments + // 01 and 0C, and the ratio of the line segments 32 and 3C. See + // the following papers for extensive discussion of how to choose + // these ratios: + // + // Dokken, Tor, et al. "Good approximation of circles by + // curvature-continuous Bezier curves." Computer-Aided + // Geometric Design 7(1990) 33--41. + // Goldapp, Michael. "Approximation of circular arcs by cubic + // polynomials." Computer-Aided Geometric Design 8(1991) 227--238. + // Maisonobe, Luc. "Drawing an elliptical arc using polylines, + // quadratic, or cubic Bezier curves." + // http://www.spaceroots.org/documents/ellipse/elliptical-arc.pdf + // + // We follow the approach in section 2 of Goldapp (least-error, + // Hermite-type approximation) and make both ratios equal to + // + // 2 2 + n - sqrt(2n + 28) + // alpha = - * --------------------- + // 3 n - 4 + // + // where n = 3( cbrt(sqrt(2)+1) - cbrt(sqrt(2)-1) ). + // + // This is the result of Goldapp's equation (10b) when the angle + // swept out by the arc is pi/2, and the parameter "a-bar" is the + // expression given immediately below equation (21). + // + // Using this value, the maximum radial error for a circle, as a + // fraction of the radius, is on the order of 0.2 x 10^-3. + // Neither Dokken nor Goldapp discusses error for a general + // ellipse; Maisonobe does, but his choice of control points + // follows different constraints, and Goldapp's expression for + // 'alpha' gives much smaller radial error, even for very flat + // ellipses, than Maisonobe's equivalent. + // + // For the various corners and for each axis, the sign of this + // constant changes, or it might be 0 -- it's multiplied by the + // appropriate multiplier from the list before using. + const gfxFloat alpha = 0.55191497064665766025; typedef struct { gfxFloat a, b; } twoFloats; twoFloats cwCornerMults[4] = { { -1, 0 }, { 0, -1 }, { +1, 0 }, { 0, +1 } }; twoFloats ccwCornerMults[4] = { { +1, 0 }, diff --git a/layout/reftests/border-radius/border-circle-2-ref.xhtml b/layout/reftests/border-radius/border-circle-2-ref.xhtml new file mode 100644 --- /dev/null +++ b/layout/reftests/border-radius/border-circle-2-ref.xhtml @@ -0,0 +1,11 @@ + +
+ + + diff --git a/layout/reftests/border-radius/border-circle-2.html b/layout/reftests/border-radius/border-circle-2.html new file mode 100644 --- /dev/null +++ b/layout/reftests/border-radius/border-circle-2.html @@ -0,0 +1,20 @@ + + +