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/. */
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"
20 #include "nsLayoutUtils.h"
22 using namespace mozilla::dom
;
23 using namespace mozilla::gfx
;
28 void CSSClipPathInstance::ApplyBasicShapeOrPathClip(
29 gfxContext
& aContext
, nsIFrame
* aFrame
, const gfxMatrix
& aTransform
) {
31 CreateClipPathForFrame(aContext
.GetDrawTarget(), aFrame
, aTransform
);
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());
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
);
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());
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
);
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
);
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(),
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