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/. */
9 #include "DrawTargetCairo.h"
11 #include "PathHelpers.h"
12 #include "HelpersCairo.h"
17 PathBuilderCairo::PathBuilderCairo(FillRule aFillRule
) : mFillRule(aFillRule
) {}
19 void PathBuilderCairo::MoveTo(const Point
& aPoint
) {
20 cairo_path_data_t data
;
21 data
.header
.type
= CAIRO_PATH_MOVE_TO
;
22 data
.header
.length
= 2;
23 mPathData
.push_back(data
);
24 data
.point
.x
= aPoint
.x
;
25 data
.point
.y
= aPoint
.y
;
26 mPathData
.push_back(data
);
28 mBeginPoint
= mCurrentPoint
= aPoint
;
31 void PathBuilderCairo::LineTo(const Point
& aPoint
) {
32 cairo_path_data_t data
;
33 data
.header
.type
= CAIRO_PATH_LINE_TO
;
34 data
.header
.length
= 2;
35 mPathData
.push_back(data
);
36 data
.point
.x
= aPoint
.x
;
37 data
.point
.y
= aPoint
.y
;
38 mPathData
.push_back(data
);
40 mCurrentPoint
= aPoint
;
43 void PathBuilderCairo::BezierTo(const Point
& aCP1
, const Point
& aCP2
,
45 cairo_path_data_t data
;
46 data
.header
.type
= CAIRO_PATH_CURVE_TO
;
47 data
.header
.length
= 4;
48 mPathData
.push_back(data
);
49 data
.point
.x
= aCP1
.x
;
50 data
.point
.y
= aCP1
.y
;
51 mPathData
.push_back(data
);
52 data
.point
.x
= aCP2
.x
;
53 data
.point
.y
= aCP2
.y
;
54 mPathData
.push_back(data
);
55 data
.point
.x
= aCP3
.x
;
56 data
.point
.y
= aCP3
.y
;
57 mPathData
.push_back(data
);
62 void PathBuilderCairo::QuadraticBezierTo(const Point
& aCP1
, const Point
& aCP2
) {
63 // We need to elevate the degree of this quadratic Bézier to cubic, so we're
64 // going to add an intermediate control point, and recompute control point 1.
65 // The first and last control points remain the same.
66 // This formula can be found on http://fontforge.sourceforge.net/bezier.html
67 Point CP0
= CurrentPoint();
68 Point CP1
= (CP0
+ aCP1
* 2.0) / 3.0;
69 Point CP2
= (aCP2
+ aCP1
* 2.0) / 3.0;
72 cairo_path_data_t data
;
73 data
.header
.type
= CAIRO_PATH_CURVE_TO
;
74 data
.header
.length
= 4;
75 mPathData
.push_back(data
);
78 mPathData
.push_back(data
);
81 mPathData
.push_back(data
);
84 mPathData
.push_back(data
);
89 void PathBuilderCairo::Close() {
90 cairo_path_data_t data
;
91 data
.header
.type
= CAIRO_PATH_CLOSE_PATH
;
92 data
.header
.length
= 1;
93 mPathData
.push_back(data
);
95 mCurrentPoint
= mBeginPoint
;
98 void PathBuilderCairo::Arc(const Point
& aOrigin
, float aRadius
,
99 float aStartAngle
, float aEndAngle
,
100 bool aAntiClockwise
) {
101 ArcToBezier(this, aOrigin
, Size(aRadius
, aRadius
), aStartAngle
, aEndAngle
,
105 already_AddRefed
<Path
> PathBuilderCairo::Finish() {
106 return MakeAndAddRef
<PathCairo
>(mFillRule
, mPathData
, mCurrentPoint
,
110 PathCairo::PathCairo(FillRule aFillRule
,
111 std::vector
<cairo_path_data_t
>& aPathData
,
112 const Point
& aCurrentPoint
, const Point
& aBeginPoint
)
113 : mFillRule(aFillRule
),
114 mContainingContext(nullptr),
115 mCurrentPoint(aCurrentPoint
),
116 mBeginPoint(aBeginPoint
) {
117 mPathData
.swap(aPathData
);
120 PathCairo::PathCairo(cairo_t
* aContext
)
121 : mFillRule(FillRule::FILL_WINDING
), mContainingContext(nullptr) {
122 cairo_path_t
* path
= cairo_copy_path(aContext
);
124 // XXX - mCurrentPoint is not properly set here, the same is true for the
125 // D2D Path code, we never require current point when hitting this codepath
126 // but this should be fixed.
127 for (int i
= 0; i
< path
->num_data
; i
++) {
128 mPathData
.push_back(path
->data
[i
]);
131 cairo_path_destroy(path
);
134 PathCairo::~PathCairo() {
135 if (mContainingContext
) {
136 cairo_destroy(mContainingContext
);
140 already_AddRefed
<PathBuilder
> PathCairo::CopyToBuilder(
141 FillRule aFillRule
) const {
142 RefPtr
<PathBuilderCairo
> builder
= new PathBuilderCairo(aFillRule
);
144 builder
->mPathData
= mPathData
;
145 builder
->mCurrentPoint
= mCurrentPoint
;
146 builder
->mBeginPoint
= mBeginPoint
;
148 return builder
.forget();
151 already_AddRefed
<PathBuilder
> PathCairo::TransformedCopyToBuilder(
152 const Matrix
& aTransform
, FillRule aFillRule
) const {
153 RefPtr
<PathBuilderCairo
> builder
= new PathBuilderCairo(aFillRule
);
155 AppendPathToBuilder(builder
, &aTransform
);
156 builder
->mCurrentPoint
= aTransform
.TransformPoint(mCurrentPoint
);
157 builder
->mBeginPoint
= aTransform
.TransformPoint(mBeginPoint
);
159 return builder
.forget();
162 bool PathCairo::ContainsPoint(const Point
& aPoint
,
163 const Matrix
& aTransform
) const {
164 Matrix inverse
= aTransform
;
166 Point transformed
= inverse
.TransformPoint(aPoint
);
168 EnsureContainingContext(aTransform
);
170 return cairo_in_fill(mContainingContext
, transformed
.x
, transformed
.y
);
173 bool PathCairo::StrokeContainsPoint(const StrokeOptions
& aStrokeOptions
,
175 const Matrix
& aTransform
) const {
176 Matrix inverse
= aTransform
;
178 Point transformed
= inverse
.TransformPoint(aPoint
);
180 EnsureContainingContext(aTransform
);
182 SetCairoStrokeOptions(mContainingContext
, aStrokeOptions
);
184 return cairo_in_stroke(mContainingContext
, transformed
.x
, transformed
.y
);
187 Rect
PathCairo::GetBounds(const Matrix
& aTransform
) const {
188 EnsureContainingContext(aTransform
);
190 double x1
, y1
, x2
, y2
;
192 cairo_path_extents(mContainingContext
, &x1
, &y1
, &x2
, &y2
);
193 Rect
bounds(Float(x1
), Float(y1
), Float(x2
- x1
), Float(y2
- y1
));
194 return aTransform
.TransformBounds(bounds
);
197 Rect
PathCairo::GetStrokedBounds(const StrokeOptions
& aStrokeOptions
,
198 const Matrix
& aTransform
) const {
199 EnsureContainingContext(aTransform
);
201 double x1
, y1
, x2
, y2
;
203 SetCairoStrokeOptions(mContainingContext
, aStrokeOptions
);
205 cairo_stroke_extents(mContainingContext
, &x1
, &y1
, &x2
, &y2
);
206 Rect
bounds((Float
)x1
, (Float
)y1
, (Float
)(x2
- x1
), (Float
)(y2
- y1
));
207 return aTransform
.TransformBounds(bounds
);
210 void PathCairo::StreamToSink(PathSink
* aSink
) const {
211 for (size_t i
= 0; i
< mPathData
.size(); i
++) {
212 switch (mPathData
[i
].header
.type
) {
213 case CAIRO_PATH_MOVE_TO
:
215 aSink
->MoveTo(Point(mPathData
[i
].point
.x
, mPathData
[i
].point
.y
));
217 case CAIRO_PATH_LINE_TO
:
219 aSink
->LineTo(Point(mPathData
[i
].point
.x
, mPathData
[i
].point
.y
));
221 case CAIRO_PATH_CURVE_TO
:
223 Point(mPathData
[i
+ 1].point
.x
, mPathData
[i
+ 1].point
.y
),
224 Point(mPathData
[i
+ 2].point
.x
, mPathData
[i
+ 2].point
.y
),
225 Point(mPathData
[i
+ 3].point
.x
, mPathData
[i
+ 3].point
.y
));
228 case CAIRO_PATH_CLOSE_PATH
:
232 // Corrupt path data!
238 void PathCairo::EnsureContainingContext(const Matrix
& aTransform
) const {
239 if (mContainingContext
) {
240 if (mContainingTransform
.ExactlyEquals(aTransform
)) {
244 mContainingContext
= cairo_create(DrawTargetCairo::GetDummySurface());
247 mContainingTransform
= aTransform
;
250 GfxMatrixToCairoMatrix(mContainingTransform
, mat
);
251 cairo_set_matrix(mContainingContext
, &mat
);
253 SetPathOnContext(mContainingContext
);
256 void PathCairo::SetPathOnContext(cairo_t
* aContext
) const {
257 // Needs the correct fill rule set.
258 cairo_set_fill_rule(aContext
, GfxFillRuleToCairoFillRule(mFillRule
));
260 cairo_new_path(aContext
);
262 if (!mPathData
.empty()) {
264 path
.data
= const_cast<cairo_path_data_t
*>(&mPathData
.front());
265 path
.num_data
= mPathData
.size();
266 path
.status
= CAIRO_STATUS_SUCCESS
;
267 cairo_append_path(aContext
, &path
);
271 void PathCairo::AppendPathToBuilder(PathBuilderCairo
* aBuilder
,
272 const Matrix
* aTransform
) const {
275 while (i
< mPathData
.size()) {
276 uint32_t pointCount
= mPathData
[i
].header
.length
- 1;
277 aBuilder
->mPathData
.push_back(mPathData
[i
]);
279 for (uint32_t c
= 0; c
< pointCount
; c
++) {
280 cairo_path_data_t data
;
281 Point newPoint
= aTransform
->TransformPoint(
282 Point(mPathData
[i
].point
.x
, mPathData
[i
].point
.y
));
283 data
.point
.x
= newPoint
.x
;
284 data
.point
.y
= newPoint
.y
;
285 aBuilder
->mPathData
.push_back(data
);
290 for (size_t i
= 0; i
< mPathData
.size(); i
++) {
291 aBuilder
->mPathData
.push_back(mPathData
[i
]);
297 } // namespace mozilla