• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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