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