Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/layout/svg/nsCSSClipPathInstance.cpp
Line
Count
Source (jump to first uncovered line)
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/. */
6
7
// Main header first:
8
#include "nsCSSClipPathInstance.h"
9
10
#include "gfx2DGlue.h"
11
#include "gfxContext.h"
12
#include "gfxPlatform.h"
13
#include "mozilla/dom/SVGPathData.h"
14
#include "mozilla/gfx/2D.h"
15
#include "mozilla/gfx/PathHelpers.h"
16
#include "mozilla/ShapeUtils.h"
17
#include "nsCSSRendering.h"
18
#include "nsIFrame.h"
19
#include "nsLayoutUtils.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
nsCSSClipPathInstance::ApplyBasicShapeOrPathClip(gfxContext& aContext,
30
                                                 nsIFrame* aFrame)
31
0
{
32
0
  auto& clipPathStyle = aFrame->StyleSVGReset()->mClipPath;
33
0
34
#ifdef DEBUG
35
  StyleShapeSourceType type = clipPathStyle.GetType();
36
  MOZ_ASSERT(type == StyleShapeSourceType::Shape ||
37
             type == StyleShapeSourceType::Box ||
38
             type == StyleShapeSourceType::Path,
39
             "This is used with basic-shape, geometry-box, and path() only");
40
#endif
41
42
0
  nsCSSClipPathInstance instance(aFrame, clipPathStyle);
43
0
44
0
  aContext.NewPath();
45
0
  RefPtr<Path> path = instance.CreateClipPath(aContext.GetDrawTarget());
46
0
  aContext.SetPath(path);
47
0
  aContext.Clip();
48
0
}
49
50
/* static*/ bool
51
nsCSSClipPathInstance::HitTestBasicShapeOrPathClip(nsIFrame* aFrame,
52
                                                   const gfxPoint& aPoint)
53
0
{
54
0
  auto& clipPathStyle = aFrame->StyleSVGReset()->mClipPath;
55
0
  StyleShapeSourceType type = clipPathStyle.GetType();
56
0
  MOZ_ASSERT(type != StyleShapeSourceType::None, "unexpected none value");
57
0
  // In the future nsCSSClipPathInstance may handle <clipPath> references as
58
0
  // well. For the time being return early.
59
0
  if (type == StyleShapeSourceType::URL) {
60
0
    return false;
61
0
  }
62
0
63
0
  nsCSSClipPathInstance instance(aFrame, clipPathStyle);
64
0
65
0
  RefPtr<DrawTarget> drawTarget =
66
0
    gfxPlatform::GetPlatform()->ScreenReferenceDrawTarget();
67
0
  RefPtr<Path> path = instance.CreateClipPath(drawTarget);
68
0
  float pixelRatio = float(AppUnitsPerCSSPixel()) /
69
0
                     aFrame->PresContext()->AppUnitsPerDevPixel();
70
0
  return path->ContainsPoint(ToPoint(aPoint) * pixelRatio, Matrix());
71
0
}
72
73
/* static */ Rect
74
nsCSSClipPathInstance::GetBoundingRectForBasicShapeOrPathClip(
75
  nsIFrame* aFrame,
76
  const StyleShapeSource& aClipPathStyle)
