[css shapes] layout for new ellipse syntax
[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 "FloatRect.h"
36 #include "LengthFunctions.h"
37 #include "Path.h"
38
39 namespace WebCore {
40
41 bool BasicShape::canBlend(const BasicShape* other) const
42 {
43     // FIXME: Support animations between different shapes in the future.
44     if (type() != other->type())
45         return false;
46
47     // Just polygons with same number of vertices can be animated.
48     if (type() == BasicShape::BasicShapePolygonType
49         && static_cast<const BasicShapePolygon*>(this)->values().size() != static_cast<const BasicShapePolygon*>(other)->values().size())
50         return false;
51
52     // Circles with keywords for radii or center coordinates cannot be animated.
53     if (type() == BasicShape::BasicShapeCircleType) {
54         const BasicShapeCircle* thisCircle = static_cast<const BasicShapeCircle*>(this);
55         const BasicShapeCircle* otherCircle = static_cast<const BasicShapeCircle*>(other);
56         if (!thisCircle->radius().canBlend(otherCircle->radius())
57             || !thisCircle->centerX().canBlend(otherCircle->centerX())
58             || !thisCircle->centerY().canBlend(otherCircle->centerY()))
59             return false;
60     }
61
62     // Ellipses with keywords for radii or center coordinates cannot be animated.
63     if (type() != BasicShape::BasicShapeEllipseType)
64         return true;
65
66     const BasicShapeEllipse* thisEllipse = static_cast<const BasicShapeEllipse*>(this);
67     const BasicShapeEllipse* otherEllipse = static_cast<const BasicShapeEllipse*>(other);
68     if (!thisEllipse->radiusX().canBlend(otherEllipse->radiusX())
69         || !thisEllipse->radiusY().canBlend(otherEllipse->radiusY())
70         || !thisEllipse->centerX().canBlend(otherEllipse->centerX())
71         || !thisEllipse->centerY().canBlend(otherEllipse->centerY()))
72         return false;
73 }
74
75 void BasicShapeRectangle::path(Path& path, const FloatRect& boundingBox)
76 {
77     ASSERT(path.isEmpty());
78     path.addRoundedRect(
79         FloatRect(
80             floatValueForLength(m_x, boundingBox.width()) + boundingBox.x(),
81             floatValueForLength(m_y, boundingBox.height()) + boundingBox.y(),
82             floatValueForLength(m_width, boundingBox.width()),
83             floatValueForLength(m_height, boundingBox.height())
84         ),
85         FloatSize(
86             floatValueForLength(m_cornerRadiusX, boundingBox.width()),
87             floatValueForLength(m_cornerRadiusY, boundingBox.height())
88         )
89     );
90 }
91
92 PassRefPtr<BasicShape> BasicShapeRectangle::blend(const BasicShape* other, double progress) const
93 {
94     ASSERT(type() == other->type());
95
96     const BasicShapeRectangle* o = static_cast<const BasicShapeRectangle*>(other);
97     RefPtr<BasicShapeRectangle> result =  BasicShapeRectangle::create();
98     result->setX(m_x.blend(o->x(), progress));
99     result->setY(m_y.blend(o->y(), progress));
100     result->setWidth(m_width.blend(o->width(), progress));
101     result->setHeight(m_height.blend(o->height(), progress));
102     result->setCornerRadiusX(m_cornerRadiusX.blend(o->cornerRadiusX(), progress));
103     result->setCornerRadiusY(m_cornerRadiusY.blend(o->cornerRadiusY(), progress));
104     return result.release();
105 }
106
107 void DeprecatedBasicShapeCircle::path(Path& path, const FloatRect& boundingBox)
108 {
109     ASSERT(path.isEmpty());
110     float diagonal = sqrtf((boundingBox.width() * boundingBox.width() + boundingBox.height() * boundingBox.height()) / 2);
111     float centerX = floatValueForLength(m_centerX, boundingBox.width());
112     float centerY = floatValueForLength(m_centerY, boundingBox.height());
113     float radius = floatValueForLength(m_radius, diagonal);
114     path.addEllipse(FloatRect(
115         centerX - radius + boundingBox.x(),
116         centerY - radius + boundingBox.y(),
117         radius * 2,
118         radius * 2
119     ));
120 }
121
122 PassRefPtr<BasicShape> DeprecatedBasicShapeCircle::blend(const BasicShape* other, double progress) const
123 {
124     ASSERT(type() == other->type());
125
126     const DeprecatedBasicShapeCircle* o = static_cast<const DeprecatedBasicShapeCircle*>(other);
127     RefPtr<DeprecatedBasicShapeCircle> result =  DeprecatedBasicShapeCircle::create();
128     result->setCenterX(m_centerX.blend(o->centerX(), progress));
129     result->setCenterY(m_centerY.blend(o->centerY(), progress));
130     result->setRadius(m_radius.blend(o->radius(), progress));
131     return result.release();
132 }
133
134 float BasicShapeCircle::floatValueForRadiusInBox(float boxWidth, float boxHeight) const
135 {
136     if (m_radius.type() == BasicShapeRadius::Value)
137         return floatValueForLength(m_radius.value(), sqrtf((boxWidth * boxWidth + boxHeight * boxHeight) / 2));
138
139     float centerX = floatValueForCenterCoordinate(m_centerX, boxWidth);
140     float centerY = floatValueForCenterCoordinate(m_centerY, boxHeight);
141
142     if (m_radius.type() == BasicShapeRadius::ClosestSide)
143         return std::min(std::min(centerX, boxWidth - centerX), std::min(centerY, boxHeight - centerY));
144
145     // If radius.type() == BasicShapeRadius::FarthestSide.
146     return std::max(std::max(centerX, boxWidth - centerX), std::max(centerY, boxHeight - centerY));
147 }
148
149 void BasicShapeCircle::path(Path& path, const FloatRect& boundingBox)
150 {
151     ASSERT(path.isEmpty());
152
153     float centerX = floatValueForCenterCoordinate(m_centerX, boundingBox.width());
154     float centerY = floatValueForCenterCoordinate(m_centerY, boundingBox.height());
155     float radius = floatValueForRadiusInBox(boundingBox.width(), boundingBox.height());
156     path.addEllipse(FloatRect(
157         centerX - radius + boundingBox.x(),
158         centerY - radius + boundingBox.y(),
159         radius * 2,
160         radius * 2
161     ));
162 }
163
164 PassRefPtr<BasicShape> BasicShapeCircle::blend(const BasicShape* other, double progress) const
165 {
166     ASSERT(type() == other->type());
167     const BasicShapeCircle* o = static_cast<const BasicShapeCircle*>(other);
168     RefPtr<BasicShapeCircle> result =  BasicShapeCircle::create();
169
170     result->setCenterX(m_centerX.blend(o->centerX(), progress));
171     result->setCenterY(m_centerY.blend(o->centerY(), progress));
172     result->setRadius(m_radius.blend(o->radius(), progress));
173     return result.release();
174 }
175
176 void DeprecatedBasicShapeEllipse::path(Path& path, const FloatRect& boundingBox)
177 {
178     ASSERT(path.isEmpty());
179     float centerX = floatValueForLength(m_centerX, boundingBox.width());
180     float centerY = floatValueForLength(m_centerY, boundingBox.height());
181     float radiusX = floatValueForLength(m_radiusX, boundingBox.width());
182     float radiusY = floatValueForLength(m_radiusY, boundingBox.height());
183     path.addEllipse(FloatRect(
184         centerX - radiusX + boundingBox.x(),
185         centerY - radiusY + boundingBox.y(),
186         radiusX * 2,
187         radiusY * 2
188     ));
189 }
190
191 PassRefPtr<BasicShape> DeprecatedBasicShapeEllipse::blend(const BasicShape* other, double progress) const
192 {
193     ASSERT(type() == other->type());
194
195     const DeprecatedBasicShapeEllipse* o = static_cast<const DeprecatedBasicShapeEllipse*>(other);
196     RefPtr<DeprecatedBasicShapeEllipse> result = DeprecatedBasicShapeEllipse::create();
197     result->setCenterX(m_centerX.blend(o->centerX(), progress));
198     result->setCenterY(m_centerY.blend(o->centerY(), progress));
199     result->setRadiusX(m_radiusX.blend(o->radiusX(), progress));
200     result->setRadiusY(m_radiusY.blend(o->radiusY(), progress));
201     return result.release();
202 }
203
204 float BasicShapeEllipse::floatValueForRadiusInBox(const BasicShapeRadius& radius, float center, float boxWidthOrHeight) const
205 {
206     if (radius.type() == BasicShapeRadius::Value)
207         return floatValueForLength(radius.value(), boxWidthOrHeight);
208
209     if (radius.type() == BasicShapeRadius::ClosestSide)
210         return std::min(center, boxWidthOrHeight - center);
211
212     ASSERT(radius.type() == BasicShapeRadius::FarthestSide);
213     return std::max(center, boxWidthOrHeight - center);
214 }
215
216 void BasicShapeEllipse::path(Path& path, const FloatRect& boundingBox)
217 {
218     ASSERT(path.isEmpty());
219
220     float centerX = floatValueForCenterCoordinate(m_centerX, boundingBox.width());
221     float centerY = floatValueForCenterCoordinate(m_centerY, boundingBox.height());
222     float radiusX = floatValueForRadiusInBox(m_radiusX, centerX, boundingBox.width());
223     float radiusY = floatValueForRadiusInBox(m_radiusY, centerY, boundingBox.height());
224     path.addEllipse(FloatRect(
225         centerX - radiusX + boundingBox.x(),
226         centerY - radiusY + boundingBox.y(),
227         radiusX * 2,
228         radiusY * 2));
229 }
230
231 PassRefPtr<BasicShape> BasicShapeEllipse::blend(const BasicShape* other, double progress) const
232 {
233     ASSERT(type() == other->type());
234     const BasicShapeEllipse* o = static_cast<const BasicShapeEllipse*>(other);
235     RefPtr<BasicShapeEllipse> result = BasicShapeEllipse::create();
236
237     if (m_radiusX.type() != BasicShapeRadius::Value || o->radiusX().type() != BasicShapeRadius::Value
238         || m_radiusY.type() != BasicShapeRadius::Value || o->radiusY().type() != BasicShapeRadius::Value) {
239         result->setCenterX(o->centerX());
240         result->setCenterY(o->centerY());
241         result->setRadiusX(o->radiusX());
242         result->setRadiusY(o->radiusY());
243         return result;
244     }
245
246     result->setCenterX(m_centerX.blend(o->centerX(), progress));
247     result->setCenterY(m_centerY.blend(o->centerY(), progress));
248     result->setRadiusX(m_radiusX.blend(o->radiusX(), progress));
249     result->setRadiusY(m_radiusY.blend(o->radiusY(), progress));
250     return result.release();
251 }
252
253 void BasicShapePolygon::path(Path& path, const FloatRect& boundingBox)
254 {
255     ASSERT(path.isEmpty());
256     ASSERT(!(m_values.size() % 2));
257     size_t length = m_values.size();
258     
259     if (!length)
260         return;
261
262     path.moveTo(FloatPoint(floatValueForLength(m_values.at(0), boundingBox.width()) + boundingBox.x(),
263         floatValueForLength(m_values.at(1), boundingBox.height()) + boundingBox.y()));
264     for (size_t i = 2; i < length; i = i + 2) {
265         path.addLineTo(FloatPoint(floatValueForLength(m_values.at(i), boundingBox.width()) + boundingBox.x(),
266             floatValueForLength(m_values.at(i + 1), boundingBox.height()) + boundingBox.y()));
267     }
268     path.closeSubpath();
269 }
270
271 PassRefPtr<BasicShape> BasicShapePolygon::blend(const BasicShape* other, double progress) const
272 {
273     ASSERT(type() == other->type());
274
275     const BasicShapePolygon* o = static_cast<const BasicShapePolygon*>(other);
276     ASSERT(m_values.size() == o->values().size());
277     ASSERT(!(m_values.size() % 2));
278
279     size_t length = m_values.size();
280     RefPtr<BasicShapePolygon> result = BasicShapePolygon::create();
281     if (!length)
282         return result.release();
283
284     result->setWindRule(o->windRule());
285
286     for (size_t i = 0; i < length; i = i + 2) {
287         result->appendPoint(m_values.at(i).blend(o->values().at(i), progress),
288             m_values.at(i + 1).blend(o->values().at(i + 1), progress));
289     }
290
291     return result.release();
292 }
293
294 void BasicShapeInsetRectangle::path(Path& path, const FloatRect& boundingBox)
295 {
296     ASSERT(path.isEmpty());
297     float left = floatValueForLength(m_left, boundingBox.width());
298     float top = floatValueForLength(m_top, boundingBox.height());
299     path.addRoundedRect(
300         FloatRect(
301             left + boundingBox.x(),
302             top + boundingBox.y(),
303             std::max<float>(boundingBox.width() - left - floatValueForLength(m_right, boundingBox.width()), 0),
304             std::max<float>(boundingBox.height() - top - floatValueForLength(m_bottom, boundingBox.height()), 0)
305         ),
306         FloatSize(
307             floatValueForLength(m_cornerRadiusX, boundingBox.width()),
308             floatValueForLength(m_cornerRadiusY, boundingBox.height())
309         )
310     );
311 }
312
313 PassRefPtr<BasicShape> BasicShapeInsetRectangle::blend(const BasicShape* other, double progress) const
314 {
315     ASSERT(type() == other->type());
316
317     const BasicShapeInsetRectangle* o = static_cast<const BasicShapeInsetRectangle*>(other);
318     RefPtr<BasicShapeInsetRectangle> result =  BasicShapeInsetRectangle::create();
319     result->setTop(m_top.blend(o->top(), progress));
320     result->setRight(m_right.blend(o->right(), progress));
321     result->setBottom(m_bottom.blend(o->bottom(), progress));
322     result->setLeft(m_left.blend(o->left(), progress));
323     result->setCornerRadiusX(m_cornerRadiusX.blend(o->cornerRadiusX(), progress));
324     result->setCornerRadiusY(m_cornerRadiusY.blend(o->cornerRadiusY(), progress));
325     return result.release();
326 }
327
328 void BasicShapeInset::path(Path& path, const FloatRect& boundingBox)
329 {
330     ASSERT(path.isEmpty());
331     float left = floatValueForLength(m_left, boundingBox.width());
332     float top = floatValueForLength(m_top, boundingBox.height());
333     path.addRoundedRect(
334         FloatRect(
335             left + boundingBox.x(),
336             top + boundingBox.y(),
337             std::max<float>(boundingBox.width() - left - floatValueForLength(m_right, boundingBox.width()), 0),
338             std::max<float>(boundingBox.height() - top - floatValueForLength(m_bottom, boundingBox.height()), 0)
339         ),
340         FloatSize(
341             floatValueForLength(m_topLeftRadius.width(), boundingBox.width()),
342             floatValueForLength(m_topLeftRadius.height(), boundingBox.height())
343         ),
344         FloatSize(
345             floatValueForLength(m_topRightRadius.width(), boundingBox.width()),
346             floatValueForLength(m_topRightRadius.height(), boundingBox.height())
347         ),
348         FloatSize(
349             floatValueForLength(m_bottomRightRadius.width(), boundingBox.width()),
350             floatValueForLength(m_bottomRightRadius.height(), boundingBox.height())
351         ),
352         FloatSize(
353             floatValueForLength(m_bottomLeftRadius.width(), boundingBox.width()),
354             floatValueForLength(m_bottomLeftRadius.height(), boundingBox.height())
355         )
356     );
357 }
358
359 PassRefPtr<BasicShape> BasicShapeInset::blend(const BasicShape* other, double progress) const
360 {
361     ASSERT(type() == other->type());
362
363     const BasicShapeInset* o = static_cast<const BasicShapeInset*>(other);
364     RefPtr<BasicShapeInset> result =  BasicShapeInset::create();
365     result->setTop(m_top.blend(o->top(), progress));
366     result->setRight(m_right.blend(o->right(), progress));
367     result->setBottom(m_bottom.blend(o->bottom(), progress));
368     result->setLeft(m_left.blend(o->left(), progress));
369
370     result->setTopLeftRadius(m_topLeftRadius.blend(o->topLeftRadius(), progress));
371     result->setTopRightRadius(m_topRightRadius.blend(o->topRightRadius(), progress));
372     result->setBottomRightRadius(m_bottomRightRadius.blend(o->bottomRightRadius(), progress));
373     result->setBottomLeftRadius(m_bottomLeftRadius.blend(o->bottomLeftRadius(), progress));
374
375     return result.release();
376 }
377 }