source: webkit/trunk/Source/WebCore/css/BasicShapeFunctions.cpp

Last change on this file was 285343, checked in by [email protected], 4 years ago

Implement parsing and animation support for offset-path
https://bugs.webkit.org/show_bug.cgi?id=231801

Patch by Kiet Ho <Kiet Ho> on 2021-11-05
Reviewed by Simon Fraser.

LayoutTests/imported/w3c:

Updated relevant test expectations.

  • web-platform-tests/css/css-cascade/all-prop-initial-xml-expected.txt:
  • web-platform-tests/css/cssom/cssstyledeclaration-csstext-expected.txt:
  • web-platform-tests/css/cssom/getComputedStyle-detached-subtree-expected.txt:
  • web-platform-tests/css/motion/animation/offset-path-interpolation-001-expected.txt:
  • web-platform-tests/css/motion/animation/offset-path-interpolation-002-expected.txt:
  • web-platform-tests/css/motion/animation/offset-path-interpolation-003-expected.txt:
  • web-platform-tests/css/motion/animation/offset-path-interpolation-004-expected.txt:
  • web-platform-tests/css/motion/animation/offset-path-interpolation-005-expected.txt:
  • web-platform-tests/css/motion/inheritance-expected.txt:
  • web-platform-tests/css/motion/offset-path-serialization-expected.txt:
  • web-platform-tests/css/motion/offset-supports-calc-expected.txt:
  • web-platform-tests/css/motion/parsing/offset-path-computed-expected.txt:
  • web-platform-tests/css/motion/parsing/offset-path-parsing-valid-expected.txt:
  • web-platform-tests/css/motion/parsing/offset-shorthand-expected.txt:

Source/WebCore:

Implements parsing and animation support for offset-path based on clip-path.
offset-path additionally supports ray() shape, which will be implemented in a future patch.

Tests: imported/w3c/web-platform-tests/css/motion/inheritance.html

imported/w3c/web-platform-tests/css/motion/offset-path-serialization.html
imported/w3c/web-platform-tests/css/motion/animation/offset-path-interpolation-001.html
imported/w3c/web-platform-tests/css/motion/animation/offset-path-interpolation-002.html
imported/w3c/web-platform-tests/css/motion/animation/offset-path-interpolation-003.html
imported/w3c/web-platform-tests/css/motion/animation/offset-path-interpolation-004.html
imported/w3c/web-platform-tests/css/motion/animation/offset-path-interpolation-005.html
imported/w3c/web-platform-tests/css/motion/parsing/offset-path-computed.html
imported/w3c/web-platform-tests/css/motion/parsing/offset-path-parsing-invalid.html
imported/w3c/web-platform-tests/css/motion/parsing/offset-path-parsing-valid.html

  • Sources.txt:
  • WebCore.xcodeproj/project.pbxproj:
  • animation/CSSPropertyAnimation.cpp:

(WebCore::CSSPropertyAnimationWrapperMap::CSSPropertyAnimationWrapperMap):
Added animation support for offset-path.

  • css/BasicShapeFunctions.cpp:

(WebCore::copySVGPathByteStream):
(WebCore::valueForBasicShape): Added enum to specify conversions done on the
resulting SVG path. So far, the only available conversion is converting relative
draw commands in a path to absolute. This is required when getting the computed value of
offset-path.

  • css/BasicShapeFunctions.h:
  • css/CSSComputedStyleDeclaration.cpp:

(WebCore::valueForPathOperation): Moved logic to get value of PathOperation
into a separate method.
(WebCore::ComputedStyleExtractor::valueForPropertyInStyle): Added support for
offset-path.

  • css/CSSProperties.json: Added entry for offset-path.
  • css/parser/CSSPropertyParser.cpp:

(WebCore::consumePathOperation): Renamed from consumeClipPath.
(WebCore::CSSPropertyParser::parseSingleValue): Added support for parsing offset-path.

  • rendering/style/RenderStyle.h: Added support for offset-path.

(WebCore::RenderStyle::offsetPath const):
(WebCore::RenderStyle::setOffsetPath):
(WebCore::RenderStyle::initialOffsetPath):

  • rendering/style/StyleRareNonInheritedData.cpp:

(WebCore::StyleRareNonInheritedData::StyleRareNonInheritedData):
(WebCore::StyleRareNonInheritedData::operator== const):

  • rendering/style/StyleRareNonInheritedData.h: Added storage space for offset-path.
  • style/StyleBuilderConverter.h:

(WebCore::Style::BuilderConverter::convertPathOperation): Renamed from convertClipPath

  • svg/SVGPathAbsoluteConverter.cpp: Added helper class to convert relative draw

