| 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
|
|
|