Line data Source code
1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* This Source Code Form is subject to the terms of the Mozilla Public
3 : * License, v. 2.0. If a copy of the MPL was not distributed with this
4 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 :
6 : // Main header first:
7 : #include "nsCSSClipPathInstance.h"
8 :
9 : #include "gfx2DGlue.h"
10 : #include "gfxContext.h"
11 : #include "gfxPlatform.h"
12 : #include "mozilla/dom/SVGSVGElement.h"
13 : #include "mozilla/gfx/2D.h"
14 : #include "mozilla/gfx/PathHelpers.h"
15 : #include "mozilla/ShapeUtils.h"
16 : #include "nsCSSRendering.h"
17 : #include "nsIFrame.h"
18 : #include "nsLayoutUtils.h"
19 : #include "nsRuleNode.h"
20 : #include "nsSVGElement.h"
21 : #include "nsSVGUtils.h"
22 : #include "nsSVGViewBox.h"
23 :
24 : using namespace mozilla;
25 : using namespace mozilla::dom;
26 : using namespace mozilla::gfx;
27 :
28 : /* static*/ void
29 0 : nsCSSClipPathInstance::ApplyBasicShapeClip(gfxContext& aContext,
30 : nsIFrame* aFrame)
31 : {
32 0 : auto& clipPathStyle = aFrame->StyleSVGReset()->mClipPath;
33 :
34 : #ifdef DEBUG
35 0 : StyleShapeSourceType type = clipPathStyle.GetType();
36 0 : MOZ_ASSERT(type == StyleShapeSourceType::Shape ||
37 : type == StyleShapeSourceType::Box,
38 : "This function is used with basic-shape and geometry-box only.");
39 : #endif
40 :
41 0 : nsCSSClipPathInstance instance(aFrame, clipPathStyle);
42 :
43 0 : aContext.NewPath();
44 0 : RefPtr<Path> path = instance.CreateClipPath(aContext.GetDrawTarget());
45 0 : aContext.SetPath(path);
46 0 : aContext.Clip();
47 0 : }
48 :
49 : /* static*/ bool
50 0 : nsCSSClipPathInstance::HitTestBasicShapeClip(nsIFrame* aFrame,
51 : const gfxPoint& aPoint)
52 : {
53 0 : auto& clipPathStyle = aFrame->StyleSVGReset()->mClipPath;
54 0 : StyleShapeSourceType type = clipPathStyle.GetType();
55 0 : MOZ_ASSERT(type != StyleShapeSourceType::None, "unexpected none value");
56 : // In the future nsCSSClipPathInstance may handle <clipPath> references as
57 : // well. For the time being return early.
58 0 : if (type == StyleShapeSourceType::URL) {
59 0 : return false;
60 : }
61 :
62 0 : nsCSSClipPathInstance instance(aFrame, clipPathStyle);
63 :
64 : RefPtr<DrawTarget> drawTarget =
65 0 : gfxPlatform::GetPlatform()->ScreenReferenceDrawTarget();
66 0 : RefPtr<Path> path = instance.CreateClipPath(drawTarget);
67 0 : float pixelRatio = float(nsPresContext::AppUnitsPerCSSPixel()) /
68 0 : aFrame->PresContext()->AppUnitsPerDevPixel();
69 0 : return path->ContainsPoint(ToPoint(aPoint) * pixelRatio, Matrix());
70 : }
71 :
72 : already_AddRefed<Path>
73 0 : nsCSSClipPathInstance::CreateClipPath(DrawTarget* aDrawTarget)
74 : {
75 : nsRect r =
76 : nsLayoutUtils::ComputeGeometryBox(mTargetFrame,
77 0 : mClipPathStyle.GetReferenceBox());
78 :
79 0 : if (mClipPathStyle.GetType() != StyleShapeSourceType::Shape) {
80 : // TODO Clip to border-radius/reference box if no shape
81 : // was specified.
82 0 : RefPtr<PathBuilder> builder = aDrawTarget->CreatePathBuilder();
83 0 : return builder->Finish();
84 : }
85 :
86 : nscoord appUnitsPerDevPixel =
87 0 : mTargetFrame->PresContext()->AppUnitsPerDevPixel();
88 0 : r = ToAppUnits(r.ToNearestPixels(appUnitsPerDevPixel), appUnitsPerDevPixel);
89 :
90 0 : StyleBasicShape* basicShape = mClipPathStyle.GetBasicShape();
91 0 : switch (basicShape->GetShapeType()) {
92 : case StyleBasicShapeType::Circle:
93 0 : return CreateClipPathCircle(aDrawTarget, r);
94 : case StyleBasicShapeType::Ellipse:
95 0 : return CreateClipPathEllipse(aDrawTarget, r);
96 : case StyleBasicShapeType::Polygon:
97 0 : return CreateClipPathPolygon(aDrawTarget, r);
98 : case StyleBasicShapeType::Inset:
99 0 : return CreateClipPathInset(aDrawTarget, r);
100 : break;
101 : default:
102 0 : MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("Unexpected shape type");
103 : }
104 : // Return an empty Path:
105 : RefPtr<PathBuilder> builder = aDrawTarget->CreatePathBuilder();
106 : return builder->Finish();
107 : }
108 :
109 : already_AddRefed<Path>
110 0 : nsCSSClipPathInstance::CreateClipPathCircle(DrawTarget* aDrawTarget,
111 : const nsRect& aRefBox)
112 : {
113 0 : StyleBasicShape* basicShape = mClipPathStyle.GetBasicShape();
114 :
115 0 : RefPtr<PathBuilder> builder = aDrawTarget->CreatePathBuilder();
116 :
117 : nsPoint center =
118 0 : ShapeUtils::ComputeCircleOrEllipseCenter(basicShape, aRefBox);
119 0 : nscoord r = ShapeUtils::ComputeCircleRadius(basicShape, center, aRefBox);
120 : nscoord appUnitsPerDevPixel =
121 0 : mTargetFrame->PresContext()->AppUnitsPerDevPixel();
122 0 : builder->Arc(Point(center.x, center.y) / appUnitsPerDevPixel,
123 0 : r / appUnitsPerDevPixel,
124 0 : 0, Float(2 * M_PI));
125 0 : builder->Close();
126 0 : return builder->Finish();
127 : }
128 :
129 : already_AddRefed<Path>
130 0 : nsCSSClipPathInstance::CreateClipPathEllipse(DrawTarget* aDrawTarget,
131 : const nsRect& aRefBox)
132 : {
133 0 : StyleBasicShape* basicShape = mClipPathStyle.GetBasicShape();
134 :
135 0 : RefPtr<PathBuilder> builder = aDrawTarget->CreatePathBuilder();
136 :
137 : nsPoint center =
138 0 : ShapeUtils::ComputeCircleOrEllipseCenter(basicShape, aRefBox);
139 0 : nsSize radii = ShapeUtils::ComputeEllipseRadii(basicShape, center, aRefBox);
140 : nscoord appUnitsPerDevPixel =
141 0 : mTargetFrame->PresContext()->AppUnitsPerDevPixel();
142 0 : EllipseToBezier(builder.get(),
143 0 : Point(center.x, center.y) / appUnitsPerDevPixel,
144 0 : Size(radii.width, radii.height) / appUnitsPerDevPixel);
145 0 : builder->Close();
146 0 : return builder->Finish();
147 : }
148 :
149 : already_AddRefed<Path>
150 0 : nsCSSClipPathInstance::CreateClipPathPolygon(DrawTarget* aDrawTarget,
151 : const nsRect& aRefBox)
152 : {
153 0 : StyleBasicShape* basicShape = mClipPathStyle.GetBasicShape();
154 0 : FillRule fillRule = basicShape->GetFillRule() == StyleFillRule::Nonzero ?
155 0 : FillRule::FILL_WINDING : FillRule::FILL_EVEN_ODD;
156 0 : RefPtr<PathBuilder> builder = aDrawTarget->CreatePathBuilder(fillRule);
157 :
158 : nsTArray<nsPoint> vertices =
159 0 : ShapeUtils::ComputePolygonVertices(basicShape, aRefBox);
160 0 : if (vertices.IsEmpty()) {
161 0 : MOZ_ASSERT_UNREACHABLE(
162 : "ComputePolygonVertices() should've given us some vertices!");
163 : } else {
164 : nscoord appUnitsPerDevPixel =
165 0 : mTargetFrame->PresContext()->AppUnitsPerDevPixel();
166 0 : builder->MoveTo(NSPointToPoint(vertices[0], appUnitsPerDevPixel));
167 0 : for (size_t i = 1; i < vertices.Length(); ++i) {
168 0 : builder->LineTo(NSPointToPoint(vertices[i], appUnitsPerDevPixel));
169 : }
170 : }
171 0 : builder->Close();
172 0 : return builder->Finish();
173 : }
174 :
175 : already_AddRefed<Path>
176 0 : nsCSSClipPathInstance::CreateClipPathInset(DrawTarget* aDrawTarget,
177 : const nsRect& aRefBox)
178 : {
179 0 : StyleBasicShape* basicShape = mClipPathStyle.GetBasicShape();
180 :
181 0 : RefPtr<PathBuilder> builder = aDrawTarget->CreatePathBuilder();
182 :
183 : nscoord appUnitsPerDevPixel =
184 0 : mTargetFrame->PresContext()->AppUnitsPerDevPixel();
185 :
186 0 : nsRect insetRect = ShapeUtils::ComputeInsetRect(basicShape, aRefBox);
187 0 : const Rect insetRectPixels = NSRectToRect(insetRect, appUnitsPerDevPixel);
188 : nscoord appUnitsRadii[8];
189 :
190 0 : if (ShapeUtils::ComputeInsetRadii(basicShape, insetRect, aRefBox,
191 : appUnitsRadii)) {
192 0 : RectCornerRadii corners;
193 : nsCSSRendering::ComputePixelRadii(appUnitsRadii,
194 0 : appUnitsPerDevPixel, &corners);
195 :
196 0 : AppendRoundedRectToPath(builder, insetRectPixels, corners, true);
197 : } else {
198 0 : AppendRectToPath(builder, insetRectPixels, true);
199 : }
200 0 : return builder->Finish();
201 : }
|