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 "BasicShapeFunctions.h"
32
33#include "BasicShapes.h"
34#include "CSSBasicShapes.h"
35#include "CSSPrimitiveValueMappings.h"
36#include "CSSValuePool.h"
37#include "Pair.h"
38#include "RenderStyle.h"
39#include "SVGPathByteStream.h"
40
41namespace WebCore {
42
43static Ref<CSSPrimitiveValue> valueForCenterCoordinate(CSSValuePool& pool, const RenderStyle& style, const BasicShapeCenterCoordinate& center, BoxOrient orientation)
44{
45 if (center.direction() == BasicShapeCenterCoordinate::TopLeft)
46 return pool.createValue(center.length(), style);
47
48 CSSValueID keyword = orientation == BoxOrient::Horizontal ? CSSValueRight : CSSValueBottom;
49
50 return pool.createValue(Pair::create(pool.createIdentifierValue(keyword), pool.createValue(center.length(), style)));
51}
52
53static Ref<CSSPrimitiveValue> basicShapeRadiusToCSSValue(const RenderStyle& style, CSSValuePool& pool, const BasicShapeRadius& radius)
54{
55 switch (radius.type()) {
56 case BasicShapeRadius::Value:
57 return pool.createValue(radius.value(), style);
58 case BasicShapeRadius::ClosestSide:
59 return pool.createIdentifierValue(CSSValueClosestSide);
60 case BasicShapeRadius::FarthestSide:
61 return pool.createIdentifierValue(CSSValueFarthestSide);
62 }
63
64 ASSERT_NOT_REACHED();
65 return pool.createIdentifierValue(CSSValueClosestSide);
66}
67
68Ref<CSSPrimitiveValue> valueForBasicShape(const RenderStyle& style, const BasicShape& basicShape)
69{
70 auto& cssValuePool = CSSValuePool::singleton();
71
72 RefPtr<CSSBasicShape> basicShapeValue;
73 switch (basicShape.type()) {
74 case BasicShape::BasicShapeCircleType: {
75 auto& circle = downcast<BasicShapeCircle>(basicShape);
76 auto circleValue = CSSBasicShapeCircle::create();
77
78 circleValue->setCenterX(valueForCenterCoordinate(cssValuePool, style, circle.centerX(), BoxOrient::Horizontal));
79 circleValue->setCenterY(valueForCenterCoordinate(cssValuePool, style, circle.centerY(), BoxOrient::Vertical));
80 circleValue->setRadius(basicShapeRadiusToCSSValue(style, cssValuePool, circle.radius()));
81
82 basicShapeValue = WTFMove(circleValue);
83 break;
84 }
85 case BasicShape::BasicShapeEllipseType: {
86 auto& ellipse = downcast<BasicShapeEllipse>(basicShape);
87 auto ellipseValue = CSSBasicShapeEllipse::create();
88
89 ellipseValue->setCenterX(valueForCenterCoordinate(cssValuePool, style, ellipse.centerX(), BoxOrient::Horizontal));
90 ellipseValue->setCenterY(valueForCenterCoordinate(cssValuePool, style, ellipse.centerY(), BoxOrient::Vertical));
91 ellipseValue->setRadiusX(basicShapeRadiusToCSSValue(style, cssValuePool, ellipse.radiusX()));
92 ellipseValue->setRadiusY(basicShapeRadiusToCSSValue(style, cssValuePool, ellipse.radiusY()));
93
94 basicShapeValue = WTFMove(ellipseValue);
95 break;
96 }
97 case BasicShape::BasicShapePolygonType: {
98 auto& polygon = downcast<BasicShapePolygon>(basicShape);
99 auto polygonValue = CSSBasicShapePolygon::create();
100
101 polygonValue->setWindRule(polygon.windRule());
102 const Vector<Length>& values = polygon.values();
103 for (unsigned i = 0; i < values.size(); i += 2)
104 polygonValue->appendPoint(cssValuePool.createValue(values.at(i), style), cssValuePool.createValue(values.at(i + 1), style));
105
106 basicShapeValue = WTFMove(polygonValue);
107 break;
108 }
109 case BasicShape::BasicShapePathType: {
110 auto& pathShape = downcast<BasicShapePath>(basicShape);
111 auto pathShapeValue = CSSBasicShapePath::create(pathShape.pathData()->copy());
112 pathShapeValue->setWindRule(pathShape.windRule());
113
114 basicShapeValue = WTFMove(pathShapeValue);
115 break;
116 }
117 case BasicShape::BasicShapeInsetType: {
118 auto& inset = downcast<BasicShapeInset>(basicShape);
119 auto insetValue = CSSBasicShapeInset::create();
120
121 insetValue->setTop(cssValuePool.createValue(inset.top(), style));
122 insetValue->setRight(cssValuePool.createValue(inset.right(), style));
123 insetValue->setBottom(cssValuePool.createValue(inset.bottom(), style));
124 insetValue->setLeft(cssValuePool.createValue(inset.left(), style));
125
126 insetValue->setTopLeftRadius(cssValuePool.createValue(inset.topLeftRadius(), style));
127 insetValue->setTopRightRadius(cssValuePool.createValue(inset.topRightRadius(), style));
128 insetValue->setBottomRightRadius(cssValuePool.createValue(inset.bottomRightRadius(), style));
129 insetValue->setBottomLeftRadius(cssValuePool.createValue(inset.bottomLeftRadius(), style));
130
131 basicShapeValue = WTFMove(insetValue);
132 break;
133 }
134 }
135
136 return cssValuePool.createValue(basicShapeValue.releaseNonNull());
137}
138
139static Length convertToLength(const CSSToLengthConversionData& conversionData, const CSSPrimitiveValue* value)
140{
141 return value->convertToLength<FixedIntegerConversion | FixedFloatConversion | PercentConversion | CalculatedConversion>(conversionData);
142}
143
144static LengthSize convertToLengthSize(const CSSToLengthConversionData& conversionData, const CSSPrimitiveValue* value)
145{
146 if (!value)
147 return { { 0, Fixed }, { 0, Fixed } };
148
149 auto& pair = *value->pairValue();
150 return { convertToLength(conversionData, pair.first()), convertToLength(conversionData, pair.second()) };
151}
152
153static BasicShapeCenterCoordinate convertToCenterCoordinate(const CSSToLengthConversionData& conversionData, CSSPrimitiveValue* value)
154{
155 CSSValueID keyword = CSSValueTop;
156 Length offset { 0, Fixed };
157 if (!value)
158 keyword = CSSValueCenter;
159 else if (value->isValueID())
160 keyword = value->valueID();
161 else if (Pair* pair = value->pairValue()) {
162 keyword = pair->first()->valueID();
163 offset = convertToLength(conversionData, pair->second());
164 } else
165 offset = convertToLength(conversionData, value);
166
167 BasicShapeCenterCoordinate::Direction direction;
168 switch (keyword) {
169 case CSSValueTop:
170 case CSSValueLeft:
171 direction = BasicShapeCenterCoordinate::TopLeft;
172 break;
173 case CSSValueRight:
174 case CSSValueBottom:
175 direction = BasicShapeCenterCoordinate::BottomRight;
176 break;
177 case CSSValueCenter:
178 direction = BasicShapeCenterCoordinate::TopLeft;
179 offset = Length(50, Percent);
180 break;
181 default:
182 ASSERT_NOT_REACHED();
183 direction = BasicShapeCenterCoordinate::TopLeft;
184 break;
185 }
186
187 return BasicShapeCenterCoordinate(direction, offset);
188}
189
190static BasicShapeRadius cssValueToBasicShapeRadius(const CSSToLengthConversionData& conversionData, CSSPrimitiveValue* radius)
191{
192 if (!radius)
193 return BasicShapeRadius(BasicShapeRadius::ClosestSide);
194
195 if (radius->isValueID()) {
196 switch (radius->valueID()) {
197 case CSSValueClosestSide:
198 return BasicShapeRadius(BasicShapeRadius::ClosestSide);
199 case CSSValueFarthestSide:
200 return BasicShapeRadius(BasicShapeRadius::FarthestSide);
201 default:
202 ASSERT_NOT_REACHED();
203 break;
204 }
205 }
206
207 return BasicShapeRadius(convertToLength(conversionData, radius));
208}
209
210Ref<BasicShape> basicShapeForValue(const CSSToLengthConversionData& conversionData, const CSSBasicShape& basicShapeValue)
211{
212 RefPtr<BasicShape> basicShape;
213
214 switch (basicShapeValue.type()) {
215 case CSSBasicShape::CSSBasicShapeCircleType: {
216 auto& circleValue = downcast<CSSBasicShapeCircle>(basicShapeValue);
217 auto circle = BasicShapeCircle::create();
218
219 circle->setCenterX(convertToCenterCoordinate(conversionData, circleValue.centerX()));
220 circle->setCenterY(convertToCenterCoordinate(conversionData, circleValue.centerY()));
221 circle->setRadius(cssValueToBasicShapeRadius(conversionData, circleValue.radius()));
222
223 basicShape = WTFMove(circle);
224 break;
225 }
226 case CSSBasicShape::CSSBasicShapeEllipseType: {
227 auto& ellipseValue = downcast<CSSBasicShapeEllipse>(basicShapeValue);
228 auto ellipse = BasicShapeEllipse::create();
229
230 ellipse->setCenterX(convertToCenterCoordinate(conversionData, ellipseValue.centerX()));
231 ellipse->setCenterY(convertToCenterCoordinate(conversionData, ellipseValue.centerY()));
232
233 ellipse->setRadiusX(cssValueToBasicShapeRadius(conversionData, ellipseValue.radiusX()));
234 ellipse->setRadiusY(cssValueToBasicShapeRadius(conversionData, ellipseValue.radiusY()));
235
236 basicShape = WTFMove(ellipse);
237 break;
238 }
239 case CSSBasicShape::CSSBasicShapePolygonType: {
240 auto& polygonValue = downcast<CSSBasicShapePolygon>(basicShapeValue);
241 auto polygon = BasicShapePolygon::create();
242
243 polygon->setWindRule(polygonValue.windRule());
244 auto& values = polygonValue.values();
245 for (unsigned i = 0; i < values.size(); i += 2)
246 polygon->appendPoint(convertToLength(conversionData, values[i].ptr()), convertToLength(conversionData, values[i + 1].ptr()));
247
248 basicShape = WTFMove(polygon);
249 break;
250 }
251 case CSSBasicShape::CSSBasicShapeInsetType: {
252 auto& rectValue = downcast<CSSBasicShapeInset>(basicShapeValue);
253 auto rect = BasicShapeInset::create();
254
255 rect->setTop(convertToLength(conversionData, rectValue.top()));
256 rect->setRight(convertToLength(conversionData, rectValue.right()));
257 rect->setBottom(convertToLength(conversionData, rectValue.bottom()));
258 rect->setLeft(convertToLength(conversionData, rectValue.left()));
259
260 rect->setTopLeftRadius(convertToLengthSize(conversionData, rectValue.topLeftRadius()));
261 rect->setTopRightRadius(convertToLengthSize(conversionData, rectValue.topRightRadius()));
262 rect->setBottomRightRadius(convertToLengthSize(conversionData, rectValue.bottomRightRadius()));
263 rect->setBottomLeftRadius(convertToLengthSize(conversionData, rectValue.bottomLeftRadius()));
264
265 basicShape = WTFMove(rect);
266 break;
267 }
268 case CSSBasicShape::CSSBasicShapePathType: {
269 auto& pathValue = downcast<CSSBasicShapePath>(basicShapeValue);
270 auto path = BasicShapePath::create(pathValue.pathData().copy());
271 path->setWindRule(pathValue.windRule());
272
273 basicShape = WTFMove(path);
274 break;
275 }
276 }
277
278 return basicShape.releaseNonNull();
279}
280
281float floatValueForCenterCoordinate(const BasicShapeCenterCoordinate& center, float boxDimension)
282{
283 float offset = floatValueForLength(center.length(), boxDimension);
284 if (center.direction() == BasicShapeCenterCoordinate::TopLeft)
285 return offset;
286 return boxDimension - offset;
287}
288
289}
290