| OLD | NEW |
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "config.h" | 5 #include "config.h" |
| 6 #include "core/paint/BoxBorderPainter.h" | 6 #include "core/paint/BoxBorderPainter.h" |
| 7 | 7 |
| 8 #include "core/paint/BoxPainter.h" | 8 #include "core/paint/BoxPainter.h" |
| 9 #include "core/paint/PaintInfo.h" | 9 #include "core/paint/PaintInfo.h" |
| 10 #include "core/style/BorderEdge.h" | 10 #include "core/style/BorderEdge.h" |
| 11 #include "platform/graphics/GraphicsContext.h" | 11 #include "platform/graphics/GraphicsContext.h" |
| 12 #include "platform/graphics/GraphicsContextStateSaver.h" | 12 #include "platform/graphics/GraphicsContextStateSaver.h" |
| 13 #include "wtf/Vector.h" |
| 14 #include <algorithm> |
| 13 | 15 |
| 14 namespace blink { | 16 namespace blink { |
| 15 | 17 |
| 16 namespace { | 18 namespace { |
| 17 | 19 |
| 18 enum BorderEdgeFlag { | 20 enum BorderEdgeFlag { |
| 19 TopBorderEdge = 1 << BSTop, | 21 TopBorderEdge = 1 << BSTop, |
| 20 RightBorderEdge = 1 << BSRight, | 22 RightBorderEdge = 1 << BSRight, |
| 21 BottomBorderEdge = 1 << BSBottom, | 23 BottomBorderEdge = 1 << BSBottom, |
| 22 LeftBorderEdge = 1 << BSLeft, | 24 LeftBorderEdge = 1 << BSLeft, |
| (...skipping 76 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 99 return true; | 101 return true; |
| 100 | 102 |
| 101 return borderStyleHasUnmatchedColorsAtCorner(edges[side].borderStyle(), side
, adjacentSide); | 103 return borderStyleHasUnmatchedColorsAtCorner(edges[side].borderStyle(), side
, adjacentSide); |
| 102 } | 104 } |
| 103 | 105 |
| 104 inline bool borderWillArcInnerEdge(const FloatSize& firstRadius, const FloatSize
& secondRadius) | 106 inline bool borderWillArcInnerEdge(const FloatSize& firstRadius, const FloatSize
& secondRadius) |
| 105 { | 107 { |
| 106 return !firstRadius.isZero() || !secondRadius.isZero(); | 108 return !firstRadius.isZero() || !secondRadius.isZero(); |
| 107 } | 109 } |
| 108 | 110 |
| 109 // This assumes that we draw in order: top, bottom, left, right. | 111 inline bool willOverdraw(BoxSide side, EBorderStyle style, BorderEdgeFlags compl
etedEdges) |
| 110 inline bool willBeOverdrawn(BoxSide side, BoxSide adjacentSide, const BorderEdge
edges[]) | |
| 111 { | 112 { |
| 112 switch (side) { | 113 // If we're done with this side, it will obviously not overdraw any portion
of the current edge. |
| 113 case BSTop: | 114 if (includesEdge(completedEdges, side)) |
| 114 case BSBottom: | 115 return false; |
| 115 if (edges[adjacentSide].presentButInvisible()) | |
| 116 return false; | |
| 117 | 116 |
| 118 if (!edges[side].sharesColorWith(edges[adjacentSide]) && edges[adjacentS
ide].color.hasAlpha()) | 117 // The side is still to be drawn. It overdraws the current edge iff it has a
solid fill style. |
| 119 return false; | 118 return borderStyleFillsBorderArea(style); |
| 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 } | 119 } |
| 133 | 120 |
| 134 inline bool borderStylesRequireMitre(BoxSide side, BoxSide adjacentSide, EBorder
Style style, EBorderStyle adjacentStyle) | 121 inline bool borderStylesRequireMitre(BoxSide side, BoxSide adjacentSide, EBorder
Style style, EBorderStyle adjacentStyle) |
| 135 { | 122 { |
| 136 if (style == DOUBLE || adjacentStyle == DOUBLE || adjacentStyle == GROOVE ||
adjacentStyle == RIDGE) | 123 if (style == DOUBLE || adjacentStyle == DOUBLE || adjacentStyle == GROOVE ||
adjacentStyle == RIDGE) |
| 137 return true; | 124 return true; |
| 138 | 125 |
| 139 if (borderStyleIsDottedOrDashed(style) != borderStyleIsDottedOrDashed(adjace
ntStyle)) | 126 if (borderStyleIsDottedOrDashed(style) != borderStyleIsDottedOrDashed(adjace
ntStyle)) |
| 140 return true; | 127 return true; |
| 141 | 128 |
| 142 if (style != adjacentStyle) | 129 if (style != adjacentStyle) |
| 143 return true; | 130 return true; |
| 144 | 131 |
| 145 return borderStyleHasUnmatchedColorsAtCorner(style, side, adjacentSide); | 132 return borderStyleHasUnmatchedColorsAtCorner(style, side, adjacentSide); |
| 146 } | 133 } |
| 147 | 134 |
| 148 inline bool joinRequiresMitre(BoxSide side, BoxSide adjacentSide, const BorderEd
ge edges[], bool allowOverdraw) | 135 inline bool joinRequiresMitre(BoxSide side, BoxSide adjacentSide, const BorderEd
ge edges[], |
| 136 bool allowOverdraw, BorderEdgeFlags completedEdges) |
| 149 { | 137 { |
| 150 if ((edges[side].isTransparent && edges[adjacentSide].isTransparent) || !edg
es[adjacentSide].isPresent) | 138 if (!edges[adjacentSide].isPresent) |
| 151 return false; | 139 return false; |
| 152 | 140 |
| 153 if (allowOverdraw && willBeOverdrawn(side, adjacentSide, edges)) | 141 if (allowOverdraw && willOverdraw(adjacentSide, edges[adjacentSide].borderSt
yle(), completedEdges)) |
| 154 return false; | 142 return false; |
| 155 | 143 |
| 156 if (!edges[side].sharesColorWith(edges[adjacentSide])) | 144 if (!edges[side].sharesColorWith(edges[adjacentSide])) |
| 157 return true; | 145 return true; |
| 158 | 146 |
| 159 if (borderStylesRequireMitre(side, adjacentSide, edges[side].borderStyle(),
edges[adjacentSide].borderStyle())) | 147 if (borderStylesRequireMitre(side, adjacentSide, edges[side].borderStyle(),
edges[adjacentSide].borderStyle())) |
| 160 return true; | 148 return true; |
| 161 | 149 |
| 162 return false; | 150 return false; |
| 163 } | 151 } |
| (...skipping 189 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 353 context->fillDRRect(outer, inner, color); | 341 context->fillDRRect(outer, inner, color); |
| 354 break; | 342 break; |
| 355 } | 343 } |
| 356 } | 344 } |
| 357 | 345 |
| 358 bool bleedAvoidanceIsClipping(BackgroundBleedAvoidance bleedAvoidance) | 346 bool bleedAvoidanceIsClipping(BackgroundBleedAvoidance bleedAvoidance) |
| 359 { | 347 { |
| 360 return bleedAvoidance == BackgroundBleedClipOnly || bleedAvoidance == Backgr
oundBleedClipLayer; | 348 return bleedAvoidance == BackgroundBleedClipOnly || bleedAvoidance == Backgr
oundBleedClipLayer; |
| 361 } | 349 } |
| 362 | 350 |
| 351 // The LUTs below assume specific enum values. |
| 352 static_assert(BNONE == 0, "unexpected EBorderStyle value"); |
| 353 static_assert(BHIDDEN == 1, "unexpected EBorderStyle value"); |
| 354 static_assert(INSET == 2, "unexpected EBorderStyle value"); |
| 355 static_assert(GROOVE == 3, "unexpected EBorderStyle value"); |
| 356 static_assert(OUTSET == 4, "unexpected EBorderStyle value"); |
| 357 static_assert(RIDGE == 5, "unexpected EBorderStyle value"); |
| 358 static_assert(DOTTED == 6, "unexpected EBorderStyle value"); |
| 359 static_assert(DASHED == 7, "unexpected EBorderStyle value"); |
| 360 static_assert(SOLID == 8, "unexpected EBorderStyle value"); |
| 361 static_assert(DOUBLE == 9, "unexpected EBorderStyle value"); |
| 362 |
| 363 static_assert(BSTop == 0, "unexpected BoxSide value"); |
| 364 static_assert(BSRight == 1, "unexpected BoxSide value"); |
| 365 static_assert(BSBottom == 2, "unexpected BoxSide value"); |
| 366 static_assert(BSLeft == 3, "unexpected BoxSide value"); |
| 367 |
| 368 // Style-based paint order: non-solid edges (dashed/dotted/double) are painted b
efore |
| 369 // solid edges (inset/outset/groove/ridge/solid) to maximize overdraw opportunit
ies. |
| 370 const unsigned kStylePriority[] = { |
| 371 0 /* BNONE */, |
| 372 0 /* BHIDDEN */, |
| 373 2 /* INSET */, |
| 374 2 /* GROOVE */, |
| 375 2 /* OUTSET */, |
| 376 2 /* RIDGE */, |
| 377 1 /* DOTTED */, |
| 378 1 /* DASHED */, |
| 379 3 /* SOLID */, |
| 380 1 /* DOUBLE */ |
| 381 }; |
| 382 |
| 383 // Given the same style, prefer drawing in non-adjacent order to minimize the nu
mber of sides |
| 384 // which require mitres. |
| 385 const unsigned kSidePriority[] = { |
| 386 0, /* BSTop */ |
| 387 2, /* BSRight */ |
| 388 1, /* BSBottom */ |
| 389 3, /* BSLeft */ |
| 390 }; |
| 391 |
| 392 // Edges sharing the same opacity. Stores both a side list and an edge bitfield
to support |
| 393 // constant time iteration + membership tests. |
| 394 struct OpacityGroup { |
| 395 OpacityGroup(unsigned alpha) : edgeFlags(0), alpha(alpha) { } |
| 396 |
| 397 Vector<BoxSide, 4> sides; |
| 398 BorderEdgeFlags edgeFlags; |
| 399 unsigned alpha; |
| 400 }; |
| 401 |
| 363 } // anonymous namespace | 402 } // anonymous namespace |
| 364 | 403 |
| 404 // Holds edges grouped by opacity and sorted in paint order. |
| 405 struct BoxBorderPainter::ComplexBorderInfo { |
| 406 ComplexBorderInfo(const BoxBorderPainter& borderPainter, bool antiAlias) |
| 407 : antiAlias(antiAlias) |
| 408 { |
| 409 Vector<BoxSide, 4> sortedSides; |
| 410 |
| 411 // First, collect all visible sides. |
| 412 for (unsigned i = borderPainter.m_firstVisibleEdge; i < 4; ++i) { |
| 413 BoxSide side = static_cast<BoxSide>(i); |
| 414 |
| 415 if (includesEdge(borderPainter.m_visibleEdgeSet, side)) |
| 416 sortedSides.append(side); |
| 417 } |
| 418 ASSERT(!sortedSides.isEmpty()); |
| 419 |
| 420 // Then sort them in paint order, based on three (prioritized) criteria:
alpha, style, side. |
| 421 std::sort(sortedSides.begin(), sortedSides.end(), |
| 422 [&borderPainter] (BoxSide a, BoxSide b) -> bool { |
| 423 const BorderEdge& edgeA = borderPainter.m_edges[a]; |
| 424 const BorderEdge& edgeB = borderPainter.m_edges[b]; |
| 425 |
| 426 const unsigned alphaA = edgeA.color.alpha(); |
| 427 const unsigned alphaB = edgeB.color.alpha(); |
| 428 if (alphaA != alphaB) |
| 429 return alphaA < alphaB; |
| 430 |
| 431 const unsigned stylePriorityA = kStylePriority[edgeA.borderStyle()]; |
| 432 const unsigned stylePriorityB = kStylePriority[edgeB.borderStyle()]; |
| 433 if (stylePriorityA != stylePriorityB) |
| 434 return stylePriorityA < stylePriorityB; |
| 435 |
| 436 return kSidePriority[a] < kSidePriority[b]; |
| 437 }); |
| 438 |
| 439 // Finally, build the opacity group structures. |
| 440 buildOpacityGroups(borderPainter, sortedSides); |
| 441 |
| 442 if (borderPainter.m_isRounded) |
| 443 roundedBorderPath.addRoundedRect(borderPainter.m_outer); |
| 444 } |
| 445 |
| 446 Vector<OpacityGroup, 4> opacityGroups; |
| 447 |
| 448 // Potentially used when drawing rounded borders. |
| 449 Path roundedBorderPath; |
| 450 |
| 451 bool antiAlias; |
| 452 |
| 453 private: |
| 454 void buildOpacityGroups(const BoxBorderPainter& borderPainter, |
| 455 const Vector<BoxSide, 4>& sortedSides) |
| 456 { |
| 457 unsigned currentAlpha = 0; |
| 458 for (BoxSide side : sortedSides) { |
| 459 const BorderEdge& edge = borderPainter.m_edges[side]; |
| 460 const unsigned edgeAlpha = edge.color.alpha(); |
| 461 |
| 462 ASSERT(edgeAlpha > 0); |
| 463 ASSERT(edgeAlpha >= currentAlpha); |
| 464 if (edgeAlpha != currentAlpha) { |
| 465 opacityGroups.append(OpacityGroup(edgeAlpha)); |
| 466 currentAlpha = edgeAlpha; |
| 467 } |
| 468 |
| 469 ASSERT(!opacityGroups.isEmpty()); |
| 470 OpacityGroup& currentGroup = opacityGroups.last(); |
| 471 currentGroup.sides.append(side); |
| 472 currentGroup.edgeFlags |= edgeFlagForSide(side); |
| 473 } |
| 474 |
| 475 ASSERT(!opacityGroups.isEmpty()); |
| 476 } |
| 477 }; |
| 478 |
| 365 void BoxBorderPainter::drawDoubleBorder(GraphicsContext* context, const LayoutRe
ct& borderRect) const | 479 void BoxBorderPainter::drawDoubleBorder(GraphicsContext* context, const LayoutRe
ct& borderRect) const |
| 366 { | 480 { |
| 367 ASSERT(m_isUniformColor); | 481 ASSERT(m_isUniformColor); |
| 368 ASSERT(m_isUniformStyle); | 482 ASSERT(m_isUniformStyle); |
| 369 ASSERT(firstEdge().borderStyle() == DOUBLE); | 483 ASSERT(firstEdge().borderStyle() == DOUBLE); |
| 370 ASSERT(m_visibleEdgeSet == AllBorderEdges); | 484 ASSERT(m_visibleEdgeSet == AllBorderEdges); |
| 371 | 485 |
| 372 const Color color = firstEdge().color; | 486 const Color color = firstEdge().color; |
| 373 | 487 |
| 374 // outer stripe | 488 // outer stripe |
| (...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 440 : m_style(style) | 554 : m_style(style) |
| 441 , m_bleedAvoidance(bleedAvoidance) | 555 , m_bleedAvoidance(bleedAvoidance) |
| 442 , m_includeLogicalLeftEdge(includeLogicalLeftEdge) | 556 , m_includeLogicalLeftEdge(includeLogicalLeftEdge) |
| 443 , m_includeLogicalRightEdge(includeLogicalRightEdge) | 557 , m_includeLogicalRightEdge(includeLogicalRightEdge) |
| 444 , m_visibleEdgeCount(0) | 558 , m_visibleEdgeCount(0) |
| 445 , m_firstVisibleEdge(0) | 559 , m_firstVisibleEdge(0) |
| 446 , m_visibleEdgeSet(0) | 560 , m_visibleEdgeSet(0) |
| 447 , m_isUniformStyle(true) | 561 , m_isUniformStyle(true) |
| 448 , m_isUniformWidth(true) | 562 , m_isUniformWidth(true) |
| 449 , m_isUniformColor(true) | 563 , m_isUniformColor(true) |
| 564 , m_isRounded(false) |
| 450 , m_hasAlpha(false) | 565 , m_hasAlpha(false) |
| 451 { | 566 { |
| 452 style.getBorderEdgeInfo(m_edges, includeLogicalLeftEdge, includeLogicalRight
Edge); | 567 style.getBorderEdgeInfo(m_edges, includeLogicalLeftEdge, includeLogicalRight
Edge); |
| 453 | 568 |
| 454 for (unsigned i = 0; i < WTF_ARRAY_LENGTH(m_edges); ++i) { | 569 for (unsigned i = 0; i < WTF_ARRAY_LENGTH(m_edges); ++i) { |
| 455 const BorderEdge& edge = m_edges[i]; | 570 const BorderEdge& edge = m_edges[i]; |
| 456 | 571 |
| 457 if (!edge.shouldRender()) { | 572 if (!edge.shouldRender()) { |
| 458 if (edge.presentButInvisible()) { | 573 if (edge.presentButInvisible()) { |
| 459 m_isUniformWidth = false; | 574 m_isUniformWidth = false; |
| (...skipping 26 matching lines...) Expand all Loading... |
| 486 m_inner = style.getRoundedInnerBorderFor(borderRect, includeLogicalLeftEdge,
includeLogicalRightEdge); | 601 m_inner = style.getRoundedInnerBorderFor(borderRect, includeLogicalLeftEdge,
includeLogicalRightEdge); |
| 487 | 602 |
| 488 // If no corner intersects the clip region, we can pretend the outer border
is | 603 // If no corner intersects the clip region, we can pretend the outer border
is |
| 489 // rectangular to improve performance. | 604 // rectangular to improve performance. |
| 490 // FIXME: why is this predicated on uniform style & solid edges? | 605 // FIXME: why is this predicated on uniform style & solid edges? |
| 491 if (m_isUniformStyle | 606 if (m_isUniformStyle |
| 492 && firstEdge().borderStyle() == SOLID | 607 && firstEdge().borderStyle() == SOLID |
| 493 && m_outer.isRounded() | 608 && m_outer.isRounded() |
| 494 && BoxPainter::allCornersClippedOut(m_outer, clipRect)) | 609 && BoxPainter::allCornersClippedOut(m_outer, clipRect)) |
| 495 m_outer.setRadii(FloatRoundedRect::Radii()); | 610 m_outer.setRadii(FloatRoundedRect::Radii()); |
| 611 |
| 612 m_isRounded = m_outer.isRounded(); |
| 496 } | 613 } |
| 497 | 614 |
| 498 void BoxBorderPainter::paintBorder(const PaintInfo& info, const LayoutRect& rect
) const | 615 void BoxBorderPainter::paintBorder(const PaintInfo& info, const LayoutRect& rect
) const |
| 499 { | 616 { |
| 500 if (!m_visibleEdgeCount || m_outer.rect().isEmpty()) | 617 if (!m_visibleEdgeCount || m_outer.rect().isEmpty()) |
| 501 return; | 618 return; |
| 502 | 619 |
| 503 GraphicsContext* graphicsContext = info.context; | 620 GraphicsContext* graphicsContext = info.context; |
| 504 | 621 |
| 505 if (paintBorderFastPath(graphicsContext, rect)) | 622 if (paintBorderFastPath(graphicsContext, rect)) |
| 506 return; | 623 return; |
| 507 | 624 |
| 508 bool clipToOuterBorder = m_outer.isRounded(); | 625 bool clipToOuterBorder = m_outer.isRounded(); |
| 509 GraphicsContextStateSaver stateSaver(*graphicsContext, clipToOuterBorder); | 626 GraphicsContextStateSaver stateSaver(*graphicsContext, clipToOuterBorder); |
| 510 if (clipToOuterBorder) { | 627 if (clipToOuterBorder) { |
| 511 // For BackgroundBleedClip{Only,Layer}, the outer rrect clip is already
applied. | 628 // For BackgroundBleedClip{Only,Layer}, the outer rrect clip is already
applied. |
| 512 if (!bleedAvoidanceIsClipping(m_bleedAvoidance)) | 629 if (!bleedAvoidanceIsClipping(m_bleedAvoidance)) |
| 513 graphicsContext->clipRoundedRect(m_outer); | 630 graphicsContext->clipRoundedRect(m_outer); |
| 514 | 631 |
| 515 // For BackgroundBleedBackgroundOverBorder, we're going to draw an opaqu
e background over | 632 // For BackgroundBleedBackgroundOverBorder, we're going to draw an opaqu
e background over |
| 516 // the inner rrect - so clipping is not needed (nor desirable due to bac
kdrop bleeding). | 633 // the inner rrect - so clipping is not needed (nor desirable due to bac
kdrop bleeding). |
| 517 if (m_bleedAvoidance != BackgroundBleedBackgroundOverBorder && m_inner.i
sRenderable() && !m_inner.isEmpty()) | 634 if (m_bleedAvoidance != BackgroundBleedBackgroundOverBorder && m_inner.i
sRenderable() && !m_inner.isEmpty()) |
| 518 graphicsContext->clipOutRoundedRect(m_inner); | 635 graphicsContext->clipOutRoundedRect(m_inner); |
| 519 } | 636 } |
| 520 | 637 |
| 521 // If only one edge visible antialiasing doesn't create seams | 638 // If only one edge visible antialiasing doesn't create seams |
| 522 bool antialias = BoxPainter::shouldAntialiasLines(graphicsContext) || m_visi
bleEdgeCount == 1; | 639 bool antialias = BoxPainter::shouldAntialiasLines(graphicsContext) || m_visi
bleEdgeCount == 1; |
| 523 if (m_hasAlpha) { | 640 |
| 524 paintTranslucentBorderSides(graphicsContext, antialias); | 641 const ComplexBorderInfo borderInfo(*this, antialias); |
| 525 } else { | 642 paintOpacityGroup(graphicsContext, borderInfo, 0, 1); |
| 526 paintBorderSides(graphicsContext, m_visibleEdgeSet, antialias); | |
| 527 } | |
| 528 } | 643 } |
| 529 | 644 |
| 530 void BoxBorderPainter::paintTranslucentBorderSides(GraphicsContext* graphicsCont
ext, | 645 // In order to maximize the use of overdraw as a corner seam avoidance technique
, we draw |
| 531 bool antialias) const | 646 // translucent border sides using the following algorithm: |
| 647 // |
| 648 // 1) cluster sides sharing the same opacity into "opacity groups" [ComplexBor
derInfo] |
| 649 // 2) sort groups in increasing opacity order [ComplexBorderInfo] |
| 650 // 3) reverse-iterate over groups (decreasing opacity order), pushing nested t
ransparency |
| 651 // layers with adjusted/relative opacity [paintOpacityGroup] |
| 652 // 4) iterate over groups (increasing opacity order), painting actual group co
ntents and |
| 653 // then ending their corresponding transparency layer [paintOpacityGroup] |
| 654 // |
| 655 // Layers are created in decreasing opacity order (top -> bottom), while actual
border sides are |
| 656 // drawn in increasing opacity order (bottom -> top). At each level, opacity is
adjusted to acount |
| 657 // for accumulated/ancestor layer alpha. Because opacity is applied via layers,
the actual draw |
| 658 // paint is opaque. |
| 659 // |
| 660 // As an example, let's consider a border with the following sides/opacities: |
| 661 // |
| 662 // top: 1.0 |
| 663 // right: 0.25 |
| 664 // bottom: 0.5 |
| 665 // left: 0.25 |
| 666 // |
| 667 // These are grouped and sorted in ComplexBorderInfo as follows: |
| 668 // |
| 669 // group[0]: { alpha: 1.0, sides: top } |
| 670 // group[1]: { alpha: 0.5, sides: bottom } |
| 671 // group[2]: { alpha: 0.25, sides: right, left } |
| 672 // |
| 673 // Applying the algorithm yields the following paint sequence: |
| 674 // |
| 675 // // no layer needed for group 0 (alpha == 1) |
| 676 // beginLayer(0.5) // layer for group 1 |
| 677 // beginLayer(0.5) // layer for group 2 (effective opacity: 0.5
* 0.5 == 0.25) |
| 678 // paintSides(right, left) // paint group 2 |
| 679 // endLayer |
| 680 // paintSides(bottom) // paint group 1 |
| 681 // endLayer |
| 682 // paintSides(top) // paint group 0 |
| 683 // |
| 684 // Note that we're always drawing using opaque paints on top of less-opaque cont
ent - hence |
| 685 // we can use overdraw to mask portions of the previous sides. |
| 686 // |
| 687 BorderEdgeFlags BoxBorderPainter::paintOpacityGroup(GraphicsContext* context, |
| 688 const ComplexBorderInfo& borderInfo, unsigned index, float effectiveOpacity)
const |
| 532 { | 689 { |
| 533 // willBeOverdrawn assumes that we draw in order: top, bottom, left, right. | 690 ASSERT(effectiveOpacity > 0 && effectiveOpacity <= 1); |
| 534 // This is different from BoxSide enum order. | |
| 535 static const BoxSide paintOrder[] = { BSTop, BSBottom, BSLeft, BSRight }; | |
| 536 | 691 |
| 537 BorderEdgeFlags edgesToDraw = m_visibleEdgeSet; | 692 const size_t opacityGroupCount = borderInfo.opacityGroups.size(); |
| 538 while (edgesToDraw) { | |
| 539 // Find undrawn edges sharing a color. | |
| 540 Color commonColor; | |
| 541 | 693 |
| 542 BorderEdgeFlags commonColorEdgeSet = 0; | 694 // For overdraw logic purposes, treat missing/transparent edges as completed
. |
| 543 for (size_t i = 0; i < sizeof(paintOrder) / sizeof(paintOrder[0]); ++i)
{ | 695 if (index >= opacityGroupCount) |
| 544 BoxSide currSide = paintOrder[i]; | 696 return ~m_visibleEdgeSet; |
| 545 if (!includesEdge(edgesToDraw, currSide)) | |
| 546 continue; | |
| 547 | 697 |
| 548 bool includeEdge; | 698 // Groups are sorted in increasing opacity order, but we need to create laye
rs in |
| 549 if (!commonColorEdgeSet) { | 699 // decreasing opacity order - hence the reverse iteration. |
| 550 commonColor = m_edges[currSide].color; | 700 const OpacityGroup& group = borderInfo.opacityGroups[opacityGroupCount - ind
ex - 1]; |
| 551 includeEdge = true; | |
| 552 } else { | |
| 553 includeEdge = m_edges[currSide].color == commonColor; | |
| 554 } | |
| 555 | 701 |
| 556 if (includeEdge) | 702 // Adjust this group's paint opacity to account for ancestor transparency la
yers |
| 557 commonColorEdgeSet |= edgeFlagForSide(currSide); | 703 // (needed in case we avoid creating a layer below). |
| 558 } | 704 unsigned paintAlpha = group.alpha / effectiveOpacity; |
| 705 ASSERT(paintAlpha <= 255); |
| 559 | 706 |
| 560 bool useTransparencyLayer = includesAdjacentEdges(commonColorEdgeSet) &&
commonColor.hasAlpha(); | 707 // For the last (bottom) group, we can skip the layer even in the presence o
f opacity iff |
| 561 if (useTransparencyLayer) { | 708 // it contains no adjecent edges (no in-group overdraw possibility). |
| 562 graphicsContext->beginLayer(static_cast<float>(commonColor.alpha())
/ 255); | 709 bool needsLayer = group.alpha != 255 |
| 563 commonColor = Color(commonColor.red(), commonColor.green(), commonCo
lor.blue()); | 710 && (includesAdjacentEdges(group.edgeFlags) || (index + 1 < borderInfo.op
acityGroups.size())); |
| 564 } | |
| 565 | 711 |
| 566 paintBorderSides(graphicsContext, commonColorEdgeSet, antialias, &common
Color); | 712 if (needsLayer) { |
| 713 const float groupOpacity = static_cast<float>(group.alpha) / 255; |
| 714 ASSERT(groupOpacity < effectiveOpacity); |
| 567 | 715 |
| 568 if (useTransparencyLayer) | 716 context->beginLayer(groupOpacity / effectiveOpacity); |
| 569 graphicsContext->endLayer(); | 717 effectiveOpacity = groupOpacity; |
| 570 | 718 |
| 571 edgesToDraw &= ~commonColorEdgeSet; | 719 // Group opacity is applied via a layer => we draw the members using opa
que paint. |
| 720 paintAlpha = 255; |
| 721 } |
| 722 |
| 723 // Recursion may seem unpalatable here, but |
| 724 // a) it has an upper bound of 4 |
| 725 // b) only triggers at all when mixing border sides with different opaciti
es |
| 726 // c) it allows us to express the layer nesting algorithm more naturally |
| 727 BorderEdgeFlags completedEdges = paintOpacityGroup(context, borderInfo, inde
x + 1, effectiveOpacity); |
| 728 |
| 729 // Paint the actual group edges with an alpha adjusted to account for ancens
tor layers opacity. |
| 730 for (BoxSide side : group.sides) { |
| 731 paintSide(context, borderInfo, side, paintAlpha, completedEdges); |
| 732 completedEdges |= edgeFlagForSide(side); |
| 733 } |
| 734 |
| 735 if (needsLayer) |
| 736 context->endLayer(); |
| 737 |
| 738 return completedEdges; |
| 739 } |
| 740 |
| 741 void BoxBorderPainter::paintSide(GraphicsContext* context, const ComplexBorderIn
fo& borderInfo, |
| 742 BoxSide side, unsigned alpha, BorderEdgeFlags completedEdges) const |
| 743 { |
| 744 const BorderEdge& edge = m_edges[side]; |
| 745 ASSERT(edge.shouldRender()); |
| 746 const Color color(edge.color.red(), edge.color.green(), edge.color.blue(), a
lpha); |
| 747 |
| 748 FloatRect sideRect = m_outer.rect(); |
| 749 const Path* path = nullptr; |
| 750 |
| 751 // TODO(fmalita): find a way to consolidate these without sacrificing readab
ility. |
| 752 switch (side) { |
| 753 case BSTop: { |
| 754 bool usePath = m_isRounded && (borderStyleHasInnerDetail(edge.borderStyl
e()) |
| 755 || borderWillArcInnerEdge(m_inner.radii().topLeft(), m_inner.radii()
.topRight())); |
| 756 if (usePath) |
| 757 path = &borderInfo.roundedBorderPath; |
| 758 else |
| 759 sideRect.setHeight(edge.width); |
| 760 |
| 761 paintOneBorderSide(context, sideRect, BSTop, BSLeft, BSRight, path, bord
erInfo.antiAlias, |
| 762 color, completedEdges); |
| 763 break; |
| 764 } |
| 765 case BSBottom: { |
| 766 bool usePath = m_isRounded && (borderStyleHasInnerDetail(edge.borderStyl
e()) |
| 767 || borderWillArcInnerEdge(m_inner.radii().bottomLeft(), m_inner.radi
i().bottomRight())); |
| 768 if (usePath) |
| 769 path = &borderInfo.roundedBorderPath; |
| 770 else |
| 771 sideRect.shiftYEdgeTo(sideRect.maxY() - edge.width); |
| 772 |
| 773 paintOneBorderSide(context, sideRect, BSBottom, BSLeft, BSRight, path, b
orderInfo.antiAlias, |
| 774 color, completedEdges); |
| 775 break; |
| 776 } |
| 777 case BSLeft: { |
| 778 bool usePath = m_isRounded && (borderStyleHasInnerDetail(edge.borderStyl
e()) |
| 779 || borderWillArcInnerEdge(m_inner.radii().bottomLeft(), m_inner.radi
i().topLeft())); |
| 780 if (usePath) |
| 781 path = &borderInfo.roundedBorderPath; |
| 782 else |
| 783 sideRect.setWidth(edge.width); |
| 784 |
| 785 paintOneBorderSide(context, sideRect, BSLeft, BSTop, BSBottom, path, bor
derInfo.antiAlias, |
| 786 color, completedEdges); |
| 787 break; |
| 788 } |
| 789 case BSRight: { |
| 790 bool usePath = m_isRounded && (borderStyleHasInnerDetail(edge.borderStyl
e()) |
| 791 || borderWillArcInnerEdge(m_inner.radii().bottomRight(), m_inner.rad
ii().topRight())); |
| 792 if (usePath) |
| 793 path = &borderInfo.roundedBorderPath; |
| 794 else |
| 795 sideRect.shiftXEdgeTo(sideRect.maxX() - edge.width); |
| 796 |
| 797 paintOneBorderSide(context, sideRect, BSRight, BSTop, BSBottom, path, bo
rderInfo.antiAlias, |
| 798 color, completedEdges); |
| 799 break; |
| 800 } |
| 801 default: |
| 802 ASSERT_NOT_REACHED(); |
| 572 } | 803 } |
| 573 } | 804 } |
| 574 | 805 |
| 575 void BoxBorderPainter::paintOneBorderSide(GraphicsContext* graphicsContext, | 806 void BoxBorderPainter::paintOneBorderSide(GraphicsContext* graphicsContext, |
| 576 const FloatRect& sideRect, BoxSide side, BoxSide adjacentSide1, BoxSide adja
centSide2, | 807 const FloatRect& sideRect, BoxSide side, BoxSide adjacentSide1, BoxSide adja
centSide2, |
| 577 const Path* path, bool antialias, const Color* overrideColor) const | 808 const Path* path, bool antialias, Color color, BorderEdgeFlags completedEdge
s) const |
| 578 { | 809 { |
| 579 const BorderEdge& edgeToRender = m_edges[side]; | 810 const BorderEdge& edgeToRender = m_edges[side]; |
| 580 ASSERT(edgeToRender.width); | 811 ASSERT(edgeToRender.width); |
| 581 const BorderEdge& adjacentEdge1 = m_edges[adjacentSide1]; | 812 const BorderEdge& adjacentEdge1 = m_edges[adjacentSide1]; |
| 582 const BorderEdge& adjacentEdge2 = m_edges[adjacentSide2]; | 813 const BorderEdge& adjacentEdge2 = m_edges[adjacentSide2]; |
| 583 | 814 |
| 584 const Color& colorToPaint = overrideColor ? *overrideColor : edgeToRender.co
lor; | |
| 585 | |
| 586 if (path) { | 815 if (path) { |
| 587 bool adjacentSide1StylesMatch = colorsMatchAtCorner(side, adjacentSide1,
m_edges); | 816 bool adjacentSide1StylesMatch = colorsMatchAtCorner(side, adjacentSide1,
m_edges); |
| 588 bool adjacentSide2StylesMatch = colorsMatchAtCorner(side, adjacentSide2,
m_edges); | 817 bool adjacentSide2StylesMatch = colorsMatchAtCorner(side, adjacentSide2,
m_edges); |
| 589 | 818 |
| 590 GraphicsContextStateSaver stateSaver(*graphicsContext); | 819 GraphicsContextStateSaver stateSaver(*graphicsContext); |
| 591 if (m_inner.isRenderable()) | 820 if (m_inner.isRenderable()) |
| 592 clipBorderSidePolygon(graphicsContext, side, adjacentSide1StylesMatc
h, adjacentSide2StylesMatch); | 821 clipBorderSidePolygon(graphicsContext, side, adjacentSide1StylesMatc
h, adjacentSide2StylesMatch); |
| 593 else | 822 else |
| 594 clipBorderSideForComplexInnerPath(graphicsContext, side); | 823 clipBorderSideForComplexInnerPath(graphicsContext, side); |
| 595 float thickness = std::max(std::max(edgeToRender.width, adjacentEdge1.wi
dth), adjacentEdge2.width); | 824 float thickness = std::max(std::max(edgeToRender.width, adjacentEdge1.wi
dth), adjacentEdge2.width); |
| 596 drawBoxSideFromPath(graphicsContext, LayoutRect(m_outer.rect()), *path,
edgeToRender.width, | 825 drawBoxSideFromPath(graphicsContext, LayoutRect(m_outer.rect()), *path,
edgeToRender.width, |
| 597 thickness, side, colorToPaint, edgeToRender.borderStyle()); | 826 thickness, side, color, edgeToRender.borderStyle()); |
| 598 } else { | 827 } else { |
| 599 bool mitreAdjacentSide1 = joinRequiresMitre(side, adjacentSide1, m_edges
, !antialias); | 828 bool mitreAdjacentSide1 = joinRequiresMitre(side, adjacentSide1, m_edges
, !antialias, completedEdges); |
| 600 bool mitreAdjacentSide2 = joinRequiresMitre(side, adjacentSide2, m_edges
, !antialias); | 829 bool mitreAdjacentSide2 = joinRequiresMitre(side, adjacentSide2, m_edges
, !antialias, completedEdges); |
| 601 | 830 |
| 602 bool clipForStyle = styleRequiresClipPolygon(edgeToRender.borderStyle()) | 831 bool clipForStyle = styleRequiresClipPolygon(edgeToRender.borderStyle()) |
| 603 && (mitreAdjacentSide1 || mitreAdjacentSide2); | 832 && (mitreAdjacentSide1 || mitreAdjacentSide2); |
| 604 bool clipAdjacentSide1 = colorNeedsAntiAliasAtCorner(side, adjacentSide1
, m_edges) && mitreAdjacentSide1; | 833 bool clipAdjacentSide1 = colorNeedsAntiAliasAtCorner(side, adjacentSide1
, m_edges) && mitreAdjacentSide1; |
| 605 bool clipAdjacentSide2 = colorNeedsAntiAliasAtCorner(side, adjacentSide2
, m_edges) && mitreAdjacentSide2; | 834 bool clipAdjacentSide2 = colorNeedsAntiAliasAtCorner(side, adjacentSide2
, m_edges) && mitreAdjacentSide2; |
| 606 bool shouldClip = clipForStyle || clipAdjacentSide1 || clipAdjacentSide2
; | 835 bool shouldClip = clipForStyle || clipAdjacentSide1 || clipAdjacentSide2
; |
| 607 | 836 |
| 608 GraphicsContextStateSaver clipStateSaver(*graphicsContext, shouldClip); | 837 GraphicsContextStateSaver clipStateSaver(*graphicsContext, shouldClip); |
| 609 if (shouldClip) { | 838 if (shouldClip) { |
| 610 bool aliasAdjacentSide1 = clipAdjacentSide1 || (clipForStyle && mitr
eAdjacentSide1); | 839 bool aliasAdjacentSide1 = clipAdjacentSide1 || (clipForStyle && mitr
eAdjacentSide1); |
| 611 bool aliasAdjacentSide2 = clipAdjacentSide2 || (clipForStyle && mitr
eAdjacentSide2); | 840 bool aliasAdjacentSide2 = clipAdjacentSide2 || (clipForStyle && mitr
eAdjacentSide2); |
| 612 clipBorderSidePolygon(graphicsContext, side, !aliasAdjacentSide1, !a
liasAdjacentSide2); | 841 clipBorderSidePolygon(graphicsContext, side, !aliasAdjacentSide1, !a
liasAdjacentSide2); |
| 613 // Since we clipped, no need to draw with a mitre. | 842 // Since we clipped, no need to draw with a mitre. |
| 614 mitreAdjacentSide1 = false; | 843 mitreAdjacentSide1 = false; |
| 615 mitreAdjacentSide2 = false; | 844 mitreAdjacentSide2 = false; |
| 616 } | 845 } |
| 617 | 846 |
| 618 ObjectPainter::drawLineForBoxSide(graphicsContext, sideRect.x(), sideRec
t.y(), | 847 ObjectPainter::drawLineForBoxSide(graphicsContext, sideRect.x(), sideRec
t.y(), |
| 619 sideRect.maxX(), sideRect.maxY(), side, colorToPaint, edgeToRender.b
orderStyle(), | 848 sideRect.maxX(), sideRect.maxY(), side, color, edgeToRender.borderSt
yle(), |
| 620 mitreAdjacentSide1 ? adjacentEdge1.width : 0, mitreAdjacentSide2 ? a
djacentEdge2.width : 0, antialias); | 849 mitreAdjacentSide1 ? adjacentEdge1.width : 0, mitreAdjacentSide2 ? a
djacentEdge2.width : 0, antialias); |
| 621 } | 850 } |
| 622 } | 851 } |
| 623 | 852 |
| 624 void BoxBorderPainter::paintBorderSides(GraphicsContext* graphicsContext, Border
EdgeFlags edgeSet, | |
| 625 bool antialias, const Color* overrideColor) const | |
| 626 { | |
| 627 bool renderRadii = m_outer.isRounded(); | |
| 628 | |
| 629 Path roundedPath; | |
| 630 if (renderRadii) | |
| 631 roundedPath.addRoundedRect(m_outer); | |
| 632 | |
| 633 // The inner border adjustment for bleed avoidance mode BackgroundBleedBackg
roundOverBorder | |
| 634 // is only applied to sideRect, which is okay since BackgroundBleedBackgroun
dOverBorder | |
| 635 // is only to be used for solid borders and the shape of the border painted
by drawBoxSideFromPath | |
| 636 // only depends on sideRect when painting solid borders. | |
| 637 | |
| 638 if (includesEdge(edgeSet, BSTop)) { | |
| 639 const BorderEdge& edge = m_edges[BSTop]; | |
| 640 ASSERT(edge.shouldRender()); | |
| 641 | |
| 642 FloatRect sideRect = m_outer.rect(); | |
| 643 sideRect.setHeight(edge.width); | |
| 644 | |
| 645 bool usePath = renderRadii && (borderStyleHasInnerDetail(edge.borderStyl
e()) | |
| 646 || borderWillArcInnerEdge(m_inner.radii().topLeft(), m_inner.radii()
.topRight())); | |
| 647 paintOneBorderSide(graphicsContext, sideRect, BSTop, BSLeft, BSRight, | |
| 648 usePath ? &roundedPath : 0, antialias, overrideColor); | |
| 649 } | |
| 650 | |
| 651 if (includesEdge(edgeSet, BSBottom)) { | |
| 652 const BorderEdge& edge = m_edges[BSBottom]; | |
| 653 ASSERT(edge.shouldRender()); | |
| 654 | |
| 655 FloatRect sideRect = m_outer.rect(); | |
| 656 sideRect.shiftYEdgeTo(sideRect.maxY() - edge.width); | |
| 657 | |
| 658 bool usePath = renderRadii && (borderStyleHasInnerDetail(edge.borderStyl
e()) | |
| 659 || borderWillArcInnerEdge(m_inner.radii().bottomLeft(), m_inner.radi
i().bottomRight())); | |
| 660 paintOneBorderSide(graphicsContext, sideRect, BSBottom, BSLeft, BSRight, | |
| 661 usePath ? &roundedPath : 0, antialias, overrideColor); | |
| 662 } | |
| 663 | |
| 664 if (includesEdge(edgeSet, BSLeft)) { | |
| 665 const BorderEdge& edge = m_edges[BSLeft]; | |
| 666 ASSERT(edge.shouldRender()); | |
| 667 | |
| 668 FloatRect sideRect = m_outer.rect(); | |
| 669 sideRect.setWidth(edge.width); | |
| 670 | |
| 671 bool usePath = renderRadii && (borderStyleHasInnerDetail(edge.borderStyl
e()) | |
| 672 || borderWillArcInnerEdge(m_inner.radii().bottomLeft(), m_inner.radi
i().topLeft())); | |
| 673 paintOneBorderSide(graphicsContext, sideRect, BSLeft, BSTop, BSBottom, | |
| 674 usePath ? &roundedPath : 0, antialias, overrideColor); | |
| 675 } | |
| 676 | |
| 677 if (includesEdge(edgeSet, BSRight)) { | |
| 678 const BorderEdge& edge = m_edges[BSRight]; | |
| 679 ASSERT(edge.shouldRender()); | |
| 680 | |
| 681 FloatRect sideRect = m_outer.rect(); | |
| 682 sideRect.shiftXEdgeTo(sideRect.maxX() - edge.width); | |
| 683 | |
| 684 bool usePath = renderRadii && (borderStyleHasInnerDetail(edge.borderStyl
e()) | |
| 685 || borderWillArcInnerEdge(m_inner.radii().bottomRight(), m_inner.rad
ii().topRight())); | |
| 686 paintOneBorderSide(graphicsContext, sideRect, BSRight, BSTop, BSBottom, | |
| 687 usePath ? &roundedPath : 0, antialias, overrideColor); | |
| 688 } | |
| 689 } | |
| 690 | |
| 691 void BoxBorderPainter::drawBoxSideFromPath(GraphicsContext* graphicsContext, | 853 void BoxBorderPainter::drawBoxSideFromPath(GraphicsContext* graphicsContext, |
| 692 const LayoutRect& borderRect, const Path& borderPath, float thickness, float
drawThickness, | 854 const LayoutRect& borderRect, const Path& borderPath, float thickness, float
drawThickness, |
| 693 BoxSide side, Color color, EBorderStyle borderStyle) const | 855 BoxSide side, Color color, EBorderStyle borderStyle) const |
| 694 { | 856 { |
| 695 if (thickness <= 0) | 857 if (thickness <= 0) |
| 696 return; | 858 return; |
| 697 | 859 |
| 698 if (borderStyle == DOUBLE && thickness < 3) | 860 if (borderStyle == DOUBLE && thickness < 3) |
| 699 borderStyle = SOLID; | 861 borderStyle = SOLID; |
| 700 | 862 |
| (...skipping 315 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1016 | 1178 |
| 1017 FloatPoint secondQuad[4]; | 1179 FloatPoint secondQuad[4]; |
| 1018 secondQuad[0] = quad[0]; | 1180 secondQuad[0] = quad[0]; |
| 1019 secondQuad[1] = FloatPoint(quad[0].x() - r1 * cx, quad[0].y() - r1 * cy); | 1181 secondQuad[1] = FloatPoint(quad[0].x() - r1 * cx, quad[0].y() - r1 * cy); |
| 1020 secondQuad[2] = quad[2]; | 1182 secondQuad[2] = quad[2]; |
| 1021 secondQuad[3] = quad[3]; | 1183 secondQuad[3] = quad[3]; |
| 1022 graphicsContext->clipPolygon(4, secondQuad, !secondEdgeMatches); | 1184 graphicsContext->clipPolygon(4, secondQuad, !secondEdgeMatches); |
| 1023 } | 1185 } |
| 1024 | 1186 |
| 1025 } // namespace blink | 1187 } // namespace blink |
| OLD | NEW |