Bug 1709347 - Add CanvasRenderingContext2D.reset(). r=lsalzman,webidl,smaug
[gecko.git] / layout / base / ShapeUtils.cpp
blob0505053aa8bc10d25990056964baa9d43ff1afdd
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "mozilla/ShapeUtils.h"
9 #include <cstdlib>
11 #include "nsCSSRendering.h"
12 #include "nsMargin.h"
13 #include "nsStyleStruct.h"
14 #include "mozilla/SVGContentUtils.h"
16 namespace mozilla {
18 nscoord ShapeUtils::ComputeShapeRadius(const StyleShapeRadius& aType,
19 const nscoord aCenter,
20 const nscoord aPosMin,
21 const nscoord aPosMax) {
22 MOZ_ASSERT(aType.IsFarthestSide() || aType.IsClosestSide());
23 nscoord dist1 = std::abs(aPosMin - aCenter);
24 nscoord dist2 = std::abs(aPosMax - aCenter);
25 nscoord length = 0;
26 if (aType.IsFarthestSide()) {
27 length = dist1 > dist2 ? dist1 : dist2;
28 } else {
29 length = dist1 > dist2 ? dist2 : dist1;
31 return length;
34 nsPoint ShapeUtils::ComputeCircleOrEllipseCenter(
35 const StyleBasicShape& aBasicShape, const nsRect& aRefBox) {
36 MOZ_ASSERT(aBasicShape.IsCircle() || aBasicShape.IsEllipse(),
37 "The basic shape must be circle() or ellipse!");
39 const auto& position = aBasicShape.IsCircle()
40 ? aBasicShape.AsCircle().position
41 : aBasicShape.AsEllipse().position;
43 nsPoint topLeft, anchor;
44 nsSize size(aRefBox.Size());
45 nsImageRenderer::ComputeObjectAnchorPoint(position, size, size, &topLeft,
46 &anchor);
47 return anchor + aRefBox.TopLeft();
50 nscoord ShapeUtils::ComputeCircleRadius(const StyleBasicShape& aBasicShape,
51 const nsPoint& aCenter,
52 const nsRect& aRefBox) {
53 MOZ_ASSERT(aBasicShape.IsCircle(), "The basic shape must be circle()!");
54 const auto& radius = aBasicShape.AsCircle().radius;
55 if (radius.IsLength()) {
56 return radius.AsLength().Resolve([&] {
57 // We resolve percent <shape-radius> value for circle() as defined here:
58 // https://drafts.csswg.org/css-shapes/#funcdef-circle
59 double referenceLength = SVGContentUtils::ComputeNormalizedHypotenuse(
60 aRefBox.width, aRefBox.height);
61 return NSToCoordRound(referenceLength);
62 });
65 nscoord horizontal =
66 ComputeShapeRadius(radius, aCenter.x, aRefBox.x, aRefBox.XMost());
67 nscoord vertical =
68 ComputeShapeRadius(radius, aCenter.y, aRefBox.y, aRefBox.YMost());
69 return radius.IsFarthestSide() ? std::max(horizontal, vertical)
70 : std::min(horizontal, vertical);
73 nsSize ShapeUtils::ComputeEllipseRadii(const StyleBasicShape& aBasicShape,
74 const nsPoint& aCenter,
75 const nsRect& aRefBox) {
76 MOZ_ASSERT(aBasicShape.IsEllipse(), "The basic shape must be ellipse()!");
77 const auto& ellipse = aBasicShape.AsEllipse();
78 nsSize radii;
79 if (ellipse.semiaxis_x.IsLength()) {
80 radii.width = ellipse.semiaxis_x.AsLength().Resolve(aRefBox.width);
81 } else {
82 radii.width = ComputeShapeRadius(ellipse.semiaxis_x, aCenter.x, aRefBox.x,
83 aRefBox.XMost());
86 if (ellipse.semiaxis_y.IsLength()) {
87 radii.height = ellipse.semiaxis_y.AsLength().Resolve(aRefBox.height);
88 } else {
89 radii.height = ComputeShapeRadius(ellipse.semiaxis_y, aCenter.y, aRefBox.y,
90 aRefBox.YMost());
93 return radii;
96 /* static */
97 nsRect ShapeUtils::ComputeInsetRect(const StyleBasicShape& aBasicShape,
98 const nsRect& aRefBox) {
99 MOZ_ASSERT(aBasicShape.IsInset(), "The basic shape must be inset()!");
100 const auto& rect = aBasicShape.AsInset().rect;
101 nsMargin inset(
102 rect._0.Resolve(aRefBox.Height()), rect._1.Resolve(aRefBox.Width()),
103 rect._2.Resolve(aRefBox.Height()), rect._3.Resolve(aRefBox.Width()));
105 nscoord x = aRefBox.X() + inset.left;
106 nscoord width = aRefBox.Width() - inset.LeftRight();
107 nscoord y = aRefBox.Y() + inset.top;
108 nscoord height = aRefBox.Height() - inset.TopBottom();
110 // Invert left and right, if necessary.
111 if (width < 0) {
112 width *= -1;
113 x -= width;
116 // Invert top and bottom, if necessary.
117 if (height < 0) {
118 height *= -1;
119 y -= height;
122 return nsRect(x, y, width, height);
125 /* static */
126 bool ShapeUtils::ComputeInsetRadii(const StyleBasicShape& aBasicShape,
127 const nsRect& aRefBox,
128 const nsRect& aInsetRect,
129 nscoord aRadii[8]) {
130 const auto& radius = aBasicShape.AsInset().round;
131 return nsIFrame::ComputeBorderRadii(radius, aRefBox.Size(), aInsetRect.Size(),
132 Sides(), aRadii);
135 /* static */
136 nsTArray<nsPoint> ShapeUtils::ComputePolygonVertices(
137 const StyleBasicShape& aBasicShape, const nsRect& aRefBox) {
138 MOZ_ASSERT(aBasicShape.IsPolygon(), "The basic shape must be polygon()!");
140 auto coords = aBasicShape.AsPolygon().coordinates.AsSpan();
141 nsTArray<nsPoint> vertices(coords.Length());
142 for (const StylePolygonCoord<LengthPercentage>& point : coords) {
143 vertices.AppendElement(nsPoint(point._0.Resolve(aRefBox.width),
144 point._1.Resolve(aRefBox.height)) +
145 aRefBox.TopLeft());
147 return vertices;
150 } // namespace mozilla