gfx/2d/PathCairo.cpp

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

mercurial