commands in an SVG path to absolute.
(WebCore::SVGPathAbsoluteConverter::SVGPathAbsoluteConverter):
(WebCore::SVGPathAbsoluteConverter::incrementPathSegmentCount):
(WebCore::SVGPathAbsoluteConverter::continueConsuming):
(WebCore::SVGPathAbsoluteConverter::moveTo):
(WebCore::SVGPathAbsoluteConverter::lineTo):
(WebCore::SVGPathAbsoluteConverter::curveToCubic):
(WebCore::SVGPathAbsoluteConverter::closePath):
(WebCore::SVGPathAbsoluteConverter::lineToHorizontal):
(WebCore::SVGPathAbsoluteConverter::lineToVertical):
(WebCore::SVGPathAbsoluteConverter::curveToCubicSmooth):
(WebCore::SVGPathAbsoluteConverter::curveToQuadratic):
(WebCore::SVGPathAbsoluteConverter::curveToQuadraticSmooth):
(WebCore::SVGPathAbsoluteConverter::arcTo):

  • svg/SVGPathAbsoluteConverter.h: Added.
  • svg/SVGPathUtilities.cpp:

(WebCore::convertSVGPathByteStreamToAbsoluteCoordinates):

  • svg/SVGPathUtilities.h:

LayoutTests:

Updated relevant test expectations.

  • platform/gtk/imported/w3c/web-platform-tests/css/cssom/cssstyledeclaration-csstext-expected.txt:
  • platform/ios-wk2/imported/w3c/web-platform-tests/css/cssom/cssstyledeclaration-csstext-expected.txt:
  • platform/ios/imported/w3c/web-platform-tests/css/cssom/cssstyledeclaration-csstext-expected.txt:
  • platform/ios/imported/w3c/web-platform-tests/css/cssom/getComputedStyle-detached-subtree-expected.txt:
  • platform/wpe/imported/w3c/web-platform-tests/css/cssom/cssstyledeclaration-csstext-expected.txt:
  • platform/wpe/imported/w3c/web-platform-tests/css/cssom/getComputedStyle-detached-subtree-expected.txt:
