| 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 |