Bug 1839338 [wpt PR 40636] - Clone encoded WebRTC audio frame when deserializing...
[gecko.git] / layout / base / ShapeUtils.cpp
bloba557ea0caf0602d6aa4c535914152f18000311fe
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 "nsLayoutUtils.h"
13 #include "nsMargin.h"
14 #include "nsStyleStruct.h"
15 #include "mozilla/SVGContentUtils.h"
16 #include "mozilla/gfx/2D.h"
17 #include "mozilla/gfx/PathHelpers.h"
19 namespace mozilla {
21 nscoord ShapeUtils::ComputeShapeRadius(const StyleShapeRadius& aType,
22 const nscoord aCenter,
23 const nscoord aPosMin,
24 const nscoord aPosMax) {
25 MOZ_ASSERT(aType.IsFarthestSide() || aType.IsClosestSide());
26 nscoord dist1 = std::abs(aPosMin - aCenter);
27 nscoord dist2 = std::abs(aPosMax - aCenter);
28 nscoord length = 0;
29 if (aType.IsFarthestSide()) {
30 length = dist1 > dist2 ? dist1 : dist2;
31 } else {
32 length = dist1 > dist2 ? dist2 : dist1;
34 return length;
37 nsPoint ShapeUtils::ComputePosition(const StylePosition& aPosition,
38 const nsRect& aRefBox) {
39 nsPoint topLeft, anchor;
40 nsSize size(aRefBox.Size());
41 nsImageRenderer::ComputeObjectAnchorPoint(aPosition, size, size, &topLeft,
42 &anchor);
43 return anchor + aRefBox.TopLeft();
46 nsPoint ShapeUtils::ComputeCircleOrEllipseCenter(
47 const StyleBasicShape& aBasicShape, const nsRect& aRefBox) {
48 MOZ_ASSERT(aBasicShape.IsCircle() || aBasicShape.IsEllipse(),
49 "The basic shape must be circle() or ellipse!");
51 const auto& position = aBasicShape.IsCircle()
52 ? aBasicShape.AsCircle().position
53 : aBasicShape.AsEllipse().position;
54 MOZ_ASSERT(position.IsPosition(), "A default position should be given");
55 return ComputePosition(position.AsPosition(), aRefBox);
58 nscoord ShapeUtils::ComputeCircleRadius(const StyleBasicShape& aBasicShape,
59 const nsPoint& aCenter,
60 const nsRect& aRefBox) {
61 MOZ_ASSERT(aBasicShape.IsCircle(), "The basic shape must be circle()!");
62 const auto& radius = aBasicShape.AsCircle().radius;
63 if (radius.IsLength()) {
64 return radius.AsLength().Resolve([&] {
65 // We resolve percent <shape-radius> value for circle() as defined here:
66 // https://drafts.csswg.org/css-shapes/#funcdef-circle
67 double referenceLength = SVGContentUtils::ComputeNormalizedHypotenuse(
68 aRefBox.width, aRefBox.height);
69 return NSToCoordRound(referenceLength);
70 });
73 nscoord horizontal =
74 ComputeShapeRadius(radius, aCenter.x, aRefBox.x, aRefBox.XMost());
75 nscoord vertical =
76 ComputeShapeRadius(radius, aCenter.y, aRefBox.y, aRefBox.YMost());
77 return radius.IsFarthestSide() ? std::max(horizontal, vertical)
78 : std::min(horizontal, vertical);
81 nsSize ShapeUtils::ComputeEllipseRadii(const StyleBasicShape& aBasicShape,
82 const nsPoint& aCenter,
83 const nsRect& aRefBox) {
84 MOZ_ASSERT(aBasicShape.IsEllipse(), "The basic shape must be ellipse()!");
85 const auto& ellipse = aBasicShape.AsEllipse();
86 nsSize radii;
87 if (ellipse.semiaxis_x.IsLength()) {
88 radii.width = ellipse.semiaxis_x.AsLength().Resolve(aRefBox.width);
89 } else {
90 radii.width = ComputeShapeRadius(ellipse.semiaxis_x, aCenter.x, aRefBox.x,
91 aRefBox.XMost());
94 if (ellipse.semiaxis_y.IsLength()) {
95 radii.height = ellipse.semiaxis_y.AsLength().Resolve(aRefBox.height);
96 } else {
97 radii.height = ComputeShapeRadius(ellipse.semiaxis_y, aCenter.y, aRefBox.y,
98 aRefBox.YMost());
101 return radii;
104 /* static */
105 nsRect ShapeUtils::ComputeInsetRect(const StyleBasicShape& aBasicShape,
106 const nsRect& aRefBox) {
107 MOZ_ASSERT(aBasicShape.IsInset(), "The basic shape must be inset()!");
108 return ComputeInsetRect(aBasicShape.AsInset().rect, aRefBox);
111 /* static */
112 nsRect ShapeUtils::ComputeInsetRect(
113 const StyleRect<LengthPercentage>& aStyleRect, const nsRect& aRefBox) {
114 nsMargin inset(aStyleRect._0.Resolve(aRefBox.Height()),
115 aStyleRect._1.Resolve(aRefBox.Width()),
116 aStyleRect._2.Resolve(aRefBox.Height()),
117 aStyleRect._3.Resolve(aRefBox.Width()));
119 nscoord x = aRefBox.X() + inset.left;
120 nscoord width = aRefBox.Width() - inset.LeftRight();
121 nscoord y = aRefBox.Y() + inset.top;
122 nscoord height = aRefBox.Height() - inset.TopBottom();
124 // Invert left and right, if necessary.
125 if (width < 0) {
126 width *= -1;
127 x -= width;
130 // Invert top and bottom, if necessary.
131 if (height < 0) {
132 height *= -1;
133 y -= height;
136 return nsRect(x, y, width, height);
139 /* static */
140 bool ShapeUtils::ComputeInsetRadii(const StyleBasicShape& aBasicShape,
141 const nsRect& aRefBox,
142 const nsRect& aInsetRect,
143 nscoord aRadii[8]) {
144 const auto& radius = aBasicShape.AsInset().round;
145 return nsIFrame::ComputeBorderRadii(radius, aRefBox.Size(), aInsetRect.Size(),
146 Sides(), aRadii);
149 /* static */
150 nsTArray<nsPoint> ShapeUtils::ComputePolygonVertices(
151 const StyleBasicShape& aBasicShape, const nsRect& aRefBox) {
152 MOZ_ASSERT(aBasicShape.IsPolygon(), "The basic shape must be polygon()!");
154 auto coords = aBasicShape.AsPolygon().coordinates.AsSpan();
155 nsTArray<nsPoint> vertices(coords.Length());
156 for (const StylePolygonCoord<LengthPercentage>& point : coords) {
157 vertices.AppendElement(nsPoint(point._0.Resolve(aRefBox.width),
158 point._1.Resolve(aRefBox.height)) +
159 aRefBox.TopLeft());
161 return vertices;
164 /* static */
165 static inline gfx::Point ConvertToGfxPoint(const nsPoint& aPoint,
166 nscoord aAppUnitsPerPixel) {
167 return {static_cast<gfx::Float>(aPoint.x) /
168 static_cast<gfx::Float>(aAppUnitsPerPixel),
169 static_cast<gfx::Float>(aPoint.y) /
170 static_cast<gfx::Float>(aAppUnitsPerPixel)};
173 /* static */
174 already_AddRefed<gfx::Path> ShapeUtils::BuildCirclePath(
175 const StyleBasicShape& aShape, const nsRect& aRefBox,
176 const nsPoint& aCenter, nscoord aAppUnitsPerPixel,
177 gfx::PathBuilder* aPathBuilder) {
178 const nscoord r = ComputeCircleRadius(aShape, aCenter, aRefBox);
179 aPathBuilder->Arc(
180 ConvertToGfxPoint(aCenter, aAppUnitsPerPixel),
181 static_cast<float>(r) / static_cast<float>(aAppUnitsPerPixel), 0.0,
182 gfx::Float(2.0 * M_PI));
183 aPathBuilder->Close();
184 return aPathBuilder->Finish();
187 static inline gfx::Size ConvertToGfxSize(const nsSize& aSize,
188 nscoord aAppUnitsPerPixel) {
189 return {static_cast<gfx::Float>(aSize.width) /
190 static_cast<gfx::Float>(aAppUnitsPerPixel),
191 static_cast<gfx::Float>(aSize.height) /
192 static_cast<gfx::Float>(aAppUnitsPerPixel)};
195 /* static */
196 already_AddRefed<gfx::Path> ShapeUtils::BuildEllipsePath(
197 const StyleBasicShape& aShape, const nsRect& aRefBox,
198 const nsPoint& aCenter, nscoord aAppUnitsPerPixel,
199 gfx::PathBuilder* aPathBuilder) {
200 const nsSize radii = ComputeEllipseRadii(aShape, aCenter, aRefBox);
201 EllipseToBezier(aPathBuilder, ConvertToGfxPoint(aCenter, aAppUnitsPerPixel),
202 ConvertToGfxSize(radii, aAppUnitsPerPixel));
203 aPathBuilder->Close();
204 return aPathBuilder->Finish();
207 /* static */
208 already_AddRefed<gfx::Path> ShapeUtils::BuildPolygonPath(
209 const StyleBasicShape& aShape, const nsRect& aRefBox,
210 nscoord aAppUnitsPerPixel, gfx::PathBuilder* aPathBuilder) {
211 nsTArray<nsPoint> vertices = ComputePolygonVertices(aShape, aRefBox);
212 if (vertices.IsEmpty()) {
213 MOZ_ASSERT_UNREACHABLE(
214 "ComputePolygonVertices() should've given us some vertices!");
215 } else {
216 aPathBuilder->MoveTo(NSPointToPoint(vertices[0], aAppUnitsPerPixel));
217 for (size_t i = 1; i < vertices.Length(); ++i) {
218 aPathBuilder->LineTo(NSPointToPoint(vertices[i], aAppUnitsPerPixel));
221 aPathBuilder->Close();
222 return aPathBuilder->Finish();
225 /* static */
226 already_AddRefed<gfx::Path> ShapeUtils::BuildInsetPath(
227 const StyleBasicShape& aShape, const nsRect& aRefBox,
228 nscoord aAppUnitsPerPixel, gfx::PathBuilder* aPathBuilder) {
229 const nsRect insetRect = ComputeInsetRect(aShape, aRefBox);
230 nscoord appUnitsRadii[8];
231 const bool hasRadii =
232 ComputeInsetRadii(aShape, aRefBox, insetRect, appUnitsRadii);
233 return BuildInsetPath(insetRect, hasRadii ? appUnitsRadii : nullptr, aRefBox,
234 aAppUnitsPerPixel, aPathBuilder);
237 /* static */
238 already_AddRefed<gfx::Path> ShapeUtils::BuildInsetPath(
239 const nsRect& aInsetRect, const nscoord aRadii[8], const nsRect& aRefBox,
240 nscoord aAppUnitsPerPixel, gfx::PathBuilder* aPathBuilder) {
241 const gfx::Rect insetRectPixels = NSRectToRect(aInsetRect, aAppUnitsPerPixel);
242 if (aRadii) {
243 gfx::RectCornerRadii corners;
244 nsCSSRendering::ComputePixelRadii(aRadii, aAppUnitsPerPixel, &corners);
246 AppendRoundedRectToPath(aPathBuilder, insetRectPixels, corners, true);
247 } else {
248 AppendRectToPath(aPathBuilder, insetRectPixels, true);
250 return aPathBuilder->Finish();
253 } // namespace mozilla