OLD | NEW |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "config.h" | 5 #include "config.h" |
6 #include "core/paint/ObjectPainter.h" | 6 #include "core/paint/ObjectPainter.h" |
7 | 7 |
| 8 #include "core/layout/LayoutInline.h" |
8 #include "core/layout/LayoutObject.h" | 9 #include "core/layout/LayoutObject.h" |
9 #include "core/layout/LayoutTheme.h" | 10 #include "core/layout/LayoutTheme.h" |
10 #include "core/paint/BoxBorderPainter.h" | 11 #include "core/paint/BoxBorderPainter.h" |
11 #include "core/paint/LayoutObjectDrawingRecorder.h" | 12 #include "core/paint/LayoutObjectDrawingRecorder.h" |
12 #include "core/paint/PaintInfo.h" | 13 #include "core/paint/PaintInfo.h" |
13 #include "core/style/BorderEdge.h" | 14 #include "core/style/BorderEdge.h" |
14 #include "core/style/ComputedStyle.h" | 15 #include "core/style/ComputedStyle.h" |
15 #include "platform/geometry/LayoutPoint.h" | 16 #include "platform/geometry/LayoutPoint.h" |
16 | 17 |
17 namespace blink { | 18 namespace blink { |
18 | 19 |
19 void ObjectPainter::paintFocusRing(const PaintInfo& paintInfo, const ComputedSty
le& style, const Vector<LayoutRect>& focusRingRects) | 20 namespace { |
20 { | 21 |
21 ASSERT(style.outlineStyleIsAuto()); | 22 void paintSingleRectangleOutline(const PaintInfo& paintInfo, const IntRect& rect
, const ComputedStyle& style, const Color& color) |
22 Vector<IntRect> focusRingIntRects; | 23 { |
23 for (size_t i = 0; i < focusRingRects.size(); ++i) | 24 ASSERT(!style.outlineStyleIsAuto()); |
24 focusRingIntRects.append(pixelSnappedIntRect(focusRingRects[i])); | 25 |
25 paintInfo.context->drawFocusRing(focusRingIntRects, style.outlineWidth(), st
yle.outlineOffset(), m_layoutObject.resolveColor(style, CSSPropertyOutlineColor)
); | 26 LayoutRect inner(rect); |
26 } | 27 inner.inflate(style.outlineOffset()); |
27 | 28 LayoutRect outer(inner); |
28 void ObjectPainter::paintOutline(const PaintInfo& paintInfo, const LayoutRect& v
isualOverflowRect, const LayoutSize& objectSize, const LayoutPoint& paintOffset) | 29 outer.inflate(style.outlineWidth()); |
29 { | 30 const BorderEdge commonEdgeInfo(style.outlineWidth(), color, style.outlineSt
yle()); |
| 31 BoxBorderPainter(style, outer, inner, commonEdgeInfo).paintBorder(paintInfo,
outer); |
| 32 } |
| 33 |
| 34 struct OutlineEdgeInfo { |
| 35 int x1; |
| 36 int y1; |
| 37 int x2; |
| 38 int y2; |
| 39 BoxSide side; |
| 40 }; |
| 41 |
| 42 // Adjust length of edges if needed. Returns the width of the joint. |
| 43 int adjustJoint(int outlineWidth, OutlineEdgeInfo& edge1, OutlineEdgeInfo& edge2
) |
| 44 { |
| 45 // A clockwise joint: |
| 46 // - needs no adjustment of edge length because our edges are along the cloc
kwise outer edge of the outline; |
| 47 // - needs a positive adjacent joint width (required by drawLineForBoxSide). |
| 48 // A counterclockwise joint: |
| 49 // - needs to increase the edge length to include the joint; |
| 50 // - needs a negative adjacent joint width (required by drawLineForBoxSide). |
| 51 switch (edge1.side) { |
| 52 case BSTop: |
| 53 switch (edge2.side) { |
| 54 case BSRight: // Clockwise |
| 55 return outlineWidth; |
| 56 case BSLeft: // Counterclockwise |
| 57 edge1.x2 += outlineWidth; |
| 58 edge2.y2 += outlineWidth; |
| 59 return -outlineWidth; |
| 60 default: // Same side or no joint. |
| 61 return 0; |
| 62 } |
| 63 case BSRight: |
| 64 switch (edge2.side) { |
| 65 case BSBottom: // Clockwise |
| 66 return outlineWidth; |
| 67 case BSTop: // Counterclockwise |
| 68 edge1.y2 += outlineWidth; |
| 69 edge2.x1 -= outlineWidth; |
| 70 return -outlineWidth; |
| 71 default: // Same side or no joint. |
| 72 return 0; |
| 73 } |
| 74 case BSBottom: |
| 75 switch (edge2.side) { |
| 76 case BSLeft: // Clockwise |
| 77 return outlineWidth; |
| 78 case BSRight: // Counterclockwise |
| 79 edge1.x1 -= outlineWidth; |
| 80 edge2.y1 -= outlineWidth; |
| 81 return -outlineWidth; |
| 82 default: // Same side or no joint. |
| 83 return 0; |
| 84 } |
| 85 case BSLeft: |
| 86 switch (edge2.side) { |
| 87 case BSTop: // Clockwise |
| 88 return outlineWidth; |
| 89 case BSBottom: // Counterclockwise |
| 90 edge1.y1 -= outlineWidth; |
| 91 edge2.x2 += outlineWidth; |
| 92 return -outlineWidth; |
| 93 default: // Same side or no joint. |
| 94 return 0; |
| 95 } |
| 96 default: |
| 97 ASSERT_NOT_REACHED(); |
| 98 return 0; |
| 99 } |
| 100 } |
| 101 |
| 102 void paintComplexOutline(GraphicsContext& graphicsContext, const Vector<IntRect>
rects, const ComputedStyle& style, const Color& color) |
| 103 { |
| 104 ASSERT(!style.outlineStyleIsAuto()); |
| 105 |
| 106 // Construct a clockwise path along the outer edge of the outline. |
| 107 SkRegion region; |
| 108 int width = style.outlineWidth(); |
| 109 int outset = style.outlineOffset() + style.outlineWidth(); |
| 110 for (auto& r : rects) { |
| 111 IntRect rect = r; |
| 112 rect.inflate(outset); |
| 113 region.op(rect, SkRegion::kUnion_Op); |
| 114 } |
| 115 SkPath path; |
| 116 if (!region.getBoundaryPath(&path)) |
| 117 return; |
| 118 |
| 119 Vector<OutlineEdgeInfo, 4> edges; |
| 120 |
| 121 SkPath::Iter iter(path, false); |
| 122 SkPoint points[4]; |
| 123 size_t count = 0; |
| 124 for (SkPath::Verb verb = iter.next(points, false); verb != SkPath::kDone_Ver
b; verb = iter.next(points, false)) { |
| 125 if (verb != SkPath::kLine_Verb) |
| 126 continue; |
| 127 |
| 128 edges.grow(++count); |
| 129 OutlineEdgeInfo& edge = edges.last(); |
| 130 edge.x1 = SkScalarTruncToInt(points[0].x()); |
| 131 edge.y1 = SkScalarTruncToInt(points[0].y()); |
| 132 edge.x2 = SkScalarTruncToInt(points[1].x()); |
| 133 edge.y2 = SkScalarTruncToInt(points[1].y()); |
| 134 if (edge.x1 == edge.x2) { |
| 135 if (edge.y1 < edge.y2) { |
| 136 edge.x1 -= width; |
| 137 edge.side = BSRight; |
| 138 } else { |
| 139 std::swap(edge.y1, edge.y2); |
| 140 edge.x2 += width; |
| 141 edge.side = BSLeft; |
| 142 } |
| 143 } else { |
| 144 ASSERT(edge.y1 == edge.y2); |
| 145 if (edge.x1 < edge.x2) { |
| 146 edge.y2 += width; |
| 147 edge.side = BSTop; |
| 148 } else { |
| 149 std::swap(edge.x1, edge.x2); |
| 150 edge.y1 -= width; |
| 151 edge.side = BSBottom; |
| 152 } |
| 153 } |
| 154 } |
| 155 |
| 156 if (!count) |
| 157 return; |
| 158 |
| 159 Color outlineColor = color; |
| 160 bool useTransparencyLayer = color.hasAlpha(); |
| 161 if (useTransparencyLayer) { |
| 162 graphicsContext.beginLayer(static_cast<float>(color.alpha()) / 255); |
| 163 outlineColor = Color(outlineColor.red(), outlineColor.green(), outlineCo
lor.blue()); |
| 164 } |
| 165 |
| 166 ASSERT(count >= 4 && edges.size() == count); |
| 167 int firstAdjacentWidth = adjustJoint(width, edges.last(), edges.first()); |
| 168 |
| 169 // The width of the angled part of starting and ending joint of the current
edge. |
| 170 int adjacentWidthStart = firstAdjacentWidth; |
| 171 int adjacentWidthEnd; |
| 172 for (size_t i = 0; i < count; ++i) { |
| 173 OutlineEdgeInfo& edge = edges[i]; |
| 174 adjacentWidthEnd = i == count - 1 ? firstAdjacentWidth : adjustJoint(wid
th, edge, edges[i + 1]); |
| 175 int adjacentWidth1 = adjacentWidthStart; |
| 176 int adjacentWidth2 = adjacentWidthEnd; |
| 177 if (edge.side == BSLeft || edge.side == BSBottom) |
| 178 std::swap(adjacentWidth1, adjacentWidth2); |
| 179 ObjectPainter::drawLineForBoxSide(&graphicsContext, edge.x1, edge.y1, ed
ge.x2, edge.y2, edge.side, outlineColor, style.outlineStyle(), adjacentWidth1, a
djacentWidth2, false); |
| 180 adjacentWidthStart = adjacentWidthEnd; |
| 181 } |
| 182 |
| 183 if (useTransparencyLayer) |
| 184 graphicsContext.endLayer(); |
| 185 } |
| 186 |
| 187 } // namespace |
| 188 |
| 189 void ObjectPainter::paintOutline(const PaintInfo& paintInfo, const LayoutPoint&
paintOffset) |
| 190 { |
| 191 ASSERT(paintInfo.phase == PaintPhaseOutline || paintInfo.phase == PaintPhase
SelfOutline); |
| 192 |
30 const ComputedStyle& styleToUse = m_layoutObject.styleRef(); | 193 const ComputedStyle& styleToUse = m_layoutObject.styleRef(); |
31 if (!styleToUse.hasOutline()) | 194 if (!styleToUse.hasOutline() || styleToUse.visibility() != VISIBLE) |
| 195 return; |
| 196 |
| 197 // Only paint the focus ring by hand if the theme isn't able to draw the foc
us ring. |
| 198 bool paintFocusRing = styleToUse.outlineStyleIsAuto(); |
| 199 if (paintFocusRing && !LayoutTheme::theme().shouldDrawDefaultFocusRing(&m_la
youtObject)) |
32 return; | 200 return; |
33 | 201 |
34 if (LayoutObjectDrawingRecorder::useCachedDrawingIfPossible(*paintInfo.conte
xt, m_layoutObject, paintInfo.phase)) | 202 if (LayoutObjectDrawingRecorder::useCachedDrawingIfPossible(*paintInfo.conte
xt, m_layoutObject, paintInfo.phase)) |
35 return; | 203 return; |
36 | 204 |
37 LayoutRect visualOverflowBounds(visualOverflowRect); | 205 Vector<LayoutRect> outlineRects; |
38 visualOverflowBounds.moveBy(paintOffset); | 206 m_layoutObject.addOutlineRects(outlineRects, paintOffset); |
39 LayoutObjectDrawingRecorder recorder(*paintInfo.context, m_layoutObject, pai
ntInfo.phase, visualOverflowBounds); | 207 |
40 | 208 Vector<IntRect> pixelSnappedOutlineRects; |
| 209 for (auto& r : outlineRects) |
| 210 pixelSnappedOutlineRects.append(pixelSnappedIntRect(r)); |
| 211 |
| 212 IntRect unitedOutlineRect = unionRect(pixelSnappedOutlineRects); |
| 213 if (unitedOutlineRect.isEmpty()) { |
| 214 if (paintFocusRing) |
| 215 return; |
| 216 // We need to draw an outline around an empty point. |
| 217 IntRect emptyPointRect = pixelSnappedIntRect(LayoutRect(paintOffset, Lay
outSize())); |
| 218 pixelSnappedOutlineRects.append(emptyPointRect); |
| 219 unitedOutlineRect = emptyPointRect; |
| 220 } |
| 221 |
| 222 IntRect bounds = unitedOutlineRect; |
| 223 bounds.inflate(m_layoutObject.styleRef().outlineOutsetExtent()); |
| 224 LayoutObjectDrawingRecorder recorder(*paintInfo.context, m_layoutObject, pai
ntInfo.phase, bounds); |
| 225 |
| 226 Color color = m_layoutObject.resolveColor(styleToUse, CSSPropertyOutlineColo
r); |
41 if (styleToUse.outlineStyleIsAuto()) { | 227 if (styleToUse.outlineStyleIsAuto()) { |
42 if (LayoutTheme::theme().shouldDrawDefaultFocusRing(&m_layoutObject)) { | 228 paintInfo.context->drawFocusRing(pixelSnappedOutlineRects, styleToUse.ou
tlineWidth(), styleToUse.outlineOffset(), color); |
43 // Only paint the focus ring by hand if the theme isn't able to draw
the focus ring. | 229 return; |
44 Vector<LayoutRect> focusRingRects; | 230 } |
45 m_layoutObject.addOutlineRects(focusRingRects, paintOffset); | 231 |
46 paintFocusRing(paintInfo, styleToUse, focusRingRects); | 232 if (unitedOutlineRect == pixelSnappedOutlineRects[0]) { |
47 } | 233 paintSingleRectangleOutline(paintInfo, unitedOutlineRect, styleToUse, co
lor); |
48 return; | 234 return; |
49 } | 235 } |
50 | 236 paintComplexOutline(*paintInfo.context, pixelSnappedOutlineRects, styleToUse
, color); |
51 if (styleToUse.outlineStyle() == BNONE) | 237 } |
52 return; | 238 |
53 | 239 void ObjectPainter::paintInlineChildrenOutlines(const PaintInfo& paintInfo, cons
t LayoutPoint& paintOffset) |
54 LayoutRect inner(paintOffset, objectSize); | 240 { |
55 inner = LayoutRect(pixelSnappedIntRect(inner)); | 241 ASSERT(paintInfo.phase == PaintPhaseOutline || paintInfo.phase == PaintPhase
ChildOutlines); |
56 inner.inflate(styleToUse.outlineOffset()); | 242 |
57 | 243 PaintInfo childPaintInfo(paintInfo); |
58 LayoutRect outer(inner); | 244 childPaintInfo.phase = paintInfo.phase == PaintPhaseChildOutlines ? PaintPha
seOutline : paintInfo.phase; |
59 int outlineWidth = styleToUse.outlineWidth(); | 245 |
60 outer.inflate(outlineWidth); | 246 for (LayoutObject* child = m_layoutObject.slowFirstChild(); child; child = c
hild->nextSibling()) { |
61 | 247 if (child->isLayoutInline() && !toLayoutInline(child)->hasSelfPaintingLa
yer()) |
62 const BorderEdge commonEdgeInfo(outlineWidth, | 248 child->paint(childPaintInfo, paintOffset); |
63 m_layoutObject.resolveColor(styleToUse, CSSPropertyOutlineColor), styleT
oUse.outlineStyle()); | 249 } |
64 BoxBorderPainter(styleToUse, outer, inner, commonEdgeInfo).paintBorder(paint
Info, outer); | |
65 } | 250 } |
66 | 251 |
67 void ObjectPainter::addPDFURLRectIfNeeded(const PaintInfo& paintInfo, const Layo
utPoint& paintOffset) | 252 void ObjectPainter::addPDFURLRectIfNeeded(const PaintInfo& paintInfo, const Layo
utPoint& paintOffset) |
68 { | 253 { |
69 ASSERT(paintInfo.isPrinting()); | 254 ASSERT(paintInfo.isPrinting()); |
70 if (m_layoutObject.isElementContinuation() || !m_layoutObject.node() || !m_l
ayoutObject.node()->isLink() || m_layoutObject.styleRef().visibility() != VISIBL
E) | 255 if (m_layoutObject.isElementContinuation() || !m_layoutObject.node() || !m_l
ayoutObject.node()->isLink() || m_layoutObject.styleRef().visibility() != VISIBL
E) |
71 return; | 256 return; |
72 | 257 |
73 KURL url = toElement(m_layoutObject.node())->hrefURL(); | 258 KURL url = toElement(m_layoutObject.node())->hrefURL(); |
74 if (!url.isValid()) | 259 if (!url.isValid()) |
(...skipping 261 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
336 quad[1] = FloatPoint(x1, y2 - std::max(adjacentWidth2, 0)); | 521 quad[1] = FloatPoint(x1, y2 - std::max(adjacentWidth2, 0)); |
337 quad[2] = FloatPoint(x2, y2 - std::max(-adjacentWidth2, 0)); | 522 quad[2] = FloatPoint(x2, y2 - std::max(-adjacentWidth2, 0)); |
338 quad[3] = FloatPoint(x2, y1 + std::max(-adjacentWidth1, 0)); | 523 quad[3] = FloatPoint(x2, y1 + std::max(-adjacentWidth1, 0)); |
339 break; | 524 break; |
340 } | 525 } |
341 | 526 |
342 graphicsContext->fillPolygon(4, quad, color, antialias); | 527 graphicsContext->fillPolygon(4, quad, color, antialias); |
343 } | 528 } |
344 | 529 |
345 } // namespace blink | 530 } // namespace blink |
OLD | NEW |