Skip to content
Permalink
Browse files
Support ray() shape in offset-path
https://bugs.webkit.org/show_bug.cgi?id=233344

Reviewed by Simon Fraser.

Source/WebCore:

Add support for rendering ray path. Involves getting containing block and offset
of the element from the containing block. Using the size of the containing block
and offset, calculate the length of the ray and create a Line path using the length
and angle. Calculating closest/farthest-side and closest/farthest-corner are simple
but calculating side requires some explanation. First, we get the two possible sides of
the containing block the ray could be intersecting with, based on the angle. To calculate
which side is being intersected, if tan(theta) * top/bottom is outside of the containing
block, this means that the ray is intersecting with the other side. Finally, we calculate
the acute angle based on which side we intersected with. Using the length and angle, we
calculate the length of the hypotenuse, which corresponds with the length of the start point
of the ray to its intersection with a side of the containing block. Test 7 is still failing
due to getting incorrect width of containing block (doesn't happen when animating the path
for some reason). Will implement contain in another patch.

* WebCore.xcodeproj/project.pbxproj:
* rendering/PathOperation.cpp:
(WebCore::toPositiveAngle):
(WebCore::RayPathOperation::getLengthForPath const):
(WebCore::RayPathOperation::pathForReferenceRect const):
* rendering/PathOperation.h:
* rendering/RenderLayer.cpp:
(WebCore::RenderLayer::setReferenceBoxForPathOperations):
(WebCore::RenderLayer::updateTransform):
* rendering/RenderLayer.h:
* rendering/style/RenderStyle.cpp:
(WebCore::getPathFromPathOperation):

LayoutTests:

* TestExpectations:

Canonical link: https://commits.webkit.org/250437@main
git-svn-id: https://svn.webkit.org/repository/webkit/trunk@294001 268f45cc-cd09-0410-ab3c-d52691b4dbfc
  • Loading branch information
nmoucht committed May 10, 2022
1 parent 9e05b28 commit a723b84405245d4fb3d00b7694b4ffc4fd4a1d00
Show file tree
Hide file tree
Showing 13 changed files with 225 additions and 25 deletions.
@@ -1,3 +1,12 @@
2022-05-09 Nikolaos Mouchtaris <nmouchtaris@apple.com>

Support ray() shape in offset-path
https://bugs.webkit.org/show_bug.cgi?id=233344

Reviewed by Simon Fraser.

* TestExpectations:

2022-05-09 Tim Nguyen <ntim@apple.com>

Implement CSS :modal pseudo class
@@ -5074,23 +5074,15 @@ webkit.org/b/233340 imported/w3c/web-platform-tests/css/motion/offset-anchor-tra
webkit.org/b/233340 imported/w3c/web-platform-tests/css/motion/offset-anchor-transform-box-fill-box-002.html [ ImageOnlyFailure ]
webkit.org/b/233340 imported/w3c/web-platform-tests/css/motion/offset-anchor-transform-box-fill-box-003.html [ ImageOnlyFailure ]

# CSS motion path that depends on ray(), currently not implemented.
webkit.org/b/233344 imported/w3c/web-platform-tests/css/motion/offset-path-ray-001.html [ ImageOnlyFailure ]
webkit.org/b/233344 imported/w3c/web-platform-tests/css/motion/offset-path-ray-002.html [ ImageOnlyFailure ]
webkit.org/b/233344 imported/w3c/web-platform-tests/css/motion/offset-path-ray-003.html [ ImageOnlyFailure ]
webkit.org/b/233344 imported/w3c/web-platform-tests/css/motion/offset-path-ray-004.html [ ImageOnlyFailure ]
webkit.org/b/233344 imported/w3c/web-platform-tests/css/motion/offset-path-ray-005.html [ ImageOnlyFailure ]
webkit.org/b/233344 imported/w3c/web-platform-tests/css/motion/offset-path-ray-006.html [ ImageOnlyFailure ]
# CSS motion path ray test failing due to getting wrong size from containing block.
webkit.org/b/233344 imported/w3c/web-platform-tests/css/motion/offset-path-ray-007.html [ ImageOnlyFailure ]
webkit.org/b/233344 imported/w3c/web-platform-tests/css/motion/offset-path-ray-008.html [ ImageOnlyFailure ]
webkit.org/b/233344 imported/w3c/web-platform-tests/css/motion/offset-path-ray-009.html [ ImageOnlyFailure ]

