Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(368)

Side by Side Diff: Source/core/paint/BoxBorderPainter.cpp

Issue 1159113010: Optimized box border side painting order (Closed) Base URL: svn://svn.chromium.org/blink/trunk
Patch Set: expectations Created 5 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « Source/core/paint/BoxBorderPainter.h ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
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
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
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
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
OLDNEW
« no previous file with comments | « Source/core/paint/BoxBorderPainter.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698