Chromium Code Reviews| 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 Vector<BoxSide, 4> sides; | |
| 396 BorderEdgeFlags edgeFlags; | |
| 397 unsigned alpha; | |
| 398 }; | |
| 399 | |
| 363 } // anonymous namespace | 400 } // anonymous namespace |
| 364 | 401 |
| 402 // Holds edges grouped by opacity and sorted in paint order. | |
| 403 struct BoxBorderPainter::ComplexBorderInfo { | |
|
chrishtr
2015/06/03 22:12:00
All of this code is just to determine the order in
f(malita)
2015/06/04 01:54:21
Right - done (split into smaller utility functions
| |
| 404 ComplexBorderInfo(const BoxBorderPainter& borderPainter, bool antiAlias) | |
| 405 : antiAlias(antiAlias) | |
| 406 { | |
| 407 BorderEdgeFlags edgeSet = borderPainter.m_visibleEdgeSet; | |
| 408 ASSERT(edgeSet); | |
| 409 | |
| 410 // Break the visible edge set into (sorted) opacity groups. | |
| 411 while (edgeSet) { | |
| 412 opacityGroupIndices.append(opacityGroupIndices.size()); | |
| 413 opacityGroups.append(OpacityGroup()); | |
| 414 OpacityGroup& currentGroup = opacityGroups.last(); | |
| 415 BorderEdgeFlags currentSet = 0; | |
| 416 | |
| 417 for (unsigned i = borderPainter.m_firstVisibleEdge; i < 4; ++i) { | |
| 418 BoxSide side = static_cast<BoxSide>(i); | |
| 419 if (!includesEdge(edgeSet, side)) | |
| 420 continue; | |
| 421 | |
| 422 unsigned sideAlpha = borderPainter.m_edges[side].color.alpha(); | |
| 423 ASSERT(sideAlpha); | |
| 424 | |
| 425 if (currentGroup.sides.isEmpty()) | |
| 426 currentGroup.alpha = sideAlpha; | |
| 427 | |
| 428 if (sideAlpha == currentGroup.alpha) { | |
| 429 currentGroup.sides.append(side); | |
| 430 currentGroup.edgeFlags |= edgeFlagForSide(side); | |
| 431 currentSet |= edgeFlagForSide(side); | |
| 432 } | |
| 433 } | |
| 434 | |
| 435 ASSERT(currentSet); | |
| 436 ASSERT(!currentGroup.sides.isEmpty()); | |
| 437 | |
| 438 // Sort edges within the current opacity group. | |
| 439 std::sort(currentGroup.sides.begin(), currentGroup.sides.end(), | |
| 440 [&borderPainter] (BoxSide a, BoxSide b) -> bool { | |
| 441 ASSERT(a != b); | |
| 442 ASSERT(includesEdge(borderPainter.m_visibleEdgeSet, a)); | |
| 443 ASSERT(includesEdge(borderPainter.m_visibleEdgeSet, a)); | |
| 444 | |
| 445 EBorderStyle style1 = borderPainter.m_edges[a].borderStyle() ; | |
| 446 EBorderStyle style2 = borderPainter.m_edges[b].borderStyle() ; | |
| 447 ASSERT(style1 != BNONE && style1 != BHIDDEN); | |
| 448 | |
| 449 unsigned stylePriority1 = kStylePriority[style1]; | |
| 450 unsigned stylePriority2 = kStylePriority[style2]; | |
| 451 | |
| 452 // style priority takes precedence over side priority | |
| 453 if (stylePriority1 != stylePriority2) | |
| 454 return stylePriority1 < stylePriority2; | |
| 455 | |
| 456 return kSidePriority[a] < kSidePriority[b]; | |
| 457 }); | |
| 458 | |
| 459 edgeSet &= ~currentSet; | |
| 460 } | |
| 461 | |
| 462 ASSERT(!opacityGroups.isEmpty()); | |
| 463 ASSERT(opacityGroups.size() == opacityGroupIndices.size()); | |
| 464 | |
| 465 // Sort opacity groups (decreasing opacity order). | |
| 466 std::sort(opacityGroupIndices.begin(), opacityGroupIndices.end(), | |
| 467 [this] (size_t a, size_t b) | |
| 468 { | |
| 469 ASSERT(a < opacityGroups.size()); | |
| 470 ASSERT(b < opacityGroups.size()); | |
| 471 ASSERT(opacityGroups[a].alpha != opacityGroups[b].alpha); | |
| 472 return opacityGroups[a].alpha > opacityGroups[b].alpha; | |
| 473 }); | |
| 474 | |
| 475 if (borderPainter.m_isRounded) | |
| 476 roundedBorderPath.addRoundedRect(borderPainter.m_outer); | |
| 477 } | |
| 478 | |
| 479 Vector<OpacityGroup, 4> opacityGroups; | |
| 480 | |
| 481 // Used for group sorting, to avoid copying OpacityGroups | |
| 482 Vector<unsigned, 4> opacityGroupIndices; | |
| 483 | |
| 484 // Potentially used when drawing rounded borders. | |
| 485 Path roundedBorderPath; | |
| 486 | |
| 487 bool antiAlias; | |
| 488 }; | |
| 489 | |
| 365 void BoxBorderPainter::drawDoubleBorder(GraphicsContext* context, const LayoutRe ct& borderRect) const | 490 void BoxBorderPainter::drawDoubleBorder(GraphicsContext* context, const LayoutRe ct& borderRect) const |
| 366 { | 491 { |
| 367 ASSERT(m_isUniformColor); | 492 ASSERT(m_isUniformColor); |
| 368 ASSERT(m_isUniformStyle); | 493 ASSERT(m_isUniformStyle); |
| 369 ASSERT(firstEdge().borderStyle() == DOUBLE); | 494 ASSERT(firstEdge().borderStyle() == DOUBLE); |
| 370 ASSERT(m_visibleEdgeSet == AllBorderEdges); | 495 ASSERT(m_visibleEdgeSet == AllBorderEdges); |
| 371 | 496 |
| 372 const Color color = firstEdge().color; | 497 const Color color = firstEdge().color; |
| 373 | 498 |
| 374 // outer stripe | 499 // outer stripe |
| (...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 440 : m_style(style) | 565 : m_style(style) |
| 441 , m_bleedAvoidance(bleedAvoidance) | 566 , m_bleedAvoidance(bleedAvoidance) |
| 442 , m_includeLogicalLeftEdge(includeLogicalLeftEdge) | 567 , m_includeLogicalLeftEdge(includeLogicalLeftEdge) |
| 443 , m_includeLogicalRightEdge(includeLogicalRightEdge) | 568 , m_includeLogicalRightEdge(includeLogicalRightEdge) |
| 444 , m_visibleEdgeCount(0) | 569 , m_visibleEdgeCount(0) |
| 445 , m_firstVisibleEdge(0) | 570 , m_firstVisibleEdge(0) |
| 446 , m_visibleEdgeSet(0) | 571 , m_visibleEdgeSet(0) |
| 447 , m_isUniformStyle(true) | 572 , m_isUniformStyle(true) |
| 448 , m_isUniformWidth(true) | 573 , m_isUniformWidth(true) |
| 449 , m_isUniformColor(true) | 574 , m_isUniformColor(true) |
| 575 , m_isRounded(false) | |
| 450 , m_hasAlpha(false) | 576 , m_hasAlpha(false) |
| 451 { | 577 { |
| 452 style.getBorderEdgeInfo(m_edges, includeLogicalLeftEdge, includeLogicalRight Edge); | 578 style.getBorderEdgeInfo(m_edges, includeLogicalLeftEdge, includeLogicalRight Edge); |
| 453 | 579 |
| 454 for (unsigned i = 0; i < WTF_ARRAY_LENGTH(m_edges); ++i) { | 580 for (unsigned i = 0; i < WTF_ARRAY_LENGTH(m_edges); ++i) { |
| 455 const BorderEdge& edge = m_edges[i]; | 581 const BorderEdge& edge = m_edges[i]; |
| 456 | 582 |
| 457 if (!edge.shouldRender()) { | 583 if (!edge.shouldRender()) { |
| 458 if (edge.presentButInvisible()) { | 584 if (edge.presentButInvisible()) { |
| 459 m_isUniformWidth = false; | 585 m_isUniformWidth = false; |
| (...skipping 26 matching lines...) Expand all Loading... | |
| 486 m_inner = style.getRoundedInnerBorderFor(borderRect, includeLogicalLeftEdge, includeLogicalRightEdge); | 612 m_inner = style.getRoundedInnerBorderFor(borderRect, includeLogicalLeftEdge, includeLogicalRightEdge); |
| 487 | 613 |
| 488 // If no corner intersects the clip region, we can pretend the outer border is | 614 // If no corner intersects the clip region, we can pretend the outer border is |
| 489 // rectangular to improve performance. | 615 // rectangular to improve performance. |
| 490 // FIXME: why is this predicated on uniform style & solid edges? | 616 // FIXME: why is this predicated on uniform style & solid edges? |
| 491 if (m_isUniformStyle | 617 if (m_isUniformStyle |
| 492 && firstEdge().borderStyle() == SOLID | 618 && firstEdge().borderStyle() == SOLID |
| 493 && m_outer.isRounded() | 619 && m_outer.isRounded() |
| 494 && BoxPainter::allCornersClippedOut(m_outer, clipRect)) | 620 && BoxPainter::allCornersClippedOut(m_outer, clipRect)) |
| 495 m_outer.setRadii(FloatRoundedRect::Radii()); | 621 m_outer.setRadii(FloatRoundedRect::Radii()); |
| 622 | |
| 623 m_isRounded = m_outer.isRounded(); | |
| 496 } | 624 } |
| 497 | 625 |
| 498 void BoxBorderPainter::paintBorder(const PaintInfo& info, const LayoutRect& rect ) const | 626 void BoxBorderPainter::paintBorder(const PaintInfo& info, const LayoutRect& rect ) const |
| 499 { | 627 { |
| 500 if (!m_visibleEdgeCount || m_outer.rect().isEmpty()) | 628 if (!m_visibleEdgeCount || m_outer.rect().isEmpty()) |
| 501 return; | 629 return; |
| 502 | 630 |
| 503 GraphicsContext* graphicsContext = info.context; | 631 GraphicsContext* graphicsContext = info.context; |
| 504 | 632 |
| 505 if (paintBorderFastPath(graphicsContext, rect)) | 633 if (paintBorderFastPath(graphicsContext, rect)) |
| 506 return; | 634 return; |
| 507 | 635 |
| 508 bool clipToOuterBorder = m_outer.isRounded(); | 636 bool clipToOuterBorder = m_outer.isRounded(); |
| 509 GraphicsContextStateSaver stateSaver(*graphicsContext, clipToOuterBorder); | 637 GraphicsContextStateSaver stateSaver(*graphicsContext, clipToOuterBorder); |
| 510 if (clipToOuterBorder) { | 638 if (clipToOuterBorder) { |
| 511 // For BackgroundBleedClip{Only,Layer}, the outer rrect clip is already applied. | 639 // For BackgroundBleedClip{Only,Layer}, the outer rrect clip is already applied. |
| 512 if (!bleedAvoidanceIsClipping(m_bleedAvoidance)) | 640 if (!bleedAvoidanceIsClipping(m_bleedAvoidance)) |
| 513 graphicsContext->clipRoundedRect(m_outer); | 641 graphicsContext->clipRoundedRect(m_outer); |
| 514 | 642 |
| 515 // For BackgroundBleedBackgroundOverBorder, we're going to draw an opaqu e background over | 643 // 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). | 644 // 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()) | 645 if (m_bleedAvoidance != BackgroundBleedBackgroundOverBorder && m_inner.i sRenderable() && !m_inner.isEmpty()) |
| 518 graphicsContext->clipOutRoundedRect(m_inner); | 646 graphicsContext->clipOutRoundedRect(m_inner); |
| 519 } | 647 } |
| 520 | 648 |
| 521 // If only one edge visible antialiasing doesn't create seams | 649 // If only one edge visible antialiasing doesn't create seams |
| 522 bool antialias = BoxPainter::shouldAntialiasLines(graphicsContext) || m_visi bleEdgeCount == 1; | 650 bool antialias = BoxPainter::shouldAntialiasLines(graphicsContext) || m_visi bleEdgeCount == 1; |
| 523 if (m_hasAlpha) { | 651 |
| 524 paintTranslucentBorderSides(graphicsContext, antialias); | 652 const ComplexBorderInfo borderInfo(*this, antialias); |
| 525 } else { | 653 paintOpacityGroup(graphicsContext, borderInfo, 0, 1); |
| 526 paintBorderSides(graphicsContext, m_visibleEdgeSet, antialias); | |
| 527 } | |
| 528 } | 654 } |
| 529 | 655 |
| 530 void BoxBorderPainter::paintTranslucentBorderSides(GraphicsContext* graphicsCont ext, | 656 // In order to maximize the use of overdraw as a corner seam avoidance technique , we draw |
| 531 bool antialias) const | 657 // translucent border sides using the following algorithm: |
| 658 // | |
| 659 // 1) cluster sides sharing the same opacity into "opacity groups" [ComplexBor derInfo] | |
| 660 // 2) sort groups in decreasing opacity order [ComplexBorderInfo] | |
| 661 // 3) iterate over groups (decreasing opacity order), pushing nested transpare ncy layers | |
| 662 // with adjusted/relative opacity [paintOpacityGroup] | |
| 663 // 4) reverse-iterate over groups (increasing opacity order), painting actual group contents | |
| 664 // and then ending their corresponding transparency layer [paintOpacityGrou p] | |
| 665 // | |
| 666 // Layers are created in decreasing opacity order (top -> bottom), while actual border sides are | |
| 667 // drawn in increasing opacity order (bottom -> up). At each level, opacity is a djusted to acount | |
| 668 // for accumulated/ancestor layer alpha. Because opacity is applied via layers, the actual draw | |
| 669 // paint is opaque. | |
| 670 // | |
| 671 // As an example, let's consider a border with the following sides/opacities: | |
| 672 // | |
| 673 // top: 1.0 | |
| 674 // right: 0.25 | |
| 675 // bottom: 0.5 | |
| 676 // left: 0.25 | |
| 677 // | |
| 678 // These are grouped and sorted in ComplexBorderInfo as follows: | |
| 679 // | |
| 680 // group[0]: { alpha: 1.0, sides: top } | |
| 681 // group[1]: { alpha: 0.5, sides: bottom } | |
| 682 // group[2]: { alpha: 0.25, sides: right, left } | |
| 683 // | |
| 684 // Applying the algorithm yields the following paint sequence: | |
| 685 // | |
| 686 // // no layer needed for group 0 (alpha == 1) | |
| 687 // beginLayer(0.5) // layer for group 1 | |
| 688 // beginLayer(0.5) // layer for group 2 (effective opacity: 0.5 * 0.5 == 0.25) | |
| 689 // paintSides(right, left) // paint group 2 | |
| 690 // endLayer | |
| 691 // paintSides(bottom) // paint group 1 | |
| 692 // endLayer | |
| 693 // paintSides(top) // paint group 0 | |
| 694 // | |
| 695 // Note that we're always drawing using opaque paints on top of less-opaque cont ent - hence | |
| 696 // we can use overdraw to mask portions of the previous sides. | |
| 697 // | |
| 698 BorderEdgeFlags BoxBorderPainter::paintOpacityGroup(GraphicsContext* context, | |
| 699 const ComplexBorderInfo& borderInfo, unsigned index, float effectiveOpacity) const | |
| 532 { | 700 { |
| 533 // willBeOverdrawn assumes that we draw in order: top, bottom, left, right. | 701 ASSERT(effectiveOpacity > 0 && effectiveOpacity <= 1); |
| 534 // This is different from BoxSide enum order. | |
| 535 static const BoxSide paintOrder[] = { BSTop, BSBottom, BSLeft, BSRight }; | |
| 536 | 702 |
| 537 BorderEdgeFlags edgesToDraw = m_visibleEdgeSet; | 703 // For overdraw logic purposes, treat missing/transparent edges as completed . |
| 538 while (edgesToDraw) { | 704 if (index >= borderInfo.opacityGroups.size()) |
| 539 // Find undrawn edges sharing a color. | 705 return ~m_visibleEdgeSet; |
| 540 Color commonColor; | |
| 541 | 706 |
| 542 BorderEdgeFlags commonColorEdgeSet = 0; | 707 const OpacityGroup& group = borderInfo.opacityGroups[borderInfo.opacityGroup Indices[index]]; |
| 543 for (size_t i = 0; i < sizeof(paintOrder) / sizeof(paintOrder[0]); ++i) { | |
| 544 BoxSide currSide = paintOrder[i]; | |
| 545 if (!includesEdge(edgesToDraw, currSide)) | |
| 546 continue; | |
| 547 | 708 |
| 548 bool includeEdge; | 709 // Adjust this group's paint opacity to account for ancestor transparency la yers |
| 549 if (!commonColorEdgeSet) { | 710 // (needed in case we avoid creating a layer below). |
| 550 commonColor = m_edges[currSide].color; | 711 unsigned paintAlpha = group.alpha / effectiveOpacity; |
| 551 includeEdge = true; | 712 ASSERT(paintAlpha <= 255); |
| 552 } else { | |
| 553 includeEdge = m_edges[currSide].color == commonColor; | |
| 554 } | |
| 555 | 713 |
| 556 if (includeEdge) | 714 // For the last (bottom) group, we can skip the layer even in the presence o f opacity iff |
| 557 commonColorEdgeSet |= edgeFlagForSide(currSide); | 715 // it contains no adjecent edges (no in-group overdraw possibility). |
| 558 } | 716 bool needsLayer = group.alpha != 255 |
| 717 && (includesAdjacentEdges(group.edgeFlags) || (index + 1 < borderInfo.op acityGroups.size())); | |
| 559 | 718 |
| 560 bool useTransparencyLayer = includesAdjacentEdges(commonColorEdgeSet) && commonColor.hasAlpha(); | 719 if (needsLayer) { |
| 561 if (useTransparencyLayer) { | 720 const float groupOpacity = static_cast<float>(group.alpha) / 255; |
| 562 graphicsContext->beginLayer(static_cast<float>(commonColor.alpha()) / 255); | 721 ASSERT(groupOpacity < effectiveOpacity); |
| 563 commonColor = Color(commonColor.red(), commonColor.green(), commonCo lor.blue()); | |
| 564 } | |
| 565 | 722 |
| 566 paintBorderSides(graphicsContext, commonColorEdgeSet, antialias, &common Color); | 723 context->beginLayer(groupOpacity / effectiveOpacity); |
| 724 effectiveOpacity *= groupOpacity; | |
| 567 | 725 |
| 568 if (useTransparencyLayer) | 726 // Group opacity is applied via a layer => we draw the members using opa que paint. |
| 569 graphicsContext->endLayer(); | 727 paintAlpha = 255; |
| 728 } | |
| 570 | 729 |
| 571 edgesToDraw &= ~commonColorEdgeSet; | 730 // Recursion may seem unpalatable here, but |
| 731 // a) it has an upper bound of 4 | |
| 732 // b) only triggers at all when mixing border sides with different opaciti es | |
| 733 // c) it allows us to express the layer nesting algorithm more naturally | |
| 734 BorderEdgeFlags completedEdges = paintOpacityGroup(context, borderInfo, inde x + 1, effectiveOpacity); | |
| 735 | |
| 736 // Paint the actual group edges with an alpha adjusted to account for ancens tor layers opacity. | |
| 737 for (BoxSide side : group.sides) { | |
| 738 paintSide(context, borderInfo, side, paintAlpha, completedEdges); | |
| 739 completedEdges |= edgeFlagForSide(side); | |
| 740 } | |
| 741 | |
| 742 if (needsLayer) | |
| 743 context->endLayer(); | |
| 744 | |
| 745 return completedEdges; | |
| 746 } | |
| 747 | |
| 748 void BoxBorderPainter::paintSide(GraphicsContext* context, const ComplexBorderIn fo& borderInfo, | |
| 749 BoxSide side, unsigned alpha, BorderEdgeFlags completedEdges) const | |
| 750 { | |
| 751 const BorderEdge& edge = m_edges[side]; | |
| 752 ASSERT(edge.shouldRender()); | |
| 753 const Color color(edge.color.red(), edge.color.green(), edge.color.blue(), a lpha); | |
| 754 | |
| 755 FloatRect sideRect = m_outer.rect(); | |
| 756 const Path* path = nullptr; | |
| 757 | |
| 758 // FIXME: find a way to rationalize this. | |
|
chrishtr
2015/06/03 22:11:59
What does this mean? Also, TODO is the new FIXME.
f(malita)
2015/06/04 01:54:21
There's too much repetition for my taste, but enou
| |
| 759 switch (side) { | |
| 760 case BSTop: { | |
| 761 bool usePath = m_isRounded && (borderStyleHasInnerDetail(edge.borderStyl e()) | |
| 762 || borderWillArcInnerEdge(m_inner.radii().topLeft(), m_inner.radii() .topRight())); | |
| 763 if (usePath) | |
| 764 path = &borderInfo.roundedBorderPath; | |
| 765 else | |
| 766 sideRect.setHeight(edge.width); | |
| 767 | |
| 768 paintOneBorderSide(context, sideRect, BSTop, BSLeft, BSRight, path, bord erInfo.antiAlias, | |
| 769 color, completedEdges); | |
| 770 break; | |
| 771 } | |
| 772 case BSBottom: { | |
| 773 bool usePath = m_isRounded && (borderStyleHasInnerDetail(edge.borderStyl e()) | |
| 774 || borderWillArcInnerEdge(m_inner.radii().bottomLeft(), m_inner.radi i().bottomRight())); | |
| 775 if (usePath) | |
| 776 path = &borderInfo.roundedBorderPath; | |
| 777 else | |
| 778 sideRect.shiftYEdgeTo(sideRect.maxY() - edge.width); | |
| 779 | |
| 780 paintOneBorderSide(context, sideRect, BSBottom, BSLeft, BSRight, path, b orderInfo.antiAlias, | |
| 781 color, completedEdges); | |
| 782 break; | |
| 783 } | |
| 784 case BSLeft: { | |
| 785 bool usePath = m_isRounded && (borderStyleHasInnerDetail(edge.borderStyl e()) | |
| 786 || borderWillArcInnerEdge(m_inner.radii().bottomLeft(), m_inner.radi i().topLeft())); | |
| 787 if (usePath) | |
| 788 path = &borderInfo.roundedBorderPath; | |
| 789 else | |
| 790 sideRect.setWidth(edge.width); | |
| 791 | |
| 792 paintOneBorderSide(context, sideRect, BSLeft, BSTop, BSBottom, path, bor derInfo.antiAlias, | |
| 793 color, completedEdges); | |
| 794 break; | |
| 795 } | |
| 796 case BSRight: { | |
| 797 bool usePath = m_isRounded && (borderStyleHasInnerDetail(edge.borderStyl e()) | |
| 798 || borderWillArcInnerEdge(m_inner.radii().bottomRight(), m_inner.rad ii().topRight())); | |
| 799 if (usePath) | |
| 800 path = &borderInfo.roundedBorderPath; | |
| 801 else | |
| 802 sideRect.shiftXEdgeTo(sideRect.maxX() - edge.width); | |
| 803 | |
| 804 paintOneBorderSide(context, sideRect, BSRight, BSTop, BSBottom, path, bo rderInfo.antiAlias, | |
| 805 color, completedEdges); | |
| 806 break; | |
| 807 } | |
| 808 default: | |
| 809 ASSERT_NOT_REACHED(); | |
| 572 } | 810 } |
| 573 } | 811 } |
| 574 | 812 |
| 575 void BoxBorderPainter::paintOneBorderSide(GraphicsContext* graphicsContext, | 813 void BoxBorderPainter::paintOneBorderSide(GraphicsContext* graphicsContext, |
| 576 const FloatRect& sideRect, BoxSide side, BoxSide adjacentSide1, BoxSide adja centSide2, | 814 const FloatRect& sideRect, BoxSide side, BoxSide adjacentSide1, BoxSide adja centSide2, |
| 577 const Path* path, bool antialias, const Color* overrideColor) const | 815 const Path* path, bool antialias, Color color, BorderEdgeFlags completedEdge s) const |
| 578 { | 816 { |
| 579 const BorderEdge& edgeToRender = m_edges[side]; | 817 const BorderEdge& edgeToRender = m_edges[side]; |
| 580 ASSERT(edgeToRender.width); | 818 ASSERT(edgeToRender.width); |
| 581 const BorderEdge& adjacentEdge1 = m_edges[adjacentSide1]; | 819 const BorderEdge& adjacentEdge1 = m_edges[adjacentSide1]; |
| 582 const BorderEdge& adjacentEdge2 = m_edges[adjacentSide2]; | 820 const BorderEdge& adjacentEdge2 = m_edges[adjacentSide2]; |
| 583 | 821 |
| 584 const Color& colorToPaint = overrideColor ? *overrideColor : edgeToRender.co lor; | |
| 585 | |
| 586 if (path) { | 822 if (path) { |
| 587 bool adjacentSide1StylesMatch = colorsMatchAtCorner(side, adjacentSide1, m_edges); | 823 bool adjacentSide1StylesMatch = colorsMatchAtCorner(side, adjacentSide1, m_edges); |
| 588 bool adjacentSide2StylesMatch = colorsMatchAtCorner(side, adjacentSide2, m_edges); | 824 bool adjacentSide2StylesMatch = colorsMatchAtCorner(side, adjacentSide2, m_edges); |
| 589 | 825 |
| 590 GraphicsContextStateSaver stateSaver(*graphicsContext); | 826 GraphicsContextStateSaver stateSaver(*graphicsContext); |
| 591 if (m_inner.isRenderable()) | 827 if (m_inner.isRenderable()) |
| 592 clipBorderSidePolygon(graphicsContext, side, adjacentSide1StylesMatc h, adjacentSide2StylesMatch); | 828 clipBorderSidePolygon(graphicsContext, side, adjacentSide1StylesMatc h, adjacentSide2StylesMatch); |
| 593 else | 829 else |
| 594 clipBorderSideForComplexInnerPath(graphicsContext, side); | 830 clipBorderSideForComplexInnerPath(graphicsContext, side); |
| 595 float thickness = std::max(std::max(edgeToRender.width, adjacentEdge1.wi dth), adjacentEdge2.width); | 831 float thickness = std::max(std::max(edgeToRender.width, adjacentEdge1.wi dth), adjacentEdge2.width); |
| 596 drawBoxSideFromPath(graphicsContext, LayoutRect(m_outer.rect()), *path, edgeToRender.width, | 832 drawBoxSideFromPath(graphicsContext, LayoutRect(m_outer.rect()), *path, edgeToRender.width, |
| 597 thickness, side, colorToPaint, edgeToRender.borderStyle()); | 833 thickness, side, color, edgeToRender.borderStyle()); |
| 598 } else { | 834 } else { |
| 599 bool mitreAdjacentSide1 = joinRequiresMitre(side, adjacentSide1, m_edges , !antialias); | 835 bool mitreAdjacentSide1 = joinRequiresMitre(side, adjacentSide1, m_edges , !antialias, completedEdges); |
| 600 bool mitreAdjacentSide2 = joinRequiresMitre(side, adjacentSide2, m_edges , !antialias); | 836 bool mitreAdjacentSide2 = joinRequiresMitre(side, adjacentSide2, m_edges , !antialias, completedEdges); |
| 601 | 837 |
| 602 bool clipForStyle = styleRequiresClipPolygon(edgeToRender.borderStyle()) | 838 bool clipForStyle = styleRequiresClipPolygon(edgeToRender.borderStyle()) |
| 603 && (mitreAdjacentSide1 || mitreAdjacentSide2); | 839 && (mitreAdjacentSide1 || mitreAdjacentSide2); |
| 604 bool clipAdjacentSide1 = colorNeedsAntiAliasAtCorner(side, adjacentSide1 , m_edges) && mitreAdjacentSide1; | 840 bool clipAdjacentSide1 = colorNeedsAntiAliasAtCorner(side, adjacentSide1 , m_edges) && mitreAdjacentSide1; |
| 605 bool clipAdjacentSide2 = colorNeedsAntiAliasAtCorner(side, adjacentSide2 , m_edges) && mitreAdjacentSide2; | 841 bool clipAdjacentSide2 = colorNeedsAntiAliasAtCorner(side, adjacentSide2 , m_edges) && mitreAdjacentSide2; |
| 606 bool shouldClip = clipForStyle || clipAdjacentSide1 || clipAdjacentSide2 ; | 842 bool shouldClip = clipForStyle || clipAdjacentSide1 || clipAdjacentSide2 ; |
| 607 | 843 |
| 608 GraphicsContextStateSaver clipStateSaver(*graphicsContext, shouldClip); | 844 GraphicsContextStateSaver clipStateSaver(*graphicsContext, shouldClip); |
| 609 if (shouldClip) { | 845 if (shouldClip) { |
| 610 bool aliasAdjacentSide1 = clipAdjacentSide1 || (clipForStyle && mitr eAdjacentSide1); | 846 bool aliasAdjacentSide1 = clipAdjacentSide1 || (clipForStyle && mitr eAdjacentSide1); |
| 611 bool aliasAdjacentSide2 = clipAdjacentSide2 || (clipForStyle && mitr eAdjacentSide2); | 847 bool aliasAdjacentSide2 = clipAdjacentSide2 || (clipForStyle && mitr eAdjacentSide2); |
| 612 clipBorderSidePolygon(graphicsContext, side, !aliasAdjacentSide1, !a liasAdjacentSide2); | 848 clipBorderSidePolygon(graphicsContext, side, !aliasAdjacentSide1, !a liasAdjacentSide2); |
| 613 // Since we clipped, no need to draw with a mitre. | 849 // Since we clipped, no need to draw with a mitre. |
| 614 mitreAdjacentSide1 = false; | 850 mitreAdjacentSide1 = false; |
| 615 mitreAdjacentSide2 = false; | 851 mitreAdjacentSide2 = false; |
| 616 } | 852 } |
| 617 | 853 |
| 618 ObjectPainter::drawLineForBoxSide(graphicsContext, sideRect.x(), sideRec t.y(), | 854 ObjectPainter::drawLineForBoxSide(graphicsContext, sideRect.x(), sideRec t.y(), |
| 619 sideRect.maxX(), sideRect.maxY(), side, colorToPaint, edgeToRender.b orderStyle(), | 855 sideRect.maxX(), sideRect.maxY(), side, color, edgeToRender.borderSt yle(), |
| 620 mitreAdjacentSide1 ? adjacentEdge1.width : 0, mitreAdjacentSide2 ? a djacentEdge2.width : 0, antialias); | 856 mitreAdjacentSide1 ? adjacentEdge1.width : 0, mitreAdjacentSide2 ? a djacentEdge2.width : 0, antialias); |
| 621 } | 857 } |
| 622 } | 858 } |
| 623 | 859 |
| 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, | 860 void BoxBorderPainter::drawBoxSideFromPath(GraphicsContext* graphicsContext, |
| 692 const LayoutRect& borderRect, const Path& borderPath, float thickness, float drawThickness, | 861 const LayoutRect& borderRect, const Path& borderPath, float thickness, float drawThickness, |
| 693 BoxSide side, Color color, EBorderStyle borderStyle) const | 862 BoxSide side, Color color, EBorderStyle borderStyle) const |
| 694 { | 863 { |
| 695 if (thickness <= 0) | 864 if (thickness <= 0) |
| 696 return; | 865 return; |
| 697 | 866 |
| 698 if (borderStyle == DOUBLE && thickness < 3) | 867 if (borderStyle == DOUBLE && thickness < 3) |
| 699 borderStyle = SOLID; | 868 borderStyle = SOLID; |
| 700 | 869 |
| (...skipping 315 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1016 | 1185 |
| 1017 FloatPoint secondQuad[4]; | 1186 FloatPoint secondQuad[4]; |
| 1018 secondQuad[0] = quad[0]; | 1187 secondQuad[0] = quad[0]; |
| 1019 secondQuad[1] = FloatPoint(quad[0].x() - r1 * cx, quad[0].y() - r1 * cy); | 1188 secondQuad[1] = FloatPoint(quad[0].x() - r1 * cx, quad[0].y() - r1 * cy); |
| 1020 secondQuad[2] = quad[2]; | 1189 secondQuad[2] = quad[2]; |
| 1021 secondQuad[3] = quad[3]; | 1190 secondQuad[3] = quad[3]; |
| 1022 graphicsContext->clipPolygon(4, secondQuad, !secondEdgeMatches); | 1191 graphicsContext->clipPolygon(4, secondQuad, !secondEdgeMatches); |
| 1023 } | 1192 } |
| 1024 | 1193 |
| 1025 } // namespace blink | 1194 } // namespace blink |
| OLD | NEW |