OLD | NEW |
(Empty) | |
| 1 // Copyright 2015 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/BoxBorderPainter.h" |
| 7 |
| 8 #include "core/paint/BoxPainter.h" |
| 9 #include "core/paint/PaintInfo.h" |
| 10 #include "core/style/BorderEdge.h" |
| 11 #include "platform/graphics/GraphicsContext.h" |
| 12 #include "platform/graphics/GraphicsContextStateSaver.h" |
| 13 |
| 14 namespace blink { |
| 15 |
| 16 namespace { |
| 17 |
| 18 enum BorderEdgeFlag { |
| 19 TopBorderEdge = 1 << BSTop, |
| 20 RightBorderEdge = 1 << BSRight, |
| 21 BottomBorderEdge = 1 << BSBottom, |
| 22 LeftBorderEdge = 1 << BSLeft, |
| 23 AllBorderEdges = TopBorderEdge | BottomBorderEdge | LeftBorderEdge | RightBo
rderEdge |
| 24 }; |
| 25 |
| 26 inline BorderEdgeFlag edgeFlagForSide(BoxSide side) |
| 27 { |
| 28 return static_cast<BorderEdgeFlag>(1 << side); |
| 29 } |
| 30 |
| 31 inline bool includesEdge(BorderEdgeFlags flags, BoxSide side) |
| 32 { |
| 33 return flags & edgeFlagForSide(side); |
| 34 } |
| 35 |
| 36 inline bool includesAdjacentEdges(BorderEdgeFlags flags) |
| 37 { |
| 38 // The set includes adjacent edges iff it contains at least one horizontal a
nd one vertical edge. |
| 39 return (flags & (TopBorderEdge | BottomBorderEdge)) |
| 40 && (flags & (LeftBorderEdge | RightBorderEdge)); |
| 41 } |
| 42 |
| 43 inline bool styleRequiresClipPolygon(EBorderStyle style) |
| 44 { |
| 45 // These are drawn with a stroke, so we have to clip to get corner miters. |
| 46 return style == DOTTED || style == DASHED; |
| 47 } |
| 48 |
| 49 inline bool borderStyleFillsBorderArea(EBorderStyle style) |
| 50 { |
| 51 return !(style == DOTTED || style == DASHED || style == DOUBLE); |
| 52 } |
| 53 |
| 54 inline bool borderStyleHasInnerDetail(EBorderStyle style) |
| 55 { |
| 56 return style == GROOVE || style == RIDGE || style == DOUBLE; |
| 57 } |
| 58 |
| 59 inline bool borderStyleIsDottedOrDashed(EBorderStyle style) |
| 60 { |
| 61 return style == DOTTED || style == DASHED; |
| 62 } |
| 63 |
| 64 // OUTSET darkens the bottom and right (and maybe lightens the top and left) |
| 65 // INSET darkens the top and left (and maybe lightens the bottom and right) |
| 66 inline bool borderStyleHasUnmatchedColorsAtCorner(EBorderStyle style, BoxSide si
de, BoxSide adjacentSide) |
| 67 { |
| 68 // These styles match at the top/left and bottom/right. |
| 69 if (style == INSET || style == GROOVE || style == RIDGE || style == OUTSET)
{ |
| 70 const BorderEdgeFlags topRightFlags = edgeFlagForSide(BSTop) | edgeFlagF
orSide(BSRight); |
| 71 const BorderEdgeFlags bottomLeftFlags = edgeFlagForSide(BSBottom) | edge
FlagForSide(BSLeft); |
| 72 |
| 73 BorderEdgeFlags flags = edgeFlagForSide(side) | edgeFlagForSide(adjacent
Side); |
| 74 return flags == topRightFlags || flags == bottomLeftFlags; |
| 75 } |
| 76 return false; |
| 77 } |
| 78 |
| 79 inline bool colorsMatchAtCorner(BoxSide side, BoxSide adjacentSide, const Border
Edge edges[]) |
| 80 { |
| 81 if (edges[side].shouldRender() != edges[adjacentSide].shouldRender()) |
| 82 return false; |
| 83 |
| 84 if (!edges[side].sharesColorWith(edges[adjacentSide])) |
| 85 return false; |
| 86 |
| 87 return !borderStyleHasUnmatchedColorsAtCorner(edges[side].borderStyle(), sid
e, adjacentSide); |
| 88 } |
| 89 |
| 90 inline bool colorNeedsAntiAliasAtCorner(BoxSide side, BoxSide adjacentSide, cons
t BorderEdge edges[]) |
| 91 { |
| 92 if (!edges[side].color.hasAlpha()) |
| 93 return false; |
| 94 |
| 95 if (edges[side].shouldRender() != edges[adjacentSide].shouldRender()) |
| 96 return false; |
| 97 |
| 98 if (!edges[side].sharesColorWith(edges[adjacentSide])) |
| 99 return true; |
| 100 |
| 101 return borderStyleHasUnmatchedColorsAtCorner(edges[side].borderStyle(), side
, adjacentSide); |
| 102 } |
| 103 |
| 104 inline bool borderWillArcInnerEdge(const FloatSize& firstRadius, const FloatSize
& secondRadius) |
| 105 { |
| 106 return !firstRadius.isZero() || !secondRadius.isZero(); |
| 107 } |
| 108 |
| 109 // This assumes that we draw in order: top, bottom, left, right. |
| 110 inline bool willBeOverdrawn(BoxSide side, BoxSide adjacentSide, const BorderEdge
edges[]) |
| 111 { |
| 112 switch (side) { |
| 113 case BSTop: |
| 114 case BSBottom: |
| 115 if (edges[adjacentSide].presentButInvisible()) |
| 116 return false; |
| 117 |
| 118 if (!edges[side].sharesColorWith(edges[adjacentSide]) && edges[adjacentS
ide].color.hasAlpha()) |
| 119 return false; |
| 120 |
| 121 if (!borderStyleFillsBorderArea(edges[adjacentSide].borderStyle())) |
| 122 return false; |
| 123 |
| 124 return true; |
| 125 |
| 126 case BSLeft: |
| 127 case BSRight: |
| 128 // These draw last, so are never overdrawn. |
| 129 return false; |
| 130 } |
| 131 return false; |
| 132 } |
| 133 |
| 134 inline bool borderStylesRequireMitre(BoxSide side, BoxSide adjacentSide, EBorder
Style style, EBorderStyle adjacentStyle) |
| 135 { |
| 136 if (style == DOUBLE || adjacentStyle == DOUBLE || adjacentStyle == GROOVE ||
adjacentStyle == RIDGE) |
| 137 return true; |
| 138 |
| 139 if (borderStyleIsDottedOrDashed(style) != borderStyleIsDottedOrDashed(adjace
ntStyle)) |
| 140 return true; |
| 141 |
| 142 if (style != adjacentStyle) |
| 143 return true; |
| 144 |
| 145 return borderStyleHasUnmatchedColorsAtCorner(style, side, adjacentSide); |
| 146 } |
| 147 |
| 148 inline bool joinRequiresMitre(BoxSide side, BoxSide adjacentSide, const BorderEd
ge edges[], bool allowOverdraw) |
| 149 { |
| 150 if ((edges[side].isTransparent && edges[adjacentSide].isTransparent) || !edg
es[adjacentSide].isPresent) |
| 151 return false; |
| 152 |
| 153 if (allowOverdraw && willBeOverdrawn(side, adjacentSide, edges)) |
| 154 return false; |
| 155 |
| 156 if (!edges[side].sharesColorWith(edges[adjacentSide])) |
| 157 return true; |
| 158 |
| 159 if (borderStylesRequireMitre(side, adjacentSide, edges[side].borderStyle(),
edges[adjacentSide].borderStyle())) |
| 160 return true; |
| 161 |
| 162 return false; |
| 163 } |
| 164 |
| 165 FloatRect calculateSideRect(const FloatRoundedRect& outerBorder, const BorderEdg
e& edge, int side) |
| 166 { |
| 167 FloatRect sideRect = outerBorder.rect(); |
| 168 int width = edge.width; |
| 169 |
| 170 if (side == BSTop) |
| 171 sideRect.setHeight(width); |
| 172 else if (side == BSBottom) |
| 173 sideRect.shiftYEdgeTo(sideRect.maxY() - width); |
| 174 else if (side == BSLeft) |
| 175 sideRect.setWidth(width); |
| 176 else |
| 177 sideRect.shiftXEdgeTo(sideRect.maxX() - width); |
| 178 |
| 179 return sideRect; |
| 180 } |
| 181 |
| 182 FloatRect calculateSideRectIncludingInner(const FloatRoundedRect& outerBorder, c
onst BorderEdge edges[], BoxSide side) |
| 183 { |
| 184 FloatRect sideRect = outerBorder.rect(); |
| 185 int width; |
| 186 |
| 187 switch (side) { |
| 188 case BSTop: |
| 189 width = sideRect.height() - edges[BSBottom].width; |
| 190 sideRect.setHeight(width); |
| 191 break; |
| 192 case BSBottom: |
| 193 width = sideRect.height() - edges[BSTop].width; |
| 194 sideRect.shiftYEdgeTo(sideRect.maxY() - width); |
| 195 break; |
| 196 case BSLeft: |
| 197 width = sideRect.width() - edges[BSRight].width; |
| 198 sideRect.setWidth(width); |
| 199 break; |
| 200 case BSRight: |
| 201 width = sideRect.width() - edges[BSLeft].width; |
| 202 sideRect.shiftXEdgeTo(sideRect.maxX() - width); |
| 203 break; |
| 204 } |
| 205 |
| 206 return sideRect; |
| 207 } |
| 208 |
| 209 FloatRoundedRect calculateAdjustedInnerBorder(const FloatRoundedRect& innerBorde
r, BoxSide side) |
| 210 { |
| 211 // Expand the inner border as necessary to make it a rounded rect (i.e. radi
i contained within each edge). |
| 212 // This function relies on the fact we only get radii not contained within e
ach edge if one of the radii |
| 213 // for an edge is zero, so we can shift the arc towards the zero radius corn
er. |
| 214 FloatRoundedRect::Radii newRadii = innerBorder.radii(); |
| 215 FloatRect newRect = innerBorder.rect(); |
| 216 |
| 217 float overshoot; |
| 218 float maxRadii; |
| 219 |
| 220 switch (side) { |
| 221 case BSTop: |
| 222 overshoot = newRadii.topLeft().width() + newRadii.topRight().width() - n
ewRect.width(); |
| 223 // FIXME: once we start pixel-snapping rounded rects after this point, t
he overshoot concept |
| 224 // should disappear. |
| 225 if (overshoot > 0.1) { |
| 226 newRect.setWidth(newRect.width() + overshoot); |
| 227 if (!newRadii.topLeft().width()) |
| 228 newRect.move(-overshoot, 0); |
| 229 } |
| 230 newRadii.setBottomLeft(IntSize(0, 0)); |
| 231 newRadii.setBottomRight(IntSize(0, 0)); |
| 232 maxRadii = std::max(newRadii.topLeft().height(), newRadii.topRight().hei
ght()); |
| 233 if (maxRadii > newRect.height()) |
| 234 newRect.setHeight(maxRadii); |
| 235 break; |
| 236 |
| 237 case BSBottom: |
| 238 overshoot = newRadii.bottomLeft().width() + newRadii.bottomRight().width
() - newRect.width(); |
| 239 if (overshoot > 0.1) { |
| 240 newRect.setWidth(newRect.width() + overshoot); |
| 241 if (!newRadii.bottomLeft().width()) |
| 242 newRect.move(-overshoot, 0); |
| 243 } |
| 244 newRadii.setTopLeft(IntSize(0, 0)); |
| 245 newRadii.setTopRight(IntSize(0, 0)); |
| 246 maxRadii = std::max(newRadii.bottomLeft().height(), newRadii.bottomRight
().height()); |
| 247 if (maxRadii > newRect.height()) { |
| 248 newRect.move(0, newRect.height() - maxRadii); |
| 249 newRect.setHeight(maxRadii); |
| 250 } |
| 251 break; |
| 252 |
| 253 case BSLeft: |
| 254 overshoot = newRadii.topLeft().height() + newRadii.bottomLeft().height()
- newRect.height(); |
| 255 if (overshoot > 0.1) { |
| 256 newRect.setHeight(newRect.height() + overshoot); |
| 257 if (!newRadii.topLeft().height()) |
| 258 newRect.move(0, -overshoot); |
| 259 } |
| 260 newRadii.setTopRight(IntSize(0, 0)); |
| 261 newRadii.setBottomRight(IntSize(0, 0)); |
| 262 maxRadii = std::max(newRadii.topLeft().width(), newRadii.bottomLeft().wi
dth()); |
| 263 if (maxRadii > newRect.width()) |
| 264 newRect.setWidth(maxRadii); |
| 265 break; |
| 266 |
| 267 case BSRight: |
| 268 overshoot = newRadii.topRight().height() + newRadii.bottomRight().height
() - newRect.height(); |
| 269 if (overshoot > 0.1) { |
| 270 newRect.setHeight(newRect.height() + overshoot); |
| 271 if (!newRadii.topRight().height()) |
| 272 newRect.move(0, -overshoot); |
| 273 } |
| 274 newRadii.setTopLeft(IntSize(0, 0)); |
| 275 newRadii.setBottomLeft(IntSize(0, 0)); |
| 276 maxRadii = std::max(newRadii.topRight().width(), newRadii.bottomRight().
width()); |
| 277 if (maxRadii > newRect.width()) { |
| 278 newRect.move(newRect.width() - maxRadii, 0); |
| 279 newRect.setWidth(maxRadii); |
| 280 } |
| 281 break; |
| 282 } |
| 283 |
| 284 return FloatRoundedRect(newRect, newRadii); |
| 285 } |
| 286 |
| 287 struct BoxBorderInfo { |
| 288 STACK_ALLOCATED(); |
| 289 public: |
| 290 BoxBorderInfo(const ComputedStyle& style, BackgroundBleedAvoidance bleedAvoi
dance, |
| 291 bool includeLogicalLeftEdge, bool includeLogicalRightEdge) |
| 292 : style(style) |
| 293 , bleedAvoidance(bleedAvoidance) |
| 294 , includeLogicalLeftEdge(includeLogicalLeftEdge) |
| 295 , includeLogicalRightEdge(includeLogicalRightEdge) |
| 296 , visibleEdgeCount(0) |
| 297 , firstVisibleEdge(0) |
| 298 , visibleEdgeSet(0) |
| 299 , isUniformStyle(true) |
| 300 , isUniformWidth(true) |
| 301 , isUniformColor(true) |
| 302 , hasAlpha(false) |
| 303 { |
| 304 |
| 305 style.getBorderEdgeInfo(edges, includeLogicalLeftEdge, includeLogicalRig
htEdge); |
| 306 |
| 307 for (unsigned i = 0; i < WTF_ARRAY_LENGTH(edges); ++i) { |
| 308 const BorderEdge& edge = edges[i]; |
| 309 |
| 310 if (!edge.shouldRender()) { |
| 311 if (edge.presentButInvisible()) { |
| 312 isUniformWidth = false; |
| 313 isUniformColor = false; |
| 314 } |
| 315 |
| 316 continue; |
| 317 } |
| 318 |
| 319 visibleEdgeCount++; |
| 320 visibleEdgeSet |= edgeFlagForSide(static_cast<BoxSide>(i)); |
| 321 |
| 322 hasAlpha = hasAlpha || edge.color.hasAlpha(); |
| 323 |
| 324 if (visibleEdgeCount == 1) { |
| 325 firstVisibleEdge = i; |
| 326 continue; |
| 327 } |
| 328 |
| 329 isUniformStyle = isUniformStyle && (edge.borderStyle() == edges[firs
tVisibleEdge].borderStyle()); |
| 330 isUniformWidth = isUniformWidth && (edge.width == edges[firstVisible
Edge].width); |
| 331 isUniformColor = isUniformColor && (edge.color == edges[firstVisible
Edge].color); |
| 332 } |
| 333 } |
| 334 |
| 335 const ComputedStyle& style; |
| 336 const BackgroundBleedAvoidance bleedAvoidance; |
| 337 const bool includeLogicalLeftEdge; |
| 338 const bool includeLogicalRightEdge; |
| 339 |
| 340 BorderEdge edges[4]; |
| 341 |
| 342 unsigned visibleEdgeCount; |
| 343 unsigned firstVisibleEdge; |
| 344 BorderEdgeFlags visibleEdgeSet; |
| 345 |
| 346 bool isUniformStyle; |
| 347 bool isUniformWidth; |
| 348 bool isUniformColor; |
| 349 bool hasAlpha; |
| 350 }; |
| 351 |
| 352 LayoutRectOutsets doubleStripeInsets(const BorderEdge edges[], BorderEdge::Doubl
eBorderStripe stripe) |
| 353 { |
| 354 // Insets are representes as negative outsets. |
| 355 return LayoutRectOutsets( |
| 356 -edges[BSTop].getDoubleBorderStripeWidth(stripe), |
| 357 -edges[BSRight].getDoubleBorderStripeWidth(stripe), |
| 358 -edges[BSBottom].getDoubleBorderStripeWidth(stripe), |
| 359 -edges[BSLeft].getDoubleBorderStripeWidth(stripe)); |
| 360 } |
| 361 |
| 362 void drawSolidBorderRect(GraphicsContext* context, const FloatRect& borderRect, |
| 363 float borderWidth, const Color& color) |
| 364 { |
| 365 FloatRect strokeRect(borderRect); |
| 366 strokeRect.inflate(-borderWidth / 2); |
| 367 |
| 368 bool antialias = BoxPainter::shouldAntialiasLines(context); |
| 369 bool wasAntialias = context->shouldAntialias(); |
| 370 if (antialias != wasAntialias) |
| 371 context->setShouldAntialias(antialias); |
| 372 |
| 373 context->setStrokeStyle(SolidStroke); |
| 374 context->setStrokeColor(color); |
| 375 context->strokeRect(strokeRect, borderWidth); |
| 376 |
| 377 if (antialias != wasAntialias) |
| 378 context->setShouldAntialias(wasAntialias); |
| 379 } |
| 380 |
| 381 void drawBleedAdjustedDRRect(GraphicsContext* context, BackgroundBleedAvoidance
bleedAvoidance, |
| 382 const FloatRoundedRect& outer, const FloatRoundedRect& inner, Color color) |
| 383 { |
| 384 switch (bleedAvoidance) { |
| 385 case BackgroundBleedBackgroundOverBorder: |
| 386 // BackgroundBleedBackgroundOverBorder draws an opaque background over t
he inner rrect, |
| 387 // so we can simply fill the outer rect here to avoid backdrop bleeding. |
| 388 context->fillRoundedRect(outer, color); |
| 389 break; |
| 390 case BackgroundBleedClipLayer: { |
| 391 // BackgroundBleedClipLayer clips the outer rrect for the whole layer. B
ased on this, |
| 392 // we can avoid background bleeding by filling the *outside* of inner rr
ect, all the |
| 393 // way to the layer bounds (enclosing int rect for the clip, in device s
pace). |
| 394 ASSERT(outer.isRounded()); |
| 395 |
| 396 SkPath path; |
| 397 path.addRRect(inner); |
| 398 path.setFillType(SkPath::kInverseWinding_FillType); |
| 399 |
| 400 SkPaint paint; |
| 401 paint.setColor(color.rgb()); |
| 402 paint.setStyle(SkPaint::kFill_Style); |
| 403 paint.setAntiAlias(true); |
| 404 context->drawPath(path, paint); |
| 405 |
| 406 break; |
| 407 } |
| 408 case BackgroundBleedClipOnly: |
| 409 if (outer.isRounded()) { |
| 410 // BackgroundBleedClipOnly clips the outer rrect corners for us. |
| 411 FloatRoundedRect adjustedOuter = outer; |
| 412 adjustedOuter.setRadii(FloatRoundedRect::Radii()); |
| 413 context->fillDRRect(adjustedOuter, inner, color); |
| 414 break; |
| 415 } |
| 416 // fall through |
| 417 default: |
| 418 context->fillDRRect(outer, inner, color); |
| 419 break; |
| 420 } |
| 421 } |
| 422 |
| 423 void drawDoubleBorder(GraphicsContext* context, const BoxBorderInfo& borderInfo,
const LayoutRect& borderRect, |
| 424 const FloatRoundedRect& outerBorder, const FloatRoundedRect& innerBorder) |
| 425 { |
| 426 ASSERT(borderInfo.isUniformColor); |
| 427 ASSERT(borderInfo.isUniformStyle); |
| 428 ASSERT(borderInfo.edges[borderInfo.firstVisibleEdge].borderStyle() == DOUBLE
); |
| 429 ASSERT(borderInfo.visibleEdgeSet == AllBorderEdges); |
| 430 |
| 431 const Color color = borderInfo.edges[borderInfo.firstVisibleEdge].color; |
| 432 |
| 433 // outer stripe |
| 434 const LayoutRectOutsets outerThirdInsets = |
| 435 doubleStripeInsets(borderInfo.edges, BorderEdge::DoubleBorderStripeOuter
); |
| 436 const FloatRoundedRect outerThirdRect = borderInfo.style.getRoundedInnerBord
erFor(borderRect, |
| 437 outerThirdInsets, borderInfo.includeLogicalLeftEdge, borderInfo.includeL
ogicalRightEdge); |
| 438 drawBleedAdjustedDRRect(context, borderInfo.bleedAvoidance, outerBorder, out
erThirdRect, color); |
| 439 |
| 440 // inner stripe |
| 441 const LayoutRectOutsets innerThirdInsets = |
| 442 doubleStripeInsets(borderInfo.edges, BorderEdge::DoubleBorderStripeInner
); |
| 443 const FloatRoundedRect innerThirdRect = borderInfo.style.getRoundedInnerBord
erFor(borderRect, |
| 444 innerThirdInsets, borderInfo.includeLogicalLeftEdge, borderInfo.includeL
ogicalRightEdge); |
| 445 context->fillDRRect(innerThirdRect, innerBorder, color); |
| 446 } |
| 447 |
| 448 bool paintBorderFastPath(GraphicsContext* context, const BoxBorderInfo& info, |
| 449 const LayoutRect& borderRect, const FloatRoundedRect& outer, const FloatRoun
dedRect& inner) |
| 450 { |
| 451 if (!info.isUniformColor || !info.isUniformStyle || !inner.isRenderable()) |
| 452 return false; |
| 453 |
| 454 const BorderEdge& firstEdge = info.edges[info.firstVisibleEdge]; |
| 455 if (firstEdge.borderStyle() != SOLID && firstEdge.borderStyle() != DOUBLE) |
| 456 return false; |
| 457 |
| 458 if (info.visibleEdgeSet == AllBorderEdges) { |
| 459 if (firstEdge.borderStyle() == SOLID) { |
| 460 if (info.isUniformWidth && !outer.isRounded()) { |
| 461 // 4-side, solid, uniform-width, rectangular border => one drawR
ect() |
| 462 drawSolidBorderRect(context, outer.rect(), firstEdge.width, firs
tEdge.color); |
| 463 } else { |
| 464 // 4-side, solid border => one drawDRRect() |
| 465 drawBleedAdjustedDRRect(context, info.bleedAvoidance, outer, inn
er, firstEdge.color); |
| 466 } |
| 467 } else { |
| 468 // 4-side, double border => 2x drawDRRect() |
| 469 ASSERT(firstEdge.borderStyle() == DOUBLE); |
| 470 drawDoubleBorder(context, info, borderRect, outer, inner); |
| 471 } |
| 472 |
| 473 return true; |
| 474 } |
| 475 |
| 476 // This is faster than the normal complex border path only if it avoids crea
ting transparency |
| 477 // layers (when the border is translucent). |
| 478 if (firstEdge.borderStyle() == SOLID && !outer.isRounded() && info.hasAlpha)
{ |
| 479 ASSERT(info.visibleEdgeSet != AllBorderEdges); |
| 480 // solid, rectangular border => one drawPath() |
| 481 Path path; |
| 482 |
| 483 for (int i = BSTop; i <= BSLeft; ++i) { |
| 484 const BorderEdge& currEdge = info.edges[i]; |
| 485 if (currEdge.shouldRender()) |
| 486 path.addRect(calculateSideRect(outer, currEdge, i)); |
| 487 } |
| 488 |
| 489 context->setFillRule(RULE_NONZERO); |
| 490 context->setFillColor(firstEdge.color); |
| 491 context->fillPath(path); |
| 492 |
| 493 return true; |
| 494 } |
| 495 |
| 496 return false; |
| 497 } |
| 498 |
| 499 bool bleedAvoidanceIsClipping(BackgroundBleedAvoidance bleedAvoidance) |
| 500 { |
| 501 return bleedAvoidance == BackgroundBleedClipOnly || bleedAvoidance == Backgr
oundBleedClipLayer; |
| 502 } |
| 503 |
| 504 } // anonymous namespace |
| 505 |
| 506 void BoxBorderPainter::paintBorder(LayoutBoxModelObject& obj, const PaintInfo& i
nfo, |
| 507 const LayoutRect& rect, const ComputedStyle& style, BackgroundBleedAvoidance
bleedAvoidance, |
| 508 bool includeLogicalLeftEdge, bool includeLogicalRightEdge) const |
| 509 { |
| 510 GraphicsContext* graphicsContext = info.context; |
| 511 // border-image is not affected by border-radius. |
| 512 if (BoxPainter::paintNinePieceImage(obj, graphicsContext, rect, style, style
.borderImage())) |
| 513 return; |
| 514 |
| 515 const BoxBorderInfo borderInfo(style, bleedAvoidance, includeLogicalLeftEdge
, includeLogicalRightEdge); |
| 516 FloatRoundedRect outerBorder = style.getRoundedBorderFor(rect, includeLogica
lLeftEdge, includeLogicalRightEdge); |
| 517 FloatRoundedRect innerBorder = style.getRoundedInnerBorderFor(rect, includeL
ogicalLeftEdge, includeLogicalRightEdge); |
| 518 |
| 519 if (outerBorder.rect().isEmpty() || !borderInfo.visibleEdgeCount) |
| 520 return; |
| 521 |
| 522 const BorderEdge& firstEdge = borderInfo.edges[borderInfo.firstVisibleEdge]; |
| 523 bool haveAllSolidEdges = borderInfo.isUniformStyle && firstEdge.borderStyle(
) == SOLID; |
| 524 |
| 525 // If no corner intersects the clip region, we can pretend outerBorder is |
| 526 // rectangular to improve performance. |
| 527 if (haveAllSolidEdges && outerBorder.isRounded() && BoxPainter::allCornersCl
ippedOut(outerBorder, info.rect)) |
| 528 outerBorder.setRadii(FloatRoundedRect::Radii()); |
| 529 |
| 530 if (paintBorderFastPath(graphicsContext, borderInfo, rect, outerBorder, inne
rBorder)) |
| 531 return; |
| 532 |
| 533 bool clipToOuterBorder = outerBorder.isRounded(); |
| 534 GraphicsContextStateSaver stateSaver(*graphicsContext, clipToOuterBorder); |
| 535 if (clipToOuterBorder) { |
| 536 // For BackgroundBleedClip{Only,Layer}, the outer rrect clip is already
applied. |
| 537 if (!bleedAvoidanceIsClipping(bleedAvoidance)) |
| 538 graphicsContext->clipRoundedRect(outerBorder); |
| 539 |
| 540 // For BackgroundBleedBackgroundOverBorder, we're going to draw an opaqu
e background over |
| 541 // the inner rrect - so clipping is not needed (nor desirable due to bac
kdrop bleeding). |
| 542 if (bleedAvoidance != BackgroundBleedBackgroundOverBorder && innerBorder
.isRenderable() && !innerBorder.isEmpty()) |
| 543 graphicsContext->clipOutRoundedRect(innerBorder); |
| 544 } |
| 545 |
| 546 // If only one edge visible antialiasing doesn't create seams |
| 547 bool antialias = BoxPainter::shouldAntialiasLines(graphicsContext) || border
Info.visibleEdgeCount == 1; |
| 548 if (borderInfo.hasAlpha) { |
| 549 paintTranslucentBorderSides(graphicsContext, style, outerBorder, innerBo
rder, borderInfo.edges, |
| 550 borderInfo.visibleEdgeSet, bleedAvoidance, includeLogicalLeftEdge, inclu
deLogicalRightEdge, antialias); |
| 551 } else { |
| 552 paintBorderSides(graphicsContext, style, outerBorder, innerBorder, borde
rInfo.edges, |
| 553 borderInfo.visibleEdgeSet, bleedAvoidance, includeLogicalLeftEdge, inclu
deLogicalRightEdge, antialias); |
| 554 } |
| 555 } |
| 556 |
| 557 void BoxBorderPainter::paintTranslucentBorderSides(GraphicsContext* graphicsCont
ext, |
| 558 const ComputedStyle& style, const FloatRoundedRect& outerBorder, const Float
RoundedRect& innerBorder, |
| 559 const BorderEdge edges[], BorderEdgeFlags edgesToDraw, BackgroundBleedAvoida
nce bleedAvoidance, |
| 560 bool includeLogicalLeftEdge, bool includeLogicalRightEdge, bool antialias) |
| 561 { |
| 562 // willBeOverdrawn assumes that we draw in order: top, bottom, left, right. |
| 563 // This is different from BoxSide enum order. |
| 564 static const BoxSide paintOrder[] = { BSTop, BSBottom, BSLeft, BSRight }; |
| 565 |
| 566 while (edgesToDraw) { |
| 567 // Find undrawn edges sharing a color. |
| 568 Color commonColor; |
| 569 |
| 570 BorderEdgeFlags commonColorEdgeSet = 0; |
| 571 for (size_t i = 0; i < sizeof(paintOrder) / sizeof(paintOrder[0]); ++i)
{ |
| 572 BoxSide currSide = paintOrder[i]; |
| 573 if (!includesEdge(edgesToDraw, currSide)) |
| 574 continue; |
| 575 |
| 576 bool includeEdge; |
| 577 if (!commonColorEdgeSet) { |
| 578 commonColor = edges[currSide].color; |
| 579 includeEdge = true; |
| 580 } else { |
| 581 includeEdge = edges[currSide].color == commonColor; |
| 582 } |
| 583 |
| 584 if (includeEdge) |
| 585 commonColorEdgeSet |= edgeFlagForSide(currSide); |
| 586 } |
| 587 |
| 588 bool useTransparencyLayer = includesAdjacentEdges(commonColorEdgeSet) &&
commonColor.hasAlpha(); |
| 589 if (useTransparencyLayer) { |
| 590 graphicsContext->beginLayer(static_cast<float>(commonColor.alpha())
/ 255); |
| 591 commonColor = Color(commonColor.red(), commonColor.green(), commonCo
lor.blue()); |
| 592 } |
| 593 |
| 594 paintBorderSides(graphicsContext, style, outerBorder, innerBorder, edges
, commonColorEdgeSet, |
| 595 bleedAvoidance, includeLogicalLeftEdge, includeLogicalRightEdge, ant
ialias, &commonColor); |
| 596 |
| 597 if (useTransparencyLayer) |
| 598 graphicsContext->endLayer(); |
| 599 |
| 600 edgesToDraw &= ~commonColorEdgeSet; |
| 601 } |
| 602 } |
| 603 |
| 604 void BoxBorderPainter::paintOneBorderSide(GraphicsContext* graphicsContext, cons
t ComputedStyle& style, |
| 605 const FloatRoundedRect& outerBorder, const FloatRoundedRect& innerBorder, co
nst FloatRect& sideRect, |
| 606 BoxSide side, BoxSide adjacentSide1, BoxSide adjacentSide2, const BorderEdge
edges[], const Path* path, |
| 607 BackgroundBleedAvoidance bleedAvoidance, bool includeLogicalLeftEdge, bool i
ncludeLogicalRightEdge, |
| 608 bool antialias, const Color* overrideColor) |
| 609 { |
| 610 const BorderEdge& edgeToRender = edges[side]; |
| 611 ASSERT(edgeToRender.width); |
| 612 const BorderEdge& adjacentEdge1 = edges[adjacentSide1]; |
| 613 const BorderEdge& adjacentEdge2 = edges[adjacentSide2]; |
| 614 |
| 615 bool mitreAdjacentSide1 = joinRequiresMitre(side, adjacentSide1, edges, !ant
ialias); |
| 616 bool mitreAdjacentSide2 = joinRequiresMitre(side, adjacentSide2, edges, !ant
ialias); |
| 617 |
| 618 bool adjacentSide1StylesMatch = colorsMatchAtCorner(side, adjacentSide1, edg
es); |
| 619 bool adjacentSide2StylesMatch = colorsMatchAtCorner(side, adjacentSide2, edg
es); |
| 620 |
| 621 const Color& colorToPaint = overrideColor ? *overrideColor : edgeToRender.co
lor; |
| 622 |
| 623 if (path) { |
| 624 GraphicsContextStateSaver stateSaver(*graphicsContext); |
| 625 if (innerBorder.isRenderable()) |
| 626 clipBorderSidePolygon(graphicsContext, outerBorder, innerBorder, sid
e, adjacentSide1StylesMatch, adjacentSide2StylesMatch); |
| 627 else |
| 628 clipBorderSideForComplexInnerPath(graphicsContext, outerBorder, inne
rBorder, side, edges); |
| 629 float thickness = std::max(std::max(edgeToRender.width, adjacentEdge1.wi
dth), adjacentEdge2.width); |
| 630 drawBoxSideFromPath(graphicsContext, LayoutRect(outerBorder.rect()), *pa
th, edges, edgeToRender.width, thickness, side, style, |
| 631 colorToPaint, edgeToRender.borderStyle(), bleedAvoidance, includeLog
icalLeftEdge, includeLogicalRightEdge); |
| 632 } else { |
| 633 bool clipForStyle = styleRequiresClipPolygon(edgeToRender.borderStyle())
&& (mitreAdjacentSide1 || mitreAdjacentSide2); |
| 634 bool clipAdjacentSide1 = colorNeedsAntiAliasAtCorner(side, adjacentSide1
, edges) && mitreAdjacentSide1; |
| 635 bool clipAdjacentSide2 = colorNeedsAntiAliasAtCorner(side, adjacentSide2
, edges) && mitreAdjacentSide2; |
| 636 bool shouldClip = clipForStyle || clipAdjacentSide1 || clipAdjacentSide2
; |
| 637 |
| 638 GraphicsContextStateSaver clipStateSaver(*graphicsContext, shouldClip); |
| 639 if (shouldClip) { |
| 640 bool aliasAdjacentSide1 = clipAdjacentSide1 || (clipForStyle && mitr
eAdjacentSide1); |
| 641 bool aliasAdjacentSide2 = clipAdjacentSide2 || (clipForStyle && mitr
eAdjacentSide2); |
| 642 clipBorderSidePolygon(graphicsContext, outerBorder, innerBorder, sid
e, !aliasAdjacentSide1, !aliasAdjacentSide2); |
| 643 // Since we clipped, no need to draw with a mitre. |
| 644 mitreAdjacentSide1 = false; |
| 645 mitreAdjacentSide2 = false; |
| 646 } |
| 647 |
| 648 ObjectPainter::drawLineForBoxSide(graphicsContext, sideRect.x(), sideRec
t.y(), sideRect.maxX(), sideRect.maxY(), side, colorToPaint, edgeToRender.border
Style(), |
| 649 mitreAdjacentSide1 ? adjacentEdge1.width : 0, mitreAdjacentSide2 ? a
djacentEdge2.width : 0, antialias); |
| 650 } |
| 651 } |
| 652 |
| 653 void BoxBorderPainter::paintBorderSides(GraphicsContext* graphicsContext, const
ComputedStyle& style, |
| 654 const FloatRoundedRect& outerBorder, const FloatRoundedRect& innerBorder, co
nst BorderEdge edges[], |
| 655 BorderEdgeFlags edgeSet, BackgroundBleedAvoidance bleedAvoidance, bool inclu
deLogicalLeftEdge, |
| 656 bool includeLogicalRightEdge, bool antialias, const Color* overrideColor) |
| 657 { |
| 658 bool renderRadii = outerBorder.isRounded(); |
| 659 |
| 660 Path roundedPath; |
| 661 if (renderRadii) |
| 662 roundedPath.addRoundedRect(outerBorder); |
| 663 |
| 664 // The inner border adjustment for bleed avoidance mode BackgroundBleedBackg
roundOverBorder |
| 665 // is only applied to sideRect, which is okay since BackgroundBleedBackgroun
dOverBorder |
| 666 // is only to be used for solid borders and the shape of the border painted
by drawBoxSideFromPath |
| 667 // only depends on sideRect when painting solid borders. |
| 668 |
| 669 if (edges[BSTop].shouldRender() && includesEdge(edgeSet, BSTop)) { |
| 670 FloatRect sideRect = outerBorder.rect(); |
| 671 sideRect.setHeight(edges[BSTop].width); |
| 672 |
| 673 bool usePath = renderRadii && (borderStyleHasInnerDetail(edges[BSTop].bo
rderStyle()) || borderWillArcInnerEdge(innerBorder.radii().topLeft(), innerBorde
r.radii().topRight())); |
| 674 paintOneBorderSide(graphicsContext, style, outerBorder, innerBorder, sid
eRect, BSTop, BSLeft, BSRight, edges, usePath ? &roundedPath : 0, bleedAvoidance
, includeLogicalLeftEdge, includeLogicalRightEdge, antialias, overrideColor); |
| 675 } |
| 676 |
| 677 if (edges[BSBottom].shouldRender() && includesEdge(edgeSet, BSBottom)) { |
| 678 FloatRect sideRect = outerBorder.rect(); |
| 679 sideRect.shiftYEdgeTo(sideRect.maxY() - edges[BSBottom].width); |
| 680 |
| 681 bool usePath = renderRadii && (borderStyleHasInnerDetail(edges[BSBottom]
.borderStyle()) || borderWillArcInnerEdge(innerBorder.radii().bottomLeft(), inne
rBorder.radii().bottomRight())); |
| 682 paintOneBorderSide(graphicsContext, style, outerBorder, innerBorder, sid
eRect, BSBottom, BSLeft, BSRight, edges, usePath ? &roundedPath : 0, bleedAvoida
nce, includeLogicalLeftEdge, includeLogicalRightEdge, antialias, overrideColor); |
| 683 } |
| 684 |
| 685 if (edges[BSLeft].shouldRender() && includesEdge(edgeSet, BSLeft)) { |
| 686 FloatRect sideRect = outerBorder.rect(); |
| 687 sideRect.setWidth(edges[BSLeft].width); |
| 688 |
| 689 bool usePath = renderRadii && (borderStyleHasInnerDetail(edges[BSLeft].b
orderStyle()) || borderWillArcInnerEdge(innerBorder.radii().bottomLeft(), innerB
order.radii().topLeft())); |
| 690 paintOneBorderSide(graphicsContext, style, outerBorder, innerBorder, sid
eRect, BSLeft, BSTop, BSBottom, edges, usePath ? &roundedPath : 0, bleedAvoidanc
e, includeLogicalLeftEdge, includeLogicalRightEdge, antialias, overrideColor); |
| 691 } |
| 692 |
| 693 if (edges[BSRight].shouldRender() && includesEdge(edgeSet, BSRight)) { |
| 694 FloatRect sideRect = outerBorder.rect(); |
| 695 sideRect.shiftXEdgeTo(sideRect.maxX() - edges[BSRight].width); |
| 696 |
| 697 bool usePath = renderRadii && (borderStyleHasInnerDetail(edges[BSRight].
borderStyle()) || borderWillArcInnerEdge(innerBorder.radii().bottomRight(), inne
rBorder.radii().topRight())); |
| 698 paintOneBorderSide(graphicsContext, style, outerBorder, innerBorder, sid
eRect, BSRight, BSTop, BSBottom, edges, usePath ? &roundedPath : 0, bleedAvoidan
ce, includeLogicalLeftEdge, includeLogicalRightEdge, antialias, overrideColor); |
| 699 } |
| 700 } |
| 701 |
| 702 void BoxBorderPainter::drawBoxSideFromPath(GraphicsContext* graphicsContext, |
| 703 const LayoutRect& borderRect, const Path& borderPath, const BorderEdge edges
[], float thickness, |
| 704 float drawThickness, BoxSide side, const ComputedStyle& style, Color color,
EBorderStyle borderStyle, |
| 705 BackgroundBleedAvoidance bleedAvoidance, bool includeLogicalLeftEdge, bool i
ncludeLogicalRightEdge) |
| 706 { |
| 707 if (thickness <= 0) |
| 708 return; |
| 709 |
| 710 if (borderStyle == DOUBLE && thickness < 3) |
| 711 borderStyle = SOLID; |
| 712 |
| 713 switch (borderStyle) { |
| 714 case BNONE: |
| 715 case BHIDDEN: |
| 716 return; |
| 717 case DOTTED: |
| 718 case DASHED: { |
| 719 graphicsContext->setStrokeColor(color); |
| 720 |
| 721 // The stroke is doubled here because the provided path is the |
| 722 // outside edge of the border so half the stroke is clipped off. |
| 723 // The extra multiplier is so that the clipping mask can antialias |
| 724 // the edges to prevent jaggies. |
| 725 graphicsContext->setStrokeThickness(drawThickness * 2 * 1.1f); |
| 726 graphicsContext->setStrokeStyle(borderStyle == DASHED ? DashedStroke : D
ottedStroke); |
| 727 |
| 728 // If the number of dashes that fit in the path is odd and non-integral
then we |
| 729 // will have an awkwardly-sized dash at the end of the path. To try to a
void that |
| 730 // here, we simply make the whitespace dashes ever so slightly bigger. |
| 731 // FIXME: This could be even better if we tried to manipulate the dash o
ffset |
| 732 // and possibly the gapLength to get the corners dash-symmetrical. |
| 733 float dashLength = thickness * ((borderStyle == DASHED) ? 3.0f : 1.0f); |
| 734 float gapLength = dashLength; |
| 735 float numberOfDashes = borderPath.length() / dashLength; |
| 736 // Don't try to show dashes if we have less than 2 dashes + 2 gaps. |
| 737 // FIXME: should do this test per side. |
| 738 if (numberOfDashes >= 4) { |
| 739 bool evenNumberOfFullDashes = !((int)numberOfDashes % 2); |
| 740 bool integralNumberOfDashes = !(numberOfDashes - (int)numberOfDashes
); |
| 741 if (!evenNumberOfFullDashes && !integralNumberOfDashes) { |
| 742 float numberOfGaps = numberOfDashes / 2; |
| 743 gapLength += (dashLength / numberOfGaps); |
| 744 } |
| 745 |
| 746 DashArray lineDash; |
| 747 lineDash.append(dashLength); |
| 748 lineDash.append(gapLength); |
| 749 graphicsContext->setLineDash(lineDash, dashLength); |
| 750 } |
| 751 |
| 752 // FIXME: stroking the border path causes issues with tight corners: |
| 753 // https://bugs.webkit.org/show_bug.cgi?id=58711 |
| 754 // Also, to get the best appearance we should stroke a path between the
two borders. |
| 755 graphicsContext->strokePath(borderPath); |
| 756 return; |
| 757 } |
| 758 case DOUBLE: { |
| 759 // Draw inner border line |
| 760 { |
| 761 GraphicsContextStateSaver stateSaver(*graphicsContext); |
| 762 const LayoutRectOutsets innerInsets = doubleStripeInsets(edges, Bord
erEdge::DoubleBorderStripeInner); |
| 763 FloatRoundedRect innerClip = style.getRoundedInnerBorderFor(borderRe
ct, |
| 764 innerInsets, includeLogicalLeftEdge, includeLogicalRightEdge); |
| 765 |
| 766 graphicsContext->clipRoundedRect(innerClip); |
| 767 drawBoxSideFromPath(graphicsContext, borderRect, borderPath, edges,
thickness, drawThickness, side, style, color, SOLID, bleedAvoidance, includeLogi
calLeftEdge, includeLogicalRightEdge); |
| 768 } |
| 769 |
| 770 // Draw outer border line |
| 771 { |
| 772 GraphicsContextStateSaver stateSaver(*graphicsContext); |
| 773 LayoutRect outerRect = borderRect; |
| 774 LayoutRectOutsets outerInsets = doubleStripeInsets(edges, BorderEdge
::DoubleBorderStripeOuter); |
| 775 |
| 776 if (bleedAvoidanceIsClipping(bleedAvoidance)) { |
| 777 outerRect.inflate(1); |
| 778 outerInsets.setTop(outerInsets.top() - 1); |
| 779 outerInsets.setRight(outerInsets.right() - 1); |
| 780 outerInsets.setBottom(outerInsets.bottom() - 1); |
| 781 outerInsets.setLeft(outerInsets.left() - 1); |
| 782 } |
| 783 |
| 784 FloatRoundedRect outerClip = style.getRoundedInnerBorderFor(outerRec
t, outerInsets, |
| 785 includeLogicalLeftEdge, includeLogicalRightEdge); |
| 786 graphicsContext->clipOutRoundedRect(outerClip); |
| 787 drawBoxSideFromPath(graphicsContext, borderRect, borderPath, edges,
thickness, drawThickness, side, style, color, SOLID, bleedAvoidance, includeLogi
calLeftEdge, includeLogicalRightEdge); |
| 788 } |
| 789 return; |
| 790 } |
| 791 case RIDGE: |
| 792 case GROOVE: |
| 793 { |
| 794 EBorderStyle s1; |
| 795 EBorderStyle s2; |
| 796 if (borderStyle == GROOVE) { |
| 797 s1 = INSET; |
| 798 s2 = OUTSET; |
| 799 } else { |
| 800 s1 = OUTSET; |
| 801 s2 = INSET; |
| 802 } |
| 803 |
| 804 // Paint full border |
| 805 drawBoxSideFromPath(graphicsContext, borderRect, borderPath, edges, thic
kness, drawThickness, side, style, color, s1, bleedAvoidance, includeLogicalLeft
Edge, includeLogicalRightEdge); |
| 806 |
| 807 // Paint inner only |
| 808 GraphicsContextStateSaver stateSaver(*graphicsContext); |
| 809 LayoutUnit topWidth = edges[BSTop].usedWidth() / 2; |
| 810 LayoutUnit bottomWidth = edges[BSBottom].usedWidth() / 2; |
| 811 LayoutUnit leftWidth = edges[BSLeft].usedWidth() / 2; |
| 812 LayoutUnit rightWidth = edges[BSRight].usedWidth() / 2; |
| 813 |
| 814 FloatRoundedRect clipRect = style.getRoundedInnerBorderFor(borderRect, |
| 815 LayoutRectOutsets(-topWidth, -rightWidth, -bottomWidth, -leftWidth), |
| 816 includeLogicalLeftEdge, includeLogicalRightEdge); |
| 817 |
| 818 graphicsContext->clipRoundedRect(clipRect); |
| 819 drawBoxSideFromPath(graphicsContext, borderRect, borderPath, edges, thic
kness, drawThickness, side, style, color, s2, bleedAvoidance, includeLogicalLeft
Edge, includeLogicalRightEdge); |
| 820 return; |
| 821 } |
| 822 case INSET: |
| 823 if (side == BSTop || side == BSLeft) |
| 824 color = color.dark(); |
| 825 break; |
| 826 case OUTSET: |
| 827 if (side == BSBottom || side == BSRight) |
| 828 color = color.dark(); |
| 829 break; |
| 830 default: |
| 831 break; |
| 832 } |
| 833 |
| 834 graphicsContext->setStrokeStyle(NoStroke); |
| 835 graphicsContext->setFillColor(color); |
| 836 graphicsContext->drawRect(pixelSnappedIntRect(borderRect)); |
| 837 } |
| 838 |
| 839 void BoxBorderPainter::clipBorderSideForComplexInnerPath(GraphicsContext* graphi
csContext, |
| 840 const FloatRoundedRect& outerBorder, const FloatRoundedRect& innerBorder, |
| 841 BoxSide side, const BorderEdge edges[]) |
| 842 { |
| 843 graphicsContext->clip(calculateSideRectIncludingInner(outerBorder, edges, si
de)); |
| 844 FloatRoundedRect adjustedInnerRect = calculateAdjustedInnerBorder(innerBorde
r, side); |
| 845 if (!adjustedInnerRect.isEmpty()) |
| 846 graphicsContext->clipOutRoundedRect(adjustedInnerRect); |
| 847 } |
| 848 |
| 849 void BoxBorderPainter::clipBorderSidePolygon(GraphicsContext* graphicsContext, |
| 850 const FloatRoundedRect& outerBorder, const FloatRoundedRect& innerBorder, Bo
xSide side, |
| 851 bool firstEdgeMatches, bool secondEdgeMatches) |
| 852 { |
| 853 FloatPoint quad[4]; |
| 854 |
| 855 const LayoutRect outerRect(outerBorder.rect()); |
| 856 const LayoutRect innerRect(innerBorder.rect()); |
| 857 |
| 858 FloatPoint centerPoint(innerRect.location().x().toFloat() + innerRect.width(
).toFloat() / 2, innerRect.location().y().toFloat() + innerRect.height().toFloat
() / 2); |
| 859 |
| 860 // For each side, create a quad that encompasses all parts of that side that
may draw, |
| 861 // including areas inside the innerBorder. |
| 862 // |
| 863 // 0----------------3 |
| 864 // 0 \ / 0 |
| 865 // |\ 1----------- 2 /| |
| 866 // | 1 1 | |
| 867 // | | | | |
| 868 // | | | | |
| 869 // | 2 2 | |
| 870 // |/ 1------------2 \| |
| 871 // 3 / \ 3 |
| 872 // 0----------------3 |
| 873 // |
| 874 switch (side) { |
| 875 case BSTop: |
| 876 quad[0] = FloatPoint(outerRect.minXMinYCorner()); |
| 877 quad[1] = FloatPoint(innerRect.minXMinYCorner()); |
| 878 quad[2] = FloatPoint(innerRect.maxXMinYCorner()); |
| 879 quad[3] = FloatPoint(outerRect.maxXMinYCorner()); |
| 880 |
| 881 if (!innerBorder.radii().topLeft().isZero()) { |
| 882 findIntersection(quad[0], quad[1], |
| 883 FloatPoint( |
| 884 quad[1].x() + innerBorder.radii().topLeft().width(), |
| 885 quad[1].y()), |
| 886 FloatPoint( |
| 887 quad[1].x(), |
| 888 quad[1].y() + innerBorder.radii().topLeft().height()), |
| 889 quad[1]); |
| 890 } |
| 891 |
| 892 if (!innerBorder.radii().topRight().isZero()) { |
| 893 findIntersection(quad[3], quad[2], |
| 894 FloatPoint( |
| 895 quad[2].x() - innerBorder.radii().topRight().width(), |
| 896 quad[2].y()), |
| 897 FloatPoint( |
| 898 quad[2].x(), |
| 899 quad[2].y() + innerBorder.radii().topRight().height()), |
| 900 quad[2]); |
| 901 } |
| 902 break; |
| 903 |
| 904 case BSLeft: |
| 905 quad[0] = FloatPoint(outerRect.minXMinYCorner()); |
| 906 quad[1] = FloatPoint(innerRect.minXMinYCorner()); |
| 907 quad[2] = FloatPoint(innerRect.minXMaxYCorner()); |
| 908 quad[3] = FloatPoint(outerRect.minXMaxYCorner()); |
| 909 |
| 910 if (!innerBorder.radii().topLeft().isZero()) { |
| 911 findIntersection(quad[0], quad[1], |
| 912 FloatPoint( |
| 913 quad[1].x() + innerBorder.radii().topLeft().width(), |
| 914 quad[1].y()), |
| 915 FloatPoint( |
| 916 quad[1].x(), |
| 917 quad[1].y() + innerBorder.radii().topLeft().height()), |
| 918 quad[1]); |
| 919 } |
| 920 |
| 921 if (!innerBorder.radii().bottomLeft().isZero()) { |
| 922 findIntersection(quad[3], quad[2], |
| 923 FloatPoint( |
| 924 quad[2].x() + innerBorder.radii().bottomLeft().width(), |
| 925 quad[2].y()), |
| 926 FloatPoint( |
| 927 quad[2].x(), |
| 928 quad[2].y() - innerBorder.radii().bottomLeft().height()), |
| 929 quad[2]); |
| 930 } |
| 931 break; |
| 932 |
| 933 case BSBottom: |
| 934 quad[0] = FloatPoint(outerRect.minXMaxYCorner()); |
| 935 quad[1] = FloatPoint(innerRect.minXMaxYCorner()); |
| 936 quad[2] = FloatPoint(innerRect.maxXMaxYCorner()); |
| 937 quad[3] = FloatPoint(outerRect.maxXMaxYCorner()); |
| 938 |
| 939 if (!innerBorder.radii().bottomLeft().isZero()) { |
| 940 findIntersection(quad[0], quad[1], |
| 941 FloatPoint( |
| 942 quad[1].x() + innerBorder.radii().bottomLeft().width(), |
| 943 quad[1].y()), |
| 944 FloatPoint( |
| 945 quad[1].x(), |
| 946 quad[1].y() - innerBorder.radii().bottomLeft().height()), |
| 947 quad[1]); |
| 948 } |
| 949 |
| 950 if (!innerBorder.radii().bottomRight().isZero()) { |
| 951 findIntersection(quad[3], quad[2], |
| 952 FloatPoint( |
| 953 quad[2].x() - innerBorder.radii().bottomRight().width(), |
| 954 quad[2].y()), |
| 955 FloatPoint( |
| 956 quad[2].x(), |
| 957 quad[2].y() - innerBorder.radii().bottomRight().height()), |
| 958 quad[2]); |
| 959 } |
| 960 break; |
| 961 |
| 962 case BSRight: |
| 963 quad[0] = FloatPoint(outerRect.maxXMinYCorner()); |
| 964 quad[1] = FloatPoint(innerRect.maxXMinYCorner()); |
| 965 quad[2] = FloatPoint(innerRect.maxXMaxYCorner()); |
| 966 quad[3] = FloatPoint(outerRect.maxXMaxYCorner()); |
| 967 |
| 968 if (!innerBorder.radii().topRight().isZero()) { |
| 969 findIntersection(quad[0], quad[1], |
| 970 FloatPoint( |
| 971 quad[1].x() - innerBorder.radii().topRight().width(), |
| 972 quad[1].y()), |
| 973 FloatPoint( |
| 974 quad[1].x(), |
| 975 quad[1].y() + innerBorder.radii().topRight().height()), |
| 976 quad[1]); |
| 977 } |
| 978 |
| 979 if (!innerBorder.radii().bottomRight().isZero()) { |
| 980 findIntersection(quad[3], quad[2], |
| 981 FloatPoint( |
| 982 quad[2].x() - innerBorder.radii().bottomRight().width(), |
| 983 quad[2].y()), |
| 984 FloatPoint( |
| 985 quad[2].x(), |
| 986 quad[2].y() - innerBorder.radii().bottomRight().height()), |
| 987 quad[2]); |
| 988 } |
| 989 break; |
| 990 } |
| 991 |
| 992 // If the border matches both of its adjacent sides, don't anti-alias the cl
ip, and |
| 993 // if neither side matches, anti-alias the clip. |
| 994 if (firstEdgeMatches == secondEdgeMatches) { |
| 995 graphicsContext->clipPolygon(4, quad, !firstEdgeMatches); |
| 996 return; |
| 997 } |
| 998 |
| 999 // If antialiasing settings for the first edge and second edge is different, |
| 1000 // they have to be addressed separately. We do this by breaking the quad int
o |
| 1001 // two parallelograms, made by moving quad[1] and quad[2]. |
| 1002 float ax = quad[1].x() - quad[0].x(); |
| 1003 float ay = quad[1].y() - quad[0].y(); |
| 1004 float bx = quad[2].x() - quad[1].x(); |
| 1005 float by = quad[2].y() - quad[1].y(); |
| 1006 float cx = quad[3].x() - quad[2].x(); |
| 1007 float cy = quad[3].y() - quad[2].y(); |
| 1008 |
| 1009 const static float kEpsilon = 1e-2f; |
| 1010 float r1, r2; |
| 1011 if (fabsf(bx) < kEpsilon && fabsf(by) < kEpsilon) { |
| 1012 // The quad was actually a triangle. |
| 1013 r1 = r2 = 1.0f; |
| 1014 } else { |
| 1015 // Extend parallelogram a bit to hide calculation error |
| 1016 const static float kExtendFill = 1e-2f; |
| 1017 |
| 1018 r1 = (-ax * by + ay * bx) / (cx * by - cy * bx) + kExtendFill; |
| 1019 r2 = (-cx * by + cy * bx) / (ax * by - ay * bx) + kExtendFill; |
| 1020 } |
| 1021 |
| 1022 FloatPoint firstQuad[4]; |
| 1023 firstQuad[0] = quad[0]; |
| 1024 firstQuad[1] = quad[1]; |
| 1025 firstQuad[2] = FloatPoint(quad[3].x() + r2 * ax, quad[3].y() + r2 * ay); |
| 1026 firstQuad[3] = quad[3]; |
| 1027 graphicsContext->clipPolygon(4, firstQuad, !firstEdgeMatches); |
| 1028 |
| 1029 FloatPoint secondQuad[4]; |
| 1030 secondQuad[0] = quad[0]; |
| 1031 secondQuad[1] = FloatPoint(quad[0].x() - r1 * cx, quad[0].y() - r1 * cy); |
| 1032 secondQuad[2] = quad[2]; |
| 1033 secondQuad[3] = quad[3]; |
| 1034 graphicsContext->clipPolygon(4, secondQuad, !secondEdgeMatches); |
| 1035 } |
| 1036 |
| 1037 } // namespace blink |
OLD | NEW |