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

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