1 : /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 : * ***** BEGIN LICENSE BLOCK *****
3 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 : *
5 : * The contents of this file are subject to the Mozilla Public License Version
6 : * 1.1 (the "License"); you may not use this file except in compliance with
7 : * the License. You may obtain a copy of the License at
8 : * http://www.mozilla.org/MPL/
9 : *
10 : * Software distributed under the License is distributed on an "AS IS" basis,
11 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 : * for the specific language governing rights and limitations under the
13 : * License.
14 : *
15 : * The Original Code is Mozilla Corporation code.
16 : *
17 : * The Initial Developer of the Original Code is Mozilla Foundation.
18 : * Portions created by the Initial Developer are Copyright (C) 2011
19 : * the Initial Developer. All Rights Reserved.
20 : *
21 : * Contributor(s):
22 : *
23 : * Alternatively, the contents of this file may be used under the terms of
24 : * either the GNU General Public License Version 2 or later (the "GPL"), or
25 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
26 : * in which case the provisions of the GPL or the LGPL are applicable instead
27 : * of those above. If you wish to allow use of your version of this file only
28 : * under the terms of either the GPL or the LGPL, and not to allow others to
29 : * use your version of this file under the terms of the MPL, indicate your
30 : * decision by deleting the provisions above and replace them with the notice
31 : * and other provisions required by the GPL or the LGPL. If you do not delete
32 : * the provisions above, a recipient may use your version of this file under
33 : * the terms of any one of the MPL, the GPL or the LGPL.
34 : *
35 : * ***** END LICENSE BLOCK ***** */
36 :
37 : #include "PathCairo.h"
38 : #include <math.h>
39 : #include "DrawTargetCairo.h"
40 : #include "Logging.h"
41 : #include "PathHelpers.h"
42 : #include "HelpersCairo.h"
43 :
44 : namespace mozilla {
45 : namespace gfx {
46 :
47 0 : CairoPathContext::CairoPathContext(cairo_t* aCtx, DrawTargetCairo* aDrawTarget,
48 : FillRule aFillRule,
49 : const Matrix& aTransform /* = Matrix() */)
50 : : mTransform(aTransform)
51 : , mContext(aCtx)
52 : , mDrawTarget(aDrawTarget)
53 0 : , mFillRule(aFillRule)
54 : {
55 0 : cairo_reference(mContext);
56 0 : cairo_set_fill_rule(mContext, GfxFillRuleToCairoFillRule(mFillRule));
57 :
58 : // If we don't have an identity transformation, we need to have a separate
59 : // context from the draw target, because we can't set a transformation on its
60 : // context.
61 0 : if (mDrawTarget && !mTransform.IsIdentity()) {
62 0 : DuplicateContextAndPath(mTransform);
63 :
64 0 : ForgetDrawTarget();
65 0 : } else if (mDrawTarget) {
66 0 : mDrawTarget->SetPathObserver(this);
67 : }
68 0 : }
69 :
70 0 : CairoPathContext::~CairoPathContext()
71 : {
72 0 : if (mDrawTarget) {
73 0 : mDrawTarget->SetPathObserver(NULL);
74 : }
75 0 : cairo_destroy(mContext);
76 0 : }
77 :
78 : void
79 0 : CairoPathContext::DuplicateContextAndPath(const Matrix& aMatrix /* = Matrix() */)
80 : {
81 : // Duplicate the path.
82 0 : cairo_path_t* path = cairo_copy_path(mContext);
83 0 : cairo_fill_rule_t rule = cairo_get_fill_rule(mContext);
84 :
85 : // Duplicate the context.
86 0 : cairo_surface_t* surf = cairo_get_target(mContext);
87 0 : cairo_destroy(mContext);
88 0 : mContext = cairo_create(surf);
89 :
90 : // Transform the context.
91 : cairo_matrix_t matrix;
92 0 : GfxMatrixToCairoMatrix(aMatrix, matrix);
93 0 : cairo_transform(mContext, &matrix);
94 :
95 : // Add the path, and throw away our duplicate.
96 0 : cairo_append_path(mContext, path);
97 0 : cairo_set_fill_rule(mContext, rule);
98 0 : cairo_path_destroy(path);
99 0 : }
100 :
101 : void
102 0 : CairoPathContext::PathWillChange()
103 : {
104 : // Once we've copied out the context's path, there's no use to holding on to
105 : // the draw target. Thus, there's nothing for us to do if we're independent
106 : // of the draw target, since we'll have already copied out the context's
107 : // path.
108 0 : if (mDrawTarget) {
109 : // The context we point to is going to change from under us. To continue
110 : // using this path, we need to copy it to a new context.
111 0 : DuplicateContextAndPath();
112 :
113 0 : ForgetDrawTarget();
114 : }
115 0 : }
116 :
117 : void
118 0 : CairoPathContext::MatrixWillChange(const Matrix& aNewMatrix)
119 : {
120 : // Cairo paths are stored in device space. Since we logically operate in user
121 : // space, we want to make it so our path will be in the same location if and
122 : // when our path is copied out.
123 : // To effect this, we copy out our path (which, in Cairo, implicitly converts
124 : // to user space), then temporarily set the context to have the new
125 : // transform. We then set the path, which ensures that the points are all
126 : // transformed correctly. Finally, we set the matrix back to its original
127 : // value.
128 0 : cairo_path_t* path = cairo_copy_path(mContext);
129 :
130 : cairo_matrix_t origMatrix;
131 0 : cairo_get_matrix(mContext, &origMatrix);
132 :
133 : cairo_matrix_t newMatrix;
134 0 : GfxMatrixToCairoMatrix(aNewMatrix, newMatrix);
135 0 : cairo_set_matrix(mContext, &newMatrix);
136 :
137 0 : cairo_new_path(mContext);
138 0 : cairo_append_path(mContext, path);
139 0 : cairo_path_destroy(path);
140 :
141 0 : cairo_set_matrix(mContext, &origMatrix);
142 0 : }
143 :
144 : void
145 0 : CairoPathContext::CopyPathTo(cairo_t* aToContext)
146 : {
147 0 : if (aToContext != mContext) {
148 0 : cairo_set_fill_rule(aToContext, GfxFillRuleToCairoFillRule(mFillRule));
149 :
150 : cairo_matrix_t origMat;
151 0 : cairo_get_matrix(aToContext, &origMat);
152 :
153 : cairo_matrix_t mat;
154 0 : GfxMatrixToCairoMatrix(mTransform, mat);
155 0 : cairo_transform(aToContext, &mat);
156 :
157 : // cairo_copy_path gives us a user-space copy of the path, so we don't have
158 : // to worry about transformations here.
159 0 : cairo_path_t* path = cairo_copy_path(mContext);
160 0 : cairo_new_path(aToContext);
161 0 : cairo_append_path(aToContext, path);
162 0 : cairo_path_destroy(path);
163 :
164 0 : cairo_set_matrix(aToContext, &origMat);
165 : }
166 0 : }
167 :
168 : void
169 0 : CairoPathContext::ForgetDrawTarget()
170 : {
171 0 : mDrawTarget = NULL;
172 0 : }
173 :
174 : bool
175 0 : CairoPathContext::ContainsPath(const Path* aPath)
176 : {
177 0 : if (aPath->GetBackendType() != BACKEND_CAIRO) {
178 0 : return false;
179 : }
180 :
181 0 : const PathCairo* path = static_cast<const PathCairo*>(aPath);
182 0 : RefPtr<CairoPathContext> ctx = const_cast<PathCairo*>(path)->GetPathContext();
183 0 : return ctx == this;
184 : }
185 :
186 0 : PathBuilderCairo::PathBuilderCairo(CairoPathContext* aPathContext,
187 : const Matrix& aTransform /* = Matrix() */)
188 0 : : mFillRule(aPathContext->GetFillRule())
189 : {
190 0 : RefPtr<DrawTargetCairo> drawTarget = aPathContext->GetDrawTarget();
191 : mPathContext = new CairoPathContext(*aPathContext, drawTarget, mFillRule,
192 0 : aPathContext->GetTransform() * aTransform);
193 :
194 : // We need to ensure that we are allowed to modify the path currently set on
195 : // aPathContext. If we don't have a draw target, CairoPathContext's
196 : // constructor has no way to make aPathContext duplicate its path (normally,
197 : // calling drawTarget->SetPathObserver() would do so). In this case, we
198 : // explicitly make aPathContext copy out its context and path, leaving our
199 : // path alone.
200 0 : if (!drawTarget) {
201 0 : aPathContext->DuplicateContextAndPath();
202 : }
203 0 : }
204 :
205 0 : PathBuilderCairo::PathBuilderCairo(cairo_t* aCtx, DrawTargetCairo* aDrawTarget, FillRule aFillRule)
206 0 : : mPathContext(new CairoPathContext(aCtx, aDrawTarget, aFillRule))
207 0 : , mFillRule(aFillRule)
208 0 : {}
209 :
210 : void
211 0 : PathBuilderCairo::MoveTo(const Point &aPoint)
212 : {
213 0 : cairo_move_to(*mPathContext, aPoint.x, aPoint.y);
214 0 : }
215 :
216 : void
217 0 : PathBuilderCairo::LineTo(const Point &aPoint)
218 : {
219 0 : cairo_line_to(*mPathContext, aPoint.x, aPoint.y);
220 0 : }
221 :
222 : void
223 0 : PathBuilderCairo::BezierTo(const Point &aCP1,
224 : const Point &aCP2,
225 : const Point &aCP3)
226 : {
227 0 : cairo_curve_to(*mPathContext, aCP1.x, aCP1.y, aCP2.x, aCP2.y, aCP3.x, aCP3.y);
228 0 : }
229 :
230 : void
231 0 : PathBuilderCairo::QuadraticBezierTo(const Point &aCP1,
232 : const Point &aCP2)
233 : {
234 : // We need to elevate the degree of this quadratic Bézier to cubic, so we're
235 : // going to add an intermediate control point, and recompute control point 1.
236 : // The first and last control points remain the same.
237 : // This formula can be found on http://fontforge.sourceforge.net/bezier.html
238 0 : Point CP0 = CurrentPoint();
239 0 : Point CP1 = (CP0 + aCP1 * 2.0) / 3.0;
240 0 : Point CP2 = (aCP2 + aCP1 * 2.0) / 3.0;
241 0 : Point CP3 = aCP2;
242 :
243 0 : cairo_curve_to(*mPathContext, CP1.x, CP1.y, CP2.x, CP2.y, CP3.x, CP3.y);
244 0 : }
245 :
246 : void
247 0 : PathBuilderCairo::Close()
248 : {
249 0 : cairo_close_path(*mPathContext);
250 0 : }
251 :
252 : void
253 0 : PathBuilderCairo::Arc(const Point &aOrigin, float aRadius, float aStartAngle,
254 : float aEndAngle, bool aAntiClockwise)
255 : {
256 0 : ArcToBezier(this, aOrigin, aRadius, aStartAngle, aEndAngle, aAntiClockwise);
257 0 : }
258 :
259 : Point
260 0 : PathBuilderCairo::CurrentPoint() const
261 : {
262 : double x, y;
263 0 : cairo_get_current_point(*mPathContext, &x, &y);
264 0 : return Point(x, y);
265 : }
266 :
267 : TemporaryRef<Path>
268 0 : PathBuilderCairo::Finish()
269 : {
270 0 : RefPtr<PathCairo> path = new PathCairo(*mPathContext,
271 0 : mPathContext->GetDrawTarget(),
272 : mFillRule,
273 0 : mPathContext->GetTransform());
274 0 : return path;
275 : }
276 :
277 : TemporaryRef<CairoPathContext>
278 0 : PathBuilderCairo::GetPathContext()
279 : {
280 0 : return mPathContext;
281 : }
282 :
283 0 : PathCairo::PathCairo(cairo_t* aCtx, DrawTargetCairo* aDrawTarget, FillRule aFillRule, const Matrix& aTransform)
284 0 : : mPathContext(new CairoPathContext(aCtx, aDrawTarget, aFillRule, aTransform))
285 0 : , mFillRule(aFillRule)
286 0 : {}
287 :
288 : TemporaryRef<PathBuilder>
289 0 : PathCairo::CopyToBuilder(FillRule aFillRule) const
290 : {
291 : // Note: This PathBuilderCairo constructor causes our mPathContext to copy
292 : // out the path, since the path builder is going to change the path on us.
293 0 : RefPtr<PathBuilderCairo> builder = new PathBuilderCairo(mPathContext);
294 0 : return builder;
295 : }
296 :
297 : TemporaryRef<PathBuilder>
298 0 : PathCairo::TransformedCopyToBuilder(const Matrix &aTransform, FillRule aFillRule) const
299 : {
300 : // Note: This PathBuilderCairo constructor causes our mPathContext to copy
301 : // out the path, since the path builder is going to change the path on us.
302 : RefPtr<PathBuilderCairo> builder = new PathBuilderCairo(mPathContext,
303 0 : aTransform);
304 0 : return builder;
305 : }
306 :
307 : bool
308 0 : PathCairo::ContainsPoint(const Point &aPoint, const Matrix &aTransform) const
309 : {
310 0 : Matrix inverse = aTransform;
311 0 : inverse.Invert();
312 0 : Point transformed = inverse * aPoint;
313 :
314 0 : return cairo_in_fill(*mPathContext, transformed.x, transformed.y);
315 : }
316 :
317 : Rect
318 0 : PathCairo::GetBounds(const Matrix &aTransform) const
319 : {
320 : double x1, y1, x2, y2;
321 :
322 0 : cairo_path_extents(*mPathContext, &x1, &y1, &x2, &y2);
323 0 : Rect bounds(x1, y1, x2 - x1, y2 - y1);
324 0 : return aTransform.TransformBounds(bounds);
325 : }
326 :
327 : Rect
328 0 : PathCairo::GetStrokedBounds(const StrokeOptions &aStrokeOptions,
329 : const Matrix &aTransform) const
330 : {
331 : double x1, y1, x2, y2;
332 :
333 0 : SetCairoStrokeOptions(*mPathContext, aStrokeOptions);
334 :
335 0 : cairo_stroke_extents(*mPathContext, &x1, &y1, &x2, &y2);
336 0 : Rect bounds(x1, y1, x2 - x1, y2 - y1);
337 0 : return aTransform.TransformBounds(bounds);
338 : }
339 :
340 : TemporaryRef<CairoPathContext>
341 0 : PathCairo::GetPathContext()
342 : {
343 0 : return mPathContext;
344 : }
345 :
346 : void
347 0 : PathCairo::CopyPathTo(cairo_t* aContext, DrawTargetCairo* aDrawTarget)
348 : {
349 0 : if (mPathContext->GetContext() != aContext) {
350 0 : mPathContext->CopyPathTo(aContext);
351 :
352 : // Since aDrawTarget wants us to be the current path on its context, we
353 : // should also listen to it for updates to that path (as an optimization).
354 : // The easiest way to do this is to just recreate mPathContext, since it
355 : // registers with aDrawTarget for updates.
356 : mPathContext = new CairoPathContext(aContext, aDrawTarget,
357 0 : mPathContext->GetFillRule(),
358 0 : mPathContext->GetTransform());
359 : }
360 0 : }
361 :
362 : }
363 : }
|