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

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

Issue 1156663008: Anti-aliased box borders (overdraw) Base URL: svn://svn.chromium.org/blink/trunk
Patch Set: split impl, RTEF Created 5 years, 7 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 | « Source/core/paint/BoxBorderPainter.h ('k') | Source/core/paint/BoxPainter.h » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: Source/core/paint/BoxBorderPainter.cpp
diff --git a/Source/core/paint/BoxBorderPainter.cpp b/Source/core/paint/BoxBorderPainter.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..b58efb741a4a4d552240b3bff35f358f66eb6096
--- /dev/null
+++ b/Source/core/paint/BoxBorderPainter.cpp
@@ -0,0 +1,585 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "config.h"
+#include "core/paint/BoxBorderPainter.h"
+
+#include "core/paint/BoxPainter.h"
+#include "core/style/ComputedStyleConstants.h"
+#include "platform/RuntimeEnabledFeatures.h"
+#include "platform/graphics/GraphicsContext.h"
+#include "platform/graphics/GraphicsContextStateSaver.h"
+
+namespace blink {
+
+namespace {
+
+static_assert(BSTop == 0, "unexpected BoxSide enum value");
+static_assert(BSRight == 1, "unexpected BoxSide enum value");
+static_assert(BSBottom == 2, "unexpected BoxSide enum value");
+static_assert(BSLeft == 3, "unexpected BoxSide enum value");
+
+const BoxSide kAdjacentSidesCW[4] = { BSRight, BSBottom, BSLeft, BSTop };
+const BoxSide kAdjacentSidesCCW[4] = { BSLeft, BSTop, BSRight, BSBottom };
+const float kEdgeShiftMap[4][4] = {
+ { 0, 0, -1, 0},
+ { 0, -1, 0, 1 },
+ { 1, 0, -1, 0 },
+ { 0, -1, 0, 0 } };
+
+inline BorderEdgeFlag edgeFlagForSide(BoxSide side)
+{
+ return static_cast<BorderEdgeFlag>(1 << side);
+}
+
+inline bool includesEdge(BorderEdgeFlags flags, BoxSide side)
+{
+ return flags & edgeFlagForSide(side);
+}
+
+inline bool includesAdjacentEdges(BorderEdgeFlags flags)
+{
+ return (flags & (TopBorderEdge | BottomBorderEdge))
+ && (flags & (LeftBorderEdge | RightBorderEdge));
+}
+
+bool borderStyleHasInnerDetail(EBorderStyle style)
+{
+ return style == GROOVE || style == RIDGE || style == DOUBLE;
+}
+
+// OUTSET darkens the bottom and right (and maybe lightens the top and left)
+// INSET darkens the top and left (and maybe lightens the bottom and right)
+inline bool borderStyleHasUnmatchedColorsAtCorner(EBorderStyle style, BoxSide side, BoxSide adjacentSide)
+{
+ // These styles match at the top/left and bottom/right.
+ if (style == INSET || style == GROOVE || style == RIDGE || style == OUTSET) {
+ const BorderEdgeFlags topRightFlags = edgeFlagForSide(BSTop) | edgeFlagForSide(BSRight);
+ const BorderEdgeFlags bottomLeftFlags = edgeFlagForSide(BSBottom) | edgeFlagForSide(BSLeft);
+
+ BorderEdgeFlags flags = edgeFlagForSide(side) | edgeFlagForSide(adjacentSide);
+ return flags == topRightFlags || flags == bottomLeftFlags;
+ }
+ return false;
+}
+
+bool borderStyleFillsBorderArea(EBorderStyle style)
+{
+ return !(style == DOTTED || style == DASHED || style == DOUBLE);
+}
+
+inline bool borderStyleIsDottedOrDashed(EBorderStyle style)
+{
+ return style == DOTTED || style == DASHED;
+}
+
+inline bool borderStylesRequireMitre(BoxSide side, BoxSide adjacentSide, EBorderStyle style, EBorderStyle adjacentStyle)
+{
+ if (style == DOUBLE || adjacentStyle == DOUBLE || adjacentStyle == GROOVE || adjacentStyle == RIDGE)
+ return true;
+
+ if (borderStyleIsDottedOrDashed(style) != borderStyleIsDottedOrDashed(adjacentStyle))
+ return true;
+
+ if (style != adjacentStyle)
+ return true;
+
+ return borderStyleHasUnmatchedColorsAtCorner(style, side, adjacentSide);
+}
+
+inline bool colorsMatchAtCorner(BoxSide side, BoxSide adjacentSide, const BorderEdge edges[])
+{
+ if (edges[side].shouldRender() != edges[adjacentSide].shouldRender())
+ return false;
+
+ if (!edges[side].sharesColorWith(edges[adjacentSide]))
+ return false;
+
+ return !borderStyleHasUnmatchedColorsAtCorner(edges[side].borderStyle(), side, adjacentSide);
+}
+
+inline bool styleRequiresClipPolygon(EBorderStyle style)
+{
+ // These are drawn with a stroke, so we have to clip to get corner miters.
+ return style == DOTTED || style == DASHED;
+}
+
+} // anonymous namespace
+
+BoxBorderInfo::BoxBorderInfo(const ComputedStyle& style, BackgroundBleedAvoidance bleedAvoidance,
+ bool includeLogicalLeftEdge, bool includeLogicalRightEdge)
+ : style(style)
+ , bleedAvoidance(bleedAvoidance)
+ , includeLogicalLeftEdge(includeLogicalLeftEdge)
+ , includeLogicalRightEdge(includeLogicalRightEdge)
+ , visibleEdgeCount(0)
+ , firstVisibleEdge(0)
+ , visibleEdgeSet(0)
+ , isUniformStyle(true)
+ , isUniformWidth(true)
+ , isUniformColor(true)
+ , hasAlpha(false)
+{
+ style.getBorderEdgeInfo(edges, includeLogicalLeftEdge, includeLogicalRightEdge);
+
+ for (unsigned i = 0; i < WTF_ARRAY_LENGTH(edges); ++i) {
+ const BorderEdge& edge = edges[i];
+
+ if (!edge.shouldRender()) {
+ if (edge.presentButInvisible()) {
+ isUniformWidth = false;
+ isUniformColor = false;
+ }
+
+ continue;
+ }
+
+ visibleEdgeCount++;
+ visibleEdgeSet |= edgeFlagForSide(static_cast<BoxSide>(i));
+
+ hasAlpha = hasAlpha || edge.color.hasAlpha();
+
+ if (visibleEdgeCount == 1) {
+ firstVisibleEdge = i;
+ continue;
+ }
+
+ isUniformStyle = isUniformStyle && (edge.borderStyle() == edges[firstVisibleEdge].borderStyle());
+ isUniformWidth = isUniformWidth && (edge.width == edges[firstVisibleEdge].width);
+ isUniformColor = isUniformColor && (edge.color == edges[firstVisibleEdge].color);
+ }
+}
+
+BoxBorderPainter::BoxBorderPainter(const BoxBorderInfo& borderInfo,
+ const FloatRoundedRect& outer, const FloatRoundedRect& inner)
+ : m_borderInfo(borderInfo)
+ , m_outer(outer)
+ , m_inner(inner)
+{
+ BorderEdgeFlags edgeSet = borderInfo.visibleEdgeSet;
+ ASSERT(edgeSet);
+
+ while (edgeSet) {
+ m_opacityGroupIndices.append(m_opacityGroupIndices.size());
+ m_opacityGroups.append(OpacityGroup());
+ OpacityGroup& currentGroup = m_opacityGroups.last();
+ BorderEdgeFlags currentSet = 0;
+
+ for (unsigned i = borderInfo.firstVisibleEdge; i < 4; ++i) {
+ BoxSide side = static_cast<BoxSide>(i);
+ if (!includesEdge(edgeSet, side))
+ continue;
+
+ unsigned sideAlpha = borderInfo.edges[side].color.alpha();
+ if (currentGroup.sides.isEmpty())
+ currentGroup.alpha = sideAlpha;
+
+ if (sideAlpha == currentGroup.alpha) {
+ currentGroup.sides.append(side);
+ currentGroup.edgeFlags |= edgeFlagForSide(side);
+ currentSet |= edgeFlagForSide(side);
+ }
+ }
+ ASSERT(currentSet);
+ ASSERT(!currentGroup.sides.isEmpty());
+
+ static const unsigned kStyleOrder[] = {
+ 0 /* BNONE */,
+ 0 /* BHIDDEN */,
+ 2 /* INSET */,
+ 2 /* GROOVE */,
+ 2 /* OUTSET */,
+ 2 /* RIDGE */,
+ 1 /* DOTTED */,
+ 1 /* DASHED */,
+ 3 /* SOLID */,
+ 2 /* DOUBLE */
+ };
+ std::sort(currentGroup.sides.begin(), currentGroup.sides.end(),
+ [&borderInfo] (BoxSide a, BoxSide b) -> bool
+ {
+ EBorderStyle style1 = borderInfo.edges[a].borderStyle();
+ EBorderStyle style2 = borderInfo.edges[b].borderStyle();
+ ASSERT(style1 != BNONE && style1 != BHIDDEN);
+ return kStyleOrder[style1] < kStyleOrder[style2];
+ });
+
+ edgeSet &= ~currentSet;
+ }
+
+ ASSERT(!m_opacityGroups.isEmpty());
+ ASSERT(m_opacityGroups.size() == m_opacityGroupIndices.size());
+
+ std::sort(m_opacityGroupIndices.begin(), m_opacityGroupIndices.end(),
+ [this] (size_t a, size_t b)
+ {
+ ASSERT(a < m_opacityGroups.size());
+ ASSERT(b < m_opacityGroups.size());
+ ASSERT(m_opacityGroups[a].alpha != m_opacityGroups[b].alpha);
+ return m_opacityGroups[a].alpha > m_opacityGroups[b].alpha;
+ });
+
+ if (outer.isRounded()) {
+ m_roundedPath.addRoundedRect(outer);
+ m_hasRadii[0] = !inner.radii().topLeft().isZero();
+ m_hasRadii[1] = !inner.radii().topRight().isZero();
+ m_hasRadii[2] = !inner.radii().bottomRight().isZero();
+ m_hasRadii[3] = !inner.radii().bottomLeft().isZero();
+ }
+}
+
+void BoxBorderPainter::paint(GraphicsContext* context) const
+{
+ paintGroup(context, 0, 1);
+}
+
+BorderEdgeFlags BoxBorderPainter::paintGroup(GraphicsContext* context, unsigned index = 0,
+ float opacity = 1) const
+{
+ ASSERT(opacity > 0 && opacity <= 1);
+
+ if (index >= m_opacityGroups.size())
+ return ~m_borderInfo.visibleEdgeSet;
+
+ const OpacityGroup& group = m_opacityGroups[m_opacityGroupIndices[index]];
+ const float groupOpacity = static_cast<float>(group.alpha) / 255;
+ ASSERT(groupOpacity <= opacity);
+
+ unsigned paintAlpha = group.alpha / opacity;
+ ASSERT(paintAlpha <= 255);
+
+ bool useLayer = group.alpha != 255
+ && (includesAdjacentEdges(group.edgeFlags) || (index + 1 < m_opacityGroups.size()));
+
+ if (useLayer) {
+ ASSERT(groupOpacity < opacity);
+
+ context->beginLayer(groupOpacity / opacity);
+ opacity *= groupOpacity;
+ paintAlpha = 255;
+ }
+
+ BorderEdgeFlags paintedSides = paintGroup(context, index + 1, opacity);
+
+ for (BoxSide side : group.sides) {
+ paintSide(context, side, paintAlpha, paintedSides);
+ paintedSides |= edgeFlagForSide(side);
+ }
+
+ if (useLayer)
+ context->endLayer();
+
+ return paintedSides;
+}
+
+void BoxBorderPainter::paintSide(GraphicsContext* context, BoxSide side, unsigned alpha,
+ BorderEdgeFlags paintedSides) const
+{
+ BoxSide adjacentSideCW = kAdjacentSidesCW[side];
+ BoxSide adjacentSideCCW = kAdjacentSidesCCW[side];
+
+ // FIXME: ugh
+ if (side == BSBottom || side == BSLeft)
+ std::swap(adjacentSideCW, adjacentSideCCW);
+
+ const BorderEdge& edge = m_borderInfo.edges[side];
+ const BorderEdge& adjacentEdgeCW = m_borderInfo.edges[adjacentSideCW];
+ const BorderEdge& adjacentEdgeCCW = m_borderInfo.edges[adjacentSideCCW];
+ const Color color(edge.color.red(), edge.color.green(), edge.color.blue(), alpha);
+
+ bool usePath = m_outer.isRounded()
+ && (borderStyleHasInnerDetail(edge.borderStyle()) || m_hasRadii[side] || m_hasRadii[(side + 1) % 4]);
+
+ if (usePath) {
+ MitreType mitre1 = colorsMatchAtCorner(side, adjacentSideCCW, m_borderInfo.edges)
+ ? NonAntiAliasedMitre : AntiAliasedMitre;
+ MitreType mitre2 = colorsMatchAtCorner(side, adjacentSideCW, m_borderInfo.edges)
+ ? NonAntiAliasedMitre : AntiAliasedMitre;
+
+ GraphicsContextStateSaver stateSaver(*context);
+ if (m_inner.isRenderable())
+ clipBorderSidePolygon(context, side, mitre1, mitre2);
+ else
+ BoxPainter::clipBorderSideForComplexInnerPath(context, m_outer, m_inner, side, m_borderInfo.edges);
+ float thickness = std::max(std::max(edge.width, adjacentEdgeCCW.width), adjacentEdgeCW.width);
+ BoxPainter::drawBoxSideFromPath(context, LayoutRect(m_outer.rect()), m_roundedPath, m_borderInfo.edges, edge.width, thickness, side, m_borderInfo.style,
+ color, edge.borderStyle(), m_borderInfo.bleedAvoidance, m_borderInfo.includeLogicalLeftEdge, m_borderInfo.includeLogicalRightEdge);
+ } else {
+ MitreType mitre1 = computeMitre(side, adjacentSideCW, paintedSides);
+ MitreType mitre2 = computeMitre(side, adjacentSideCCW, paintedSides);
+
+ bool clipForStyle = (mitre1 != NoMitre || mitre2 != NoMitre)
+ && styleRequiresClipPolygon(edge.borderStyle());
+ bool clipForMitre = mitre1 == NonAntiAliasedMitre || mitre2 == NonAntiAliasedMitre;
+ bool shouldClip = clipForStyle || clipForMitre;
+
+ if (0) {
+ printf("*** side: %d, painted: %x, adjSide1: %d, mitre1: %d, adjSide2: %d, mitre2: %d, clip: %d\n",
+ side, paintedSides, adjacentSideCW, mitre1, adjacentSideCCW, mitre2, shouldClip);
+ if (shouldClip) {
+ printf(" clipForStyle: %d, clipForMitre: %d\n", clipForStyle, clipForMitre);
+ }
+ }
+
+ GraphicsContextStateSaver clipStateSaver(*context, shouldClip);
+ if (shouldClip) {
+ clipBorderSidePolygon(context, side, mitre2, mitre1);
+ // Since we clipped, no need to draw with a mitre.
+ mitre1 = mitre2 = NoMitre;
+ }
+
+ ASSERT(mitre1 == NoMitre || mitre1 == AntiAliasedMitre);
+ ASSERT(mitre2 == NoMitre || mitre2 == AntiAliasedMitre);
+
+ const FloatRect& borderRect = m_outer.rect();
+ const FloatSize edgeShift(borderRect.width() - edge.width, borderRect.height() - edge.width);
+ const FloatRect sideRect(
+ borderRect.x() + edgeShift.width() * kEdgeShiftMap[side][BSLeft],
+ borderRect.y() + edgeShift.height() * kEdgeShiftMap[side][BSTop],
+ borderRect.width() + edgeShift.width() * kEdgeShiftMap[side][BSRight],
+ borderRect.height() + edgeShift.height() * kEdgeShiftMap[side][BSBottom]);
+ ObjectPainter::drawLineForBoxSide(context, sideRect.x(), sideRect.y(), sideRect.maxX(), sideRect.maxY(), side, color, edge.borderStyle(),
+ mitre2 != NoMitre ? adjacentEdgeCCW.width : 0, mitre1 != NoMitre ? adjacentEdgeCW.width : 0, true);
+ }
+}
+
+BoxBorderPainter::MitreType BoxBorderPainter::computeMitre(BoxSide side, BoxSide adjacentSide,
+ BorderEdgeFlags paintedSides) const
+{
+ const BorderEdge& edge = m_borderInfo.edges[side];
+ const BorderEdge& adjacentEdge = m_borderInfo.edges[adjacentSide];
+
+ if (!adjacentEdge.usedWidth())
+ return NoMitre;
+
+ if (!includesEdge(paintedSides, adjacentSide) && borderStyleFillsBorderArea(adjacentEdge.borderStyle()))
+ return NoMitre;
+
+ if (!edge.sharesColorWith(adjacentEdge))
+ return AntiAliasedMitre;
+
+ if (borderStylesRequireMitre(side, adjacentSide, edge.borderStyle(), adjacentEdge.borderStyle())) {
+ return borderStyleHasUnmatchedColorsAtCorner(edge.borderStyle(), side, adjacentSide)
+ ? AntiAliasedMitre : NonAntiAliasedMitre;
+ }
+
+ return NoMitre;
+}
+
+void BoxBorderPainter::clipBorderSidePolygon(GraphicsContext* context, BoxSide side,
+ MitreType mitre1, MitreType mitre2) const
+{
+ ASSERT(mitre1 != NoMitre || mitre2 != NoMitre);
+
+ FloatPoint quad[4];
+
+ const LayoutRect outerRect(m_outer.rect());
+ const LayoutRect innerRect(m_inner.rect());
+
+ // For each side, create a quad that encompasses all parts of that side that may draw,
+ // including areas inside the innerBorder.
+ //
+ // 0----------------3
+ // 0 \ / 0
+ // |\ 1----------- 2 /|
+ // | 1 1 |
+ // | | | |
+ // | | | |
+ // | 2 2 |
+ // |/ 1------------2 \|
+ // 3 / \ 3
+ // 0----------------3
+ //
+ switch (side) {
+ case BSTop:
+ quad[0] = FloatPoint(outerRect.minXMinYCorner());
+ quad[1] = FloatPoint(innerRect.minXMinYCorner());
+ quad[2] = FloatPoint(innerRect.maxXMinYCorner());
+ quad[3] = FloatPoint(outerRect.maxXMinYCorner());
+
+ if (!m_inner.radii().topLeft().isZero()) {
+ findIntersection(quad[0], quad[1],
+ FloatPoint(
+ quad[1].x() + m_inner.radii().topLeft().width(),
+ quad[1].y()),
+ FloatPoint(
+ quad[1].x(),
+ quad[1].y() + m_inner.radii().topLeft().height()),
+ quad[1]);
+ }
+
+ if (!m_inner.radii().topRight().isZero()) {
+ findIntersection(quad[3], quad[2],
+ FloatPoint(
+ quad[2].x() - m_inner.radii().topRight().width(),
+ quad[2].y()),
+ FloatPoint(
+ quad[2].x(),
+ quad[2].y() + m_inner.radii().topRight().height()),
+ quad[2]);
+ }
+
+ if (mitre1 == NoMitre)
+ quad[1].setX(quad[0].x());
+ if (mitre2 == NoMitre)
+ quad[2].setX(quad[3].x());
+
+ break;
+
+ case BSLeft:
+ quad[0] = FloatPoint(outerRect.minXMinYCorner());
+ quad[1] = FloatPoint(innerRect.minXMinYCorner());
+ quad[2] = FloatPoint(innerRect.minXMaxYCorner());
+ quad[3] = FloatPoint(outerRect.minXMaxYCorner());
+
+ if (!m_inner.radii().topLeft().isZero()) {
+ findIntersection(quad[0], quad[1],
+ FloatPoint(
+ quad[1].x() + m_inner.radii().topLeft().width(),
+ quad[1].y()),
+ FloatPoint(
+ quad[1].x(),
+ quad[1].y() + m_inner.radii().topLeft().height()),
+ quad[1]);
+ }
+
+ if (!m_inner.radii().bottomLeft().isZero()) {
+ findIntersection(quad[3], quad[2],
+ FloatPoint(
+ quad[2].x() + m_inner.radii().bottomLeft().width(),
+ quad[2].y()),
+ FloatPoint(
+ quad[2].x(),
+ quad[2].y() - m_inner.radii().bottomLeft().height()),
+ quad[2]);
+ }
+
+ if (mitre1 == NoMitre)
+ quad[1].setY(quad[0].y());
+ if (mitre2 == NoMitre)
+ quad[2].setY(quad[3].y());
+
+ break;
+
+ case BSBottom:
+ quad[0] = FloatPoint(outerRect.minXMaxYCorner());
+ quad[1] = FloatPoint(innerRect.minXMaxYCorner());
+ quad[2] = FloatPoint(innerRect.maxXMaxYCorner());
+ quad[3] = FloatPoint(outerRect.maxXMaxYCorner());
+
+ if (!m_inner.radii().bottomLeft().isZero()) {
+ findIntersection(quad[0], quad[1],
+ FloatPoint(
+ quad[1].x() + m_inner.radii().bottomLeft().width(),
+ quad[1].y()),
+ FloatPoint(
+ quad[1].x(),
+ quad[1].y() - m_inner.radii().bottomLeft().height()),
+ quad[1]);
+ }
+
+ if (!m_inner.radii().bottomRight().isZero()) {
+ findIntersection(quad[3], quad[2],
+ FloatPoint(
+ quad[2].x() - m_inner.radii().bottomRight().width(),
+ quad[2].y()),
+ FloatPoint(
+ quad[2].x(),
+ quad[2].y() - m_inner.radii().bottomRight().height()),
+ quad[2]);
+ }
+
+ if (mitre1 == NoMitre)
+ quad[1].setX(quad[0].x());
+ if (mitre2 == NoMitre)
+ quad[2].setX(quad[3].x());
+
+ break;
+
+ case BSRight:
+ quad[0] = FloatPoint(outerRect.maxXMinYCorner());
+ quad[1] = FloatPoint(innerRect.maxXMinYCorner());
+ quad[2] = FloatPoint(innerRect.maxXMaxYCorner());
+ quad[3] = FloatPoint(outerRect.maxXMaxYCorner());
+
+ if (!m_inner.radii().topRight().isZero()) {
+ findIntersection(quad[0], quad[1],
+ FloatPoint(
+ quad[1].x() - m_inner.radii().topRight().width(),
+ quad[1].y()),
+ FloatPoint(
+ quad[1].x(),
+ quad[1].y() + m_inner.radii().topRight().height()),
+ quad[1]);
+ }
+
+ if (!m_inner.radii().bottomRight().isZero()) {
+ findIntersection(quad[3], quad[2],
+ FloatPoint(
+ quad[2].x() - m_inner.radii().bottomRight().width(),
+ quad[2].y()),
+ FloatPoint(
+ quad[2].x(),
+ quad[2].y() - m_inner.radii().bottomRight().height()),
+ quad[2]);
+ }
+
+ if (mitre1 == NoMitre)
+ quad[1].setY(quad[0].y());
+ if (mitre2 == NoMitre)
+ quad[2].setY(quad[3].y());
+
+ break;
+ }
+
+ bool antiAliasEdge1 = mitre1 != NonAntiAliasedMitre;
+ bool antiAliasEdge2 = mitre2 != NonAntiAliasedMitre;
+ if (antiAliasEdge1 == antiAliasEdge2) {
+ context->clipPolygon(4, quad, antiAliasEdge1);
+ 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;
+
+ r1 = (-ax * by + ay * bx) / (cx * by - cy * bx) + kExtendFill;
+ r2 = (-cx * by + cy * bx) / (ax * by - ay * bx) + kExtendFill;
+ }
+
+ if (mitre1 != NoMitre) {
+ 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];
+ context->clipPolygon(4, firstQuad, antiAliasEdge1);
+ }
+
+ if (mitre2 != NoMitre) {
+ 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];
+ context->clipPolygon(4, secondQuad, antiAliasEdge2);
+ }
+}
+
+} // namespace blink
« no previous file with comments | « Source/core/paint/BoxBorderPainter.h ('k') | Source/core/paint/BoxPainter.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698