1 : /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
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 Oracle Corporation code.
16 : *
17 : * The Initial Developer of the Original Code is Oracle Corporation.
18 : * Portions created by the Initial Developer are Copyright (C) 2005
19 : * the Initial Developer. All Rights Reserved.
20 : *
21 : * Contributor(s):
22 : * Stuart Parmenter <pavlov@pavlov.net>
23 : * Vladimir Vukicevic <vladimir@pobox.com>
24 : *
25 : * Alternatively, the contents of this file may be used under the terms of
26 : * either the GNU General Public License Version 2 or later (the "GPL"), or
27 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 : * in which case the provisions of the GPL or the LGPL are applicable instead
29 : * of those above. If you wish to allow use of your version of this file only
30 : * under the terms of either the GPL or the LGPL, and not to allow others to
31 : * use your version of this file under the terms of the MPL, indicate your
32 : * decision by deleting the provisions above and replace them with the notice
33 : * and other provisions required by the GPL or the LGPL. If you do not delete
34 : * the provisions above, a recipient may use your version of this file under
35 : * the terms of any one of the MPL, the GPL or the LGPL.
36 : *
37 : * ***** END LICENSE BLOCK ***** */
38 :
39 : #ifdef _MSC_VER
40 : #define _USE_MATH_DEFINES
41 : #endif
42 : #include <math.h>
43 :
44 : #ifndef M_PI
45 : #define M_PI 3.14159265358979323846
46 : #endif
47 :
48 : #include "cairo.h"
49 :
50 : #include "gfxContext.h"
51 :
52 : #include "gfxColor.h"
53 : #include "gfxMatrix.h"
54 : #include "gfxASurface.h"
55 : #include "gfxPattern.h"
56 : #include "gfxPlatform.h"
57 : #include "gfxTeeSurface.h"
58 :
59 : using namespace mozilla;
60 : using namespace mozilla::gfx;
61 :
62 : /* This class lives on the stack and allows gfxContext users to easily, and
63 : * performantly get a gfx::Pattern to use for drawing in their current context.
64 : */
65 : class GeneralPattern
66 : {
67 : public:
68 0 : GeneralPattern(gfxContext *aContext) : mContext(aContext), mPattern(NULL) {}
69 0 : ~GeneralPattern() { if (mPattern) { mPattern->~Pattern(); } }
70 :
71 0 : operator mozilla::gfx::Pattern&()
72 : {
73 0 : gfxContext::AzureState &state = mContext->CurrentState();
74 :
75 0 : if (state.pattern) {
76 0 : return *state.pattern->GetPattern(mContext->mDT);
77 0 : } else if (state.sourceSurface) {
78 0 : mPattern = new (mSurfacePattern.addr())
79 0 : SurfacePattern(state.sourceSurface, EXTEND_CLAMP, state.surfTransform);
80 0 : return *mPattern;
81 : } else {
82 0 : mPattern = new (mColorPattern.addr())
83 0 : ColorPattern(state.color);
84 0 : return *mPattern;
85 : }
86 : }
87 :
88 : private:
89 : union {
90 : mozilla::AlignedStorage2<mozilla::gfx::ColorPattern> mColorPattern;
91 : mozilla::AlignedStorage2<mozilla::gfx::SurfacePattern> mSurfacePattern;
92 : };
93 :
94 : gfxContext *mContext;
95 : Pattern *mPattern;
96 : };
97 :
98 64 : gfxContext::gfxContext(gfxASurface *surface)
99 : : mRefCairo(NULL)
100 64 : , mSurface(surface)
101 : {
102 64 : MOZ_COUNT_CTOR(gfxContext);
103 :
104 64 : mCairo = cairo_create(surface->CairoSurface());
105 64 : mFlags = surface->GetDefaultContextFlags();
106 64 : if (mSurface->GetRotateForLandscape()) {
107 : // Rotate page 90 degrees to draw landscape page on portrait paper
108 0 : gfxIntSize size = mSurface->GetSize();
109 0 : Translate(gfxPoint(0, size.width));
110 : gfxMatrix matrix(0, -1,
111 : 1, 0,
112 0 : 0, 0);
113 0 : Multiply(matrix);
114 : }
115 64 : }
116 :
117 0 : gfxContext::gfxContext(DrawTarget *aTarget)
118 : : mPathIsRect(false)
119 : , mTransformChanged(false)
120 : , mCairo(NULL)
121 : , mRefCairo(NULL)
122 : , mSurface(NULL)
123 : , mFlags(0)
124 : , mDT(aTarget)
125 0 : , mOriginalDT(aTarget)
126 : {
127 0 : MOZ_COUNT_CTOR(gfxContext);
128 :
129 0 : mStateStack.SetLength(1);
130 0 : CurrentState().drawTarget = mDT;
131 0 : mDT->SetTransform(Matrix());
132 0 : }
133 :
134 128 : gfxContext::~gfxContext()
135 : {
136 64 : if (mCairo) {
137 64 : cairo_destroy(mCairo);
138 : }
139 64 : if (mRefCairo) {
140 0 : cairo_destroy(mRefCairo);
141 : }
142 64 : if (mDT) {
143 0 : for (int i = mStateStack.Length() - 1; i >= 0; i--) {
144 0 : for (unsigned int c = 0; c < mStateStack[i].pushedClips.Length(); c++) {
145 0 : mDT->PopClip();
146 : }
147 :
148 0 : if (mStateStack[i].clipWasReset) {
149 0 : break;
150 : }
151 : }
152 0 : mDT->Flush();
153 : }
154 64 : MOZ_COUNT_DTOR(gfxContext);
155 64 : }
156 :
157 : gfxASurface *
158 0 : gfxContext::OriginalSurface()
159 : {
160 0 : return mSurface;
161 : }
162 :
163 : already_AddRefed<gfxASurface>
164 0 : gfxContext::CurrentSurface(gfxFloat *dx, gfxFloat *dy)
165 : {
166 0 : if (mCairo) {
167 0 : cairo_surface_t *s = cairo_get_group_target(mCairo);
168 0 : if (s == mSurface->CairoSurface()) {
169 0 : if (dx && dy)
170 0 : cairo_surface_get_device_offset(s, dx, dy);
171 0 : gfxASurface *ret = mSurface;
172 0 : NS_ADDREF(ret);
173 0 : return ret;
174 : }
175 :
176 0 : if (dx && dy)
177 0 : cairo_surface_get_device_offset(s, dx, dy);
178 0 : return gfxASurface::Wrap(s);
179 : } else {
180 0 : if (dx && dy) {
181 0 : *dx = *dy = 0;
182 : }
183 : // An Azure context doesn't have a surface backing it.
184 0 : return NULL;
185 : }
186 : }
187 :
188 : cairo_t *
189 0 : gfxContext::GetCairo()
190 : {
191 0 : if (mCairo) {
192 0 : return mCairo;
193 : }
194 :
195 0 : if (mRefCairo) {
196 : // Set transform!
197 0 : return mRefCairo;
198 : }
199 :
200 0 : mRefCairo = cairo_create(gfxPlatform::GetPlatform()->ScreenReferenceSurface()->CairoSurface());
201 :
202 0 : return mRefCairo;
203 : }
204 :
205 : void
206 0 : gfxContext::Save()
207 : {
208 0 : if (mCairo) {
209 0 : cairo_save(mCairo);
210 : } else {
211 0 : CurrentState().transform = mDT->GetTransform();
212 0 : mStateStack.AppendElement(AzureState(CurrentState()));
213 0 : CurrentState().clipWasReset = false;
214 0 : CurrentState().pushedClips.Clear();
215 : }
216 0 : }
217 :
218 : void
219 0 : gfxContext::Restore()
220 : {
221 0 : if (mCairo) {
222 0 : cairo_restore(mCairo);
223 : } else {
224 0 : for (unsigned int c = 0; c < CurrentState().pushedClips.Length(); c++) {
225 0 : mDT->PopClip();
226 : }
227 :
228 0 : if (CurrentState().clipWasReset &&
229 0 : CurrentState().drawTarget == mStateStack[mStateStack.Length() - 2].drawTarget) {
230 0 : PushClipsToDT(mDT);
231 : }
232 :
233 0 : mStateStack.RemoveElementAt(mStateStack.Length() - 1);
234 :
235 0 : if (mPathBuilder || mPath || mPathIsRect) {
236 0 : mTransformChanged = true;
237 0 : mPathTransform = mDT->GetTransform();
238 : }
239 :
240 0 : mDT = CurrentState().drawTarget;
241 0 : mDT->SetTransform(CurrentState().transform);
242 : }
243 0 : }
244 :
245 : // drawing
246 : void
247 0 : gfxContext::NewPath()
248 : {
249 0 : if (mCairo) {
250 0 : cairo_new_path(mCairo);
251 : } else {
252 0 : mPath = NULL;
253 0 : mPathBuilder = NULL;
254 0 : mPathIsRect = false;
255 0 : mTransformChanged = false;
256 : }
257 0 : }
258 :
259 : void
260 0 : gfxContext::ClosePath()
261 : {
262 0 : if (mCairo) {
263 0 : cairo_close_path(mCairo);
264 : } else {
265 0 : EnsurePathBuilder();
266 0 : mPathBuilder->Close();
267 : }
268 0 : }
269 :
270 0 : already_AddRefed<gfxPath> gfxContext::CopyPath() const
271 : {
272 0 : if (mCairo) {
273 0 : nsRefPtr<gfxPath> path = new gfxPath(cairo_copy_path(mCairo));
274 0 : return path.forget();
275 : } else {
276 : // XXX - This is not yet supported for Azure.
277 0 : return NULL;
278 : }
279 : }
280 :
281 0 : void gfxContext::AppendPath(gfxPath* path)
282 : {
283 0 : if (mCairo) {
284 0 : if (path->mPath->status == CAIRO_STATUS_SUCCESS && path->mPath->num_data != 0)
285 0 : cairo_append_path(mCairo, path->mPath);
286 : } else {
287 : // XXX - This is not yet supported for Azure.
288 0 : return;
289 : }
290 : }
291 :
292 : gfxPoint
293 0 : gfxContext::CurrentPoint()
294 : {
295 0 : if (mCairo) {
296 : double x, y;
297 0 : cairo_get_current_point(mCairo, &x, &y);
298 0 : return gfxPoint(x, y);
299 : } else {
300 0 : EnsurePathBuilder();
301 0 : return ThebesPoint(mPathBuilder->CurrentPoint());
302 : }
303 : }
304 :
305 : void
306 0 : gfxContext::Stroke()
307 : {
308 0 : if (mCairo) {
309 0 : cairo_stroke_preserve(mCairo);
310 : } else {
311 0 : AzureState &state = CurrentState();
312 0 : if (mPathIsRect) {
313 0 : mDT->StrokeRect(mRect, GeneralPattern(this),
314 : state.strokeOptions,
315 0 : DrawOptions(1.0f, GetOp(), state.aaMode));
316 : } else {
317 0 : EnsurePath();
318 :
319 0 : mDT->Stroke(mPath, GeneralPattern(this), state.strokeOptions,
320 0 : DrawOptions(1.0f, GetOp(), state.aaMode));
321 : }
322 : }
323 0 : }
324 :
325 : void
326 21 : gfxContext::Fill()
327 : {
328 21 : if (mCairo) {
329 21 : cairo_fill_preserve(mCairo);
330 : } else {
331 0 : FillAzure(1.0f);
332 : }
333 21 : }
334 :
335 : void
336 0 : gfxContext::FillWithOpacity(gfxFloat aOpacity)
337 : {
338 0 : if (mCairo) {
339 : // This method exists in the hope that one day cairo gets a direct
340 : // API for this, and then we would change this method to use that
341 : // API instead.
342 0 : if (aOpacity != 1.0) {
343 0 : gfxContextAutoSaveRestore saveRestore(this);
344 0 : Clip();
345 0 : Paint(aOpacity);
346 : } else {
347 0 : Fill();
348 : }
349 : } else {
350 0 : FillAzure(Float(aOpacity));
351 : }
352 0 : }
353 :
354 : void
355 0 : gfxContext::MoveTo(const gfxPoint& pt)
356 : {
357 0 : if (mCairo) {
358 0 : cairo_move_to(mCairo, pt.x, pt.y);
359 : } else {
360 0 : EnsurePathBuilder();
361 0 : mPathBuilder->MoveTo(ToPoint(pt));
362 : }
363 0 : }
364 :
365 : void
366 0 : gfxContext::NewSubPath()
367 : {
368 0 : if (mCairo) {
369 0 : cairo_new_sub_path(mCairo);
370 : } else {
371 : // XXX - This has no users, we should kill it, it should be equivelant to a
372 : // MoveTo to the path's current point.
373 : }
374 0 : }
375 :
376 : void
377 0 : gfxContext::LineTo(const gfxPoint& pt)
378 : {
379 0 : if (mCairo) {
380 0 : cairo_line_to(mCairo, pt.x, pt.y);
381 : } else {
382 0 : EnsurePathBuilder();
383 0 : mPathBuilder->LineTo(ToPoint(pt));
384 : }
385 0 : }
386 :
387 : void
388 0 : gfxContext::CurveTo(const gfxPoint& pt1, const gfxPoint& pt2, const gfxPoint& pt3)
389 : {
390 0 : if (mCairo) {
391 0 : cairo_curve_to(mCairo, pt1.x, pt1.y, pt2.x, pt2.y, pt3.x, pt3.y);
392 : } else {
393 0 : EnsurePathBuilder();
394 0 : mPathBuilder->BezierTo(ToPoint(pt1), ToPoint(pt2), ToPoint(pt3));
395 : }
396 0 : }
397 :
398 : void
399 0 : gfxContext::QuadraticCurveTo(const gfxPoint& pt1, const gfxPoint& pt2)
400 : {
401 0 : if (mCairo) {
402 : double cx, cy;
403 0 : cairo_get_current_point(mCairo, &cx, &cy);
404 : cairo_curve_to(mCairo,
405 : (cx + pt1.x * 2.0) / 3.0,
406 : (cy + pt1.y * 2.0) / 3.0,
407 : (pt1.x * 2.0 + pt2.x) / 3.0,
408 : (pt1.y * 2.0 + pt2.y) / 3.0,
409 : pt2.x,
410 0 : pt2.y);
411 : } else {
412 0 : EnsurePathBuilder();
413 0 : mPathBuilder->QuadraticBezierTo(ToPoint(pt1), ToPoint(pt2));
414 : }
415 0 : }
416 :
417 : void
418 0 : gfxContext::Arc(const gfxPoint& center, gfxFloat radius,
419 : gfxFloat angle1, gfxFloat angle2)
420 : {
421 0 : if (mCairo) {
422 0 : cairo_arc(mCairo, center.x, center.y, radius, angle1, angle2);
423 : } else {
424 0 : EnsurePathBuilder();
425 0 : mPathBuilder->Arc(ToPoint(center), Float(radius), Float(angle1), Float(angle2));
426 : }
427 0 : }
428 :
429 : void
430 0 : gfxContext::NegativeArc(const gfxPoint& center, gfxFloat radius,
431 : gfxFloat angle1, gfxFloat angle2)
432 : {
433 0 : if (mCairo) {
434 0 : cairo_arc_negative(mCairo, center.x, center.y, radius, angle1, angle2);
435 : } else {
436 0 : EnsurePathBuilder();
437 0 : mPathBuilder->Arc(ToPoint(center), Float(radius), Float(angle2), Float(angle1));
438 : }
439 0 : }
440 :
441 : void
442 0 : gfxContext::Line(const gfxPoint& start, const gfxPoint& end)
443 : {
444 0 : if (mCairo) {
445 0 : MoveTo(start);
446 0 : LineTo(end);
447 : } else {
448 0 : EnsurePathBuilder();
449 0 : mPathBuilder->MoveTo(ToPoint(start));
450 0 : mPathBuilder->LineTo(ToPoint(end));
451 : }
452 0 : }
453 :
454 : // XXX snapToPixels is only valid when snapping for filled
455 : // rectangles and for even-width stroked rectangles.
456 : // For odd-width stroked rectangles, we need to offset x/y by
457 : // 0.5...
458 : void
459 21 : gfxContext::Rectangle(const gfxRect& rect, bool snapToPixels)
460 : {
461 21 : if (mCairo) {
462 21 : if (snapToPixels) {
463 0 : gfxRect snappedRect(rect);
464 :
465 0 : if (UserToDevicePixelSnapped(snappedRect, true))
466 : {
467 : cairo_matrix_t mat;
468 0 : cairo_get_matrix(mCairo, &mat);
469 0 : cairo_identity_matrix(mCairo);
470 0 : Rectangle(snappedRect);
471 0 : cairo_set_matrix(mCairo, &mat);
472 :
473 0 : return;
474 : }
475 : }
476 :
477 21 : cairo_rectangle(mCairo, rect.X(), rect.Y(), rect.Width(), rect.Height());
478 : } else {
479 0 : Rect rec = ToRect(rect);
480 :
481 0 : if (snapToPixels) {
482 0 : gfxRect newRect(rect);
483 0 : if (UserToDevicePixelSnapped(newRect, true)) {
484 0 : gfxMatrix mat = ThebesMatrix(mDT->GetTransform());
485 0 : mat.Invert();
486 :
487 : // We need the user space rect.
488 0 : rec = ToRect(mat.TransformBounds(newRect));
489 : }
490 : }
491 :
492 0 : if (!mPathBuilder && !mPathIsRect) {
493 0 : mPathIsRect = true;
494 0 : mRect = rec;
495 0 : return;
496 0 : } else if (!mPathBuilder) {
497 0 : EnsurePathBuilder();
498 : }
499 :
500 0 : mPathBuilder->MoveTo(rec.TopLeft());
501 0 : mPathBuilder->LineTo(rec.TopRight());
502 0 : mPathBuilder->LineTo(rec.BottomRight());
503 0 : mPathBuilder->LineTo(rec.BottomLeft());
504 0 : mPathBuilder->Close();
505 : }
506 : }
507 :
508 : void
509 0 : gfxContext::Ellipse(const gfxPoint& center, const gfxSize& dimensions)
510 : {
511 0 : gfxSize halfDim = dimensions / 2.0;
512 0 : gfxRect r(center - gfxPoint(halfDim.width, halfDim.height), dimensions);
513 0 : gfxCornerSizes c(halfDim, halfDim, halfDim, halfDim);
514 :
515 0 : RoundedRectangle (r, c);
516 0 : }
517 :
518 : void
519 0 : gfxContext::Polygon(const gfxPoint *points, PRUint32 numPoints)
520 : {
521 0 : if (mCairo) {
522 0 : if (numPoints == 0)
523 0 : return;
524 :
525 0 : cairo_move_to(mCairo, points[0].x, points[0].y);
526 0 : for (PRUint32 i = 1; i < numPoints; ++i) {
527 0 : cairo_line_to(mCairo, points[i].x, points[i].y);
528 : }
529 : } else {
530 0 : if (numPoints == 0) {
531 0 : return;
532 : }
533 :
534 0 : EnsurePathBuilder();
535 :
536 0 : mPathBuilder->MoveTo(ToPoint(points[0]));
537 0 : for (PRUint32 i = 1; i < numPoints; i++) {
538 0 : mPathBuilder->LineTo(ToPoint(points[i]));
539 : }
540 : }
541 : }
542 :
543 : void
544 0 : gfxContext::DrawSurface(gfxASurface *surface, const gfxSize& size)
545 : {
546 0 : if (mCairo) {
547 0 : cairo_save(mCairo);
548 0 : cairo_set_source_surface(mCairo, surface->CairoSurface(), 0, 0);
549 0 : cairo_new_path(mCairo);
550 :
551 : // pixel-snap this
552 0 : Rectangle(gfxRect(gfxPoint(0.0, 0.0), size), true);
553 :
554 0 : cairo_fill(mCairo);
555 0 : cairo_restore(mCairo);
556 : } else {
557 : RefPtr<SourceSurface> surf =
558 0 : gfxPlatform::GetPlatform()->GetSourceSurfaceForSurface(mDT, surface);
559 :
560 0 : Rect rect(0, 0, Float(size.width), Float(size.height));
561 0 : rect.Intersect(Rect(0, 0, Float(surf->GetSize().width), Float(surf->GetSize().height)));
562 :
563 : // XXX - Should fix pixel snapping.
564 0 : mDT->DrawSurface(surf, rect, rect);
565 : }
566 0 : }
567 :
568 : // transform stuff
569 : void
570 19 : gfxContext::Translate(const gfxPoint& pt)
571 : {
572 19 : if (mCairo) {
573 19 : cairo_translate(mCairo, pt.x, pt.y);
574 : } else {
575 0 : MOZ_ASSERT(!mPathBuilder);
576 :
577 0 : Matrix newMatrix = mDT->GetTransform();
578 0 : mDT->SetTransform(newMatrix.Translate(Float(pt.x), Float(pt.y)));
579 : }
580 19 : }
581 :
582 : void
583 12 : gfxContext::Scale(gfxFloat x, gfxFloat y)
584 : {
585 12 : if (mCairo) {
586 12 : cairo_scale(mCairo, x, y);
587 : } else {
588 0 : MOZ_ASSERT(!mPathBuilder);
589 :
590 0 : Matrix newMatrix = mDT->GetTransform();
591 0 : mDT->SetTransform(newMatrix.Scale(Float(x), Float(y)));
592 : }
593 12 : }
594 :
595 : void
596 0 : gfxContext::Rotate(gfxFloat angle)
597 : {
598 0 : if (mCairo) {
599 0 : cairo_rotate(mCairo, angle);
600 : } else {
601 0 : MOZ_ASSERT(!mPathBuilder);
602 :
603 0 : Matrix rotation = Matrix::Rotation(Float(angle));
604 0 : mDT->SetTransform(rotation * mDT->GetTransform());
605 : }
606 0 : }
607 :
608 : void
609 0 : gfxContext::Multiply(const gfxMatrix& matrix)
610 : {
611 0 : if (mCairo) {
612 0 : const cairo_matrix_t& mat = reinterpret_cast<const cairo_matrix_t&>(matrix);
613 0 : cairo_transform(mCairo, &mat);
614 : } else {
615 0 : MOZ_ASSERT(!mPathBuilder);
616 :
617 0 : mDT->SetTransform(ToMatrix(matrix) * mDT->GetTransform());
618 : }
619 0 : }
620 :
621 : void
622 0 : gfxContext::SetMatrix(const gfxMatrix& matrix)
623 : {
624 0 : if (mCairo) {
625 0 : const cairo_matrix_t& mat = reinterpret_cast<const cairo_matrix_t&>(matrix);
626 0 : cairo_set_matrix(mCairo, &mat);
627 : } else {
628 0 : MOZ_ASSERT(!mPathBuilder);
629 :
630 0 : mDT->SetTransform(ToMatrix(matrix));
631 : }
632 0 : }
633 :
634 : void
635 0 : gfxContext::IdentityMatrix()
636 : {
637 0 : if (mCairo) {
638 0 : cairo_identity_matrix(mCairo);
639 : } else {
640 0 : MOZ_ASSERT(!mPathBuilder);
641 :
642 0 : mDT->SetTransform(Matrix());
643 : }
644 0 : }
645 :
646 : gfxMatrix
647 0 : gfxContext::CurrentMatrix() const
648 : {
649 0 : if (mCairo) {
650 : cairo_matrix_t mat;
651 0 : cairo_get_matrix(mCairo, &mat);
652 0 : return gfxMatrix(*reinterpret_cast<gfxMatrix*>(&mat));
653 : } else {
654 0 : return ThebesMatrix(mDT->GetTransform());
655 : }
656 : }
657 :
658 : void
659 0 : gfxContext::NudgeCurrentMatrixToIntegers()
660 : {
661 0 : if (mCairo) {
662 : cairo_matrix_t mat;
663 0 : cairo_get_matrix(mCairo, &mat);
664 0 : gfxMatrix(*reinterpret_cast<gfxMatrix*>(&mat)).NudgeToIntegers();
665 0 : cairo_set_matrix(mCairo, &mat);
666 : } else {
667 0 : gfxMatrix matrix = ThebesMatrix(mTransform);
668 0 : matrix.NudgeToIntegers();
669 0 : mTransform = ToMatrix(matrix);
670 : }
671 0 : }
672 :
673 : gfxPoint
674 0 : gfxContext::DeviceToUser(const gfxPoint& point) const
675 : {
676 0 : if (mCairo) {
677 0 : gfxPoint ret = point;
678 0 : cairo_device_to_user(mCairo, &ret.x, &ret.y);
679 0 : return ret;
680 : } else {
681 0 : Matrix matrix = mDT->GetTransform();
682 :
683 0 : matrix.Invert();
684 :
685 0 : return ThebesPoint(matrix * ToPoint(point));
686 : }
687 : }
688 :
689 : gfxSize
690 0 : gfxContext::DeviceToUser(const gfxSize& size) const
691 : {
692 0 : if (mCairo) {
693 0 : gfxSize ret = size;
694 0 : cairo_device_to_user_distance(mCairo, &ret.width, &ret.height);
695 0 : return ret;
696 : } else {
697 0 : Matrix matrix = mDT->GetTransform();
698 :
699 0 : matrix.Invert();
700 :
701 0 : return ThebesSize(matrix * ToSize(size));
702 : }
703 : }
704 :
705 : gfxRect
706 0 : gfxContext::DeviceToUser(const gfxRect& rect) const
707 : {
708 0 : if (mCairo) {
709 0 : gfxRect ret = rect;
710 0 : cairo_device_to_user(mCairo, &ret.x, &ret.y);
711 0 : cairo_device_to_user_distance(mCairo, &ret.width, &ret.height);
712 0 : return ret;
713 : } else {
714 0 : Matrix matrix = mDT->GetTransform();
715 :
716 0 : matrix.Invert();
717 :
718 0 : return ThebesRect(matrix.TransformBounds(ToRect(rect)));
719 : }
720 : }
721 :
722 : gfxPoint
723 0 : gfxContext::UserToDevice(const gfxPoint& point) const
724 : {
725 0 : if (mCairo) {
726 0 : gfxPoint ret = point;
727 0 : cairo_user_to_device(mCairo, &ret.x, &ret.y);
728 0 : return ret;
729 : } else {
730 0 : return ThebesPoint(mDT->GetTransform() * ToPoint(point));
731 : }
732 : }
733 :
734 : gfxSize
735 0 : gfxContext::UserToDevice(const gfxSize& size) const
736 : {
737 0 : if (mCairo) {
738 0 : gfxSize ret = size;
739 0 : cairo_user_to_device_distance(mCairo, &ret.width, &ret.height);
740 0 : return ret;
741 : } else {
742 0 : const Matrix &matrix = mDT->GetTransform();
743 :
744 0 : gfxSize newSize = size;
745 0 : newSize.width = newSize.width * matrix._11 + newSize.height * matrix._12;
746 0 : newSize.height = newSize.width * matrix._21 + newSize.height * matrix._22;
747 0 : return newSize;
748 : }
749 : }
750 :
751 : gfxRect
752 0 : gfxContext::UserToDevice(const gfxRect& rect) const
753 : {
754 0 : if (mCairo) {
755 0 : double xmin = rect.X(), ymin = rect.Y(), xmax = rect.XMost(), ymax = rect.YMost();
756 :
757 : double x[3], y[3];
758 0 : x[0] = xmin; y[0] = ymax;
759 0 : x[1] = xmax; y[1] = ymax;
760 0 : x[2] = xmax; y[2] = ymin;
761 :
762 0 : cairo_user_to_device(mCairo, &xmin, &ymin);
763 0 : xmax = xmin;
764 0 : ymax = ymin;
765 0 : for (int i = 0; i < 3; i++) {
766 0 : cairo_user_to_device(mCairo, &x[i], &y[i]);
767 0 : xmin = NS_MIN(xmin, x[i]);
768 0 : xmax = NS_MAX(xmax, x[i]);
769 0 : ymin = NS_MIN(ymin, y[i]);
770 0 : ymax = NS_MAX(ymax, y[i]);
771 : }
772 :
773 0 : return gfxRect(xmin, ymin, xmax - xmin, ymax - ymin);
774 : } else {
775 0 : const Matrix &matrix = mDT->GetTransform();
776 0 : return ThebesRect(matrix.TransformBounds(ToRect(rect)));
777 : }
778 : }
779 :
780 : bool
781 0 : gfxContext::UserToDevicePixelSnapped(gfxRect& rect, bool ignoreScale) const
782 : {
783 0 : if (GetFlags() & FLAG_DISABLE_SNAPPING)
784 0 : return false;
785 :
786 : // if we're not at 1.0 scale, don't snap, unless we're
787 : // ignoring the scale. If we're not -just- a scale,
788 : // never snap.
789 0 : const gfxFloat epsilon = 0.0000001;
790 : #define WITHIN_E(a,b) (fabs((a)-(b)) < epsilon)
791 0 : if (mCairo) {
792 : cairo_matrix_t mat;
793 0 : cairo_get_matrix(mCairo, &mat);
794 0 : if (!ignoreScale &&
795 0 : (!WITHIN_E(mat.xx,1.0) || !WITHIN_E(mat.yy,1.0) ||
796 0 : !WITHIN_E(mat.xy,0.0) || !WITHIN_E(mat.yx,0.0)))
797 0 : return false;
798 : } else {
799 0 : Matrix mat = mDT->GetTransform();
800 0 : if (!ignoreScale &&
801 0 : (!WITHIN_E(mat._11,1.0) || !WITHIN_E(mat._22,1.0) ||
802 0 : !WITHIN_E(mat._12,0.0) || !WITHIN_E(mat._21,0.0)))
803 0 : return false;
804 : }
805 : #undef WITHIN_E
806 :
807 0 : gfxPoint p1 = UserToDevice(rect.TopLeft());
808 0 : gfxPoint p2 = UserToDevice(rect.TopRight());
809 0 : gfxPoint p3 = UserToDevice(rect.BottomRight());
810 :
811 : // Check that the rectangle is axis-aligned. For an axis-aligned rectangle,
812 : // two opposite corners define the entire rectangle. So check if
813 : // the axis-aligned rectangle with opposite corners p1 and p3
814 : // define an axis-aligned rectangle whose other corners are p2 and p4.
815 : // We actually only need to check one of p2 and p4, since an affine
816 : // transform maps parallelograms to parallelograms.
817 0 : if (p2 == gfxPoint(p1.x, p3.y) || p2 == gfxPoint(p3.x, p1.y)) {
818 0 : p1.Round();
819 0 : p3.Round();
820 :
821 0 : rect.MoveTo(gfxPoint(NS_MIN(p1.x, p3.x), NS_MIN(p1.y, p3.y)));
822 0 : rect.SizeTo(gfxSize(NS_MAX(p1.x, p3.x) - rect.X(),
823 0 : NS_MAX(p1.y, p3.y) - rect.Y()));
824 0 : return true;
825 : }
826 :
827 0 : return false;
828 : }
829 :
830 : bool
831 0 : gfxContext::UserToDevicePixelSnapped(gfxPoint& pt, bool ignoreScale) const
832 : {
833 0 : if (GetFlags() & FLAG_DISABLE_SNAPPING)
834 0 : return false;
835 :
836 : // if we're not at 1.0 scale, don't snap, unless we're
837 : // ignoring the scale. If we're not -just- a scale,
838 : // never snap.
839 0 : const gfxFloat epsilon = 0.0000001;
840 : #define WITHIN_E(a,b) (fabs((a)-(b)) < epsilon)
841 0 : if (mCairo) {
842 : cairo_matrix_t mat;
843 0 : cairo_get_matrix(mCairo, &mat);
844 0 : if (!ignoreScale &&
845 0 : (!WITHIN_E(mat.xx,1.0) || !WITHIN_E(mat.yy,1.0) ||
846 0 : !WITHIN_E(mat.xy,0.0) || !WITHIN_E(mat.yx,0.0)))
847 0 : return false;
848 : } else {
849 0 : Matrix mat = mDT->GetTransform();
850 0 : if (!ignoreScale &&
851 0 : (!WITHIN_E(mat._11,1.0) || !WITHIN_E(mat._22,1.0) ||
852 0 : !WITHIN_E(mat._12,0.0) || !WITHIN_E(mat._21,0.0)))
853 0 : return false;
854 : }
855 : #undef WITHIN_E
856 :
857 0 : pt = UserToDevice(pt);
858 0 : pt.Round();
859 0 : return true;
860 : }
861 :
862 : void
863 0 : gfxContext::PixelSnappedRectangleAndSetPattern(const gfxRect& rect,
864 : gfxPattern *pattern)
865 : {
866 0 : gfxRect r(rect);
867 :
868 : // Bob attempts to pixel-snap the rectangle, and returns true if
869 : // the snapping succeeds. If it does, we need to set up an
870 : // identity matrix, because the rectangle given back is in device
871 : // coordinates.
872 : //
873 : // We then have to call a translate to dr.pos afterwards, to make
874 : // sure the image lines up in the right place with our pixel
875 : // snapped rectangle.
876 : //
877 : // If snapping wasn't successful, we just translate to where the
878 : // pattern would normally start (in app coordinates) and do the
879 : // same thing.
880 0 : Rectangle(r, true);
881 0 : SetPattern(pattern);
882 0 : }
883 :
884 : void
885 0 : gfxContext::SetAntialiasMode(AntialiasMode mode)
886 : {
887 0 : if (mCairo) {
888 0 : if (mode == MODE_ALIASED) {
889 0 : cairo_set_antialias(mCairo, CAIRO_ANTIALIAS_NONE);
890 0 : } else if (mode == MODE_COVERAGE) {
891 0 : cairo_set_antialias(mCairo, CAIRO_ANTIALIAS_DEFAULT);
892 : }
893 : } else {
894 0 : if (mode == MODE_ALIASED) {
895 0 : CurrentState().aaMode = AA_NONE;
896 0 : } else if (mode == MODE_COVERAGE) {
897 0 : CurrentState().aaMode = AA_SUBPIXEL;
898 : }
899 : }
900 0 : }
901 :
902 : gfxContext::AntialiasMode
903 0 : gfxContext::CurrentAntialiasMode() const
904 : {
905 0 : if (mCairo) {
906 0 : cairo_antialias_t aa = cairo_get_antialias(mCairo);
907 0 : if (aa == CAIRO_ANTIALIAS_NONE)
908 0 : return MODE_ALIASED;
909 0 : return MODE_COVERAGE;
910 : } else {
911 0 : if (CurrentState().aaMode == AA_NONE) {
912 0 : return MODE_ALIASED;
913 : }
914 0 : return MODE_COVERAGE;
915 : }
916 : }
917 :
918 : void
919 0 : gfxContext::SetDash(gfxLineType ltype)
920 : {
921 : static double dash[] = {5.0, 5.0};
922 : static double dot[] = {1.0, 1.0};
923 :
924 0 : switch (ltype) {
925 : case gfxLineDashed:
926 0 : SetDash(dash, 2, 0.0);
927 0 : break;
928 : case gfxLineDotted:
929 0 : SetDash(dot, 2, 0.0);
930 0 : break;
931 : case gfxLineSolid:
932 : default:
933 0 : SetDash(nsnull, 0, 0.0);
934 0 : break;
935 : }
936 0 : }
937 :
938 : void
939 0 : gfxContext::SetDash(gfxFloat *dashes, int ndash, gfxFloat offset)
940 : {
941 0 : if (mCairo) {
942 0 : cairo_set_dash(mCairo, dashes, ndash, offset);
943 : } else {
944 0 : AzureState &state = CurrentState();
945 :
946 0 : state.dashPattern.SetLength(ndash);
947 0 : for (int i = 0; i < ndash; i++) {
948 0 : state.dashPattern[i] = Float(dashes[i]);
949 : }
950 0 : state.strokeOptions.mDashLength = ndash;
951 0 : state.strokeOptions.mDashOffset = Float(offset);
952 0 : state.strokeOptions.mDashPattern = ndash ? state.dashPattern.Elements() : NULL;
953 : }
954 0 : }
955 :
956 : bool
957 0 : gfxContext::CurrentDash(FallibleTArray<gfxFloat>& dashes, gfxFloat* offset) const
958 : {
959 0 : if (mCairo) {
960 0 : int count = cairo_get_dash_count(mCairo);
961 0 : if (count <= 0 || !dashes.SetLength(count)) {
962 0 : return false;
963 : }
964 0 : cairo_get_dash(mCairo, dashes.Elements(), offset);
965 0 : return true;
966 : } else {
967 0 : const AzureState &state = CurrentState();
968 0 : int count = state.strokeOptions.mDashLength;
969 :
970 0 : if (count <= 0 || !dashes.SetLength(count)) {
971 0 : return false;
972 : }
973 :
974 0 : for (int i = 0; i < count; i++) {
975 0 : dashes[i] = state.dashPattern[i];
976 : }
977 :
978 0 : *offset = state.strokeOptions.mDashOffset;
979 :
980 0 : return true;
981 : }
982 : }
983 :
984 : gfxFloat
985 0 : gfxContext::CurrentDashOffset() const
986 : {
987 0 : if (mCairo) {
988 0 : if (cairo_get_dash_count(mCairo) <= 0) {
989 0 : return 0.0;
990 : }
991 : gfxFloat offset;
992 0 : cairo_get_dash(mCairo, NULL, &offset);
993 0 : return offset;
994 : } else {
995 0 : return CurrentState().strokeOptions.mDashOffset;
996 : }
997 : }
998 :
999 : void
1000 0 : gfxContext::SetLineWidth(gfxFloat width)
1001 : {
1002 0 : if (mCairo) {
1003 0 : cairo_set_line_width(mCairo, width);
1004 : } else {
1005 0 : CurrentState().strokeOptions.mLineWidth = Float(width);
1006 : }
1007 0 : }
1008 :
1009 : gfxFloat
1010 0 : gfxContext::CurrentLineWidth() const
1011 : {
1012 0 : if (mCairo) {
1013 0 : return cairo_get_line_width(mCairo);
1014 : } else {
1015 0 : return CurrentState().strokeOptions.mLineWidth;
1016 : }
1017 : }
1018 :
1019 : void
1020 64 : gfxContext::SetOperator(GraphicsOperator op)
1021 : {
1022 64 : if (mCairo) {
1023 64 : if (mFlags & FLAG_SIMPLIFY_OPERATORS) {
1024 0 : if (op != OPERATOR_SOURCE &&
1025 : op != OPERATOR_CLEAR &&
1026 : op != OPERATOR_OVER)
1027 0 : op = OPERATOR_OVER;
1028 : }
1029 :
1030 64 : cairo_set_operator(mCairo, (cairo_operator_t)op);
1031 : } else {
1032 0 : if (op == OPERATOR_CLEAR) {
1033 0 : CurrentState().opIsClear = true;
1034 0 : return;
1035 : }
1036 0 : CurrentState().opIsClear = false;
1037 0 : CurrentState().op = CompositionOpForOp(op);
1038 : }
1039 : }
1040 :
1041 : gfxContext::GraphicsOperator
1042 0 : gfxContext::CurrentOperator() const
1043 : {
1044 0 : if (mCairo) {
1045 0 : return (GraphicsOperator)cairo_get_operator(mCairo);
1046 : } else {
1047 0 : return ThebesOp(CurrentState().op);
1048 : }
1049 : }
1050 :
1051 : void
1052 0 : gfxContext::SetLineCap(GraphicsLineCap cap)
1053 : {
1054 0 : if (mCairo) {
1055 0 : cairo_set_line_cap(mCairo, (cairo_line_cap_t)cap);
1056 : } else {
1057 0 : CurrentState().strokeOptions.mLineCap = ToCapStyle(cap);
1058 : }
1059 0 : }
1060 :
1061 : gfxContext::GraphicsLineCap
1062 0 : gfxContext::CurrentLineCap() const
1063 : {
1064 0 : if (mCairo) {
1065 0 : return (GraphicsLineCap)cairo_get_line_cap(mCairo);
1066 : } else {
1067 0 : return ThebesLineCap(CurrentState().strokeOptions.mLineCap);
1068 : }
1069 : }
1070 :
1071 : void
1072 0 : gfxContext::SetLineJoin(GraphicsLineJoin join)
1073 : {
1074 0 : if (mCairo) {
1075 0 : cairo_set_line_join(mCairo, (cairo_line_join_t)join);
1076 : } else {
1077 0 : CurrentState().strokeOptions.mLineJoin = ToJoinStyle(join);
1078 : }
1079 0 : }
1080 :
1081 : gfxContext::GraphicsLineJoin
1082 0 : gfxContext::CurrentLineJoin() const
1083 : {
1084 0 : if (mCairo) {
1085 0 : return (GraphicsLineJoin)cairo_get_line_join(mCairo);
1086 : } else {
1087 0 : return ThebesLineJoin(CurrentState().strokeOptions.mLineJoin);
1088 : }
1089 : }
1090 :
1091 : void
1092 0 : gfxContext::SetMiterLimit(gfxFloat limit)
1093 : {
1094 0 : if (mCairo) {
1095 0 : cairo_set_miter_limit(mCairo, limit);
1096 : } else {
1097 0 : CurrentState().strokeOptions.mMiterLimit = Float(limit);
1098 : }
1099 0 : }
1100 :
1101 : gfxFloat
1102 0 : gfxContext::CurrentMiterLimit() const
1103 : {
1104 0 : if (mCairo) {
1105 0 : return cairo_get_miter_limit(mCairo);
1106 : } else {
1107 0 : return CurrentState().strokeOptions.mMiterLimit;
1108 : }
1109 : }
1110 :
1111 : void
1112 0 : gfxContext::SetFillRule(FillRule rule)
1113 : {
1114 0 : if (mCairo) {
1115 0 : cairo_set_fill_rule(mCairo, (cairo_fill_rule_t)rule);
1116 : } else {
1117 0 : CurrentState().fillRule = rule == FILL_RULE_WINDING ? FILL_WINDING : FILL_EVEN_ODD;
1118 : }
1119 0 : }
1120 :
1121 : gfxContext::FillRule
1122 0 : gfxContext::CurrentFillRule() const
1123 : {
1124 0 : if (mCairo) {
1125 0 : return (FillRule)cairo_get_fill_rule(mCairo);
1126 : } else {
1127 0 : return FILL_RULE_WINDING;
1128 : }
1129 : }
1130 :
1131 : // clipping
1132 : void
1133 0 : gfxContext::Clip(const gfxRect& rect)
1134 : {
1135 0 : if (mCairo) {
1136 0 : cairo_new_path(mCairo);
1137 0 : cairo_rectangle(mCairo, rect.X(), rect.Y(), rect.Width(), rect.Height());
1138 0 : cairo_clip(mCairo);
1139 : } else {
1140 0 : AzureState::PushedClip clip = { NULL, ToRect(rect), mDT->GetTransform() };
1141 0 : CurrentState().pushedClips.AppendElement(clip);
1142 0 : mDT->PushClipRect(ToRect(rect));
1143 0 : NewPath();
1144 : }
1145 0 : }
1146 :
1147 : void
1148 0 : gfxContext::Clip()
1149 : {
1150 0 : if (mCairo) {
1151 0 : cairo_clip_preserve(mCairo);
1152 : } else {
1153 0 : if (mPathIsRect && !mTransformChanged) {
1154 0 : AzureState::PushedClip clip = { NULL, mRect, mDT->GetTransform() };
1155 0 : CurrentState().pushedClips.AppendElement(clip);
1156 0 : mDT->PushClipRect(mRect);
1157 : } else {
1158 0 : EnsurePath();
1159 0 : mDT->PushClip(mPath);
1160 0 : AzureState::PushedClip clip = { mPath, Rect(), mDT->GetTransform() };
1161 0 : CurrentState().pushedClips.AppendElement(clip);
1162 : }
1163 : }
1164 0 : }
1165 :
1166 : void
1167 0 : gfxContext::ResetClip()
1168 : {
1169 0 : if (mCairo) {
1170 0 : cairo_reset_clip(mCairo);
1171 : } else {
1172 0 : for (int i = mStateStack.Length() - 1; i >= 0; i--) {
1173 0 : for (unsigned int c = 0; c < mStateStack[i].pushedClips.Length(); c++) {
1174 0 : mDT->PopClip();
1175 : }
1176 :
1177 0 : if (mStateStack[i].clipWasReset) {
1178 0 : break;
1179 : }
1180 : }
1181 0 : CurrentState().pushedClips.Clear();
1182 0 : CurrentState().clipWasReset = true;
1183 : }
1184 0 : }
1185 :
1186 : void
1187 0 : gfxContext::UpdateSurfaceClip()
1188 : {
1189 0 : if (mCairo) {
1190 0 : NewPath();
1191 : // we paint an empty rectangle to ensure the clip is propagated to
1192 : // the destination surface
1193 0 : SetDeviceColor(gfxRGBA(0,0,0,0));
1194 0 : Rectangle(gfxRect(0,1,1,0));
1195 0 : Fill();
1196 : }
1197 0 : }
1198 :
1199 : gfxRect
1200 0 : gfxContext::GetClipExtents()
1201 : {
1202 0 : if (mCairo) {
1203 : double xmin, ymin, xmax, ymax;
1204 0 : cairo_clip_extents(mCairo, &xmin, &ymin, &xmax, &ymax);
1205 0 : return gfxRect(xmin, ymin, xmax - xmin, ymax - ymin);
1206 : } else {
1207 0 : unsigned int lastReset = 0;
1208 0 : for (int i = mStateStack.Length() - 1; i > 0; i--) {
1209 0 : if (mStateStack[i].clipWasReset) {
1210 0 : lastReset = i;
1211 : }
1212 : }
1213 :
1214 0 : Rect rect(0, 0, Float(mDT->GetSize().width), Float(mDT->GetSize().height));
1215 0 : for (unsigned int i = lastReset; i < mStateStack.Length(); i++) {
1216 0 : for (unsigned int c = 0; c < mStateStack[i].pushedClips.Length(); c++) {
1217 0 : AzureState::PushedClip &clip = mStateStack[i].pushedClips[c];
1218 0 : if (clip.path) {
1219 0 : Rect bounds = clip.path->GetBounds(clip.transform);
1220 0 : rect.IntersectRect(rect, bounds);
1221 : } else {
1222 0 : rect.IntersectRect(rect, clip.transform.TransformBounds(clip.rect));
1223 : }
1224 : }
1225 : }
1226 :
1227 0 : if (rect.width == 0 || rect.height == 0) {
1228 0 : return gfxRect(0, 0, 0, 0);
1229 : }
1230 :
1231 0 : Matrix mat = mDT->GetTransform();
1232 0 : mat.Invert();
1233 0 : rect = mat.TransformBounds(rect);
1234 :
1235 0 : return ThebesRect(rect);
1236 : }
1237 : }
1238 :
1239 : bool
1240 0 : gfxContext::ClipContainsRect(const gfxRect& aRect)
1241 : {
1242 0 : if (mCairo) {
1243 : cairo_rectangle_list_t *clip =
1244 0 : cairo_copy_clip_rectangle_list(mCairo);
1245 :
1246 0 : bool result = false;
1247 :
1248 0 : if (clip->status == CAIRO_STATUS_SUCCESS) {
1249 0 : for (int i = 0; i < clip->num_rectangles; i++) {
1250 0 : gfxRect rect(clip->rectangles[i].x, clip->rectangles[i].y,
1251 0 : clip->rectangles[i].width, clip->rectangles[i].height);
1252 0 : if (rect.Contains(aRect)) {
1253 0 : result = true;
1254 0 : break;
1255 : }
1256 : }
1257 : }
1258 :
1259 0 : cairo_rectangle_list_destroy(clip);
1260 0 : return result;
1261 : } else {
1262 0 : unsigned int lastReset = 0;
1263 0 : for (int i = mStateStack.Length() - 2; i > 0; i--) {
1264 0 : if (mStateStack[i].clipWasReset) {
1265 0 : lastReset = i;
1266 : }
1267 : }
1268 :
1269 : // Since we always return false when the clip list contains a
1270 : // non-rectangular clip or a non-rectilinear transform, our 'total' clip
1271 : // is always a rectangle if we hit the end of this function.
1272 0 : Rect clipBounds(0, 0, Float(mDT->GetSize().width), Float(mDT->GetSize().height));
1273 :
1274 0 : for (unsigned int i = lastReset; i < mStateStack.Length(); i++) {
1275 0 : for (unsigned int c = 0; c < mStateStack[i].pushedClips.Length(); c++) {
1276 0 : AzureState::PushedClip &clip = mStateStack[i].pushedClips[c];
1277 0 : if (clip.path || !clip.transform.IsRectilinear()) {
1278 : // Cairo behavior is we return false if the clip contains a non-
1279 : // rectangle.
1280 0 : return false;
1281 : } else {
1282 0 : Rect clipRect = mTransform.TransformBounds(clip.rect);
1283 :
1284 0 : clipBounds.IntersectRect(clipBounds, clipRect);
1285 : }
1286 : }
1287 : }
1288 :
1289 0 : return clipBounds.Contains(ToRect(aRect));
1290 : }
1291 : }
1292 :
1293 : // rendering sources
1294 :
1295 : void
1296 0 : gfxContext::SetColor(const gfxRGBA& c)
1297 : {
1298 0 : if (mCairo) {
1299 0 : if (gfxPlatform::GetCMSMode() == eCMSMode_All) {
1300 :
1301 0 : gfxRGBA cms;
1302 0 : gfxPlatform::TransformPixel(c, cms, gfxPlatform::GetCMSRGBTransform());
1303 :
1304 : // Use the original alpha to avoid unnecessary float->byte->float
1305 : // conversion errors
1306 0 : cairo_set_source_rgba(mCairo, cms.r, cms.g, cms.b, c.a);
1307 : }
1308 : else
1309 0 : cairo_set_source_rgba(mCairo, c.r, c.g, c.b, c.a);
1310 : } else {
1311 0 : CurrentState().pattern = NULL;
1312 0 : CurrentState().sourceSurface = NULL;
1313 :
1314 0 : if (gfxPlatform::GetCMSMode() == eCMSMode_All) {
1315 :
1316 0 : gfxRGBA cms;
1317 0 : gfxPlatform::TransformPixel(c, cms, gfxPlatform::GetCMSRGBTransform());
1318 :
1319 : // Use the original alpha to avoid unnecessary float->byte->float
1320 : // conversion errors
1321 0 : CurrentState().color = ToColor(cms);
1322 : }
1323 : else
1324 0 : CurrentState().color = ToColor(c);
1325 : }
1326 0 : }
1327 :
1328 : void
1329 0 : gfxContext::SetDeviceColor(const gfxRGBA& c)
1330 : {
1331 0 : if (mCairo) {
1332 0 : cairo_set_source_rgba(mCairo, c.r, c.g, c.b, c.a);
1333 : } else {
1334 0 : CurrentState().pattern = NULL;
1335 0 : CurrentState().sourceSurface = NULL;
1336 0 : CurrentState().color = ToColor(c);
1337 : }
1338 0 : }
1339 :
1340 : bool
1341 0 : gfxContext::GetDeviceColor(gfxRGBA& c)
1342 : {
1343 0 : if (mCairo) {
1344 : return cairo_pattern_get_rgba(cairo_get_source(mCairo),
1345 : &c.r,
1346 : &c.g,
1347 : &c.b,
1348 0 : &c.a) == CAIRO_STATUS_SUCCESS;
1349 : } else {
1350 0 : if (CurrentState().sourceSurface) {
1351 0 : return false;
1352 : }
1353 0 : if (CurrentState().pattern) {
1354 0 : gfxRGBA color;
1355 0 : return CurrentState().pattern->GetSolidColor(c);
1356 : }
1357 :
1358 0 : c = ThebesRGBA(CurrentState().color);
1359 0 : return true;
1360 : }
1361 : }
1362 :
1363 : void
1364 28 : gfxContext::SetSource(gfxASurface *surface, const gfxPoint& offset)
1365 : {
1366 28 : if (mCairo) {
1367 28 : NS_ASSERTION(surface->GetAllowUseAsSource(), "Surface not allowed to be used as source!");
1368 28 : cairo_set_source_surface(mCairo, surface->CairoSurface(), offset.x, offset.y);
1369 : } else {
1370 0 : CurrentState().surfTransform = Matrix(1.0f, 0, 0, 1.0f, Float(offset.x), Float(offset.y));
1371 0 : CurrentState().pattern = NULL;
1372 0 : CurrentState().sourceSurface =
1373 0 : gfxPlatform::GetPlatform()->GetSourceSurfaceForSurface(mDT, surface);
1374 : }
1375 28 : }
1376 :
1377 : void
1378 19 : gfxContext::SetPattern(gfxPattern *pattern)
1379 : {
1380 19 : if (mCairo) {
1381 19 : cairo_set_source(mCairo, pattern->CairoPattern());
1382 : } else {
1383 0 : CurrentState().sourceSurface = NULL;
1384 0 : CurrentState().pattern = pattern;
1385 : }
1386 19 : }
1387 :
1388 : already_AddRefed<gfxPattern>
1389 0 : gfxContext::GetPattern()
1390 : {
1391 0 : if (mCairo) {
1392 0 : cairo_pattern_t *pat = cairo_get_source(mCairo);
1393 0 : NS_ASSERTION(pat, "I was told this couldn't be null");
1394 :
1395 0 : gfxPattern *wrapper = nsnull;
1396 0 : if (pat)
1397 0 : wrapper = new gfxPattern(pat);
1398 : else
1399 0 : wrapper = new gfxPattern(gfxRGBA(0,0,0,0));
1400 :
1401 0 : NS_IF_ADDREF(wrapper);
1402 0 : return wrapper;
1403 : } else {
1404 0 : nsRefPtr<gfxPattern> pat;
1405 :
1406 0 : AzureState &state = CurrentState();
1407 0 : if (state.pattern) {
1408 0 : pat = state.pattern;
1409 0 : } else if (state.sourceSurface) {
1410 0 : NS_ASSERTION(false, "Ugh, this isn't good.");
1411 : } else {
1412 0 : pat = new gfxPattern(ThebesRGBA(state.color));
1413 : }
1414 0 : return pat.forget();
1415 : }
1416 : }
1417 :
1418 :
1419 : // masking
1420 :
1421 : void
1422 0 : gfxContext::Mask(gfxPattern *pattern)
1423 : {
1424 0 : if (mCairo) {
1425 0 : cairo_mask(mCairo, pattern->CairoPattern());
1426 : } else {
1427 0 : mDT->Mask(GeneralPattern(this), *pattern->GetPattern(mDT), DrawOptions(1.0f, CurrentState().op, CurrentState().aaMode));
1428 : }
1429 0 : }
1430 :
1431 : void
1432 0 : gfxContext::Mask(gfxASurface *surface, const gfxPoint& offset)
1433 : {
1434 0 : if (mCairo) {
1435 0 : cairo_mask_surface(mCairo, surface->CairoSurface(), offset.x, offset.y);
1436 : } else {
1437 : RefPtr<SourceSurface> sourceSurf =
1438 0 : gfxPlatform::GetPlatform()->GetSourceSurfaceForSurface(mDT, surface);
1439 :
1440 0 : gfxPoint pt = surface->GetDeviceOffset();
1441 0 : mDT->Mask(GeneralPattern(this),
1442 : SurfacePattern(sourceSurf, EXTEND_CLAMP,
1443 0 : Matrix(1.0f, 0, 0, 1.0f, Float(offset.x - pt.x), Float(offset.y - pt.y))),
1444 0 : DrawOptions(1.0f, CurrentState().op, CurrentState().aaMode));
1445 : }
1446 0 : }
1447 :
1448 : void
1449 43 : gfxContext::Paint(gfxFloat alpha)
1450 : {
1451 43 : if (mCairo) {
1452 43 : cairo_paint_with_alpha(mCairo, alpha);
1453 : } else {
1454 0 : AzureState &state = CurrentState();
1455 :
1456 0 : Matrix mat = mDT->GetTransform();
1457 0 : mat.Invert();
1458 0 : Rect paintRect = mat.TransformBounds(Rect(Point(0, 0), Size(mDT->GetSize())));
1459 :
1460 0 : if (state.opIsClear) {
1461 0 : mDT->ClearRect(paintRect);
1462 : } else {
1463 0 : mDT->FillRect(paintRect, GeneralPattern(this),
1464 0 : DrawOptions(Float(alpha), GetOp()));
1465 : }
1466 : }
1467 43 : }
1468 :
1469 : // groups
1470 :
1471 : void
1472 0 : gfxContext::PushGroup(gfxASurface::gfxContentType content)
1473 : {
1474 0 : if (mCairo) {
1475 0 : cairo_push_group_with_content(mCairo, (cairo_content_t) content);
1476 : } else {
1477 : RefPtr<DrawTarget> newDT =
1478 0 : mDT->CreateSimilarDrawTarget(mDT->GetSize(), FormatForContent(content));
1479 :
1480 0 : Save();
1481 :
1482 0 : CurrentState().drawTarget = newDT;
1483 :
1484 0 : PushClipsToDT(newDT);
1485 0 : newDT->SetTransform(mDT->GetTransform());
1486 0 : mDT = newDT;
1487 : }
1488 0 : }
1489 :
1490 : static gfxRect
1491 0 : GetRoundOutDeviceClipExtents(gfxContext* aCtx)
1492 : {
1493 0 : gfxContextMatrixAutoSaveRestore save(aCtx);
1494 0 : aCtx->IdentityMatrix();
1495 0 : gfxRect r = aCtx->GetClipExtents();
1496 0 : r.RoundOut();
1497 : return r;
1498 : }
1499 :
1500 : /**
1501 : * Copy the contents of aSrc to aDest, translated by aTranslation.
1502 : */
1503 : static void
1504 0 : CopySurface(gfxASurface* aSrc, gfxASurface* aDest, const gfxPoint& aTranslation)
1505 : {
1506 0 : cairo_t *cr = cairo_create(aDest->CairoSurface());
1507 0 : cairo_set_source_surface(cr, aSrc->CairoSurface(), aTranslation.x, aTranslation.y);
1508 0 : cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
1509 0 : cairo_paint(cr);
1510 0 : cairo_destroy(cr);
1511 0 : }
1512 :
1513 : void
1514 0 : gfxContext::PushGroupAndCopyBackground(gfxASurface::gfxContentType content)
1515 : {
1516 0 : if (mCairo) {
1517 0 : if (content == gfxASurface::CONTENT_COLOR_ALPHA &&
1518 0 : !(GetFlags() & FLAG_DISABLE_COPY_BACKGROUND)) {
1519 0 : nsRefPtr<gfxASurface> s = CurrentSurface();
1520 0 : if ((s->GetAllowUseAsSource() || s->GetType() == gfxASurface::SurfaceTypeTee) &&
1521 0 : (s->GetContentType() == gfxASurface::CONTENT_COLOR ||
1522 0 : s->GetOpaqueRect().Contains(GetRoundOutDeviceClipExtents(this)))) {
1523 0 : cairo_push_group_with_content(mCairo, CAIRO_CONTENT_COLOR);
1524 0 : nsRefPtr<gfxASurface> d = CurrentSurface();
1525 :
1526 0 : if (d->GetType() == gfxASurface::SurfaceTypeTee) {
1527 0 : NS_ASSERTION(s->GetType() == gfxASurface::SurfaceTypeTee, "Mismatched types");
1528 0 : nsAutoTArray<nsRefPtr<gfxASurface>,2> ss;
1529 0 : nsAutoTArray<nsRefPtr<gfxASurface>,2> ds;
1530 0 : static_cast<gfxTeeSurface*>(s.get())->GetSurfaces(&ss);
1531 0 : static_cast<gfxTeeSurface*>(d.get())->GetSurfaces(&ds);
1532 0 : NS_ASSERTION(ss.Length() == ds.Length(), "Mismatched lengths");
1533 0 : gfxPoint translation = d->GetDeviceOffset() - s->GetDeviceOffset();
1534 0 : for (PRUint32 i = 0; i < ss.Length(); ++i) {
1535 0 : CopySurface(ss[i], ds[i], translation);
1536 : }
1537 : } else {
1538 0 : CopySurface(s, d, gfxPoint(0, 0));
1539 : }
1540 0 : d->SetOpaqueRect(s->GetOpaqueRect());
1541 : return;
1542 : }
1543 : }
1544 0 : cairo_push_group_with_content(mCairo, (cairo_content_t) content);
1545 : } else {
1546 0 : RefPtr<SourceSurface> source = mDT->Snapshot();
1547 0 : PushGroup(content);
1548 0 : Rect surfRect(0, 0, Float(mDT->GetSize().width), Float(mDT->GetSize().height));
1549 0 : mDT->DrawSurface(source, surfRect, surfRect);
1550 : }
1551 : }
1552 :
1553 : already_AddRefed<gfxPattern>
1554 0 : gfxContext::PopGroup()
1555 : {
1556 0 : if (mCairo) {
1557 0 : cairo_pattern_t *pat = cairo_pop_group(mCairo);
1558 0 : gfxPattern *wrapper = new gfxPattern(pat);
1559 0 : cairo_pattern_destroy(pat);
1560 0 : NS_IF_ADDREF(wrapper);
1561 0 : return wrapper;
1562 : } else {
1563 0 : RefPtr<SourceSurface> src = mDT->Snapshot();
1564 :
1565 0 : Restore();
1566 :
1567 0 : Matrix mat = mDT->GetTransform();
1568 0 : mat.Invert();
1569 0 : nsRefPtr<gfxPattern> pat = new gfxPattern(src, mat);
1570 :
1571 0 : return pat.forget();
1572 : }
1573 : }
1574 :
1575 : void
1576 0 : gfxContext::PopGroupToSource()
1577 : {
1578 0 : if (mCairo) {
1579 0 : cairo_pop_group_to_source(mCairo);
1580 : } else {
1581 0 : RefPtr<SourceSurface> src = mDT->Snapshot();
1582 0 : Restore();
1583 0 : CurrentState().sourceSurface = src;
1584 0 : CurrentState().pattern = NULL;
1585 :
1586 0 : Matrix mat = mDT->GetTransform();
1587 0 : mat.Invert();
1588 0 : CurrentState().surfTransform = mat;
1589 : }
1590 0 : }
1591 :
1592 : bool
1593 0 : gfxContext::PointInFill(const gfxPoint& pt)
1594 : {
1595 0 : if (mCairo) {
1596 0 : return cairo_in_fill(mCairo, pt.x, pt.y);
1597 : } else {
1598 0 : return mPath->ContainsPoint(ToPoint(pt), mTransform);
1599 : }
1600 : }
1601 :
1602 : bool
1603 0 : gfxContext::PointInStroke(const gfxPoint& pt)
1604 : {
1605 0 : if (mCairo) {
1606 0 : return cairo_in_stroke(mCairo, pt.x, pt.y);
1607 : } else {
1608 : // XXX - Used by SVG, needs fixing.
1609 0 : return false;
1610 : }
1611 : }
1612 :
1613 : gfxRect
1614 0 : gfxContext::GetUserPathExtent()
1615 : {
1616 0 : if (mCairo) {
1617 : double xmin, ymin, xmax, ymax;
1618 0 : cairo_path_extents(mCairo, &xmin, &ymin, &xmax, &ymax);
1619 0 : return gfxRect(xmin, ymin, xmax - xmin, ymax - ymin);
1620 : } else {
1621 0 : return ThebesRect(mPath->GetBounds());
1622 : }
1623 : }
1624 :
1625 : gfxRect
1626 0 : gfxContext::GetUserFillExtent()
1627 : {
1628 0 : if (mCairo) {
1629 : double xmin, ymin, xmax, ymax;
1630 0 : cairo_fill_extents(mCairo, &xmin, &ymin, &xmax, &ymax);
1631 0 : return gfxRect(xmin, ymin, xmax - xmin, ymax - ymin);
1632 : } else {
1633 0 : return ThebesRect(mPath->GetBounds());
1634 : }
1635 : }
1636 :
1637 : gfxRect
1638 0 : gfxContext::GetUserStrokeExtent()
1639 : {
1640 0 : if (mCairo) {
1641 : double xmin, ymin, xmax, ymax;
1642 0 : cairo_stroke_extents(mCairo, &xmin, &ymin, &xmax, &ymax);
1643 0 : return gfxRect(xmin, ymin, xmax - xmin, ymax - ymin);
1644 : } else {
1645 0 : return ThebesRect(mPath->GetStrokedBounds(CurrentState().strokeOptions, mTransform));
1646 : }
1647 : }
1648 :
1649 : already_AddRefed<gfxFlattenedPath>
1650 0 : gfxContext::GetFlattenedPath()
1651 : {
1652 0 : if (mCairo) {
1653 : gfxFlattenedPath *path =
1654 0 : new gfxFlattenedPath(cairo_copy_path_flat(mCairo));
1655 0 : NS_IF_ADDREF(path);
1656 0 : return path;
1657 : } else {
1658 : // XXX - Used by SVG, needs fixing.
1659 0 : return NULL;
1660 : }
1661 : }
1662 :
1663 : bool
1664 0 : gfxContext::HasError()
1665 : {
1666 0 : if (mCairo) {
1667 0 : return cairo_status(mCairo) != CAIRO_STATUS_SUCCESS;
1668 : } else {
1669 : // As far as this is concerned, an Azure context is never in error.
1670 0 : return false;
1671 : }
1672 : }
1673 :
1674 : void
1675 0 : gfxContext::RoundedRectangle(const gfxRect& rect,
1676 : const gfxCornerSizes& corners,
1677 : bool draw_clockwise)
1678 : {
1679 : //
1680 : // For CW drawing, this looks like:
1681 : //
1682 : // ...******0** 1 C
1683 : // ****
1684 : // *** 2
1685 : // **
1686 : // *
1687 : // *
1688 : // 3
1689 : // *
1690 : // *
1691 : //
1692 : // Where 0, 1, 2, 3 are the control points of the Bezier curve for
1693 : // the corner, and C is the actual corner point.
1694 : //
1695 : // At the start of the loop, the current point is assumed to be
1696 : // the point adjacent to the top left corner on the top
1697 : // horizontal. Note that corner indices start at the top left and
1698 : // continue clockwise, whereas in our loop i = 0 refers to the top
1699 : // right corner.
1700 : //
1701 : // When going CCW, the control points are swapped, and the first
1702 : // corner that's drawn is the top left (along with the top segment).
1703 : //
1704 : // There is considerable latitude in how one chooses the four
1705 : // control points for a Bezier curve approximation to an ellipse.
1706 : // For the overall path to be continuous and show no corner at the
1707 : // endpoints of the arc, points 0 and 3 must be at the ends of the
1708 : // straight segments of the rectangle; points 0, 1, and C must be
1709 : // collinear; and points 3, 2, and C must also be collinear. This
1710 : // leaves only two free parameters: the ratio of the line segments
1711 : // 01 and 0C, and the ratio of the line segments 32 and 3C. See
1712 : // the following papers for extensive discussion of how to choose
1713 : // these ratios:
1714 : //
1715 : // Dokken, Tor, et al. "Good approximation of circles by
1716 : // curvature-continuous Bezier curves." Computer-Aided
1717 : // Geometric Design 7(1990) 33--41.
1718 : // Goldapp, Michael. "Approximation of circular arcs by cubic
1719 : // polynomials." Computer-Aided Geometric Design 8(1991) 227--238.
1720 : // Maisonobe, Luc. "Drawing an elliptical arc using polylines,
1721 : // quadratic, or cubic Bezier curves."
1722 : // http://www.spaceroots.org/documents/ellipse/elliptical-arc.pdf
1723 : //
1724 : // We follow the approach in section 2 of Goldapp (least-error,
1725 : // Hermite-type approximation) and make both ratios equal to
1726 : //
1727 : // 2 2 + n - sqrt(2n + 28)
1728 : // alpha = - * ---------------------
1729 : // 3 n - 4
1730 : //
1731 : // where n = 3( cbrt(sqrt(2)+1) - cbrt(sqrt(2)-1) ).
1732 : //
1733 : // This is the result of Goldapp's equation (10b) when the angle
1734 : // swept out by the arc is pi/2, and the parameter "a-bar" is the
1735 : // expression given immediately below equation (21).
1736 : //
1737 : // Using this value, the maximum radial error for a circle, as a
1738 : // fraction of the radius, is on the order of 0.2 x 10^-3.
1739 : // Neither Dokken nor Goldapp discusses error for a general
1740 : // ellipse; Maisonobe does, but his choice of control points
1741 : // follows different constraints, and Goldapp's expression for
1742 : // 'alpha' gives much smaller radial error, even for very flat
1743 : // ellipses, than Maisonobe's equivalent.
1744 : //
1745 : // For the various corners and for each axis, the sign of this
1746 : // constant changes, or it might be 0 -- it's multiplied by the
1747 : // appropriate multiplier from the list before using.
1748 :
1749 0 : if (mCairo) {
1750 0 : const gfxFloat alpha = 0.55191497064665766025;
1751 :
1752 : typedef struct { gfxFloat a, b; } twoFloats;
1753 :
1754 : twoFloats cwCornerMults[4] = { { -1, 0 },
1755 : { 0, -1 },
1756 : { +1, 0 },
1757 0 : { 0, +1 } };
1758 : twoFloats ccwCornerMults[4] = { { +1, 0 },
1759 : { 0, -1 },
1760 : { -1, 0 },
1761 0 : { 0, +1 } };
1762 :
1763 0 : twoFloats *cornerMults = draw_clockwise ? cwCornerMults : ccwCornerMults;
1764 :
1765 0 : gfxPoint pc, p0, p1, p2, p3;
1766 :
1767 0 : if (draw_clockwise)
1768 0 : cairo_move_to(mCairo, rect.X() + corners[NS_CORNER_TOP_LEFT].width, rect.Y());
1769 : else
1770 0 : cairo_move_to(mCairo, rect.X() + rect.Width() - corners[NS_CORNER_TOP_RIGHT].width, rect.Y());
1771 :
1772 0 : NS_FOR_CSS_CORNERS(i) {
1773 : // the corner index -- either 1 2 3 0 (cw) or 0 3 2 1 (ccw)
1774 0 : mozilla::css::Corner c = mozilla::css::Corner(draw_clockwise ? ((i+1) % 4) : ((4-i) % 4));
1775 :
1776 : // i+2 and i+3 respectively. These are used to index into the corner
1777 : // multiplier table, and were deduced by calculating out the long form
1778 : // of each corner and finding a pattern in the signs and values.
1779 0 : int i2 = (i+2) % 4;
1780 0 : int i3 = (i+3) % 4;
1781 :
1782 0 : pc = rect.AtCorner(c);
1783 :
1784 0 : if (corners[c].width > 0.0 && corners[c].height > 0.0) {
1785 0 : p0.x = pc.x + cornerMults[i].a * corners[c].width;
1786 0 : p0.y = pc.y + cornerMults[i].b * corners[c].height;
1787 :
1788 0 : p3.x = pc.x + cornerMults[i3].a * corners[c].width;
1789 0 : p3.y = pc.y + cornerMults[i3].b * corners[c].height;
1790 :
1791 0 : p1.x = p0.x + alpha * cornerMults[i2].a * corners[c].width;
1792 0 : p1.y = p0.y + alpha * cornerMults[i2].b * corners[c].height;
1793 :
1794 0 : p2.x = p3.x - alpha * cornerMults[i3].a * corners[c].width;
1795 0 : p2.y = p3.y - alpha * cornerMults[i3].b * corners[c].height;
1796 :
1797 0 : cairo_line_to (mCairo, p0.x, p0.y);
1798 : cairo_curve_to (mCairo,
1799 : p1.x, p1.y,
1800 : p2.x, p2.y,
1801 0 : p3.x, p3.y);
1802 : } else {
1803 0 : cairo_line_to (mCairo, pc.x, pc.y);
1804 : }
1805 : }
1806 :
1807 0 : cairo_close_path (mCairo);
1808 : } else {
1809 0 : EnsurePathBuilder();
1810 :
1811 0 : const gfxFloat alpha = 0.55191497064665766025;
1812 :
1813 : typedef struct { gfxFloat a, b; } twoFloats;
1814 :
1815 : twoFloats cwCornerMults[4] = { { -1, 0 },
1816 : { 0, -1 },
1817 : { +1, 0 },
1818 0 : { 0, +1 } };
1819 : twoFloats ccwCornerMults[4] = { { +1, 0 },
1820 : { 0, -1 },
1821 : { -1, 0 },
1822 0 : { 0, +1 } };
1823 :
1824 0 : twoFloats *cornerMults = draw_clockwise ? cwCornerMults : ccwCornerMults;
1825 :
1826 0 : gfxPoint pc, p0, p1, p2, p3;
1827 :
1828 0 : if (draw_clockwise)
1829 0 : mPathBuilder->MoveTo(Point(Float(rect.X() + corners[NS_CORNER_TOP_LEFT].width), Float(rect.Y())));
1830 : else
1831 0 : mPathBuilder->MoveTo(Point(Float(rect.X() + rect.Width() - corners[NS_CORNER_TOP_RIGHT].width), Float(rect.Y())));
1832 :
1833 0 : NS_FOR_CSS_CORNERS(i) {
1834 : // the corner index -- either 1 2 3 0 (cw) or 0 3 2 1 (ccw)
1835 0 : mozilla::css::Corner c = mozilla::css::Corner(draw_clockwise ? ((i+1) % 4) : ((4-i) % 4));
1836 :
1837 : // i+2 and i+3 respectively. These are used to index into the corner
1838 : // multiplier table, and were deduced by calculating out the long form
1839 : // of each corner and finding a pattern in the signs and values.
1840 0 : int i2 = (i+2) % 4;
1841 0 : int i3 = (i+3) % 4;
1842 :
1843 0 : pc = rect.AtCorner(c);
1844 :
1845 0 : if (corners[c].width > 0.0 && corners[c].height > 0.0) {
1846 0 : p0.x = pc.x + cornerMults[i].a * corners[c].width;
1847 0 : p0.y = pc.y + cornerMults[i].b * corners[c].height;
1848 :
1849 0 : p3.x = pc.x + cornerMults[i3].a * corners[c].width;
1850 0 : p3.y = pc.y + cornerMults[i3].b * corners[c].height;
1851 :
1852 0 : p1.x = p0.x + alpha * cornerMults[i2].a * corners[c].width;
1853 0 : p1.y = p0.y + alpha * cornerMults[i2].b * corners[c].height;
1854 :
1855 0 : p2.x = p3.x - alpha * cornerMults[i3].a * corners[c].width;
1856 0 : p2.y = p3.y - alpha * cornerMults[i3].b * corners[c].height;
1857 :
1858 0 : mPathBuilder->LineTo(ToPoint(p0));
1859 0 : mPathBuilder->BezierTo(ToPoint(p1), ToPoint(p2), ToPoint(p3));
1860 : } else {
1861 0 : mPathBuilder->LineTo(ToPoint(pc));
1862 : }
1863 : }
1864 :
1865 0 : mPathBuilder->Close();
1866 : }
1867 0 : }
1868 :
1869 : #ifdef MOZ_DUMP_PAINTING
1870 : void
1871 0 : gfxContext::WriteAsPNG(const char* aFile)
1872 : {
1873 0 : nsRefPtr<gfxASurface> surf = CurrentSurface();
1874 0 : if (surf) {
1875 0 : surf->WriteAsPNG(aFile);
1876 : } else {
1877 0 : NS_WARNING("No surface found!");
1878 : }
1879 0 : }
1880 :
1881 : void
1882 0 : gfxContext::DumpAsDataURL()
1883 : {
1884 0 : nsRefPtr<gfxASurface> surf = CurrentSurface();
1885 0 : if (surf) {
1886 0 : surf->DumpAsDataURL();
1887 : } else {
1888 0 : NS_WARNING("No surface found!");
1889 : }
1890 0 : }
1891 :
1892 : void
1893 0 : gfxContext::CopyAsDataURL()
1894 : {
1895 0 : nsRefPtr<gfxASurface> surf = CurrentSurface();
1896 0 : if (surf) {
1897 0 : surf->CopyAsDataURL();
1898 : } else {
1899 0 : NS_WARNING("No surface found!");
1900 : }
1901 0 : }
1902 : #endif
1903 :
1904 : void
1905 0 : gfxContext::EnsurePath()
1906 : {
1907 0 : if (mPathBuilder) {
1908 0 : mPath = mPathBuilder->Finish();
1909 0 : mPathBuilder = NULL;
1910 : }
1911 :
1912 0 : if (mPath) {
1913 0 : if (mTransformChanged) {
1914 0 : Matrix mat = mDT->GetTransform();
1915 0 : mat.Invert();
1916 0 : mat = mPathTransform * mat;
1917 0 : mPathBuilder = mPath->TransformedCopyToBuilder(mat, CurrentState().fillRule);
1918 0 : mPath = mPathBuilder->Finish();
1919 0 : mPathBuilder = NULL;
1920 :
1921 0 : mTransformChanged = false;
1922 : }
1923 :
1924 0 : if (CurrentState().fillRule == mPath->GetFillRule()) {
1925 0 : return;
1926 : }
1927 :
1928 0 : mPathBuilder = mPath->CopyToBuilder(CurrentState().fillRule);
1929 :
1930 0 : mPath = mPathBuilder->Finish();
1931 0 : mPathBuilder = NULL;
1932 0 : return;
1933 : }
1934 :
1935 0 : EnsurePathBuilder();
1936 0 : mPath = mPathBuilder->Finish();
1937 0 : mPathBuilder = NULL;
1938 : }
1939 :
1940 : void
1941 0 : gfxContext::EnsurePathBuilder()
1942 : {
1943 0 : if (mPathBuilder) {
1944 0 : return;
1945 : }
1946 :
1947 0 : if (mPath) {
1948 0 : mPathBuilder = mPath->CopyToBuilder(CurrentState().fillRule);
1949 0 : mPath = NULL;
1950 : }
1951 :
1952 0 : mPathBuilder = mDT->CreatePathBuilder(CurrentState().fillRule);
1953 :
1954 0 : if (mPathIsRect && !mTransformChanged) {
1955 0 : mPathBuilder->MoveTo(mRect.TopLeft());
1956 0 : mPathBuilder->LineTo(mRect.TopRight());
1957 0 : mPathBuilder->LineTo(mRect.BottomRight());
1958 0 : mPathBuilder->LineTo(mRect.BottomLeft());
1959 0 : mPathBuilder->Close();
1960 0 : } else if (mPathIsRect) {
1961 0 : mTransformChanged = false;
1962 0 : Matrix mat = mDT->GetTransform();
1963 0 : mat.Invert();
1964 0 : mat = mPathTransform * mat;
1965 0 : mPathBuilder->MoveTo(mat * mRect.TopLeft());
1966 0 : mPathBuilder->LineTo(mat * mRect.TopRight());
1967 0 : mPathBuilder->LineTo(mat * mRect.BottomRight());
1968 0 : mPathBuilder->LineTo(mat * mRect.BottomLeft());
1969 0 : mPathBuilder->Close();
1970 : }
1971 :
1972 0 : mPathIsRect = false;
1973 : }
1974 :
1975 : void
1976 0 : gfxContext::FillAzure(Float aOpacity)
1977 : {
1978 0 : AzureState &state = CurrentState();
1979 :
1980 0 : CompositionOp op = GetOp();
1981 :
1982 0 : if (mPathIsRect && !mTransformChanged) {
1983 0 : if (state.opIsClear) {
1984 0 : mDT->ClearRect(mRect);
1985 0 : } else if (op == OP_SOURCE) {
1986 : // Emulate cairo operator source which is bound by mask!
1987 0 : mDT->ClearRect(mRect);
1988 0 : mDT->FillRect(mRect, GeneralPattern(this), DrawOptions(aOpacity));
1989 : } else {
1990 0 : mDT->FillRect(mRect, GeneralPattern(this), DrawOptions(aOpacity, op, state.aaMode));
1991 : }
1992 : } else {
1993 0 : EnsurePath();
1994 :
1995 0 : NS_ASSERTION(!state.opIsClear, "We shouldn't be clearing complex paths!");
1996 :
1997 0 : mDT->Fill(mPath, GeneralPattern(this), DrawOptions(aOpacity, op, state.aaMode));
1998 : }
1999 0 : }
2000 :
2001 : void
2002 0 : gfxContext::PushClipsToDT(DrawTarget *aDT)
2003 : {
2004 : // Tricky, we have to restore all clips -since the last time- the clip
2005 : // was reset. If we didn't reset the clip, just popping the clips we
2006 : // added was fine.
2007 0 : unsigned int lastReset = 0;
2008 0 : for (int i = mStateStack.Length() - 2; i > 0; i--) {
2009 0 : if (mStateStack[i].clipWasReset) {
2010 0 : lastReset = i;
2011 : }
2012 : }
2013 :
2014 : // Don't need to save the old transform, we'll be setting a new one soon!
2015 :
2016 : // Push all clips from the last state on the stack where the clip was
2017 : // reset to the clip before ours.
2018 0 : for (unsigned int i = lastReset; i < mStateStack.Length() - 1; i++) {
2019 0 : for (unsigned int c = 0; c < mStateStack[i].pushedClips.Length(); c++) {
2020 0 : aDT->SetTransform(mStateStack[i].pushedClips[c].transform);
2021 0 : if (mStateStack[i].pushedClips[c].path) {
2022 0 : aDT->PushClip(mStateStack[i].pushedClips[c].path);
2023 : } else {
2024 0 : aDT->PushClipRect(mStateStack[i].pushedClips[c].rect);
2025 : }
2026 : }
2027 : }
2028 0 : }
2029 :
2030 : CompositionOp
2031 0 : gfxContext::GetOp()
2032 : {
2033 0 : if (CurrentState().op != OP_SOURCE) {
2034 0 : return CurrentState().op;
2035 : }
2036 :
2037 0 : AzureState &state = CurrentState();
2038 0 : if (state.pattern) {
2039 0 : if (state.pattern->IsOpaque()) {
2040 0 : return OP_OVER;
2041 : } else {
2042 0 : return OP_SOURCE;
2043 : }
2044 0 : } else if (state.sourceSurface) {
2045 0 : if (state.sourceSurface->GetFormat() == FORMAT_B8G8R8X8) {
2046 0 : return OP_OVER;
2047 : } else {
2048 0 : return OP_SOURCE;
2049 : }
2050 : } else {
2051 0 : if (state.color.a > 0.999) {
2052 0 : return OP_OVER;
2053 : } else {
2054 0 : return OP_SOURCE;
2055 : }
2056 : }
2057 : }
|