File size: 12.4 KB
Line 
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
68static std::unique_ptr<SVGPathByteStream> copySVGPathByteStream(const SVGPathByteStream& source, SVGPathConversion conversion)
69{
70 switch (conversion) {
71 case SVGPathConversion::None:
72 return source.copy();
73
74 case SVGPathConversion::ForceAbsolute:
75 // Only returns the resulting absolute path if the conversion succeeds.
76 if (auto result = convertSVGPathByteStreamToAbsoluteCoordinates(source))
77 return result;
78
79 return source.copy();
80 }
81
82 ASSERT_NOT_REACHED();
83 return source.copy();
84}
85
86Ref<CSSPrimitiveValue> valueForBasicShape(const RenderStyle& style, const BasicShape& basicShape, SVGPathConversion conversion)
87{
88 auto& cssValuePool = CSSValuePool::singleton();
89
90 RefPtr<CSSBasicShape> basicShapeValue;
91 switch (basicShape.type()) {
92 case BasicShape::Type::Circle: {
93 auto& circle = downcast<BasicShapeCircle>(basicShape);
94 auto circleValue = CSSBasicShapeCircle::create();
95
96 circleValue->setCenterX(valueForCenterCoordinate(cssValuePool, style, circle.centerX(), BoxOrient::Horizontal));
97 circleValue->setCenterY(valueForCenterCoordinate(cssValuePool, style, circle.centerY(), BoxOrient::Vertical));
98 circleValue->setRadius(basicShapeRadiusToCSSValue(style, cssValuePool, circle.radius()));
99
100 basicShapeValue = WTFMove(circleValue);
101 break;
102 }
103 case BasicShape::Type::Ellipse: {
104 auto& ellipse = downcast<BasicShapeEllipse>(basicShape);
105 auto ellipseValue = CSSBasicShapeEllipse::create();
106
107 ellipseValue->setCenterX(valueForCenterCoordinate(cssValuePool, style, ellipse.centerX(), BoxOrient::Horizontal));
108 ellipseValue->setCenterY(valueForCenterCoordinate(cssValuePool, style, ellipse.centerY(), BoxOrient::Vertical));
109 ellipseValue->setRadiusX(basicShapeRadiusToCSSValue(style, cssValuePool, ellipse.radiusX()));
110 ellipseValue->setRadiusY(basicShapeRadiusToCSSValue(style, cssValuePool, ellipse.radiusY()));
111
112 basicShapeValue = WTFMove(ellipseValue);
113 break;
114 }
115 case BasicShape::Type::Polygon: {
116 auto& polygon = downcast<BasicShapePolygon>(basicShape);
117 auto polygonValue = CSSBasicShapePolygon::create();
118
119 polygonValue->setWindRule(polygon.windRule());
120 const Vector<Length>& values = polygon.values();
121 for (unsigned i = 0; i < values.size(); i += 2)
122 polygonValue->appendPoint(cssValuePool.createValue(values.at(i), style), cssValuePool.createValue(values.at(i + 1), style));
123
124 basicShapeValue = WTFMove(polygonValue);
125 break;
126 }
127 case BasicShape::Type::Path: {
128 auto& pathShape = downcast<BasicShapePath>(basicShape);
129
130 ASSERT(pathShape.pathData());
131 auto pathByteStream = copySVGPathByteStream(*pathShape.pathData(), conversion);
132
133 auto pathShapeValue = CSSBasicShapePath::create(WTFMove(pathByteStream));
134 pathShapeValue->setWindRule(pathShape.windRule());
135
136 basicShapeValue = WTFMove(pathShapeValue);
137
138 break;
139 }
140 case BasicShape::Type::Inset: {
141 auto& inset = downcast<BasicShapeInset>(basicShape);
142 auto insetValue = CSSBasicShapeInset::create();
143
144 insetValue->setTop(cssValuePool.createValue(inset.top(), style));
145 insetValue->setRight(cssValuePool.createValue(inset.right(), style));
146 insetValue->setBottom(cssValuePool.createValue(inset.bottom(), style));
147 insetValue->setLeft(cssValuePool.createValue(inset.left(), style));
148
149 insetValue->setTopLeftRadius(cssValuePool.createValue(inset.topLeftRadius(), style));
150 insetValue->setTopRightRadius(cssValuePool.createValue(inset.topRightRadius(), style));
151 insetValue->setBottomRightRadius(cssValuePool.createValue(inset.bottomRightRadius(), style));
152 insetValue->setBottomLeftRadius(cssValuePool.createValue(inset.bottomLeftRadius(), style));
153
154 basicShapeValue = WTFMove(insetValue);
155 break;
156 }
157 }
158
159 return cssValuePool.createValue(basicShapeValue.releaseNonNull());
160}
161
162static Length convertToLength(const CSSToLengthConversionData& conversionData, const CSSPrimitiveValue* value)
163{
164 return value->convertToLength<FixedIntegerConversion | FixedFloatConversion | PercentConversion | CalculatedConversion>(conversionData);
165}
166
167static LengthSize convertToLengthSize(const CSSToLengthConversionData& conversionData, const CSSPrimitiveValue* value)
168{
169 if (!value)
170 return { { 0, LengthType::Fixed }, { 0, LengthType::Fixed } };
171
172 auto& pair = *value->pairValue();
173 return { convertToLength(conversionData, pair.first()), convertToLength(conversionData, pair.second()) };
174}
175
176static BasicShapeCenterCoordinate convertToCenterCoordinate(const CSSToLengthConversionData& conversionData, CSSPrimitiveValue* value)
177{
178 CSSValueID keyword = CSSValueTop;
179 Length offset { 0, LengthType::Fixed };
180 if (!value)
181 keyword = CSSValueCenter;
182 else if (value->isValueID())
183 keyword = value->valueID();
184 else if (Pair* pair = value->pairValue()) {
185 keyword = pair->first()->valueID();
186 offset = convertToLength(conversionData, pair->second());
187 } else
188 offset = convertToLength(conversionData, value);
189
190 BasicShapeCenterCoordinate::Direction direction;
191 switch (keyword) {
192 case CSSValueTop:
193 case CSSValueLeft:
194 direction = BasicShapeCenterCoordinate::TopLeft;
195 break;
196 case CSSValueRight:
197 case CSSValueBottom:
198 direction = BasicShapeCenterCoordinate::BottomRight;
199 break;
200 case CSSValueCenter:
201 direction = BasicShapeCenterCoordinate::TopLeft;
202 offset = Length(50, LengthType::Percent);
203 break;
204 default:
205 ASSERT_NOT_REACHED();
206 direction = BasicShapeCenterCoordinate::TopLeft;
207 break;
208 }
209
210 return BasicShapeCenterCoordinate(direction, offset);
211}
212
213static BasicShapeRadius cssValueToBasicShapeRadius(const CSSToLengthConversionData& conversionData, CSSPrimitiveValue* radius)
214{
215 if (!radius)
216 return BasicShapeRadius(BasicShapeRadius::ClosestSide);
217
218 if (radius->isValueID()) {
219 switch (radius->valueID()) {
220 case CSSValueClosestSide:
221 return BasicShapeRadius(BasicShapeRadius::ClosestSide);
222 case CSSValueFarthestSide:
223 return BasicShapeRadius(BasicShapeRadius::FarthestSide);
224 default:
225 ASSERT_NOT_REACHED();
226 break;
227 }
228 }
229
230 return BasicShapeRadius(convertToLength(conversionData, radius));
231}
232
233Ref<BasicShape> basicShapeForValue(const CSSToLengthConversionData& conversionData, const CSSBasicShape& basicShapeValue, float zoom)
234{
235 RefPtr<BasicShape> basicShape;
236
237 switch (basicShapeValue.type()) {
238 case CSSBasicShape::CSSBasicShapeCircleType: {
239 auto& circleValue = downcast<CSSBasicShapeCircle>(basicShapeValue);
240 auto circle = BasicShapeCircle::create();
241
242 circle->setCenterX(convertToCenterCoordinate(conversionData, circleValue.centerX()));
243 circle->setCenterY(convertToCenterCoordinate(conversionData, circleValue.centerY()));
244 circle->setRadius(cssValueToBasicShapeRadius(conversionData, circleValue.radius()));
245
246 basicShape = WTFMove(circle);
247 break;
248 }
249 case CSSBasicShape::CSSBasicShapeEllipseType: {
250 auto& ellipseValue = downcast<CSSBasicShapeEllipse>(basicShapeValue);
251 auto ellipse = BasicShapeEllipse::create();
252
253 ellipse->setCenterX(convertToCenterCoordinate(conversionData, ellipseValue.centerX()));
254 ellipse->setCenterY(convertToCenterCoordinate(conversionData, ellipseValue.centerY()));
255
256 ellipse->setRadiusX(cssValueToBasicShapeRadius(conversionData, ellipseValue.radiusX()));
257 ellipse->setRadiusY(cssValueToBasicShapeRadius(conversionData, ellipseValue.radiusY()));
258
259 basicShape = WTFMove(ellipse);
260 break;
261 }
262 case CSSBasicShape::CSSBasicShapePolygonType: {
263 auto& polygonValue = downcast<CSSBasicShapePolygon>(basicShapeValue);
264 auto polygon = BasicShapePolygon::create();
265
266 polygon->setWindRule(polygonValue.windRule());
267 auto& values = polygonValue.values();
268 for (unsigned i = 0; i < values.size(); i += 2)
269 polygon->appendPoint(convertToLength(conversionData, values[i].ptr()), convertToLength(conversionData, values[i + 1].ptr()));
270
271 basicShape = WTFMove(polygon);
272 break;
273 }
274 case CSSBasicShape::CSSBasicShapeInsetType: {
275 auto& rectValue = downcast<CSSBasicShapeInset>(basicShapeValue);
276 auto rect = BasicShapeInset::create();
277
278 rect->setTop(convertToLength(conversionData, rectValue.top()));
279 rect->setRight(convertToLength(conversionData, rectValue.right()));
280 rect->setBottom(convertToLength(conversionData, rectValue.bottom()));
281 rect->setLeft(convertToLength(conversionData, rectValue.left()));
282
283 rect->setTopLeftRadius(convertToLengthSize(conversionData, rectValue.topLeftRadius()));
284 rect->setTopRightRadius(convertToLengthSize(conversionData, rectValue.topRightRadius()));
285 rect->setBottomRightRadius(convertToLengthSize(conversionData, rectValue.bottomRightRadius()));
286 rect->setBottomLeftRadius(convertToLengthSize(conversionData, rectValue.bottomLeftRadius()));
287
288 basicShape = WTFMove(rect);
289 break;
290 }
291 case CSSBasicShape::CSSBasicShapePathType: {
292 auto& pathValue = downcast<CSSBasicShapePath>(basicShapeValue);
293 auto path = BasicShapePath::create(pathValue.pathData().copy());
294 path->setWindRule(pathValue.windRule());
295 path->setZoom(zoom);
296
297 basicShape = WTFMove(path);
298 break;
299 }
300 }
301
302 return basicShape.releaseNonNull();
303}
304
305float floatValueForCenterCoordinate(const BasicShapeCenterCoordinate& center, float boxDimension)
306{
307 float offset = floatValueForLength(center.length(), boxDimension);
308 if (center.direction() == BasicShapeCenterCoordinate::TopLeft)
309 return offset;
310 return boxDimension - offset;
311}
312
313}
Note: See TracBrowser for help on using the repository browser.