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

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

Issue 2695013011: Fix miter clipping of borders with large radius opposite a wide edge (Closed)
Patch Set: New image baselines Created 3 years, 10 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
« no previous file with comments | « third_party/WebKit/LayoutTests/platform/win7/fast/writing-mode/border-vertical-lr-expected.png ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2015 The Chromium Authors. All rights reserved. 1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "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 1106 matching lines...) Expand 10 before | Expand all | Expand 10 after
1117 if (!adjustedInnerRect.isEmpty()) 1117 if (!adjustedInnerRect.isEmpty())
1118 graphicsContext.clipOutRoundedRect(adjustedInnerRect); 1118 graphicsContext.clipOutRoundedRect(adjustedInnerRect);
1119 } 1119 }
1120 1120
1121 void BoxBorderPainter::clipBorderSidePolygon(GraphicsContext& graphicsContext, 1121 void BoxBorderPainter::clipBorderSidePolygon(GraphicsContext& graphicsContext,
1122 BoxSide side, 1122 BoxSide side,
1123 MiterType firstMiter, 1123 MiterType firstMiter,
1124 MiterType secondMiter) const { 1124 MiterType secondMiter) const {
1125 DCHECK(firstMiter != NoMiter || secondMiter != NoMiter); 1125 DCHECK(firstMiter != NoMiter || secondMiter != NoMiter);
1126 1126
1127 FloatPoint quad[4]; 1127 FloatPoint edgeQuad[4]; // The boundary of the edge for fill
1128 FloatPoint boundQuad1; // Point 1 of the rectilinear bounding box of EdgeQuad
1129 FloatPoint boundQuad2; // Point 2 of the rectilinear bounding box of EdgeQuad
1128 1130
1129 const LayoutRect outerRect(m_outer.rect()); 1131 const LayoutRect outerRect(m_outer.rect());
1130 const LayoutRect innerRect(m_inner.rect()); 1132 const LayoutRect innerRect(m_inner.rect());
1131 1133
1132 // For each side, create a quad that encompasses all parts of that side that 1134 // For each side, create a quad that encompasses all parts of that side that
1133 // may draw, including areas inside the innerBorder. 1135 // may draw, including areas inside the innerBorder.
1134 // 1136 //
1135 // 0----------------3 1137 // 0----------------3
1136 // 0 \ / 0 1138 // 3 \ / 0
1137 // |\ 1----------- 2 /| 1139 // |\ 1----------- 2 /|
1138 // | 1 1 | 1140 // | 2 1 |
1139 // | | | | 1141 // | | | |
1140 // | | | | 1142 // | | | |
1141 // | 2 2 | 1143 // | 1 2 |
1142 // |/ 1------------2 \| 1144 // |/ 2------------1 \|
1143 // 3 / \ 3 1145 // 0 / \ 3
1144 // 0----------------3 1146 // 3----------------0
1145 // 1147
1148 // Offset size and direction to expand clipping quad
1149 const static float kExtensionLength = 1e-1f;
1150 FloatSize extensionOffset;
1146 switch (side) { 1151 switch (side) {
1147 case BSTop: 1152 case BSTop:
1148 quad[0] = FloatPoint(outerRect.minXMinYCorner()); 1153 edgeQuad[0] = FloatPoint(outerRect.minXMinYCorner());
1149 quad[1] = FloatPoint(innerRect.minXMinYCorner()); 1154 edgeQuad[1] = FloatPoint(innerRect.minXMinYCorner());
1150 quad[2] = FloatPoint(innerRect.maxXMinYCorner()); 1155 edgeQuad[2] = FloatPoint(innerRect.maxXMinYCorner());
1151 quad[3] = FloatPoint(outerRect.maxXMinYCorner()); 1156 edgeQuad[3] = FloatPoint(outerRect.maxXMinYCorner());
1157
1158 DCHECK(edgeQuad[0].y() == edgeQuad[3].y());
1159 DCHECK(edgeQuad[1].y() == edgeQuad[2].y());
1160
1161 boundQuad1 = FloatPoint(edgeQuad[0].x(), edgeQuad[1].y());
1162 boundQuad2 = FloatPoint(edgeQuad[3].x(), edgeQuad[2].y());
1163
1164 extensionOffset.setWidth(-kExtensionLength);
1165 extensionOffset.setHeight(0);
1152 1166
1153 if (!m_inner.getRadii().topLeft().isZero()) { 1167 if (!m_inner.getRadii().topLeft().isZero()) {
1154 findIntersection( 1168 findIntersection(
1155 quad[0], quad[1], 1169 edgeQuad[0], edgeQuad[1],
1156 FloatPoint(quad[1].x() + m_inner.getRadii().topLeft().width(), 1170 FloatPoint(edgeQuad[1].x() + m_inner.getRadii().topLeft().width(),
1157 quad[1].y()), 1171 edgeQuad[1].y()),
1158 FloatPoint(quad[1].x(), 1172 FloatPoint(edgeQuad[1].x(),
1159 quad[1].y() + m_inner.getRadii().topLeft().height()), 1173 edgeQuad[1].y() + m_inner.getRadii().topLeft().height()),
1160 quad[1]); 1174 edgeQuad[1]);
1175 DCHECK(boundQuad1.y() <= edgeQuad[1].y());
1176 boundQuad1.setY(edgeQuad[1].y());
1177 boundQuad2.setY(edgeQuad[1].y());
1161 } 1178 }
1162 1179
1163 if (!m_inner.getRadii().topRight().isZero()) { 1180 if (!m_inner.getRadii().topRight().isZero()) {
1164 findIntersection( 1181 findIntersection(
1165 quad[3], quad[2], 1182 edgeQuad[3], edgeQuad[2],
1166 FloatPoint(quad[2].x() - m_inner.getRadii().topRight().width(), 1183 FloatPoint(edgeQuad[2].x() - m_inner.getRadii().topRight().width(),
1167 quad[2].y()), 1184 edgeQuad[2].y()),
1168 FloatPoint(quad[2].x(), 1185 FloatPoint(
1169 quad[2].y() + m_inner.getRadii().topRight().height()), 1186 edgeQuad[2].x(),
1170 quad[2]); 1187 edgeQuad[2].y() + m_inner.getRadii().topRight().height()),
1188 edgeQuad[2]);
1189 if (boundQuad1.y() < edgeQuad[2].y()) {
1190 boundQuad1.setY(edgeQuad[2].y());
1191 boundQuad2.setY(edgeQuad[2].y());
1192 }
1171 } 1193 }
1172 break; 1194 break;
1173 1195
1174 case BSLeft: 1196 case BSLeft:
1175 quad[0] = FloatPoint(outerRect.minXMinYCorner()); 1197 // Swap the order of adjacent edges to allow common code
1176 quad[1] = FloatPoint(innerRect.minXMinYCorner()); 1198 std::swap(firstMiter, secondMiter);
1177 quad[2] = FloatPoint(innerRect.minXMaxYCorner()); 1199 edgeQuad[0] = FloatPoint(outerRect.minXMaxYCorner());
1178 quad[3] = FloatPoint(outerRect.minXMaxYCorner()); 1200 edgeQuad[1] = FloatPoint(innerRect.minXMaxYCorner());
1201 edgeQuad[2] = FloatPoint(innerRect.minXMinYCorner());
1202 edgeQuad[3] = FloatPoint(outerRect.minXMinYCorner());
1203
1204 DCHECK(edgeQuad[0].x() == edgeQuad[3].x());
1205 DCHECK(edgeQuad[1].x() == edgeQuad[2].x());
1206
1207 boundQuad1 = FloatPoint(edgeQuad[1].x(), edgeQuad[0].y());
1208 boundQuad2 = FloatPoint(edgeQuad[2].x(), edgeQuad[3].y());
1209
1210 extensionOffset.setWidth(0);
1211 extensionOffset.setHeight(kExtensionLength);
1179 1212
1180 if (!m_inner.getRadii().topLeft().isZero()) { 1213 if (!m_inner.getRadii().topLeft().isZero()) {
1181 findIntersection( 1214 findIntersection(
1182 quad[0], quad[1], 1215 edgeQuad[3], edgeQuad[2],
1183 FloatPoint(quad[1].x() + m_inner.getRadii().topLeft().width(), 1216 FloatPoint(edgeQuad[2].x() + m_inner.getRadii().topLeft().width(),
1184 quad[1].y()), 1217 edgeQuad[2].y()),
1185 FloatPoint(quad[1].x(), 1218 FloatPoint(edgeQuad[2].x(),
1186 quad[1].y() + m_inner.getRadii().topLeft().height()), 1219 edgeQuad[2].y() + m_inner.getRadii().topLeft().height()),
1187 quad[1]); 1220 edgeQuad[2]);
1221 DCHECK(boundQuad2.x() <= edgeQuad[2].x());
1222 boundQuad1.setX(edgeQuad[2].x());
1223 boundQuad2.setX(edgeQuad[2].x());
1188 } 1224 }
1189 1225
1190 if (!m_inner.getRadii().bottomLeft().isZero()) { 1226 if (!m_inner.getRadii().bottomLeft().isZero()) {
1191 findIntersection( 1227 findIntersection(
1192 quad[3], quad[2], 1228 edgeQuad[0], edgeQuad[1],
1193 FloatPoint(quad[2].x() + m_inner.getRadii().bottomLeft().width(), 1229 FloatPoint(
1194 quad[2].y()), 1230 edgeQuad[1].x() + m_inner.getRadii().bottomLeft().width(),
1195 FloatPoint(quad[2].x(), 1231 edgeQuad[1].y()),
1196 quad[2].y() - m_inner.getRadii().bottomLeft().height()), 1232 FloatPoint(
1197 quad[2]); 1233 edgeQuad[1].x(),
1234 edgeQuad[1].y() - m_inner.getRadii().bottomLeft().height()),
1235 edgeQuad[1]);
1236 if (boundQuad1.x() < edgeQuad[1].x()) {
1237 boundQuad1.setX(edgeQuad[1].x());
1238 boundQuad2.setX(edgeQuad[1].x());
1239 }
1198 } 1240 }
1199 break; 1241 break;
1200 1242
1201 case BSBottom: 1243 case BSBottom:
1202 quad[0] = FloatPoint(outerRect.minXMaxYCorner()); 1244 // Swap the order of adjacent edges to allow common code
1203 quad[1] = FloatPoint(innerRect.minXMaxYCorner()); 1245 std::swap(firstMiter, secondMiter);
1204 quad[2] = FloatPoint(innerRect.maxXMaxYCorner()); 1246 edgeQuad[0] = FloatPoint(outerRect.maxXMaxYCorner());
1205 quad[3] = FloatPoint(outerRect.maxXMaxYCorner()); 1247 edgeQuad[1] = FloatPoint(innerRect.maxXMaxYCorner());
1248 edgeQuad[2] = FloatPoint(innerRect.minXMaxYCorner());
1249 edgeQuad[3] = FloatPoint(outerRect.minXMaxYCorner());
1250
1251 DCHECK(edgeQuad[0].y() == edgeQuad[3].y());
1252 DCHECK(edgeQuad[1].y() == edgeQuad[2].y());
1253
1254 boundQuad1 = FloatPoint(edgeQuad[0].x(), edgeQuad[1].y());
1255 boundQuad2 = FloatPoint(edgeQuad[3].x(), edgeQuad[2].y());
1256
1257 extensionOffset.setWidth(kExtensionLength);
1258 extensionOffset.setHeight(0);
1206 1259
1207 if (!m_inner.getRadii().bottomLeft().isZero()) { 1260 if (!m_inner.getRadii().bottomLeft().isZero()) {
1208 findIntersection( 1261 findIntersection(
1209 quad[0], quad[1], 1262 edgeQuad[3], edgeQuad[2],
1210 FloatPoint(quad[1].x() + m_inner.getRadii().bottomLeft().width(), 1263 FloatPoint(
1211 quad[1].y()), 1264 edgeQuad[2].x() + m_inner.getRadii().bottomLeft().width(),
1212 FloatPoint(quad[1].x(), 1265 edgeQuad[2].y()),
1213 quad[1].y() - m_inner.getRadii().bottomLeft().height()), 1266 FloatPoint(
1214 quad[1]); 1267 edgeQuad[2].x(),
1268 edgeQuad[2].y() - m_inner.getRadii().bottomLeft().height()),
1269 edgeQuad[2]);
1270 DCHECK(boundQuad2.y() >= edgeQuad[2].y());
1271 boundQuad1.setY(edgeQuad[2].y());
1272 boundQuad2.setY(edgeQuad[2].y());
1215 } 1273 }
1216 1274
1217 if (!m_inner.getRadii().bottomRight().isZero()) { 1275 if (!m_inner.getRadii().bottomRight().isZero()) {
1218 findIntersection( 1276 findIntersection(
1219 quad[3], quad[2], 1277 edgeQuad[0], edgeQuad[1],
1220 FloatPoint(quad[2].x() - m_inner.getRadii().bottomRight().width(), 1278 FloatPoint(
1221 quad[2].y()), 1279 edgeQuad[1].x() - m_inner.getRadii().bottomRight().width(),
1222 FloatPoint(quad[2].x(), 1280 edgeQuad[1].y()),
1223 quad[2].y() - m_inner.getRadii().bottomRight().height()), 1281 FloatPoint(
1224 quad[2]); 1282 edgeQuad[1].x(),
1283 edgeQuad[1].y() - m_inner.getRadii().bottomRight().height()),
1284 edgeQuad[1]);
1285 if (boundQuad1.y() > edgeQuad[1].y()) {
1286 boundQuad1.setY(edgeQuad[1].y());
1287 boundQuad2.setY(edgeQuad[1].y());
1288 }
1225 } 1289 }
1226 break; 1290 break;
1227 1291
1228 case BSRight: 1292 case BSRight:
1229 quad[0] = FloatPoint(outerRect.maxXMinYCorner()); 1293 edgeQuad[0] = FloatPoint(outerRect.maxXMinYCorner());
1230 quad[1] = FloatPoint(innerRect.maxXMinYCorner()); 1294 edgeQuad[1] = FloatPoint(innerRect.maxXMinYCorner());
1231 quad[2] = FloatPoint(innerRect.maxXMaxYCorner()); 1295 edgeQuad[2] = FloatPoint(innerRect.maxXMaxYCorner());
1232 quad[3] = FloatPoint(outerRect.maxXMaxYCorner()); 1296 edgeQuad[3] = FloatPoint(outerRect.maxXMaxYCorner());
1297
1298 DCHECK(edgeQuad[0].x() == edgeQuad[3].x());
1299 DCHECK(edgeQuad[1].x() == edgeQuad[2].x());
1300
1301 boundQuad1 = FloatPoint(edgeQuad[1].x(), edgeQuad[0].y());
1302 boundQuad2 = FloatPoint(edgeQuad[2].x(), edgeQuad[3].y());
1303
1304 extensionOffset.setWidth(0);
1305 extensionOffset.setHeight(-kExtensionLength);
1233 1306
1234 if (!m_inner.getRadii().topRight().isZero()) { 1307 if (!m_inner.getRadii().topRight().isZero()) {
1235 findIntersection( 1308 findIntersection(
1236 quad[0], quad[1], 1309 edgeQuad[0], edgeQuad[1],
1237 FloatPoint(quad[1].x() - m_inner.getRadii().topRight().width(), 1310 FloatPoint(edgeQuad[1].x() - m_inner.getRadii().topRight().width(),
1238 quad[1].y()), 1311 edgeQuad[1].y()),
1239 FloatPoint(quad[1].x(), 1312 FloatPoint(
1240 quad[1].y() + m_inner.getRadii().topRight().height()), 1313 edgeQuad[1].x(),
1241 quad[1]); 1314 edgeQuad[1].y() + m_inner.getRadii().topRight().height()),
1315 edgeQuad[1]);
1316 DCHECK(boundQuad1.x() >= edgeQuad[1].x());
1317 boundQuad1.setX(edgeQuad[1].x());
1318 boundQuad2.setX(edgeQuad[1].x());
1242 } 1319 }
1243 1320
1244 if (!m_inner.getRadii().bottomRight().isZero()) { 1321 if (!m_inner.getRadii().bottomRight().isZero()) {
1245 findIntersection( 1322 findIntersection(
1246 quad[3], quad[2], 1323 edgeQuad[3], edgeQuad[2],
1247 FloatPoint(quad[2].x() - m_inner.getRadii().bottomRight().width(), 1324 FloatPoint(
1248 quad[2].y()), 1325 edgeQuad[2].x() - m_inner.getRadii().bottomRight().width(),
1249 FloatPoint(quad[2].x(), 1326 edgeQuad[2].y()),
1250 quad[2].y() - m_inner.getRadii().bottomRight().height()), 1327 FloatPoint(
1251 quad[2]); 1328 edgeQuad[2].x(),
1329 edgeQuad[2].y() - m_inner.getRadii().bottomRight().height()),
1330 edgeQuad[2]);
1331 if (boundQuad1.x() > edgeQuad[2].x()) {
1332 boundQuad1.setX(edgeQuad[2].x());
1333 boundQuad2.setX(edgeQuad[2].x());
1334 }
1252 } 1335 }
1253 break; 1336 break;
1254 } 1337 }
1255 1338
1256 if (firstMiter == secondMiter) { 1339 if (firstMiter == secondMiter) {
1257 clipQuad(graphicsContext, quad, firstMiter == SoftMiter); 1340 clipQuad(graphicsContext, edgeQuad, firstMiter == SoftMiter);
1258 return; 1341 return;
1259 } 1342 }
1260 1343
1261 // If antialiasing settings for the first edge and second edge is different, 1344 // If antialiasing settings for the first edge and second edge are different,
1262 // they have to be addressed separately. We do this by breaking the quad into 1345 // they have to be addressed separately. We do this by applying 2 clips, one
1263 // two parallelograms, made by moving quad[1] and quad[2]. 1346 // for each miter, with the appropriate anti-aliasing setting. Each clip uses
1264 float ax = quad[1].x() - quad[0].x(); 1347 // 3 sides of the quad rectilinear bounding box and a 4th side aligned with
1265 float ay = quad[1].y() - quad[0].y(); 1348 // the miter edge. We extend the clip in the miter direction to ensure overlap
1266 float bx = quad[2].x() - quad[1].x(); 1349 // as each edge is drawn.
1267 float by = quad[2].y() - quad[1].y();
1268 float cx = quad[3].x() - quad[2].x();
1269 float cy = quad[3].y() - quad[2].y();
1270
1271 const static float kEpsilon = 1e-2f;
1272 float r1, r2;
1273 if (fabsf(bx) < kEpsilon && fabsf(by) < kEpsilon) {
1274 // The quad was actually a triangle.
1275 r1 = r2 = 1.0f;
1276 } else {
1277 // Extend parallelogram a bit to hide calculation error
1278 const static float kExtendFill = 1e-2f;
1279
1280 r1 = (-ax * by + ay * bx) / (cx * by - cy * bx) + kExtendFill;
1281 r2 = (-cx * by + cy * bx) / (ax * by - ay * bx) + kExtendFill;
1282 }
1283
1284 if (firstMiter != NoMiter) { 1350 if (firstMiter != NoMiter) {
1285 FloatPoint firstQuad[4]; 1351 FloatPoint clippingQuad[4];
1286 firstQuad[0] = quad[0]; 1352
1287 firstQuad[1] = quad[1]; 1353 clippingQuad[0] = edgeQuad[0] + extensionOffset;
1288 firstQuad[2] = FloatPoint(quad[3].x() + r2 * ax, quad[3].y() + r2 * ay); 1354 findIntersection(edgeQuad[0], edgeQuad[1], boundQuad1, boundQuad2,
1289 firstQuad[3] = quad[3]; 1355 clippingQuad[1]);
1290 clipQuad(graphicsContext, firstQuad, firstMiter == SoftMiter); 1356 clippingQuad[1] += extensionOffset;
1357 clippingQuad[2] = boundQuad2;
1358 clippingQuad[3] = edgeQuad[3];
1359
1360 clipQuad(graphicsContext, clippingQuad, firstMiter == SoftMiter);
1291 } 1361 }
1292 1362
1293 if (secondMiter != NoMiter) { 1363 if (secondMiter != NoMiter) {
1294 FloatPoint secondQuad[4]; 1364 FloatPoint clippingQuad[4];
1295 secondQuad[0] = quad[0]; 1365
1296 secondQuad[1] = FloatPoint(quad[0].x() - r1 * cx, quad[0].y() - r1 * cy); 1366 clippingQuad[0] = edgeQuad[0];
1297 secondQuad[2] = quad[2]; 1367 clippingQuad[1] = boundQuad1;
1298 secondQuad[3] = quad[3]; 1368 findIntersection(edgeQuad[2], edgeQuad[3], boundQuad1, boundQuad2,
1299 clipQuad(graphicsContext, secondQuad, secondMiter == SoftMiter); 1369 clippingQuad[2]);
1370 clippingQuad[2] -= extensionOffset;
1371 clippingQuad[3] = edgeQuad[3] - extensionOffset;
1372
1373 clipQuad(graphicsContext, clippingQuad, secondMiter == SoftMiter);
1300 } 1374 }
1301 } 1375 }
1302 1376
1303 } // namespace blink 1377 } // namespace blink
OLDNEW
« no previous file with comments | « third_party/WebKit/LayoutTests/platform/win7/fast/writing-mode/border-vertical-lr-expected.png ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698