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
3 // found in the LICENSE file.
6 #include "core/paint/ObjectPainter.h"
8 #include "core/rendering/PaintInfo.h"
9 #include "core/rendering/RenderObject.h"
10 #include "core/rendering/RenderTheme.h"
11 #include "core/rendering/style/RenderStyle.h"
12 #include "platform/geometry/LayoutPoint.h"
13 #include "platform/graphics/GraphicsContextStateSaver.h"
17 void ObjectPainter::paintFocusRing(PaintInfo& paintInfo, const LayoutPoint& paintOffset, RenderStyle* style)
19 Vector<LayoutRect> focusRingRects;
20 m_renderObject.addFocusRingRects(focusRingRects, paintOffset, paintInfo.paintContainer());
21 ASSERT(style->outlineStyleIsAuto());
22 Vector<IntRect> focusRingIntRects;
23 for (size_t i = 0; i < focusRingRects.size(); ++i)
24 focusRingIntRects.append(pixelSnappedIntRect(focusRingRects[i]));
25 paintInfo.context->drawFocusRing(focusRingIntRects, style->outlineWidth(), style->outlineOffset(), m_renderObject.resolveColor(style, CSSPropertyOutlineColor));
28 void ObjectPainter::paintOutline(PaintInfo& paintInfo, const LayoutRect& paintRect)
30 RenderStyle* styleToUse = m_renderObject.style();
31 if (!styleToUse->hasOutline())
34 LayoutUnit outlineWidth = styleToUse->outlineWidth();
36 int outlineOffset = styleToUse->outlineOffset();
38 if (styleToUse->outlineStyleIsAuto()) {
39 if (RenderTheme::theme().shouldDrawDefaultFocusRing(&m_renderObject)) {
40 // Only paint the focus ring by hand if the theme isn't able to draw the focus ring.
41 paintFocusRing(paintInfo, paintRect.location(), styleToUse);
46 if (styleToUse->outlineStyle() == BNONE)
49 IntRect inner = pixelSnappedIntRect(paintRect);
50 inner.inflate(outlineOffset);
52 IntRect outer = pixelSnappedIntRect(inner);
53 outer.inflate(outlineWidth);
55 // FIXME: This prevents outlines from painting inside the object. See bug 12042
59 EBorderStyle outlineStyle = styleToUse->outlineStyle();
60 Color outlineColor = m_renderObject.resolveColor(styleToUse, CSSPropertyOutlineColor);
62 GraphicsContext* graphicsContext = paintInfo.context;
63 bool useTransparencyLayer = outlineColor.hasAlpha();
64 if (useTransparencyLayer) {
65 if (outlineStyle == SOLID) {
69 graphicsContext->setFillRule(RULE_EVENODD);
70 graphicsContext->setFillColor(outlineColor);
71 graphicsContext->fillPath(path);
74 graphicsContext->beginTransparencyLayer(static_cast<float>(outlineColor.alpha()) / 255);
75 outlineColor = Color(outlineColor.red(), outlineColor.green(), outlineColor.blue());
78 int leftOuter = outer.x();
79 int leftInner = inner.x();
80 int rightOuter = outer.maxX();
81 int rightInner = inner.maxX();
82 int topOuter = outer.y();
83 int topInner = inner.y();
84 int bottomOuter = outer.maxY();
85 int bottomInner = inner.maxY();
87 drawLineForBoxSide(graphicsContext, leftOuter, topOuter, leftInner, bottomOuter, BSLeft, outlineColor, outlineStyle, outlineWidth, outlineWidth);
88 drawLineForBoxSide(graphicsContext, leftOuter, topOuter, rightOuter, topInner, BSTop, outlineColor, outlineStyle, outlineWidth, outlineWidth);
89 drawLineForBoxSide(graphicsContext, rightInner, topOuter, rightOuter, bottomOuter, BSRight, outlineColor, outlineStyle, outlineWidth, outlineWidth);
90 drawLineForBoxSide(graphicsContext, leftOuter, bottomInner, rightOuter, bottomOuter, BSBottom, outlineColor, outlineStyle, outlineWidth, outlineWidth);
92 if (useTransparencyLayer)
93 graphicsContext->endLayer();
96 void ObjectPainter::drawLineForBoxSide(GraphicsContext* graphicsContext, int x1, int y1, int x2, int y2,
97 BoxSide side, Color color, EBorderStyle style,
98 int adjacentWidth1, int adjacentWidth2, bool antialias)
102 if (side == BSTop || side == BSBottom) {
110 // FIXME: We really would like this check to be an ASSERT as we don't want to draw empty borders. However
111 // nothing guarantees that the following recursive calls to drawLineForBoxSide will have non-null dimensions.
112 if (!thickness || !length)
115 if (style == DOUBLE && thickness < 3)
124 drawDashedOrDottedBoxSide(graphicsContext, x1, y1, x2, y2, side,
125 color, thickness, style, antialias);
128 drawDoubleBoxSide(graphicsContext, x1, y1, x2, y2, length, side, color,
129 thickness, adjacentWidth1, adjacentWidth2, antialias);
133 drawRidgeOrGrooveBoxSide(graphicsContext, x1, y1, x2, y2, side, color,
134 style, adjacentWidth1, adjacentWidth2, antialias);
137 // FIXME: Maybe we should lighten the colors on one side like Firefox.
138 // https://bugs.webkit.org/show_bug.cgi?id=58608
139 if (side == BSTop || side == BSLeft)
140 color = color.dark();
143 if (style == OUTSET && (side == BSBottom || side == BSRight))
144 color = color.dark();
147 drawSolidBoxSide(graphicsContext, x1, y1, x2, y2, side, color, adjacentWidth1, adjacentWidth2, antialias);
152 void ObjectPainter::drawDashedOrDottedBoxSide(GraphicsContext* graphicsContext, int x1, int y1, int x2, int y2,
153 BoxSide side, Color color, int thickness, EBorderStyle style, bool antialias)
158 bool wasAntialiased = graphicsContext->shouldAntialias();
159 StrokeStyle oldStrokeStyle = graphicsContext->strokeStyle();
160 graphicsContext->setShouldAntialias(antialias);
161 graphicsContext->setStrokeColor(color);
162 graphicsContext->setStrokeThickness(thickness);
163 graphicsContext->setStrokeStyle(style == DASHED ? DashedStroke : DottedStroke);
168 graphicsContext->drawLine(IntPoint(x1, (y1 + y2) / 2), IntPoint(x2, (y1 + y2) / 2));
172 graphicsContext->drawLine(IntPoint((x1 + x2) / 2, y1), IntPoint((x1 + x2) / 2, y2));
175 graphicsContext->setShouldAntialias(wasAntialiased);
176 graphicsContext->setStrokeStyle(oldStrokeStyle);
179 void ObjectPainter::drawDoubleBoxSide(GraphicsContext* graphicsContext, int x1, int y1, int x2, int y2,
180 int length, BoxSide side, Color color, int thickness, int adjacentWidth1, int adjacentWidth2, bool antialias)
182 int thirdOfThickness = (thickness + 1) / 3;
183 ASSERT(thirdOfThickness);
185 if (!adjacentWidth1 && !adjacentWidth2) {
186 StrokeStyle oldStrokeStyle = graphicsContext->strokeStyle();
187 graphicsContext->setStrokeStyle(NoStroke);
188 graphicsContext->setFillColor(color);
190 bool wasAntialiased = graphicsContext->shouldAntialias();
191 graphicsContext->setShouldAntialias(antialias);
196 graphicsContext->drawRect(IntRect(x1, y1, length, thirdOfThickness));
197 graphicsContext->drawRect(IntRect(x1, y2 - thirdOfThickness, length, thirdOfThickness));
201 // FIXME: Why do we offset the border by 1 in this case but not the other one?
203 graphicsContext->drawRect(IntRect(x1, y1 + 1, thirdOfThickness, length - 1));
204 graphicsContext->drawRect(IntRect(x2 - thirdOfThickness, y1 + 1, thirdOfThickness, length - 1));
209 graphicsContext->setShouldAntialias(wasAntialiased);
210 graphicsContext->setStrokeStyle(oldStrokeStyle);
214 int adjacent1BigThird = ((adjacentWidth1 > 0) ? adjacentWidth1 + 1 : adjacentWidth1 - 1) / 3;
215 int adjacent2BigThird = ((adjacentWidth2 > 0) ? adjacentWidth2 + 1 : adjacentWidth2 - 1) / 3;
219 drawLineForBoxSide(graphicsContext, x1 + std::max((-adjacentWidth1 * 2 + 1) / 3, 0),
220 y1, x2 - std::max((-adjacentWidth2 * 2 + 1) / 3, 0), y1 + thirdOfThickness,
221 side, color, SOLID, adjacent1BigThird, adjacent2BigThird, antialias);
222 drawLineForBoxSide(graphicsContext, x1 + std::max((adjacentWidth1 * 2 + 1) / 3, 0),
223 y2 - thirdOfThickness, x2 - std::max((adjacentWidth2 * 2 + 1) / 3, 0), y2,
224 side, color, SOLID, adjacent1BigThird, adjacent2BigThird, antialias);
227 drawLineForBoxSide(graphicsContext, x1, y1 + std::max((-adjacentWidth1 * 2 + 1) / 3, 0),
228 x1 + thirdOfThickness, y2 - std::max((-adjacentWidth2 * 2 + 1) / 3, 0),
229 side, color, SOLID, adjacent1BigThird, adjacent2BigThird, antialias);
230 drawLineForBoxSide(graphicsContext, x2 - thirdOfThickness, y1 + std::max((adjacentWidth1 * 2 + 1) / 3, 0),
231 x2, y2 - std::max((adjacentWidth2 * 2 + 1) / 3, 0),
232 side, color, SOLID, adjacent1BigThird, adjacent2BigThird, antialias);
235 drawLineForBoxSide(graphicsContext, x1 + std::max((adjacentWidth1 * 2 + 1) / 3, 0),
236 y1, x2 - std::max((adjacentWidth2 * 2 + 1) / 3, 0), y1 + thirdOfThickness,
237 side, color, SOLID, adjacent1BigThird, adjacent2BigThird, antialias);
238 drawLineForBoxSide(graphicsContext, x1 + std::max((-adjacentWidth1 * 2 + 1) / 3, 0),
239 y2 - thirdOfThickness, x2 - std::max((-adjacentWidth2 * 2 + 1) / 3, 0), y2,
240 side, color, SOLID, adjacent1BigThird, adjacent2BigThird, antialias);
243 drawLineForBoxSide(graphicsContext, x1, y1 + std::max((adjacentWidth1 * 2 + 1) / 3, 0),
244 x1 + thirdOfThickness, y2 - std::max((adjacentWidth2 * 2 + 1) / 3, 0),
245 side, color, SOLID, adjacent1BigThird, adjacent2BigThird, antialias);
246 drawLineForBoxSide(graphicsContext, x2 - thirdOfThickness, y1 + std::max((-adjacentWidth1 * 2 + 1) / 3, 0),
247 x2, y2 - std::max((-adjacentWidth2 * 2 + 1) / 3, 0),
248 side, color, SOLID, adjacent1BigThird, adjacent2BigThird, antialias);
255 void ObjectPainter::drawRidgeOrGrooveBoxSide(GraphicsContext* graphicsContext, int x1, int y1, int x2, int y2,
256 BoxSide side, Color color, EBorderStyle style, int adjacentWidth1, int adjacentWidth2, bool antialias)
260 if (style == GROOVE) {
268 int adjacent1BigHalf = ((adjacentWidth1 > 0) ? adjacentWidth1 + 1 : adjacentWidth1 - 1) / 2;
269 int adjacent2BigHalf = ((adjacentWidth2 > 0) ? adjacentWidth2 + 1 : adjacentWidth2 - 1) / 2;
273 drawLineForBoxSide(graphicsContext, x1 + std::max(-adjacentWidth1, 0) / 2, y1, x2 - std::max(-adjacentWidth2, 0) / 2, (y1 + y2 + 1) / 2,
274 side, color, s1, adjacent1BigHalf, adjacent2BigHalf, antialias);
275 drawLineForBoxSide(graphicsContext, x1 + std::max(adjacentWidth1 + 1, 0) / 2, (y1 + y2 + 1) / 2, x2 - std::max(adjacentWidth2 + 1, 0) / 2, y2,
276 side, color, s2, adjacentWidth1 / 2, adjacentWidth2 / 2, antialias);
279 drawLineForBoxSide(graphicsContext, x1, y1 + std::max(-adjacentWidth1, 0) / 2, (x1 + x2 + 1) / 2, y2 - std::max(-adjacentWidth2, 0) / 2,
280 side, color, s1, adjacent1BigHalf, adjacent2BigHalf, antialias);
281 drawLineForBoxSide(graphicsContext, (x1 + x2 + 1) / 2, y1 + std::max(adjacentWidth1 + 1, 0) / 2, x2, y2 - std::max(adjacentWidth2 + 1, 0) / 2,
282 side, color, s2, adjacentWidth1 / 2, adjacentWidth2 / 2, antialias);
285 drawLineForBoxSide(graphicsContext, x1 + std::max(adjacentWidth1, 0) / 2, y1, x2 - std::max(adjacentWidth2, 0) / 2, (y1 + y2 + 1) / 2,
286 side, color, s2, adjacent1BigHalf, adjacent2BigHalf, antialias);
287 drawLineForBoxSide(graphicsContext, x1 + std::max(-adjacentWidth1 + 1, 0) / 2, (y1 + y2 + 1) / 2, x2 - std::max(-adjacentWidth2 + 1, 0) / 2, y2,
288 side, color, s1, adjacentWidth1 / 2, adjacentWidth2 / 2, antialias);
291 drawLineForBoxSide(graphicsContext, x1, y1 + std::max(adjacentWidth1, 0) / 2, (x1 + x2 + 1) / 2, y2 - std::max(adjacentWidth2, 0) / 2,
292 side, color, s2, adjacent1BigHalf, adjacent2BigHalf, antialias);
293 drawLineForBoxSide(graphicsContext, (x1 + x2 + 1) / 2, y1 + std::max(-adjacentWidth1 + 1, 0) / 2, x2, y2 - std::max(-adjacentWidth2 + 1, 0) / 2,
294 side, color, s1, adjacentWidth1 / 2, adjacentWidth2 / 2, antialias);
299 void ObjectPainter::drawSolidBoxSide(GraphicsContext* graphicsContext, int x1, int y1, int x2, int y2,
300 BoxSide side, Color color, int adjacentWidth1, int adjacentWidth2, bool antialias)
302 StrokeStyle oldStrokeStyle = graphicsContext->strokeStyle();
303 graphicsContext->setStrokeStyle(NoStroke);
304 graphicsContext->setFillColor(color);
307 if (!adjacentWidth1 && !adjacentWidth2) {
308 // Turn off antialiasing to match the behavior of drawConvexPolygon();
309 // this matters for rects in transformed contexts.
310 bool wasAntialiased = graphicsContext->shouldAntialias();
311 graphicsContext->setShouldAntialias(antialias);
312 graphicsContext->drawRect(IntRect(x1, y1, x2 - x1, y2 - y1));
313 graphicsContext->setShouldAntialias(wasAntialiased);
314 graphicsContext->setStrokeStyle(oldStrokeStyle);
320 quad[0] = FloatPoint(x1 + std::max(-adjacentWidth1, 0), y1);
321 quad[1] = FloatPoint(x1 + std::max(adjacentWidth1, 0), y2);
322 quad[2] = FloatPoint(x2 - std::max(adjacentWidth2, 0), y2);
323 quad[3] = FloatPoint(x2 - std::max(-adjacentWidth2, 0), y1);
326 quad[0] = FloatPoint(x1 + std::max(adjacentWidth1, 0), y1);
327 quad[1] = FloatPoint(x1 + std::max(-adjacentWidth1, 0), y2);
328 quad[2] = FloatPoint(x2 - std::max(-adjacentWidth2, 0), y2);
329 quad[3] = FloatPoint(x2 - std::max(adjacentWidth2, 0), y1);
332 quad[0] = FloatPoint(x1, y1 + std::max(-adjacentWidth1, 0));
333 quad[1] = FloatPoint(x1, y2 - std::max(-adjacentWidth2, 0));
334 quad[2] = FloatPoint(x2, y2 - std::max(adjacentWidth2, 0));
335 quad[3] = FloatPoint(x2, y1 + std::max(adjacentWidth1, 0));
338 quad[0] = FloatPoint(x1, y1 + std::max(adjacentWidth1, 0));
339 quad[1] = FloatPoint(x1, y2 - std::max(adjacentWidth2, 0));
340 quad[2] = FloatPoint(x2, y2 - std::max(-adjacentWidth2, 0));
341 quad[3] = FloatPoint(x2, y1 + std::max(-adjacentWidth1, 0));
345 graphicsContext->drawConvexPolygon(4, quad, antialias);
346 graphicsContext->setStrokeStyle(oldStrokeStyle);