Bug 1910248 - Remove nsIFrame::LayerIsPrerenderedDataKey r=layout-reviewers,TYLin
[gecko.git] / layout / svg / CSSClipPathInstance.cpp
bloba8e0306d3edeaf10837f6643280aa68b2bf1d1fe
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 float(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::PathOrShape:
125 return basicShape.AsPathOrShape().IsPath()
126 ? CreateClipPathPath(aDrawTarget, r)
127 : CreateClipPathShape(aDrawTarget, r);
128 default:
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(),
167 builder);
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();
187 const Point offset =
188 LayoutDevicePoint::FromAppUnits(aRefBox.TopLeft(), appUnitsPerDevPixel)
189 .ToUnknownPoint();
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,
194 scale);
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());
207 const Point offset =
208 LayoutDevicePoint::FromAppUnits(aRefBox.TopLeft(), appUnitsPerDevPixel)
209 .ToUnknownPoint();
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,
214 scale);
217 } // namespace mozilla