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/. */
6
7 #include "mozilla/ShapeUtils.h"
8
9 #include <cstdlib>
10
11 #include "nsCSSRendering.h"
12 #include "nsMargin.h"
13 #include "nsStyleCoord.h"
14 #include "nsStyleStruct.h"
15 #include "SVGContentUtils.h"
16
17 namespace mozilla {
18
ComputeShapeRadius(const StyleShapeRadius aType,const nscoord aCenter,const nscoord aPosMin,const nscoord aPosMax)19 nscoord ShapeUtils::ComputeShapeRadius(const StyleShapeRadius aType,
20 const nscoord aCenter,
21 const nscoord aPosMin,
22 const nscoord aPosMax) {
23 nscoord dist1 = std::abs(aPosMin - aCenter);
24 nscoord dist2 = std::abs(aPosMax - aCenter);
25 nscoord length = 0;
26 switch (aType) {
27 case StyleShapeRadius::FarthestSide:
28 length = dist1 > dist2 ? dist1 : dist2;
29 break;
30 case StyleShapeRadius::ClosestSide:
31 length = dist1 > dist2 ? dist2 : dist1;
32 break;
33 }
34 return length;
35 }
36
ComputeCircleOrEllipseCenter(const UniquePtr<StyleBasicShape> & aBasicShape,const nsRect & aRefBox)37 nsPoint ShapeUtils::ComputeCircleOrEllipseCenter(
38 const UniquePtr<StyleBasicShape>& aBasicShape, const nsRect& aRefBox) {
39 MOZ_ASSERT(aBasicShape->GetShapeType() == StyleBasicShapeType::Circle ||
40 aBasicShape->GetShapeType() == StyleBasicShapeType::Ellipse,
41 "The basic shape must be circle() or ellipse!");
42
43 nsPoint topLeft, anchor;
44 nsSize size(aRefBox.Size());
45 nsImageRenderer::ComputeObjectAnchorPoint(aBasicShape->GetPosition(), size,
46 size, &topLeft, &anchor);
47 return anchor + aRefBox.TopLeft();
48 }
49
ComputeCircleRadius(const UniquePtr<StyleBasicShape> & aBasicShape,const nsPoint & aCenter,const nsRect & aRefBox)50 nscoord ShapeUtils::ComputeCircleRadius(
51 const UniquePtr<StyleBasicShape>& aBasicShape, const nsPoint& aCenter,
52 const nsRect& aRefBox) {
53 MOZ_ASSERT(aBasicShape->GetShapeType() == StyleBasicShapeType::Circle,
54 "The basic shape must be circle()!");
55
56 const nsTArray<nsStyleCoord>& coords = aBasicShape->Coordinates();
57 MOZ_ASSERT(coords.Length() == 1, "wrong number of arguments");
58 nscoord r = 0;
59 if (coords[0].GetUnit() == eStyleUnit_Enumerated) {
60 const auto styleShapeRadius = coords[0].GetEnumValue<StyleShapeRadius>();
61 nscoord horizontal = ComputeShapeRadius(styleShapeRadius, aCenter.x,
62 aRefBox.x, aRefBox.XMost());
63 nscoord vertical = ComputeShapeRadius(styleShapeRadius, aCenter.y,
64 aRefBox.y, aRefBox.YMost());
65 r = styleShapeRadius == StyleShapeRadius::FarthestSide
66 ? std::max(horizontal, vertical)
67 : std::min(horizontal, vertical);
68 } else {
69 // We resolve percent <shape-radius> value for circle() as defined here:
70 // https://drafts.csswg.org/css-shapes/#funcdef-circle
71 double referenceLength = SVGContentUtils::ComputeNormalizedHypotenuse(
72 aRefBox.width, aRefBox.height);
73 r = coords[0].ComputeCoordPercentCalc(NSToCoordRound(referenceLength));
74 }
75 return r;
76 }
77
ComputeEllipseRadii(const UniquePtr<StyleBasicShape> & aBasicShape,const nsPoint & aCenter,const nsRect & aRefBox)78 nsSize ShapeUtils::ComputeEllipseRadii(
79 const UniquePtr<StyleBasicShape>& aBasicShape, const nsPoint& aCenter,
80 const nsRect& aRefBox) {
81 MOZ_ASSERT(aBasicShape->GetShapeType() == StyleBasicShapeType::Ellipse,
82 "The basic shape must be ellipse()!");
83
84 const nsTArray<nsStyleCoord>& coords = aBasicShape->Coordinates();
85 MOZ_ASSERT(coords.Length() == 2, "wrong number of arguments");
86 nsSize radii;
87
88 if (coords[0].GetUnit() == eStyleUnit_Enumerated) {
89 const StyleShapeRadius radiusX = coords[0].GetEnumValue<StyleShapeRadius>();
90 radii.width =
91 ComputeShapeRadius(radiusX, aCenter.x, aRefBox.x, aRefBox.XMost());
92 } else {
93 radii.width = coords[0].ComputeCoordPercentCalc(aRefBox.width);
94 }
95
96 if (coords[1].GetUnit() == eStyleUnit_Enumerated) {
97 const StyleShapeRadius radiusY = coords[1].GetEnumValue<StyleShapeRadius>();
98 radii.height =
99 ComputeShapeRadius(radiusY, aCenter.y, aRefBox.y, aRefBox.YMost());
100 } else {
101 radii.height = coords[1].ComputeCoordPercentCalc(aRefBox.height);
102 }
103
104 return radii;
105 }
106
ComputeInsetRect(const UniquePtr<StyleBasicShape> & aBasicShape,const nsRect & aRefBox)107 /* static */ nsRect ShapeUtils::ComputeInsetRect(
108 const UniquePtr<StyleBasicShape>& aBasicShape, const nsRect& aRefBox) {
109 MOZ_ASSERT(aBasicShape->GetShapeType() == StyleBasicShapeType::Inset,
110 "The basic shape must be inset()!");
111
112 const nsTArray<nsStyleCoord>& coords = aBasicShape->Coordinates();
113 MOZ_ASSERT(coords.Length() == 4, "wrong number of arguments");
114
115 nsMargin inset(coords[0].ComputeCoordPercentCalc(aRefBox.height),
116 coords[1].ComputeCoordPercentCalc(aRefBox.width),
117 coords[2].ComputeCoordPercentCalc(aRefBox.height),
118 coords[3].ComputeCoordPercentCalc(aRefBox.width));
119
120 nsRect insetRect(aRefBox);
121 insetRect.Deflate(inset);
122
123 return insetRect;
124 }
125
ComputeInsetRadii(const UniquePtr<StyleBasicShape> & aBasicShape,const nsRect & aInsetRect,const nsRect & aRefBox,nscoord aRadii[8])126 /* static */ bool ShapeUtils::ComputeInsetRadii(
127 const UniquePtr<StyleBasicShape>& aBasicShape, const nsRect& aInsetRect,
128 const nsRect& aRefBox, nscoord aRadii[8]) {
129 const nsStyleCorners& radius = aBasicShape->GetRadius();
130 return nsIFrame::ComputeBorderRadii(radius, aInsetRect.Size(), aRefBox.Size(),
131 Sides(), aRadii);
132 }
133
ComputePolygonVertices(const UniquePtr<StyleBasicShape> & aBasicShape,const nsRect & aRefBox)134 /* static */ nsTArray<nsPoint> ShapeUtils::ComputePolygonVertices(
135 const UniquePtr<StyleBasicShape>& aBasicShape, const nsRect& aRefBox) {
136 MOZ_ASSERT(aBasicShape->GetShapeType() == StyleBasicShapeType::Polygon,
137 "The basic shape must be polygon()!");
138
139 const nsTArray<nsStyleCoord>& coords = aBasicShape->Coordinates();
140 MOZ_ASSERT(coords.Length() % 2 == 0 && coords.Length() >= 2,
141 "Wrong number of arguments!");
142
143 nsTArray<nsPoint> vertices(coords.Length() / 2);
144 for (size_t i = 0; i + 1 < coords.Length(); i += 2) {
145 vertices.AppendElement(
146 nsPoint(coords[i].ComputeCoordPercentCalc(aRefBox.width),
147 coords[i + 1].ComputeCoordPercentCalc(aRefBox.height)) +
148 aRefBox.TopLeft());
149 }
150 return vertices;
151 }
152
153 } // namespace mozilla
154