| OLD | NEW |
| (Empty) | |
| 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 |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #include "config.h" |
| 6 #include "core/paint/BoxBorderPainter.h" |
| 7 |
| 8 #include "core/paint/BoxPainter.h" |
| 9 #include "core/style/ComputedStyleConstants.h" |
| 10 #include "platform/RuntimeEnabledFeatures.h" |
| 11 #include "platform/graphics/GraphicsContext.h" |
| 12 #include "platform/graphics/GraphicsContextStateSaver.h" |
| 13 |
| 14 namespace blink { |
| 15 |
| 16 namespace { |
| 17 |
| 18 static_assert(BSTop == 0, "unexpected BoxSide enum value"); |
| 19 static_assert(BSRight == 1, "unexpected BoxSide enum value"); |
| 20 static_assert(BSBottom == 2, "unexpected BoxSide enum value"); |
| 21 static_assert(BSLeft == 3, "unexpected BoxSide enum value"); |
| 22 |
| 23 const BoxSide kAdjacentSidesCW[4] = { BSRight, BSBottom, BSLeft, BSTop }; |
| 24 const BoxSide kAdjacentSidesCCW[4] = { BSLeft, BSTop, BSRight, BSBottom }; |
| 25 const float kEdgeShiftMap[4][4] = { |
| 26 { 0, 0, -1, 0}, |
| 27 { 0, -1, 0, 1 }, |
| 28 { 1, 0, -1, 0 }, |
| 29 { 0, -1, 0, 0 } }; |
| 30 |
| 31 inline BorderEdgeFlag edgeFlagForSide(BoxSide side) |
| 32 { |
| 33 return static_cast<BorderEdgeFlag>(1 << side); |
| 34 } |
| 35 |
| 36 inline bool includesEdge(BorderEdgeFlags flags, BoxSide side) |
| 37 { |
| 38 return flags & edgeFlagForSide(side); |
| 39 } |
| 40 |
| 41 inline bool includesAdjacentEdges(BorderEdgeFlags flags) |
| 42 { |
| 43 return (flags & (TopBorderEdge | BottomBorderEdge)) |
| 44 && (flags & (LeftBorderEdge | RightBorderEdge)); |
| 45 } |
| 46 |
| 47 bool borderStyleHasInnerDetail(EBorderStyle style) |
| 48 { |
| 49 return style == GROOVE || style == RIDGE || style == DOUBLE; |
| 50 } |
| 51 |
| 52 // OUTSET darkens the bottom and right (and maybe lightens the top and left) |
| 53 // INSET darkens the top and left (and maybe lightens the bottom and right) |
| 54 inline bool borderStyleHasUnmatchedColorsAtCorner(EBorderStyle style, BoxSide si
de, BoxSide adjacentSide) |
| 55 { |
| 56 // These styles match at the top/left and bottom/right. |
| 57 if (style == INSET || style == GROOVE || style == RIDGE || style == OUTSET)
{ |
| 58 const BorderEdgeFlags topRightFlags = edgeFlagForSide(BSTop) | edgeFlagF
orSide(BSRight); |
| 59 const BorderEdgeFlags bottomLeftFlags = edgeFlagForSide(BSBottom) | edge
FlagForSide(BSLeft); |
| 60 |
| 61 BorderEdgeFlags flags = edgeFlagForSide(side) | edgeFlagForSide(adjacent
Side); |
| 62 return flags == topRightFlags || flags == bottomLeftFlags; |
| 63 } |
| 64 return false; |
| 65 } |
| 66 |
| 67 bool borderStyleFillsBorderArea(EBorderStyle style) |
| 68 { |
| 69 return !(style == DOTTED || style == DASHED || style == DOUBLE); |
| 70 } |
| 71 |
| 72 inline bool borderStyleIsDottedOrDashed(EBorderStyle style) |
| 73 { |
| 74 return style == DOTTED || style == DASHED; |
| 75 } |
| 76 |
| 77 inline bool borderStylesRequireMitre(BoxSide side, BoxSide adjacentSide, EBorder
Style style, EBorderStyle adjacentStyle) |
| 78 { |
| 79 if (style == DOUBLE || adjacentStyle == DOUBLE || adjacentStyle == GROOVE ||
adjacentStyle == RIDGE) |
| 80 return true; |
| 81 |
| 82 if (borderStyleIsDottedOrDashed(style) != borderStyleIsDottedOrDashed(adjace
ntStyle)) |
| 83 return true; |
| 84 |
| 85 if (style != adjacentStyle) |
| 86 return true; |
| 87 |
| 88 return borderStyleHasUnmatchedColorsAtCorner(style, side, adjacentSide); |
| 89 } |
| 90 |
| 91 inline bool colorsMatchAtCorner(BoxSide side, BoxSide adjacentSide, const Border
Edge edges[]) |
| 92 { |
| 93 if (edges[side].shouldRender() != edges[adjacentSide].shouldRender()) |
| 94 return false; |
| 95 |
| 96 if (!edges[side].sharesColorWith(edges[adjacentSide])) |
| 97 return false; |
| 98 |
| 99 return !borderStyleHasUnmatchedColorsAtCorner(edges[side].borderStyle(), sid
e, adjacentSide); |
| 100 } |
| 101 |
| 102 inline bool styleRequiresClipPolygon(EBorderStyle style) |
| 103 { |
| 104 // These are drawn with a stroke, so we have to clip to get corner miters. |
| 105 return style == DOTTED || style == DASHED; |
| 106 } |
| 107 |
| 108 } // anonymous namespace |
| 109 |
| 110 BoxBorderInfo::BoxBorderInfo(const ComputedStyle& style, BackgroundBleedAvoidanc
e bleedAvoidance, |
| 111 bool includeLogicalLeftEdge, bool includeLogicalRightEdge) |
| 112 : style(style) |
| 113 , bleedAvoidance(bleedAvoidance) |
| 114 , includeLogicalLeftEdge(includeLogicalLeftEdge) |
| 115 , includeLogicalRightEdge(includeLogicalRightEdge) |
| 116 , visibleEdgeCount(0) |
| 117 , firstVisibleEdge(0) |
| 118 , visibleEdgeSet(0) |
| 119 , isUniformStyle(true) |
| 120 , isUniformWidth(true) |
| 121 , isUniformColor(true) |
| 122 , hasAlpha(false) |
| 123 { |
| 124 style.getBorderEdgeInfo(edges, includeLogicalLeftEdge, includeLogicalRightEd
ge); |
| 125 |
| 126 for (unsigned i = 0; i < WTF_ARRAY_LENGTH(edges); ++i) { |
| 127 const BorderEdge& edge = edges[i]; |
| 128 |
| 129 if (!edge.shouldRender()) { |
| 130 if (edge.presentButInvisible()) { |
| 131 isUniformWidth = false; |
| 132 isUniformColor = false; |
| 133 } |
| 134 |
| 135 continue; |
| 136 } |
| 137 |
| 138 visibleEdgeCount++; |
| 139 visibleEdgeSet |= edgeFlagForSide(static_cast<BoxSide>(i)); |
| 140 |
| 141 hasAlpha = hasAlpha || edge.color.hasAlpha(); |
| 142 |
| 143 if (visibleEdgeCount == 1) { |
| 144 firstVisibleEdge = i; |
| 145 continue; |
| 146 } |
| 147 |
| 148 isUniformStyle = isUniformStyle && (edge.borderStyle() == edges[firstVis
ibleEdge].borderStyle()); |
| 149 isUniformWidth = isUniformWidth && (edge.width == edges[firstVisibleEdge
].width); |
| 150 isUniformColor = isUniformColor && (edge.color == edges[firstVisibleEdge
].color); |
| 151 } |
| 152 } |
| 153 |
| 154 BoxBorderPainter::BoxBorderPainter(const BoxBorderInfo& borderInfo, |
| 155 const FloatRoundedRect& outer, const FloatRoundedRect& inner) |
| 156 : m_borderInfo(borderInfo) |
| 157 , m_outer(outer) |
| 158 , m_inner(inner) |
| 159 { |
| 160 BorderEdgeFlags edgeSet = borderInfo.visibleEdgeSet; |
| 161 ASSERT(edgeSet); |
| 162 |
| 163 while (edgeSet) { |
| 164 m_opacityGroupIndices.append(m_opacityGroupIndices.size()); |
| 165 m_opacityGroups.append(OpacityGroup()); |
| 166 OpacityGroup& currentGroup = m_opacityGroups.last(); |
| 167 BorderEdgeFlags currentSet = 0; |
| 168 |
| 169 for (unsigned i = borderInfo.firstVisibleEdge; i < 4; ++i) { |
| 170 BoxSide side = static_cast<BoxSide>(i); |
| 171 if (!includesEdge(edgeSet, side)) |
| 172 continue; |
| 173 |
| 174 unsigned sideAlpha = borderInfo.edges[side].color.alpha(); |
| 175 if (currentGroup.sides.isEmpty()) |
| 176 currentGroup.alpha = sideAlpha; |
| 177 |
| 178 if (sideAlpha == currentGroup.alpha) { |
| 179 currentGroup.sides.append(side); |
| 180 currentGroup.edgeFlags |= edgeFlagForSide(side); |
| 181 currentSet |= edgeFlagForSide(side); |
| 182 } |
| 183 } |
| 184 ASSERT(currentSet); |
| 185 ASSERT(!currentGroup.sides.isEmpty()); |
| 186 |
| 187 static const unsigned kStyleOrder[] = { |
| 188 0 /* BNONE */, |
| 189 0 /* BHIDDEN */, |
| 190 2 /* INSET */, |
| 191 2 /* GROOVE */, |
| 192 2 /* OUTSET */, |
| 193 2 /* RIDGE */, |
| 194 1 /* DOTTED */, |
| 195 1 /* DASHED */, |
| 196 3 /* SOLID */, |
| 197 2 /* DOUBLE */ |
| 198 }; |
| 199 std::sort(currentGroup.sides.begin(), currentGroup.sides.end(), |
| 200 [&borderInfo] (BoxSide a, BoxSide b) -> bool |
| 201 { |
| 202 EBorderStyle style1 = borderInfo.edges[a].borderStyle(); |
| 203 EBorderStyle style2 = borderInfo.edges[b].borderStyle(); |
| 204 ASSERT(style1 != BNONE && style1 != BHIDDEN); |
| 205 return kStyleOrder[style1] < kStyleOrder[style2]; |
| 206 }); |
| 207 |
| 208 edgeSet &= ~currentSet; |
| 209 } |
| 210 |
| 211 ASSERT(!m_opacityGroups.isEmpty()); |
| 212 ASSERT(m_opacityGroups.size() == m_opacityGroupIndices.size()); |
| 213 |
| 214 std::sort(m_opacityGroupIndices.begin(), m_opacityGroupIndices.end(), |
| 215 [this] (size_t a, size_t b) |
| 216 { |
| 217 ASSERT(a < m_opacityGroups.size()); |
| 218 ASSERT(b < m_opacityGroups.size()); |
| 219 ASSERT(m_opacityGroups[a].alpha != m_opacityGroups[b].alpha); |
| 220 return m_opacityGroups[a].alpha > m_opacityGroups[b].alpha; |
| 221 }); |
| 222 |
| 223 if (outer.isRounded()) { |
| 224 m_roundedPath.addRoundedRect(outer); |
| 225 m_hasRadii[0] = !inner.radii().topLeft().isZero(); |
| 226 m_hasRadii[1] = !inner.radii().topRight().isZero(); |
| 227 m_hasRadii[2] = !inner.radii().bottomRight().isZero(); |
| 228 m_hasRadii[3] = !inner.radii().bottomLeft().isZero(); |
| 229 } |
| 230 } |
| 231 |
| 232 void BoxBorderPainter::paint(GraphicsContext* context) const |
| 233 { |
| 234 paintGroup(context, 0, 1); |
| 235 } |
| 236 |
| 237 BorderEdgeFlags BoxBorderPainter::paintGroup(GraphicsContext* context, unsigned
index = 0, |
| 238 float opacity = 1) const |
| 239 { |
| 240 ASSERT(opacity > 0 && opacity <= 1); |
| 241 |
| 242 if (index >= m_opacityGroups.size()) |
| 243 return ~m_borderInfo.visibleEdgeSet; |
| 244 |
| 245 const OpacityGroup& group = m_opacityGroups[m_opacityGroupIndices[index]]; |
| 246 const float groupOpacity = static_cast<float>(group.alpha) / 255; |
| 247 ASSERT(groupOpacity <= opacity); |
| 248 |
| 249 unsigned paintAlpha = group.alpha / opacity; |
| 250 ASSERT(paintAlpha <= 255); |
| 251 |
| 252 bool useLayer = group.alpha != 255 |
| 253 && (includesAdjacentEdges(group.edgeFlags) || (index + 1 < m_opacityGrou
ps.size())); |
| 254 |
| 255 if (useLayer) { |
| 256 ASSERT(groupOpacity < opacity); |
| 257 |
| 258 context->beginLayer(groupOpacity / opacity); |
| 259 opacity *= groupOpacity; |
| 260 paintAlpha = 255; |
| 261 } |
| 262 |
| 263 BorderEdgeFlags paintedSides = paintGroup(context, index + 1, opacity); |
| 264 |
| 265 for (BoxSide side : group.sides) { |
| 266 paintSide(context, side, paintAlpha, paintedSides); |
| 267 paintedSides |= edgeFlagForSide(side); |
| 268 } |
| 269 |
| 270 if (useLayer) |
| 271 context->endLayer(); |
| 272 |
| 273 return paintedSides; |
| 274 } |
| 275 |
| 276 void BoxBorderPainter::paintSide(GraphicsContext* context, BoxSide side, unsigne
d alpha, |
| 277 BorderEdgeFlags paintedSides) const |
| 278 { |
| 279 BoxSide adjacentSideCW = kAdjacentSidesCW[side]; |
| 280 BoxSide adjacentSideCCW = kAdjacentSidesCCW[side]; |
| 281 |
| 282 // FIXME: ugh |
| 283 if (side == BSBottom || side == BSLeft) |
| 284 std::swap(adjacentSideCW, adjacentSideCCW); |
| 285 |
| 286 const BorderEdge& edge = m_borderInfo.edges[side]; |
| 287 const BorderEdge& adjacentEdgeCW = m_borderInfo.edges[adjacentSideCW]; |
| 288 const BorderEdge& adjacentEdgeCCW = m_borderInfo.edges[adjacentSideCCW]; |
| 289 const Color color(edge.color.red(), edge.color.green(), edge.color.blue(), a
lpha); |
| 290 |
| 291 bool usePath = m_outer.isRounded() |
| 292 && (borderStyleHasInnerDetail(edge.borderStyle()) || m_hasRadii[side] ||
m_hasRadii[(side + 1) % 4]); |
| 293 |
| 294 if (usePath) { |
| 295 MitreType mitre1 = colorsMatchAtCorner(side, adjacentSideCCW, m_borderIn
fo.edges) |
| 296 ? NonAntiAliasedMitre : AntiAliasedMitre; |
| 297 MitreType mitre2 = colorsMatchAtCorner(side, adjacentSideCW, m_borderInf
o.edges) |
| 298 ? NonAntiAliasedMitre : AntiAliasedMitre; |
| 299 |
| 300 GraphicsContextStateSaver stateSaver(*context); |
| 301 if (m_inner.isRenderable()) |
| 302 clipBorderSidePolygon(context, side, mitre1, mitre2); |
| 303 else |
| 304 BoxPainter::clipBorderSideForComplexInnerPath(context, m_outer, m_in
ner, side, m_borderInfo.edges); |
| 305 float thickness = std::max(std::max(edge.width, adjacentEdgeCCW.width),
adjacentEdgeCW.width); |
| 306 BoxPainter::drawBoxSideFromPath(context, LayoutRect(m_outer.rect()), m_r
oundedPath, m_borderInfo.edges, edge.width, thickness, side, m_borderInfo.style, |
| 307 color, edge.borderStyle(), m_borderInfo.bleedAvoidance, m_borderInfo
.includeLogicalLeftEdge, m_borderInfo.includeLogicalRightEdge); |
| 308 } else { |
| 309 MitreType mitre1 = computeMitre(side, adjacentSideCW, paintedSides); |
| 310 MitreType mitre2 = computeMitre(side, adjacentSideCCW, paintedSides); |
| 311 |
| 312 bool clipForStyle = (mitre1 != NoMitre || mitre2 != NoMitre) |
| 313 && styleRequiresClipPolygon(edge.borderStyle()); |
| 314 bool clipForMitre = mitre1 == NonAntiAliasedMitre || mitre2 == NonAntiAl
iasedMitre; |
| 315 bool shouldClip = clipForStyle || clipForMitre; |
| 316 |
| 317 if (0) { |
| 318 printf("*** side: %d, painted: %x, adjSide1: %d, mitre1: %d, adjSide
2: %d, mitre2: %d, clip: %d\n", |
| 319 side, paintedSides, adjacentSideCW, mitre1, adjacentSideCCW, mit
re2, shouldClip); |
| 320 if (shouldClip) { |
| 321 printf(" clipForStyle: %d, clipForMitre: %d\n", clipForStyle, c
lipForMitre); |
| 322 } |
| 323 } |
| 324 |
| 325 GraphicsContextStateSaver clipStateSaver(*context, shouldClip); |
| 326 if (shouldClip) { |
| 327 clipBorderSidePolygon(context, side, mitre2, mitre1); |
| 328 // Since we clipped, no need to draw with a mitre. |
| 329 mitre1 = mitre2 = NoMitre; |
| 330 } |
| 331 |
| 332 ASSERT(mitre1 == NoMitre || mitre1 == AntiAliasedMitre); |
| 333 ASSERT(mitre2 == NoMitre || mitre2 == AntiAliasedMitre); |
| 334 |
| 335 const FloatRect& borderRect = m_outer.rect(); |
| 336 const FloatSize edgeShift(borderRect.width() - edge.width, borderRect.he
ight() - edge.width); |
| 337 const FloatRect sideRect( |
| 338 borderRect.x() + edgeShift.width() * kEdgeShiftMap[side][BSLeft], |
| 339 borderRect.y() + edgeShift.height() * kEdgeShiftMap[side][BSTop], |
| 340 borderRect.width() + edgeShift.width() * kEdgeShiftMap[side][BSRight
], |
| 341 borderRect.height() + edgeShift.height() * kEdgeShiftMap[side][BSBot
tom]); |
| 342 ObjectPainter::drawLineForBoxSide(context, sideRect.x(), sideRect.y(), s
ideRect.maxX(), sideRect.maxY(), side, color, edge.borderStyle(), |
| 343 mitre2 != NoMitre ? adjacentEdgeCCW.width : 0, mitre1 != NoMitre ? a
djacentEdgeCW.width : 0, true); |
| 344 } |
| 345 } |
| 346 |
| 347 BoxBorderPainter::MitreType BoxBorderPainter::computeMitre(BoxSide side, BoxSide
adjacentSide, |
| 348 BorderEdgeFlags paintedSides) const |
| 349 { |
| 350 const BorderEdge& edge = m_borderInfo.edges[side]; |
| 351 const BorderEdge& adjacentEdge = m_borderInfo.edges[adjacentSide]; |
| 352 |
| 353 if (!adjacentEdge.usedWidth()) |
| 354 return NoMitre; |
| 355 |
| 356 if (!includesEdge(paintedSides, adjacentSide) && borderStyleFillsBorderArea(
adjacentEdge.borderStyle())) |
| 357 return NoMitre; |
| 358 |
| 359 if (!edge.sharesColorWith(adjacentEdge)) |
| 360 return AntiAliasedMitre; |
| 361 |
| 362 if (borderStylesRequireMitre(side, adjacentSide, edge.borderStyle(), adjacen
tEdge.borderStyle())) { |
| 363 return borderStyleHasUnmatchedColorsAtCorner(edge.borderStyle(), side, a
djacentSide) |
| 364 ? AntiAliasedMitre : NonAntiAliasedMitre; |
| 365 } |
| 366 |
| 367 return NoMitre; |
| 368 } |
| 369 |
| 370 void BoxBorderPainter::clipBorderSidePolygon(GraphicsContext* context, BoxSide s
ide, |
| 371 MitreType mitre1, MitreType mitre2) const |
| 372 { |
| 373 ASSERT(mitre1 != NoMitre || mitre2 != NoMitre); |
| 374 |
| 375 FloatPoint quad[4]; |
| 376 |
| 377 const LayoutRect outerRect(m_outer.rect()); |
| 378 const LayoutRect innerRect(m_inner.rect()); |
| 379 |
| 380 // For each side, create a quad that encompasses all parts of that side that
may draw, |
| 381 // including areas inside the innerBorder. |
| 382 // |
| 383 // 0----------------3 |
| 384 // 0 \ / 0 |
| 385 // |\ 1----------- 2 /| |
| 386 // | 1 1 | |
| 387 // | | | | |
| 388 // | | | | |
| 389 // | 2 2 | |
| 390 // |/ 1------------2 \| |
| 391 // 3 / \ 3 |
| 392 // 0----------------3 |
| 393 // |
| 394 switch (side) { |
| 395 case BSTop: |
| 396 quad[0] = FloatPoint(outerRect.minXMinYCorner()); |
| 397 quad[1] = FloatPoint(innerRect.minXMinYCorner()); |
| 398 quad[2] = FloatPoint(innerRect.maxXMinYCorner()); |
| 399 quad[3] = FloatPoint(outerRect.maxXMinYCorner()); |
| 400 |
| 401 if (!m_inner.radii().topLeft().isZero()) { |
| 402 findIntersection(quad[0], quad[1], |
| 403 FloatPoint( |
| 404 quad[1].x() + m_inner.radii().topLeft().width(), |
| 405 quad[1].y()), |
| 406 FloatPoint( |
| 407 quad[1].x(), |
| 408 quad[1].y() + m_inner.radii().topLeft().height()), |
| 409 quad[1]); |
| 410 } |
| 411 |
| 412 if (!m_inner.radii().topRight().isZero()) { |
| 413 findIntersection(quad[3], quad[2], |
| 414 FloatPoint( |
| 415 quad[2].x() - m_inner.radii().topRight().width(), |
| 416 quad[2].y()), |
| 417 FloatPoint( |
| 418 quad[2].x(), |
| 419 quad[2].y() + m_inner.radii().topRight().height()), |
| 420 quad[2]); |
| 421 } |
| 422 |
| 423 if (mitre1 == NoMitre) |
| 424 quad[1].setX(quad[0].x()); |
| 425 if (mitre2 == NoMitre) |
| 426 quad[2].setX(quad[3].x()); |
| 427 |
| 428 break; |
| 429 |
| 430 case BSLeft: |
| 431 quad[0] = FloatPoint(outerRect.minXMinYCorner()); |
| 432 quad[1] = FloatPoint(innerRect.minXMinYCorner()); |
| 433 quad[2] = FloatPoint(innerRect.minXMaxYCorner()); |
| 434 quad[3] = FloatPoint(outerRect.minXMaxYCorner()); |
| 435 |
| 436 if (!m_inner.radii().topLeft().isZero()) { |
| 437 findIntersection(quad[0], quad[1], |
| 438 FloatPoint( |
| 439 quad[1].x() + m_inner.radii().topLeft().width(), |
| 440 quad[1].y()), |
| 441 FloatPoint( |
| 442 quad[1].x(), |
| 443 quad[1].y() + m_inner.radii().topLeft().height()), |
| 444 quad[1]); |
| 445 } |
| 446 |
| 447 if (!m_inner.radii().bottomLeft().isZero()) { |
| 448 findIntersection(quad[3], quad[2], |
| 449 FloatPoint( |
| 450 quad[2].x() + m_inner.radii().bottomLeft().width(), |
| 451 quad[2].y()), |
| 452 FloatPoint( |
| 453 quad[2].x(), |
| 454 quad[2].y() - m_inner.radii().bottomLeft().height()), |
| 455 quad[2]); |
| 456 } |
| 457 |
| 458 if (mitre1 == NoMitre) |
| 459 quad[1].setY(quad[0].y()); |
| 460 if (mitre2 == NoMitre) |
| 461 quad[2].setY(quad[3].y()); |
| 462 |
| 463 break; |
| 464 |
| 465 case BSBottom: |
| 466 quad[0] = FloatPoint(outerRect.minXMaxYCorner()); |
| 467 quad[1] = FloatPoint(innerRect.minXMaxYCorner()); |
| 468 quad[2] = FloatPoint(innerRect.maxXMaxYCorner()); |
| 469 quad[3] = FloatPoint(outerRect.maxXMaxYCorner()); |
| 470 |
| 471 if (!m_inner.radii().bottomLeft().isZero()) { |
| 472 findIntersection(quad[0], quad[1], |
| 473 FloatPoint( |
| 474 quad[1].x() + m_inner.radii().bottomLeft().width(), |
| 475 quad[1].y()), |
| 476 FloatPoint( |
| 477 quad[1].x(), |
| 478 quad[1].y() - m_inner.radii().bottomLeft().height()), |
| 479 quad[1]); |
| 480 } |
| 481 |
| 482 if (!m_inner.radii().bottomRight().isZero()) { |
| 483 findIntersection(quad[3], quad[2], |
| 484 FloatPoint( |
| 485 quad[2].x() - m_inner.radii().bottomRight().width(), |
| 486 quad[2].y()), |
| 487 FloatPoint( |
| 488 quad[2].x(), |
| 489 quad[2].y() - m_inner.radii().bottomRight().height()), |
| 490 quad[2]); |
| 491 } |
| 492 |
| 493 if (mitre1 == NoMitre) |
| 494 quad[1].setX(quad[0].x()); |
| 495 if (mitre2 == NoMitre) |
| 496 quad[2].setX(quad[3].x()); |
| 497 |
| 498 break; |
| 499 |
| 500 case BSRight: |
| 501 quad[0] = FloatPoint(outerRect.maxXMinYCorner()); |
| 502 quad[1] = FloatPoint(innerRect.maxXMinYCorner()); |
| 503 quad[2] = FloatPoint(innerRect.maxXMaxYCorner()); |
| 504 quad[3] = FloatPoint(outerRect.maxXMaxYCorner()); |
| 505 |
| 506 if (!m_inner.radii().topRight().isZero()) { |
| 507 findIntersection(quad[0], quad[1], |
| 508 FloatPoint( |
| 509 quad[1].x() - m_inner.radii().topRight().width(), |
| 510 quad[1].y()), |
| 511 FloatPoint( |
| 512 quad[1].x(), |
| 513 quad[1].y() + m_inner.radii().topRight().height()), |
| 514 quad[1]); |
| 515 } |
| 516 |
| 517 if (!m_inner.radii().bottomRight().isZero()) { |
| 518 findIntersection(quad[3], quad[2], |
| 519 FloatPoint( |
| 520 quad[2].x() - m_inner.radii().bottomRight().width(), |
| 521 quad[2].y()), |
| 522 FloatPoint( |
| 523 quad[2].x(), |
| 524 quad[2].y() - m_inner.radii().bottomRight().height()), |
| 525 quad[2]); |
| 526 } |
| 527 |
| 528 if (mitre1 == NoMitre) |
| 529 quad[1].setY(quad[0].y()); |
| 530 if (mitre2 == NoMitre) |
| 531 quad[2].setY(quad[3].y()); |
| 532 |
| 533 break; |
| 534 } |
| 535 |
| 536 bool antiAliasEdge1 = mitre1 != NonAntiAliasedMitre; |
| 537 bool antiAliasEdge2 = mitre2 != NonAntiAliasedMitre; |
| 538 if (antiAliasEdge1 == antiAliasEdge2) { |
| 539 context->clipPolygon(4, quad, antiAliasEdge1); |
| 540 return; |
| 541 } |
| 542 |
| 543 // If antialiasing settings for the first edge and second edge is different, |
| 544 // they have to be addressed separately. We do this by breaking the quad int
o |
| 545 // two parallelograms, made by moving quad[1] and quad[2]. |
| 546 float ax = quad[1].x() - quad[0].x(); |
| 547 float ay = quad[1].y() - quad[0].y(); |
| 548 float bx = quad[2].x() - quad[1].x(); |
| 549 float by = quad[2].y() - quad[1].y(); |
| 550 float cx = quad[3].x() - quad[2].x(); |
| 551 float cy = quad[3].y() - quad[2].y(); |
| 552 |
| 553 const static float kEpsilon = 1e-2f; |
| 554 float r1, r2; |
| 555 if (fabsf(bx) < kEpsilon && fabsf(by) < kEpsilon) { |
| 556 // The quad was actually a triangle. |
| 557 r1 = r2 = 1.0f; |
| 558 } else { |
| 559 // Extend parallelogram a bit to hide calculation error |
| 560 const static float kExtendFill = 1e-2f; |
| 561 |
| 562 r1 = (-ax * by + ay * bx) / (cx * by - cy * bx) + kExtendFill; |
| 563 r2 = (-cx * by + cy * bx) / (ax * by - ay * bx) + kExtendFill; |
| 564 } |
| 565 |
| 566 if (mitre1 != NoMitre) { |
| 567 FloatPoint firstQuad[4]; |
| 568 firstQuad[0] = quad[0]; |
| 569 firstQuad[1] = quad[1]; |
| 570 firstQuad[2] = FloatPoint(quad[3].x() + r2 * ax, quad[3].y() + r2 * ay); |
| 571 firstQuad[3] = quad[3]; |
| 572 context->clipPolygon(4, firstQuad, antiAliasEdge1); |
| 573 } |
| 574 |
| 575 if (mitre2 != NoMitre) { |
| 576 FloatPoint secondQuad[4]; |
| 577 secondQuad[0] = quad[0]; |
| 578 secondQuad[1] = FloatPoint(quad[0].x() - r1 * cx, quad[0].y() - r1 * cy)
; |
| 579 secondQuad[2] = quad[2]; |
| 580 secondQuad[3] = quad[3]; |
| 581 context->clipPolygon(4, secondQuad, antiAliasEdge2); |
| 582 } |
| 583 } |
| 584 |
| 585 } // namespace blink |
| OLD | NEW |