[WTF] Import std::optional reference implementation as WTF::Optional
[WebKit-https.git] / Source / WebCore / platform / graphics / PathUtilities.cpp
1 /*
2  * Copyright (C) 2014-2015 Apple Inc.  All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
24  */
25
26
27 #include "config.h"
28 #include "PathUtilities.h"
29
30 #include "AffineTransform.h"
31 #include "BorderData.h"
32 #include "FloatPoint.h"
33 #include "FloatRect.h"
34 #include "FloatRoundedRect.h"
35 #include "GeometryUtilities.h"
36 #include <math.h>
37 #include <wtf/MathExtras.h>
38
39 namespace WebCore {
40
41 class FloatPointGraph {
42     WTF_MAKE_NONCOPYABLE(FloatPointGraph);
43 public:
44     FloatPointGraph() { }
45
46     class Node : public FloatPoint {
47         WTF_MAKE_NONCOPYABLE(Node);
48     public:
49         Node(FloatPoint point)
50             : FloatPoint(point)
51         { }
52
53         const Vector<Node*>& nextPoints() const { return m_nextPoints; }
54         void addNextPoint(Node* node)
55         {
56             if (!m_nextPoints.contains(node))
57                 m_nextPoints.append(node);
58         }
59
60         bool isVisited() const { return m_visited; }
61         void visit() { m_visited = true; }
62
63         void reset() { m_visited = false; m_nextPoints.clear(); }
64
65     private:
66         Vector<Node*> m_nextPoints;
67         bool m_visited { false };
68     };
69
70     typedef std::pair<Node*, Node*> Edge;
71     typedef Vector<Edge> Polygon;
72
73     Node* findOrCreateNode(FloatPoint);
74
75     void reset()
76     {
77         for (auto& node : m_allNodes)
78             node->reset();
79     }
80
81 private:
82     Vector<std::unique_ptr<Node>> m_allNodes;
83 };
84
85 FloatPointGraph::Node* FloatPointGraph::findOrCreateNode(FloatPoint point)
86 {
87     for (auto& testNode : m_allNodes) {
88         if (areEssentiallyEqual(*testNode, point))
89             return testNode.get();
90     }
91
92     m_allNodes.append(std::make_unique<FloatPointGraph::Node>(point));
93     return m_allNodes.last().get();
94 }
95
96 static bool findLineSegmentIntersection(const FloatPointGraph::Edge& edgeA, const FloatPointGraph::Edge& edgeB, FloatPoint& intersectionPoint)
97 {
98     if (!findIntersection(*edgeA.first, *edgeA.second, *edgeB.first, *edgeB.second, intersectionPoint))
99         return false;
100
101     FloatPoint edgeAVec(*edgeA.second - *edgeA.first);
102     FloatPoint edgeBVec(*edgeB.second - *edgeB.first);
103
104     float dotA = edgeAVec.dot(toFloatPoint(intersectionPoint - *edgeA.first));
105     if (dotA < 0 || dotA > edgeAVec.lengthSquared())
106         return false;
107
108     float dotB = edgeBVec.dot(toFloatPoint(intersectionPoint - *edgeB.first));
109     if (dotB < 0 || dotB > edgeBVec.lengthSquared())
110         return false;
111
112     return true;
113 }
114
115 static bool addIntersectionPoints(Vector<FloatPointGraph::Polygon>& polys, FloatPointGraph& graph)
116 {
117     bool foundAnyIntersections = false;
118
119     Vector<FloatPointGraph::Edge> allEdges;
120     for (auto& poly : polys)
121         allEdges.appendVector(poly);
122
123     for (const FloatPointGraph::Edge& edgeA : allEdges) {
124         Vector<FloatPointGraph::Node*> intersectionPoints({edgeA.first, edgeA.second});
125
126         for (const FloatPointGraph::Edge& edgeB : allEdges) {
127             if (&edgeA == &edgeB)
128                 continue;
129
130             FloatPoint intersectionPoint;
131             if (!findLineSegmentIntersection(edgeA, edgeB, intersectionPoint))
132                 continue;
133             foundAnyIntersections = true;
134             intersectionPoints.append(graph.findOrCreateNode(intersectionPoint));
135         }
136
137         std::sort(intersectionPoints.begin(), intersectionPoints.end(), [edgeA](auto* a, auto* b) {
138             return FloatPoint(*edgeA.first - *b).lengthSquared() > FloatPoint(*edgeA.first - *a).lengthSquared();
139         });
140
141         for (unsigned pointIndex = 1; pointIndex < intersectionPoints.size(); pointIndex++)
142             intersectionPoints[pointIndex - 1]->addNextPoint(intersectionPoints[pointIndex]);
143     }
144
145     return foundAnyIntersections;
146 }
147
148 static FloatPointGraph::Polygon walkGraphAndExtractPolygon(FloatPointGraph::Node* startNode)
149 {
150     FloatPointGraph::Polygon outPoly;
151
152     FloatPointGraph::Node* currentNode = startNode;
153     FloatPointGraph::Node* previousNode = startNode;
154
155     do {
156         currentNode->visit();
157
158         FloatPoint currentVec(*previousNode - *currentNode);
159         currentVec.normalize();
160
161         // Walk the graph, at each node choosing the next non-visited
162         // point with the greatest internal angle.
163         FloatPointGraph::Node* nextNode = nullptr;
164         float nextNodeAngle = 0;
165         for (auto* potentialNextNode : currentNode->nextPoints()) {
166             if (potentialNextNode == currentNode)
167                 continue;
168
169             // If we can get back to the start, we should, ignoring the fact that we already visited it.
170             // Otherwise we'll head inside the shape.
171             if (potentialNextNode == startNode) {
172                 nextNode = startNode;
173                 break;
174             }
175
176             if (potentialNextNode->isVisited())
177                 continue;
178
179             FloatPoint nextVec(*potentialNextNode - *currentNode);
180             nextVec.normalize();
181
182             float angle = acos(nextVec.dot(currentVec));
183             float crossZ = nextVec.x() * currentVec.y() - nextVec.y() * currentVec.x();
184
185             if (crossZ < 0)
186                 angle = (2 * piFloat) - angle;
187
188             if (!nextNode || angle > nextNodeAngle) {
189                 nextNode = potentialNextNode;
190                 nextNodeAngle = angle;
191             }
192         }
193
194         // If we don't end up at a node adjacent to the starting node,
195         // something went wrong (there's probably a hole in the shape),
196         // so bail out. We'll use a bounding box instead.
197         if (!nextNode)
198             return FloatPointGraph::Polygon();
199
200         outPoly.append(std::make_pair(currentNode, nextNode));
201
202         previousNode = currentNode;
203         currentNode = nextNode;
204     } while (currentNode != startNode);
205
206     return outPoly;
207 }
208
209 static FloatPointGraph::Node* findUnvisitedPolygonStartPoint(Vector<FloatPointGraph::Polygon>& polys)
210 {
211     for (auto& poly : polys) {
212         for (auto& edge : poly) {
213             if (edge.first->isVisited() || edge.second->isVisited())
214                 goto nextPolygon;
215         }
216
217         // FIXME: We should make sure we find an outside edge to start with.
218         return poly[0].first;
219     nextPolygon:
220         continue;
221     }
222     return nullptr;
223 }
224
225 static Vector<FloatPointGraph::Polygon> unitePolygons(Vector<FloatPointGraph::Polygon>& polys, FloatPointGraph& graph)
226 {
227     graph.reset();
228
229     // There are no intersections, so the polygons are disjoint (we already removed wholly-contained rects in an earlier step).
230     if (!addIntersectionPoints(polys, graph))
231         return polys;
232
233     Vector<FloatPointGraph::Polygon> unitedPolygons;
234
235     while (FloatPointGraph::Node* startNode = findUnvisitedPolygonStartPoint(polys)) {
236         FloatPointGraph::Polygon unitedPolygon = walkGraphAndExtractPolygon(startNode);
237         if (unitedPolygon.isEmpty())
238             return Vector<FloatPointGraph::Polygon>();
239         unitedPolygons.append(unitedPolygon);
240     }
241
242     return unitedPolygons;
243 }
244
245 static FloatPointGraph::Polygon edgesForRect(FloatRect rect, FloatPointGraph& graph)
246 {
247     auto minMin = graph.findOrCreateNode(rect.minXMinYCorner());
248     auto minMax = graph.findOrCreateNode(rect.minXMaxYCorner());
249     auto maxMax = graph.findOrCreateNode(rect.maxXMaxYCorner());
250     auto maxMin = graph.findOrCreateNode(rect.maxXMinYCorner());
251
252     return FloatPointGraph::Polygon({
253         std::make_pair(minMin, maxMin),
254         std::make_pair(maxMin, maxMax),
255         std::make_pair(maxMax, minMax),
256         std::make_pair(minMax, minMin)
257     });
258 }
259
260 static Vector<FloatPointGraph::Polygon> polygonsForRect(const Vector<FloatRect>& rects, FloatPointGraph& graph)
261 {
262     Vector<FloatRect> sortedRects = rects;
263     std::sort(sortedRects.begin(), sortedRects.end(), [](FloatRect a, FloatRect b) { return b.y() > a.y(); });
264
265     Vector<FloatPointGraph::Polygon> rectPolygons;
266     rectPolygons.reserveInitialCapacity(sortedRects.size());
267
268     for (auto& rect : sortedRects) {
269         bool isContained = false;
270         for (auto& otherRect : sortedRects) {
271             if (&rect == &otherRect)
272                 continue;
273             if (otherRect.contains(rect)) {
274                 isContained = true;
275                 break;
276             }
277         }
278
279         if (!isContained)
280             rectPolygons.append(edgesForRect(rect, graph));
281     }
282     return unitePolygons(rectPolygons, graph);
283 }
284
285 Vector<Path> PathUtilities::pathsWithShrinkWrappedRects(const Vector<FloatRect>& rects, float radius)
286 {
287     Vector<Path> paths;
288
289     if (rects.isEmpty())
290         return paths;
291
292     if (rects.size() > 20) {
293         Path path;
294         path.addRoundedRect(unionRect(rects), FloatSize(radius, radius));
295         paths.append(path);
296         return paths;
297     }
298
299     FloatPointGraph graph;
300     Vector<FloatPointGraph::Polygon> polys = polygonsForRect(rects, graph);
301     if (polys.isEmpty()) {
302         Path path;
303         path.addRoundedRect(unionRect(rects), FloatSize(radius, radius));
304         paths.append(path);
305         return paths;
306     }
307
308     for (auto& poly : polys) {
309         Path path;
310         for (unsigned i = 0; i < poly.size(); ++i) {
311             FloatPointGraph::Edge& toEdge = poly[i];
312             // Connect the first edge to the last.
313             FloatPointGraph::Edge& fromEdge = (i > 0) ? poly[i - 1] : poly[poly.size() - 1];
314
315             FloatPoint fromEdgeVec = toFloatPoint(*fromEdge.second - *fromEdge.first);
316             FloatPoint toEdgeVec = toFloatPoint(*toEdge.second - *toEdge.first);
317
318             // Clamp the radius to no more than half the length of either adjacent edge,
319             // because we want a smooth curve and don't want unequal radii.
320             float clampedRadius = std::min(radius, fabsf(fromEdgeVec.x() ? fromEdgeVec.x() : fromEdgeVec.y()) / 2);
321             clampedRadius = std::min(clampedRadius, fabsf(toEdgeVec.x() ? toEdgeVec.x() : toEdgeVec.y()) / 2);
322
323             FloatPoint fromEdgeNorm = fromEdgeVec;
324             fromEdgeNorm.normalize();
325             FloatPoint toEdgeNorm = toEdgeVec;
326             toEdgeNorm.normalize();
327
328             // Project the radius along the incoming and outgoing edge.
329             FloatSize fromOffset = clampedRadius * toFloatSize(fromEdgeNorm);
330             FloatSize toOffset = clampedRadius * toFloatSize(toEdgeNorm);
331
332             if (!i)
333                 path.moveTo(*fromEdge.second - fromOffset);
334             else
335                 path.addLineTo(*fromEdge.second - fromOffset);
336             path.addArcTo(*fromEdge.second, *toEdge.first + toOffset, clampedRadius);
337         }
338         path.closeSubpath();
339         paths.append(path);
340     }
341     return paths;
342 }
343
344 Path PathUtilities::pathWithShrinkWrappedRects(const Vector<FloatRect>& rects, float radius)
345 {
346     Vector<Path> paths = pathsWithShrinkWrappedRects(rects, radius);
347
348     Path unionPath;
349     for (const auto& path : paths)
350         unionPath.addPath(path, AffineTransform());
351
352     return unionPath;
353 }
354
355 static std::pair<FloatPoint, FloatPoint> startAndEndPointsForCorner(const FloatPointGraph::Edge& fromEdge, const FloatPointGraph::Edge& toEdge, const FloatSize& radius)
356 {
357     FloatPoint startPoint;
358     FloatPoint endPoint;
359     
360     FloatSize fromEdgeVector = *fromEdge.second - *fromEdge.first;
361     FloatSize toEdgeVector = *toEdge.second - *toEdge.first;
362
363     FloatPoint fromEdgeNorm = toFloatPoint(fromEdgeVector);
364     fromEdgeNorm.normalize();
365     FloatSize fromOffset = FloatSize(radius.width() * fromEdgeNorm.x(), radius.height() * fromEdgeNorm.y());
366     startPoint = *fromEdge.second - fromOffset;
367
368     FloatPoint toEdgeNorm = toFloatPoint(toEdgeVector);
369     toEdgeNorm.normalize();
370     FloatSize toOffset = FloatSize(radius.width() * toEdgeNorm.x(), radius.height() * toEdgeNorm.y());
371     endPoint = *toEdge.first + toOffset;
372     return std::make_pair(startPoint, endPoint);
373 }
374
375 enum class CornerType { TopLeft, TopRight, BottomRight, BottomLeft, Other };
376 static CornerType cornerType(const FloatPointGraph::Edge& fromEdge, const FloatPointGraph::Edge& toEdge)
377 {
378     auto fromEdgeVector = *fromEdge.second - *fromEdge.first;
379     auto toEdgeVector = *toEdge.second - *toEdge.first;
380
381     if (fromEdgeVector.height() < 0 && toEdgeVector.width() > 0)
382         return CornerType::TopLeft;
383     if (fromEdgeVector.width() > 0 && toEdgeVector.height() > 0)
384         return CornerType::TopRight;
385     if (fromEdgeVector.height() > 0 && toEdgeVector.width() < 0)
386         return CornerType::BottomRight;
387     if (fromEdgeVector.width() < 0 && toEdgeVector.height() < 0)
388         return CornerType::BottomLeft;
389     return CornerType::Other;
390 }
391
392 static CornerType cornerTypeForMultiline(const FloatPointGraph::Edge& fromEdge, const FloatPointGraph::Edge& toEdge, const Vector<FloatPoint>& corners)
393 {
394     auto corner = cornerType(fromEdge, toEdge);
395     if (corner == CornerType::TopLeft && corners.at(0) == *fromEdge.second)
396         return corner;
397     if (corner == CornerType::TopRight && corners.at(1) == *fromEdge.second)
398         return corner;
399     if (corner == CornerType::BottomRight && corners.at(2) == *fromEdge.second)
400         return corner;
401     if (corner == CornerType::BottomLeft && corners.at(3) == *fromEdge.second)
402         return corner;
403     return CornerType::Other;
404 }
405
406 static std::pair<FloatPoint, FloatPoint> controlPointsForBezierCurve(CornerType cornerType, const FloatPointGraph::Edge& fromEdge,
407     const FloatPointGraph::Edge& toEdge, const FloatSize& radius)
408 {
409     FloatPoint cp1;
410     FloatPoint cp2;
411     switch (cornerType) {
412     case CornerType::TopLeft: {
413         cp1 = FloatPoint(fromEdge.second->x(), fromEdge.second->y() + radius.height() * Path::circleControlPoint());
414         cp2 = FloatPoint(toEdge.first->x() + radius.width() * Path::circleControlPoint(), toEdge.first->y());
415         break;
416     }
417     case CornerType::TopRight: {
418         cp1 = FloatPoint(fromEdge.second->x() - radius.width() * Path::circleControlPoint(), fromEdge.second->y());
419         cp2 = FloatPoint(toEdge.first->x(), toEdge.first->y() + radius.height() * Path::circleControlPoint());
420         break;
421     }
422     case CornerType::BottomRight: {
423         cp1 = FloatPoint(fromEdge.second->x(), fromEdge.second->y() - radius.height() * Path::circleControlPoint());
424         cp2 = FloatPoint(toEdge.first->x() - radius.width() * Path::circleControlPoint(), toEdge.first->y());
425         break;
426     }
427     case CornerType::BottomLeft: {
428         cp1 = FloatPoint(fromEdge.second->x() + radius.width() * Path::circleControlPoint(), fromEdge.second->y());
429         cp2 = FloatPoint(toEdge.first->x(), toEdge.first->y() - radius.height() * Path::circleControlPoint());
430         break;
431     }
432     case CornerType::Other: {
433         ASSERT_NOT_REACHED();
434         break;
435     }
436     }
437     return std::make_pair(cp1, cp2);
438 }
439
440 static FloatRoundedRect::Radii adjustedtRadiiForHuggingCurve(const FloatSize& topLeftRadius, const FloatSize& topRightRadius,
441     const FloatSize& bottomLeftRadius, const FloatSize& bottomRightRadius, float outlineOffset)
442 {
443     FloatRoundedRect::Radii radii;
444     // This adjusts the radius so that it follows the border curve even when offset is present.
445     auto adjustedRadius = [outlineOffset](const FloatSize& radius)
446     {
447         FloatSize expandSize;
448         if (radius.width() > outlineOffset)
449             expandSize.setWidth(std::min(outlineOffset, radius.width() - outlineOffset));
450         if (radius.height() > outlineOffset)
451             expandSize.setHeight(std::min(outlineOffset, radius.height() - outlineOffset));
452         FloatSize adjustedRadius = radius;
453         adjustedRadius.expand(expandSize.width(), expandSize.height());
454         // Do not go to negative radius.
455         return adjustedRadius.expandedTo(FloatSize(0, 0));
456     };
457
458     radii.setTopLeft(adjustedRadius(topLeftRadius));
459     radii.setTopRight(adjustedRadius(topRightRadius));
460     radii.setBottomRight(adjustedRadius(bottomRightRadius));
461     radii.setBottomLeft(adjustedRadius(bottomLeftRadius));
462     return radii;
463 }
464     
465 static std::optional<FloatRect> rectFromPolygon(const FloatPointGraph::Polygon& poly)
466 {
467     if (poly.size() != 4)
468         return std::optional<FloatRect>();
469
470     std::optional<FloatPoint> topLeft;
471     std::optional<FloatPoint> bottomRight;
472     for (unsigned i = 0; i < poly.size(); ++i) {
473         const auto& toEdge = poly[i];
474         const auto& fromEdge = (i > 0) ? poly[i - 1] : poly[poly.size() - 1];
475         auto corner = cornerType(fromEdge, toEdge);
476         if (corner == CornerType::TopLeft) {
477             ASSERT(!topLeft);
478             topLeft = *fromEdge.second;
479         } else if (corner == CornerType::BottomRight) {
480             ASSERT(!bottomRight);
481             bottomRight = *fromEdge.second;
482         }
483     }
484     if (!topLeft || !bottomRight)
485         return std::optional<FloatRect>();
486     return FloatRect(topLeft.value(), bottomRight.value());
487 }
488
489 Path PathUtilities::pathWithShrinkWrappedRectsForOutline(const Vector<FloatRect>& rects, const BorderData& borderData, float outlineOffset, TextDirection direction,
490     WritingMode writingMode, float deviceScaleFactor)
491 {
492     ASSERT(borderData.hasBorderRadius());
493     FloatSize topLeftRadius = FloatSize(borderData.topLeft().width().value(), borderData.topLeft().height().value());
494     FloatSize topRightRadius = FloatSize(borderData.topRight().width().value(), borderData.topRight().height().value());
495     FloatSize bottomRightRadius = FloatSize(borderData.bottomRight().width().value(), borderData.bottomRight().height().value());
496     FloatSize bottomLeftRadius = FloatSize(borderData.bottomLeft().width().value(), borderData.bottomLeft().height().value());
497
498     auto roundedRect = [topLeftRadius, topRightRadius, bottomRightRadius, bottomLeftRadius, outlineOffset, deviceScaleFactor] (const FloatRect& rect)
499     {
500         auto radii = adjustedtRadiiForHuggingCurve(topLeftRadius, topRightRadius, bottomLeftRadius, bottomRightRadius, outlineOffset);
501         radii.scale(calcBorderRadiiConstraintScaleFor(rect, radii));
502         RoundedRect roundedRect(LayoutRect(rect),
503             RoundedRect::Radii(LayoutSize(radii.topLeft()), LayoutSize(radii.topRight()), LayoutSize(radii.bottomLeft()), LayoutSize(radii.bottomRight())));
504         Path path;
505         path.addRoundedRect(roundedRect.pixelSnappedRoundedRectForPainting(deviceScaleFactor));
506         return path;
507     };
508
509     if (rects.size() == 1)
510         return roundedRect(rects.at(0));
511
512     FloatPointGraph graph;
513     const auto polys = polygonsForRect(rects, graph);
514     // Fall back to corner painting with no radius for empty and disjoint rectangles.
515     if (!polys.size() || polys.size() > 1)
516         return Path();
517     const auto& poly = polys.at(0);
518     // Fast path when poly has one rect only.
519     std::optional<FloatRect> rect = rectFromPolygon(poly);
520     if (rect)
521         return roundedRect(rect.value());
522
523     Path path;
524     // Multiline outline needs to match multiline border painting. Only first and last lines are getting rounded borders.
525     auto isLeftToRight = isLeftToRightDirection(direction);
526     auto firstLineRect = isLeftToRight ? rects.at(0) : rects.at(rects.size() - 1);
527     auto lastLineRect = isLeftToRight ? rects.at(rects.size() - 1) : rects.at(0);
528     // Adjust radius so that it matches the box border.
529     auto firstLineRadii = FloatRoundedRect::Radii(topLeftRadius, topRightRadius, bottomLeftRadius, bottomRightRadius);
530     auto lastLineRadii = FloatRoundedRect::Radii(topLeftRadius, topRightRadius, bottomLeftRadius, bottomRightRadius);
531     firstLineRadii.scale(calcBorderRadiiConstraintScaleFor(firstLineRect, firstLineRadii));
532     lastLineRadii.scale(calcBorderRadiiConstraintScaleFor(lastLineRect, lastLineRadii));
533     topLeftRadius = firstLineRadii.topLeft();
534     bottomLeftRadius = firstLineRadii.bottomLeft();
535     topRightRadius = lastLineRadii.topRight();
536     bottomRightRadius = lastLineRadii.bottomRight();
537     Vector<FloatPoint> corners;
538     // physical topLeft/topRight/bottomRight/bottomLeft
539     auto isHorizontal = isHorizontalWritingMode(writingMode);
540     corners.append(firstLineRect.minXMinYCorner());
541     corners.append(isHorizontal ? lastLineRect.maxXMinYCorner() : firstLineRect.maxXMinYCorner());
542     corners.append(lastLineRect.maxXMaxYCorner());
543     corners.append(isHorizontal ? firstLineRect.minXMaxYCorner() : lastLineRect.minXMaxYCorner());
544
545     for (unsigned i = 0; i < poly.size(); ++i) {
546         auto moveOrAddLineTo = [i, &path] (const FloatPoint& startPoint)
547         {
548             if (!i)
549                 path.moveTo(startPoint);
550             else
551                 path.addLineTo(startPoint);
552         };
553         const auto& toEdge = poly[i];
554         const auto& fromEdge = (i > 0) ? poly[i - 1] : poly[poly.size() - 1];
555         FloatSize radius;
556         auto corner = cornerTypeForMultiline(fromEdge, toEdge, corners);
557         switch (corner) {
558         case CornerType::TopLeft: {
559             radius = topLeftRadius;
560             break;
561         }
562         case CornerType::TopRight: {
563             radius = topRightRadius;
564             break;
565         }
566         case CornerType::BottomRight: {
567             radius = bottomRightRadius;
568             break;
569         }
570         case CornerType::BottomLeft: {
571             radius = bottomLeftRadius;
572             break;
573         }
574         case CornerType::Other: {
575             // Do not apply border radius on corners that normal border painting skips. (multiline content)
576             moveOrAddLineTo(*fromEdge.second);
577             continue;
578         }
579         }
580         FloatPoint startPoint;
581         FloatPoint endPoint;
582         std::tie(startPoint, endPoint) = startAndEndPointsForCorner(fromEdge, toEdge, radius);
583         moveOrAddLineTo(startPoint);
584
585         FloatPoint cp1;
586         FloatPoint cp2;
587         std::tie(cp1, cp2) = controlPointsForBezierCurve(corner, fromEdge, toEdge, radius);
588         path.addBezierCurveTo(cp1, cp2, endPoint);
589     }
590     path.closeSubpath();
591     return path;
592 }
593
594
595 }