Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/gfx/2d/PathCairo.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
#include "PathCairo.h"
8
#include <math.h>
9
#include "DrawTargetCairo.h"
10
#include "Logging.h"
11
#include "PathHelpers.h"
12
#include "HelpersCairo.h"
13
14
namespace mozilla {
15
namespace gfx {
16
17
PathBuilderCairo::PathBuilderCairo(FillRule aFillRule)
18
  : mFillRule(aFillRule)
19
0
{
20
0
}
21
22
void
23
PathBuilderCairo::MoveTo(const Point &aPoint)
24
0
{
25
0
  cairo_path_data_t data;
26
0
  data.header.type = CAIRO_PATH_MOVE_TO;
27
0
  data.header.length = 2;
28
0
  mPathData.push_back(data);
29
0
  data.point.x = aPoint.x;
30
0
  data.point.y = aPoint.y;
31
0
  mPathData.push_back(data);
32
0
33
0
  mBeginPoint = mCurrentPoint = aPoint;
34
0
}
35
36
void
37
PathBuilderCairo::LineTo(const Point &aPoint)
38
0
{
39
0
  cairo_path_data_t data;
40
0
  data.header.type = CAIRO_PATH_LINE_TO;
41
0
  data.header.length = 2;
42
0
  mPathData.push_back(data);
43
0
  data.point.x = aPoint.x;
44
0
  data.point.y = aPoint.y;
45
0
  mPathData.push_back(data);
46
0
47
0
  mCurrentPoint = aPoint;
48
0
}
49
50
void
51
PathBuilderCairo::BezierTo(const Point &aCP1,
52
                           const Point &aCP2,
53
                           const Point &aCP3)
54
0
{
55
0
  cairo_path_data_t data;
56
0
  data.header.type = CAIRO_PATH_CURVE_TO;
57
0
  data.header.length = 4;
58
0
  mPathData.push_back(data);
59
0
  data.point.x = aCP1.x;
60
0
  data.point.y = aCP1.y;
61
0
  mPathData.push_back(data);
62
0
  data.point.x = aCP2.x;
63
0
  data.point.y = aCP2.y;
64
0
  mPathData.push_back(data);
65
0
  data.point.x = aCP3.x;
66
0
  data.point.y = aCP3.y;
67
0
  mPathData.push_back(data);
68
0
69
0
  mCurrentPoint = aCP3;
70
0
}
71
72
void
73
PathBuilderCairo::QuadraticBezierTo(const Point &aCP1,
74
                                    const Point &aCP2)
75
0
{
76
0
  // We need to elevate the degree of this quadratic Bézier to cubic, so we're
77
0
  // going to add an intermediate control point, and recompute control point 1.
78
0
  // The first and last control points remain the same.
79
0
  // This formula can be found on http://fontforge.sourceforge.net/bezier.html
80
0
  Point CP0 = CurrentPoint();
81
0
  Point CP1 = (CP0 + aCP1 * 2.0) / 3.0;
82
0
  Point CP2 = (aCP2 + aCP1 * 2.0) / 3.0;
83
0
  Point CP3 = aCP2;
84
0
85
0
  cairo_path_data_t data;
86
0
  data.header.type = CAIRO_PATH_CURVE_TO;
87
0
  data.header.length = 4;
88
0
  mPathData.push_back(data);
89
0
  data.point.x = CP1.x;
90
0
  data.point.y = CP1.y;
91
0
  mPathData.push_back(data);
92
0
  data.point.x = CP2.x;
93
0
  data.point.y = CP2.y;
94
0
  mPathData.push_back(data);
95
0
  data.point.x = CP3.x;
96
0
  data.point.y = CP3.y;
97
0
  mPathData.push_back(data);
98
0
99
0
  mCurrentPoint = aCP2;
100
0
}
101
102
void
103
PathBuilderCairo::Close()
104
0
{
105
0
  cairo_path_data_t data;
106
0
  data.header.type = CAIRO_PATH_CLOSE_PATH;
107
0
  data.header.length = 1;
108
0
  mPathData.push_back(data);
109
0
110
0
  mCurrentPoint = mBeginPoint;
111
0
}
112
113
void
114
PathBuilderCairo::Arc(const Point &aOrigin, float aRadius, float aStartAngle,
115
                     float aEndAngle, bool aAntiClockwise)
116
0
{
117
0
  ArcToBezier(this, aOrigin, Size(aRadius, aRadius), aStartAngle, aEndAngle, aAntiClockwise);
118
0
}
119
120
Point
121
PathBuilderCairo::CurrentPoint() const
122
0
{
123
0
  return mCurrentPoint;
124
0
}
125
126
already_AddRefed<Path>
127
PathBuilderCairo::Finish()
128
0
{
129
0
  return MakeAndAddRef<PathCairo>(mFillRule, mPathData, mCurrentPoint);
130
0
}
131
132
PathCairo::PathCairo(FillRule aFillRule, std::vector<cairo_path_data_t> &aPathData, const Point &aCurrentPoint)
133
  : mFillRule(aFillRule)
134
  , mContainingContext(nullptr)
135
  , mCurrentPoint(aCurrentPoint)
136
0
{
137
0
  mPathData.swap(aPathData);
138
0
}
139
140
PathCairo::PathCairo(cairo_t *aContext)
141
  : mFillRule(FillRule::FILL_WINDING)
142
  , mContainingContext(nullptr)
143
0
{
144
0
  cairo_path_t *path = cairo_copy_path(aContext);
145
0
146
0
  // XXX - mCurrentPoint is not properly set here, the same is true for the
147
0
  // D2D Path code, we never require current point when hitting this codepath
148
0
  // but this should be fixed.
149
0
  for (int i = 0; i < path->num_data; i++) {
150
0
    mPathData.push_back(path->data[i]);
151
0
  }
152
0
153
0
  cairo_path_destroy(path);
154
0
}
155
156
PathCairo::~PathCairo()
157
0
{
158
0
  if (mContainingContext) {
159
0
    cairo_destroy(mContainingContext);
160
0
  }
161
0
}
162
163
already_AddRefed<PathBuilder>
164
PathCairo::CopyToBuilder(FillRule aFillRule) const
165
0
{
166
0
  RefPtr<PathBuilderCairo> builder = new PathBuilderCairo(aFillRule);
167
0
168
0
  builder->mPathData = mPathData;
169
0
  builder->mCurrentPoint = mCurrentPoint;
170
0
171
0
  return builder.forget();
172
0
}
173
174
already_AddRefed<PathBuilder>
175
PathCairo::TransformedCopyToBuilder(const Matrix &aTransform, FillRule aFillRule) const
176
0
{
177
0
  RefPtr<PathBuilderCairo> builder = new PathBuilderCairo(aFillRule);
178
0
179
0
  AppendPathToBuilder(builder, &aTransform);
180
0
  builder->mCurrentPoint = aTransform.TransformPoint(mCurrentPoint);
181
0
182
0
  return builder.forget();
183
0
}
184
185
bool
186
PathCairo::ContainsPoint(const Point &aPoint, const Matrix &aTransform) const
187
0
{
188
0
  Matrix inverse = aTransform;
189
0
  inverse.Invert();
190
0
  Point transformed = inverse.TransformPoint(aPoint);
191
0
192
0
  EnsureContainingContext(aTransform);
193
0
194
0
  return cairo_in_fill(mContainingContext, transformed.x, transformed.y);
195
0
}
196
197
bool
198
PathCairo::StrokeContainsPoint(const StrokeOptions &aStrokeOptions,
199
                               const Point &aPoint,
200
                               const Matrix &aTransform) const
201
0
{
202
0
  Matrix inverse = aTransform;
203
0
  inverse.Invert();
204
0
  Point transformed = inverse.TransformPoint(aPoint);
205
0
206
0
  EnsureContainingContext(aTransform);
207
0
208
0
  SetCairoStrokeOptions(mContainingContext, aStrokeOptions);
209
0
210
0
  return cairo_in_stroke(mContainingContext, transformed.x, transformed.y);
211
0
}
212
213
Rect
214
PathCairo::GetBounds(const Matrix &aTransform) const
215
0
{
216
0
  EnsureContainingContext(aTransform);
217
0
218
0
  double x1, y1, x2, y2;
219
0
220
0
  cairo_path_extents(mContainingContext, &x1, &y1, &x2, &y2);
221
0
  Rect bounds(Float(x1), Float(y1), Float(x2 - x1), Float(y2 - y1));
222
0
  return aTransform.TransformBounds(bounds);
223
0
}
224
225
Rect
226
PathCairo::GetStrokedBounds(const StrokeOptions &aStrokeOptions,
227
                            const Matrix &aTransform) const
228
0
{
229
0
  EnsureContainingContext(aTransform);
230
0
231
0
  double x1, y1, x2, y2;
232
0
233
0
  SetCairoStrokeOptions(mContainingContext, aStrokeOptions);
234
0
235
0
  cairo_stroke_extents(mContainingContext, &x1, &y1, &x2, &y2);
236
0
  Rect bounds((Float)x1, (Float)y1, (Float)(x2 - x1), (Float)(y2 - y1));
237
0
  return aTransform.TransformBounds(bounds);
238
0
}
239
240
void
241
PathCairo::StreamToSink(PathSink *aSink) const
242
0
{
243
0
  for (size_t i = 0; i < mPathData.size(); i++) {
244
0
    switch (mPathData[i].header.type) {
245
0
    case CAIRO_PATH_MOVE_TO:
246
0
      i++;
247
0
      aSink->MoveTo(Point(mPathData[i].point.x, mPathData[i].point.y));
248
0
      break;
249
0
    case CAIRO_PATH_LINE_TO:
250
0
      i++;
251
0
      aSink->LineTo(Point(mPathData[i].point.x, mPathData[i].point.y));
252
0
      break;
253
0
    case CAIRO_PATH_CURVE_TO:
254
0
      aSink->BezierTo(Point(mPathData[i + 1].point.x, mPathData[i + 1].point.y),
255
0
                      Point(mPathData[i + 2].point.x, mPathData[i + 2].point.y),
256
0
                      Point(mPathData[i + 3].point.x, mPathData[i + 3].point.y));
257
0
      i += 3;
258
0
      break;
259
0
    case CAIRO_PATH_CLOSE_PATH:
260
0
      aSink->Close();
261
0
      break;
262
0
    default:
263
0
      // Corrupt path data!
264
0
      MOZ_ASSERT(false);
265
0
    }
266
0
  }
267
0
}
268
269
void
270
PathCairo::EnsureContainingContext(const Matrix &aTransform) const
271
0
{
272
0
  if (mContainingContext) {
273
0
    if (mContainingTransform.ExactlyEquals(aTransform)) {
274
0
      return;
275
0
    }
276
0
  } else {
277
0
    mContainingContext = cairo_create(DrawTargetCairo::GetDummySurface());
278
0
  }
279
0
280
0
  mContainingTransform = aTransform;
281
0
282
0
  cairo_matrix_t mat;
283
0
  GfxMatrixToCairoMatrix(mContainingTransform, mat);
284
0
  cairo_set_matrix(mContainingContext, &mat);
285
0
286
0
  SetPathOnContext(mContainingContext);
287
0
}
288
289
void
290
PathCairo::SetPathOnContext(cairo_t *aContext) const
291
0
{
292
0
  // Needs the correct fill rule set.
293
0
  cairo_set_fill_rule(aContext, GfxFillRuleToCairoFillRule(mFillRule));
294
0
295
0
  cairo_new_path(aContext);
296
0
297
0
  if (!mPathData.empty()) {
298
0
    cairo_path_t path;
299
0
    path.data = const_cast<cairo_path_data_t*>(&mPathData.front());
300
0
    path.num_data = mPathData.size();
301
0
    path.status = CAIRO_STATUS_SUCCESS;
302
0
    cairo_append_path(aContext, &path);
303
0
  }
304
0
}
305
306
void
307
PathCairo::AppendPathToBuilder(PathBuilderCairo *aBuilder, const Matrix *aTransform) const
308
0
{
309
0
  if (aTransform) {
310
0
    size_t i = 0;
311
0
    while (i < mPathData.size()) {
312
0
      uint32_t pointCount = mPathData[i].header.length - 1;
313
0
      aBuilder->mPathData.push_back(mPathData[i]);
314
0
      i++;
315
0
      for (uint32_t c = 0; c < pointCount; c++) {
316
0
        cairo_path_data_t data;
317
0
        Point newPoint = aTransform->TransformPoint(Point(mPathData[i].point.x, mPathData[i].point.y));
318
0
        data.point.x = newPoint.x;
319
0
        data.point.y = newPoint.y;
320
0
        aBuilder->mPathData.push_back(data);
321
0
        i++;
322
0
      }
323
0
    }
324
0
  } else {
325
0
    for (size_t i = 0; i < mPathData.size(); i++) {
326
0
      aBuilder->mPathData.push_back(mPathData[i]);
327
0
    }
328
0
  }
329
0
}
330
331
} // namespace gfx
332
} // namespace mozilla