OLD | NEW |
1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "core/paint/BoxBorderPainter.h" | 5 #include "core/paint/BoxBorderPainter.h" |
6 | 6 |
7 #include "core/paint/BoxPainter.h" | 7 #include "core/paint/BoxPainter.h" |
8 #include "core/paint/ObjectPainter.h" | 8 #include "core/paint/ObjectPainter.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" |
(...skipping 19 matching lines...) Expand all Loading... |
30 | 30 |
31 inline BorderEdgeFlag edgeFlagForSide(BoxSide side) { | 31 inline BorderEdgeFlag edgeFlagForSide(BoxSide side) { |
32 return static_cast<BorderEdgeFlag>(1 << side); | 32 return static_cast<BorderEdgeFlag>(1 << side); |
33 } | 33 } |
34 | 34 |
35 inline bool includesEdge(BorderEdgeFlags flags, BoxSide side) { | 35 inline bool includesEdge(BorderEdgeFlags flags, BoxSide side) { |
36 return flags & edgeFlagForSide(side); | 36 return flags & edgeFlagForSide(side); |
37 } | 37 } |
38 | 38 |
39 inline bool includesAdjacentEdges(BorderEdgeFlags flags) { | 39 inline bool includesAdjacentEdges(BorderEdgeFlags flags) { |
40 // The set includes adjacent edges iff it contains at least one horizontal and
one vertical edge. | 40 // The set includes adjacent edges iff it contains at least one horizontal and |
| 41 // one vertical edge. |
41 return (flags & (TopBorderEdge | BottomBorderEdge)) && | 42 return (flags & (TopBorderEdge | BottomBorderEdge)) && |
42 (flags & (LeftBorderEdge | RightBorderEdge)); | 43 (flags & (LeftBorderEdge | RightBorderEdge)); |
43 } | 44 } |
44 | 45 |
45 inline bool styleRequiresClipPolygon(EBorderStyle style) { | 46 inline bool styleRequiresClipPolygon(EBorderStyle style) { |
46 // These are drawn with a stroke, so we have to clip to get corner miters. | 47 // These are drawn with a stroke, so we have to clip to get corner miters. |
47 return style == BorderStyleDotted || style == BorderStyleDashed; | 48 return style == BorderStyleDotted || style == BorderStyleDashed; |
48 } | 49 } |
49 | 50 |
50 inline bool borderStyleFillsBorderArea(EBorderStyle style) { | 51 inline bool borderStyleFillsBorderArea(EBorderStyle style) { |
51 return !(style == BorderStyleDotted || style == BorderStyleDashed || | 52 return !(style == BorderStyleDotted || style == BorderStyleDashed || |
52 style == BorderStyleDouble); | 53 style == BorderStyleDouble); |
53 } | 54 } |
54 | 55 |
55 inline bool borderStyleHasInnerDetail(EBorderStyle style) { | 56 inline bool borderStyleHasInnerDetail(EBorderStyle style) { |
56 return style == BorderStyleGroove || style == BorderStyleRidge || | 57 return style == BorderStyleGroove || style == BorderStyleRidge || |
57 style == BorderStyleDouble; | 58 style == BorderStyleDouble; |
58 } | 59 } |
59 | 60 |
60 inline bool borderStyleIsDottedOrDashed(EBorderStyle style) { | 61 inline bool borderStyleIsDottedOrDashed(EBorderStyle style) { |
61 return style == BorderStyleDotted || style == BorderStyleDashed; | 62 return style == BorderStyleDotted || style == BorderStyleDashed; |
62 } | 63 } |
63 | 64 |
64 // BorderStyleOutset darkens the bottom and right (and maybe lightens the top an
d left) | 65 // BorderStyleOutset darkens the bottom and right (and maybe lightens the top |
65 // BorderStyleInset darkens the top and left (and maybe lightens the bottom and
right) | 66 // and left) BorderStyleInset darkens the top and left (and maybe lightens the |
| 67 // bottom and right). |
66 inline bool borderStyleHasUnmatchedColorsAtCorner(EBorderStyle style, | 68 inline bool borderStyleHasUnmatchedColorsAtCorner(EBorderStyle style, |
67 BoxSide side, | 69 BoxSide side, |
68 BoxSide adjacentSide) { | 70 BoxSide adjacentSide) { |
69 // These styles match at the top/left and bottom/right. | 71 // These styles match at the top/left and bottom/right. |
70 if (style == BorderStyleInset || style == BorderStyleGroove || | 72 if (style == BorderStyleInset || style == BorderStyleGroove || |
71 style == BorderStyleRidge || style == BorderStyleOutset) { | 73 style == BorderStyleRidge || style == BorderStyleOutset) { |
72 const BorderEdgeFlags topRightFlags = | 74 const BorderEdgeFlags topRightFlags = |
73 edgeFlagForSide(BSTop) | edgeFlagForSide(BSRight); | 75 edgeFlagForSide(BSTop) | edgeFlagForSide(BSRight); |
74 const BorderEdgeFlags bottomLeftFlags = | 76 const BorderEdgeFlags bottomLeftFlags = |
75 edgeFlagForSide(BSBottom) | edgeFlagForSide(BSLeft); | 77 edgeFlagForSide(BSBottom) | edgeFlagForSide(BSLeft); |
(...skipping 19 matching lines...) Expand all Loading... |
95 } | 97 } |
96 | 98 |
97 inline bool borderWillArcInnerEdge(const FloatSize& firstRadius, | 99 inline bool borderWillArcInnerEdge(const FloatSize& firstRadius, |
98 const FloatSize& secondRadius) { | 100 const FloatSize& secondRadius) { |
99 return !firstRadius.isZero() || !secondRadius.isZero(); | 101 return !firstRadius.isZero() || !secondRadius.isZero(); |
100 } | 102 } |
101 | 103 |
102 inline bool willOverdraw(BoxSide side, | 104 inline bool willOverdraw(BoxSide side, |
103 EBorderStyle style, | 105 EBorderStyle style, |
104 BorderEdgeFlags completedEdges) { | 106 BorderEdgeFlags completedEdges) { |
105 // If we're done with this side, it will obviously not overdraw any portion of
the current edge. | 107 // If we're done with this side, it will obviously not overdraw any portion of |
| 108 // the current edge. |
106 if (includesEdge(completedEdges, side)) | 109 if (includesEdge(completedEdges, side)) |
107 return false; | 110 return false; |
108 | 111 |
109 // The side is still to be drawn. It overdraws the current edge iff it has a s
olid fill style. | 112 // The side is still to be drawn. It overdraws the current edge iff it has a |
| 113 // solid fill style. |
110 return borderStyleFillsBorderArea(style); | 114 return borderStyleFillsBorderArea(style); |
111 } | 115 } |
112 | 116 |
113 inline bool borderStylesRequireMiter(BoxSide side, | 117 inline bool borderStylesRequireMiter(BoxSide side, |
114 BoxSide adjacentSide, | 118 BoxSide adjacentSide, |
115 EBorderStyle style, | 119 EBorderStyle style, |
116 EBorderStyle adjacentStyle) { | 120 EBorderStyle adjacentStyle) { |
117 if (style == BorderStyleDouble || adjacentStyle == BorderStyleDouble || | 121 if (style == BorderStyleDouble || adjacentStyle == BorderStyleDouble || |
118 adjacentStyle == BorderStyleGroove || adjacentStyle == BorderStyleRidge) | 122 adjacentStyle == BorderStyleGroove || adjacentStyle == BorderStyleRidge) |
119 return true; | 123 return true; |
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
170 sideRect.shiftXEdgeTo(sideRect.maxX() - width); | 174 sideRect.shiftXEdgeTo(sideRect.maxX() - width); |
171 break; | 175 break; |
172 } | 176 } |
173 | 177 |
174 return sideRect; | 178 return sideRect; |
175 } | 179 } |
176 | 180 |
177 FloatRoundedRect calculateAdjustedInnerBorder( | 181 FloatRoundedRect calculateAdjustedInnerBorder( |
178 const FloatRoundedRect& innerBorder, | 182 const FloatRoundedRect& innerBorder, |
179 BoxSide side) { | 183 BoxSide side) { |
180 // Expand the inner border as necessary to make it a rounded rect (i.e. radii
contained within each edge). | 184 // Expand the inner border as necessary to make it a rounded rect (i.e. radii |
181 // This function relies on the fact we only get radii not contained within eac
h edge if one of the radii | 185 // contained within each edge). This function relies on the fact we only get |
182 // for an edge is zero, so we can shift the arc towards the zero radius corner
. | 186 // radii not contained within each edge if one of the radii for an edge is |
| 187 // zero, so we can shift the arc towards the zero radius corner. |
183 FloatRoundedRect::Radii newRadii = innerBorder.getRadii(); | 188 FloatRoundedRect::Radii newRadii = innerBorder.getRadii(); |
184 FloatRect newRect = innerBorder.rect(); | 189 FloatRect newRect = innerBorder.rect(); |
185 | 190 |
186 float overshoot; | 191 float overshoot; |
187 float maxRadii; | 192 float maxRadii; |
188 | 193 |
189 switch (side) { | 194 switch (side) { |
190 case BSTop: | 195 case BSTop: |
191 overshoot = newRadii.topLeft().width() + newRadii.topRight().width() - | 196 overshoot = newRadii.topLeft().width() + newRadii.topRight().width() - |
192 newRect.width(); | 197 newRect.width(); |
193 // FIXME: once we start pixel-snapping rounded rects after this point, the
overshoot concept | 198 // FIXME: once we start pixel-snapping rounded rects after this point, the |
194 // should disappear. | 199 // overshoot concept should disappear. |
195 if (overshoot > 0.1) { | 200 if (overshoot > 0.1) { |
196 newRect.setWidth(newRect.width() + overshoot); | 201 newRect.setWidth(newRect.width() + overshoot); |
197 if (!newRadii.topLeft().width()) | 202 if (!newRadii.topLeft().width()) |
198 newRect.move(-overshoot, 0); | 203 newRect.move(-overshoot, 0); |
199 } | 204 } |
200 newRadii.setBottomLeft(FloatSize(0, 0)); | 205 newRadii.setBottomLeft(FloatSize(0, 0)); |
201 newRadii.setBottomRight(FloatSize(0, 0)); | 206 newRadii.setBottomRight(FloatSize(0, 0)); |
202 maxRadii = | 207 maxRadii = |
203 std::max(newRadii.topLeft().height(), newRadii.topRight().height()); | 208 std::max(newRadii.topLeft().height(), newRadii.topRight().height()); |
204 if (maxRadii > newRect.height()) | 209 if (maxRadii > newRect.height()) |
(...skipping 84 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
289 context.setShouldAntialias(false); | 294 context.setShouldAntialias(false); |
290 } | 295 } |
291 | 296 |
292 void drawBleedAdjustedDRRect(GraphicsContext& context, | 297 void drawBleedAdjustedDRRect(GraphicsContext& context, |
293 BackgroundBleedAvoidance bleedAvoidance, | 298 BackgroundBleedAvoidance bleedAvoidance, |
294 const FloatRoundedRect& outer, | 299 const FloatRoundedRect& outer, |
295 const FloatRoundedRect& inner, | 300 const FloatRoundedRect& inner, |
296 Color color) { | 301 Color color) { |
297 switch (bleedAvoidance) { | 302 switch (bleedAvoidance) { |
298 case BackgroundBleedClipLayer: { | 303 case BackgroundBleedClipLayer: { |
299 // BackgroundBleedClipLayer clips the outer rrect for the whole layer. Bas
ed on this, | 304 // BackgroundBleedClipLayer clips the outer rrect for the whole layer. |
300 // we can avoid background bleeding by filling the *outside* of inner rrec
t, all the | 305 // Based on this, we can avoid background bleeding by filling the |
301 // way to the layer bounds (enclosing int rect for the clip, in device spa
ce). | 306 // *outside* of inner rrect, all the way to the layer bounds (enclosing |
| 307 // int rect for the clip, in device space). |
302 ASSERT(outer.isRounded()); | 308 ASSERT(outer.isRounded()); |
303 | 309 |
304 SkPath path; | 310 SkPath path; |
305 path.addRRect(inner); | 311 path.addRRect(inner); |
306 path.setFillType(SkPath::kInverseWinding_FillType); | 312 path.setFillType(SkPath::kInverseWinding_FillType); |
307 | 313 |
308 SkPaint paint; | 314 SkPaint paint; |
309 paint.setColor(color.rgb()); | 315 paint.setColor(color.rgb()); |
310 paint.setStyle(SkPaint::kFill_Style); | 316 paint.setStyle(SkPaint::kFill_Style); |
311 paint.setAntiAlias(true); | 317 paint.setAntiAlias(true); |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
343 static_assert(BorderStyleDotted == 6, "unexpected EBorderStyle value"); | 349 static_assert(BorderStyleDotted == 6, "unexpected EBorderStyle value"); |
344 static_assert(BorderStyleDashed == 7, "unexpected EBorderStyle value"); | 350 static_assert(BorderStyleDashed == 7, "unexpected EBorderStyle value"); |
345 static_assert(BorderStyleSolid == 8, "unexpected EBorderStyle value"); | 351 static_assert(BorderStyleSolid == 8, "unexpected EBorderStyle value"); |
346 static_assert(BorderStyleDouble == 9, "unexpected EBorderStyle value"); | 352 static_assert(BorderStyleDouble == 9, "unexpected EBorderStyle value"); |
347 | 353 |
348 static_assert(BSTop == 0, "unexpected BoxSide value"); | 354 static_assert(BSTop == 0, "unexpected BoxSide value"); |
349 static_assert(BSRight == 1, "unexpected BoxSide value"); | 355 static_assert(BSRight == 1, "unexpected BoxSide value"); |
350 static_assert(BSBottom == 2, "unexpected BoxSide value"); | 356 static_assert(BSBottom == 2, "unexpected BoxSide value"); |
351 static_assert(BSLeft == 3, "unexpected BoxSide value"); | 357 static_assert(BSLeft == 3, "unexpected BoxSide value"); |
352 | 358 |
353 // Style-based paint order: non-solid edges (dashed/dotted/double) are painted b
efore | 359 // Style-based paint order: non-solid edges (dashed/dotted/double) are painted |
354 // solid edges (inset/outset/groove/ridge/solid) to maximize overdraw opportunit
ies. | 360 // before solid edges (inset/outset/groove/ridge/solid) to maximize overdraw |
| 361 // opportunities. |
355 const unsigned kStylePriority[] = { | 362 const unsigned kStylePriority[] = { |
356 0 /* BorderStyleNone */, 0 /* BorderStyleHidden */, | 363 0 /* BorderStyleNone */, 0 /* BorderStyleHidden */, |
357 2 /* BorderStyleInset */, 2 /* BorderStyleGroove */, | 364 2 /* BorderStyleInset */, 2 /* BorderStyleGroove */, |
358 2 /* BorderStyleOutset */, 2 /* BorderStyleRidge */, | 365 2 /* BorderStyleOutset */, 2 /* BorderStyleRidge */, |
359 1 /* BorderStyleDotted */, 1 /* BorderStyleDashed */, | 366 1 /* BorderStyleDotted */, 1 /* BorderStyleDashed */, |
360 3 /* BorderStyleSolid */, 1 /* BorderStyleDouble */ | 367 3 /* BorderStyleSolid */, 1 /* BorderStyleDouble */ |
361 }; | 368 }; |
362 | 369 |
363 // Given the same style, prefer drawing in non-adjacent order to minimize the nu
mber of sides | 370 // Given the same style, prefer drawing in non-adjacent order to minimize the |
364 // which require miters. | 371 // number of sides which require miters. |
365 const unsigned kSidePriority[] = { | 372 const unsigned kSidePriority[] = { |
366 0, /* BSTop */ | 373 0, /* BSTop */ |
367 2, /* BSRight */ | 374 2, /* BSRight */ |
368 1, /* BSBottom */ | 375 1, /* BSBottom */ |
369 3, /* BSLeft */ | 376 3, /* BSLeft */ |
370 }; | 377 }; |
371 | 378 |
372 // Edges sharing the same opacity. Stores both a side list and an edge bitfield
to support | 379 // Edges sharing the same opacity. Stores both a side list and an edge bitfield |
373 // constant time iteration + membership tests. | 380 // to support constant time iteration + membership tests. |
374 struct OpacityGroup { | 381 struct OpacityGroup { |
375 OpacityGroup(unsigned alpha) : edgeFlags(0), alpha(alpha) {} | 382 OpacityGroup(unsigned alpha) : edgeFlags(0), alpha(alpha) {} |
376 | 383 |
377 Vector<BoxSide, 4> sides; | 384 Vector<BoxSide, 4> sides; |
378 BorderEdgeFlags edgeFlags; | 385 BorderEdgeFlags edgeFlags; |
379 unsigned alpha; | 386 unsigned alpha; |
380 }; | 387 }; |
381 | 388 |
382 void clipQuad(GraphicsContext& context, | 389 void clipQuad(GraphicsContext& context, |
383 const FloatPoint quad[], | 390 const FloatPoint quad[], |
(...skipping 17 matching lines...) Expand all Loading... |
401 | 408 |
402 // First, collect all visible sides. | 409 // First, collect all visible sides. |
403 for (unsigned i = borderPainter.m_firstVisibleEdge; i < 4; ++i) { | 410 for (unsigned i = borderPainter.m_firstVisibleEdge; i < 4; ++i) { |
404 BoxSide side = static_cast<BoxSide>(i); | 411 BoxSide side = static_cast<BoxSide>(i); |
405 | 412 |
406 if (includesEdge(borderPainter.m_visibleEdgeSet, side)) | 413 if (includesEdge(borderPainter.m_visibleEdgeSet, side)) |
407 sortedSides.append(side); | 414 sortedSides.append(side); |
408 } | 415 } |
409 ASSERT(!sortedSides.isEmpty()); | 416 ASSERT(!sortedSides.isEmpty()); |
410 | 417 |
411 // Then sort them in paint order, based on three (prioritized) criteria: alp
ha, style, side. | 418 // Then sort them in paint order, based on three (prioritized) criteria: |
| 419 // alpha, style, side. |
412 std::sort( | 420 std::sort( |
413 sortedSides.begin(), sortedSides.end(), | 421 sortedSides.begin(), sortedSides.end(), |
414 [&borderPainter](BoxSide a, BoxSide b) -> bool { | 422 [&borderPainter](BoxSide a, BoxSide b) -> bool { |
415 const BorderEdge& edgeA = borderPainter.m_edges[a]; | 423 const BorderEdge& edgeA = borderPainter.m_edges[a]; |
416 const BorderEdge& edgeB = borderPainter.m_edges[b]; | 424 const BorderEdge& edgeB = borderPainter.m_edges[b]; |
417 | 425 |
418 const unsigned alphaA = edgeA.color.alpha(); | 426 const unsigned alphaA = edgeA.color.alpha(); |
419 const unsigned alphaB = edgeB.color.alpha(); | 427 const unsigned alphaB = edgeB.color.alpha(); |
420 if (alphaA != alphaB) | 428 if (alphaA != alphaB) |
421 return alphaA < alphaB; | 429 return alphaA < alphaB; |
(...skipping 94 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
516 } | 524 } |
517 } else { | 525 } else { |
518 // 4-side, double border => 2x drawDRRect() | 526 // 4-side, double border => 2x drawDRRect() |
519 ASSERT(firstEdge().borderStyle() == BorderStyleDouble); | 527 ASSERT(firstEdge().borderStyle() == BorderStyleDouble); |
520 drawDoubleBorder(context, borderRect); | 528 drawDoubleBorder(context, borderRect); |
521 } | 529 } |
522 | 530 |
523 return true; | 531 return true; |
524 } | 532 } |
525 | 533 |
526 // This is faster than the normal complex border path only if it avoids creati
ng transparency | 534 // This is faster than the normal complex border path only if it avoids |
527 // layers (when the border is translucent). | 535 // creating transparency layers (when the border is translucent). |
528 if (firstEdge().borderStyle() == BorderStyleSolid && !m_outer.isRounded() && | 536 if (firstEdge().borderStyle() == BorderStyleSolid && !m_outer.isRounded() && |
529 m_hasAlpha) { | 537 m_hasAlpha) { |
530 ASSERT(m_visibleEdgeSet != AllBorderEdges); | 538 ASSERT(m_visibleEdgeSet != AllBorderEdges); |
531 // solid, rectangular border => one drawPath() | 539 // solid, rectangular border => one drawPath() |
532 Path path; | 540 Path path; |
533 path.setWindRule(RULE_NONZERO); | 541 path.setWindRule(RULE_NONZERO); |
534 | 542 |
535 for (int i = BSTop; i <= BSLeft; ++i) { | 543 for (int i = BSTop; i <= BSLeft; ++i) { |
536 const BorderEdge& currEdge = m_edges[i]; | 544 const BorderEdge& currEdge = m_edges[i]; |
537 if (currEdge.shouldRender()) | 545 if (currEdge.shouldRender()) |
(...skipping 103 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
641 return; | 649 return; |
642 | 650 |
643 GraphicsContext& graphicsContext = info.context; | 651 GraphicsContext& graphicsContext = info.context; |
644 | 652 |
645 if (paintBorderFastPath(graphicsContext, rect)) | 653 if (paintBorderFastPath(graphicsContext, rect)) |
646 return; | 654 return; |
647 | 655 |
648 bool clipToOuterBorder = m_outer.isRounded(); | 656 bool clipToOuterBorder = m_outer.isRounded(); |
649 GraphicsContextStateSaver stateSaver(graphicsContext, clipToOuterBorder); | 657 GraphicsContextStateSaver stateSaver(graphicsContext, clipToOuterBorder); |
650 if (clipToOuterBorder) { | 658 if (clipToOuterBorder) { |
651 // For BackgroundBleedClip{Only,Layer}, the outer rrect clip is already appl
ied. | 659 // For BackgroundBleedClip{Only,Layer}, the outer rrect clip is already |
| 660 // applied. |
652 if (!bleedAvoidanceIsClipping(m_bleedAvoidance)) | 661 if (!bleedAvoidanceIsClipping(m_bleedAvoidance)) |
653 graphicsContext.clipRoundedRect(m_outer); | 662 graphicsContext.clipRoundedRect(m_outer); |
654 | 663 |
655 if (m_inner.isRenderable() && !m_inner.isEmpty()) | 664 if (m_inner.isRenderable() && !m_inner.isEmpty()) |
656 graphicsContext.clipOutRoundedRect(m_inner); | 665 graphicsContext.clipOutRoundedRect(m_inner); |
657 } | 666 } |
658 | 667 |
659 const ComplexBorderInfo borderInfo(*this, true); | 668 const ComplexBorderInfo borderInfo(*this, true); |
660 paintOpacityGroup(graphicsContext, borderInfo, 0, 1); | 669 paintOpacityGroup(graphicsContext, borderInfo, 0, 1); |
661 } | 670 } |
662 | 671 |
663 // In order to maximize the use of overdraw as a corner seam avoidance technique
, we draw | 672 // In order to maximize the use of overdraw as a corner seam avoidance |
664 // translucent border sides using the following algorithm: | 673 // technique, we draw translucent border sides using the following algorithm: |
665 // | 674 // |
666 // 1) cluster sides sharing the same opacity into "opacity groups" [ComplexBor
derInfo] | 675 // 1) cluster sides sharing the same opacity into "opacity groups" |
| 676 // [ComplexBorderInfo] |
667 // 2) sort groups in increasing opacity order [ComplexBorderInfo] | 677 // 2) sort groups in increasing opacity order [ComplexBorderInfo] |
668 // 3) reverse-iterate over groups (decreasing opacity order), pushing nested t
ransparency | 678 // 3) reverse-iterate over groups (decreasing opacity order), pushing nested |
669 // layers with adjusted/relative opacity [paintOpacityGroup] | 679 // transparency layers with adjusted/relative opacity [paintOpacityGroup] |
670 // 4) iterate over groups (increasing opacity order), painting actual group co
ntents and | 680 // 4) iterate over groups (increasing opacity order), painting actual group |
671 // then ending their corresponding transparency layer [paintOpacityGroup] | 681 // contents and then ending their corresponding transparency layer |
| 682 // [paintOpacityGroup] |
672 // | 683 // |
673 // Layers are created in decreasing opacity order (top -> bottom), while actual
border sides are | 684 // Layers are created in decreasing opacity order (top -> bottom), while actual |
674 // drawn in increasing opacity order (bottom -> top). At each level, opacity is
adjusted to acount | 685 // border sides are drawn in increasing opacity order (bottom -> top). At each |
675 // for accumulated/ancestor layer alpha. Because opacity is applied via layers,
the actual draw | 686 // level, opacity is adjusted to acount for accumulated/ancestor layer alpha. |
676 // paint is opaque. | 687 // Because opacity is applied via layers, the actual draw paint is opaque. |
677 // | 688 // |
678 // As an example, let's consider a border with the following sides/opacities: | 689 // As an example, let's consider a border with the following sides/opacities: |
679 // | 690 // |
680 // top: 1.0 | 691 // top: 1.0 |
681 // right: 0.25 | 692 // right: 0.25 |
682 // bottom: 0.5 | 693 // bottom: 0.5 |
683 // left: 0.25 | 694 // left: 0.25 |
684 // | 695 // |
685 // These are grouped and sorted in ComplexBorderInfo as follows: | 696 // These are grouped and sorted in ComplexBorderInfo as follows: |
686 // | 697 // |
687 // group[0]: { alpha: 1.0, sides: top } | 698 // group[0]: { alpha: 1.0, sides: top } |
688 // group[1]: { alpha: 0.5, sides: bottom } | 699 // group[1]: { alpha: 0.5, sides: bottom } |
689 // group[2]: { alpha: 0.25, sides: right, left } | 700 // group[2]: { alpha: 0.25, sides: right, left } |
690 // | 701 // |
691 // Applying the algorithm yields the following paint sequence: | 702 // Applying the algorithm yields the following paint sequence: |
692 // | 703 // |
693 // // no layer needed for group 0 (alpha == 1) | 704 // // no layer needed for group 0 (alpha = 1) |
694 // beginLayer(0.5) // layer for group 1 | 705 // beginLayer(0.5) // layer for group 1 |
695 // beginLayer(0.5) // layer for group 2 (effective opacity: 0.5
* 0.5 == 0.25) | 706 // beginLayer(0.5) // layer for group 2 (alpha: 0.5 * 0.5 = 0.25) |
696 // paintSides(right, left) // paint group 2 | 707 // paintSides(right, left) // paint group 2 |
697 // endLayer | 708 // endLayer |
698 // paintSides(bottom) // paint group 1 | 709 // paintSides(bottom) // paint group 1 |
699 // endLayer | 710 // endLayer |
700 // paintSides(top) // paint group 0 | 711 // paintSides(top) // paint group 0 |
701 // | 712 // |
702 // Note that we're always drawing using opaque paints on top of less-opaque cont
ent - hence | 713 // Note that we're always drawing using opaque paints on top of less-opaque |
703 // we can use overdraw to mask portions of the previous sides. | 714 // content - hence we can use overdraw to mask portions of the previous sides. |
704 // | 715 // |
705 BorderEdgeFlags BoxBorderPainter::paintOpacityGroup( | 716 BorderEdgeFlags BoxBorderPainter::paintOpacityGroup( |
706 GraphicsContext& context, | 717 GraphicsContext& context, |
707 const ComplexBorderInfo& borderInfo, | 718 const ComplexBorderInfo& borderInfo, |
708 unsigned index, | 719 unsigned index, |
709 float effectiveOpacity) const { | 720 float effectiveOpacity) const { |
710 ASSERT(effectiveOpacity > 0 && effectiveOpacity <= 1); | 721 ASSERT(effectiveOpacity > 0 && effectiveOpacity <= 1); |
711 | 722 |
712 const size_t opacityGroupCount = borderInfo.opacityGroups.size(); | 723 const size_t opacityGroupCount = borderInfo.opacityGroups.size(); |
713 | 724 |
714 // For overdraw logic purposes, treat missing/transparent edges as completed. | 725 // For overdraw logic purposes, treat missing/transparent edges as completed. |
715 if (index >= opacityGroupCount) | 726 if (index >= opacityGroupCount) |
716 return ~m_visibleEdgeSet; | 727 return ~m_visibleEdgeSet; |
717 | 728 |
718 // Groups are sorted in increasing opacity order, but we need to create layers
in | 729 // Groups are sorted in increasing opacity order, but we need to create layers |
719 // decreasing opacity order - hence the reverse iteration. | 730 // in decreasing opacity order - hence the reverse iteration. |
720 const OpacityGroup& group = | 731 const OpacityGroup& group = |
721 borderInfo.opacityGroups[opacityGroupCount - index - 1]; | 732 borderInfo.opacityGroups[opacityGroupCount - index - 1]; |
722 | 733 |
723 // Adjust this group's paint opacity to account for ancestor transparency laye
rs | 734 // Adjust this group's paint opacity to account for ancestor transparency |
724 // (needed in case we avoid creating a layer below). | 735 // layers (needed in case we avoid creating a layer below). |
725 unsigned paintAlpha = group.alpha / effectiveOpacity; | 736 unsigned paintAlpha = group.alpha / effectiveOpacity; |
726 ASSERT(paintAlpha <= 255); | 737 ASSERT(paintAlpha <= 255); |
727 | 738 |
728 // For the last (bottom) group, we can skip the layer even in the presence of
opacity iff | 739 // For the last (bottom) group, we can skip the layer even in the presence of |
729 // it contains no adjecent edges (no in-group overdraw possibility). | 740 // opacity iff it contains no adjecent edges (no in-group overdraw |
| 741 // possibility). |
730 bool needsLayer = | 742 bool needsLayer = |
731 group.alpha != 255 && (includesAdjacentEdges(group.edgeFlags) || | 743 group.alpha != 255 && (includesAdjacentEdges(group.edgeFlags) || |
732 (index + 1 < borderInfo.opacityGroups.size())); | 744 (index + 1 < borderInfo.opacityGroups.size())); |
733 | 745 |
734 if (needsLayer) { | 746 if (needsLayer) { |
735 const float groupOpacity = static_cast<float>(group.alpha) / 255; | 747 const float groupOpacity = static_cast<float>(group.alpha) / 255; |
736 ASSERT(groupOpacity < effectiveOpacity); | 748 ASSERT(groupOpacity < effectiveOpacity); |
737 | 749 |
738 context.beginLayer(groupOpacity / effectiveOpacity); | 750 context.beginLayer(groupOpacity / effectiveOpacity); |
739 effectiveOpacity = groupOpacity; | 751 effectiveOpacity = groupOpacity; |
740 | 752 |
741 // Group opacity is applied via a layer => we draw the members using opaque
paint. | 753 // Group opacity is applied via a layer => we draw the members using opaque |
| 754 // paint. |
742 paintAlpha = 255; | 755 paintAlpha = 255; |
743 } | 756 } |
744 | 757 |
745 // Recursion may seem unpalatable here, but | 758 // Recursion may seem unpalatable here, but |
746 // a) it has an upper bound of 4 | 759 // a) it has an upper bound of 4 |
747 // b) only triggers at all when mixing border sides with different opacities | 760 // b) only triggers at all when mixing border sides with different opacities |
748 // c) it allows us to express the layer nesting algorithm more naturally | 761 // c) it allows us to express the layer nesting algorithm more naturally |
749 BorderEdgeFlags completedEdges = | 762 BorderEdgeFlags completedEdges = |
750 paintOpacityGroup(context, borderInfo, index + 1, effectiveOpacity); | 763 paintOpacityGroup(context, borderInfo, index + 1, effectiveOpacity); |
751 | 764 |
752 // Paint the actual group edges with an alpha adjusted to account for ancensto
r layers opacity. | 765 // Paint the actual group edges with an alpha adjusted to account for |
| 766 // ancenstor layers opacity. |
753 for (BoxSide side : group.sides) { | 767 for (BoxSide side : group.sides) { |
754 paintSide(context, borderInfo, side, paintAlpha, completedEdges); | 768 paintSide(context, borderInfo, side, paintAlpha, completedEdges); |
755 completedEdges |= edgeFlagForSide(side); | 769 completedEdges |= edgeFlagForSide(side); |
756 } | 770 } |
757 | 771 |
758 if (needsLayer) | 772 if (needsLayer) |
759 context.endLayer(); | 773 context.endLayer(); |
760 | 774 |
761 return completedEdges; | 775 return completedEdges; |
762 } | 776 } |
763 | 777 |
764 void BoxBorderPainter::paintSide(GraphicsContext& context, | 778 void BoxBorderPainter::paintSide(GraphicsContext& context, |
765 const ComplexBorderInfo& borderInfo, | 779 const ComplexBorderInfo& borderInfo, |
766 BoxSide side, | 780 BoxSide side, |
767 unsigned alpha, | 781 unsigned alpha, |
768 BorderEdgeFlags completedEdges) const { | 782 BorderEdgeFlags completedEdges) const { |
769 const BorderEdge& edge = m_edges[side]; | 783 const BorderEdge& edge = m_edges[side]; |
770 ASSERT(edge.shouldRender()); | 784 ASSERT(edge.shouldRender()); |
771 const Color color(edge.color.red(), edge.color.green(), edge.color.blue(), | 785 const Color color(edge.color.red(), edge.color.green(), edge.color.blue(), |
772 alpha); | 786 alpha); |
773 | 787 |
774 FloatRect sideRect = m_outer.rect(); | 788 FloatRect sideRect = m_outer.rect(); |
775 const Path* path = nullptr; | 789 const Path* path = nullptr; |
776 | 790 |
777 // TODO(fmalita): find a way to consolidate these without sacrificing readabil
ity. | 791 // TODO(fmalita): find a way to consolidate these without sacrificing |
| 792 // readability. |
778 switch (side) { | 793 switch (side) { |
779 case BSTop: { | 794 case BSTop: { |
780 bool usePath = m_isRounded && | 795 bool usePath = m_isRounded && |
781 (borderStyleHasInnerDetail(edge.borderStyle()) || | 796 (borderStyleHasInnerDetail(edge.borderStyle()) || |
782 borderWillArcInnerEdge(m_inner.getRadii().topLeft(), | 797 borderWillArcInnerEdge(m_inner.getRadii().topLeft(), |
783 m_inner.getRadii().topRight())); | 798 m_inner.getRadii().topRight())); |
784 if (usePath) | 799 if (usePath) |
785 path = &borderInfo.roundedBorderPath; | 800 path = &borderInfo.roundedBorderPath; |
786 else | 801 else |
787 sideRect.setHeight(edge.width); | 802 sideRect.setHeight(edge.width); |
(...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
845 const BorderEdge& adjacentEdge = m_edges[adjacentSide]; | 860 const BorderEdge& adjacentEdge = m_edges[adjacentSide]; |
846 | 861 |
847 // No miters for missing edges. | 862 // No miters for missing edges. |
848 if (!adjacentEdge.isPresent) | 863 if (!adjacentEdge.isPresent) |
849 return NoMiter; | 864 return NoMiter; |
850 | 865 |
851 // The adjacent edge will overdraw this corner, resulting in a correct miter. | 866 // The adjacent edge will overdraw this corner, resulting in a correct miter. |
852 if (willOverdraw(adjacentSide, adjacentEdge.borderStyle(), completedEdges)) | 867 if (willOverdraw(adjacentSide, adjacentEdge.borderStyle(), completedEdges)) |
853 return NoMiter; | 868 return NoMiter; |
854 | 869 |
855 // Color transitions require miters. Use miters compatible with the AA drawing
mode to avoid | 870 // Color transitions require miters. Use miters compatible with the AA drawing |
856 // introducing extra clips. | 871 // mode to avoid introducing extra clips. |
857 if (!colorsMatchAtCorner(side, adjacentSide, m_edges)) | 872 if (!colorsMatchAtCorner(side, adjacentSide, m_edges)) |
858 return antialias ? SoftMiter : HardMiter; | 873 return antialias ? SoftMiter : HardMiter; |
859 | 874 |
860 // Non-anti-aliased miters ensure correct same-color seaming when required by
style. | 875 // Non-anti-aliased miters ensure correct same-color seaming when required by |
| 876 // style. |
861 if (borderStylesRequireMiter(side, adjacentSide, m_edges[side].borderStyle(), | 877 if (borderStylesRequireMiter(side, adjacentSide, m_edges[side].borderStyle(), |
862 adjacentEdge.borderStyle())) | 878 adjacentEdge.borderStyle())) |
863 return HardMiter; | 879 return HardMiter; |
864 | 880 |
865 // Overdraw the adjacent edge when the colors match and we have no style restr
ictions. | 881 // Overdraw the adjacent edge when the colors match and we have no style |
| 882 // restrictions. |
866 return NoMiter; | 883 return NoMiter; |
867 } | 884 } |
868 | 885 |
869 bool BoxBorderPainter::mitersRequireClipping(MiterType miter1, | 886 bool BoxBorderPainter::mitersRequireClipping(MiterType miter1, |
870 MiterType miter2, | 887 MiterType miter2, |
871 EBorderStyle style, | 888 EBorderStyle style, |
872 bool antialias) { | 889 bool antialias) { |
873 // Clipping is required if any of the present miters doesn't match the current
AA mode. | 890 // Clipping is required if any of the present miters doesn't match the current |
| 891 // AA mode. |
874 bool shouldClip = antialias ? miter1 == HardMiter || miter2 == HardMiter | 892 bool shouldClip = antialias ? miter1 == HardMiter || miter2 == HardMiter |
875 : miter1 == SoftMiter || miter2 == SoftMiter; | 893 : miter1 == SoftMiter || miter2 == SoftMiter; |
876 | 894 |
877 // Some styles require clipping for any type of miter. | 895 // Some styles require clipping for any type of miter. |
878 shouldClip = shouldClip || ((miter1 != NoMiter || miter2 != NoMiter) && | 896 shouldClip = shouldClip || ((miter1 != NoMiter || miter2 != NoMiter) && |
879 styleRequiresClipPolygon(style)); | 897 styleRequiresClipPolygon(style)); |
880 | 898 |
881 return shouldClip; | 899 return shouldClip; |
882 } | 900 } |
883 | 901 |
(...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
961 graphicsContext.setStrokeColor(color); | 979 graphicsContext.setStrokeColor(color); |
962 | 980 |
963 // The stroke is doubled here because the provided path is the | 981 // The stroke is doubled here because the provided path is the |
964 // outside edge of the border so half the stroke is clipped off. | 982 // outside edge of the border so half the stroke is clipped off. |
965 // The extra multiplier is so that the clipping mask can antialias | 983 // The extra multiplier is so that the clipping mask can antialias |
966 // the edges to prevent jaggies. | 984 // the edges to prevent jaggies. |
967 graphicsContext.setStrokeThickness(drawThickness * 2 * 1.1f); | 985 graphicsContext.setStrokeThickness(drawThickness * 2 * 1.1f); |
968 graphicsContext.setStrokeStyle( | 986 graphicsContext.setStrokeStyle( |
969 borderStyle == BorderStyleDashed ? DashedStroke : DottedStroke); | 987 borderStyle == BorderStyleDashed ? DashedStroke : DottedStroke); |
970 | 988 |
971 // If the number of dashes that fit in the path is odd and non-integral th
en we | 989 // If the number of dashes that fit in the path is odd and non-integral |
972 // will have an awkwardly-sized dash at the end of the path. To try to avo
id that | 990 // then we will have an awkwardly-sized dash at the end of the path. To |
973 // here, we simply make the whitespace dashes ever so slightly bigger. | 991 // try to avoid that here, we simply make the whitespace dashes ever so |
974 // FIXME: This could be even better if we tried to manipulate the dash off
set | 992 // slightly bigger. |
975 // and possibly the gapLength to get the corners dash-symmetrical. | 993 // FIXME: This could be even better if we tried to manipulate the dash |
| 994 // offset and possibly the gapLength to get the corners dash-symmetrical. |
976 float dashLength = | 995 float dashLength = |
977 thickness * ((borderStyle == BorderStyleDashed) ? 3.0f : 1.0f); | 996 thickness * ((borderStyle == BorderStyleDashed) ? 3.0f : 1.0f); |
978 float gapLength = dashLength; | 997 float gapLength = dashLength; |
979 float numberOfDashes = borderPath.length() / dashLength; | 998 float numberOfDashes = borderPath.length() / dashLength; |
980 // Don't try to show dashes if we have less than 2 dashes + 2 gaps. | 999 // Don't try to show dashes if we have less than 2 dashes + 2 gaps. |
981 // FIXME: should do this test per side. | 1000 // FIXME: should do this test per side. |
982 if (numberOfDashes >= 4) { | 1001 if (numberOfDashes >= 4) { |
983 bool evenNumberOfFullDashes = !((int)numberOfDashes % 2); | 1002 bool evenNumberOfFullDashes = !((int)numberOfDashes % 2); |
984 bool integralNumberOfDashes = !(numberOfDashes - (int)numberOfDashes); | 1003 bool integralNumberOfDashes = !(numberOfDashes - (int)numberOfDashes); |
985 if (!evenNumberOfFullDashes && !integralNumberOfDashes) { | 1004 if (!evenNumberOfFullDashes && !integralNumberOfDashes) { |
986 float numberOfGaps = numberOfDashes / 2; | 1005 float numberOfGaps = numberOfDashes / 2; |
987 gapLength += (dashLength / numberOfGaps); | 1006 gapLength += (dashLength / numberOfGaps); |
988 } | 1007 } |
989 | 1008 |
990 DashArray lineDash; | 1009 DashArray lineDash; |
991 lineDash.append(dashLength); | 1010 lineDash.append(dashLength); |
992 lineDash.append(gapLength); | 1011 lineDash.append(gapLength); |
993 graphicsContext.setLineDash(lineDash, dashLength); | 1012 graphicsContext.setLineDash(lineDash, dashLength); |
994 } | 1013 } |
995 | 1014 |
996 // FIXME: stroking the border path causes issues with tight corners: | 1015 // FIXME: stroking the border path causes issues with tight corners: |
997 // https://bugs.webkit.org/show_bug.cgi?id=58711 | 1016 // https://bugs.webkit.org/show_bug.cgi?id=58711 |
998 // Also, to get the best appearance we should stroke a path between the tw
o borders. | 1017 // Also, to get the best appearance we should stroke a path between the |
| 1018 // two borders. |
999 graphicsContext.strokePath(borderPath); | 1019 graphicsContext.strokePath(borderPath); |
1000 return; | 1020 return; |
1001 } | 1021 } |
1002 case BorderStyleDouble: { | 1022 case BorderStyleDouble: { |
1003 // Draw inner border line | 1023 // Draw inner border line |
1004 { | 1024 { |
1005 GraphicsContextStateSaver stateSaver(graphicsContext); | 1025 GraphicsContextStateSaver stateSaver(graphicsContext); |
1006 const LayoutRectOutsets innerInsets = | 1026 const LayoutRectOutsets innerInsets = |
1007 doubleStripeInsets(m_edges, BorderEdge::DoubleBorderStripeInner); | 1027 doubleStripeInsets(m_edges, BorderEdge::DoubleBorderStripeInner); |
1008 FloatRoundedRect innerClip = m_style.getRoundedInnerBorderFor( | 1028 FloatRoundedRect innerClip = m_style.getRoundedInnerBorderFor( |
(...skipping 93 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1102 BoxSide side, | 1122 BoxSide side, |
1103 MiterType firstMiter, | 1123 MiterType firstMiter, |
1104 MiterType secondMiter) const { | 1124 MiterType secondMiter) const { |
1105 ASSERT(firstMiter != NoMiter || secondMiter != NoMiter); | 1125 ASSERT(firstMiter != NoMiter || secondMiter != NoMiter); |
1106 | 1126 |
1107 FloatPoint quad[4]; | 1127 FloatPoint quad[4]; |
1108 | 1128 |
1109 const LayoutRect outerRect(m_outer.rect()); | 1129 const LayoutRect outerRect(m_outer.rect()); |
1110 const LayoutRect innerRect(m_inner.rect()); | 1130 const LayoutRect innerRect(m_inner.rect()); |
1111 | 1131 |
1112 // For each side, create a quad that encompasses all parts of that side that m
ay draw, | 1132 // For each side, create a quad that encompasses all parts of that side that |
1113 // including areas inside the innerBorder. | 1133 // may draw, including areas inside the innerBorder. |
1114 // | 1134 // |
1115 // 0----------------3 | 1135 // 0----------------3 |
1116 // 0 \ / 0 | 1136 // 0 \ / 0 |
1117 // |\ 1----------- 2 /| | 1137 // |\ 1----------- 2 /| |
1118 // | 1 1 | | 1138 // | 1 1 | |
1119 // | | | | | 1139 // | | | | |
1120 // | | | | | 1140 // | | | | |
1121 // | 2 2 | | 1141 // | 2 2 | |
1122 // |/ 1------------2 \| | 1142 // |/ 1------------2 \| |
1123 // 3 / \ 3 | 1143 // 3 / \ 3 |
(...skipping 150 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1274 FloatPoint secondQuad[4]; | 1294 FloatPoint secondQuad[4]; |
1275 secondQuad[0] = quad[0]; | 1295 secondQuad[0] = quad[0]; |
1276 secondQuad[1] = FloatPoint(quad[0].x() - r1 * cx, quad[0].y() - r1 * cy); | 1296 secondQuad[1] = FloatPoint(quad[0].x() - r1 * cx, quad[0].y() - r1 * cy); |
1277 secondQuad[2] = quad[2]; | 1297 secondQuad[2] = quad[2]; |
1278 secondQuad[3] = quad[3]; | 1298 secondQuad[3] = quad[3]; |
1279 clipQuad(graphicsContext, secondQuad, secondMiter == SoftMiter); | 1299 clipQuad(graphicsContext, secondQuad, secondMiter == SoftMiter); |
1280 } | 1300 } |
1281 } | 1301 } |
1282 | 1302 |
1283 } // namespace blink | 1303 } // namespace blink |
OLD | NEW |