Implement CSS `display: flow-root` (modern clearfix)
[WebKit-https.git] / Source / WebCore / rendering / style / BasicShapes.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 HOLDER "AS IS" AND ANY
17  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE
20  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
21  * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
22  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
23  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
25  * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
26  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  */
29
30 #include "config.h"
31
32 #include "BasicShapes.h"
33
34 #include "BasicShapeFunctions.h"
35 #include "CalculationValue.h"
36 #include "FloatRect.h"
37 #include "FloatRoundedRect.h"
38 #include "LengthFunctions.h"
39 #include "Path.h"
40 #include "RenderBox.h"
41 #include "SVGPathByteStream.h"
42 #include "SVGPathUtilities.h"
43
44 #include <wtf/NeverDestroyed.h>
45 #include <wtf/TinyLRUCache.h>
46
47 namespace WebCore {
48
49 void BasicShapeCenterCoordinate::updateComputedLength()
50 {
51     if (m_direction == TopLeft) {
52         m_computedLength = m_length.isUndefined() ? Length(0, Fixed) : m_length;
53         return;
54     }
55
56     if (m_length.isUndefined()) {
57         m_computedLength = Length(100, Percent);
58         return;
59     }
60     
61     m_computedLength = convertTo100PercentMinusLength(m_length);
62 }
63
64 struct SVGPathTranslatedByteStream {
65     SVGPathTranslatedByteStream(const FloatPoint& offset, const SVGPathByteStream& rawStream)
66         : m_offset(offset)
67         , m_rawStream(rawStream)
68     { }
69
70     bool operator==(const SVGPathTranslatedByteStream& other) const { return other.m_offset == m_offset && other.m_rawStream == m_rawStream; }
71     bool operator!=(const SVGPathTranslatedByteStream& other) const { return !(*this == other); }
72     bool isEmpty() const { return m_rawStream.isEmpty(); }
73
74     Path path() const
75     {
76         Path path = buildPathFromByteStream(m_rawStream);
77         path.translate(toFloatSize(m_offset));
78         return path;
79     }
80     
81     FloatPoint m_offset;
82     SVGPathByteStream m_rawStream;
83 };
84
85 struct EllipsePathPolicy : public TinyLRUCachePolicy<FloatRect, Path> {
86 public:
87     static bool isKeyNull(const FloatRect& rect) { return rect.isEmpty(); }
88
89     static Path createValueForKey(const FloatRect& rect)
90     {
91         Path path;
92         path.addEllipse(rect);
93         return path;
94     }
95 };
96
97 struct RoundedRectPathPolicy : public TinyLRUCachePolicy<FloatRoundedRect, Path> {
98 public:
99     static bool isKeyNull(const FloatRoundedRect& rect) { return rect.isEmpty(); }
100
101     static Path createValueForKey(const FloatRoundedRect& rect)
102     {
103         Path path;
104         path.addRoundedRect(rect);
105         return path;
106     }
107 };
108
109 struct PolygonPathPolicy : public TinyLRUCachePolicy<Vector<FloatPoint>, Path> {
110 public:
111     static bool isKeyNull(const Vector<FloatPoint>& points) { return !points.size(); }
112
113     static Path createValueForKey(const Vector<FloatPoint>& points) { return Path::polygonPathFromPoints(points); }
114 };
115
116 struct TranslatedByteStreamPathPolicy : public TinyLRUCachePolicy<SVGPathTranslatedByteStream, Path> {
117 public:
118     static bool isKeyNull(const SVGPathTranslatedByteStream& stream) { return stream.isEmpty(); }
119
120     static Path createValueForKey(const SVGPathTranslatedByteStream& stream) { return stream.path(); }
121 };
122
123 static const Path& cachedEllipsePath(const FloatRect& rect)
124 {
125     static NeverDestroyed<TinyLRUCache<FloatRect, Path, 4, EllipsePathPolicy>> cache;
126     return cache.get().get(rect);
127 }
128
129 static const Path& cachedRoundedRectPath(const FloatRoundedRect& rect)
130 {
131     static NeverDestroyed<TinyLRUCache<FloatRoundedRect, Path, 4, RoundedRectPathPolicy>> cache;
132     return cache.get().get(rect);
133 }
134
135 static const Path& cachedPolygonPath(const Vector<FloatPoint>& points)
136 {
137     static NeverDestroyed<TinyLRUCache<Vector<FloatPoint>, Path, 4, PolygonPathPolicy>> cache;
138     return cache.get().get(points);
139 }
140
141 static const Path& cachedTranslatedByteStreamPath(const SVGPathByteStream& stream, const FloatPoint& offset)
142 {
143     static NeverDestroyed<TinyLRUCache<SVGPathTranslatedByteStream, Path, 4, TranslatedByteStreamPathPolicy>> cache;
144     return cache.get().get(SVGPathTranslatedByteStream(offset, stream));
145 }
146
147 bool BasicShapeCircle::operator==(const BasicShape& other) const
148 {
149     if (type() != other.type())
150         return false;
151
152     auto& otherCircle = downcast<BasicShapeCircle>(other);
153     return m_centerX == otherCircle.m_centerX
154         && m_centerY == otherCircle.m_centerY
155         && m_radius == otherCircle.m_radius;
156 }
157
158 float BasicShapeCircle::floatValueForRadiusInBox(float boxWidth, float boxHeight) const
159 {
160     if (m_radius.type() == BasicShapeRadius::Value)
161         return floatValueForLength(m_radius.value(), sqrtf((boxWidth * boxWidth + boxHeight * boxHeight) / 2));
162
163     float centerX = floatValueForCenterCoordinate(m_centerX, boxWidth);
164     float centerY = floatValueForCenterCoordinate(m_centerY, boxHeight);
165
166     float widthDelta = std::abs(boxWidth - centerX);
167     float heightDelta = std::abs(boxHeight - centerY);
168     if (m_radius.type() == BasicShapeRadius::ClosestSide)
169         return std::min(std::min(std::abs(centerX), widthDelta), std::min(std::abs(centerY), heightDelta));
170
171     // If radius.type() == BasicShapeRadius::FarthestSide.
172     return std::max(std::max(std::abs(centerX), widthDelta), std::max(std::abs(centerY), heightDelta));
173 }
174
175 const Path& BasicShapeCircle::path(const FloatRect& boundingBox)
176 {
177     float centerX = floatValueForCenterCoordinate(m_centerX, boundingBox.width());
178     float centerY = floatValueForCenterCoordinate(m_centerY, boundingBox.height());
179     float radius = floatValueForRadiusInBox(boundingBox.width(), boundingBox.height());
180
181     return cachedEllipsePath(FloatRect(centerX - radius + boundingBox.x(), centerY - radius + boundingBox.y(), radius * 2, radius * 2));
182 }
183
184 bool BasicShapeCircle::canBlend(const BasicShape& other) const
185 {
186     if (type() != other.type())
187         return false;
188
189     return radius().canBlend(downcast<BasicShapeCircle>(other).radius());
190 }
191
192 Ref<BasicShape> BasicShapeCircle::blend(const BasicShape& other, double progress) const
193 {
194     ASSERT(type() == other.type());
195     auto& otherCircle = downcast<BasicShapeCircle>(other);
196     auto result =  BasicShapeCircle::create();
197
198     result->setCenterX(m_centerX.blend(otherCircle.centerX(), progress));
199     result->setCenterY(m_centerY.blend(otherCircle.centerY(), progress));
200     result->setRadius(m_radius.blend(otherCircle.radius(), progress));
201     return result;
202 }
203
204 bool BasicShapeEllipse::operator==(const BasicShape& other) const
205 {
206     if (type() != other.type())
207         return false;
208
209     auto& otherEllipse = downcast<BasicShapeEllipse>(other);
210     return m_centerX == otherEllipse.m_centerX
211         && m_centerY == otherEllipse.m_centerY
212         && m_radiusX == otherEllipse.m_radiusX
213         && m_radiusY == otherEllipse.m_radiusY;
214 }
215
216 float BasicShapeEllipse::floatValueForRadiusInBox(const BasicShapeRadius& radius, float center, float boxWidthOrHeight) const
217 {
218     if (radius.type() == BasicShapeRadius::Value)
219         return floatValueForLength(radius.value(), std::abs(boxWidthOrHeight));
220
221     float widthOrHeightDelta = std::abs(boxWidthOrHeight - center);
222     if (radius.type() == BasicShapeRadius::ClosestSide)
223         return std::min(std::abs(center), widthOrHeightDelta);
224
225     ASSERT(radius.type() == BasicShapeRadius::FarthestSide);
226     return std::max(std::abs(center), widthOrHeightDelta);
227 }
228
229 const Path& BasicShapeEllipse::path(const FloatRect& boundingBox)
230 {
231     float centerX = floatValueForCenterCoordinate(m_centerX, boundingBox.width());
232     float centerY = floatValueForCenterCoordinate(m_centerY, boundingBox.height());
233     float radiusX = floatValueForRadiusInBox(m_radiusX, centerX, boundingBox.width());
234     float radiusY = floatValueForRadiusInBox(m_radiusY, centerY, boundingBox.height());
235
236     return cachedEllipsePath(FloatRect(centerX - radiusX + boundingBox.x(), centerY - radiusY + boundingBox.y(), radiusX * 2, radiusY * 2));
237 }
238
239 bool BasicShapeEllipse::canBlend(const BasicShape& other) const
240 {
241     if (type() != other.type())
242         return false;
243
244     auto& otherEllipse = downcast<BasicShapeEllipse>(other);
245     return radiusX().canBlend(otherEllipse.radiusX()) && radiusY().canBlend(otherEllipse.radiusY());
246 }
247
248 Ref<BasicShape> BasicShapeEllipse::blend(const BasicShape& other, double progress) const
249 {
250     ASSERT(type() == other.type());
251     auto& otherEllipse = downcast<BasicShapeEllipse>(other);
252     auto result = BasicShapeEllipse::create();
253
254     if (m_radiusX.type() != BasicShapeRadius::Value || otherEllipse.radiusX().type() != BasicShapeRadius::Value
255         || m_radiusY.type() != BasicShapeRadius::Value || otherEllipse.radiusY().type() != BasicShapeRadius::Value) {
256         result->setCenterX(otherEllipse.centerX());
257         result->setCenterY(otherEllipse.centerY());
258         result->setRadiusX(otherEllipse.radiusX());
259         result->setRadiusY(otherEllipse.radiusY());
260         return result;
261     }
262
263     result->setCenterX(m_centerX.blend(otherEllipse.centerX(), progress));
264     result->setCenterY(m_centerY.blend(otherEllipse.centerY(), progress));
265     result->setRadiusX(m_radiusX.blend(otherEllipse.radiusX(), progress));
266     result->setRadiusY(m_radiusY.blend(otherEllipse.radiusY(), progress));
267     return result;
268 }
269
270 bool BasicShapePolygon::operator==(const BasicShape& other) const
271 {
272     if (type() != other.type())
273         return false;
274
275     auto& otherPolygon = downcast<BasicShapePolygon>(other);
276     return m_windRule == otherPolygon.m_windRule
277         && m_values == otherPolygon.m_values;
278 }
279
280 const Path& BasicShapePolygon::path(const FloatRect& boundingBox)
281 {
282     ASSERT(!(m_values.size() % 2));
283     size_t length = m_values.size();
284
285     Vector<FloatPoint> points(length / 2);
286     for (size_t i = 0; i < points.size(); ++i) {
287         points[i].setX(floatValueForLength(m_values.at(i * 2), boundingBox.width()) + boundingBox.x());
288         points[i].setY(floatValueForLength(m_values.at(i * 2 + 1), boundingBox.height()) + boundingBox.y());
289     }
290
291     return cachedPolygonPath(points);
292 }
293
294 bool BasicShapePolygon::canBlend(const BasicShape& other) const
295 {
296     if (type() != other.type())
297         return false;
298
299     auto& otherPolygon = downcast<BasicShapePolygon>(other);
300     return values().size() == otherPolygon.values().size() && windRule() == otherPolygon.windRule();
301 }
302
303 Ref<BasicShape> BasicShapePolygon::blend(const BasicShape& other, double progress) const
304 {
305     ASSERT(type() == other.type());
306
307     auto& otherPolygon = downcast<BasicShapePolygon>(other);
308     ASSERT(m_values.size() == otherPolygon.values().size());
309     ASSERT(!(m_values.size() % 2));
310
311     size_t length = m_values.size();
312     auto result = BasicShapePolygon::create();
313     if (!length)
314         return result;
315
316     result->setWindRule(otherPolygon.windRule());
317
318     for (size_t i = 0; i < length; i = i + 2) {
319         result->appendPoint(
320             WebCore::blend(otherPolygon.values().at(i), m_values.at(i), progress),
321             WebCore::blend(otherPolygon.values().at(i + 1), m_values.at(i + 1), progress));
322     }
323
324     return result;
325 }
326
327 BasicShapePath::BasicShapePath(std::unique_ptr<SVGPathByteStream>&& byteStream)
328     : m_byteStream(WTFMove(byteStream))
329 {
330 }
331
332 const Path& BasicShapePath::path(const FloatRect& boundingBox)
333 {
334     return cachedTranslatedByteStreamPath(*m_byteStream, boundingBox.location());
335 }
336
337 bool BasicShapePath::operator==(const BasicShape& other) const
338 {
339     if (type() != other.type())
340         return false;
341
342     auto& otherPath = downcast<BasicShapePath>(other);
343     return m_windRule == otherPath.m_windRule && *m_byteStream == *otherPath.m_byteStream;
344 }
345
346 bool BasicShapePath::canBlend(const BasicShape& other) const
347 {
348     if (type() != other.type())
349         return false;
350
351     auto& otherPath = downcast<BasicShapePath>(other);
352     return windRule() == otherPath.windRule() && canBlendSVGPathByteStreams(*m_byteStream, *otherPath.pathData());
353 }
354
355 Ref<BasicShape> BasicShapePath::blend(const BasicShape& from, double progress) const
356 {
357     ASSERT(type() == from.type());
358
359     auto& fromPath = downcast<BasicShapePath>(from);
360
361     auto resultingPathBytes = std::make_unique<SVGPathByteStream>();
362     buildAnimatedSVGPathByteStream(*fromPath.m_byteStream, *m_byteStream, *resultingPathBytes, progress);
363
364     auto result = BasicShapePath::create(WTFMove(resultingPathBytes));
365     result->setWindRule(windRule());
366     return result;
367 }
368
369 bool BasicShapeInset::operator==(const BasicShape& other) const
370 {
371     if (type() != other.type())
372         return false;
373
374     auto& otherInset = downcast<BasicShapeInset>(other);
375     return m_right == otherInset.m_right
376         && m_top == otherInset.m_top
377         && m_bottom == otherInset.m_bottom
378         && m_left == otherInset.m_left
379         && m_topLeftRadius == otherInset.m_topLeftRadius
380         && m_topRightRadius == otherInset.m_topRightRadius
381         && m_bottomRightRadius == otherInset.m_bottomRightRadius
382         && m_bottomLeftRadius == otherInset.m_bottomLeftRadius;
383 }
384
385 static FloatSize floatSizeForLengthSize(const LengthSize& lengthSize, const FloatRect& boundingBox)
386 {
387     return { floatValueForLength(lengthSize.width, boundingBox.width()),
388         floatValueForLength(lengthSize.height, boundingBox.height()) };
389 }
390
391 const Path& BasicShapeInset::path(const FloatRect& boundingBox)
392 {
393     float left = floatValueForLength(m_left, boundingBox.width());
394     float top = floatValueForLength(m_top, boundingBox.height());
395     auto rect = FloatRect(left + boundingBox.x(), top + boundingBox.y(),
396         std::max<float>(boundingBox.width() - left - floatValueForLength(m_right, boundingBox.width()), 0),
397         std::max<float>(boundingBox.height() - top - floatValueForLength(m_bottom, boundingBox.height()), 0));
398     auto radii = FloatRoundedRect::Radii(floatSizeForLengthSize(m_topLeftRadius, boundingBox),
399         floatSizeForLengthSize(m_topRightRadius, boundingBox),
400         floatSizeForLengthSize(m_bottomLeftRadius, boundingBox),
401         floatSizeForLengthSize(m_bottomRightRadius, boundingBox));
402     radii.scale(calcBorderRadiiConstraintScaleFor(rect, radii));
403
404     return cachedRoundedRectPath(FloatRoundedRect(rect, radii));
405 }
406
407 bool BasicShapeInset::canBlend(const BasicShape& other) const
408 {
409     return type() == other.type();
410 }
411
412 Ref<BasicShape> BasicShapeInset::blend(const BasicShape& from, double progress) const
413 {
414     ASSERT(type() == from.type());
415
416     auto& fromInset = downcast<BasicShapeInset>(from);
417     auto result =  BasicShapeInset::create();
418     result->setTop(WebCore::blend(fromInset.top(), top(), progress));
419     result->setRight(WebCore::blend(fromInset.right(), right(), progress));
420     result->setBottom(WebCore::blend(fromInset.bottom(), bottom(), progress));
421     result->setLeft(WebCore::blend(fromInset.left(), left(), progress));
422
423     result->setTopLeftRadius(WebCore::blend(fromInset.topLeftRadius(), topLeftRadius(), progress));
424     result->setTopRightRadius(WebCore::blend(fromInset.topRightRadius(), topRightRadius(), progress));
425     result->setBottomRightRadius(WebCore::blend(fromInset.bottomRightRadius(), bottomRightRadius(), progress));
426     result->setBottomLeftRadius(WebCore::blend(fromInset.bottomLeftRadius(), bottomLeftRadius(), progress));
427
428     return result;
429 }
430 }