2 * Copyright (C) 2012 Adobe Systems Incorporated. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above
9 * copyright notice, this list of conditions and the following
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.
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
31 #include "core/style/BasicShapes.h"
33 #include "core/css/BasicShapeFunctions.h"
34 #include "core/style/ComputedStyle.h"
35 #include "platform/CalculationValue.h"
36 #include "platform/LengthFunctions.h"
37 #include "platform/geometry/FloatRect.h"
38 #include "platform/graphics/Path.h"
42 bool BasicShape::canBlend(const BasicShape
* other
) const
44 // FIXME: Support animations between different shapes in the future.
45 if (!other
|| !isSameType(*other
))
48 // Just polygons with same number of vertices can be animated.
49 if (type() == BasicShape::BasicShapePolygonType
50 && (toBasicShapePolygon(this)->values().size() != toBasicShapePolygon(other
)->values().size()
51 || toBasicShapePolygon(this)->windRule() != toBasicShapePolygon(other
)->windRule()))
54 // Circles with keywords for radii or center coordinates cannot be animated.
55 if (type() == BasicShape::BasicShapeCircleType
) {
56 if (!toBasicShapeCircle(this)->radius().canBlend(toBasicShapeCircle(other
)->radius()))
60 // Ellipses with keywords for radii or center coordinates cannot be animated.
61 if (type() != BasicShape::BasicShapeEllipseType
)
64 return (toBasicShapeEllipse(this)->radiusX().canBlend(toBasicShapeEllipse(other
)->radiusX())
65 && toBasicShapeEllipse(this)->radiusY().canBlend(toBasicShapeEllipse(other
)->radiusY()));
68 bool BasicShapeCircle::operator==(const BasicShape
& o
) const
72 const BasicShapeCircle
& other
= toBasicShapeCircle(o
);
73 return m_centerX
== other
.m_centerX
&& m_centerY
== other
.m_centerY
&& m_radius
== other
.m_radius
;
76 float BasicShapeCircle::floatValueForRadiusInBox(FloatSize boxSize
) const
78 if (m_radius
.type() == BasicShapeRadius::Value
)
79 return floatValueForLength(m_radius
.value(), hypotf(boxSize
.width(), boxSize
.height()) / sqrtf(2));
81 FloatPoint center
= floatPointForCenterCoordinate(m_centerX
, m_centerY
, boxSize
);
83 float widthDelta
= std::abs(boxSize
.width() - center
.x());
84 float heightDelta
= std::abs(boxSize
.height() - center
.y());
85 if (m_radius
.type() == BasicShapeRadius::ClosestSide
)
86 return std::min(std::min(std::abs(center
.x()), widthDelta
), std::min(std::abs(center
.y()), heightDelta
));
88 // If radius.type() == BasicShapeRadius::FarthestSide.
89 return std::max(std::max(center
.x(), widthDelta
), std::max(center
.y(), heightDelta
));
92 void BasicShapeCircle::path(Path
& path
, const FloatRect
& boundingBox
)
94 ASSERT(path
.isEmpty());
95 FloatPoint center
= floatPointForCenterCoordinate(m_centerX
, m_centerY
, boundingBox
.size());
96 float radius
= floatValueForRadiusInBox(boundingBox
.size());
97 path
.addEllipse(FloatRect(
98 center
.x() - radius
+ boundingBox
.x(),
99 center
.y() - radius
+ boundingBox
.y(),
105 PassRefPtr
<BasicShape
> BasicShapeCircle::blend(const BasicShape
* other
, double progress
) const
107 ASSERT(type() == other
->type());
108 const BasicShapeCircle
* o
= toBasicShapeCircle(other
);
109 RefPtr
<BasicShapeCircle
> result
= BasicShapeCircle::create();
111 result
->setCenterX(m_centerX
.blend(o
->centerX(), progress
));
112 result
->setCenterY(m_centerY
.blend(o
->centerY(), progress
));
113 result
->setRadius(m_radius
.blend(o
->radius(), progress
));
114 return result
.release();
117 bool BasicShapeEllipse::operator==(const BasicShape
& o
) const
121 const BasicShapeEllipse
& other
= toBasicShapeEllipse(o
);
122 return m_centerX
== other
.m_centerX
&& m_centerY
== other
.m_centerY
&& m_radiusX
== other
.m_radiusX
&& m_radiusY
== other
.m_radiusY
;
125 float BasicShapeEllipse::floatValueForRadiusInBox(const BasicShapeRadius
& radius
, float center
, float boxWidthOrHeight
) const
127 if (radius
.type() == BasicShapeRadius::Value
)
128 return floatValueForLength(radius
.value(), boxWidthOrHeight
);
130 float widthOrHeightDelta
= std::abs(boxWidthOrHeight
- center
);
131 if (radius
.type() == BasicShapeRadius::ClosestSide
)
132 return std::min(std::abs(center
), widthOrHeightDelta
);
134 ASSERT(radius
.type() == BasicShapeRadius::FarthestSide
);
135 return std::max(center
, widthOrHeightDelta
);
138 void BasicShapeEllipse::path(Path
& path
, const FloatRect
& boundingBox
)
140 ASSERT(path
.isEmpty());
141 FloatPoint center
= floatPointForCenterCoordinate(m_centerX
, m_centerY
, boundingBox
.size());
142 float radiusX
= floatValueForRadiusInBox(m_radiusX
, center
.x(), boundingBox
.width());
143 float radiusY
= floatValueForRadiusInBox(m_radiusY
, center
.y(), boundingBox
.height());
144 path
.addEllipse(FloatRect(
145 center
.x() - radiusX
+ boundingBox
.x(),
146 center
.y() - radiusY
+ boundingBox
.y(),
152 PassRefPtr
<BasicShape
> BasicShapeEllipse::blend(const BasicShape
* other
, double progress
) const
154 ASSERT(type() == other
->type());
155 const BasicShapeEllipse
* o
= toBasicShapeEllipse(other
);
156 RefPtr
<BasicShapeEllipse
> result
= BasicShapeEllipse::create();
158 if (m_radiusX
.type() != BasicShapeRadius::Value
|| o
->radiusX().type() != BasicShapeRadius::Value
159 || m_radiusY
.type() != BasicShapeRadius::Value
|| o
->radiusY().type() != BasicShapeRadius::Value
) {
160 result
->setCenterX(o
->centerX());
161 result
->setCenterY(o
->centerY());
162 result
->setRadiusX(o
->radiusX());
163 result
->setRadiusY(o
->radiusY());
167 result
->setCenterX(m_centerX
.blend(o
->centerX(), progress
));
168 result
->setCenterY(m_centerY
.blend(o
->centerY(), progress
));
169 result
->setRadiusX(m_radiusX
.blend(o
->radiusX(), progress
));
170 result
->setRadiusY(m_radiusY
.blend(o
->radiusY(), progress
));
171 return result
.release();
174 void BasicShapePolygon::path(Path
& path
, const FloatRect
& boundingBox
)
176 ASSERT(path
.isEmpty());
177 ASSERT(!(m_values
.size() % 2));
178 size_t length
= m_values
.size();
183 path
.moveTo(FloatPoint(floatValueForLength(m_values
.at(0), boundingBox
.width()) + boundingBox
.x(),
184 floatValueForLength(m_values
.at(1), boundingBox
.height()) + boundingBox
.y()));
185 for (size_t i
= 2; i
< length
; i
= i
+ 2) {
186 path
.addLineTo(FloatPoint(floatValueForLength(m_values
.at(i
), boundingBox
.width()) + boundingBox
.x(),
187 floatValueForLength(m_values
.at(i
+ 1), boundingBox
.height()) + boundingBox
.y()));
192 PassRefPtr
<BasicShape
> BasicShapePolygon::blend(const BasicShape
* other
, double progress
) const
194 ASSERT(other
&& isSameType(*other
));
196 const BasicShapePolygon
* o
= toBasicShapePolygon(other
);
197 ASSERT(m_values
.size() == o
->values().size());
198 ASSERT(!(m_values
.size() % 2));
200 size_t length
= m_values
.size();
201 RefPtr
<BasicShapePolygon
> result
= BasicShapePolygon::create();
203 return result
.release();
205 result
->setWindRule(o
->windRule());
207 for (size_t i
= 0; i
< length
; i
= i
+ 2) {
208 result
->appendPoint(m_values
.at(i
).blend(o
->values().at(i
), progress
, ValueRangeAll
),
209 m_values
.at(i
+ 1).blend(o
->values().at(i
+ 1), progress
, ValueRangeAll
));
212 return result
.release();
215 bool BasicShapePolygon::operator==(const BasicShape
& o
) const
219 const BasicShapePolygon
& other
= toBasicShapePolygon(o
);
220 return m_windRule
== other
.m_windRule
&& m_values
== other
.m_values
;
223 static FloatSize
floatSizeForLengthSize(const LengthSize
& lengthSize
, const FloatRect
& boundingBox
)
225 return FloatSize(floatValueForLength(lengthSize
.width(), boundingBox
.width()),
226 floatValueForLength(lengthSize
.height(), boundingBox
.height()));
229 void BasicShapeInset::path(Path
& path
, const FloatRect
& boundingBox
)
231 ASSERT(path
.isEmpty());
232 float left
= floatValueForLength(m_left
, boundingBox
.width());
233 float top
= floatValueForLength(m_top
, boundingBox
.height());
234 FloatRect
rect(left
+ boundingBox
.x(), top
+ boundingBox
.y(),
235 std::max
<float>(boundingBox
.width() - left
- floatValueForLength(m_right
, boundingBox
.width()), 0),
236 std::max
<float>(boundingBox
.height() - top
- floatValueForLength(m_bottom
, boundingBox
.height()), 0));
237 auto radii
= FloatRoundedRect::Radii(floatSizeForLengthSize(m_topLeftRadius
, boundingBox
),
238 floatSizeForLengthSize(m_topRightRadius
, boundingBox
),
239 floatSizeForLengthSize(m_bottomLeftRadius
, boundingBox
),
240 floatSizeForLengthSize(m_bottomRightRadius
, boundingBox
));
242 FloatRoundedRect
finalRect(rect
, radii
);
243 finalRect
.constrainRadii();
244 path
.addRoundedRect(finalRect
);
247 static inline LengthSize
blendLengthSize(const LengthSize
& to
, const LengthSize
& from
, double progress
)
249 return LengthSize(to
.width().blend(from
.width(), progress
, ValueRangeAll
),
250 to
.height().blend(from
.height(), progress
, ValueRangeAll
));
253 PassRefPtr
<BasicShape
> BasicShapeInset::blend(const BasicShape
* other
, double progress
) const
255 ASSERT(other
&& isSameType(*other
));
257 const BasicShapeInset
& otherInset
= toBasicShapeInset(*other
);
258 RefPtr
<BasicShapeInset
> result
= BasicShapeInset::create();
259 result
->setTop(m_top
.blend(otherInset
.top(), progress
, ValueRangeAll
));
260 result
->setRight(m_right
.blend(otherInset
.right(), progress
, ValueRangeAll
));
261 result
->setBottom(m_bottom
.blend(otherInset
.bottom(), progress
, ValueRangeAll
));
262 result
->setLeft(m_left
.blend(otherInset
.left(), progress
, ValueRangeAll
));
264 result
->setTopLeftRadius(blendLengthSize(m_topLeftRadius
, otherInset
.topLeftRadius(), progress
));
265 result
->setTopRightRadius(blendLengthSize(m_topRightRadius
, otherInset
.topRightRadius(), progress
));
266 result
->setBottomRightRadius(blendLengthSize(m_bottomRightRadius
, otherInset
.bottomRightRadius(), progress
));
267 result
->setBottomLeftRadius(blendLengthSize(m_bottomLeftRadius
, otherInset
.bottomLeftRadius(), progress
));
269 return result
.release();
272 bool BasicShapeInset::operator==(const BasicShape
& o
) const
276 const BasicShapeInset
& other
= toBasicShapeInset(o
);
277 return m_right
== other
.m_right
278 && m_top
== other
.m_top
279 && m_bottom
== other
.m_bottom
280 && m_left
== other
.m_left
281 && m_topLeftRadius
== other
.m_topLeftRadius
282 && m_topRightRadius
== other
.m_topRightRadius
283 && m_bottomRightRadius
== other
.m_bottomRightRadius
284 && m_bottomLeftRadius
== other
.m_bottomLeftRadius
;