Bug 1867925 - Mark some storage-access-api tests as intermittent after wpt-sync....
[gecko.git] / layout / svg / CSSClipPathInstance.cpp
blob77bde2bb543405e90769726fdd53bad0246810af
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 // Main header first:
8 #include "CSSClipPathInstance.h"
10 #include "mozilla/dom/SVGElement.h"
11 #include "mozilla/dom/SVGPathData.h"
12 #include "mozilla/gfx/2D.h"
13 #include "mozilla/gfx/PathHelpers.h"
14 #include "mozilla/ShapeUtils.h"
15 #include "mozilla/SVGUtils.h"
16 #include "gfx2DGlue.h"
17 #include "gfxContext.h"
18 #include "gfxPlatform.h"
19 #include "nsIFrame.h"
20 #include "nsLayoutUtils.h"
22 using namespace mozilla::dom;
23 using namespace mozilla::gfx;
25 namespace mozilla {
27 /* static*/
28 void CSSClipPathInstance::ApplyBasicShapeOrPathClip(
29 gfxContext& aContext, nsIFrame* aFrame, const gfxMatrix& aTransform) {
30 RefPtr<Path> path =
31 CreateClipPathForFrame(aContext.GetDrawTarget(), aFrame, aTransform);
32 if (!path) {
33 // This behavior matches |SVGClipPathFrame::ApplyClipPath()|.
34 // https://www.w3.org/TR/css-masking-1/#ClipPathElement:
35 // "An empty clipping path will completely clip away the element that had
36 // the clip-path property applied."
37 aContext.Clip(Rect());
38 return;
40 aContext.Clip(path);
43 /* static*/
44 RefPtr<Path> CSSClipPathInstance::CreateClipPathForFrame(
45 gfx::DrawTarget* aDt, nsIFrame* aFrame, const gfxMatrix& aTransform) {
46 const auto& clipPathStyle = aFrame->StyleSVGReset()->mClipPath;
47 MOZ_ASSERT(clipPathStyle.IsShape() || clipPathStyle.IsBox(),
48 "This is used with basic-shape, and geometry-box only");
50 CSSClipPathInstance instance(aFrame, clipPathStyle);
52 return instance.CreateClipPath(aDt, aTransform);
55 /* static*/
56 bool CSSClipPathInstance::HitTestBasicShapeOrPathClip(nsIFrame* aFrame,
57 const gfxPoint& aPoint) {
58 const auto& clipPathStyle = aFrame->StyleSVGReset()->mClipPath;
59 MOZ_ASSERT(!clipPathStyle.IsNone(), "unexpected none value");
60 MOZ_ASSERT(!clipPathStyle.IsUrl(), "unexpected url value");
62 CSSClipPathInstance instance(aFrame, clipPathStyle);
64 RefPtr<DrawTarget> drawTarget =
65 gfxPlatform::GetPlatform()->ScreenReferenceDrawTarget();
66 RefPtr<Path> path = instance.CreateClipPath(
67 drawTarget, SVGUtils::GetCSSPxToDevPxMatrix(aFrame));
68 float pixelRatio = float(AppUnitsPerCSSPixel()) /
69 aFrame->PresContext()->AppUnitsPerDevPixel();
70 return path && path->ContainsPoint(ToPoint(aPoint) * pixelRatio, Matrix());
73 /* static */
74 Maybe<Rect> CSSClipPathInstance::GetBoundingRectForBasicShapeOrPathClip(
75 nsIFrame* aFrame, const StyleClipPath& aClipPathStyle) {
76 MOZ_ASSERT(aClipPathStyle.IsShape() || aClipPathStyle.IsBox());
78 CSSClipPathInstance instance(aFrame, aClipPathStyle);
80 RefPtr<DrawTarget> drawTarget =
81 gfxPlatform::GetPlatform()->ScreenReferenceDrawTarget();
82 RefPtr<Path> path = instance.CreateClipPath(
83 drawTarget, SVGUtils::GetCSSPxToDevPxMatrix(aFrame));
84 return path ? Some(path->GetBounds()) : Nothing();
87 already_AddRefed<Path> CSSClipPathInstance::CreateClipPath(
88 DrawTarget* aDrawTarget, const gfxMatrix& aTransform) {
89 nscoord appUnitsPerDevPixel =
90 mTargetFrame->PresContext()->AppUnitsPerDevPixel();
92 nsRect r = nsLayoutUtils::ComputeClipPathGeometryBox(
93 mTargetFrame, mClipPathStyle.IsBox() ? mClipPathStyle.AsBox()
94 : mClipPathStyle.AsShape()._1);
96 gfxRect rr(r.x, r.y, r.width, r.height);
97 rr.Scale(1.0 / AppUnitsPerCSSPixel());
98 rr = aTransform.TransformRect(rr);
99 rr.Scale(appUnitsPerDevPixel);
100 rr.Round();
102 r = nsRect(int(rr.x), int(rr.y), int(rr.width), int(rr.height));
104 if (mClipPathStyle.IsBox()) {
105 RefPtr<PathBuilder> builder = aDrawTarget->CreatePathBuilder();
106 AppendRectToPath(builder, NSRectToRect(r, appUnitsPerDevPixel), true);
107 return builder->Finish();
110 MOZ_ASSERT(mClipPathStyle.IsShape());
112 r = ToAppUnits(r.ToNearestPixels(appUnitsPerDevPixel), appUnitsPerDevPixel);
114 const auto& basicShape = *mClipPathStyle.AsShape()._0;
115 switch (basicShape.tag) {
116 case StyleBasicShape::Tag::Circle:
117 return CreateClipPathCircle(aDrawTarget, r);
118 case StyleBasicShape::Tag::Ellipse:
119 return CreateClipPathEllipse(aDrawTarget, r);
120 case StyleBasicShape::Tag::Polygon:
121 return CreateClipPathPolygon(aDrawTarget, r);
122 case StyleBasicShape::Tag::Rect:
123 return CreateClipPathInset(aDrawTarget, r);
124 case StyleBasicShape::Tag::Path:
125 return CreateClipPathPath(aDrawTarget, r);
126 default:
127 MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("Unexpected shape type");
129 // Return an empty Path:
130 RefPtr<PathBuilder> builder = aDrawTarget->CreatePathBuilder();
131 return builder->Finish();
134 already_AddRefed<Path> CSSClipPathInstance::CreateClipPathCircle(
135 DrawTarget* aDrawTarget, const nsRect& aRefBox) {
136 const StyleBasicShape& shape = *mClipPathStyle.AsShape()._0;
137 const nsPoint& center =
138 ShapeUtils::ComputeCircleOrEllipseCenter(shape, aRefBox);
139 RefPtr<PathBuilder> builder = aDrawTarget->CreatePathBuilder();
140 return ShapeUtils::BuildCirclePath(
141 shape, aRefBox, center,
142 mTargetFrame->PresContext()->AppUnitsPerDevPixel(), builder);
145 already_AddRefed<Path> CSSClipPathInstance::CreateClipPathEllipse(
146 DrawTarget* aDrawTarget, const nsRect& aRefBox) {
147 const StyleBasicShape& shape = *mClipPathStyle.AsShape()._0;
148 const nsPoint& center =
149 ShapeUtils::ComputeCircleOrEllipseCenter(shape, aRefBox);
150 RefPtr<PathBuilder> builder = aDrawTarget->CreatePathBuilder();
151 return ShapeUtils::BuildEllipsePath(
152 shape, aRefBox, center,
153 mTargetFrame->PresContext()->AppUnitsPerDevPixel(), builder);
156 already_AddRefed<Path> CSSClipPathInstance::CreateClipPathPolygon(
157 DrawTarget* aDrawTarget, const nsRect& aRefBox) {
158 const auto& basicShape = *mClipPathStyle.AsShape()._0;
159 auto fillRule = basicShape.AsPolygon().fill == StyleFillRule::Nonzero
160 ? FillRule::FILL_WINDING
161 : FillRule::FILL_EVEN_ODD;
162 RefPtr<PathBuilder> builder = aDrawTarget->CreatePathBuilder(fillRule);
163 return ShapeUtils::BuildPolygonPath(
164 basicShape, aRefBox, mTargetFrame->PresContext()->AppUnitsPerDevPixel(),
165 builder);
168 already_AddRefed<Path> CSSClipPathInstance::CreateClipPathInset(
169 DrawTarget* aDrawTarget, const nsRect& aRefBox) {
170 RefPtr<PathBuilder> builder = aDrawTarget->CreatePathBuilder();
171 return ShapeUtils::BuildInsetPath(
172 *mClipPathStyle.AsShape()._0, aRefBox,
173 mTargetFrame->PresContext()->AppUnitsPerDevPixel(), builder);
176 already_AddRefed<Path> CSSClipPathInstance::CreateClipPathPath(
177 DrawTarget* aDrawTarget, const nsRect& aRefBox) {
178 const auto& path = mClipPathStyle.AsShape()._0->AsPath();
180 RefPtr<PathBuilder> builder = aDrawTarget->CreatePathBuilder(
181 path.fill == StyleFillRule::Nonzero ? FillRule::FILL_WINDING
182 : FillRule::FILL_EVEN_ODD);
183 nscoord appUnitsPerDevPixel =
184 mTargetFrame->PresContext()->AppUnitsPerDevPixel();
185 float scale = float(AppUnitsPerCSSPixel()) / appUnitsPerDevPixel;
186 Point offset = Point(aRefBox.x, aRefBox.y) / appUnitsPerDevPixel;
188 return SVGPathData::BuildPath(path.path._0.AsSpan(), builder,
189 StyleStrokeLinecap::Butt, 0.0, offset, scale);
192 } // namespace mozilla