77
0
{
78
0
  MOZ_ASSERT(aClipPathStyle.GetType() == StyleShapeSourceType::Shape ||
79
0
             aClipPathStyle.GetType() == StyleShapeSourceType::Box ||
80
0
             aClipPathStyle.GetType() == StyleShapeSourceType::Path);
81
0
82
0
  nsCSSClipPathInstance instance(aFrame, aClipPathStyle);
83
0
84
0
  RefPtr<DrawTarget> drawTarget =
85
0
    gfxPlatform::GetPlatform()->ScreenReferenceDrawTarget();
86
0
  RefPtr<Path> path = instance.CreateClipPath(drawTarget);
87
0
  return path->GetBounds();
88
0
}
89
90
already_AddRefed<Path>
91
nsCSSClipPathInstance::CreateClipPath(DrawTarget* aDrawTarget)
92
0
{
93
0
  if (mClipPathStyle.GetType() == StyleShapeSourceType::Path) {
94
0
    return CreateClipPathPath(aDrawTarget);
95
0
  }
96
0
97
0
  nsRect r =
98
0
    nsLayoutUtils::ComputeGeometryBox(mTargetFrame,
99
0
                                      mClipPathStyle.GetReferenceBox());
100
0
101
0
  if (mClipPathStyle.GetType() != StyleShapeSourceType::Shape) {
102
0
    // TODO Clip to border-radius/reference box if no shape
103
0
    // was specified.
104
0
    RefPtr<PathBuilder> builder = aDrawTarget->CreatePathBuilder();
105
0
    return builder->Finish();
106
0
  }
107
0
108
0
  nscoord appUnitsPerDevPixel =
109
0
    mTargetFrame->PresContext()->AppUnitsPerDevPixel();
110
0
  r = ToAppUnits(r.ToNearestPixels(appUnitsPerDevPixel), appUnitsPerDevPixel);
111
0
112
0
  const UniquePtr<StyleBasicShape>& basicShape = mClipPathStyle.GetBasicShape();
113
0
  switch (basicShape->GetShapeType()) {
114
0
    case StyleBasicShapeType::Circle:
115
0
      return CreateClipPathCircle(aDrawTarget, r);
116
0
    case StyleBasicShapeType::Ellipse:
117
0
      return CreateClipPathEllipse(aDrawTarget, r);
118
0
    case StyleBasicShapeType::Polygon:
119
0
      return CreateClipPathPolygon(aDrawTarget, r);
120
0
    case StyleBasicShapeType::Inset:
121
0
      return CreateClipPathInset(aDrawTarget, r);
122
0
      break;
123
0
    default:
124
0
      MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("Unexpected shape type");
125
0
  }
126
0
  // Return an empty Path:
127
0
  RefPtr<PathBuilder> builder = aDrawTarget->CreatePathBuilder();
128
0
  return builder->Finish();
129
0
}
130
131
already_AddRefed<Path>
132
nsCSSClipPathInstance::CreateClipPathCircle(DrawTarget* aDrawTarget,
133
                                            const nsRect& aRefBox)
134
0
{
135
0
  const UniquePtr<StyleBasicShape>& basicShape = mClipPathStyle.GetBasicShape();
136
0
137
0
  RefPtr<PathBuilder> builder = aDrawTarget->CreatePathBuilder();
138
0
139
0
  nsPoint center =
140
0
    ShapeUtils::ComputeCircleOrEllipseCenter(basicShape, aRefBox);
141
0
  nscoord r = ShapeUtils::ComputeCircleRadius(basicShape, center, aRefBox);
142
0
  nscoord appUnitsPerDevPixel =
143
0
    mTargetFrame->PresContext()->AppUnitsPerDevPixel();
144
0
  builder->Arc(Point(center.x, center.y) / appUnitsPerDevPixel,
145
0
               r / appUnitsPerDevPixel,
146
0
               0, Float(2 * M_PI));
147
0
  builder->Close();
148
0
  return builder->Finish();
149
0
}
150
151
already_AddRefed<Path>
152
nsCSSClipPathInstance::CreateClipPathEllipse(DrawTarget* aDrawTarget,
153
                                             const nsRect& aRefBox)
154
0
{
155
0
  const UniquePtr<StyleBasicShape>& basicShape = mClipPathStyle.GetBasicShape();
156
0
157
0
  RefPtr<PathBuilder> builder = aDrawTarget->CreatePathBuilder();
158
0
159
0
  nsPoint center =
160
0
    ShapeUtils::ComputeCircleOrEllipseCenter(basicShape, aRefBox);
161
0
  nsSize radii = ShapeUtils::ComputeEllipseRadii(basicShape, center, aRefBox);
162
0
  nscoord appUnitsPerDevPixel =
163
0
    mTargetFrame->PresContext()->AppUnitsPerDevPixel();
164
0
  EllipseToBezier(builder.get(),
165
0
                  Point(center.x, center.y) / appUnitsPerDevPixel,
166
0
                  Size(radii.width, radii.height) / appUnitsPerDevPixel);
167
0
  builder->Close();
168
0
  return builder->Finish();
169
0
}
170
171
already_AddRefed<Path>
172
nsCSSClipPathInstance::CreateClipPathPolygon(DrawTarget* aDrawTarget,
173
                                             const nsRect& aRefBox)
