• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #include "core/rendering/style/BasicShapes.h"
32 
33 #include "core/css/BasicShapeFunctions.h"
34 #include "platform/CalculationValue.h"
35 #include "platform/LengthFunctions.h"
36 #include "platform/geometry/FloatRect.h"
37 #include "platform/graphics/Path.h"
38 
39 namespace WebCore {
40 
updateComputedLength()41 void BasicShapeCenterCoordinate::updateComputedLength()
42 {
43     if (m_direction == TopLeft) {
44         m_computedLength = m_length.isUndefined() ? Length(0, Fixed) : m_length;
45         return;
46     }
47     if (m_length.isUndefined()) {
48         m_computedLength = Length(100, Percent);
49         return;
50     }
51 
52     m_computedLength = m_length.subtractFromOneHundredPercent();
53 }
54 
canBlend(const BasicShape * other) const55 bool BasicShape::canBlend(const BasicShape* other) const
56 {
57     // FIXME: Support animations between different shapes in the future.
58     if (!other || !isSameType(*other))
59         return false;
60 
61     // Just polygons with same number of vertices can be animated.
62     if (type() == BasicShape::BasicShapePolygonType
63         && (static_cast<const BasicShapePolygon*>(this)->values().size() != static_cast<const BasicShapePolygon*>(other)->values().size()
64         || static_cast<const BasicShapePolygon*>(this)->windRule() != static_cast<const BasicShapePolygon*>(other)->windRule()))
65         return false;
66 
67     // Circles with keywords for radii or center coordinates cannot be animated.
68     if (type() == BasicShape::BasicShapeCircleType) {
69         const BasicShapeCircle* thisCircle = static_cast<const BasicShapeCircle*>(this);
70         const BasicShapeCircle* otherCircle = static_cast<const BasicShapeCircle*>(other);
71         if (!thisCircle->radius().canBlend(otherCircle->radius()))
72             return false;
73     }
74 
75     // Ellipses with keywords for radii or center coordinates cannot be animated.
76     if (type() != BasicShape::BasicShapeEllipseType)
77         return true;
78 
79     const BasicShapeEllipse* thisEllipse = static_cast<const BasicShapeEllipse*>(this);
80     const BasicShapeEllipse* otherEllipse = static_cast<const BasicShapeEllipse*>(other);
81     return (thisEllipse->radiusX().canBlend(otherEllipse->radiusX())
82         && thisEllipse->radiusY().canBlend(otherEllipse->radiusY()));
83 }
84 
operator ==(const BasicShape & o) const85 bool BasicShapeCircle::operator==(const BasicShape& o) const
86 {
87     if (!isSameType(o))
88         return false;
89     const BasicShapeCircle& other = toBasicShapeCircle(o);
90     return m_centerX == other.m_centerX && m_centerY == other.m_centerY && m_radius == other.m_radius;
91 }
92 
floatValueForRadiusInBox(FloatSize boxSize) const93 float BasicShapeCircle::floatValueForRadiusInBox(FloatSize boxSize) const
94 {
95     if (m_radius.type() == BasicShapeRadius::Value)
96         return floatValueForLength(m_radius.value(), hypotf(boxSize.width(), boxSize.height()) / sqrtf(2));
97 
98     FloatPoint center = floatPointForCenterCoordinate(m_centerX, m_centerY, boxSize);
99 
100     if (m_radius.type() == BasicShapeRadius::ClosestSide)
101         return std::min(std::min(center.x(), boxSize.width() - center.x()), std::min(center.y(), boxSize.height() - center.y()));
102 
103     // If radius.type() == BasicShapeRadius::FarthestSide.
104     return std::max(std::max(center.x(), boxSize.width() - center.x()), std::max(center.y(), boxSize.height() - center.y()));
105 }
106 
path(Path & path,const FloatRect & boundingBox)107 void BasicShapeCircle::path(Path& path, const FloatRect& boundingBox)
108 {
109     ASSERT(path.isEmpty());
110     FloatPoint center = floatPointForCenterCoordinate(m_centerX, m_centerY, boundingBox.size());
111     float radius = floatValueForRadiusInBox(boundingBox.size());
112     path.addEllipse(FloatRect(
113         center.x() - radius + boundingBox.x(),
114         center.y() - radius + boundingBox.y(),
115         radius * 2,
116         radius * 2
117     ));
118 }
119 
blend(const BasicShape * other,double progress) const120 PassRefPtr<BasicShape> BasicShapeCircle::blend(const BasicShape* other, double progress) const
121 {
122     ASSERT(type() == other->type());
123     const BasicShapeCircle* o = static_cast<const BasicShapeCircle*>(other);
124     RefPtr<BasicShapeCircle> result =  BasicShapeCircle::create();
125 
126     result->setCenterX(m_centerX.blend(o->centerX(), progress));
127     result->setCenterY(m_centerY.blend(o->centerY(), progress));
128     result->setRadius(m_radius.blend(o->radius(), progress));
129     return result.release();
130 }
131 
operator ==(const BasicShape & o) const132 bool BasicShapeEllipse::operator==(const BasicShape& o) const
133 {
134     if (!isSameType(o))
135         return false;
136     const BasicShapeEllipse& other = toBasicShapeEllipse(o);
137     return m_centerX == other.m_centerX && m_centerY == other.m_centerY && m_radiusX == other.m_radiusX && m_radiusY == other.m_radiusY;
138 }
139 
floatValueForRadiusInBox(const BasicShapeRadius & radius,float center,float boxWidthOrHeight) const140 float BasicShapeEllipse::floatValueForRadiusInBox(const BasicShapeRadius& radius, float center, float boxWidthOrHeight) const
141 {
142     if (radius.type() == BasicShapeRadius::Value)
143         return floatValueForLength(radius.value(), boxWidthOrHeight);
144 
145     if (radius.type() == BasicShapeRadius::ClosestSide)
146         return std::min(center, boxWidthOrHeight - center);
147 
148     ASSERT(radius.type() == BasicShapeRadius::FarthestSide);
149     return std::max(center, boxWidthOrHeight - center);
150 }
151 
path(Path & path,const FloatRect & boundingBox)152 void BasicShapeEllipse::path(Path& path, const FloatRect& boundingBox)
153 {
154     ASSERT(path.isEmpty());
155     FloatPoint center = floatPointForCenterCoordinate(m_centerX, m_centerY, boundingBox.size());
156     float radiusX = floatValueForRadiusInBox(m_radiusX, center.x(), boundingBox.width());
157     float radiusY = floatValueForRadiusInBox(m_radiusY, center.y(), boundingBox.height());
158     path.addEllipse(FloatRect(
159         center.x() - radiusX + boundingBox.x(),
160         center.y() - radiusY + boundingBox.y(),
161         radiusX * 2,
162         radiusY * 2
163     ));
164 }
165 
blend(const BasicShape * other,double progress) const166 PassRefPtr<BasicShape> BasicShapeEllipse::blend(const BasicShape* other, double progress) const
167 {
168     ASSERT(type() == other->type());
169     const BasicShapeEllipse* o = static_cast<const BasicShapeEllipse*>(other);
170     RefPtr<BasicShapeEllipse> result =  BasicShapeEllipse::create();
171 
172     if (m_radiusX.type() != BasicShapeRadius::Value || o->radiusX().type() != BasicShapeRadius::Value
173         || m_radiusY.type() != BasicShapeRadius::Value || o->radiusY().type() != BasicShapeRadius::Value) {
174         result->setCenterX(o->centerX());
175         result->setCenterY(o->centerY());
176         result->setRadiusX(o->radiusX());
177         result->setRadiusY(o->radiusY());
178         return result;
179     }
180 
181     result->setCenterX(m_centerX.blend(o->centerX(), progress));
182     result->setCenterY(m_centerY.blend(o->centerY(), progress));
183     result->setRadiusX(m_radiusX.blend(o->radiusX(), progress));
184     result->setRadiusY(m_radiusY.blend(o->radiusY(), progress));
185     return result.release();
186 }
187 
path(Path & path,const FloatRect & boundingBox)188 void BasicShapePolygon::path(Path& path, const FloatRect& boundingBox)
189 {
190     ASSERT(path.isEmpty());
191     ASSERT(!(m_values.size() % 2));
192     size_t length = m_values.size();
193 
194     if (!length)
195         return;
196 
197     path.moveTo(FloatPoint(floatValueForLength(m_values.at(0), boundingBox.width()) + boundingBox.x(),
198         floatValueForLength(m_values.at(1), boundingBox.height()) + boundingBox.y()));
199     for (size_t i = 2; i < length; i = i + 2) {
200         path.addLineTo(FloatPoint(floatValueForLength(m_values.at(i), boundingBox.width()) + boundingBox.x(),
201             floatValueForLength(m_values.at(i + 1), boundingBox.height()) + boundingBox.y()));
202     }
203     path.closeSubpath();
204 }
205 
blend(const BasicShape * other,double progress) const206 PassRefPtr<BasicShape> BasicShapePolygon::blend(const BasicShape* other, double progress) const
207 {
208     ASSERT(other && isSameType(*other));
209 
210     const BasicShapePolygon* o = static_cast<const BasicShapePolygon*>(other);
211     ASSERT(m_values.size() == o->values().size());
212     ASSERT(!(m_values.size() % 2));
213 
214     size_t length = m_values.size();
215     RefPtr<BasicShapePolygon> result = BasicShapePolygon::create();
216     if (!length)
217         return result.release();
218 
219     result->setWindRule(o->windRule());
220 
221     for (size_t i = 0; i < length; i = i + 2) {
222         result->appendPoint(m_values.at(i).blend(o->values().at(i), progress, ValueRangeAll),
223             m_values.at(i + 1).blend(o->values().at(i + 1), progress, ValueRangeAll));
224     }
225 
226     return result.release();
227 }
228 
operator ==(const BasicShape & o) const229 bool BasicShapePolygon::operator==(const BasicShape& o) const
230 {
231     if (!isSameType(o))
232         return false;
233     const BasicShapePolygon& other = toBasicShapePolygon(o);
234     return m_windRule == other.m_windRule && m_values == other.m_values;
235 }
236 
floatSizeForLengthSize(const LengthSize & lengthSize,const FloatRect & boundingBox)237 static FloatSize floatSizeForLengthSize(const LengthSize& lengthSize, const FloatRect& boundingBox)
238 {
239     return FloatSize(floatValueForLength(lengthSize.width(), boundingBox.width()),
240         floatValueForLength(lengthSize.height(), boundingBox.height()));
241 }
242 
path(Path & path,const FloatRect & boundingBox)243 void BasicShapeInset::path(Path& path, const FloatRect& boundingBox)
244 {
245     ASSERT(path.isEmpty());
246     float left = floatValueForLength(m_left, boundingBox.width());
247     float top = floatValueForLength(m_top, boundingBox.height());
248     path.addRoundedRect(
249         FloatRect(
250             left + boundingBox.x(),
251             top + boundingBox.y(),
252             std::max<float>(boundingBox.width() - left - floatValueForLength(m_right, boundingBox.width()), 0),
253             std::max<float>(boundingBox.height() - top - floatValueForLength(m_bottom, boundingBox.height()), 0)
254         ),
255         floatSizeForLengthSize(m_topLeftRadius, boundingBox),
256         floatSizeForLengthSize(m_topRightRadius, boundingBox),
257         floatSizeForLengthSize(m_bottomLeftRadius, boundingBox),
258         floatSizeForLengthSize(m_bottomRightRadius, boundingBox)
259     );
260 }
261 
blend(const BasicShape * other,double) const262 PassRefPtr<BasicShape> BasicShapeInset::blend(const BasicShape* other, double) const
263 {
264     ASSERT(type() == other->type());
265     // FIXME: Implement blend for BasicShapeInset.
266     return nullptr;
267 }
268 
operator ==(const BasicShape & o) const269 bool BasicShapeInset::operator==(const BasicShape& o) const
270 {
271     if (!isSameType(o))
272         return false;
273     const BasicShapeInset& other = toBasicShapeInset(o);
274     return m_right == other.m_right
275         && m_top == other.m_top
276         && m_bottom == other.m_bottom
277         && m_left == other.m_left
278         && m_topLeftRadius == other.m_topLeftRadius
279         && m_topRightRadius == other.m_topRightRadius
280         && m_bottomRightRadius == other.m_bottomRightRadius
281         && m_bottomLeftRadius == other.m_bottomLeftRadius;
282 }
283 
284 }
285