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

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

Issue 1171583003: Rework BoxBorderPainter's mitre clipping logic (Closed) Base URL: svn://svn.chromium.org/blink/trunk
Patch Set: expectations Created 5 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
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"
(...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after
73 const BorderEdgeFlags bottomLeftFlags = edgeFlagForSide(BSBottom) | edge FlagForSide(BSLeft); 73 const BorderEdgeFlags bottomLeftFlags = edgeFlagForSide(BSBottom) | edge FlagForSide(BSLeft);
74 74
75 BorderEdgeFlags flags = edgeFlagForSide(side) | edgeFlagForSide(adjacent Side); 75 BorderEdgeFlags flags = edgeFlagForSide(side) | edgeFlagForSide(adjacent Side);
76 return flags == topRightFlags || flags == bottomLeftFlags; 76 return flags == topRightFlags || flags == bottomLeftFlags;
77 } 77 }
78 return false; 78 return false;
79 } 79 }
80 80
81 inline bool colorsMatchAtCorner(BoxSide side, BoxSide adjacentSide, const Border Edge edges[]) 81 inline bool colorsMatchAtCorner(BoxSide side, BoxSide adjacentSide, const Border Edge edges[])
82 { 82 {
83 if (edges[side].shouldRender() != edges[adjacentSide].shouldRender()) 83 if (!edges[adjacentSide].shouldRender())
84 return false; 84 return false;
85 85
86 if (!edges[side].sharesColorWith(edges[adjacentSide])) 86 if (!edges[side].sharesColorWith(edges[adjacentSide]))
87 return false; 87 return false;
88 88
89 return !borderStyleHasUnmatchedColorsAtCorner(edges[side].borderStyle(), sid e, adjacentSide); 89 return !borderStyleHasUnmatchedColorsAtCorner(edges[side].borderStyle(), sid e, adjacentSide);
90 } 90 }
91 91
92 inline bool colorNeedsAntiAliasAtCorner(BoxSide side, BoxSide adjacentSide, cons t BorderEdge edges[])
93 {
94 if (!edges[side].color.hasAlpha())
95 return false;
96
97 if (edges[side].shouldRender() != edges[adjacentSide].shouldRender())
98 return false;
99
100 if (!edges[side].sharesColorWith(edges[adjacentSide]))
101 return true;
102
103 return borderStyleHasUnmatchedColorsAtCorner(edges[side].borderStyle(), side , adjacentSide);
104 }
105
106 inline bool borderWillArcInnerEdge(const FloatSize& firstRadius, const FloatSize & secondRadius) 92 inline bool borderWillArcInnerEdge(const FloatSize& firstRadius, const FloatSize & secondRadius)
107 { 93 {
108 return !firstRadius.isZero() || !secondRadius.isZero(); 94 return !firstRadius.isZero() || !secondRadius.isZero();
109 } 95 }
110 96
111 inline bool willOverdraw(BoxSide side, EBorderStyle style, BorderEdgeFlags compl etedEdges) 97 inline bool willOverdraw(BoxSide side, EBorderStyle style, BorderEdgeFlags compl etedEdges)
112 { 98 {
113 // If we're done with this side, it will obviously not overdraw any portion of the current edge. 99 // If we're done with this side, it will obviously not overdraw any portion of the current edge.
114 if (includesEdge(completedEdges, side)) 100 if (includesEdge(completedEdges, side))
115 return false; 101 return false;
116 102
117 // The side is still to be drawn. It overdraws the current edge iff it has a solid fill style. 103 // The side is still to be drawn. It overdraws the current edge iff it has a solid fill style.
118 return borderStyleFillsBorderArea(style); 104 return borderStyleFillsBorderArea(style);
119 } 105 }
120 106
121 inline bool borderStylesRequireMitre(BoxSide side, BoxSide adjacentSide, EBorder Style style, EBorderStyle adjacentStyle) 107 inline bool borderStylesRequireMitre(BoxSide side, BoxSide adjacentSide, EBorder Style style, EBorderStyle adjacentStyle)
122 { 108 {
123 if (style == DOUBLE || adjacentStyle == DOUBLE || adjacentStyle == GROOVE || adjacentStyle == RIDGE) 109 if (style == DOUBLE || adjacentStyle == DOUBLE || adjacentStyle == GROOVE || adjacentStyle == RIDGE)
124 return true; 110 return true;
125 111
126 if (borderStyleIsDottedOrDashed(style) != borderStyleIsDottedOrDashed(adjace ntStyle)) 112 if (borderStyleIsDottedOrDashed(style) != borderStyleIsDottedOrDashed(adjace ntStyle))
127 return true; 113 return true;
128 114
129 if (style != adjacentStyle) 115 if (style != adjacentStyle)
130 return true; 116 return true;
131 117
132 return borderStyleHasUnmatchedColorsAtCorner(style, side, adjacentSide); 118 return borderStyleHasUnmatchedColorsAtCorner(style, side, adjacentSide);
133 } 119 }
134 120
135 inline bool joinRequiresMitre(BoxSide side, BoxSide adjacentSide, const BorderEd ge edges[],
136 bool allowOverdraw, BorderEdgeFlags completedEdges)
137 {
138 if (!edges[adjacentSide].isPresent)
139 return false;
140
141 if (allowOverdraw && willOverdraw(adjacentSide, edges[adjacentSide].borderSt yle(), completedEdges))
142 return false;
143
144 if (!edges[side].sharesColorWith(edges[adjacentSide]))
145 return true;
146
147 if (borderStylesRequireMitre(side, adjacentSide, edges[side].borderStyle(), edges[adjacentSide].borderStyle()))
148 return true;
149
150 return false;
151 }
152
153 FloatRect calculateSideRect(const FloatRoundedRect& outerBorder, const BorderEdg e& edge, int side) 121 FloatRect calculateSideRect(const FloatRoundedRect& outerBorder, const BorderEdg e& edge, int side)
154 { 122 {
155 FloatRect sideRect = outerBorder.rect(); 123 FloatRect sideRect = outerBorder.rect();
156 int width = edge.width; 124 int width = edge.width;
157 125
158 if (side == BSTop) 126 if (side == BSTop)
159 sideRect.setHeight(width); 127 sideRect.setHeight(width);
160 else if (side == BSBottom) 128 else if (side == BSBottom)
161 sideRect.shiftYEdgeTo(sideRect.maxY() - width); 129 sideRect.shiftYEdgeTo(sideRect.maxY() - width);
162 else if (side == BSLeft) 130 else if (side == BSLeft)
(...skipping 633 matching lines...) Expand 10 before | Expand all | Expand 10 after
796 764
797 paintOneBorderSide(context, sideRect, BSRight, BSTop, BSBottom, path, bo rderInfo.antiAlias, 765 paintOneBorderSide(context, sideRect, BSRight, BSTop, BSBottom, path, bo rderInfo.antiAlias,
798 color, completedEdges); 766 color, completedEdges);
799 break; 767 break;
800 } 768 }
801 default: 769 default:
802 ASSERT_NOT_REACHED(); 770 ASSERT_NOT_REACHED();
803 } 771 }
804 } 772 }
805 773
774 BoxBorderPainter::MitreType BoxBorderPainter::computeMitre(BoxSide side, BoxSide adjacentSide,
775 BorderEdgeFlags completedEdges, bool antialias) const
776 {
777 const BorderEdge& adjacentEdge = m_edges[adjacentSide];
778
779 // No miters for missing edges.
780 if (!adjacentEdge.isPresent)
781 return NoMitre;
782
783 // Legacy behavior - preserve for now.
784 bool allowOverdraw = !antialias;
785
786 // The adjacent edge will overdraw this corner, resulting in a correct mitre .
787 if (allowOverdraw && willOverdraw(adjacentSide, adjacentEdge.borderStyle(), completedEdges))
788 return NoMitre;
789
790 // Color transitions require mitres. Use mitres compatible with the AA drawi ng mode to avoid
791 // introducing extra clips.
792 if (!colorsMatchAtCorner(side, adjacentSide, m_edges))
793 return antialias ? SoftMitre : HardMitre;
794
795 // Non-anti-aliased mitres ensure correct same-color seaming when required b y style.
796 if (borderStylesRequireMitre(side, adjacentSide, m_edges[side].borderStyle() , adjacentEdge.borderStyle()))
797 return HardMitre;
798
799 // Overdraw the adjacent edge when the colors match and we have no style res trictions.
800 return NoMitre;
801 }
802
803 bool BoxBorderPainter::mitresRequireClipping(MitreType mitre1, MitreType mitre2, EBorderStyle style,
804 bool antialias)
805 {
806 // Clipping is required if any of the present mitres doesn't match the curre nt AA mode.
807 bool shouldClip = antialias
808 ? mitre1 == HardMitre || mitre2 == HardMitre
809 : mitre1 == SoftMitre || mitre2 == SoftMitre;
810
811 // Some styles require clipping for any type of mitre.
812 shouldClip = shouldClip
813 || ((mitre1 != NoMitre || mitre2 != NoMitre) && styleRequiresClipPolygon (style));
814
815 return shouldClip;
816 }
817
806 void BoxBorderPainter::paintOneBorderSide(GraphicsContext* graphicsContext, 818 void BoxBorderPainter::paintOneBorderSide(GraphicsContext* graphicsContext,
807 const FloatRect& sideRect, BoxSide side, BoxSide adjacentSide1, BoxSide adja centSide2, 819 const FloatRect& sideRect, BoxSide side, BoxSide adjacentSide1, BoxSide adja centSide2,
808 const Path* path, bool antialias, Color color, BorderEdgeFlags completedEdge s) const 820 const Path* path, bool antialias, Color color, BorderEdgeFlags completedEdge s) const
809 { 821 {
810 const BorderEdge& edgeToRender = m_edges[side]; 822 const BorderEdge& edgeToRender = m_edges[side];
811 ASSERT(edgeToRender.width); 823 ASSERT(edgeToRender.width);
812 const BorderEdge& adjacentEdge1 = m_edges[adjacentSide1]; 824 const BorderEdge& adjacentEdge1 = m_edges[adjacentSide1];
813 const BorderEdge& adjacentEdge2 = m_edges[adjacentSide2]; 825 const BorderEdge& adjacentEdge2 = m_edges[adjacentSide2];
814 826
815 if (path) { 827 if (path) {
816 bool adjacentSide1StylesMatch = colorsMatchAtCorner(side, adjacentSide1, m_edges); 828 MitreType mitre1 = colorsMatchAtCorner(side, adjacentSide1, m_edges) ? H ardMitre : SoftMitre;
817 bool adjacentSide2StylesMatch = colorsMatchAtCorner(side, adjacentSide2, m_edges); 829 MitreType mitre2 = colorsMatchAtCorner(side, adjacentSide2, m_edges) ? H ardMitre : SoftMitre;
818 830
819 GraphicsContextStateSaver stateSaver(*graphicsContext); 831 GraphicsContextStateSaver stateSaver(*graphicsContext);
820 if (m_inner.isRenderable()) 832 if (m_inner.isRenderable())
821 clipBorderSidePolygon(graphicsContext, side, adjacentSide1StylesMatc h, adjacentSide2StylesMatch); 833 clipBorderSidePolygon(graphicsContext, side, mitre1, mitre2);
822 else 834 else
823 clipBorderSideForComplexInnerPath(graphicsContext, side); 835 clipBorderSideForComplexInnerPath(graphicsContext, side);
824 float thickness = std::max(std::max(edgeToRender.width, adjacentEdge1.wi dth), adjacentEdge2.width); 836 float thickness = std::max(std::max(edgeToRender.width, adjacentEdge1.wi dth), adjacentEdge2.width);
825 drawBoxSideFromPath(graphicsContext, LayoutRect(m_outer.rect()), *path, edgeToRender.width, 837 drawBoxSideFromPath(graphicsContext, LayoutRect(m_outer.rect()), *path, edgeToRender.width,
826 thickness, side, color, edgeToRender.borderStyle()); 838 thickness, side, color, edgeToRender.borderStyle());
827 } else { 839 } else {
828 bool mitreAdjacentSide1 = joinRequiresMitre(side, adjacentSide1, m_edges , !antialias, completedEdges); 840 MitreType mitre1 = computeMitre(side, adjacentSide1, completedEdges, ant ialias);
829 bool mitreAdjacentSide2 = joinRequiresMitre(side, adjacentSide2, m_edges , !antialias, completedEdges); 841 MitreType mitre2 = computeMitre(side, adjacentSide2, completedEdges, ant ialias);
830 842 bool shouldClip = mitresRequireClipping(mitre1, mitre2, edgeToRender.bor derStyle(), antialias);
831 bool clipForStyle = styleRequiresClipPolygon(edgeToRender.borderStyle())
832 && (mitreAdjacentSide1 || mitreAdjacentSide2);
833 bool clipAdjacentSide1 = colorNeedsAntiAliasAtCorner(side, adjacentSide1 , m_edges) && mitreAdjacentSide1;
834 bool clipAdjacentSide2 = colorNeedsAntiAliasAtCorner(side, adjacentSide2 , m_edges) && mitreAdjacentSide2;
835 bool shouldClip = clipForStyle || clipAdjacentSide1 || clipAdjacentSide2 ;
836 843
837 GraphicsContextStateSaver clipStateSaver(*graphicsContext, shouldClip); 844 GraphicsContextStateSaver clipStateSaver(*graphicsContext, shouldClip);
838 if (shouldClip) { 845 if (shouldClip) {
839 bool aliasAdjacentSide1 = clipAdjacentSide1 || (clipForStyle && mitr eAdjacentSide1); 846 clipBorderSidePolygon(graphicsContext, side, mitre1, mitre2);
840 bool aliasAdjacentSide2 = clipAdjacentSide2 || (clipForStyle && mitr eAdjacentSide2); 847
841 clipBorderSidePolygon(graphicsContext, side, !aliasAdjacentSide1, !a liasAdjacentSide2); 848 // Mitres are applied via clipping, no need to draw them.
842 // Since we clipped, no need to draw with a mitre. 849 mitre1 = mitre2 = NoMitre;
843 mitreAdjacentSide1 = false;
844 mitreAdjacentSide2 = false;
845 } 850 }
846 851
847 ObjectPainter::drawLineForBoxSide(graphicsContext, sideRect.x(), sideRec t.y(), 852 ObjectPainter::drawLineForBoxSide(graphicsContext, sideRect.x(), sideRec t.y(),
848 sideRect.maxX(), sideRect.maxY(), side, color, edgeToRender.borderSt yle(), 853 sideRect.maxX(), sideRect.maxY(), side, color, edgeToRender.borderSt yle(),
849 mitreAdjacentSide1 ? adjacentEdge1.width : 0, mitreAdjacentSide2 ? a djacentEdge2.width : 0, antialias); 854 mitre1 != NoMitre ? adjacentEdge1.width : 0, mitre2 != NoMitre ? adj acentEdge2.width : 0,
855 antialias);
850 } 856 }
851 } 857 }
852 858
853 void BoxBorderPainter::drawBoxSideFromPath(GraphicsContext* graphicsContext, 859 void BoxBorderPainter::drawBoxSideFromPath(GraphicsContext* graphicsContext,
854 const LayoutRect& borderRect, const Path& borderPath, float thickness, float drawThickness, 860 const LayoutRect& borderRect, const Path& borderPath, float thickness, float drawThickness,
855 BoxSide side, Color color, EBorderStyle borderStyle) const 861 BoxSide side, Color color, EBorderStyle borderStyle) const
856 { 862 {
857 if (thickness <= 0) 863 if (thickness <= 0)
858 return; 864 return;
859 865
(...skipping 133 matching lines...) Expand 10 before | Expand all | Expand 10 after
993 void BoxBorderPainter::clipBorderSideForComplexInnerPath(GraphicsContext* graphi csContext, 999 void BoxBorderPainter::clipBorderSideForComplexInnerPath(GraphicsContext* graphi csContext,
994 BoxSide side) const 1000 BoxSide side) const
995 { 1001 {
996 graphicsContext->clip(calculateSideRectIncludingInner(m_outer, m_edges, side )); 1002 graphicsContext->clip(calculateSideRectIncludingInner(m_outer, m_edges, side ));
997 FloatRoundedRect adjustedInnerRect = calculateAdjustedInnerBorder(m_inner, s ide); 1003 FloatRoundedRect adjustedInnerRect = calculateAdjustedInnerBorder(m_inner, s ide);
998 if (!adjustedInnerRect.isEmpty()) 1004 if (!adjustedInnerRect.isEmpty())
999 graphicsContext->clipOutRoundedRect(adjustedInnerRect); 1005 graphicsContext->clipOutRoundedRect(adjustedInnerRect);
1000 } 1006 }
1001 1007
1002 void BoxBorderPainter::clipBorderSidePolygon(GraphicsContext* graphicsContext, B oxSide side, 1008 void BoxBorderPainter::clipBorderSidePolygon(GraphicsContext* graphicsContext, B oxSide side,
1003 bool firstEdgeMatches, bool secondEdgeMatches) const 1009 MitreType firstMitre, MitreType secondMitre) const
1004 { 1010 {
1011 ASSERT(firstMitre != NoMitre || secondMitre != NoMitre);
1012
1005 FloatPoint quad[4]; 1013 FloatPoint quad[4];
1006 1014
1007 const LayoutRect outerRect(m_outer.rect()); 1015 const LayoutRect outerRect(m_outer.rect());
1008 const LayoutRect innerRect(m_inner.rect()); 1016 const LayoutRect innerRect(m_inner.rect());
1009 1017
1010 // For each side, create a quad that encompasses all parts of that side that may draw, 1018 // For each side, create a quad that encompasses all parts of that side that may draw,
1011 // including areas inside the innerBorder. 1019 // including areas inside the innerBorder.
1012 // 1020 //
1013 // 0----------------3 1021 // 0----------------3
1014 // 0 \ / 0 1022 // 0 \ / 0
(...skipping 117 matching lines...) Expand 10 before | Expand all | Expand 10 after
1132 quad[2].x() - m_inner.radii().bottomRight().width(), 1140 quad[2].x() - m_inner.radii().bottomRight().width(),
1133 quad[2].y()), 1141 quad[2].y()),
1134 FloatPoint( 1142 FloatPoint(
1135 quad[2].x(), 1143 quad[2].x(),
1136 quad[2].y() - m_inner.radii().bottomRight().height()), 1144 quad[2].y() - m_inner.radii().bottomRight().height()),
1137 quad[2]); 1145 quad[2]);
1138 } 1146 }
1139 break; 1147 break;
1140 } 1148 }
1141 1149
1142 // If the border matches both of its adjacent sides, don't anti-alias the cl ip, and 1150 if (firstMitre == secondMitre) {
1143 // if neither side matches, anti-alias the clip. 1151 graphicsContext->clipPolygon(4, quad, firstMitre == SoftMitre);
1144 if (firstEdgeMatches == secondEdgeMatches) {
1145 graphicsContext->clipPolygon(4, quad, !firstEdgeMatches);
1146 return; 1152 return;
1147 } 1153 }
1148 1154
1149 // If antialiasing settings for the first edge and second edge is different, 1155 // If antialiasing settings for the first edge and second edge is different,
1150 // they have to be addressed separately. We do this by breaking the quad int o 1156 // they have to be addressed separately. We do this by breaking the quad int o
1151 // two parallelograms, made by moving quad[1] and quad[2]. 1157 // two parallelograms, made by moving quad[1] and quad[2].
1152 float ax = quad[1].x() - quad[0].x(); 1158 float ax = quad[1].x() - quad[0].x();
1153 float ay = quad[1].y() - quad[0].y(); 1159 float ay = quad[1].y() - quad[0].y();
1154 float bx = quad[2].x() - quad[1].x(); 1160 float bx = quad[2].x() - quad[1].x();
1155 float by = quad[2].y() - quad[1].y(); 1161 float by = quad[2].y() - quad[1].y();
1156 float cx = quad[3].x() - quad[2].x(); 1162 float cx = quad[3].x() - quad[2].x();
1157 float cy = quad[3].y() - quad[2].y(); 1163 float cy = quad[3].y() - quad[2].y();
1158 1164
1159 const static float kEpsilon = 1e-2f; 1165 const static float kEpsilon = 1e-2f;
1160 float r1, r2; 1166 float r1, r2;
1161 if (fabsf(bx) < kEpsilon && fabsf(by) < kEpsilon) { 1167 if (fabsf(bx) < kEpsilon && fabsf(by) < kEpsilon) {
1162 // The quad was actually a triangle. 1168 // The quad was actually a triangle.
1163 r1 = r2 = 1.0f; 1169 r1 = r2 = 1.0f;
1164 } else { 1170 } else {
1165 // Extend parallelogram a bit to hide calculation error 1171 // Extend parallelogram a bit to hide calculation error
1166 const static float kExtendFill = 1e-2f; 1172 const static float kExtendFill = 1e-2f;
1167 1173
1168 r1 = (-ax * by + ay * bx) / (cx * by - cy * bx) + kExtendFill; 1174 r1 = (-ax * by + ay * bx) / (cx * by - cy * bx) + kExtendFill;
1169 r2 = (-cx * by + cy * bx) / (ax * by - ay * bx) + kExtendFill; 1175 r2 = (-cx * by + cy * bx) / (ax * by - ay * bx) + kExtendFill;
1170 } 1176 }
1171 1177
1172 FloatPoint firstQuad[4]; 1178 if (firstMitre != NoMitre) {
1173 firstQuad[0] = quad[0]; 1179 FloatPoint firstQuad[4];
1174 firstQuad[1] = quad[1]; 1180 firstQuad[0] = quad[0];
1175 firstQuad[2] = FloatPoint(quad[3].x() + r2 * ax, quad[3].y() + r2 * ay); 1181 firstQuad[1] = quad[1];
1176 firstQuad[3] = quad[3]; 1182 firstQuad[2] = FloatPoint(quad[3].x() + r2 * ax, quad[3].y() + r2 * ay);
1177 graphicsContext->clipPolygon(4, firstQuad, !firstEdgeMatches); 1183 firstQuad[3] = quad[3];
1184 graphicsContext->clipPolygon(4, firstQuad, firstMitre == SoftMitre);
1185 }
1178 1186
1179 FloatPoint secondQuad[4]; 1187 if (secondMitre != NoMitre) {
1180 secondQuad[0] = quad[0]; 1188 FloatPoint secondQuad[4];
1181 secondQuad[1] = FloatPoint(quad[0].x() - r1 * cx, quad[0].y() - r1 * cy); 1189 secondQuad[0] = quad[0];
1182 secondQuad[2] = quad[2]; 1190 secondQuad[1] = FloatPoint(quad[0].x() - r1 * cx, quad[0].y() - r1 * cy) ;
1183 secondQuad[3] = quad[3]; 1191 secondQuad[2] = quad[2];
1184 graphicsContext->clipPolygon(4, secondQuad, !secondEdgeMatches); 1192 secondQuad[3] = quad[3];
1193 graphicsContext->clipPolygon(4, secondQuad, secondMitre == SoftMitre);
1194 }
1185 } 1195 }
1186 1196
1187 } // namespace blink 1197 } // namespace blink
OLDNEW
« Source/core/paint/BoxBorderPainter.h ('K') | « Source/core/paint/BoxBorderPainter.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698