# CSS motion path needs to implement contain for ray: https://bugs.webkit.org/show_bug.cgi?id=240259.
webkit.org/b/233344 imported/w3c/web-platform-tests/css/motion/offset-path-ray-contain-001.html [ ImageOnlyFailure ]
webkit.org/b/233344 imported/w3c/web-platform-tests/css/motion/offset-path-ray-contain-002.html [ ImageOnlyFailure ]
webkit.org/b/233344 imported/w3c/web-platform-tests/css/motion/offset-path-ray-contain-003.html [ ImageOnlyFailure ]
webkit.org/b/233344 imported/w3c/web-platform-tests/css/motion/offset-path-ray-contain-004.html [ ImageOnlyFailure ]
webkit.org/b/233344 imported/w3c/web-platform-tests/css/motion/offset-path-ray-contain-005.html [ ImageOnlyFailure ]
webkit.org/b/233344 imported/w3c/web-platform-tests/css/motion/offset-rotate-001.html [ ImageOnlyFailure ]
webkit.org/b/233344 imported/w3c/web-platform-tests/css/motion/offset-rotate-002.html [ ImageOnlyFailure ]

# IPC test failing in Debug mode due to assert.
[ Debug ] ipc/send-invalid-message.html [ Skip ]
@@ -1855,6 +1855,9 @@ webkit.org/b/160119 fast/repaint/selection-gap-flipped-fixed-child.html [ Failur
# UNSORTED Expectations. When in doubt, put it here.
#////////////////////////////////////////////////////////////////////////////////////////

webkit.org/b/240270 imported/w3c/web-platform-tests/css/motion/offset-rotate-001.html [ ImageOnlyFailure ]
webkit.org/b/240270 imported/w3c/web-platform-tests/css/motion/offset-rotate-002.html [ ImageOnlyFailure ]

accessibility/dialog-properties.html [ Skip ]
accessibility/ignored-aria-role-description.html [ Skip ]

@@ -1,3 +1,37 @@
2022-05-09 Nikolaos Mouchtaris <nmouchtaris@apple.com>

Support ray() shape in offset-path
https://bugs.webkit.org/show_bug.cgi?id=233344

Reviewed by Simon Fraser.

Add support for rendering ray path. Involves getting containing block and offset
of the element from the containing block. Using the size of the containing block
and offset, calculate the length of the ray and create a Line path using the length
and angle. Calculating closest/farthest-side and closest/farthest-corner are simple
but calculating side requires some explanation. First, we get the two possible sides of
the containing block the ray could be intersecting with, based on the angle. To calculate
which side is being intersected, if tan(theta) * top/bottom is outside of the containing
block, this means that the ray is intersecting with the other side. Finally, we calculate
the acute angle based on which side we intersected with. Using the length and angle, we
calculate the length of the hypotenuse, which corresponds with the length of the start point
of the ray to its intersection with a side of the containing block. Test 7 is still failing
due to getting incorrect width of containing block (doesn't happen when animating the path
for some reason). Will implement contain in another patch.

* WebCore.xcodeproj/project.pbxproj:
* rendering/PathOperation.cpp:
(WebCore::toPositiveAngle):
(WebCore::RayPathOperation::getLengthForPath const):
(WebCore::RayPathOperation::pathForReferenceRect const):
* rendering/PathOperation.h:
* rendering/RenderLayer.cpp:
(WebCore::RenderLayer::setReferenceBoxForPathOperations):
(WebCore::RenderLayer::updateTransform):
* rendering/RenderLayer.h:
* rendering/style/RenderStyle.cpp:
(WebCore::getPathFromPathOperation):

2022-05-09 Alan Bujtas <zalan@apple.com>

Do not use the cached renderer's parent in handleFragmentedFlowStateChange lambda
@@ -6988,6 +6988,7 @@
1AB7FC660A8B92EC00D9D37B /* XPathVariableReference.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = XPathVariableReference.cpp; sourceTree = "<group>"; };
1AB7FC670A8B92EC00D9D37B /* XPathVariableReference.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = XPathVariableReference.h; sourceTree = "<group>"; };
1ABA7FFF1897341200DCE9D6 /* VisitedLinkStore.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = VisitedLinkStore.h; sourceTree = "<group>"; };
1ABC260327FD02140064F68F /* PathOperation.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PathOperation.cpp; sourceTree = "<group>"; };
1AC2260A0DB69F190089B669 /* JSDOMApplicationCache.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSDOMApplicationCache.cpp; sourceTree = "<group>"; };
1AC2260B0DB69F190089B669 /* JSDOMApplicationCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSDOMApplicationCache.h; sourceTree = "<group>"; };
1AC2D89C1B1E291F00D52E87 /* FontAntialiasingStateSaver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FontAntialiasingStateSaver.h; sourceTree = "<group>"; };
@@ -32250,6 +32251,7 @@
55EC95972069C92D007DD0A9 /* PaintFrequencyTracker.h */,
0885067D11DA045B00182B98 /* PaintInfo.h */,
0885067E11DA045B00182B98 /* PaintPhase.h */,
1ABC260327FD02140064F68F /* PathOperation.cpp */,
FB92DF4915FED08700994433 /* PathOperation.h */,
B2B1F7140D00CAA8004AEA64 /* PointerEventsHitRules.cpp */,
B2B1F7150D00CAA8004AEA64 /* PointerEventsHitRules.h */,
@@ -30,6 +30,7 @@
#include "CSSToLengthConversionData.h"
#include "CSSValueKeywords.h"
#include "ColorInterpolation.h"
#include "GeometryUtilities.h"
#include "GradientImage.h"
#include "NodeRenderStyle.h"
#include "Pair.h"
@@ -815,9 +816,7 @@ static void endPointsFromAngle(float angleDeg, const FloatSize& size, FloatPoint
if (type == CSSPrefixedLinearGradient)
angleDeg = 90 - angleDeg;

angleDeg = fmodf(angleDeg, 360);
if (angleDeg < 0)
angleDeg += 360;
angleDeg = toPositiveAngle(angleDeg);

if (!angleDeg) {
firstPoint.set(0, size.height());
@@ -268,4 +268,74 @@ RotatedRect rotatedBoundingRectWithMinimumAngleOfRotation(const FloatQuad& quad,
return { center, { width, height }, angle };
}

float toPositiveAngle(float angle)
{
angle = fmod(angle, 360);
while (angle < 0)
angle += 360.0;
return angle;
}

// Compute acute angle from vertical axis
static float toRelatedAcuteAngle(float angle)
{
angle = toPositiveAngle(angle);
if (angle < 90)
return angle;
if (angle > 90 || angle < 180)
return std::abs(180 - angle);
return std::abs(360 - angle);
}

RectEdges<double> distanceOfPointToSidesOfRect(const FloatRect& boundingRect, const FloatPoint& position)
{
// Compute distance to each side of the containing box
double top = std::abs(position.y());
double bottom = std::abs(position.y() - boundingRect.height());
double left = std::abs(position.x());
double right = std::abs(position.x() - boundingRect.width());
return RectEdges<double>(top, right, bottom, left);
}

double lengthOfRayIntersectionWithBoundingBox(const FloatRect& boundingRect, const std::pair<const FloatPoint&, float> ray)
{
auto length = lengthOfPointToSideOfIntersection(boundingRect, ray);
auto angleOfTriangle = angleOfPointToSideOfIntersection(boundingRect, ray);
// Given a length and angle of a right triangle, calculate the hypotenuse, which corresponds to
// the length from the given point to the intersecting point on the box
return length / cos(deg2rad(angleOfTriangle));
}

// Get the side of box the ray intersects with
static BoxSide intersectionSide(const FloatRect& boundingRect, const std::pair<const FloatPoint&, float> ray)
{
auto position = ray.first;
auto angleInRadians = deg2rad(ray.second);
auto distances = distanceOfPointToSidesOfRect(boundingRect, position);
// Get possible intersection sides
auto s1 = cos(angleInRadians) >= 0 ? distances.top() : distances.bottom();
auto s2 = sin(angleInRadians) >= 0 ? distances.right() : distances.left();
auto vertical = cos(angleInRadians) >= 0 ? BoxSide::Top : BoxSide::Bottom;
auto horizontal = sin(angleInRadians) >= 0 ? BoxSide::Right : BoxSide::Left;
auto acuteAngle = deg2rad(toRelatedAcuteAngle(ray.second));
return sin(acuteAngle) * s1 > cos(acuteAngle) * s2 ? horizontal : vertical;
}

double lengthOfPointToSideOfIntersection(const FloatRect& boundingRect, const std::pair<const FloatPoint&, float> ray)
{
auto position = ray.first;
if (position.x() < 0 || position.x() > boundingRect.width() || position.y() < 0 || position.y() > boundingRect.height())
return 0;
auto distances = distanceOfPointToSidesOfRect(boundingRect, position);
return distances.at(intersectionSide(boundingRect, ray));
}

float angleOfPointToSideOfIntersection(const FloatRect& boundingRect, const std::pair<const FloatPoint&, float> ray)
{
auto angle = ray.second;
auto side = intersectionSide(boundingRect, ray);
angle = toRelatedAcuteAngle(toPositiveAngle(angle));
return side == BoxSide::Top || side == BoxSide::Bottom ? angle : 90 - angle;
}

}
@@ -64,6 +64,32 @@ bool ellipseContainsPoint(const FloatPoint& center, const FloatSize& radii, cons

FloatPoint midPoint(const FloatPoint&, const FloatPoint&);

// -------------
// | h\ |s |
// | \a| |
// | \| |
// | * |
// | (x,y) |
// -------------
// Given a box and a ray (described by an offset from the top left corner of the box and angle from vertical in degrees), compute
// the length from the starting position to the intersection of the ray with the box. Given the above diagram, we are
// trying to calculate h, with lengthOfPointToSideOfIntersection computing the length of s, and angleOfPointToSideOfIntersection
// computing a.
double lengthOfRayIntersectionWithBoundingBox(const FloatRect& boundingRect, const std::pair<const FloatPoint&, float> ray);

// Given a box and a ray (described by an offset from the top left corner of the box and angle from vertical in degrees),
// compute the closest length from the starting position to the side that the ray intersects with.
double lengthOfPointToSideOfIntersection(const FloatRect& boundingRect, const std::pair<const FloatPoint&, float> ray);

// Given a box and a ray (described by an offset from the top left corner of the box and angle from vertical in degrees)
// compute the acute angle between the ray and the line segment from the starting point to the closest point on the
// side that the ray intersects with.
float angleOfPointToSideOfIntersection(const FloatRect& boundingRect, const std::pair<const FloatPoint&, float> ray);

// Given a box and an offset from the top left corner, calculate the distance of the point from each side
RectEdges<double> distanceOfPointToSidesOfRect(const FloatRect&, const FloatPoint&);
float toPositiveAngle(float angle);

struct RotatedRect {
FloatPoint center;
FloatSize size;
@@ -26,6 +26,7 @@
#include "config.h"
#include "PathOperation.h"

#include "GeometryUtilities.h"
#include "SVGElement.h"

namespace WebCore {
@@ -48,4 +49,36 @@ const SVGElement* ReferencePathOperation::element() const
{
return m_element.get();
}

double RayPathOperation::lengthForPath() const
{
auto boundingBox = m_containingBlockBoundingRect;
auto distances = distanceOfPointToSidesOfRect(boundingBox, m_position);

switch (m_size) {
case Size::ClosestSide:
return std::min( { distances.top(), distances.bottom(), distances.left(), distances.right() } );
case Size::FarthestSide:
return std::max( { distances.top(), distances.bottom(), distances.left(), distances.right() } );
case Size::FarthestCorner:
return std::sqrt(std::pow(std::max(distances.left(), distances.right()), 2) + std::pow(std::max(distances.top(), distances.bottom()), 2));
case Size::ClosestCorner:
return std::sqrt(std::pow(std::min(distances.left(), distances.right()), 2) + std::pow(std::min(distances.top(), distances.bottom()), 2));
case Size::Sides:
return lengthOfRayIntersectionWithBoundingBox(boundingBox, std::make_pair(m_position, m_angle));
}
}

const Path RayPathOperation::pathForReferenceRect() const
{
Path path;
if (m_containingBlockBoundingRect.isZero())
return path;
auto length = lengthForPath();
auto radians = deg2rad(toPositiveAngle(m_angle) - 90);
auto point = FloatPoint(std::cos(radians) * length, std::sin(radians) * length);
path.addLineTo(point);
return path;
}

} // namespace WebCore
@@ -196,6 +196,17 @@ class RayPathOperation final : public PathOperation {
return RayPathOperation::create(WebCore::blend(m_angle, to.m_angle, context), m_size, m_isContaining);
}

const Path pathForReferenceRect() const;
double lengthForPath() const;

void setContainingBlockReferenceRect(const FloatRect& boundingRect)
{
m_containingBlockBoundingRect = boundingRect;
}
void setStartingPosition(const FloatPoint& position)
{
m_position = position;
}
private:
bool operator==(const PathOperation& other) const override
{
@@ -216,9 +227,11 @@ class RayPathOperation final : public PathOperation {
{
}

float m_angle;
float m_angle { 0 };
Size m_size;
bool m_isContaining;
bool m_isContaining { false };
FloatRect m_containingBlockBoundingRect;
FloatPoint m_position;
};

} // namespace WebCore
@@ -1317,6 +1317,31 @@ void RenderLayer::updateTransformFromStyle(TransformationMatrix& transform, cons
makeMatrixRenderable(transform, canRender3DTransforms());
}

void RenderLayer::setReferenceBoxForPathOperations()
{
auto pathOperation = renderer().style().offsetPath();
if (!pathOperation)
return;
if (is<BoxPathOperation>(pathOperation)) {
auto& boxPathOperation = downcast<BoxPathOperation>(*pathOperation);
auto pathReferenceBoxRect = snapRectToDevicePixelsIfNeeded(renderer().referenceBoxRect(boxPathOperation.referenceBox()), renderer());
boxPathOperation.setPathForReferenceRect(FloatRoundedRect { pathReferenceBoxRect });
} else if (is<RayPathOperation>(pathOperation)) {
if (const auto* containingBlock = renderer().containingBlock()) {
auto& rayPathOperation = downcast<RayPathOperation>(*pathOperation);
auto pathReferenceBoxRect = snapRectToDevicePixelsIfNeeded(containingBlock->transformReferenceBoxRect(containingBlock->style()), renderer());
if (!pathReferenceBoxRect.width())
pathReferenceBoxRect.setWidth(pathReferenceBoxRect.height());
if (!pathReferenceBoxRect.height())
pathReferenceBoxRect.setHeight(pathReferenceBoxRect.width());
rayPathOperation.setContainingBlockReferenceRect(pathReferenceBoxRect);
auto left = renderer().style().left();
auto top = renderer().style().top();
rayPathOperation.setStartingPosition(FloatPoint(left.isPercent() ? left.value() / 100 * pathReferenceBoxRect.width() : left.value(), top.isPercent() ? top.value() / 100 * pathReferenceBoxRect.height() : top.value()));
}
}
}

void RenderLayer::updateTransform()
{
bool hasTransform = renderer().hasTransform();
@@ -1335,13 +1360,7 @@ void RenderLayer::updateTransform()

if (hasTransform) {
m_transform->makeIdentity();

if (auto pathOperation = renderer().style().offsetPath(); pathOperation && is<BoxPathOperation>(pathOperation)) {
auto& boxPathOperation = downcast<BoxPathOperation>(*pathOperation);
auto pathReferenceBoxRect = snapRectToDevicePixelsIfNeeded(renderer().referenceBoxRect(boxPathOperation.referenceBox()), renderer());
boxPathOperation.setPathForReferenceRect(FloatRoundedRect { pathReferenceBoxRect });
}

setReferenceBoxForPathOperations();
updateTransformFromStyle(*m_transform, renderer().style(), RenderStyle::allTransformOperations);
}

@@ -509,6 +509,7 @@ class RenderLayer : public CanMakeWeakPtr<RenderLayer> {
return m_enclosingPaginationLayer.get();
}

void setReferenceBoxForPathOperations();
void updateTransform();

#if ENABLE(CSS_COMPOSITING)
@@ -1569,8 +1569,7 @@ static std::optional<Path> getPathFromPathOperation(const FloatRect& box, const
case PathOperation::Box:
return downcast<BoxPathOperation>(operation).getPath();
case PathOperation::Ray:
// FIXME: implement ray- https://bugs.webkit.org/show_bug.cgi?id=233344
return std::nullopt;
return downcast<RayPathOperation>(operation).pathForReferenceRect();
}
RELEASE_ASSERT_NOT_REACHED();
}

0 comments on commit a723b84

Please sign in to comment.