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 float(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::PathOrShape
:
125 return basicShape
.AsPathOrShape().IsPath()
126 ? CreateClipPathPath(aDrawTarget
, r
)
127 : CreateClipPathShape(aDrawTarget
, r
);
129 MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("Unexpected shape type");
131 // Return an empty Path:
132 RefPtr
<PathBuilder
> builder
= aDrawTarget
->CreatePathBuilder();
133 return builder
->Finish();
136 already_AddRefed
<Path
> CSSClipPathInstance::CreateClipPathCircle(
137 DrawTarget
* aDrawTarget
, const nsRect
& aRefBox
) {
138 const StyleBasicShape
& shape
= *mClipPathStyle
.AsShape()._0
;
139 const nsPoint
& center
=
140 ShapeUtils::ComputeCircleOrEllipseCenter(shape
, aRefBox
);
141 RefPtr
<PathBuilder
> builder
= aDrawTarget
->CreatePathBuilder();
142 return ShapeUtils::BuildCirclePath(
143 shape
, aRefBox
, center
,
144 mTargetFrame
->PresContext()->AppUnitsPerDevPixel(), builder
);
147 already_AddRefed
<Path
> CSSClipPathInstance::CreateClipPathEllipse(
148 DrawTarget
* aDrawTarget
, const nsRect
& aRefBox
) {
149 const StyleBasicShape
& shape
= *mClipPathStyle
.AsShape()._0
;
150 const nsPoint
& center
=
151 ShapeUtils::ComputeCircleOrEllipseCenter(shape
, aRefBox
);
152 RefPtr
<PathBuilder
> builder
= aDrawTarget
->CreatePathBuilder();
153 return ShapeUtils::BuildEllipsePath(
154 shape
, aRefBox
, center
,
155 mTargetFrame
->PresContext()->AppUnitsPerDevPixel(), builder
);
158 already_AddRefed
<Path
> CSSClipPathInstance::CreateClipPathPolygon(
159 DrawTarget
* aDrawTarget
, const nsRect
& aRefBox
) {
160 const auto& basicShape
= *mClipPathStyle
.AsShape()._0
;
161 auto fillRule
= basicShape
.AsPolygon().fill
== StyleFillRule::Nonzero
162 ? FillRule::FILL_WINDING
163 : FillRule::FILL_EVEN_ODD
;
164 RefPtr
<PathBuilder
> builder
= aDrawTarget
->CreatePathBuilder(fillRule
);
165 return ShapeUtils::BuildPolygonPath(
166 basicShape
, aRefBox
, mTargetFrame
->PresContext()->AppUnitsPerDevPixel(),
170 already_AddRefed
<Path
> CSSClipPathInstance::CreateClipPathInset(
171 DrawTarget
* aDrawTarget
, const nsRect
& aRefBox
) {
172 RefPtr
<PathBuilder
> builder
= aDrawTarget
->CreatePathBuilder();
173 return ShapeUtils::BuildInsetPath(
174 *mClipPathStyle
.AsShape()._0
, aRefBox
,
175 mTargetFrame
->PresContext()->AppUnitsPerDevPixel(), builder
);
178 already_AddRefed
<Path
> CSSClipPathInstance::CreateClipPathPath(
179 DrawTarget
* aDrawTarget
, const nsRect
& aRefBox
) {
180 const auto& path
= mClipPathStyle
.AsShape()._0
->AsPathOrShape().AsPath();
182 RefPtr
<PathBuilder
> builder
= aDrawTarget
->CreatePathBuilder(
183 path
.fill
== StyleFillRule::Nonzero
? FillRule::FILL_WINDING
184 : FillRule::FILL_EVEN_ODD
);
185 const nscoord appUnitsPerDevPixel
=
186 mTargetFrame
->PresContext()->AppUnitsPerDevPixel();
188 LayoutDevicePoint::FromAppUnits(aRefBox
.TopLeft(), appUnitsPerDevPixel
)
190 const float scale
= mTargetFrame
->Style()->EffectiveZoom().Zoom(
191 float(AppUnitsPerCSSPixel()) / float(appUnitsPerDevPixel
));
192 return SVGPathData::BuildPath(path
.path
._0
.AsSpan(), builder
,
193 StyleStrokeLinecap::Butt
, 0.0, {}, offset
,
197 already_AddRefed
<Path
> CSSClipPathInstance::CreateClipPathShape(
198 DrawTarget
* aDrawTarget
, const nsRect
& aRefBox
) {
199 const auto& shape
= mClipPathStyle
.AsShape()._0
->AsPathOrShape().AsShape();
201 RefPtr
<PathBuilder
> builder
= aDrawTarget
->CreatePathBuilder(
202 shape
.fill
== StyleFillRule::Nonzero
? FillRule::FILL_WINDING
203 : FillRule::FILL_EVEN_ODD
);
204 const nscoord appUnitsPerDevPixel
=
205 mTargetFrame
->PresContext()->AppUnitsPerDevPixel();
206 const CSSSize basis
= CSSSize::FromAppUnits(aRefBox
.Size());
208 LayoutDevicePoint::FromAppUnits(aRefBox
.TopLeft(), appUnitsPerDevPixel
)
210 const float scale
= mTargetFrame
->Style()->EffectiveZoom().Zoom(
211 float(AppUnitsPerCSSPixel()) / float(appUnitsPerDevPixel
));
212 return SVGPathData::BuildPath(shape
.commands
.AsSpan(), builder
,
213 StyleStrokeLinecap::Butt
, 0.0, basis
, offset
,
217 } // namespace mozilla