Web Inspector: [CSS Shapes] Refactor highlighting code to decrease Shapes API surface
[WebKit-https.git] / Source / WebCore / rendering / shapes / RectangleShape.cpp
1 /*
2  * Copyright (C) 2012 Adobe Systems Incorporated. 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  *
8  * 1. Redistributions of source code must retain the above
9  *    copyright notice, this list of conditions and the following
10  *    disclaimer.
11  * 2. Redistributions in binary form must reproduce the above
12  *    copyright notice, this list of conditions and the following
13  *    disclaimer in the documentation and/or other materials
14  *    provided with the distribution.
15  * 
16  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
19  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
20  * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
21  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
23  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
25  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
27  * OF THE POSSIBILITY OF SUCH DAMAGE.
28  */
29
30 #include "config.h"
31 #include "RectangleShape.h"
32
33 #include <wtf/MathExtras.h>
34
35 namespace WebCore {
36
37 static inline float ellipseXIntercept(float y, float rx, float ry)
38 {
39     ASSERT(ry > 0);
40     return rx * sqrt(1 - (y * y) / (ry * ry));
41 }
42
43 static inline float ellipseYIntercept(float x, float rx, float ry)
44 {
45     ASSERT(rx > 0);
46     return ry * sqrt(1 - (x * x) / (rx * rx));
47 }
48
49 RectangleShape::ShapeBounds RectangleShape::ShapeBounds::paddingBounds(float shapePadding) const
50 {
51     ASSERT(shapePadding >= 0);
52     if (!shapePadding || isEmpty())
53         return *this;
54
55     float boundsX = x() + std::min(width() / 2, shapePadding);
56     float boundsY = y() + std::min(height() / 2, shapePadding);
57     float boundsWidth = std::max(0.0f, width() - shapePadding * 2);
58     float boundsHeight = std::max(0.0f, height() - shapePadding * 2);
59     float boundsRadiusX = std::max(0.0f, rx() - shapePadding);
60     float boundsRadiusY = std::max(0.0f, ry() - shapePadding);
61     return RectangleShape::ShapeBounds(FloatRect(boundsX, boundsY, boundsWidth, boundsHeight), FloatSize(boundsRadiusX, boundsRadiusY));
62 }
63
64 RectangleShape::ShapeBounds RectangleShape::ShapeBounds::marginBounds(float shapeMargin) const
65 {
66     ASSERT(shapeMargin >= 0);
67     if (!shapeMargin)
68         return *this;
69
70     float boundsX = x() - shapeMargin;
71     float boundsY = y() - shapeMargin;
72     float boundsWidth = width() + shapeMargin * 2;
73     float boundsHeight = height() + shapeMargin * 2;
74     float boundsRadiusX = rx() + shapeMargin;
75     float boundsRadiusY = ry() + shapeMargin;
76     return RectangleShape::ShapeBounds(FloatRect(boundsX, boundsY, boundsWidth, boundsHeight), FloatSize(boundsRadiusX, boundsRadiusY));
77 }
78
79 FloatPoint RectangleShape::ShapeBounds::cornerInterceptForWidth(float widthAtIntercept) const
80 {
81     float xi = (width() - widthAtIntercept) / 2;
82     float yi = ry() - ellipseYIntercept(rx() - xi, rx(), ry());
83     return FloatPoint(xi, yi);
84 }
85
86 RectangleShape::ShapeBounds RectangleShape::shapePaddingBounds() const
87 {
88     if (!m_haveInitializedPaddingBounds) {
89         m_haveInitializedPaddingBounds = true;
90         m_paddingBounds = m_bounds.paddingBounds(shapePadding());
91     }
92     return m_paddingBounds;
93 }
94
95 RectangleShape::ShapeBounds RectangleShape::shapeMarginBounds() const
96 {
97     if (!m_haveInitializedMarginBounds) {
98         m_haveInitializedMarginBounds = true;
99         m_marginBounds = m_bounds.marginBounds(shapeMargin());
100     }
101     return m_marginBounds;
102 }
103
104 void RectangleShape::getExcludedIntervals(LayoutUnit logicalTop, LayoutUnit logicalHeight, SegmentList& result) const
105 {
106     const RectangleShape::ShapeBounds& bounds = shapeMarginBounds();
107     if (bounds.isEmpty())
108         return;
109
110     float y1 = logicalTop;
111     float y2 = logicalTop + logicalHeight;
112
113     if (y2 < bounds.y() || y1 >= bounds.maxY())
114         return;
115
116     float x1 = bounds.x();
117     float x2 = bounds.maxX();
118
119     if (bounds.ry() > 0) {
120         if (y2 < bounds.y() + bounds.ry()) {
121             float yi = y2 - bounds.y() - bounds.ry();
122             float xi = ellipseXIntercept(yi, bounds.rx(), bounds.ry());
123             x1 = bounds.x() + bounds.rx() - xi;
124             x2 = bounds.maxX() - bounds.rx() + xi;
125         } else if (y1 > bounds.maxY() - bounds.ry()) {
126             float yi =  y1 - (bounds.maxY() - bounds.ry());
127             float xi = ellipseXIntercept(yi, bounds.rx(), bounds.ry());
128             x1 = bounds.x() + bounds.rx() - xi;
129             x2 = bounds.maxX() - bounds.rx() + xi;
130         }
131     }
132
133     result.append(LineSegment(x1, x2));
134 }
135
136 void RectangleShape::getIncludedIntervals(LayoutUnit logicalTop, LayoutUnit logicalHeight, SegmentList& result) const
137 {
138     const RectangleShape::ShapeBounds& bounds = shapePaddingBounds();
139     if (bounds.isEmpty())
140         return;
141
142     float y1 = logicalTop;
143     float y2 = logicalTop + logicalHeight;
144
145     if (y1 < bounds.y() || y2 > bounds.maxY())
146         return;
147
148     float x1 = bounds.x();
149     float x2 = bounds.maxX();
150
151     if (bounds.ry() > 0) {
152         bool y1InterceptsCorner = y1 < bounds.y() + bounds.ry();
153         bool y2InterceptsCorner = y2 > bounds.maxY() - bounds.ry();
154         float xi = 0;
155
156         if (y1InterceptsCorner && y2InterceptsCorner) {
157             if  (y1 < bounds.height() + 2 * bounds.y() - y2) {
158                 float yi = y1 - bounds.y() - bounds.ry();
159                 xi = ellipseXIntercept(yi, bounds.rx(), bounds.ry());
160             } else {
161                 float yi =  y2 - (bounds.maxY() - bounds.ry());
162                 xi = ellipseXIntercept(yi, bounds.rx(), bounds.ry());
163             }
164         } else if (y1InterceptsCorner) {
165             float yi = y1 - bounds.y() - bounds.ry();
166             xi = ellipseXIntercept(yi, bounds.rx(), bounds.ry());
167         } else if (y2InterceptsCorner) {
168             float yi =  y2 - (bounds.maxY() - bounds.ry());
169             xi = ellipseXIntercept(yi, bounds.rx(), bounds.ry());
170         }
171
172         if (y1InterceptsCorner || y2InterceptsCorner) {
173             x1 = bounds.x() + bounds.rx() - xi;
174             x2 = bounds.maxX() - bounds.rx() + xi;
175         }
176     }
177
178     result.append(LineSegment(x1, x2));
179 }
180
181 bool RectangleShape::firstIncludedIntervalLogicalTop(LayoutUnit minLogicalIntervalTop, const LayoutSize& minLogicalIntervalSize, LayoutUnit& result) const
182 {
183     float minIntervalTop = minLogicalIntervalTop;
184     float minIntervalHeight = minLogicalIntervalSize.height();
185     float minIntervalWidth = minLogicalIntervalSize.width();
186
187     const RectangleShape::ShapeBounds& bounds = shapePaddingBounds();
188     if (bounds.isEmpty() || minIntervalWidth > bounds.width())
189         return false;
190
191     float minY = std::max(bounds.y(), minIntervalTop);
192     float maxY = minY + minIntervalHeight;
193
194     if (maxY > bounds.maxY())
195         return false;
196
197     bool intervalOverlapsMinCorner = minY < bounds.y() + bounds.ry();
198     bool intervalOverlapsMaxCorner = maxY > bounds.maxY() - bounds.ry();
199
200     if (!intervalOverlapsMinCorner && !intervalOverlapsMaxCorner) {
201         result = minY;
202         return true;
203     }
204
205     float centerY = bounds.y() + bounds.height() / 2;
206     bool minCornerDefinesX = fabs(centerY - minY) > fabs(centerY - maxY);
207     bool intervalFitsWithinCorners = minIntervalWidth + 2 * bounds.rx() <= bounds.width();
208     FloatPoint cornerIntercept = bounds.cornerInterceptForWidth(minIntervalWidth);
209
210     if (intervalOverlapsMinCorner && (!intervalOverlapsMaxCorner || minCornerDefinesX)) {
211         if (intervalFitsWithinCorners || bounds.y() + cornerIntercept.y() < minY) {
212             result = minY;
213             return true;
214         }
215         if (minIntervalHeight < bounds.height() - (2 * cornerIntercept.y())) {
216             result = ceiledLayoutUnit(bounds.y() + cornerIntercept.y());
217             return true;
218         }
219     }
220
221     if (intervalOverlapsMaxCorner && (!intervalOverlapsMinCorner || !minCornerDefinesX)) {
222         if (intervalFitsWithinCorners || minY <=  bounds.maxY() - cornerIntercept.y() - minIntervalHeight) {
223             result = minY;
224             return true;
225         }
226     }
227
228     return false;
229 }
230
231 void RectangleShape::buildPath(Path& path) const
232 {
233     path.addRoundedRect(m_bounds, FloatSize(m_bounds.rx(), m_bounds.ry()), Path::PreferBezierRoundedRect);
234 }
235
236 } // namespace WebCore