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 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 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 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 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 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 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 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