| Index: Source/core/paint/ObjectPainter.cpp
|
| diff --git a/Source/core/paint/ObjectPainter.cpp b/Source/core/paint/ObjectPainter.cpp
|
| index 3e96ff3dd80573bd7b68ca47a84bda586d44ee73..96cb82c35d673eaf88a35ff9ceb1d164d6308b7c 100644
|
| --- a/Source/core/paint/ObjectPainter.cpp
|
| +++ b/Source/core/paint/ObjectPainter.cpp
|
| @@ -5,6 +5,7 @@
|
| #include "config.h"
|
| #include "core/paint/ObjectPainter.h"
|
|
|
| +#include "core/layout/LayoutInline.h"
|
| #include "core/layout/LayoutObject.h"
|
| #include "core/layout/LayoutTheme.h"
|
| #include "core/paint/BoxBorderPainter.h"
|
| @@ -16,52 +17,236 @@
|
|
|
| namespace blink {
|
|
|
| -void ObjectPainter::paintFocusRing(const PaintInfo& paintInfo, const ComputedStyle& style, const Vector<LayoutRect>& focusRingRects)
|
| +namespace {
|
| +
|
| +void paintSingleRectangleOutline(const PaintInfo& paintInfo, const IntRect& rect, const ComputedStyle& style, const Color& color)
|
| +{
|
| + ASSERT(!style.outlineStyleIsAuto());
|
| +
|
| + LayoutRect inner(rect);
|
| + inner.inflate(style.outlineOffset());
|
| + LayoutRect outer(inner);
|
| + outer.inflate(style.outlineWidth());
|
| + const BorderEdge commonEdgeInfo(style.outlineWidth(), color, style.outlineStyle());
|
| + BoxBorderPainter(style, outer, inner, commonEdgeInfo).paintBorder(paintInfo, outer);
|
| +}
|
| +
|
| +struct OutlineEdgeInfo {
|
| + int x1;
|
| + int y1;
|
| + int x2;
|
| + int y2;
|
| + BoxSide side;
|
| +};
|
| +
|
| +// Adjust length of edges if needed. Returns the width of the joint.
|
| +int adjustJoint(int outlineWidth, OutlineEdgeInfo& edge1, OutlineEdgeInfo& edge2)
|
| {
|
| - ASSERT(style.outlineStyleIsAuto());
|
| - Vector<IntRect> focusRingIntRects;
|
| - for (size_t i = 0; i < focusRingRects.size(); ++i)
|
| - focusRingIntRects.append(pixelSnappedIntRect(focusRingRects[i]));
|
| - paintInfo.context->drawFocusRing(focusRingIntRects, style.outlineWidth(), style.outlineOffset(), m_layoutObject.resolveColor(style, CSSPropertyOutlineColor));
|
| + // A clockwise joint:
|
| + // - needs no adjustment of edge length because our edges are along the clockwise outer edge of the outline;
|
| + // - needs a positive adjacent joint width (required by drawLineForBoxSide).
|
| + // A counterclockwise joint:
|
| + // - needs to increase the edge length to include the joint;
|
| + // - needs a negative adjacent joint width (required by drawLineForBoxSide).
|
| + switch (edge1.side) {
|
| + case BSTop:
|
| + switch (edge2.side) {
|
| + case BSRight: // Clockwise
|
| + return outlineWidth;
|
| + case BSLeft: // Counterclockwise
|
| + edge1.x2 += outlineWidth;
|
| + edge2.y2 += outlineWidth;
|
| + return -outlineWidth;
|
| + default: // Same side or no joint.
|
| + return 0;
|
| + }
|
| + case BSRight:
|
| + switch (edge2.side) {
|
| + case BSBottom: // Clockwise
|
| + return outlineWidth;
|
| + case BSTop: // Counterclockwise
|
| + edge1.y2 += outlineWidth;
|
| + edge2.x1 -= outlineWidth;
|
| + return -outlineWidth;
|
| + default: // Same side or no joint.
|
| + return 0;
|
| + }
|
| + case BSBottom:
|
| + switch (edge2.side) {
|
| + case BSLeft: // Clockwise
|
| + return outlineWidth;
|
| + case BSRight: // Counterclockwise
|
| + edge1.x1 -= outlineWidth;
|
| + edge2.y1 -= outlineWidth;
|
| + return -outlineWidth;
|
| + default: // Same side or no joint.
|
| + return 0;
|
| + }
|
| + case BSLeft:
|
| + switch (edge2.side) {
|
| + case BSTop: // Clockwise
|
| + return outlineWidth;
|
| + case BSBottom: // Counterclockwise
|
| + edge1.y1 -= outlineWidth;
|
| + edge2.x2 += outlineWidth;
|
| + return -outlineWidth;
|
| + default: // Same side or no joint.
|
| + return 0;
|
| + }
|
| + default:
|
| + ASSERT_NOT_REACHED();
|
| + return 0;
|
| + }
|
| }
|
|
|
| -void ObjectPainter::paintOutline(const PaintInfo& paintInfo, const LayoutRect& visualOverflowRect, const LayoutSize& objectSize, const LayoutPoint& paintOffset)
|
| +void paintComplexOutline(GraphicsContext& graphicsContext, const Vector<IntRect> rects, const ComputedStyle& style, const Color& color)
|
| {
|
| + ASSERT(!style.outlineStyleIsAuto());
|
| +
|
| + // Construct a clockwise path along the outer edge of the outline.
|
| + SkRegion region;
|
| + int width = style.outlineWidth();
|
| + int outset = style.outlineOffset() + style.outlineWidth();
|
| + for (auto& r : rects) {
|
| + IntRect rect = r;
|
| + rect.inflate(outset);
|
| + region.op(rect, SkRegion::kUnion_Op);
|
| + }
|
| + SkPath path;
|
| + if (!region.getBoundaryPath(&path))
|
| + return;
|
| +
|
| + Vector<OutlineEdgeInfo, 4> edges;
|
| +
|
| + SkPath::Iter iter(path, false);
|
| + SkPoint points[4];
|
| + size_t count = 0;
|
| + for (SkPath::Verb verb = iter.next(points, false); verb != SkPath::kDone_Verb; verb = iter.next(points, false)) {
|
| + if (verb != SkPath::kLine_Verb)
|
| + continue;
|
| +
|
| + edges.grow(++count);
|
| + OutlineEdgeInfo& edge = edges.last();
|
| + edge.x1 = SkScalarTruncToInt(points[0].x());
|
| + edge.y1 = SkScalarTruncToInt(points[0].y());
|
| + edge.x2 = SkScalarTruncToInt(points[1].x());
|
| + edge.y2 = SkScalarTruncToInt(points[1].y());
|
| + if (edge.x1 == edge.x2) {
|
| + if (edge.y1 < edge.y2) {
|
| + edge.x1 -= width;
|
| + edge.side = BSRight;
|
| + } else {
|
| + std::swap(edge.y1, edge.y2);
|
| + edge.x2 += width;
|
| + edge.side = BSLeft;
|
| + }
|
| + } else {
|
| + ASSERT(edge.y1 == edge.y2);
|
| + if (edge.x1 < edge.x2) {
|
| + edge.y2 += width;
|
| + edge.side = BSTop;
|
| + } else {
|
| + std::swap(edge.x1, edge.x2);
|
| + edge.y1 -= width;
|
| + edge.side = BSBottom;
|
| + }
|
| + }
|
| + }
|
| +
|
| + if (!count)
|
| + return;
|
| +
|
| + Color outlineColor = color;
|
| + bool useTransparencyLayer = color.hasAlpha();
|
| + if (useTransparencyLayer) {
|
| + graphicsContext.beginLayer(static_cast<float>(color.alpha()) / 255);
|
| + outlineColor = Color(outlineColor.red(), outlineColor.green(), outlineColor.blue());
|
| + }
|
| +
|
| + ASSERT(count >= 4 && edges.size() == count);
|
| + int firstAdjacentWidth = adjustJoint(width, edges.last(), edges.first());
|
| +
|
| + // The width of the angled part of starting and ending joint of the current edge.
|
| + int adjacentWidthStart = firstAdjacentWidth;
|
| + int adjacentWidthEnd;
|
| + for (size_t i = 0; i < count; ++i) {
|
| + OutlineEdgeInfo& edge = edges[i];
|
| + adjacentWidthEnd = i == count - 1 ? firstAdjacentWidth : adjustJoint(width, edge, edges[i + 1]);
|
| + int adjacentWidth1 = adjacentWidthStart;
|
| + int adjacentWidth2 = adjacentWidthEnd;
|
| + if (edge.side == BSLeft || edge.side == BSBottom)
|
| + std::swap(adjacentWidth1, adjacentWidth2);
|
| + ObjectPainter::drawLineForBoxSide(&graphicsContext, edge.x1, edge.y1, edge.x2, edge.y2, edge.side, outlineColor, style.outlineStyle(), adjacentWidth1, adjacentWidth2, false);
|
| + adjacentWidthStart = adjacentWidthEnd;
|
| + }
|
| +
|
| + if (useTransparencyLayer)
|
| + graphicsContext.endLayer();
|
| +}
|
| +
|
| +} // namespace
|
| +
|
| +void ObjectPainter::paintOutline(const PaintInfo& paintInfo, const LayoutPoint& paintOffset)
|
| +{
|
| + ASSERT(paintInfo.phase == PaintPhaseOutline || paintInfo.phase == PaintPhaseSelfOutline);
|
| +
|
| const ComputedStyle& styleToUse = m_layoutObject.styleRef();
|
| - if (!styleToUse.hasOutline())
|
| + if (!styleToUse.hasOutline() || styleToUse.visibility() != VISIBLE)
|
| + return;
|
| +
|
| + // Only paint the focus ring by hand if the theme isn't able to draw the focus ring.
|
| + bool paintFocusRing = styleToUse.outlineStyleIsAuto();
|
| + if (paintFocusRing && !LayoutTheme::theme().shouldDrawDefaultFocusRing(&m_layoutObject))
|
| return;
|
|
|
| if (LayoutObjectDrawingRecorder::useCachedDrawingIfPossible(*paintInfo.context, m_layoutObject, paintInfo.phase))
|
| return;
|
|
|
| - LayoutRect visualOverflowBounds(visualOverflowRect);
|
| - visualOverflowBounds.moveBy(paintOffset);
|
| - LayoutObjectDrawingRecorder recorder(*paintInfo.context, m_layoutObject, paintInfo.phase, visualOverflowBounds);
|
| + Vector<LayoutRect> outlineRects;
|
| + m_layoutObject.addOutlineRects(outlineRects, paintOffset);
|
| +
|
| + Vector<IntRect> pixelSnappedOutlineRects;
|
| + for (auto& r : outlineRects)
|
| + pixelSnappedOutlineRects.append(pixelSnappedIntRect(r));
|
| +
|
| + IntRect unitedOutlineRect = unionRect(pixelSnappedOutlineRects);
|
| + if (unitedOutlineRect.isEmpty()) {
|
| + if (paintFocusRing)
|
| + return;
|
| + // We need to draw an outline around an empty point.
|
| + IntRect emptyPointRect = pixelSnappedIntRect(LayoutRect(paintOffset, LayoutSize()));
|
| + pixelSnappedOutlineRects.append(emptyPointRect);
|
| + unitedOutlineRect = emptyPointRect;
|
| + }
|
| +
|
| + IntRect bounds = unitedOutlineRect;
|
| + bounds.inflate(m_layoutObject.styleRef().outlineOutsetExtent());
|
| + LayoutObjectDrawingRecorder recorder(*paintInfo.context, m_layoutObject, paintInfo.phase, bounds);
|
|
|
| + Color color = m_layoutObject.resolveColor(styleToUse, CSSPropertyOutlineColor);
|
| if (styleToUse.outlineStyleIsAuto()) {
|
| - if (LayoutTheme::theme().shouldDrawDefaultFocusRing(&m_layoutObject)) {
|
| - // Only paint the focus ring by hand if the theme isn't able to draw the focus ring.
|
| - Vector<LayoutRect> focusRingRects;
|
| - m_layoutObject.addOutlineRects(focusRingRects, paintOffset);
|
| - paintFocusRing(paintInfo, styleToUse, focusRingRects);
|
| - }
|
| + paintInfo.context->drawFocusRing(pixelSnappedOutlineRects, styleToUse.outlineWidth(), styleToUse.outlineOffset(), color);
|
| return;
|
| }
|
|
|
| - if (styleToUse.outlineStyle() == BNONE)
|
| + if (unitedOutlineRect == pixelSnappedOutlineRects[0]) {
|
| + paintSingleRectangleOutline(paintInfo, unitedOutlineRect, styleToUse, color);
|
| return;
|
| + }
|
| + paintComplexOutline(*paintInfo.context, pixelSnappedOutlineRects, styleToUse, color);
|
| +}
|
|
|
| - LayoutRect inner(paintOffset, objectSize);
|
| - inner = LayoutRect(pixelSnappedIntRect(inner));
|
| - inner.inflate(styleToUse.outlineOffset());
|
| +void ObjectPainter::paintInlineChildrenOutlines(const PaintInfo& paintInfo, const LayoutPoint& paintOffset)
|
| +{
|
| + ASSERT(paintInfo.phase == PaintPhaseOutline || paintInfo.phase == PaintPhaseChildOutlines);
|
|
|
| - LayoutRect outer(inner);
|
| - int outlineWidth = styleToUse.outlineWidth();
|
| - outer.inflate(outlineWidth);
|
| + PaintInfo childPaintInfo(paintInfo);
|
| + childPaintInfo.phase = paintInfo.phase == PaintPhaseChildOutlines ? PaintPhaseOutline : paintInfo.phase;
|
|
|
| - const BorderEdge commonEdgeInfo(outlineWidth,
|
| - m_layoutObject.resolveColor(styleToUse, CSSPropertyOutlineColor), styleToUse.outlineStyle());
|
| - BoxBorderPainter(styleToUse, outer, inner, commonEdgeInfo).paintBorder(paintInfo, outer);
|
| + for (LayoutObject* child = m_layoutObject.slowFirstChild(); child; child = child->nextSibling()) {
|
| + if (child->isLayoutInline() && !toLayoutInline(child)->hasSelfPaintingLayer())
|
| + child->paint(childPaintInfo, paintOffset);
|
| + }
|
| }
|
|
|
| void ObjectPainter::addPDFURLRectIfNeeded(const PaintInfo& paintInfo, const LayoutPoint& paintOffset)
|
|
|