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

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