| OLD | NEW |
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 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/BoxPainter.h" | 6 #include "core/paint/BoxPainter.h" |
| 7 | 7 |
| 8 #include "core/HTMLNames.h" | 8 #include "core/HTMLNames.h" |
| 9 #include "core/frame/Settings.h" | 9 #include "core/frame/Settings.h" |
| 10 #include "core/html/HTMLFrameOwnerElement.h" | 10 #include "core/html/HTMLFrameOwnerElement.h" |
| 11 #include "core/layout/ImageQualityController.h" | 11 #include "core/layout/ImageQualityController.h" |
| 12 #include "core/layout/LayoutBox.h" | 12 #include "core/layout/LayoutBox.h" |
| 13 #include "core/layout/LayoutBoxModelObject.h" | 13 #include "core/layout/LayoutBoxModelObject.h" |
| 14 #include "core/layout/LayoutObject.h" | 14 #include "core/layout/LayoutObject.h" |
| 15 #include "core/layout/LayoutTable.h" | 15 #include "core/layout/LayoutTable.h" |
| 16 #include "core/layout/LayoutTheme.h" | 16 #include "core/layout/LayoutTheme.h" |
| 17 #include "core/layout/LayoutView.h" | 17 #include "core/layout/LayoutView.h" |
| 18 #include "core/layout/compositing/CompositedDeprecatedPaintLayerMapping.h" | 18 #include "core/layout/compositing/CompositedDeprecatedPaintLayerMapping.h" |
| 19 #include "core/style/BorderEdge.h" | 19 #include "core/style/BorderEdge.h" |
| 20 #include "core/style/ShadowList.h" | 20 #include "core/style/ShadowList.h" |
| 21 #include "core/paint/BackgroundImageGeometry.h" | 21 #include "core/paint/BackgroundImageGeometry.h" |
| 22 #include "core/paint/BoxBorderPainter.h" |
| 22 #include "core/paint/BoxDecorationData.h" | 23 #include "core/paint/BoxDecorationData.h" |
| 23 #include "core/paint/DeprecatedPaintLayer.h" | 24 #include "core/paint/DeprecatedPaintLayer.h" |
| 24 #include "core/paint/LayoutObjectDrawingRecorder.h" | 25 #include "core/paint/LayoutObjectDrawingRecorder.h" |
| 25 #include "core/paint/PaintInfo.h" | 26 #include "core/paint/PaintInfo.h" |
| 26 #include "core/paint/RoundedInnerRectClipper.h" | 27 #include "core/paint/RoundedInnerRectClipper.h" |
| 27 #include "core/paint/ThemePainter.h" | 28 #include "core/paint/ThemePainter.h" |
| 28 #include "platform/LengthFunctions.h" | 29 #include "platform/LengthFunctions.h" |
| 29 #include "platform/geometry/LayoutPoint.h" | 30 #include "platform/geometry/LayoutPoint.h" |
| 30 #include "platform/geometry/LayoutRectOutsets.h" | 31 #include "platform/geometry/LayoutRectOutsets.h" |
| 31 #include "platform/graphics/GraphicsContextStateSaver.h" | 32 #include "platform/graphics/GraphicsContextStateSaver.h" |
| (...skipping 1090 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1122 | 1123 |
| 1123 graphicsContext->drawTiledImage(image.get(), | 1124 graphicsContext->drawTiledImage(image.get(), |
| 1124 IntRect(borderImageRect.x() + leftWidth, borderImageRect.y() + topWi
dth, destinationWidth, destinationHeight), | 1125 IntRect(borderImageRect.x() + leftWidth, borderImageRect.y() + topWi
dth, destinationWidth, destinationHeight), |
| 1125 IntRect(leftSlice, topSlice, sourceWidth, sourceHeight), | 1126 IntRect(leftSlice, topSlice, sourceWidth, sourceHeight), |
| 1126 middleScaleFactor, (Image::TileRule)hRule, (Image::TileRule)vRule, o
p); | 1127 middleScaleFactor, (Image::TileRule)hRule, (Image::TileRule)vRule, o
p); |
| 1127 } | 1128 } |
| 1128 graphicsContext->setImageInterpolationQuality(previousInterpolationQuality); | 1129 graphicsContext->setImageInterpolationQuality(previousInterpolationQuality); |
| 1129 return true; | 1130 return true; |
| 1130 } | 1131 } |
| 1131 | 1132 |
| 1132 static FloatRect calculateSideRect(const FloatRoundedRect& outerBorder, const Bo
rderEdge& edge, int side) | |
| 1133 { | |
| 1134 FloatRect sideRect = outerBorder.rect(); | |
| 1135 int width = edge.width; | |
| 1136 | |
| 1137 if (side == BSTop) | |
| 1138 sideRect.setHeight(width); | |
| 1139 else if (side == BSBottom) | |
| 1140 sideRect.shiftYEdgeTo(sideRect.maxY() - width); | |
| 1141 else if (side == BSLeft) | |
| 1142 sideRect.setWidth(width); | |
| 1143 else | |
| 1144 sideRect.shiftXEdgeTo(sideRect.maxX() - width); | |
| 1145 | |
| 1146 return sideRect; | |
| 1147 } | |
| 1148 | |
| 1149 enum BorderEdgeFlag { | |
| 1150 TopBorderEdge = 1 << BSTop, | |
| 1151 RightBorderEdge = 1 << BSRight, | |
| 1152 BottomBorderEdge = 1 << BSBottom, | |
| 1153 LeftBorderEdge = 1 << BSLeft, | |
| 1154 AllBorderEdges = TopBorderEdge | BottomBorderEdge | LeftBorderEdge | RightBo
rderEdge | |
| 1155 }; | |
| 1156 | |
| 1157 static inline BorderEdgeFlag edgeFlagForSide(BoxSide side) | |
| 1158 { | |
| 1159 return static_cast<BorderEdgeFlag>(1 << side); | |
| 1160 } | |
| 1161 | |
| 1162 static inline bool includesEdge(BorderEdgeFlags flags, BoxSide side) | |
| 1163 { | |
| 1164 return flags & edgeFlagForSide(side); | |
| 1165 } | |
| 1166 | |
| 1167 inline bool styleRequiresClipPolygon(EBorderStyle style) | |
| 1168 { | |
| 1169 return style == DOTTED || style == DASHED; // These are drawn with a stroke,
so we have to clip to get corner miters. | |
| 1170 } | |
| 1171 | |
| 1172 static bool borderStyleFillsBorderArea(EBorderStyle style) | |
| 1173 { | |
| 1174 return !(style == DOTTED || style == DASHED || style == DOUBLE); | |
| 1175 } | |
| 1176 | |
| 1177 static bool borderStyleHasInnerDetail(EBorderStyle style) | |
| 1178 { | |
| 1179 return style == GROOVE || style == RIDGE || style == DOUBLE; | |
| 1180 } | |
| 1181 | |
| 1182 static bool borderStyleIsDottedOrDashed(EBorderStyle style) | |
| 1183 { | |
| 1184 return style == DOTTED || style == DASHED; | |
| 1185 } | |
| 1186 | |
| 1187 // OUTSET darkens the bottom and right (and maybe lightens the top and left) | |
| 1188 // INSET darkens the top and left (and maybe lightens the bottom and right) | |
| 1189 static inline bool borderStyleHasUnmatchedColorsAtCorner(EBorderStyle style, Box
Side side, BoxSide adjacentSide) | |
| 1190 { | |
| 1191 // These styles match at the top/left and bottom/right. | |
| 1192 if (style == INSET || style == GROOVE || style == RIDGE || style == OUTSET)
{ | |
| 1193 const BorderEdgeFlags topRightFlags = edgeFlagForSide(BSTop) | edgeFlagF
orSide(BSRight); | |
| 1194 const BorderEdgeFlags bottomLeftFlags = edgeFlagForSide(BSBottom) | edge
FlagForSide(BSLeft); | |
| 1195 | |
| 1196 BorderEdgeFlags flags = edgeFlagForSide(side) | edgeFlagForSide(adjacent
Side); | |
| 1197 return flags == topRightFlags || flags == bottomLeftFlags; | |
| 1198 } | |
| 1199 return false; | |
| 1200 } | |
| 1201 | |
| 1202 static inline bool colorsMatchAtCorner(BoxSide side, BoxSide adjacentSide, const
BorderEdge edges[]) | |
| 1203 { | |
| 1204 if (edges[side].shouldRender() != edges[adjacentSide].shouldRender()) | |
| 1205 return false; | |
| 1206 | |
| 1207 if (!edges[side].sharesColorWith(edges[adjacentSide])) | |
| 1208 return false; | |
| 1209 | |
| 1210 return !borderStyleHasUnmatchedColorsAtCorner(edges[side].borderStyle(), sid
e, adjacentSide); | |
| 1211 } | |
| 1212 | |
| 1213 static inline bool colorNeedsAntiAliasAtCorner(BoxSide side, BoxSide adjacentSid
e, const BorderEdge edges[]) | |
| 1214 { | |
| 1215 if (!edges[side].color.hasAlpha()) | |
| 1216 return false; | |
| 1217 | |
| 1218 if (edges[side].shouldRender() != edges[adjacentSide].shouldRender()) | |
| 1219 return false; | |
| 1220 | |
| 1221 if (!edges[side].sharesColorWith(edges[adjacentSide])) | |
| 1222 return true; | |
| 1223 | |
| 1224 return borderStyleHasUnmatchedColorsAtCorner(edges[side].borderStyle(), side
, adjacentSide); | |
| 1225 } | |
| 1226 | |
| 1227 bool BoxPainter::shouldAntialiasLines(GraphicsContext* context) | 1133 bool BoxPainter::shouldAntialiasLines(GraphicsContext* context) |
| 1228 { | 1134 { |
| 1229 // FIXME: We may want to not antialias when scaled by an integral value, | 1135 // FIXME: We may want to not antialias when scaled by an integral value, |
| 1230 // and we may want to antialias when translated by a non-integral value. | 1136 // and we may want to antialias when translated by a non-integral value. |
| 1231 // FIXME: See crbug.com/382491. getCTM does not include scale factors applie
d at raster time, such | 1137 // FIXME: See crbug.com/382491. getCTM does not include scale factors applie
d at raster time, such |
| 1232 // as device zoom. | 1138 // as device zoom. |
| 1233 return !context->getCTM().isIdentityOrTranslationOrFlipped(); | 1139 return !context->getCTM().isIdentityOrTranslationOrFlipped(); |
| 1234 } | 1140 } |
| 1235 | 1141 |
| 1236 static bool borderWillArcInnerEdge(const FloatSize& firstRadius, const FloatSize
& secondRadius) | 1142 bool BoxPainter::allCornersClippedOut(const FloatRoundedRect& border, const IntR
ect& intClipRect) |
| 1237 { | |
| 1238 return !firstRadius.isZero() || !secondRadius.isZero(); | |
| 1239 } | |
| 1240 | |
| 1241 // This assumes that we draw in order: top, bottom, left, right. | |
| 1242 static inline bool willBeOverdrawn(BoxSide side, BoxSide adjacentSide, const Bor
derEdge edges[]) | |
| 1243 { | |
| 1244 switch (side) { | |
| 1245 case BSTop: | |
| 1246 case BSBottom: | |
| 1247 if (edges[adjacentSide].presentButInvisible()) | |
| 1248 return false; | |
| 1249 | |
| 1250 if (!edges[side].sharesColorWith(edges[adjacentSide]) && edges[adjacentS
ide].color.hasAlpha()) | |
| 1251 return false; | |
| 1252 | |
| 1253 if (!borderStyleFillsBorderArea(edges[adjacentSide].borderStyle())) | |
| 1254 return false; | |
| 1255 | |
| 1256 return true; | |
| 1257 | |
| 1258 case BSLeft: | |
| 1259 case BSRight: | |
| 1260 // These draw last, so are never overdrawn. | |
| 1261 return false; | |
| 1262 } | |
| 1263 return false; | |
| 1264 } | |
| 1265 | |
| 1266 static inline bool borderStylesRequireMitre(BoxSide side, BoxSide adjacentSide,
EBorderStyle style, EBorderStyle adjacentStyle) | |
| 1267 { | |
| 1268 if (style == DOUBLE || adjacentStyle == DOUBLE || adjacentStyle == GROOVE ||
adjacentStyle == RIDGE) | |
| 1269 return true; | |
| 1270 | |
| 1271 if (borderStyleIsDottedOrDashed(style) != borderStyleIsDottedOrDashed(adjace
ntStyle)) | |
| 1272 return true; | |
| 1273 | |
| 1274 if (style != adjacentStyle) | |
| 1275 return true; | |
| 1276 | |
| 1277 return borderStyleHasUnmatchedColorsAtCorner(style, side, adjacentSide); | |
| 1278 } | |
| 1279 | |
| 1280 static bool joinRequiresMitre(BoxSide side, BoxSide adjacentSide, const BorderEd
ge edges[], bool allowOverdraw) | |
| 1281 { | |
| 1282 if ((edges[side].isTransparent && edges[adjacentSide].isTransparent) || !edg
es[adjacentSide].isPresent) | |
| 1283 return false; | |
| 1284 | |
| 1285 if (allowOverdraw && willBeOverdrawn(side, adjacentSide, edges)) | |
| 1286 return false; | |
| 1287 | |
| 1288 if (!edges[side].sharesColorWith(edges[adjacentSide])) | |
| 1289 return true; | |
| 1290 | |
| 1291 if (borderStylesRequireMitre(side, adjacentSide, edges[side].borderStyle(),
edges[adjacentSide].borderStyle())) | |
| 1292 return true; | |
| 1293 | |
| 1294 return false; | |
| 1295 } | |
| 1296 | |
| 1297 static FloatRect calculateSideRectIncludingInner(const FloatRoundedRect& outerBo
rder, const BorderEdge edges[], BoxSide side) | |
| 1298 { | |
| 1299 FloatRect sideRect = outerBorder.rect(); | |
| 1300 int width; | |
| 1301 | |
| 1302 switch (side) { | |
| 1303 case BSTop: | |
| 1304 width = sideRect.height() - edges[BSBottom].width; | |
| 1305 sideRect.setHeight(width); | |
| 1306 break; | |
| 1307 case BSBottom: | |
| 1308 width = sideRect.height() - edges[BSTop].width; | |
| 1309 sideRect.shiftYEdgeTo(sideRect.maxY() - width); | |
| 1310 break; | |
| 1311 case BSLeft: | |
| 1312 width = sideRect.width() - edges[BSRight].width; | |
| 1313 sideRect.setWidth(width); | |
| 1314 break; | |
| 1315 case BSRight: | |
| 1316 width = sideRect.width() - edges[BSLeft].width; | |
| 1317 sideRect.shiftXEdgeTo(sideRect.maxX() - width); | |
| 1318 break; | |
| 1319 } | |
| 1320 | |
| 1321 return sideRect; | |
| 1322 } | |
| 1323 | |
| 1324 static FloatRoundedRect calculateAdjustedInnerBorder(const FloatRoundedRect& inn
erBorder, BoxSide side) | |
| 1325 { | |
| 1326 // Expand the inner border as necessary to make it a rounded rect (i.e. radi
i contained within each edge). | |
| 1327 // This function relies on the fact we only get radii not contained within e
ach edge if one of the radii | |
| 1328 // for an edge is zero, so we can shift the arc towards the zero radius corn
er. | |
| 1329 FloatRoundedRect::Radii newRadii = innerBorder.radii(); | |
| 1330 FloatRect newRect = innerBorder.rect(); | |
| 1331 | |
| 1332 float overshoot; | |
| 1333 float maxRadii; | |
| 1334 | |
| 1335 switch (side) { | |
| 1336 case BSTop: | |
| 1337 overshoot = newRadii.topLeft().width() + newRadii.topRight().width() - n
ewRect.width(); | |
| 1338 // FIXME: once we start pixel-snapping rounded rects after this point, t
he overshoot concept | |
| 1339 // should disappear. | |
| 1340 if (overshoot > 0.1) { | |
| 1341 newRect.setWidth(newRect.width() + overshoot); | |
| 1342 if (!newRadii.topLeft().width()) | |
| 1343 newRect.move(-overshoot, 0); | |
| 1344 } | |
| 1345 newRadii.setBottomLeft(IntSize(0, 0)); | |
| 1346 newRadii.setBottomRight(IntSize(0, 0)); | |
| 1347 maxRadii = std::max(newRadii.topLeft().height(), newRadii.topRight().hei
ght()); | |
| 1348 if (maxRadii > newRect.height()) | |
| 1349 newRect.setHeight(maxRadii); | |
| 1350 break; | |
| 1351 | |
| 1352 case BSBottom: | |
| 1353 overshoot = newRadii.bottomLeft().width() + newRadii.bottomRight().width
() - newRect.width(); | |
| 1354 if (overshoot > 0.1) { | |
| 1355 newRect.setWidth(newRect.width() + overshoot); | |
| 1356 if (!newRadii.bottomLeft().width()) | |
| 1357 newRect.move(-overshoot, 0); | |
| 1358 } | |
| 1359 newRadii.setTopLeft(IntSize(0, 0)); | |
| 1360 newRadii.setTopRight(IntSize(0, 0)); | |
| 1361 maxRadii = std::max(newRadii.bottomLeft().height(), newRadii.bottomRight
().height()); | |
| 1362 if (maxRadii > newRect.height()) { | |
| 1363 newRect.move(0, newRect.height() - maxRadii); | |
| 1364 newRect.setHeight(maxRadii); | |
| 1365 } | |
| 1366 break; | |
| 1367 | |
| 1368 case BSLeft: | |
| 1369 overshoot = newRadii.topLeft().height() + newRadii.bottomLeft().height()
- newRect.height(); | |
| 1370 if (overshoot > 0.1) { | |
| 1371 newRect.setHeight(newRect.height() + overshoot); | |
| 1372 if (!newRadii.topLeft().height()) | |
| 1373 newRect.move(0, -overshoot); | |
| 1374 } | |
| 1375 newRadii.setTopRight(IntSize(0, 0)); | |
| 1376 newRadii.setBottomRight(IntSize(0, 0)); | |
| 1377 maxRadii = std::max(newRadii.topLeft().width(), newRadii.bottomLeft().wi
dth()); | |
| 1378 if (maxRadii > newRect.width()) | |
| 1379 newRect.setWidth(maxRadii); | |
| 1380 break; | |
| 1381 | |
| 1382 case BSRight: | |
| 1383 overshoot = newRadii.topRight().height() + newRadii.bottomRight().height
() - newRect.height(); | |
| 1384 if (overshoot > 0.1) { | |
| 1385 newRect.setHeight(newRect.height() + overshoot); | |
| 1386 if (!newRadii.topRight().height()) | |
| 1387 newRect.move(0, -overshoot); | |
| 1388 } | |
| 1389 newRadii.setTopLeft(IntSize(0, 0)); | |
| 1390 newRadii.setBottomLeft(IntSize(0, 0)); | |
| 1391 maxRadii = std::max(newRadii.topRight().width(), newRadii.bottomRight().
width()); | |
| 1392 if (maxRadii > newRect.width()) { | |
| 1393 newRect.move(newRect.width() - maxRadii, 0); | |
| 1394 newRect.setWidth(maxRadii); | |
| 1395 } | |
| 1396 break; | |
| 1397 } | |
| 1398 | |
| 1399 return FloatRoundedRect(newRect, newRadii); | |
| 1400 } | |
| 1401 | |
| 1402 void BoxPainter::clipBorderSideForComplexInnerPath(GraphicsContext* graphicsCont
ext, const FloatRoundedRect& outerBorder, const FloatRoundedRect& innerBorder, | |
| 1403 BoxSide side, const BorderEdge edges[]) | |
| 1404 { | |
| 1405 graphicsContext->clip(calculateSideRectIncludingInner(outerBorder, edges, si
de)); | |
| 1406 FloatRoundedRect adjustedInnerRect = calculateAdjustedInnerBorder(innerBorde
r, side); | |
| 1407 if (!adjustedInnerRect.isEmpty()) | |
| 1408 graphicsContext->clipOutRoundedRect(adjustedInnerRect); | |
| 1409 } | |
| 1410 | |
| 1411 namespace { | |
| 1412 | |
| 1413 bool allCornersClippedOut(const FloatRoundedRect& border, const IntRect& intClip
Rect) | |
| 1414 { | 1143 { |
| 1415 LayoutRect boundingRect(border.rect()); | 1144 LayoutRect boundingRect(border.rect()); |
| 1416 LayoutRect clipRect(intClipRect); | 1145 LayoutRect clipRect(intClipRect); |
| 1417 if (clipRect.contains(boundingRect)) | 1146 if (clipRect.contains(boundingRect)) |
| 1418 return false; | 1147 return false; |
| 1419 | 1148 |
| 1420 FloatRoundedRect::Radii radii = border.radii(); | 1149 FloatRoundedRect::Radii radii = border.radii(); |
| 1421 | 1150 |
| 1422 LayoutRect topLeftRect(boundingRect.location(), LayoutSize(radii.topLeft()))
; | 1151 LayoutRect topLeftRect(boundingRect.location(), LayoutSize(radii.topLeft()))
; |
| 1423 if (clipRect.intersects(topLeftRect)) | 1152 if (clipRect.intersects(topLeftRect)) |
| (...skipping 11 matching lines...) Expand all Loading... |
| 1435 | 1164 |
| 1436 LayoutRect bottomRightRect(boundingRect.location(), LayoutSize(radii.bottomR
ight())); | 1165 LayoutRect bottomRightRect(boundingRect.location(), LayoutSize(radii.bottomR
ight())); |
| 1437 bottomRightRect.setX(boundingRect.maxX() - bottomRightRect.width()); | 1166 bottomRightRect.setX(boundingRect.maxX() - bottomRightRect.width()); |
| 1438 bottomRightRect.setY(boundingRect.maxY() - bottomRightRect.height()); | 1167 bottomRightRect.setY(boundingRect.maxY() - bottomRightRect.height()); |
| 1439 if (clipRect.intersects(bottomRightRect)) | 1168 if (clipRect.intersects(bottomRightRect)) |
| 1440 return false; | 1169 return false; |
| 1441 | 1170 |
| 1442 return true; | 1171 return true; |
| 1443 } | 1172 } |
| 1444 | 1173 |
| 1445 // TODO(fmalita): Border painting is complex enough to warrant a standalone clas
s. Move all related | |
| 1446 // logic out into BoxBorderPainter or such. | |
| 1447 // TODO(fmalita): Move static BoxPainter border methods to anonymous ns and upda
te to use a | |
| 1448 // BoxBorderInfo argument. | |
| 1449 struct BoxBorderInfo { | |
| 1450 STACK_ALLOCATED(); | |
| 1451 public: | |
| 1452 BoxBorderInfo(const ComputedStyle& style, BackgroundBleedAvoidance bleedAvoi
dance, | |
| 1453 bool includeLogicalLeftEdge, bool includeLogicalRightEdge) | |
| 1454 : style(style) | |
| 1455 , bleedAvoidance(bleedAvoidance) | |
| 1456 , includeLogicalLeftEdge(includeLogicalLeftEdge) | |
| 1457 , includeLogicalRightEdge(includeLogicalRightEdge) | |
| 1458 , visibleEdgeCount(0) | |
| 1459 , firstVisibleEdge(0) | |
| 1460 , visibleEdgeSet(0) | |
| 1461 , isUniformStyle(true) | |
| 1462 , isUniformWidth(true) | |
| 1463 , isUniformColor(true) | |
| 1464 , hasAlpha(false) | |
| 1465 { | |
| 1466 | |
| 1467 style.getBorderEdgeInfo(edges, includeLogicalLeftEdge, includeLogicalRig
htEdge); | |
| 1468 | |
| 1469 for (unsigned i = 0; i < WTF_ARRAY_LENGTH(edges); ++i) { | |
| 1470 const BorderEdge& edge = edges[i]; | |
| 1471 | |
| 1472 if (!edge.shouldRender()) { | |
| 1473 if (edge.presentButInvisible()) { | |
| 1474 isUniformWidth = false; | |
| 1475 isUniformColor = false; | |
| 1476 } | |
| 1477 | |
| 1478 continue; | |
| 1479 } | |
| 1480 | |
| 1481 visibleEdgeCount++; | |
| 1482 visibleEdgeSet |= edgeFlagForSide(static_cast<BoxSide>(i)); | |
| 1483 | |
| 1484 hasAlpha = hasAlpha || edge.color.hasAlpha(); | |
| 1485 | |
| 1486 if (visibleEdgeCount == 1) { | |
| 1487 firstVisibleEdge = i; | |
| 1488 continue; | |
| 1489 } | |
| 1490 | |
| 1491 isUniformStyle = isUniformStyle && (edge.borderStyle() == edges[firs
tVisibleEdge].borderStyle()); | |
| 1492 isUniformWidth = isUniformWidth && (edge.width == edges[firstVisible
Edge].width); | |
| 1493 isUniformColor = isUniformColor && (edge.color == edges[firstVisible
Edge].color); | |
| 1494 } | |
| 1495 } | |
| 1496 | |
| 1497 const ComputedStyle& style; | |
| 1498 const BackgroundBleedAvoidance bleedAvoidance; | |
| 1499 const bool includeLogicalLeftEdge; | |
| 1500 const bool includeLogicalRightEdge; | |
| 1501 | |
| 1502 BorderEdge edges[4]; | |
| 1503 | |
| 1504 unsigned visibleEdgeCount; | |
| 1505 unsigned firstVisibleEdge; | |
| 1506 BorderEdgeFlags visibleEdgeSet; | |
| 1507 | |
| 1508 bool isUniformStyle; | |
| 1509 bool isUniformWidth; | |
| 1510 bool isUniformColor; | |
| 1511 bool hasAlpha; | |
| 1512 }; | |
| 1513 | |
| 1514 LayoutRectOutsets doubleStripeInsets(const BorderEdge edges[], BorderEdge::Doubl
eBorderStripe stripe) | |
| 1515 { | |
| 1516 // Insets are representes as negative outsets. | |
| 1517 return LayoutRectOutsets( | |
| 1518 -edges[BSTop].getDoubleBorderStripeWidth(stripe), | |
| 1519 -edges[BSRight].getDoubleBorderStripeWidth(stripe), | |
| 1520 -edges[BSBottom].getDoubleBorderStripeWidth(stripe), | |
| 1521 -edges[BSLeft].getDoubleBorderStripeWidth(stripe)); | |
| 1522 } | |
| 1523 | |
| 1524 void drawSolidBorderRect(GraphicsContext* context, const FloatRect& borderRect, | |
| 1525 float borderWidth, const Color& color) | |
| 1526 { | |
| 1527 FloatRect strokeRect(borderRect); | |
| 1528 strokeRect.inflate(-borderWidth / 2); | |
| 1529 | |
| 1530 bool antialias = BoxPainter::shouldAntialiasLines(context); | |
| 1531 bool wasAntialias = context->shouldAntialias(); | |
| 1532 if (antialias != wasAntialias) | |
| 1533 context->setShouldAntialias(antialias); | |
| 1534 | |
| 1535 context->setStrokeStyle(SolidStroke); | |
| 1536 context->setStrokeColor(color); | |
| 1537 context->strokeRect(strokeRect, borderWidth); | |
| 1538 | |
| 1539 if (antialias != wasAntialias) | |
| 1540 context->setShouldAntialias(wasAntialias); | |
| 1541 } | |
| 1542 | |
| 1543 void drawBleedAdjustedDRRect(GraphicsContext* context, BackgroundBleedAvoidance
bleedAvoidance, | |
| 1544 const FloatRoundedRect& outer, const FloatRoundedRect& inner, Color color) | |
| 1545 { | |
| 1546 switch (bleedAvoidance) { | |
| 1547 case BackgroundBleedBackgroundOverBorder: | |
| 1548 // BackgroundBleedBackgroundOverBorder draws an opaque background over t
he inner rrect, | |
| 1549 // so we can simply fill the outer rect here to avoid backdrop bleeding. | |
| 1550 context->fillRoundedRect(outer, color); | |
| 1551 break; | |
| 1552 case BackgroundBleedClipLayer: { | |
| 1553 // BackgroundBleedClipLayer clips the outer rrect for the whole layer. B
ased on this, | |
| 1554 // we can avoid background bleeding by filling the *outside* of inner rr
ect, all the | |
| 1555 // way to the layer bounds (enclosing int rect for the clip, in device s
pace). | |
| 1556 ASSERT(outer.isRounded()); | |
| 1557 | |
| 1558 SkPath path; | |
| 1559 path.addRRect(inner); | |
| 1560 path.setFillType(SkPath::kInverseWinding_FillType); | |
| 1561 | |
| 1562 SkPaint paint; | |
| 1563 paint.setColor(color.rgb()); | |
| 1564 paint.setStyle(SkPaint::kFill_Style); | |
| 1565 paint.setAntiAlias(true); | |
| 1566 context->drawPath(path, paint); | |
| 1567 | |
| 1568 break; | |
| 1569 } | |
| 1570 case BackgroundBleedClipOnly: | |
| 1571 if (outer.isRounded()) { | |
| 1572 // BackgroundBleedClipOnly clips the outer rrect corners for us. | |
| 1573 FloatRoundedRect adjustedOuter = outer; | |
| 1574 adjustedOuter.setRadii(FloatRoundedRect::Radii()); | |
| 1575 context->fillDRRect(adjustedOuter, inner, color); | |
| 1576 break; | |
| 1577 } | |
| 1578 // fall through | |
| 1579 default: | |
| 1580 context->fillDRRect(outer, inner, color); | |
| 1581 break; | |
| 1582 } | |
| 1583 } | |
| 1584 | |
| 1585 void drawDoubleBorder(GraphicsContext* context, const BoxBorderInfo& borderInfo,
const LayoutRect& borderRect, | |
| 1586 const FloatRoundedRect& outerBorder, const FloatRoundedRect& innerBorder) | |
| 1587 { | |
| 1588 ASSERT(borderInfo.isUniformColor); | |
| 1589 ASSERT(borderInfo.isUniformStyle); | |
| 1590 ASSERT(borderInfo.edges[borderInfo.firstVisibleEdge].borderStyle() == DOUBLE
); | |
| 1591 ASSERT(borderInfo.visibleEdgeSet == AllBorderEdges); | |
| 1592 | |
| 1593 const Color color = borderInfo.edges[borderInfo.firstVisibleEdge].color; | |
| 1594 | |
| 1595 // outer stripe | |
| 1596 const LayoutRectOutsets outerThirdInsets = | |
| 1597 doubleStripeInsets(borderInfo.edges, BorderEdge::DoubleBorderStripeOuter
); | |
| 1598 const FloatRoundedRect outerThirdRect = borderInfo.style.getRoundedInnerBord
erFor(borderRect, | |
| 1599 outerThirdInsets, borderInfo.includeLogicalLeftEdge, borderInfo.includeL
ogicalRightEdge); | |
| 1600 drawBleedAdjustedDRRect(context, borderInfo.bleedAvoidance, outerBorder, out
erThirdRect, color); | |
| 1601 | |
| 1602 // inner stripe | |
| 1603 const LayoutRectOutsets innerThirdInsets = | |
| 1604 doubleStripeInsets(borderInfo.edges, BorderEdge::DoubleBorderStripeInner
); | |
| 1605 const FloatRoundedRect innerThirdRect = borderInfo.style.getRoundedInnerBord
erFor(borderRect, | |
| 1606 innerThirdInsets, borderInfo.includeLogicalLeftEdge, borderInfo.includeL
ogicalRightEdge); | |
| 1607 context->fillDRRect(innerThirdRect, innerBorder, color); | |
| 1608 } | |
| 1609 | |
| 1610 bool paintBorderFastPath(GraphicsContext* context, const BoxBorderInfo& info, | |
| 1611 const LayoutRect& borderRect, const FloatRoundedRect& outer, const FloatRoun
dedRect& inner) | |
| 1612 { | |
| 1613 if (!info.isUniformColor || !info.isUniformStyle || !inner.isRenderable()) | |
| 1614 return false; | |
| 1615 | |
| 1616 const BorderEdge& firstEdge = info.edges[info.firstVisibleEdge]; | |
| 1617 if (firstEdge.borderStyle() != SOLID && firstEdge.borderStyle() != DOUBLE) | |
| 1618 return false; | |
| 1619 | |
| 1620 if (info.visibleEdgeSet == AllBorderEdges) { | |
| 1621 if (firstEdge.borderStyle() == SOLID) { | |
| 1622 if (info.isUniformWidth && !outer.isRounded()) { | |
| 1623 // 4-side, solid, uniform-width, rectangular border => one drawR
ect() | |
| 1624 drawSolidBorderRect(context, outer.rect(), firstEdge.width, firs
tEdge.color); | |
| 1625 } else { | |
| 1626 // 4-side, solid border => one drawDRRect() | |
| 1627 drawBleedAdjustedDRRect(context, info.bleedAvoidance, outer, inn
er, firstEdge.color); | |
| 1628 } | |
| 1629 } else { | |
| 1630 // 4-side, double border => 2x drawDRRect() | |
| 1631 ASSERT(firstEdge.borderStyle() == DOUBLE); | |
| 1632 drawDoubleBorder(context, info, borderRect, outer, inner); | |
| 1633 } | |
| 1634 | |
| 1635 return true; | |
| 1636 } | |
| 1637 | |
| 1638 // This is faster than the normal complex border path only if it avoids crea
ting transparency | |
| 1639 // layers (when the border is translucent). | |
| 1640 if (firstEdge.borderStyle() == SOLID && !outer.isRounded() && info.hasAlpha)
{ | |
| 1641 ASSERT(info.visibleEdgeSet != AllBorderEdges); | |
| 1642 // solid, rectangular border => one drawPath() | |
| 1643 Path path; | |
| 1644 | |
| 1645 for (int i = BSTop; i <= BSLeft; ++i) { | |
| 1646 const BorderEdge& currEdge = info.edges[i]; | |
| 1647 if (currEdge.shouldRender()) | |
| 1648 path.addRect(calculateSideRect(outer, currEdge, i)); | |
| 1649 } | |
| 1650 | |
| 1651 context->setFillRule(RULE_NONZERO); | |
| 1652 context->setFillColor(firstEdge.color); | |
| 1653 context->fillPath(path); | |
| 1654 | |
| 1655 return true; | |
| 1656 } | |
| 1657 | |
| 1658 return false; | |
| 1659 } | |
| 1660 | |
| 1661 } // anonymous namespace | |
| 1662 | |
| 1663 void BoxPainter::paintBorder(LayoutBoxModelObject& obj, const PaintInfo& info, c
onst LayoutRect& rect, const ComputedStyle& style, BackgroundBleedAvoidance blee
dAvoidance, bool includeLogicalLeftEdge, bool includeLogicalRightEdge) | 1174 void BoxPainter::paintBorder(LayoutBoxModelObject& obj, const PaintInfo& info, c
onst LayoutRect& rect, const ComputedStyle& style, BackgroundBleedAvoidance blee
dAvoidance, bool includeLogicalLeftEdge, bool includeLogicalRightEdge) |
| 1664 { | 1175 { |
| 1665 GraphicsContext* graphicsContext = info.context; | 1176 BoxBorderPainter().paintBorder(obj, info, rect, style, bleedAvoidance, inclu
deLogicalLeftEdge, includeLogicalRightEdge); |
| 1666 // border-image is not affected by border-radius. | |
| 1667 if (paintNinePieceImage(obj, graphicsContext, rect, style, style.borderImage
())) | |
| 1668 return; | |
| 1669 | |
| 1670 const BoxBorderInfo borderInfo(style, bleedAvoidance, includeLogicalLeftEdge
, includeLogicalRightEdge); | |
| 1671 FloatRoundedRect outerBorder = style.getRoundedBorderFor(rect, includeLogica
lLeftEdge, includeLogicalRightEdge); | |
| 1672 FloatRoundedRect innerBorder = style.getRoundedInnerBorderFor(rect, includeL
ogicalLeftEdge, includeLogicalRightEdge); | |
| 1673 | |
| 1674 if (outerBorder.rect().isEmpty() || !borderInfo.visibleEdgeCount) | |
| 1675 return; | |
| 1676 | |
| 1677 const BorderEdge& firstEdge = borderInfo.edges[borderInfo.firstVisibleEdge]; | |
| 1678 bool haveAllSolidEdges = borderInfo.isUniformStyle && firstEdge.borderStyle(
) == SOLID; | |
| 1679 | |
| 1680 // If no corner intersects the clip region, we can pretend outerBorder is | |
| 1681 // rectangular to improve performance. | |
| 1682 if (haveAllSolidEdges && outerBorder.isRounded() && allCornersClippedOut(out
erBorder, info.rect)) | |
| 1683 outerBorder.setRadii(FloatRoundedRect::Radii()); | |
| 1684 | |
| 1685 if (paintBorderFastPath(graphicsContext, borderInfo, rect, outerBorder, inne
rBorder)) | |
| 1686 return; | |
| 1687 | |
| 1688 bool clipToOuterBorder = outerBorder.isRounded(); | |
| 1689 GraphicsContextStateSaver stateSaver(*graphicsContext, clipToOuterBorder); | |
| 1690 if (clipToOuterBorder) { | |
| 1691 // For BackgroundBleedClip{Only,Layer}, the outer rrect clip is already
applied. | |
| 1692 if (!bleedAvoidanceIsClipping(bleedAvoidance)) | |
| 1693 graphicsContext->clipRoundedRect(outerBorder); | |
| 1694 | |
| 1695 // For BackgroundBleedBackgroundOverBorder, we're going to draw an opaqu
e background over | |
| 1696 // the inner rrect - so clipping is not needed (nor desirable due to bac
kdrop bleeding). | |
| 1697 if (bleedAvoidance != BackgroundBleedBackgroundOverBorder && innerBorder
.isRenderable() && !innerBorder.isEmpty()) | |
| 1698 graphicsContext->clipOutRoundedRect(innerBorder); | |
| 1699 } | |
| 1700 | |
| 1701 // If only one edge visible antialiasing doesn't create seams | |
| 1702 bool antialias = shouldAntialiasLines(graphicsContext) || borderInfo.visible
EdgeCount == 1; | |
| 1703 if (borderInfo.hasAlpha) { | |
| 1704 paintTranslucentBorderSides(graphicsContext, style, outerBorder, innerBo
rder, borderInfo.edges, | |
| 1705 borderInfo.visibleEdgeSet, bleedAvoidance, includeLogicalLeftEdge, inclu
deLogicalRightEdge, antialias); | |
| 1706 } else { | |
| 1707 paintBorderSides(graphicsContext, style, outerBorder, innerBorder, borde
rInfo.edges, | |
| 1708 borderInfo.visibleEdgeSet, bleedAvoidance, includeLogicalLeftEdge, inclu
deLogicalRightEdge, antialias); | |
| 1709 } | |
| 1710 } | |
| 1711 | |
| 1712 static inline bool includesAdjacentEdges(BorderEdgeFlags flags) | |
| 1713 { | |
| 1714 // The set includes adjacent edges iff it contains at least one horizontal a
nd one vertical edge. | |
| 1715 return (flags & (TopBorderEdge | BottomBorderEdge)) | |
| 1716 && (flags & (LeftBorderEdge | RightBorderEdge)); | |
| 1717 } | |
| 1718 | |
| 1719 void BoxPainter::paintTranslucentBorderSides(GraphicsContext* graphicsContext, c
onst ComputedStyle& style, | |
| 1720 const FloatRoundedRect& outerBorder, const FloatRoundedRect& innerBorder, co
nst BorderEdge edges[], | |
| 1721 BorderEdgeFlags edgesToDraw, BackgroundBleedAvoidance bleedAvoidance, bool i
ncludeLogicalLeftEdge, | |
| 1722 bool includeLogicalRightEdge, bool antialias) | |
| 1723 { | |
| 1724 // willBeOverdrawn assumes that we draw in order: top, bottom, left, right. | |
| 1725 // This is different from BoxSide enum order. | |
| 1726 static const BoxSide paintOrder[] = { BSTop, BSBottom, BSLeft, BSRight }; | |
| 1727 | |
| 1728 while (edgesToDraw) { | |
| 1729 // Find undrawn edges sharing a color. | |
| 1730 Color commonColor; | |
| 1731 | |
| 1732 BorderEdgeFlags commonColorEdgeSet = 0; | |
| 1733 for (size_t i = 0; i < sizeof(paintOrder) / sizeof(paintOrder[0]); ++i)
{ | |
| 1734 BoxSide currSide = paintOrder[i]; | |
| 1735 if (!includesEdge(edgesToDraw, currSide)) | |
| 1736 continue; | |
| 1737 | |
| 1738 bool includeEdge; | |
| 1739 if (!commonColorEdgeSet) { | |
| 1740 commonColor = edges[currSide].color; | |
| 1741 includeEdge = true; | |
| 1742 } else { | |
| 1743 includeEdge = edges[currSide].color == commonColor; | |
| 1744 } | |
| 1745 | |
| 1746 if (includeEdge) | |
| 1747 commonColorEdgeSet |= edgeFlagForSide(currSide); | |
| 1748 } | |
| 1749 | |
| 1750 bool useTransparencyLayer = includesAdjacentEdges(commonColorEdgeSet) &&
commonColor.hasAlpha(); | |
| 1751 if (useTransparencyLayer) { | |
| 1752 graphicsContext->beginLayer(static_cast<float>(commonColor.alpha())
/ 255); | |
| 1753 commonColor = Color(commonColor.red(), commonColor.green(), commonCo
lor.blue()); | |
| 1754 } | |
| 1755 | |
| 1756 paintBorderSides(graphicsContext, style, outerBorder, innerBorder, edges
, commonColorEdgeSet, | |
| 1757 bleedAvoidance, includeLogicalLeftEdge, includeLogicalRightEdge, ant
ialias, &commonColor); | |
| 1758 | |
| 1759 if (useTransparencyLayer) | |
| 1760 graphicsContext->endLayer(); | |
| 1761 | |
| 1762 edgesToDraw &= ~commonColorEdgeSet; | |
| 1763 } | |
| 1764 } | |
| 1765 | |
| 1766 void BoxPainter::paintOneBorderSide(GraphicsContext* graphicsContext, const Comp
utedStyle& style, const FloatRoundedRect& outerBorder, const FloatRoundedRect& i
nnerBorder, | |
| 1767 const FloatRect& sideRect, BoxSide side, BoxSide adjacentSide1, BoxSide adja
centSide2, const BorderEdge edges[], const Path* path, | |
| 1768 BackgroundBleedAvoidance bleedAvoidance, bool includeLogicalLeftEdge, bool i
ncludeLogicalRightEdge, bool antialias, const Color* overrideColor) | |
| 1769 { | |
| 1770 const BorderEdge& edgeToRender = edges[side]; | |
| 1771 ASSERT(edgeToRender.width); | |
| 1772 const BorderEdge& adjacentEdge1 = edges[adjacentSide1]; | |
| 1773 const BorderEdge& adjacentEdge2 = edges[adjacentSide2]; | |
| 1774 | |
| 1775 bool mitreAdjacentSide1 = joinRequiresMitre(side, adjacentSide1, edges, !ant
ialias); | |
| 1776 bool mitreAdjacentSide2 = joinRequiresMitre(side, adjacentSide2, edges, !ant
ialias); | |
| 1777 | |
| 1778 bool adjacentSide1StylesMatch = colorsMatchAtCorner(side, adjacentSide1, edg
es); | |
| 1779 bool adjacentSide2StylesMatch = colorsMatchAtCorner(side, adjacentSide2, edg
es); | |
| 1780 | |
| 1781 const Color& colorToPaint = overrideColor ? *overrideColor : edgeToRender.co
lor; | |
| 1782 | |
| 1783 if (path) { | |
| 1784 GraphicsContextStateSaver stateSaver(*graphicsContext); | |
| 1785 if (innerBorder.isRenderable()) | |
| 1786 clipBorderSidePolygon(graphicsContext, outerBorder, innerBorder, sid
e, adjacentSide1StylesMatch, adjacentSide2StylesMatch); | |
| 1787 else | |
| 1788 clipBorderSideForComplexInnerPath(graphicsContext, outerBorder, inne
rBorder, side, edges); | |
| 1789 float thickness = std::max(std::max(edgeToRender.width, adjacentEdge1.wi
dth), adjacentEdge2.width); | |
| 1790 drawBoxSideFromPath(graphicsContext, LayoutRect(outerBorder.rect()), *pa
th, edges, edgeToRender.width, thickness, side, style, | |
| 1791 colorToPaint, edgeToRender.borderStyle(), bleedAvoidance, includeLog
icalLeftEdge, includeLogicalRightEdge); | |
| 1792 } else { | |
| 1793 bool clipForStyle = styleRequiresClipPolygon(edgeToRender.borderStyle())
&& (mitreAdjacentSide1 || mitreAdjacentSide2); | |
| 1794 bool clipAdjacentSide1 = colorNeedsAntiAliasAtCorner(side, adjacentSide1
, edges) && mitreAdjacentSide1; | |
| 1795 bool clipAdjacentSide2 = colorNeedsAntiAliasAtCorner(side, adjacentSide2
, edges) && mitreAdjacentSide2; | |
| 1796 bool shouldClip = clipForStyle || clipAdjacentSide1 || clipAdjacentSide2
; | |
| 1797 | |
| 1798 GraphicsContextStateSaver clipStateSaver(*graphicsContext, shouldClip); | |
| 1799 if (shouldClip) { | |
| 1800 bool aliasAdjacentSide1 = clipAdjacentSide1 || (clipForStyle && mitr
eAdjacentSide1); | |
| 1801 bool aliasAdjacentSide2 = clipAdjacentSide2 || (clipForStyle && mitr
eAdjacentSide2); | |
| 1802 clipBorderSidePolygon(graphicsContext, outerBorder, innerBorder, sid
e, !aliasAdjacentSide1, !aliasAdjacentSide2); | |
| 1803 // Since we clipped, no need to draw with a mitre. | |
| 1804 mitreAdjacentSide1 = false; | |
| 1805 mitreAdjacentSide2 = false; | |
| 1806 } | |
| 1807 | |
| 1808 ObjectPainter::drawLineForBoxSide(graphicsContext, sideRect.x(), sideRec
t.y(), sideRect.maxX(), sideRect.maxY(), side, colorToPaint, edgeToRender.border
Style(), | |
| 1809 mitreAdjacentSide1 ? adjacentEdge1.width : 0, mitreAdjacentSide2 ? a
djacentEdge2.width : 0, antialias); | |
| 1810 } | |
| 1811 } | |
| 1812 | |
| 1813 void BoxPainter::paintBorderSides(GraphicsContext* graphicsContext, const Comput
edStyle& style, const FloatRoundedRect& outerBorder, const FloatRoundedRect& inn
erBorder, | |
| 1814 const BorderEdge edges[], BorderEdgeFlags edgeSet, BackgroundBleedAvoidance
bleedAvoidance, | |
| 1815 bool includeLogicalLeftEdge, bool includeLogicalRightEdge, bool antialias, c
onst Color* overrideColor) | |
| 1816 { | |
| 1817 bool renderRadii = outerBorder.isRounded(); | |
| 1818 | |
| 1819 Path roundedPath; | |
| 1820 if (renderRadii) | |
| 1821 roundedPath.addRoundedRect(outerBorder); | |
| 1822 | |
| 1823 // The inner border adjustment for bleed avoidance mode BackgroundBleedBackg
roundOverBorder | |
| 1824 // is only applied to sideRect, which is okay since BackgroundBleedBackgroun
dOverBorder | |
| 1825 // is only to be used for solid borders and the shape of the border painted
by drawBoxSideFromPath | |
| 1826 // only depends on sideRect when painting solid borders. | |
| 1827 | |
| 1828 if (edges[BSTop].shouldRender() && includesEdge(edgeSet, BSTop)) { | |
| 1829 FloatRect sideRect = outerBorder.rect(); | |
| 1830 sideRect.setHeight(edges[BSTop].width); | |
| 1831 | |
| 1832 bool usePath = renderRadii && (borderStyleHasInnerDetail(edges[BSTop].bo
rderStyle()) || borderWillArcInnerEdge(innerBorder.radii().topLeft(), innerBorde
r.radii().topRight())); | |
| 1833 paintOneBorderSide(graphicsContext, style, outerBorder, innerBorder, sid
eRect, BSTop, BSLeft, BSRight, edges, usePath ? &roundedPath : 0, bleedAvoidance
, includeLogicalLeftEdge, includeLogicalRightEdge, antialias, overrideColor); | |
| 1834 } | |
| 1835 | |
| 1836 if (edges[BSBottom].shouldRender() && includesEdge(edgeSet, BSBottom)) { | |
| 1837 FloatRect sideRect = outerBorder.rect(); | |
| 1838 sideRect.shiftYEdgeTo(sideRect.maxY() - edges[BSBottom].width); | |
| 1839 | |
| 1840 bool usePath = renderRadii && (borderStyleHasInnerDetail(edges[BSBottom]
.borderStyle()) || borderWillArcInnerEdge(innerBorder.radii().bottomLeft(), inne
rBorder.radii().bottomRight())); | |
| 1841 paintOneBorderSide(graphicsContext, style, outerBorder, innerBorder, sid
eRect, BSBottom, BSLeft, BSRight, edges, usePath ? &roundedPath : 0, bleedAvoida
nce, includeLogicalLeftEdge, includeLogicalRightEdge, antialias, overrideColor); | |
| 1842 } | |
| 1843 | |
| 1844 if (edges[BSLeft].shouldRender() && includesEdge(edgeSet, BSLeft)) { | |
| 1845 FloatRect sideRect = outerBorder.rect(); | |
| 1846 sideRect.setWidth(edges[BSLeft].width); | |
| 1847 | |
| 1848 bool usePath = renderRadii && (borderStyleHasInnerDetail(edges[BSLeft].b
orderStyle()) || borderWillArcInnerEdge(innerBorder.radii().bottomLeft(), innerB
order.radii().topLeft())); | |
| 1849 paintOneBorderSide(graphicsContext, style, outerBorder, innerBorder, sid
eRect, BSLeft, BSTop, BSBottom, edges, usePath ? &roundedPath : 0, bleedAvoidanc
e, includeLogicalLeftEdge, includeLogicalRightEdge, antialias, overrideColor); | |
| 1850 } | |
| 1851 | |
| 1852 if (edges[BSRight].shouldRender() && includesEdge(edgeSet, BSRight)) { | |
| 1853 FloatRect sideRect = outerBorder.rect(); | |
| 1854 sideRect.shiftXEdgeTo(sideRect.maxX() - edges[BSRight].width); | |
| 1855 | |
| 1856 bool usePath = renderRadii && (borderStyleHasInnerDetail(edges[BSRight].
borderStyle()) || borderWillArcInnerEdge(innerBorder.radii().bottomRight(), inne
rBorder.radii().topRight())); | |
| 1857 paintOneBorderSide(graphicsContext, style, outerBorder, innerBorder, sid
eRect, BSRight, BSTop, BSBottom, edges, usePath ? &roundedPath : 0, bleedAvoidan
ce, includeLogicalLeftEdge, includeLogicalRightEdge, antialias, overrideColor); | |
| 1858 } | |
| 1859 } | |
| 1860 | |
| 1861 void BoxPainter::drawBoxSideFromPath(GraphicsContext* graphicsContext, const Lay
outRect& borderRect, const Path& borderPath, const BorderEdge edges[], | |
| 1862 float thickness, float drawThickness, BoxSide side, const ComputedStyle& sty
le, Color color, EBorderStyle borderStyle, BackgroundBleedAvoidance bleedAvoidan
ce, | |
| 1863 bool includeLogicalLeftEdge, bool includeLogicalRightEdge) | |
| 1864 { | |
| 1865 if (thickness <= 0) | |
| 1866 return; | |
| 1867 | |
| 1868 if (borderStyle == DOUBLE && thickness < 3) | |
| 1869 borderStyle = SOLID; | |
| 1870 | |
| 1871 switch (borderStyle) { | |
| 1872 case BNONE: | |
| 1873 case BHIDDEN: | |
| 1874 return; | |
| 1875 case DOTTED: | |
| 1876 case DASHED: { | |
| 1877 graphicsContext->setStrokeColor(color); | |
| 1878 | |
| 1879 // The stroke is doubled here because the provided path is the | |
| 1880 // outside edge of the border so half the stroke is clipped off. | |
| 1881 // The extra multiplier is so that the clipping mask can antialias | |
| 1882 // the edges to prevent jaggies. | |
| 1883 graphicsContext->setStrokeThickness(drawThickness * 2 * 1.1f); | |
| 1884 graphicsContext->setStrokeStyle(borderStyle == DASHED ? DashedStroke : D
ottedStroke); | |
| 1885 | |
| 1886 // If the number of dashes that fit in the path is odd and non-integral
then we | |
| 1887 // will have an awkwardly-sized dash at the end of the path. To try to a
void that | |
| 1888 // here, we simply make the whitespace dashes ever so slightly bigger. | |
| 1889 // FIXME: This could be even better if we tried to manipulate the dash o
ffset | |
| 1890 // and possibly the gapLength to get the corners dash-symmetrical. | |
| 1891 float dashLength = thickness * ((borderStyle == DASHED) ? 3.0f : 1.0f); | |
| 1892 float gapLength = dashLength; | |
| 1893 float numberOfDashes = borderPath.length() / dashLength; | |
| 1894 // Don't try to show dashes if we have less than 2 dashes + 2 gaps. | |
| 1895 // FIXME: should do this test per side. | |
| 1896 if (numberOfDashes >= 4) { | |
| 1897 bool evenNumberOfFullDashes = !((int)numberOfDashes % 2); | |
| 1898 bool integralNumberOfDashes = !(numberOfDashes - (int)numberOfDashes
); | |
| 1899 if (!evenNumberOfFullDashes && !integralNumberOfDashes) { | |
| 1900 float numberOfGaps = numberOfDashes / 2; | |
| 1901 gapLength += (dashLength / numberOfGaps); | |
| 1902 } | |
| 1903 | |
| 1904 DashArray lineDash; | |
| 1905 lineDash.append(dashLength); | |
| 1906 lineDash.append(gapLength); | |
| 1907 graphicsContext->setLineDash(lineDash, dashLength); | |
| 1908 } | |
| 1909 | |
| 1910 // FIXME: stroking the border path causes issues with tight corners: | |
| 1911 // https://bugs.webkit.org/show_bug.cgi?id=58711 | |
| 1912 // Also, to get the best appearance we should stroke a path between the
two borders. | |
| 1913 graphicsContext->strokePath(borderPath); | |
| 1914 return; | |
| 1915 } | |
| 1916 case DOUBLE: { | |
| 1917 // Draw inner border line | |
| 1918 { | |
| 1919 GraphicsContextStateSaver stateSaver(*graphicsContext); | |
| 1920 const LayoutRectOutsets innerInsets = doubleStripeInsets(edges, Bord
erEdge::DoubleBorderStripeInner); | |
| 1921 FloatRoundedRect innerClip = style.getRoundedInnerBorderFor(borderRe
ct, | |
| 1922 innerInsets, includeLogicalLeftEdge, includeLogicalRightEdge); | |
| 1923 | |
| 1924 graphicsContext->clipRoundedRect(innerClip); | |
| 1925 drawBoxSideFromPath(graphicsContext, borderRect, borderPath, edges,
thickness, drawThickness, side, style, color, SOLID, bleedAvoidance, includeLogi
calLeftEdge, includeLogicalRightEdge); | |
| 1926 } | |
| 1927 | |
| 1928 // Draw outer border line | |
| 1929 { | |
| 1930 GraphicsContextStateSaver stateSaver(*graphicsContext); | |
| 1931 LayoutRect outerRect = borderRect; | |
| 1932 LayoutRectOutsets outerInsets = doubleStripeInsets(edges, BorderEdge
::DoubleBorderStripeOuter); | |
| 1933 | |
| 1934 if (bleedAvoidanceIsClipping(bleedAvoidance)) { | |
| 1935 outerRect.inflate(1); | |
| 1936 outerInsets.setTop(outerInsets.top() - 1); | |
| 1937 outerInsets.setRight(outerInsets.right() - 1); | |
| 1938 outerInsets.setBottom(outerInsets.bottom() - 1); | |
| 1939 outerInsets.setLeft(outerInsets.left() - 1); | |
| 1940 } | |
| 1941 | |
| 1942 FloatRoundedRect outerClip = style.getRoundedInnerBorderFor(outerRec
t, outerInsets, | |
| 1943 includeLogicalLeftEdge, includeLogicalRightEdge); | |
| 1944 graphicsContext->clipOutRoundedRect(outerClip); | |
| 1945 drawBoxSideFromPath(graphicsContext, borderRect, borderPath, edges,
thickness, drawThickness, side, style, color, SOLID, bleedAvoidance, includeLogi
calLeftEdge, includeLogicalRightEdge); | |
| 1946 } | |
| 1947 return; | |
| 1948 } | |
| 1949 case RIDGE: | |
| 1950 case GROOVE: | |
| 1951 { | |
| 1952 EBorderStyle s1; | |
| 1953 EBorderStyle s2; | |
| 1954 if (borderStyle == GROOVE) { | |
| 1955 s1 = INSET; | |
| 1956 s2 = OUTSET; | |
| 1957 } else { | |
| 1958 s1 = OUTSET; | |
| 1959 s2 = INSET; | |
| 1960 } | |
| 1961 | |
| 1962 // Paint full border | |
| 1963 drawBoxSideFromPath(graphicsContext, borderRect, borderPath, edges, thic
kness, drawThickness, side, style, color, s1, bleedAvoidance, includeLogicalLeft
Edge, includeLogicalRightEdge); | |
| 1964 | |
| 1965 // Paint inner only | |
| 1966 GraphicsContextStateSaver stateSaver(*graphicsContext); | |
| 1967 LayoutUnit topWidth = edges[BSTop].usedWidth() / 2; | |
| 1968 LayoutUnit bottomWidth = edges[BSBottom].usedWidth() / 2; | |
| 1969 LayoutUnit leftWidth = edges[BSLeft].usedWidth() / 2; | |
| 1970 LayoutUnit rightWidth = edges[BSRight].usedWidth() / 2; | |
| 1971 | |
| 1972 FloatRoundedRect clipRect = style.getRoundedInnerBorderFor(borderRect, | |
| 1973 LayoutRectOutsets(-topWidth, -rightWidth, -bottomWidth, -leftWidth), | |
| 1974 includeLogicalLeftEdge, includeLogicalRightEdge); | |
| 1975 | |
| 1976 graphicsContext->clipRoundedRect(clipRect); | |
| 1977 drawBoxSideFromPath(graphicsContext, borderRect, borderPath, edges, thic
kness, drawThickness, side, style, color, s2, bleedAvoidance, includeLogicalLeft
Edge, includeLogicalRightEdge); | |
| 1978 return; | |
| 1979 } | |
| 1980 case INSET: | |
| 1981 if (side == BSTop || side == BSLeft) | |
| 1982 color = color.dark(); | |
| 1983 break; | |
| 1984 case OUTSET: | |
| 1985 if (side == BSBottom || side == BSRight) | |
| 1986 color = color.dark(); | |
| 1987 break; | |
| 1988 default: | |
| 1989 break; | |
| 1990 } | |
| 1991 | |
| 1992 graphicsContext->setStrokeStyle(NoStroke); | |
| 1993 graphicsContext->setFillColor(color); | |
| 1994 graphicsContext->drawRect(pixelSnappedIntRect(borderRect)); | |
| 1995 } | 1177 } |
| 1996 | 1178 |
| 1997 void BoxPainter::paintBoxShadow(const PaintInfo& info, const LayoutRect& paintRe
ct, const ComputedStyle& style, ShadowStyle shadowStyle, bool includeLogicalLeft
Edge, bool includeLogicalRightEdge) | 1179 void BoxPainter::paintBoxShadow(const PaintInfo& info, const LayoutRect& paintRe
ct, const ComputedStyle& style, ShadowStyle shadowStyle, bool includeLogicalLeft
Edge, bool includeLogicalRightEdge) |
| 1998 { | 1180 { |
| 1999 // FIXME: Deal with border-image. Would be great to use border-image as a ma
sk. | 1181 // FIXME: Deal with border-image. Would be great to use border-image as a ma
sk. |
| 2000 GraphicsContext* context = info.context; | 1182 GraphicsContext* context = info.context; |
| 2001 if (!style.boxShadow()) | 1183 if (!style.boxShadow()) |
| 2002 return; | 1184 return; |
| 2003 FloatRoundedRect border = (shadowStyle == Inset) ? style.getRoundedInnerBord
erFor(paintRect, includeLogicalLeftEdge, includeLogicalRightEdge) | 1185 FloatRoundedRect border = (shadowStyle == Inset) ? style.getRoundedInnerBord
erFor(paintRect, includeLogicalLeftEdge, includeLogicalRightEdge) |
| 2004 : style.getRoundedBorderFor(paintRect, includeLogicalLeftEdge, includeLo
gicalRightEdge); | 1186 : style.getRoundedBorderFor(paintRect, includeLogicalLeftEdge, includeLo
gicalRightEdge); |
| (...skipping 105 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2110 clippedEdges |= GraphicsContext::RightEdge; | 1292 clippedEdges |= GraphicsContext::RightEdge; |
| 2111 else | 1293 else |
| 2112 clippedEdges |= GraphicsContext::BottomEdge; | 1294 clippedEdges |= GraphicsContext::BottomEdge; |
| 2113 } | 1295 } |
| 2114 // TODO: support non-integer shadows - crbug.com/334828 | 1296 // TODO: support non-integer shadows - crbug.com/334828 |
| 2115 context->drawInnerShadow(border, shadowColor, flooredIntSize(shadowO
ffset), shadowBlur, shadowSpread, clippedEdges); | 1297 context->drawInnerShadow(border, shadowColor, flooredIntSize(shadowO
ffset), shadowBlur, shadowSpread, clippedEdges); |
| 2116 } | 1298 } |
| 2117 } | 1299 } |
| 2118 } | 1300 } |
| 2119 | 1301 |
| 2120 void BoxPainter::clipBorderSidePolygon(GraphicsContext* graphicsContext, const F
loatRoundedRect& outerBorder, const FloatRoundedRect& innerBorder, BoxSide side,
bool firstEdgeMatches, bool secondEdgeMatches) | |
| 2121 { | |
| 2122 FloatPoint quad[4]; | |
| 2123 | |
| 2124 const LayoutRect outerRect(outerBorder.rect()); | |
| 2125 const LayoutRect innerRect(innerBorder.rect()); | |
| 2126 | |
| 2127 FloatPoint centerPoint(innerRect.location().x().toFloat() + innerRect.width(
).toFloat() / 2, innerRect.location().y().toFloat() + innerRect.height().toFloat
() / 2); | |
| 2128 | |
| 2129 // For each side, create a quad that encompasses all parts of that side that
may draw, | |
| 2130 // including areas inside the innerBorder. | |
| 2131 // | |
| 2132 // 0----------------3 | |
| 2133 // 0 \ / 0 | |
| 2134 // |\ 1----------- 2 /| | |
| 2135 // | 1 1 | | |
| 2136 // | | | | | |
| 2137 // | | | | | |
| 2138 // | 2 2 | | |
| 2139 // |/ 1------------2 \| | |
| 2140 // 3 / \ 3 | |
| 2141 // 0----------------3 | |
| 2142 // | |
| 2143 switch (side) { | |
| 2144 case BSTop: | |
| 2145 quad[0] = FloatPoint(outerRect.minXMinYCorner()); | |
| 2146 quad[1] = FloatPoint(innerRect.minXMinYCorner()); | |
| 2147 quad[2] = FloatPoint(innerRect.maxXMinYCorner()); | |
| 2148 quad[3] = FloatPoint(outerRect.maxXMinYCorner()); | |
| 2149 | |
| 2150 if (!innerBorder.radii().topLeft().isZero()) { | |
| 2151 findIntersection(quad[0], quad[1], | |
| 2152 FloatPoint( | |
| 2153 quad[1].x() + innerBorder.radii().topLeft().width(), | |
| 2154 quad[1].y()), | |
| 2155 FloatPoint( | |
| 2156 quad[1].x(), | |
| 2157 quad[1].y() + innerBorder.radii().topLeft().height()), | |
| 2158 quad[1]); | |
| 2159 } | |
| 2160 | |
| 2161 if (!innerBorder.radii().topRight().isZero()) { | |
| 2162 findIntersection(quad[3], quad[2], | |
| 2163 FloatPoint( | |
| 2164 quad[2].x() - innerBorder.radii().topRight().width(), | |
| 2165 quad[2].y()), | |
| 2166 FloatPoint( | |
| 2167 quad[2].x(), | |
| 2168 quad[2].y() + innerBorder.radii().topRight().height()), | |
| 2169 quad[2]); | |
| 2170 } | |
| 2171 break; | |
| 2172 | |
| 2173 case BSLeft: | |
| 2174 quad[0] = FloatPoint(outerRect.minXMinYCorner()); | |
| 2175 quad[1] = FloatPoint(innerRect.minXMinYCorner()); | |
| 2176 quad[2] = FloatPoint(innerRect.minXMaxYCorner()); | |
| 2177 quad[3] = FloatPoint(outerRect.minXMaxYCorner()); | |
| 2178 | |
| 2179 if (!innerBorder.radii().topLeft().isZero()) { | |
| 2180 findIntersection(quad[0], quad[1], | |
| 2181 FloatPoint( | |
| 2182 quad[1].x() + innerBorder.radii().topLeft().width(), | |
| 2183 quad[1].y()), | |
| 2184 FloatPoint( | |
| 2185 quad[1].x(), | |
| 2186 quad[1].y() + innerBorder.radii().topLeft().height()), | |
| 2187 quad[1]); | |
| 2188 } | |
| 2189 | |
| 2190 if (!innerBorder.radii().bottomLeft().isZero()) { | |
| 2191 findIntersection(quad[3], quad[2], | |
| 2192 FloatPoint( | |
| 2193 quad[2].x() + innerBorder.radii().bottomLeft().width(), | |
| 2194 quad[2].y()), | |
| 2195 FloatPoint( | |
| 2196 quad[2].x(), | |
| 2197 quad[2].y() - innerBorder.radii().bottomLeft().height()), | |
| 2198 quad[2]); | |
| 2199 } | |
| 2200 break; | |
| 2201 | |
| 2202 case BSBottom: | |
| 2203 quad[0] = FloatPoint(outerRect.minXMaxYCorner()); | |
| 2204 quad[1] = FloatPoint(innerRect.minXMaxYCorner()); | |
| 2205 quad[2] = FloatPoint(innerRect.maxXMaxYCorner()); | |
| 2206 quad[3] = FloatPoint(outerRect.maxXMaxYCorner()); | |
| 2207 | |
| 2208 if (!innerBorder.radii().bottomLeft().isZero()) { | |
| 2209 findIntersection(quad[0], quad[1], | |
| 2210 FloatPoint( | |
| 2211 quad[1].x() + innerBorder.radii().bottomLeft().width(), | |
| 2212 quad[1].y()), | |
| 2213 FloatPoint( | |
| 2214 quad[1].x(), | |
| 2215 quad[1].y() - innerBorder.radii().bottomLeft().height()), | |
| 2216 quad[1]); | |
| 2217 } | |
| 2218 | |
| 2219 if (!innerBorder.radii().bottomRight().isZero()) { | |
| 2220 findIntersection(quad[3], quad[2], | |
| 2221 FloatPoint( | |
| 2222 quad[2].x() - innerBorder.radii().bottomRight().width(), | |
| 2223 quad[2].y()), | |
| 2224 FloatPoint( | |
| 2225 quad[2].x(), | |
| 2226 quad[2].y() - innerBorder.radii().bottomRight().height()), | |
| 2227 quad[2]); | |
| 2228 } | |
| 2229 break; | |
| 2230 | |
| 2231 case BSRight: | |
| 2232 quad[0] = FloatPoint(outerRect.maxXMinYCorner()); | |
| 2233 quad[1] = FloatPoint(innerRect.maxXMinYCorner()); | |
| 2234 quad[2] = FloatPoint(innerRect.maxXMaxYCorner()); | |
| 2235 quad[3] = FloatPoint(outerRect.maxXMaxYCorner()); | |
| 2236 | |
| 2237 if (!innerBorder.radii().topRight().isZero()) { | |
| 2238 findIntersection(quad[0], quad[1], | |
| 2239 FloatPoint( | |
| 2240 quad[1].x() - innerBorder.radii().topRight().width(), | |
| 2241 quad[1].y()), | |
| 2242 FloatPoint( | |
| 2243 quad[1].x(), | |
| 2244 quad[1].y() + innerBorder.radii().topRight().height()), | |
| 2245 quad[1]); | |
| 2246 } | |
| 2247 | |
| 2248 if (!innerBorder.radii().bottomRight().isZero()) { | |
| 2249 findIntersection(quad[3], quad[2], | |
| 2250 FloatPoint( | |
| 2251 quad[2].x() - innerBorder.radii().bottomRight().width(), | |
| 2252 quad[2].y()), | |
| 2253 FloatPoint( | |
| 2254 quad[2].x(), | |
| 2255 quad[2].y() - innerBorder.radii().bottomRight().height()), | |
| 2256 quad[2]); | |
| 2257 } | |
| 2258 break; | |
| 2259 } | |
| 2260 | |
| 2261 // If the border matches both of its adjacent sides, don't anti-alias the cl
ip, and | |
| 2262 // if neither side matches, anti-alias the clip. | |
| 2263 if (firstEdgeMatches == secondEdgeMatches) { | |
| 2264 graphicsContext->clipPolygon(4, quad, !firstEdgeMatches); | |
| 2265 return; | |
| 2266 } | |
| 2267 | |
| 2268 // If antialiasing settings for the first edge and second edge is different, | |
| 2269 // they have to be addressed separately. We do this by breaking the quad int
o | |
| 2270 // two parallelograms, made by moving quad[1] and quad[2]. | |
| 2271 float ax = quad[1].x() - quad[0].x(); | |
| 2272 float ay = quad[1].y() - quad[0].y(); | |
| 2273 float bx = quad[2].x() - quad[1].x(); | |
| 2274 float by = quad[2].y() - quad[1].y(); | |
| 2275 float cx = quad[3].x() - quad[2].x(); | |
| 2276 float cy = quad[3].y() - quad[2].y(); | |
| 2277 | |
| 2278 const static float kEpsilon = 1e-2f; | |
| 2279 float r1, r2; | |
| 2280 if (fabsf(bx) < kEpsilon && fabsf(by) < kEpsilon) { | |
| 2281 // The quad was actually a triangle. | |
| 2282 r1 = r2 = 1.0f; | |
| 2283 } else { | |
| 2284 // Extend parallelogram a bit to hide calculation error | |
| 2285 const static float kExtendFill = 1e-2f; | |
| 2286 | |
| 2287 r1 = (-ax * by + ay * bx) / (cx * by - cy * bx) + kExtendFill; | |
| 2288 r2 = (-cx * by + cy * bx) / (ax * by - ay * bx) + kExtendFill; | |
| 2289 } | |
| 2290 | |
| 2291 FloatPoint firstQuad[4]; | |
| 2292 firstQuad[0] = quad[0]; | |
| 2293 firstQuad[1] = quad[1]; | |
| 2294 firstQuad[2] = FloatPoint(quad[3].x() + r2 * ax, quad[3].y() + r2 * ay); | |
| 2295 firstQuad[3] = quad[3]; | |
| 2296 graphicsContext->clipPolygon(4, firstQuad, !firstEdgeMatches); | |
| 2297 | |
| 2298 FloatPoint secondQuad[4]; | |
| 2299 secondQuad[0] = quad[0]; | |
| 2300 secondQuad[1] = FloatPoint(quad[0].x() - r1 * cx, quad[0].y() - r1 * cy); | |
| 2301 secondQuad[2] = quad[2]; | |
| 2302 secondQuad[3] = quad[3]; | |
| 2303 graphicsContext->clipPolygon(4, secondQuad, !secondEdgeMatches); | |
| 2304 } | |
| 2305 | |
| 2306 } // namespace blink | 1302 } // namespace blink |
| OLD | NEW |