Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(709)

Unified Diff: third_party/WebKit/Source/core/paint/BoxBorderPainter.cpp

Issue 2695013011: Fix miter clipping of borders with large radius opposite a wide edge (Closed)
Patch Set: New image baselines Created 3 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « third_party/WebKit/LayoutTests/platform/win7/fast/writing-mode/border-vertical-lr-expected.png ('k') | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: third_party/WebKit/Source/core/paint/BoxBorderPainter.cpp
diff --git a/third_party/WebKit/Source/core/paint/BoxBorderPainter.cpp b/third_party/WebKit/Source/core/paint/BoxBorderPainter.cpp
index 31355c04a9bc26a4a9e38a89a2294df5336349e3..e66f7b6453a3213abcbec05bc35656e586644df0 100644
--- a/third_party/WebKit/Source/core/paint/BoxBorderPainter.cpp
+++ b/third_party/WebKit/Source/core/paint/BoxBorderPainter.cpp
@@ -1124,7 +1124,9 @@ void BoxBorderPainter::clipBorderSidePolygon(GraphicsContext& graphicsContext,
MiterType secondMiter) const {
DCHECK(firstMiter != NoMiter || secondMiter != NoMiter);
- FloatPoint quad[4];
+ FloatPoint edgeQuad[4]; // The boundary of the edge for fill
+ FloatPoint boundQuad1; // Point 1 of the rectilinear bounding box of EdgeQuad
+ FloatPoint boundQuad2; // Point 2 of the rectilinear bounding box of EdgeQuad
const LayoutRect outerRect(m_outer.rect());
const LayoutRect innerRect(m_inner.rect());
@@ -1133,170 +1135,242 @@ void BoxBorderPainter::clipBorderSidePolygon(GraphicsContext& graphicsContext,
// may draw, including areas inside the innerBorder.
//
// 0----------------3
- // 0 \ / 0
+ // 3 \ / 0
// |\ 1----------- 2 /|
- // | 1 1 |
+ // | 2 1 |
// | | | |
// | | | |
- // | 2 2 |
- // |/ 1------------2 \|
- // 3 / \ 3
- // 0----------------3
- //
+ // | 1 2 |
+ // |/ 2------------1 \|
+ // 0 / \ 3
+ // 3----------------0
+
+ // Offset size and direction to expand clipping quad
+ const static float kExtensionLength = 1e-1f;
+ FloatSize extensionOffset;
switch (side) {
case BSTop:
- quad[0] = FloatPoint(outerRect.minXMinYCorner());
- quad[1] = FloatPoint(innerRect.minXMinYCorner());
- quad[2] = FloatPoint(innerRect.maxXMinYCorner());
- quad[3] = FloatPoint(outerRect.maxXMinYCorner());
+ edgeQuad[0] = FloatPoint(outerRect.minXMinYCorner());
+ edgeQuad[1] = FloatPoint(innerRect.minXMinYCorner());
+ edgeQuad[2] = FloatPoint(innerRect.maxXMinYCorner());
+ edgeQuad[3] = FloatPoint(outerRect.maxXMinYCorner());
+
+ DCHECK(edgeQuad[0].y() == edgeQuad[3].y());
+ DCHECK(edgeQuad[1].y() == edgeQuad[2].y());
+
+ boundQuad1 = FloatPoint(edgeQuad[0].x(), edgeQuad[1].y());
+ boundQuad2 = FloatPoint(edgeQuad[3].x(), edgeQuad[2].y());
+
+ extensionOffset.setWidth(-kExtensionLength);
+ extensionOffset.setHeight(0);
if (!m_inner.getRadii().topLeft().isZero()) {
findIntersection(
- quad[0], quad[1],
- FloatPoint(quad[1].x() + m_inner.getRadii().topLeft().width(),
- quad[1].y()),
- FloatPoint(quad[1].x(),
- quad[1].y() + m_inner.getRadii().topLeft().height()),
- quad[1]);
+ edgeQuad[0], edgeQuad[1],
+ FloatPoint(edgeQuad[1].x() + m_inner.getRadii().topLeft().width(),
+ edgeQuad[1].y()),
+ FloatPoint(edgeQuad[1].x(),
+ edgeQuad[1].y() + m_inner.getRadii().topLeft().height()),
+ edgeQuad[1]);
+ DCHECK(boundQuad1.y() <= edgeQuad[1].y());
+ boundQuad1.setY(edgeQuad[1].y());
+ boundQuad2.setY(edgeQuad[1].y());
}
if (!m_inner.getRadii().topRight().isZero()) {
findIntersection(
- quad[3], quad[2],
- FloatPoint(quad[2].x() - m_inner.getRadii().topRight().width(),
- quad[2].y()),
- FloatPoint(quad[2].x(),
- quad[2].y() + m_inner.getRadii().topRight().height()),
- quad[2]);
+ edgeQuad[3], edgeQuad[2],
+ FloatPoint(edgeQuad[2].x() - m_inner.getRadii().topRight().width(),
+ edgeQuad[2].y()),
+ FloatPoint(
+ edgeQuad[2].x(),
+ edgeQuad[2].y() + m_inner.getRadii().topRight().height()),
+ edgeQuad[2]);
+ if (boundQuad1.y() < edgeQuad[2].y()) {
+ boundQuad1.setY(edgeQuad[2].y());
+ boundQuad2.setY(edgeQuad[2].y());
+ }
}
break;
case BSLeft:
- quad[0] = FloatPoint(outerRect.minXMinYCorner());
- quad[1] = FloatPoint(innerRect.minXMinYCorner());
- quad[2] = FloatPoint(innerRect.minXMaxYCorner());
- quad[3] = FloatPoint(outerRect.minXMaxYCorner());
+ // Swap the order of adjacent edges to allow common code
+ std::swap(firstMiter, secondMiter);
+ edgeQuad[0] = FloatPoint(outerRect.minXMaxYCorner());
+ edgeQuad[1] = FloatPoint(innerRect.minXMaxYCorner());
+ edgeQuad[2] = FloatPoint(innerRect.minXMinYCorner());
+ edgeQuad[3] = FloatPoint(outerRect.minXMinYCorner());
+
+ DCHECK(edgeQuad[0].x() == edgeQuad[3].x());
+ DCHECK(edgeQuad[1].x() == edgeQuad[2].x());
+
+ boundQuad1 = FloatPoint(edgeQuad[1].x(), edgeQuad[0].y());
+ boundQuad2 = FloatPoint(edgeQuad[2].x(), edgeQuad[3].y());
+
+ extensionOffset.setWidth(0);
+ extensionOffset.setHeight(kExtensionLength);
if (!m_inner.getRadii().topLeft().isZero()) {
findIntersection(
- quad[0], quad[1],
- FloatPoint(quad[1].x() + m_inner.getRadii().topLeft().width(),
- quad[1].y()),
- FloatPoint(quad[1].x(),
- quad[1].y() + m_inner.getRadii().topLeft().height()),
- quad[1]);
+ edgeQuad[3], edgeQuad[2],
+ FloatPoint(edgeQuad[2].x() + m_inner.getRadii().topLeft().width(),
+ edgeQuad[2].y()),
+ FloatPoint(edgeQuad[2].x(),
+ edgeQuad[2].y() + m_inner.getRadii().topLeft().height()),
+ edgeQuad[2]);
+ DCHECK(boundQuad2.x() <= edgeQuad[2].x());
+ boundQuad1.setX(edgeQuad[2].x());
+ boundQuad2.setX(edgeQuad[2].x());
}
if (!m_inner.getRadii().bottomLeft().isZero()) {
findIntersection(
- quad[3], quad[2],
- FloatPoint(quad[2].x() + m_inner.getRadii().bottomLeft().width(),
- quad[2].y()),
- FloatPoint(quad[2].x(),
- quad[2].y() - m_inner.getRadii().bottomLeft().height()),
- quad[2]);
+ edgeQuad[0], edgeQuad[1],
+ FloatPoint(
+ edgeQuad[1].x() + m_inner.getRadii().bottomLeft().width(),
+ edgeQuad[1].y()),
+ FloatPoint(
+ edgeQuad[1].x(),
+ edgeQuad[1].y() - m_inner.getRadii().bottomLeft().height()),
+ edgeQuad[1]);
+ if (boundQuad1.x() < edgeQuad[1].x()) {
+ boundQuad1.setX(edgeQuad[1].x());
+ boundQuad2.setX(edgeQuad[1].x());
+ }
}
break;
case BSBottom:
- quad[0] = FloatPoint(outerRect.minXMaxYCorner());
- quad[1] = FloatPoint(innerRect.minXMaxYCorner());
- quad[2] = FloatPoint(innerRect.maxXMaxYCorner());
- quad[3] = FloatPoint(outerRect.maxXMaxYCorner());
+ // Swap the order of adjacent edges to allow common code
+ std::swap(firstMiter, secondMiter);
+ edgeQuad[0] = FloatPoint(outerRect.maxXMaxYCorner());
+ edgeQuad[1] = FloatPoint(innerRect.maxXMaxYCorner());
+ edgeQuad[2] = FloatPoint(innerRect.minXMaxYCorner());
+ edgeQuad[3] = FloatPoint(outerRect.minXMaxYCorner());
+
+ DCHECK(edgeQuad[0].y() == edgeQuad[3].y());
+ DCHECK(edgeQuad[1].y() == edgeQuad[2].y());
+
+ boundQuad1 = FloatPoint(edgeQuad[0].x(), edgeQuad[1].y());
+ boundQuad2 = FloatPoint(edgeQuad[3].x(), edgeQuad[2].y());
+
+ extensionOffset.setWidth(kExtensionLength);
+ extensionOffset.setHeight(0);
if (!m_inner.getRadii().bottomLeft().isZero()) {
findIntersection(
- quad[0], quad[1],
- FloatPoint(quad[1].x() + m_inner.getRadii().bottomLeft().width(),
- quad[1].y()),
- FloatPoint(quad[1].x(),
- quad[1].y() - m_inner.getRadii().bottomLeft().height()),
- quad[1]);
+ edgeQuad[3], edgeQuad[2],
+ FloatPoint(
+ edgeQuad[2].x() + m_inner.getRadii().bottomLeft().width(),
+ edgeQuad[2].y()),
+ FloatPoint(
+ edgeQuad[2].x(),
+ edgeQuad[2].y() - m_inner.getRadii().bottomLeft().height()),
+ edgeQuad[2]);
+ DCHECK(boundQuad2.y() >= edgeQuad[2].y());
+ boundQuad1.setY(edgeQuad[2].y());
+ boundQuad2.setY(edgeQuad[2].y());
}
if (!m_inner.getRadii().bottomRight().isZero()) {
findIntersection(
- quad[3], quad[2],
- FloatPoint(quad[2].x() - m_inner.getRadii().bottomRight().width(),
- quad[2].y()),
- FloatPoint(quad[2].x(),
- quad[2].y() - m_inner.getRadii().bottomRight().height()),
- quad[2]);
+ edgeQuad[0], edgeQuad[1],
+ FloatPoint(
+ edgeQuad[1].x() - m_inner.getRadii().bottomRight().width(),
+ edgeQuad[1].y()),
+ FloatPoint(
+ edgeQuad[1].x(),
+ edgeQuad[1].y() - m_inner.getRadii().bottomRight().height()),
+ edgeQuad[1]);
+ if (boundQuad1.y() > edgeQuad[1].y()) {
+ boundQuad1.setY(edgeQuad[1].y());
+ boundQuad2.setY(edgeQuad[1].y());
+ }
}
break;
case BSRight:
- quad[0] = FloatPoint(outerRect.maxXMinYCorner());
- quad[1] = FloatPoint(innerRect.maxXMinYCorner());
- quad[2] = FloatPoint(innerRect.maxXMaxYCorner());
- quad[3] = FloatPoint(outerRect.maxXMaxYCorner());
+ edgeQuad[0] = FloatPoint(outerRect.maxXMinYCorner());
+ edgeQuad[1] = FloatPoint(innerRect.maxXMinYCorner());
+ edgeQuad[2] = FloatPoint(innerRect.maxXMaxYCorner());
+ edgeQuad[3] = FloatPoint(outerRect.maxXMaxYCorner());
+
+ DCHECK(edgeQuad[0].x() == edgeQuad[3].x());
+ DCHECK(edgeQuad[1].x() == edgeQuad[2].x());
+
+ boundQuad1 = FloatPoint(edgeQuad[1].x(), edgeQuad[0].y());
+ boundQuad2 = FloatPoint(edgeQuad[2].x(), edgeQuad[3].y());
+
+ extensionOffset.setWidth(0);
+ extensionOffset.setHeight(-kExtensionLength);
if (!m_inner.getRadii().topRight().isZero()) {
findIntersection(
- quad[0], quad[1],
- FloatPoint(quad[1].x() - m_inner.getRadii().topRight().width(),
- quad[1].y()),
- FloatPoint(quad[1].x(),
- quad[1].y() + m_inner.getRadii().topRight().height()),
- quad[1]);
+ edgeQuad[0], edgeQuad[1],
+ FloatPoint(edgeQuad[1].x() - m_inner.getRadii().topRight().width(),
+ edgeQuad[1].y()),
+ FloatPoint(
+ edgeQuad[1].x(),
+ edgeQuad[1].y() + m_inner.getRadii().topRight().height()),
+ edgeQuad[1]);
+ DCHECK(boundQuad1.x() >= edgeQuad[1].x());
+ boundQuad1.setX(edgeQuad[1].x());
+ boundQuad2.setX(edgeQuad[1].x());
}
if (!m_inner.getRadii().bottomRight().isZero()) {
findIntersection(
- quad[3], quad[2],
- FloatPoint(quad[2].x() - m_inner.getRadii().bottomRight().width(),
- quad[2].y()),
- FloatPoint(quad[2].x(),
- quad[2].y() - m_inner.getRadii().bottomRight().height()),
- quad[2]);
+ edgeQuad[3], edgeQuad[2],
+ FloatPoint(
+ edgeQuad[2].x() - m_inner.getRadii().bottomRight().width(),
+ edgeQuad[2].y()),
+ FloatPoint(
+ edgeQuad[2].x(),
+ edgeQuad[2].y() - m_inner.getRadii().bottomRight().height()),
+ edgeQuad[2]);
+ if (boundQuad1.x() > edgeQuad[2].x()) {
+ boundQuad1.setX(edgeQuad[2].x());
+ boundQuad2.setX(edgeQuad[2].x());
+ }
}
break;
}
if (firstMiter == secondMiter) {
- clipQuad(graphicsContext, quad, firstMiter == SoftMiter);
+ clipQuad(graphicsContext, edgeQuad, firstMiter == SoftMiter);
return;
}
- // If antialiasing settings for the first edge and second edge is different,
- // they have to be addressed separately. We do this by breaking the quad into
- // two parallelograms, made by moving quad[1] and quad[2].
- float ax = quad[1].x() - quad[0].x();
- float ay = quad[1].y() - quad[0].y();
- float bx = quad[2].x() - quad[1].x();
- float by = quad[2].y() - quad[1].y();
- float cx = quad[3].x() - quad[2].x();
- float cy = quad[3].y() - quad[2].y();
-
- const static float kEpsilon = 1e-2f;
- float r1, r2;
- if (fabsf(bx) < kEpsilon && fabsf(by) < kEpsilon) {
- // The quad was actually a triangle.
- r1 = r2 = 1.0f;
- } else {
- // Extend parallelogram a bit to hide calculation error
- const static float kExtendFill = 1e-2f;
+ // If antialiasing settings for the first edge and second edge are different,
+ // they have to be addressed separately. We do this by applying 2 clips, one
+ // for each miter, with the appropriate anti-aliasing setting. Each clip uses
+ // 3 sides of the quad rectilinear bounding box and a 4th side aligned with
+ // the miter edge. We extend the clip in the miter direction to ensure overlap
+ // as each edge is drawn.
+ if (firstMiter != NoMiter) {
+ FloatPoint clippingQuad[4];
- r1 = (-ax * by + ay * bx) / (cx * by - cy * bx) + kExtendFill;
- r2 = (-cx * by + cy * bx) / (ax * by - ay * bx) + kExtendFill;
- }
+ clippingQuad[0] = edgeQuad[0] + extensionOffset;
+ findIntersection(edgeQuad[0], edgeQuad[1], boundQuad1, boundQuad2,
+ clippingQuad[1]);
+ clippingQuad[1] += extensionOffset;
+ clippingQuad[2] = boundQuad2;
+ clippingQuad[3] = edgeQuad[3];
- if (firstMiter != NoMiter) {
- FloatPoint firstQuad[4];
- firstQuad[0] = quad[0];
- firstQuad[1] = quad[1];
- firstQuad[2] = FloatPoint(quad[3].x() + r2 * ax, quad[3].y() + r2 * ay);
- firstQuad[3] = quad[3];
- clipQuad(graphicsContext, firstQuad, firstMiter == SoftMiter);
+ clipQuad(graphicsContext, clippingQuad, firstMiter == SoftMiter);
}
if (secondMiter != NoMiter) {
- FloatPoint secondQuad[4];
- secondQuad[0] = quad[0];
- secondQuad[1] = FloatPoint(quad[0].x() - r1 * cx, quad[0].y() - r1 * cy);
- secondQuad[2] = quad[2];
- secondQuad[3] = quad[3];
- clipQuad(graphicsContext, secondQuad, secondMiter == SoftMiter);
+ FloatPoint clippingQuad[4];
+
+ clippingQuad[0] = edgeQuad[0];
+ clippingQuad[1] = boundQuad1;
+ findIntersection(edgeQuad[2], edgeQuad[3], boundQuad1, boundQuad2,
+ clippingQuad[2]);
+ clippingQuad[2] -= extensionOffset;
+ clippingQuad[3] = edgeQuad[3] - extensionOffset;
+
+ clipQuad(graphicsContext, clippingQuad, secondMiter == SoftMiter);
}
}
« no previous file with comments | « third_party/WebKit/LayoutTests/platform/win7/fast/writing-mode/border-vertical-lr-expected.png ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698