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.
4
5 #include "config.h"
6 #include "core/paint/ObjectPainter.h"
7
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"
14
15 namespace blink {
16
paintFocusRing(PaintInfo & paintInfo,const LayoutPoint & paintOffset,RenderStyle * style)17 void ObjectPainter::paintFocusRing(PaintInfo& paintInfo, const LayoutPoint& paintOffset, RenderStyle* style)
18 {
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));
26 }
27
paintOutline(PaintInfo & paintInfo,const LayoutRect & paintRect)28 void ObjectPainter::paintOutline(PaintInfo& paintInfo, const LayoutRect& paintRect)
29 {
30 RenderStyle* styleToUse = m_renderObject.style();
31 if (!styleToUse->hasOutline())
32 return;
33
34 LayoutUnit outlineWidth = styleToUse->outlineWidth();
35
36 int outlineOffset = styleToUse->outlineOffset();
37
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);
42 }
43 return;
44 }
45
46 if (styleToUse->outlineStyle() == BNONE)
47 return;
48
49 IntRect inner = pixelSnappedIntRect(paintRect);
50 inner.inflate(outlineOffset);
51
52 IntRect outer = pixelSnappedIntRect(inner);
53 outer.inflate(outlineWidth);
54
55 // FIXME: This prevents outlines from painting inside the object. See bug 12042
56 if (outer.isEmpty())
57 return;
58
59 EBorderStyle outlineStyle = styleToUse->outlineStyle();
60 Color outlineColor = m_renderObject.resolveColor(styleToUse, CSSPropertyOutlineColor);
61
62 GraphicsContext* graphicsContext = paintInfo.context;
63 bool useTransparencyLayer = outlineColor.hasAlpha();
64 if (useTransparencyLayer) {
65 if (outlineStyle == SOLID) {
66 Path path;
67 path.addRect(outer);
68 path.addRect(inner);
69 graphicsContext->setFillRule(RULE_EVENODD);
70 graphicsContext->setFillColor(outlineColor);
71 graphicsContext->fillPath(path);
72 return;
73 }
74 graphicsContext->beginTransparencyLayer(static_cast<float>(outlineColor.alpha()) / 255);
75 outlineColor = Color(outlineColor.red(), outlineColor.green(), outlineColor.blue());
76 }
77
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();
86
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);
91
92 if (useTransparencyLayer)
93 graphicsContext->endLayer();
94 }
95
drawLineForBoxSide(GraphicsContext * graphicsContext,int x1,int y1,int x2,int y2,BoxSide side,Color color,EBorderStyle style,int adjacentWidth1,int adjacentWidth2,bool antialias)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)
99 {
100 int thickness;
101 int length;
102 if (side == BSTop || side == BSBottom) {
103 thickness = y2 - y1;
104 length = x2 - x1;
105 } else {
106 thickness = x2 - x1;
107 length = y2 - y1;
108 }
109
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)
113 return;
114
115 if (style == DOUBLE && thickness < 3)
116 style = SOLID;
117
118 switch (style) {
119 case BNONE:
120 case BHIDDEN:
121 return;
122 case DOTTED:
123 case DASHED:
124 drawDashedOrDottedBoxSide(graphicsContext, x1, y1, x2, y2, side,
125 color, thickness, style, antialias);
126 break;
127 case DOUBLE:
128 drawDoubleBoxSide(graphicsContext, x1, y1, x2, y2, length, side, color,
129 thickness, adjacentWidth1, adjacentWidth2, antialias);
130 break;
131 case RIDGE:
132 case GROOVE:
133 drawRidgeOrGrooveBoxSide(graphicsContext, x1, y1, x2, y2, side, color,
134 style, adjacentWidth1, adjacentWidth2, antialias);
135 break;
136 case INSET:
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();
141 // fall through
142 case OUTSET:
143 if (style == OUTSET && (side == BSBottom || side == BSRight))
144 color = color.dark();
145 // fall through
146 case SOLID:
147 drawSolidBoxSide(graphicsContext, x1, y1, x2, y2, side, color, adjacentWidth1, adjacentWidth2, antialias);
148 break;
149 }
150 }
151
drawDashedOrDottedBoxSide(GraphicsContext * graphicsContext,int x1,int y1,int x2,int y2,BoxSide side,Color color,int thickness,EBorderStyle style,bool 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)
154 {
155 if (thickness <= 0)
156 return;
157
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);
164
165 switch (side) {
166 case BSBottom:
167 case BSTop:
168 graphicsContext->drawLine(IntPoint(x1, (y1 + y2) / 2), IntPoint(x2, (y1 + y2) / 2));
169 break;
170 case BSRight:
171 case BSLeft:
172 graphicsContext->drawLine(IntPoint((x1 + x2) / 2, y1), IntPoint((x1 + x2) / 2, y2));
173 break;
174 }
175 graphicsContext->setShouldAntialias(wasAntialiased);
176 graphicsContext->setStrokeStyle(oldStrokeStyle);
177 }
178
drawDoubleBoxSide(GraphicsContext * graphicsContext,int x1,int y1,int x2,int y2,int length,BoxSide side,Color color,int thickness,int adjacentWidth1,int adjacentWidth2,bool antialias)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)
181 {
182 int thirdOfThickness = (thickness + 1) / 3;
183 ASSERT(thirdOfThickness);
184
185 if (!adjacentWidth1 && !adjacentWidth2) {
186 StrokeStyle oldStrokeStyle = graphicsContext->strokeStyle();
187 graphicsContext->setStrokeStyle(NoStroke);
188 graphicsContext->setFillColor(color);
189
190 bool wasAntialiased = graphicsContext->shouldAntialias();
191 graphicsContext->setShouldAntialias(antialias);
192
193 switch (side) {
194 case BSTop:
195 case BSBottom:
196 graphicsContext->drawRect(IntRect(x1, y1, length, thirdOfThickness));
197 graphicsContext->drawRect(IntRect(x1, y2 - thirdOfThickness, length, thirdOfThickness));
198 break;
199 case BSLeft:
200 case BSRight:
201 // FIXME: Why do we offset the border by 1 in this case but not the other one?
202 if (length > 1) {
203 graphicsContext->drawRect(IntRect(x1, y1 + 1, thirdOfThickness, length - 1));
204 graphicsContext->drawRect(IntRect(x2 - thirdOfThickness, y1 + 1, thirdOfThickness, length - 1));
205 }
206 break;
207 }
208
209 graphicsContext->setShouldAntialias(wasAntialiased);
210 graphicsContext->setStrokeStyle(oldStrokeStyle);
211 return;
212 }
213
214 int adjacent1BigThird = ((adjacentWidth1 > 0) ? adjacentWidth1 + 1 : adjacentWidth1 - 1) / 3;
215 int adjacent2BigThird = ((adjacentWidth2 > 0) ? adjacentWidth2 + 1 : adjacentWidth2 - 1) / 3;
216
217 switch (side) {
218 case BSTop:
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);
225 break;
226 case BSLeft:
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);
233 break;
234 case BSBottom:
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);
241 break;
242 case BSRight:
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);
249 break;
250 default:
251 break;
252 }
253 }
254
drawRidgeOrGrooveBoxSide(GraphicsContext * graphicsContext,int x1,int y1,int x2,int y2,BoxSide side,Color color,EBorderStyle style,int adjacentWidth1,int adjacentWidth2,bool 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)
257 {
258 EBorderStyle s1;
259 EBorderStyle s2;
260 if (style == GROOVE) {
261 s1 = INSET;
262 s2 = OUTSET;
263 } else {
264 s1 = OUTSET;
265 s2 = INSET;
266 }
267
268 int adjacent1BigHalf = ((adjacentWidth1 > 0) ? adjacentWidth1 + 1 : adjacentWidth1 - 1) / 2;
269 int adjacent2BigHalf = ((adjacentWidth2 > 0) ? adjacentWidth2 + 1 : adjacentWidth2 - 1) / 2;
270
271 switch (side) {
272 case BSTop:
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);
277 break;
278 case BSLeft:
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);
283 break;
284 case BSBottom:
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);
289 break;
290 case BSRight:
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);
295 break;
296 }
297 }
298
drawSolidBoxSide(GraphicsContext * graphicsContext,int x1,int y1,int x2,int y2,BoxSide side,Color color,int adjacentWidth1,int adjacentWidth2,bool 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)
301 {
302 StrokeStyle oldStrokeStyle = graphicsContext->strokeStyle();
303 graphicsContext->setStrokeStyle(NoStroke);
304 graphicsContext->setFillColor(color);
305 ASSERT(x2 >= x1);
306 ASSERT(y2 >= y1);
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);
315 return;
316 }
317 FloatPoint quad[4];
318 switch (side) {
319 case BSTop:
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);
324 break;
325 case BSBottom:
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);
330 break;
331 case BSLeft:
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));
336 break;
337 case BSRight:
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));
342 break;
343 }
344
345 graphicsContext->drawConvexPolygon(4, quad, antialias);
346 graphicsContext->setStrokeStyle(oldStrokeStyle);
347 }
348
349 } // namespace blink
350