Add WTF::move()
[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
42 namespace WebCore {
43
44 void BasicShapeCenterCoordinate::updateComputedLength()
45 {
46     if (m_direction == TopLeft) {
47         m_computedLength = m_length.isUndefined() ? Length(0, Fixed) : m_length;
48         return;
49     }
50     if (m_length.isUndefined()) {
51         m_computedLength = Length(100, Percent);
52         return;
53     }
54
55     auto lhs = std::make_unique<CalcExpressionLength>(Length(100, Percent));
56     auto rhs = std::make_unique<CalcExpressionLength>(m_length);
57     auto op = std::make_unique<CalcExpressionBinaryOperation>(WTF::move(lhs), WTF::move(rhs), CalcSubtract);
58     m_computedLength = Length(CalculationValue::create(WTF::move(op), CalculationRangeAll));
59 }
60
61 bool BasicShape::canBlend(const BasicShape* other) const
62 {
63     // FIXME: Support animations between different shapes in the future.
64     if (type() != other->type())
65         return false;
66
67     // Just polygons with same number of vertices can be animated.
68     if (type() == BasicShape::BasicShapePolygonType
69         && (static_cast<const BasicShapePolygon*>(this)->values().size() != static_cast<const BasicShapePolygon*>(other)->values().size()
70         || static_cast<const BasicShapePolygon*>(this)->windRule() != static_cast<const BasicShapePolygon*>(other)->windRule()))
71         return false;
72
73     // Circles with keywords for radii coordinates cannot be animated.
74     if (type() == BasicShape::BasicShapeCircleType) {
75         const BasicShapeCircle* thisCircle = static_cast<const BasicShapeCircle*>(this);
76         const BasicShapeCircle* otherCircle = static_cast<const BasicShapeCircle*>(other);
77         if (!thisCircle->radius().canBlend(otherCircle->radius()))
78             return false;
79     }
80
81     // Ellipses with keywords for radii coordinates cannot be animated.
82     if (type() != BasicShape::BasicShapeEllipseType)
83         return true;
84
85     const BasicShapeEllipse* thisEllipse = static_cast<const BasicShapeEllipse*>(this);
86     const BasicShapeEllipse* otherEllipse = static_cast<const BasicShapeEllipse*>(other);
87     return (thisEllipse->radiusX().canBlend(otherEllipse->radiusX())
88         && thisEllipse->radiusY().canBlend(otherEllipse->radiusY()));
89 }
90
91 float BasicShapeCircle::floatValueForRadiusInBox(float boxWidth, float boxHeight) const
92 {
93     if (m_radius.type() == BasicShapeRadius::Value)
94         return floatValueForLength(m_radius.value(), sqrtf((boxWidth * boxWidth + boxHeight * boxHeight) / 2));
95
96     float centerX = floatValueForCenterCoordinate(m_centerX, boxWidth);
97     float centerY = floatValueForCenterCoordinate(m_centerY, boxHeight);
98
99     if (m_radius.type() == BasicShapeRadius::ClosestSide)
100         return std::min(std::min(centerX, boxWidth - centerX), std::min(centerY, boxHeight - centerY));
101
102     // If radius.type() == BasicShapeRadius::FarthestSide.
103     return std::max(std::max(centerX, boxWidth - centerX), std::max(centerY, boxHeight - centerY));
104 }
105
106 void BasicShapeCircle::path(Path& path, const FloatRect& boundingBox)
107 {
108     ASSERT(path.isEmpty());
109
110     float centerX = floatValueForCenterCoordinate(m_centerX, boundingBox.width());
111     float centerY = floatValueForCenterCoordinate(m_centerY, boundingBox.height());
112     float radius = floatValueForRadiusInBox(boundingBox.width(), boundingBox.height());
113     path.addEllipse(FloatRect(
114         centerX - radius + boundingBox.x(),
115         centerY - radius + boundingBox.y(),
116         radius * 2,
117         radius * 2
118     ));
119 }
120
121 PassRefPtr<BasicShape> BasicShapeCircle::blend(const BasicShape* other, double progress) const
122 {
123     ASSERT(type() == other->type());
124     const BasicShapeCircle* o = static_cast<const BasicShapeCircle*>(other);
125     RefPtr<BasicShapeCircle> result =  BasicShapeCircle::create();
126
127     result->setCenterX(m_centerX.blend(o->centerX(), progress));
128     result->setCenterY(m_centerY.blend(o->centerY(), progress));
129     result->setRadius(m_radius.blend(o->radius(), progress));
130     return result.release();
131 }
132
133 float BasicShapeEllipse::floatValueForRadiusInBox(const BasicShapeRadius& radius, float center, float boxWidthOrHeight) const
134 {
135     if (radius.type() == BasicShapeRadius::Value)
136         return floatValueForLength(radius.value(), boxWidthOrHeight);
137
138     if (radius.type() == BasicShapeRadius::ClosestSide)
139         return std::min(center, boxWidthOrHeight - center);
140
141     ASSERT(radius.type() == BasicShapeRadius::FarthestSide);
142     return std::max(center, boxWidthOrHeight - center);
143 }
144
145 void BasicShapeEllipse::path(Path& path, const FloatRect& boundingBox)
146 {
147     ASSERT(path.isEmpty());
148
149     float centerX = floatValueForCenterCoordinate(m_centerX, boundingBox.width());
150     float centerY = floatValueForCenterCoordinate(m_centerY, boundingBox.height());
151     float radiusX = floatValueForRadiusInBox(m_radiusX, centerX, boundingBox.width());
152     float radiusY = floatValueForRadiusInBox(m_radiusY, centerY, boundingBox.height());
153     path.addEllipse(FloatRect(
154         centerX - radiusX + boundingBox.x(),
155         centerY - radiusY + boundingBox.y(),
156         radiusX * 2,
157         radiusY * 2));
158 }
159
160 PassRefPtr<BasicShape> BasicShapeEllipse::blend(const BasicShape* other, double progress) const
161 {
162     ASSERT(type() == other->type());
163     const BasicShapeEllipse* o = static_cast<const BasicShapeEllipse*>(other);
164     RefPtr<BasicShapeEllipse> result = BasicShapeEllipse::create();
165
166     if (m_radiusX.type() != BasicShapeRadius::Value || o->radiusX().type() != BasicShapeRadius::Value
167         || m_radiusY.type() != BasicShapeRadius::Value || o->radiusY().type() != BasicShapeRadius::Value) {
168         result->setCenterX(o->centerX());
169         result->setCenterY(o->centerY());
170         result->setRadiusX(o->radiusX());
171         result->setRadiusY(o->radiusY());
172         return result;
173     }
174
175     result->setCenterX(m_centerX.blend(o->centerX(), progress));
176     result->setCenterY(m_centerY.blend(o->centerY(), progress));
177     result->setRadiusX(m_radiusX.blend(o->radiusX(), progress));
178     result->setRadiusY(m_radiusY.blend(o->radiusY(), progress));
179     return result.release();
180 }
181
182 void BasicShapePolygon::path(Path& path, const FloatRect& boundingBox)
183 {
184     ASSERT(path.isEmpty());
185     ASSERT(!(m_values.size() % 2));
186     size_t length = m_values.size();
187     
188     if (!length)
189         return;
190
191     path.moveTo(FloatPoint(floatValueForLength(m_values.at(0), boundingBox.width()) + boundingBox.x(),
192         floatValueForLength(m_values.at(1), boundingBox.height()) + boundingBox.y()));
193     for (size_t i = 2; i < length; i = i + 2) {
194         path.addLineTo(FloatPoint(floatValueForLength(m_values.at(i), boundingBox.width()) + boundingBox.x(),
195             floatValueForLength(m_values.at(i + 1), boundingBox.height()) + boundingBox.y()));
196     }
197     path.closeSubpath();
198 }
199
200 PassRefPtr<BasicShape> BasicShapePolygon::blend(const BasicShape* other, double progress) const
201 {
202     ASSERT(type() == other->type());
203
204     const BasicShapePolygon* o = static_cast<const BasicShapePolygon*>(other);
205     ASSERT(m_values.size() == o->values().size());
206     ASSERT(!(m_values.size() % 2));
207
208     size_t length = m_values.size();
209     RefPtr<BasicShapePolygon> result = BasicShapePolygon::create();
210     if (!length)
211         return result.release();
212
213     result->setWindRule(o->windRule());
214
215     for (size_t i = 0; i < length; i = i + 2) {
216         result->appendPoint(m_values.at(i).blend(o->values().at(i), progress),
217             m_values.at(i + 1).blend(o->values().at(i + 1), progress));
218     }
219
220     return result.release();
221 }
222
223 static FloatSize floatSizeForLengthSize(const LengthSize& lengthSize, const FloatRect& boundingBox)
224 {
225     return FloatSize(floatValueForLength(lengthSize.width(), boundingBox.width()),
226         floatValueForLength(lengthSize.height(), boundingBox.height()));
227 }
228
229 void BasicShapeInset::path(Path& path, const FloatRect& boundingBox)
230 {
231     ASSERT(path.isEmpty());
232     float left = floatValueForLength(m_left, boundingBox.width());
233     float top = floatValueForLength(m_top, boundingBox.height());
234     FloatRoundedRect r = FloatRoundedRect(
235         FloatRect(
236             left + boundingBox.x(),
237             top + boundingBox.y(),
238             std::max<float>(boundingBox.width() - left - floatValueForLength(m_right, boundingBox.width()), 0),
239             std::max<float>(boundingBox.height() - top - floatValueForLength(m_bottom, boundingBox.height()), 0)
240         ),
241         floatSizeForLengthSize(m_topLeftRadius, boundingBox),
242         floatSizeForLengthSize(m_topRightRadius, boundingBox),
243         floatSizeForLengthSize(m_bottomLeftRadius, boundingBox),
244         floatSizeForLengthSize(m_bottomRightRadius, boundingBox)
245     );
246     path.addRoundedRect(r);
247 }
248
249 PassRefPtr<BasicShape> BasicShapeInset::blend(const BasicShape* other, double progress) const
250 {
251     ASSERT(type() == other->type());
252
253     const BasicShapeInset* o = static_cast<const BasicShapeInset*>(other);
254     RefPtr<BasicShapeInset> result =  BasicShapeInset::create();
255     result->setTop(m_top.blend(o->top(), progress));
256     result->setRight(m_right.blend(o->right(), progress));
257     result->setBottom(m_bottom.blend(o->bottom(), progress));
258     result->setLeft(m_left.blend(o->left(), progress));
259
260     result->setTopLeftRadius(m_topLeftRadius.blend(o->topLeftRadius(), progress));
261     result->setTopRightRadius(m_topRightRadius.blend(o->topRightRadius(), progress));
262     result->setBottomRightRadius(m_bottomRightRadius.blend(o->bottomRightRadius(), progress));
263     result->setBottomLeftRadius(m_bottomLeftRadius.blend(o->bottomLeftRadius(), progress));
264
265     return result.release();
266 }
267 }