174
0
{
175
0
  const UniquePtr<StyleBasicShape>& basicShape = mClipPathStyle.GetBasicShape();
176
0
  FillRule fillRule = basicShape->GetFillRule() == StyleFillRule::Nonzero ?
177
0
                        FillRule::FILL_WINDING : FillRule::FILL_EVEN_ODD;
178
0
  RefPtr<PathBuilder> builder = aDrawTarget->CreatePathBuilder(fillRule);
179
0
180
0
  nsTArray<nsPoint> vertices =
181
0
    ShapeUtils::ComputePolygonVertices(basicShape, aRefBox);
182
0
  if (vertices.IsEmpty()) {
183
0
    MOZ_ASSERT_UNREACHABLE(
184
0
      "ComputePolygonVertices() should've given us some vertices!");
185
0
  } else {
186
0
    nscoord appUnitsPerDevPixel =
187
0
      mTargetFrame->PresContext()->AppUnitsPerDevPixel();
188
0
    builder->MoveTo(NSPointToPoint(vertices[0], appUnitsPerDevPixel));
189
0
    for (size_t i = 1; i < vertices.Length(); ++i) {
190
0
      builder->LineTo(NSPointToPoint(vertices[i], appUnitsPerDevPixel));
191
0
    }
192
0
  }
193
0
  builder->Close();
194
0
  return builder->Finish();
195
0
}
196
197
already_AddRefed<Path>
198
nsCSSClipPathInstance::CreateClipPathInset(DrawTarget* aDrawTarget,
199
                                           const nsRect& aRefBox)
200
0
{
201
0
  const UniquePtr<StyleBasicShape>& basicShape = mClipPathStyle.GetBasicShape();
202
0
203
0
  RefPtr<PathBuilder> builder = aDrawTarget->CreatePathBuilder();
204
0
205
0
  nscoord appUnitsPerDevPixel =
206
0
    mTargetFrame->PresContext()->AppUnitsPerDevPixel();
207
0
208
0
  nsRect insetRect = ShapeUtils::ComputeInsetRect(basicShape, aRefBox);
209
0
  const Rect insetRectPixels = NSRectToRect(insetRect, appUnitsPerDevPixel);
210
0
  nscoord appUnitsRadii[8];
211
0
212
0
  if (ShapeUtils::ComputeInsetRadii(basicShape, insetRect, aRefBox,
213
0
                                    appUnitsRadii)) {
214
0
    RectCornerRadii corners;
215
0
    nsCSSRendering::ComputePixelRadii(appUnitsRadii,
216
0
                                      appUnitsPerDevPixel, &corners);
217
0
218
0
    AppendRoundedRectToPath(builder, insetRectPixels, corners, true);
219
0
  } else {
220
0
    AppendRectToPath(builder, insetRectPixels, true);
221
0
  }
222
0
  return builder->Finish();
223
0
}
224
225
already_AddRefed<Path>
226
nsCSSClipPathInstance::CreateClipPathPath(DrawTarget* aDrawTarget)
227
0
{
228
0
  const StyleSVGPath* path = mClipPathStyle.GetPath();
229
0
  MOZ_ASSERT(path);
230
0
231
0
  RefPtr<PathBuilder> builder = aDrawTarget->CreatePathBuilder(
232
0
    path->FillRule() == StyleFillRule::Nonzero ? FillRule::FILL_WINDING
233
0
                                               : FillRule::FILL_EVEN_ODD);
234
0
  float scale = float(AppUnitsPerCSSPixel()) /
235
0
                mTargetFrame->PresContext()->AppUnitsPerDevPixel();
236
0
  return SVGPathData::BuildPath(
237
0
    path->Path(), builder, NS_STYLE_STROKE_LINECAP_BUTT, 0.0, scale);
238
0
}