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 | |
41 | namespace WebCore { |
42 | |
43 | static 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 | |
53 | static 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 | |
68 | Ref<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 | |
139 | static Length convertToLength(const CSSToLengthConversionData& conversionData, const CSSPrimitiveValue* value) |
140 | { |
141 | return value->convertToLength<FixedIntegerConversion | FixedFloatConversion | PercentConversion | CalculatedConversion>(conversionData); |
142 | } |
143 | |
144 | static 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 | |
153 | static 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 | |
190 | static 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 | |
210 | Ref<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 | |
281 | float 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 | |