Bug 835381 - Update libnestegg to 38c83d9d4c0c5c84373aa285bd30094a12d6b6f6. r=kinetik
[gecko.git] / gfx / thebes / gfxContext.cpp
blob217dedec88daa951134547829f95e36d7b10002b
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/. */
6 #ifdef _MSC_VER
7 #define _USE_MATH_DEFINES
8 #endif
9 #include <math.h>
11 #include "mozilla/Constants.h"
13 #include "cairo.h"
15 #include "gfxContext.h"
17 #include "gfxColor.h"
18 #include "gfxMatrix.h"
19 #include "gfxASurface.h"
20 #include "gfxPattern.h"
21 #include "gfxPlatform.h"
22 #include "gfxTeeSurface.h"
23 #include "sampler.h"
24 #include <algorithm>
26 #if CAIRO_HAS_DWRITE_FONT
27 #include "gfxWindowsPlatform.h"
28 #endif
30 using namespace mozilla;
31 using namespace mozilla::gfx;
33 /* This class lives on the stack and allows gfxContext users to easily, and
34 * performantly get a gfx::Pattern to use for drawing in their current context.
36 class GeneralPattern
38 public:
39 GeneralPattern(gfxContext *aContext) : mContext(aContext), mPattern(NULL) {}
40 ~GeneralPattern() { if (mPattern) { mPattern->~Pattern(); } }
42 operator mozilla::gfx::Pattern&()
44 gfxContext::AzureState &state = mContext->CurrentState();
46 if (state.pattern) {
47 return *state.pattern->GetPattern(mContext->mDT, state.patternTransformChanged ? &state.patternTransform : nullptr);
48 } else if (state.sourceSurface) {
49 Matrix transform = state.surfTransform;
51 if (state.patternTransformChanged) {
52 Matrix mat = mContext->mTransform;
53 mat.Invert();
55 transform = transform * state.patternTransform * mat;
58 mPattern = new (mSurfacePattern.addr())
59 SurfacePattern(state.sourceSurface, EXTEND_CLAMP, transform);
60 return *mPattern;
61 } else {
62 mPattern = new (mColorPattern.addr())
63 ColorPattern(state.color);
64 return *mPattern;
68 private:
69 union {
70 mozilla::AlignedStorage2<mozilla::gfx::ColorPattern> mColorPattern;
71 mozilla::AlignedStorage2<mozilla::gfx::SurfacePattern> mSurfacePattern;
74 gfxContext *mContext;
75 Pattern *mPattern;
78 gfxContext::gfxContext(gfxASurface *surface)
79 : mRefCairo(NULL)
80 , mSurface(surface)
82 MOZ_COUNT_CTOR(gfxContext);
84 mCairo = cairo_create(surface->CairoSurface());
85 mFlags = surface->GetDefaultContextFlags();
86 if (mSurface->GetRotateForLandscape()) {
87 // Rotate page 90 degrees to draw landscape page on portrait paper
88 gfxIntSize size = mSurface->GetSize();
89 Translate(gfxPoint(0, size.width));
90 gfxMatrix matrix(0, -1,
91 1, 0,
92 0, 0);
93 Multiply(matrix);
97 gfxContext::gfxContext(DrawTarget *aTarget)
98 : mPathIsRect(false)
99 , mTransformChanged(false)
100 , mCairo(NULL)
101 , mRefCairo(NULL)
102 , mSurface(NULL)
103 , mFlags(0)
104 , mDT(aTarget)
105 , mOriginalDT(aTarget)
107 MOZ_COUNT_CTOR(gfxContext);
109 mStateStack.SetLength(1);
110 CurrentState().drawTarget = mDT;
111 mDT->SetTransform(Matrix());
114 gfxContext::~gfxContext()
116 if (mCairo) {
117 cairo_destroy(mCairo);
119 if (mRefCairo) {
120 cairo_destroy(mRefCairo);
122 if (mDT) {
123 for (int i = mStateStack.Length() - 1; i >= 0; i--) {
124 for (unsigned int c = 0; c < mStateStack[i].pushedClips.Length(); c++) {
125 mDT->PopClip();
128 if (mStateStack[i].clipWasReset) {
129 break;
132 mDT->Flush();
134 MOZ_COUNT_DTOR(gfxContext);
137 gfxASurface *
138 gfxContext::OriginalSurface()
140 return mSurface;
143 already_AddRefed<gfxASurface>
144 gfxContext::CurrentSurface(gfxFloat *dx, gfxFloat *dy)
146 if (mCairo) {
147 cairo_surface_t *s = cairo_get_group_target(mCairo);
148 if (s == mSurface->CairoSurface()) {
149 if (dx && dy)
150 cairo_surface_get_device_offset(s, dx, dy);
151 gfxASurface *ret = mSurface;
152 NS_ADDREF(ret);
153 return ret;
156 if (dx && dy)
157 cairo_surface_get_device_offset(s, dx, dy);
158 return gfxASurface::Wrap(s);
159 } else {
160 if (dx && dy) {
161 *dx = *dy = 0;
163 // An Azure context doesn't have a surface backing it.
164 return NULL;
168 cairo_t *
169 gfxContext::GetCairo()
171 if (mCairo) {
172 return mCairo;
175 if (mRefCairo) {
176 // Set transform!
177 return mRefCairo;
180 mRefCairo = cairo_create(gfxPlatform::GetPlatform()->ScreenReferenceSurface()->CairoSurface());
182 return mRefCairo;
185 void
186 gfxContext::Save()
188 if (mCairo) {
189 cairo_save(mCairo);
190 } else {
191 CurrentState().transform = mTransform;
192 mStateStack.AppendElement(AzureState(CurrentState()));
193 CurrentState().clipWasReset = false;
194 CurrentState().pushedClips.Clear();
198 void
199 gfxContext::Restore()
201 if (mCairo) {
202 cairo_restore(mCairo);
203 } else {
204 for (unsigned int c = 0; c < CurrentState().pushedClips.Length(); c++) {
205 mDT->PopClip();
208 if (CurrentState().clipWasReset &&
209 CurrentState().drawTarget == mStateStack[mStateStack.Length() - 2].drawTarget) {
210 PushClipsToDT(mDT);
213 mStateStack.RemoveElementAt(mStateStack.Length() - 1);
215 mDT = CurrentState().drawTarget;
217 ChangeTransform(CurrentState().transform, false);
221 // drawing
222 void
223 gfxContext::NewPath()
225 if (mCairo) {
226 cairo_new_path(mCairo);
227 } else {
228 mPath = NULL;
229 mPathBuilder = NULL;
230 mPathIsRect = false;
231 mTransformChanged = false;
235 void
236 gfxContext::ClosePath()
238 if (mCairo) {
239 cairo_close_path(mCairo);
240 } else {
241 EnsurePathBuilder();
242 mPathBuilder->Close();
246 already_AddRefed<gfxPath> gfxContext::CopyPath() const
248 if (mCairo) {
249 nsRefPtr<gfxPath> path = new gfxPath(cairo_copy_path(mCairo));
250 return path.forget();
251 } else {
252 // XXX - This is not yet supported for Azure.
253 return NULL;
257 void gfxContext::AppendPath(gfxPath* path)
259 if (mCairo) {
260 if (path->mPath->status == CAIRO_STATUS_SUCCESS && path->mPath->num_data != 0)
261 cairo_append_path(mCairo, path->mPath);
262 } else {
263 // XXX - This is not yet supported for Azure.
264 return;
268 gfxPoint
269 gfxContext::CurrentPoint()
271 if (mCairo) {
272 double x, y;
273 cairo_get_current_point(mCairo, &x, &y);
274 return gfxPoint(x, y);
275 } else {
276 EnsurePathBuilder();
277 return ThebesPoint(mPathBuilder->CurrentPoint());
281 void
282 gfxContext::Stroke()
284 if (mCairo) {
285 cairo_stroke_preserve(mCairo);
286 } else {
287 AzureState &state = CurrentState();
288 if (mPathIsRect) {
289 MOZ_ASSERT(!mTransformChanged);
291 mDT->StrokeRect(mRect, GeneralPattern(this),
292 state.strokeOptions,
293 DrawOptions(1.0f, GetOp(), state.aaMode));
294 } else {
295 EnsurePath();
297 mDT->Stroke(mPath, GeneralPattern(this), state.strokeOptions,
298 DrawOptions(1.0f, GetOp(), state.aaMode));
303 void
304 gfxContext::Fill()
306 SAMPLE_LABEL("gfxContext", "Fill");
307 if (mCairo) {
308 cairo_fill_preserve(mCairo);
309 } else {
310 FillAzure(1.0f);
314 void
315 gfxContext::FillWithOpacity(gfxFloat aOpacity)
317 if (mCairo) {
318 // This method exists in the hope that one day cairo gets a direct
319 // API for this, and then we would change this method to use that
320 // API instead.
321 if (aOpacity != 1.0) {
322 gfxContextAutoSaveRestore saveRestore(this);
323 Clip();
324 Paint(aOpacity);
325 } else {
326 Fill();
328 } else {
329 FillAzure(Float(aOpacity));
333 void
334 gfxContext::MoveTo(const gfxPoint& pt)
336 if (mCairo) {
337 cairo_move_to(mCairo, pt.x, pt.y);
338 } else {
339 EnsurePathBuilder();
340 mPathBuilder->MoveTo(ToPoint(pt));
344 void
345 gfxContext::NewSubPath()
347 if (mCairo) {
348 cairo_new_sub_path(mCairo);
349 } else {
350 // XXX - This has no users, we should kill it, it should be equivelant to a
351 // MoveTo to the path's current point.
355 void
356 gfxContext::LineTo(const gfxPoint& pt)
358 if (mCairo) {
359 cairo_line_to(mCairo, pt.x, pt.y);
360 } else {
361 EnsurePathBuilder();
362 mPathBuilder->LineTo(ToPoint(pt));
366 void
367 gfxContext::CurveTo(const gfxPoint& pt1, const gfxPoint& pt2, const gfxPoint& pt3)
369 if (mCairo) {
370 cairo_curve_to(mCairo, pt1.x, pt1.y, pt2.x, pt2.y, pt3.x, pt3.y);
371 } else {
372 EnsurePathBuilder();
373 mPathBuilder->BezierTo(ToPoint(pt1), ToPoint(pt2), ToPoint(pt3));
377 void
378 gfxContext::QuadraticCurveTo(const gfxPoint& pt1, const gfxPoint& pt2)
380 if (mCairo) {
381 double cx, cy;
382 cairo_get_current_point(mCairo, &cx, &cy);
383 cairo_curve_to(mCairo,
384 (cx + pt1.x * 2.0) / 3.0,
385 (cy + pt1.y * 2.0) / 3.0,
386 (pt1.x * 2.0 + pt2.x) / 3.0,
387 (pt1.y * 2.0 + pt2.y) / 3.0,
388 pt2.x,
389 pt2.y);
390 } else {
391 EnsurePathBuilder();
392 mPathBuilder->QuadraticBezierTo(ToPoint(pt1), ToPoint(pt2));
396 void
397 gfxContext::Arc(const gfxPoint& center, gfxFloat radius,
398 gfxFloat angle1, gfxFloat angle2)
400 if (mCairo) {
401 cairo_arc(mCairo, center.x, center.y, radius, angle1, angle2);
402 } else {
403 EnsurePathBuilder();
404 mPathBuilder->Arc(ToPoint(center), Float(radius), Float(angle1), Float(angle2));
408 void
409 gfxContext::NegativeArc(const gfxPoint& center, gfxFloat radius,
410 gfxFloat angle1, gfxFloat angle2)
412 if (mCairo) {
413 cairo_arc_negative(mCairo, center.x, center.y, radius, angle1, angle2);
414 } else {
415 EnsurePathBuilder();
416 mPathBuilder->Arc(ToPoint(center), Float(radius), Float(angle2), Float(angle1));
420 void
421 gfxContext::Line(const gfxPoint& start, const gfxPoint& end)
423 if (mCairo) {
424 MoveTo(start);
425 LineTo(end);
426 } else {
427 EnsurePathBuilder();
428 mPathBuilder->MoveTo(ToPoint(start));
429 mPathBuilder->LineTo(ToPoint(end));
433 // XXX snapToPixels is only valid when snapping for filled
434 // rectangles and for even-width stroked rectangles.
435 // For odd-width stroked rectangles, we need to offset x/y by
436 // 0.5...
437 void
438 gfxContext::Rectangle(const gfxRect& rect, bool snapToPixels)
440 if (mCairo) {
441 if (snapToPixels) {
442 gfxRect snappedRect(rect);
444 if (UserToDevicePixelSnapped(snappedRect, true))
446 cairo_matrix_t mat;
447 cairo_get_matrix(mCairo, &mat);
448 cairo_identity_matrix(mCairo);
449 Rectangle(snappedRect);
450 cairo_set_matrix(mCairo, &mat);
452 return;
456 cairo_rectangle(mCairo, rect.X(), rect.Y(), rect.Width(), rect.Height());
457 } else {
458 Rect rec = ToRect(rect);
460 if (snapToPixels) {
461 gfxRect newRect(rect);
462 if (UserToDevicePixelSnapped(newRect, true)) {
463 gfxMatrix mat = ThebesMatrix(mTransform);
464 mat.Invert();
466 // We need the user space rect.
467 rec = ToRect(mat.TransformBounds(newRect));
471 if (!mPathBuilder && !mPathIsRect) {
472 mPathIsRect = true;
473 mRect = rec;
474 return;
477 EnsurePathBuilder();
479 mPathBuilder->MoveTo(rec.TopLeft());
480 mPathBuilder->LineTo(rec.TopRight());
481 mPathBuilder->LineTo(rec.BottomRight());
482 mPathBuilder->LineTo(rec.BottomLeft());
483 mPathBuilder->Close();
487 void
488 gfxContext::Ellipse(const gfxPoint& center, const gfxSize& dimensions)
490 gfxSize halfDim = dimensions / 2.0;
491 gfxRect r(center - gfxPoint(halfDim.width, halfDim.height), dimensions);
492 gfxCornerSizes c(halfDim, halfDim, halfDim, halfDim);
494 RoundedRectangle (r, c);
497 void
498 gfxContext::Polygon(const gfxPoint *points, uint32_t numPoints)
500 if (mCairo) {
501 if (numPoints == 0)
502 return;
504 cairo_move_to(mCairo, points[0].x, points[0].y);
505 for (uint32_t i = 1; i < numPoints; ++i) {
506 cairo_line_to(mCairo, points[i].x, points[i].y);
508 } else {
509 if (numPoints == 0) {
510 return;
513 EnsurePathBuilder();
515 mPathBuilder->MoveTo(ToPoint(points[0]));
516 for (uint32_t i = 1; i < numPoints; i++) {
517 mPathBuilder->LineTo(ToPoint(points[i]));
522 void
523 gfxContext::DrawSurface(gfxASurface *surface, const gfxSize& size)
525 if (mCairo) {
526 cairo_save(mCairo);
527 cairo_set_source_surface(mCairo, surface->CairoSurface(), 0, 0);
528 cairo_new_path(mCairo);
530 // pixel-snap this
531 Rectangle(gfxRect(gfxPoint(0.0, 0.0), size), true);
533 cairo_fill(mCairo);
534 cairo_restore(mCairo);
535 } else {
536 // Lifetime needs to be limited here since we may wrap surface's data.
537 RefPtr<SourceSurface> surf =
538 gfxPlatform::GetPlatform()->GetSourceSurfaceForSurface(mDT, surface);
540 Rect rect(0, 0, Float(size.width), Float(size.height));
541 rect.Intersect(Rect(0, 0, Float(surf->GetSize().width), Float(surf->GetSize().height)));
543 // XXX - Should fix pixel snapping.
544 mDT->DrawSurface(surf, rect, rect);
548 // transform stuff
549 void
550 gfxContext::Translate(const gfxPoint& pt)
552 if (mCairo) {
553 cairo_translate(mCairo, pt.x, pt.y);
554 } else {
555 Matrix newMatrix = mTransform;
557 ChangeTransform(newMatrix.Translate(Float(pt.x), Float(pt.y)));
561 void
562 gfxContext::Scale(gfxFloat x, gfxFloat y)
564 if (mCairo) {
565 cairo_scale(mCairo, x, y);
566 } else {
567 Matrix newMatrix = mTransform;
569 ChangeTransform(newMatrix.Scale(Float(x), Float(y)));
573 void
574 gfxContext::Rotate(gfxFloat angle)
576 if (mCairo) {
577 cairo_rotate(mCairo, angle);
578 } else {
579 Matrix rotation = Matrix::Rotation(Float(angle));
580 ChangeTransform(rotation * mTransform);
584 void
585 gfxContext::Multiply(const gfxMatrix& matrix)
587 if (mCairo) {
588 const cairo_matrix_t& mat = reinterpret_cast<const cairo_matrix_t&>(matrix);
589 cairo_transform(mCairo, &mat);
590 } else {
591 ChangeTransform(ToMatrix(matrix) * mTransform);
595 void
596 gfxContext::MultiplyAndNudgeToIntegers(const gfxMatrix& matrix)
598 if (mCairo) {
599 const cairo_matrix_t& mat = reinterpret_cast<const cairo_matrix_t&>(matrix);
600 cairo_transform(mCairo, &mat);
601 // XXX nudging to integers not currently supported for Thebes
602 } else {
603 Matrix transform = ToMatrix(matrix) * mTransform;
604 transform.NudgeToIntegers();
605 ChangeTransform(transform);
609 void
610 gfxContext::SetMatrix(const gfxMatrix& matrix)
612 if (mCairo) {
613 const cairo_matrix_t& mat = reinterpret_cast<const cairo_matrix_t&>(matrix);
614 cairo_set_matrix(mCairo, &mat);
615 } else {
616 Matrix mat;
617 mat.Translate(-CurrentState().deviceOffset.x, -CurrentState().deviceOffset.y);
618 ChangeTransform(ToMatrix(matrix));
622 void
623 gfxContext::IdentityMatrix()
625 if (mCairo) {
626 cairo_identity_matrix(mCairo);
627 } else {
628 ChangeTransform(Matrix());
632 gfxMatrix
633 gfxContext::CurrentMatrix() const
635 if (mCairo) {
636 cairo_matrix_t mat;
637 cairo_get_matrix(mCairo, &mat);
638 return gfxMatrix(*reinterpret_cast<gfxMatrix*>(&mat));
639 } else {
640 return ThebesMatrix(mTransform);
644 void
645 gfxContext::NudgeCurrentMatrixToIntegers()
647 if (mCairo) {
648 cairo_matrix_t mat;
649 cairo_get_matrix(mCairo, &mat);
650 gfxMatrix(*reinterpret_cast<gfxMatrix*>(&mat)).NudgeToIntegers();
651 cairo_set_matrix(mCairo, &mat);
652 } else {
653 gfxMatrix matrix = ThebesMatrix(mTransform);
654 matrix.NudgeToIntegers();
655 ChangeTransform(ToMatrix(matrix));
659 gfxPoint
660 gfxContext::DeviceToUser(const gfxPoint& point) const
662 if (mCairo) {
663 gfxPoint ret = point;
664 cairo_device_to_user(mCairo, &ret.x, &ret.y);
665 return ret;
666 } else {
667 Matrix matrix = mTransform;
669 matrix.Invert();
671 return ThebesPoint(matrix * ToPoint(point));
675 gfxSize
676 gfxContext::DeviceToUser(const gfxSize& size) const
678 if (mCairo) {
679 gfxSize ret = size;
680 cairo_device_to_user_distance(mCairo, &ret.width, &ret.height);
681 return ret;
682 } else {
683 Matrix matrix = mTransform;
685 matrix.Invert();
687 return ThebesSize(matrix * ToSize(size));
691 gfxRect
692 gfxContext::DeviceToUser(const gfxRect& rect) const
694 if (mCairo) {
695 gfxRect ret = rect;
696 cairo_device_to_user(mCairo, &ret.x, &ret.y);
697 cairo_device_to_user_distance(mCairo, &ret.width, &ret.height);
698 return ret;
699 } else {
700 Matrix matrix = mTransform;
702 matrix.Invert();
704 return ThebesRect(matrix.TransformBounds(ToRect(rect)));
708 gfxPoint
709 gfxContext::UserToDevice(const gfxPoint& point) const
711 if (mCairo) {
712 gfxPoint ret = point;
713 cairo_user_to_device(mCairo, &ret.x, &ret.y);
714 return ret;
715 } else {
716 return ThebesPoint(mTransform * ToPoint(point));
720 gfxSize
721 gfxContext::UserToDevice(const gfxSize& size) const
723 if (mCairo) {
724 gfxSize ret = size;
725 cairo_user_to_device_distance(mCairo, &ret.width, &ret.height);
726 return ret;
727 } else {
728 const Matrix &matrix = mTransform;
730 gfxSize newSize = size;
731 newSize.width = newSize.width * matrix._11 + newSize.height * matrix._12;
732 newSize.height = newSize.width * matrix._21 + newSize.height * matrix._22;
733 return newSize;
737 gfxRect
738 gfxContext::UserToDevice(const gfxRect& rect) const
740 if (mCairo) {
741 double xmin = rect.X(), ymin = rect.Y(), xmax = rect.XMost(), ymax = rect.YMost();
743 double x[3], y[3];
744 x[0] = xmin; y[0] = ymax;
745 x[1] = xmax; y[1] = ymax;
746 x[2] = xmax; y[2] = ymin;
748 cairo_user_to_device(mCairo, &xmin, &ymin);
749 xmax = xmin;
750 ymax = ymin;
751 for (int i = 0; i < 3; i++) {
752 cairo_user_to_device(mCairo, &x[i], &y[i]);
753 xmin = std::min(xmin, x[i]);
754 xmax = std::max(xmax, x[i]);
755 ymin = std::min(ymin, y[i]);
756 ymax = std::max(ymax, y[i]);
759 return gfxRect(xmin, ymin, xmax - xmin, ymax - ymin);
760 } else {
761 const Matrix &matrix = mTransform;
762 return ThebesRect(matrix.TransformBounds(ToRect(rect)));
766 bool
767 gfxContext::UserToDevicePixelSnapped(gfxRect& rect, bool ignoreScale) const
769 if (GetFlags() & FLAG_DISABLE_SNAPPING)
770 return false;
772 // if we're not at 1.0 scale, don't snap, unless we're
773 // ignoring the scale. If we're not -just- a scale,
774 // never snap.
775 const gfxFloat epsilon = 0.0000001;
776 #define WITHIN_E(a,b) (fabs((a)-(b)) < epsilon)
777 if (mCairo) {
778 cairo_matrix_t mat;
779 cairo_get_matrix(mCairo, &mat);
780 if (!ignoreScale &&
781 (!WITHIN_E(mat.xx,1.0) || !WITHIN_E(mat.yy,1.0) ||
782 !WITHIN_E(mat.xy,0.0) || !WITHIN_E(mat.yx,0.0)))
783 return false;
784 } else {
785 Matrix mat = mTransform;
786 if (!ignoreScale &&
787 (!WITHIN_E(mat._11,1.0) || !WITHIN_E(mat._22,1.0) ||
788 !WITHIN_E(mat._12,0.0) || !WITHIN_E(mat._21,0.0)))
789 return false;
791 #undef WITHIN_E
793 gfxPoint p1 = UserToDevice(rect.TopLeft());
794 gfxPoint p2 = UserToDevice(rect.TopRight());
795 gfxPoint p3 = UserToDevice(rect.BottomRight());
797 // Check that the rectangle is axis-aligned. For an axis-aligned rectangle,
798 // two opposite corners define the entire rectangle. So check if
799 // the axis-aligned rectangle with opposite corners p1 and p3
800 // define an axis-aligned rectangle whose other corners are p2 and p4.
801 // We actually only need to check one of p2 and p4, since an affine
802 // transform maps parallelograms to parallelograms.
803 if (p2 == gfxPoint(p1.x, p3.y) || p2 == gfxPoint(p3.x, p1.y)) {
804 p1.Round();
805 p3.Round();
807 rect.MoveTo(gfxPoint(std::min(p1.x, p3.x), std::min(p1.y, p3.y)));
808 rect.SizeTo(gfxSize(std::max(p1.x, p3.x) - rect.X(),
809 std::max(p1.y, p3.y) - rect.Y()));
810 return true;
813 return false;
816 bool
817 gfxContext::UserToDevicePixelSnapped(gfxPoint& pt, bool ignoreScale) const
819 if (GetFlags() & FLAG_DISABLE_SNAPPING)
820 return false;
822 // if we're not at 1.0 scale, don't snap, unless we're
823 // ignoring the scale. If we're not -just- a scale,
824 // never snap.
825 const gfxFloat epsilon = 0.0000001;
826 #define WITHIN_E(a,b) (fabs((a)-(b)) < epsilon)
827 if (mCairo) {
828 cairo_matrix_t mat;
829 cairo_get_matrix(mCairo, &mat);
830 if (!ignoreScale &&
831 (!WITHIN_E(mat.xx,1.0) || !WITHIN_E(mat.yy,1.0) ||
832 !WITHIN_E(mat.xy,0.0) || !WITHIN_E(mat.yx,0.0)))
833 return false;
834 } else {
835 Matrix mat = mTransform;
836 if (!ignoreScale &&
837 (!WITHIN_E(mat._11,1.0) || !WITHIN_E(mat._22,1.0) ||
838 !WITHIN_E(mat._12,0.0) || !WITHIN_E(mat._21,0.0)))
839 return false;
841 #undef WITHIN_E
843 pt = UserToDevice(pt);
844 pt.Round();
845 return true;
848 void
849 gfxContext::PixelSnappedRectangleAndSetPattern(const gfxRect& rect,
850 gfxPattern *pattern)
852 gfxRect r(rect);
854 // Bob attempts to pixel-snap the rectangle, and returns true if
855 // the snapping succeeds. If it does, we need to set up an
856 // identity matrix, because the rectangle given back is in device
857 // coordinates.
859 // We then have to call a translate to dr.pos afterwards, to make
860 // sure the image lines up in the right place with our pixel
861 // snapped rectangle.
863 // If snapping wasn't successful, we just translate to where the
864 // pattern would normally start (in app coordinates) and do the
865 // same thing.
866 Rectangle(r, true);
867 SetPattern(pattern);
870 void
871 gfxContext::SetAntialiasMode(AntialiasMode mode)
873 if (mCairo) {
874 if (mode == MODE_ALIASED) {
875 cairo_set_antialias(mCairo, CAIRO_ANTIALIAS_NONE);
876 } else if (mode == MODE_COVERAGE) {
877 cairo_set_antialias(mCairo, CAIRO_ANTIALIAS_DEFAULT);
879 } else {
880 if (mode == MODE_ALIASED) {
881 CurrentState().aaMode = AA_NONE;
882 } else if (mode == MODE_COVERAGE) {
883 CurrentState().aaMode = AA_SUBPIXEL;
888 gfxContext::AntialiasMode
889 gfxContext::CurrentAntialiasMode() const
891 if (mCairo) {
892 cairo_antialias_t aa = cairo_get_antialias(mCairo);
893 if (aa == CAIRO_ANTIALIAS_NONE)
894 return MODE_ALIASED;
895 return MODE_COVERAGE;
896 } else {
897 if (CurrentState().aaMode == AA_NONE) {
898 return MODE_ALIASED;
900 return MODE_COVERAGE;
904 void
905 gfxContext::SetDash(gfxLineType ltype)
907 static double dash[] = {5.0, 5.0};
908 static double dot[] = {1.0, 1.0};
910 switch (ltype) {
911 case gfxLineDashed:
912 SetDash(dash, 2, 0.0);
913 break;
914 case gfxLineDotted:
915 SetDash(dot, 2, 0.0);
916 break;
917 case gfxLineSolid:
918 default:
919 SetDash(nullptr, 0, 0.0);
920 break;
924 void
925 gfxContext::SetDash(gfxFloat *dashes, int ndash, gfxFloat offset)
927 if (mCairo) {
928 cairo_set_dash(mCairo, dashes, ndash, offset);
929 } else {
930 AzureState &state = CurrentState();
932 state.dashPattern.SetLength(ndash);
933 for (int i = 0; i < ndash; i++) {
934 state.dashPattern[i] = Float(dashes[i]);
936 state.strokeOptions.mDashLength = ndash;
937 state.strokeOptions.mDashOffset = Float(offset);
938 state.strokeOptions.mDashPattern = ndash ? state.dashPattern.Elements() : NULL;
942 bool
943 gfxContext::CurrentDash(FallibleTArray<gfxFloat>& dashes, gfxFloat* offset) const
945 if (mCairo) {
946 int count = cairo_get_dash_count(mCairo);
947 if (count <= 0 || !dashes.SetLength(count)) {
948 return false;
950 cairo_get_dash(mCairo, dashes.Elements(), offset);
951 return true;
952 } else {
953 const AzureState &state = CurrentState();
954 int count = state.strokeOptions.mDashLength;
956 if (count <= 0 || !dashes.SetLength(count)) {
957 return false;
960 for (int i = 0; i < count; i++) {
961 dashes[i] = state.dashPattern[i];
964 *offset = state.strokeOptions.mDashOffset;
966 return true;
970 gfxFloat
971 gfxContext::CurrentDashOffset() const
973 if (mCairo) {
974 if (cairo_get_dash_count(mCairo) <= 0) {
975 return 0.0;
977 gfxFloat offset;
978 cairo_get_dash(mCairo, NULL, &offset);
979 return offset;
980 } else {
981 return CurrentState().strokeOptions.mDashOffset;
985 void
986 gfxContext::SetLineWidth(gfxFloat width)
988 if (mCairo) {
989 cairo_set_line_width(mCairo, width);
990 } else {
991 CurrentState().strokeOptions.mLineWidth = Float(width);
995 gfxFloat
996 gfxContext::CurrentLineWidth() const
998 if (mCairo) {
999 return cairo_get_line_width(mCairo);
1000 } else {
1001 return CurrentState().strokeOptions.mLineWidth;
1005 void
1006 gfxContext::SetOperator(GraphicsOperator op)
1008 if (mCairo) {
1009 if (mFlags & FLAG_SIMPLIFY_OPERATORS) {
1010 if (op != OPERATOR_SOURCE &&
1011 op != OPERATOR_CLEAR &&
1012 op != OPERATOR_OVER)
1013 op = OPERATOR_OVER;
1016 cairo_set_operator(mCairo, (cairo_operator_t)op);
1017 } else {
1018 if (op == OPERATOR_CLEAR) {
1019 CurrentState().opIsClear = true;
1020 return;
1022 CurrentState().opIsClear = false;
1023 CurrentState().op = CompositionOpForOp(op);
1027 gfxContext::GraphicsOperator
1028 gfxContext::CurrentOperator() const
1030 if (mCairo) {
1031 return (GraphicsOperator)cairo_get_operator(mCairo);
1032 } else {
1033 return ThebesOp(CurrentState().op);
1037 void
1038 gfxContext::SetLineCap(GraphicsLineCap cap)
1040 if (mCairo) {
1041 cairo_set_line_cap(mCairo, (cairo_line_cap_t)cap);
1042 } else {
1043 CurrentState().strokeOptions.mLineCap = ToCapStyle(cap);
1047 gfxContext::GraphicsLineCap
1048 gfxContext::CurrentLineCap() const
1050 if (mCairo) {
1051 return (GraphicsLineCap)cairo_get_line_cap(mCairo);
1052 } else {
1053 return ThebesLineCap(CurrentState().strokeOptions.mLineCap);
1057 void
1058 gfxContext::SetLineJoin(GraphicsLineJoin join)
1060 if (mCairo) {
1061 cairo_set_line_join(mCairo, (cairo_line_join_t)join);
1062 } else {
1063 CurrentState().strokeOptions.mLineJoin = ToJoinStyle(join);
1067 gfxContext::GraphicsLineJoin
1068 gfxContext::CurrentLineJoin() const
1070 if (mCairo) {
1071 return (GraphicsLineJoin)cairo_get_line_join(mCairo);
1072 } else {
1073 return ThebesLineJoin(CurrentState().strokeOptions.mLineJoin);
1077 void
1078 gfxContext::SetMiterLimit(gfxFloat limit)
1080 if (mCairo) {
1081 cairo_set_miter_limit(mCairo, limit);
1082 } else {
1083 CurrentState().strokeOptions.mMiterLimit = Float(limit);
1087 gfxFloat
1088 gfxContext::CurrentMiterLimit() const
1090 if (mCairo) {
1091 return cairo_get_miter_limit(mCairo);
1092 } else {
1093 return CurrentState().strokeOptions.mMiterLimit;
1097 void
1098 gfxContext::SetFillRule(FillRule rule)
1100 if (mCairo) {
1101 cairo_set_fill_rule(mCairo, (cairo_fill_rule_t)rule);
1102 } else {
1103 CurrentState().fillRule = rule == FILL_RULE_WINDING ? FILL_WINDING : FILL_EVEN_ODD;
1107 gfxContext::FillRule
1108 gfxContext::CurrentFillRule() const
1110 if (mCairo) {
1111 return (FillRule)cairo_get_fill_rule(mCairo);
1112 } else {
1113 return FILL_RULE_WINDING;
1117 // clipping
1118 void
1119 gfxContext::Clip(const gfxRect& rect)
1121 if (mCairo) {
1122 cairo_new_path(mCairo);
1123 cairo_rectangle(mCairo, rect.X(), rect.Y(), rect.Width(), rect.Height());
1124 cairo_clip(mCairo);
1125 } else {
1126 AzureState::PushedClip clip = { NULL, ToRect(rect), mTransform };
1127 CurrentState().pushedClips.AppendElement(clip);
1128 mDT->PushClipRect(ToRect(rect));
1129 NewPath();
1133 void
1134 gfxContext::Clip()
1136 if (mCairo) {
1137 cairo_clip_preserve(mCairo);
1138 } else {
1139 if (mPathIsRect) {
1140 MOZ_ASSERT(!mTransformChanged);
1142 AzureState::PushedClip clip = { NULL, mRect, mTransform };
1143 CurrentState().pushedClips.AppendElement(clip);
1144 mDT->PushClipRect(mRect);
1145 } else {
1146 EnsurePath();
1147 mDT->PushClip(mPath);
1148 AzureState::PushedClip clip = { mPath, Rect(), mTransform };
1149 CurrentState().pushedClips.AppendElement(clip);
1154 void
1155 gfxContext::ResetClip()
1157 if (mCairo) {
1158 cairo_reset_clip(mCairo);
1159 } else {
1160 for (int i = mStateStack.Length() - 1; i >= 0; i--) {
1161 for (unsigned int c = 0; c < mStateStack[i].pushedClips.Length(); c++) {
1162 mDT->PopClip();
1165 if (mStateStack[i].clipWasReset) {
1166 break;
1169 CurrentState().pushedClips.Clear();
1170 CurrentState().clipWasReset = true;
1174 void
1175 gfxContext::UpdateSurfaceClip()
1177 if (mCairo) {
1178 NewPath();
1179 // we paint an empty rectangle to ensure the clip is propagated to
1180 // the destination surface
1181 SetDeviceColor(gfxRGBA(0,0,0,0));
1182 Rectangle(gfxRect(0,1,1,0));
1183 Fill();
1187 gfxRect
1188 gfxContext::GetClipExtents()
1190 if (mCairo) {
1191 double xmin, ymin, xmax, ymax;
1192 cairo_clip_extents(mCairo, &xmin, &ymin, &xmax, &ymax);
1193 return gfxRect(xmin, ymin, xmax - xmin, ymax - ymin);
1194 } else {
1195 Rect rect = GetAzureDeviceSpaceClipBounds();
1197 if (rect.width == 0 || rect.height == 0) {
1198 return gfxRect(0, 0, 0, 0);
1201 Matrix mat = mTransform;
1202 mat.Invert();
1203 rect = mat.TransformBounds(rect);
1205 return ThebesRect(rect);
1209 bool
1210 gfxContext::ClipContainsRect(const gfxRect& aRect)
1212 if (mCairo) {
1213 cairo_rectangle_list_t *clip =
1214 cairo_copy_clip_rectangle_list(mCairo);
1216 bool result = false;
1218 if (clip->status == CAIRO_STATUS_SUCCESS) {
1219 for (int i = 0; i < clip->num_rectangles; i++) {
1220 gfxRect rect(clip->rectangles[i].x, clip->rectangles[i].y,
1221 clip->rectangles[i].width, clip->rectangles[i].height);
1222 if (rect.Contains(aRect)) {
1223 result = true;
1224 break;
1229 cairo_rectangle_list_destroy(clip);
1230 return result;
1231 } else {
1232 unsigned int lastReset = 0;
1233 for (int i = mStateStack.Length() - 2; i > 0; i--) {
1234 if (mStateStack[i].clipWasReset) {
1235 lastReset = i;
1239 // Since we always return false when the clip list contains a
1240 // non-rectangular clip or a non-rectilinear transform, our 'total' clip
1241 // is always a rectangle if we hit the end of this function.
1242 Rect clipBounds(0, 0, Float(mDT->GetSize().width), Float(mDT->GetSize().height));
1244 for (unsigned int i = lastReset; i < mStateStack.Length(); i++) {
1245 for (unsigned int c = 0; c < mStateStack[i].pushedClips.Length(); c++) {
1246 AzureState::PushedClip &clip = mStateStack[i].pushedClips[c];
1247 if (clip.path || !clip.transform.IsRectilinear()) {
1248 // Cairo behavior is we return false if the clip contains a non-
1249 // rectangle.
1250 return false;
1251 } else {
1252 Rect clipRect = mTransform.TransformBounds(clip.rect);
1254 clipBounds.IntersectRect(clipBounds, clipRect);
1259 return clipBounds.Contains(ToRect(aRect));
1263 // rendering sources
1265 void
1266 gfxContext::SetColor(const gfxRGBA& c)
1268 if (mCairo) {
1269 if (gfxPlatform::GetCMSMode() == eCMSMode_All) {
1271 gfxRGBA cms;
1272 qcms_transform *transform = gfxPlatform::GetCMSRGBTransform();
1273 if (transform)
1274 gfxPlatform::TransformPixel(c, cms, transform);
1276 // Use the original alpha to avoid unnecessary float->byte->float
1277 // conversion errors
1278 cairo_set_source_rgba(mCairo, cms.r, cms.g, cms.b, c.a);
1280 else
1281 cairo_set_source_rgba(mCairo, c.r, c.g, c.b, c.a);
1282 } else {
1283 CurrentState().pattern = NULL;
1284 CurrentState().sourceSurfCairo = NULL;
1285 CurrentState().sourceSurface = NULL;
1287 if (gfxPlatform::GetCMSMode() == eCMSMode_All) {
1289 gfxRGBA cms;
1290 qcms_transform *transform = gfxPlatform::GetCMSRGBTransform();
1291 if (transform)
1292 gfxPlatform::TransformPixel(c, cms, transform);
1294 // Use the original alpha to avoid unnecessary float->byte->float
1295 // conversion errors
1296 CurrentState().color = ToColor(cms);
1298 else
1299 CurrentState().color = ToColor(c);
1303 void
1304 gfxContext::SetDeviceColor(const gfxRGBA& c)
1306 if (mCairo) {
1307 cairo_set_source_rgba(mCairo, c.r, c.g, c.b, c.a);
1308 } else {
1309 CurrentState().pattern = NULL;
1310 CurrentState().sourceSurfCairo = NULL;
1311 CurrentState().sourceSurface = NULL;
1312 CurrentState().color = ToColor(c);
1316 bool
1317 gfxContext::GetDeviceColor(gfxRGBA& c)
1319 if (mCairo) {
1320 return cairo_pattern_get_rgba(cairo_get_source(mCairo),
1321 &c.r,
1322 &c.g,
1323 &c.b,
1324 &c.a) == CAIRO_STATUS_SUCCESS;
1325 } else {
1326 if (CurrentState().sourceSurface) {
1327 return false;
1329 if (CurrentState().pattern) {
1330 gfxRGBA color;
1331 return CurrentState().pattern->GetSolidColor(c);
1334 c = ThebesRGBA(CurrentState().color);
1335 return true;
1339 void
1340 gfxContext::SetSource(gfxASurface *surface, const gfxPoint& offset)
1342 if (mCairo) {
1343 NS_ASSERTION(surface->GetAllowUseAsSource(), "Surface not allowed to be used as source!");
1344 cairo_set_source_surface(mCairo, surface->CairoSurface(), offset.x, offset.y);
1345 } else {
1346 CurrentState().surfTransform = Matrix(1.0f, 0, 0, 1.0f, Float(offset.x), Float(offset.y));
1347 CurrentState().pattern = NULL;
1348 CurrentState().patternTransformChanged = false;
1349 // Keep the underlying cairo surface around while we keep the
1350 // sourceSurface.
1351 CurrentState().sourceSurfCairo = surface;
1352 CurrentState().sourceSurface =
1353 gfxPlatform::GetPlatform()->GetSourceSurfaceForSurface(mDT, surface);
1357 void
1358 gfxContext::SetPattern(gfxPattern *pattern)
1360 if (mCairo) {
1361 cairo_set_source(mCairo, pattern->CairoPattern());
1362 } else {
1363 CurrentState().sourceSurfCairo = NULL;
1364 CurrentState().sourceSurface = NULL;
1365 CurrentState().patternTransformChanged = false;
1366 CurrentState().pattern = pattern;
1370 already_AddRefed<gfxPattern>
1371 gfxContext::GetPattern()
1373 if (mCairo) {
1374 cairo_pattern_t *pat = cairo_get_source(mCairo);
1375 NS_ASSERTION(pat, "I was told this couldn't be null");
1377 gfxPattern *wrapper = nullptr;
1378 if (pat)
1379 wrapper = new gfxPattern(pat);
1380 else
1381 wrapper = new gfxPattern(gfxRGBA(0,0,0,0));
1383 NS_IF_ADDREF(wrapper);
1384 return wrapper;
1385 } else {
1386 nsRefPtr<gfxPattern> pat;
1388 AzureState &state = CurrentState();
1389 if (state.pattern) {
1390 pat = state.pattern;
1391 } else if (state.sourceSurface) {
1392 NS_ASSERTION(false, "Ugh, this isn't good.");
1393 } else {
1394 pat = new gfxPattern(ThebesRGBA(state.color));
1396 return pat.forget();
1401 // masking
1403 void
1404 gfxContext::Mask(gfxPattern *pattern)
1406 if (mCairo) {
1407 cairo_mask(mCairo, pattern->CairoPattern());
1408 } else {
1409 mDT->Mask(GeneralPattern(this), *pattern->GetPattern(mDT), DrawOptions(1.0f, CurrentState().op, CurrentState().aaMode));
1413 void
1414 gfxContext::Mask(gfxASurface *surface, const gfxPoint& offset)
1416 SAMPLE_LABEL("gfxContext", "Mask");
1417 if (mCairo) {
1418 cairo_mask_surface(mCairo, surface->CairoSurface(), offset.x, offset.y);
1419 } else {
1420 // Lifetime needs to be limited here as we may simply wrap surface's data.
1421 RefPtr<SourceSurface> sourceSurf =
1422 gfxPlatform::GetPlatform()->GetSourceSurfaceForSurface(mDT, surface);
1424 gfxPoint pt = surface->GetDeviceOffset();
1425 mDT->Mask(GeneralPattern(this),
1426 SurfacePattern(sourceSurf, EXTEND_CLAMP,
1427 Matrix(1.0f, 0, 0, 1.0f, Float(offset.x - pt.x), Float(offset.y - pt.y))),
1428 DrawOptions(1.0f, CurrentState().op, CurrentState().aaMode));
1432 void
1433 gfxContext::Paint(gfxFloat alpha)
1435 SAMPLE_LABEL("gfxContext", "Paint");
1436 if (mCairo) {
1437 cairo_paint_with_alpha(mCairo, alpha);
1438 } else {
1439 AzureState &state = CurrentState();
1441 Matrix mat = mDT->GetTransform();
1442 mat.Invert();
1443 Rect paintRect = mat.TransformBounds(Rect(Point(0, 0), Size(mDT->GetSize())));
1445 if (state.opIsClear) {
1446 mDT->ClearRect(paintRect);
1447 } else {
1448 mDT->FillRect(paintRect, GeneralPattern(this),
1449 DrawOptions(Float(alpha), GetOp()));
1454 // groups
1456 void
1457 gfxContext::PushGroup(gfxASurface::gfxContentType content)
1459 if (mCairo) {
1460 cairo_push_group_with_content(mCairo, (cairo_content_t) content);
1461 } else {
1462 PushNewDT(content);
1464 PushClipsToDT(mDT);
1465 mDT->SetTransform(GetDTTransform());
1469 static gfxRect
1470 GetRoundOutDeviceClipExtents(gfxContext* aCtx)
1472 gfxContextMatrixAutoSaveRestore save(aCtx);
1473 aCtx->IdentityMatrix();
1474 gfxRect r = aCtx->GetClipExtents();
1475 r.RoundOut();
1476 return r;
1480 * Copy the contents of aSrc to aDest, translated by aTranslation.
1482 static void
1483 CopySurface(gfxASurface* aSrc, gfxASurface* aDest, const gfxPoint& aTranslation)
1485 cairo_t *cr = cairo_create(aDest->CairoSurface());
1486 cairo_set_source_surface(cr, aSrc->CairoSurface(), aTranslation.x, aTranslation.y);
1487 cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
1488 cairo_paint(cr);
1489 cairo_destroy(cr);
1492 void
1493 gfxContext::PushGroupAndCopyBackground(gfxASurface::gfxContentType content)
1495 if (mCairo) {
1496 if (content == gfxASurface::CONTENT_COLOR_ALPHA &&
1497 !(GetFlags() & FLAG_DISABLE_COPY_BACKGROUND)) {
1498 nsRefPtr<gfxASurface> s = CurrentSurface();
1499 if ((s->GetAllowUseAsSource() || s->GetType() == gfxASurface::SurfaceTypeTee) &&
1500 (s->GetContentType() == gfxASurface::CONTENT_COLOR ||
1501 s->GetOpaqueRect().Contains(GetRoundOutDeviceClipExtents(this)))) {
1502 cairo_push_group_with_content(mCairo, CAIRO_CONTENT_COLOR);
1503 nsRefPtr<gfxASurface> d = CurrentSurface();
1505 if (d->GetType() == gfxASurface::SurfaceTypeTee) {
1506 NS_ASSERTION(s->GetType() == gfxASurface::SurfaceTypeTee, "Mismatched types");
1507 nsAutoTArray<nsRefPtr<gfxASurface>,2> ss;
1508 nsAutoTArray<nsRefPtr<gfxASurface>,2> ds;
1509 static_cast<gfxTeeSurface*>(s.get())->GetSurfaces(&ss);
1510 static_cast<gfxTeeSurface*>(d.get())->GetSurfaces(&ds);
1511 NS_ASSERTION(ss.Length() == ds.Length(), "Mismatched lengths");
1512 gfxPoint translation = d->GetDeviceOffset() - s->GetDeviceOffset();
1513 for (uint32_t i = 0; i < ss.Length(); ++i) {
1514 CopySurface(ss[i], ds[i], translation);
1516 } else {
1517 CopySurface(s, d, gfxPoint(0, 0));
1519 d->SetOpaqueRect(s->GetOpaqueRect());
1520 return;
1523 } else {
1524 IntRect clipExtents;
1525 if (mDT->GetFormat() != FORMAT_B8G8R8X8) {
1526 gfxRect clipRect = GetRoundOutDeviceClipExtents(this);
1527 clipExtents = IntRect(clipRect.x, clipRect.y, clipRect.width, clipRect.height);
1529 if (mDT->GetFormat() == FORMAT_B8G8R8X8 ||
1530 mDT->GetOpaqueRect().Contains(clipExtents)) {
1531 DrawTarget *oldDT = mDT;
1532 RefPtr<SourceSurface> source = mDT->Snapshot();
1533 Point oldDeviceOffset = CurrentState().deviceOffset;
1535 PushNewDT(gfxASurface::CONTENT_COLOR);
1537 Point offset = CurrentState().deviceOffset - oldDeviceOffset;
1538 Rect surfRect(0, 0, Float(mDT->GetSize().width), Float(mDT->GetSize().height));
1539 Rect sourceRect = surfRect;
1540 sourceRect.x += offset.x;
1541 sourceRect.y += offset.y;
1543 mDT->SetTransform(Matrix());
1544 mDT->DrawSurface(source, surfRect, sourceRect);
1545 mDT->SetOpaqueRect(oldDT->GetOpaqueRect());
1547 PushClipsToDT(mDT);
1548 mDT->SetTransform(GetDTTransform());
1549 return;
1552 PushGroup(content);
1555 already_AddRefed<gfxPattern>
1556 gfxContext::PopGroup()
1558 if (mCairo) {
1559 cairo_pattern_t *pat = cairo_pop_group(mCairo);
1560 gfxPattern *wrapper = new gfxPattern(pat);
1561 cairo_pattern_destroy(pat);
1562 NS_IF_ADDREF(wrapper);
1563 return wrapper;
1564 } else {
1565 RefPtr<SourceSurface> src = mDT->Snapshot();
1566 Point deviceOffset = CurrentState().deviceOffset;
1568 Restore();
1570 Matrix mat = mTransform;
1571 mat.Invert();
1573 Matrix deviceOffsetTranslation;
1574 deviceOffsetTranslation.Translate(deviceOffset.x, deviceOffset.y);
1576 nsRefPtr<gfxPattern> pat = new gfxPattern(src, deviceOffsetTranslation * mat);
1578 return pat.forget();
1582 void
1583 gfxContext::PopGroupToSource()
1585 if (mCairo) {
1586 cairo_pop_group_to_source(mCairo);
1587 } else {
1588 RefPtr<SourceSurface> src = mDT->Snapshot();
1589 Point deviceOffset = CurrentState().deviceOffset;
1590 Restore();
1591 CurrentState().sourceSurfCairo = NULL;
1592 CurrentState().sourceSurface = src;
1593 CurrentState().pattern = NULL;
1594 CurrentState().patternTransformChanged = false;
1596 Matrix mat = mTransform;
1597 mat.Invert();
1599 Matrix deviceOffsetTranslation;
1600 deviceOffsetTranslation.Translate(deviceOffset.x, deviceOffset.y);
1601 CurrentState().surfTransform = deviceOffsetTranslation * mat;
1605 bool
1606 gfxContext::PointInFill(const gfxPoint& pt)
1608 if (mCairo) {
1609 return cairo_in_fill(mCairo, pt.x, pt.y);
1610 } else {
1611 return mPath->ContainsPoint(ToPoint(pt), mTransform);
1615 bool
1616 gfxContext::PointInStroke(const gfxPoint& pt)
1618 if (mCairo) {
1619 return cairo_in_stroke(mCairo, pt.x, pt.y);
1620 } else {
1621 return mPath->StrokeContainsPoint(CurrentState().strokeOptions,
1622 ToPoint(pt),
1623 mTransform);
1627 gfxRect
1628 gfxContext::GetUserPathExtent()
1630 if (mCairo) {
1631 double xmin, ymin, xmax, ymax;
1632 cairo_path_extents(mCairo, &xmin, &ymin, &xmax, &ymax);
1633 return gfxRect(xmin, ymin, xmax - xmin, ymax - ymin);
1634 } else {
1635 return ThebesRect(mPath->GetBounds());
1639 gfxRect
1640 gfxContext::GetUserFillExtent()
1642 if (mCairo) {
1643 double xmin, ymin, xmax, ymax;
1644 cairo_fill_extents(mCairo, &xmin, &ymin, &xmax, &ymax);
1645 return gfxRect(xmin, ymin, xmax - xmin, ymax - ymin);
1646 } else {
1647 return ThebesRect(mPath->GetBounds());
1651 gfxRect
1652 gfxContext::GetUserStrokeExtent()
1654 if (mCairo) {
1655 double xmin, ymin, xmax, ymax;
1656 cairo_stroke_extents(mCairo, &xmin, &ymin, &xmax, &ymax);
1657 return gfxRect(xmin, ymin, xmax - xmin, ymax - ymin);
1658 } else {
1659 return ThebesRect(mPath->GetStrokedBounds(CurrentState().strokeOptions, mTransform));
1663 already_AddRefed<gfxFlattenedPath>
1664 gfxContext::GetFlattenedPath()
1666 if (mCairo) {
1667 gfxFlattenedPath *path =
1668 new gfxFlattenedPath(cairo_copy_path_flat(mCairo));
1669 NS_IF_ADDREF(path);
1670 return path;
1671 } else {
1672 // XXX - Used by SVG, needs fixing.
1673 return NULL;
1677 bool
1678 gfxContext::HasError()
1680 if (mCairo) {
1681 return cairo_status(mCairo) != CAIRO_STATUS_SUCCESS;
1682 } else {
1683 // As far as this is concerned, an Azure context is never in error.
1684 return false;
1688 void
1689 gfxContext::RoundedRectangle(const gfxRect& rect,
1690 const gfxCornerSizes& corners,
1691 bool draw_clockwise)
1694 // For CW drawing, this looks like:
1696 // ...******0** 1 C
1697 // ****
1698 // *** 2
1699 // **
1700 // *
1701 // *
1702 // 3
1703 // *
1704 // *
1706 // Where 0, 1, 2, 3 are the control points of the Bezier curve for
1707 // the corner, and C is the actual corner point.
1709 // At the start of the loop, the current point is assumed to be
1710 // the point adjacent to the top left corner on the top
1711 // horizontal. Note that corner indices start at the top left and
1712 // continue clockwise, whereas in our loop i = 0 refers to the top
1713 // right corner.
1715 // When going CCW, the control points are swapped, and the first
1716 // corner that's drawn is the top left (along with the top segment).
1718 // There is considerable latitude in how one chooses the four
1719 // control points for a Bezier curve approximation to an ellipse.
1720 // For the overall path to be continuous and show no corner at the
1721 // endpoints of the arc, points 0 and 3 must be at the ends of the
1722 // straight segments of the rectangle; points 0, 1, and C must be
1723 // collinear; and points 3, 2, and C must also be collinear. This
1724 // leaves only two free parameters: the ratio of the line segments
1725 // 01 and 0C, and the ratio of the line segments 32 and 3C. See
1726 // the following papers for extensive discussion of how to choose
1727 // these ratios:
1729 // Dokken, Tor, et al. "Good approximation of circles by
1730 // curvature-continuous Bezier curves." Computer-Aided
1731 // Geometric Design 7(1990) 33--41.
1732 // Goldapp, Michael. "Approximation of circular arcs by cubic
1733 // polynomials." Computer-Aided Geometric Design 8(1991) 227--238.
1734 // Maisonobe, Luc. "Drawing an elliptical arc using polylines,
1735 // quadratic, or cubic Bezier curves."
1736 // http://www.spaceroots.org/documents/ellipse/elliptical-arc.pdf
1738 // We follow the approach in section 2 of Goldapp (least-error,
1739 // Hermite-type approximation) and make both ratios equal to
1741 // 2 2 + n - sqrt(2n + 28)
1742 // alpha = - * ---------------------
1743 // 3 n - 4
1745 // where n = 3( cbrt(sqrt(2)+1) - cbrt(sqrt(2)-1) ).
1747 // This is the result of Goldapp's equation (10b) when the angle
1748 // swept out by the arc is pi/2, and the parameter "a-bar" is the
1749 // expression given immediately below equation (21).
1751 // Using this value, the maximum radial error for a circle, as a
1752 // fraction of the radius, is on the order of 0.2 x 10^-3.
1753 // Neither Dokken nor Goldapp discusses error for a general
1754 // ellipse; Maisonobe does, but his choice of control points
1755 // follows different constraints, and Goldapp's expression for
1756 // 'alpha' gives much smaller radial error, even for very flat
1757 // ellipses, than Maisonobe's equivalent.
1759 // For the various corners and for each axis, the sign of this
1760 // constant changes, or it might be 0 -- it's multiplied by the
1761 // appropriate multiplier from the list before using.
1763 if (mCairo) {
1764 const gfxFloat alpha = 0.55191497064665766025;
1766 typedef struct { gfxFloat a, b; } twoFloats;
1768 twoFloats cwCornerMults[4] = { { -1, 0 },
1769 { 0, -1 },
1770 { +1, 0 },
1771 { 0, +1 } };
1772 twoFloats ccwCornerMults[4] = { { +1, 0 },
1773 { 0, -1 },
1774 { -1, 0 },
1775 { 0, +1 } };
1777 twoFloats *cornerMults = draw_clockwise ? cwCornerMults : ccwCornerMults;
1779 gfxPoint pc, p0, p1, p2, p3;
1781 if (draw_clockwise)
1782 cairo_move_to(mCairo, rect.X() + corners[NS_CORNER_TOP_LEFT].width, rect.Y());
1783 else
1784 cairo_move_to(mCairo, rect.X() + rect.Width() - corners[NS_CORNER_TOP_RIGHT].width, rect.Y());
1786 NS_FOR_CSS_CORNERS(i) {
1787 // the corner index -- either 1 2 3 0 (cw) or 0 3 2 1 (ccw)
1788 mozilla::css::Corner c = mozilla::css::Corner(draw_clockwise ? ((i+1) % 4) : ((4-i) % 4));
1790 // i+2 and i+3 respectively. These are used to index into the corner
1791 // multiplier table, and were deduced by calculating out the long form
1792 // of each corner and finding a pattern in the signs and values.
1793 int i2 = (i+2) % 4;
1794 int i3 = (i+3) % 4;
1796 pc = rect.AtCorner(c);
1798 if (corners[c].width > 0.0 && corners[c].height > 0.0) {
1799 p0.x = pc.x + cornerMults[i].a * corners[c].width;
1800 p0.y = pc.y + cornerMults[i].b * corners[c].height;
1802 p3.x = pc.x + cornerMults[i3].a * corners[c].width;
1803 p3.y = pc.y + cornerMults[i3].b * corners[c].height;
1805 p1.x = p0.x + alpha * cornerMults[i2].a * corners[c].width;
1806 p1.y = p0.y + alpha * cornerMults[i2].b * corners[c].height;
1808 p2.x = p3.x - alpha * cornerMults[i3].a * corners[c].width;
1809 p2.y = p3.y - alpha * cornerMults[i3].b * corners[c].height;
1811 cairo_line_to (mCairo, p0.x, p0.y);
1812 cairo_curve_to (mCairo,
1813 p1.x, p1.y,
1814 p2.x, p2.y,
1815 p3.x, p3.y);
1816 } else {
1817 cairo_line_to (mCairo, pc.x, pc.y);
1821 cairo_close_path (mCairo);
1822 } else {
1823 EnsurePathBuilder();
1825 const gfxFloat alpha = 0.55191497064665766025;
1827 typedef struct { gfxFloat a, b; } twoFloats;
1829 twoFloats cwCornerMults[4] = { { -1, 0 },
1830 { 0, -1 },
1831 { +1, 0 },
1832 { 0, +1 } };
1833 twoFloats ccwCornerMults[4] = { { +1, 0 },
1834 { 0, -1 },
1835 { -1, 0 },
1836 { 0, +1 } };
1838 twoFloats *cornerMults = draw_clockwise ? cwCornerMults : ccwCornerMults;
1840 gfxPoint pc, p0, p1, p2, p3;
1842 if (draw_clockwise)
1843 mPathBuilder->MoveTo(Point(Float(rect.X() + corners[NS_CORNER_TOP_LEFT].width), Float(rect.Y())));
1844 else
1845 mPathBuilder->MoveTo(Point(Float(rect.X() + rect.Width() - corners[NS_CORNER_TOP_RIGHT].width), Float(rect.Y())));
1847 NS_FOR_CSS_CORNERS(i) {
1848 // the corner index -- either 1 2 3 0 (cw) or 0 3 2 1 (ccw)
1849 mozilla::css::Corner c = mozilla::css::Corner(draw_clockwise ? ((i+1) % 4) : ((4-i) % 4));
1851 // i+2 and i+3 respectively. These are used to index into the corner
1852 // multiplier table, and were deduced by calculating out the long form
1853 // of each corner and finding a pattern in the signs and values.
1854 int i2 = (i+2) % 4;
1855 int i3 = (i+3) % 4;
1857 pc = rect.AtCorner(c);
1859 if (corners[c].width > 0.0 && corners[c].height > 0.0) {
1860 p0.x = pc.x + cornerMults[i].a * corners[c].width;
1861 p0.y = pc.y + cornerMults[i].b * corners[c].height;
1863 p3.x = pc.x + cornerMults[i3].a * corners[c].width;
1864 p3.y = pc.y + cornerMults[i3].b * corners[c].height;
1866 p1.x = p0.x + alpha * cornerMults[i2].a * corners[c].width;
1867 p1.y = p0.y + alpha * cornerMults[i2].b * corners[c].height;
1869 p2.x = p3.x - alpha * cornerMults[i3].a * corners[c].width;
1870 p2.y = p3.y - alpha * cornerMults[i3].b * corners[c].height;
1872 mPathBuilder->LineTo(ToPoint(p0));
1873 mPathBuilder->BezierTo(ToPoint(p1), ToPoint(p2), ToPoint(p3));
1874 } else {
1875 mPathBuilder->LineTo(ToPoint(pc));
1879 mPathBuilder->Close();
1883 #ifdef MOZ_DUMP_PAINTING
1884 void
1885 gfxContext::WriteAsPNG(const char* aFile)
1887 nsRefPtr<gfxASurface> surf = CurrentSurface();
1888 if (surf) {
1889 surf->WriteAsPNG(aFile);
1890 } else {
1891 NS_WARNING("No surface found!");
1895 void
1896 gfxContext::DumpAsDataURL()
1898 nsRefPtr<gfxASurface> surf = CurrentSurface();
1899 if (surf) {
1900 surf->DumpAsDataURL();
1901 } else {
1902 NS_WARNING("No surface found!");
1906 void
1907 gfxContext::CopyAsDataURL()
1909 nsRefPtr<gfxASurface> surf = CurrentSurface();
1910 if (surf) {
1911 surf->CopyAsDataURL();
1912 } else {
1913 NS_WARNING("No surface found!");
1916 #endif
1918 void
1919 gfxContext::EnsurePath()
1921 if (mPathBuilder) {
1922 mPath = mPathBuilder->Finish();
1923 mPathBuilder = NULL;
1926 if (mPath) {
1927 if (mTransformChanged) {
1928 Matrix mat = mTransform;
1929 mat.Invert();
1930 mat = mPathTransform * mat;
1931 mPathBuilder = mPath->TransformedCopyToBuilder(mat, CurrentState().fillRule);
1932 mPath = mPathBuilder->Finish();
1933 mPathBuilder = NULL;
1935 mTransformChanged = false;
1938 if (CurrentState().fillRule == mPath->GetFillRule()) {
1939 return;
1942 mPathBuilder = mPath->CopyToBuilder(CurrentState().fillRule);
1944 mPath = mPathBuilder->Finish();
1945 mPathBuilder = NULL;
1946 return;
1949 EnsurePathBuilder();
1950 mPath = mPathBuilder->Finish();
1951 mPathBuilder = NULL;
1954 void
1955 gfxContext::EnsurePathBuilder()
1957 if (mPathBuilder && !mTransformChanged) {
1958 return;
1961 if (mPath) {
1962 if (!mTransformChanged) {
1963 mPathBuilder = mPath->CopyToBuilder(CurrentState().fillRule);
1964 mPath = NULL;
1965 } else {
1966 Matrix invTransform = mTransform;
1967 invTransform.Invert();
1968 Matrix toNewUS = mPathTransform * invTransform;
1969 mPathBuilder = mPath->TransformedCopyToBuilder(toNewUS, CurrentState().fillRule);
1971 return;
1974 DebugOnly<PathBuilder*> oldPath = mPathBuilder.get();
1976 if (!mPathBuilder) {
1977 mPathBuilder = mDT->CreatePathBuilder(CurrentState().fillRule);
1979 if (mPathIsRect) {
1980 mPathBuilder->MoveTo(mRect.TopLeft());
1981 mPathBuilder->LineTo(mRect.TopRight());
1982 mPathBuilder->LineTo(mRect.BottomRight());
1983 mPathBuilder->LineTo(mRect.BottomLeft());
1984 mPathBuilder->Close();
1988 if (mTransformChanged) {
1989 // This could be an else if since this should never happen when
1990 // mPathBuilder is NULL and mPath is NULL. But this way we can assert
1991 // if all the state is as expected.
1992 MOZ_ASSERT(oldPath);
1993 MOZ_ASSERT(!mPathIsRect);
1995 Matrix invTransform = mTransform;
1996 invTransform.Invert();
1997 Matrix toNewUS = mPathTransform * invTransform;
1999 RefPtr<Path> path = mPathBuilder->Finish();
2000 mPathBuilder = path->TransformedCopyToBuilder(toNewUS, CurrentState().fillRule);
2003 mPathIsRect = false;
2006 void
2007 gfxContext::FillAzure(Float aOpacity)
2009 AzureState &state = CurrentState();
2011 CompositionOp op = GetOp();
2013 if (mPathIsRect) {
2014 MOZ_ASSERT(!mTransformChanged);
2016 if (state.opIsClear) {
2017 mDT->ClearRect(mRect);
2018 } else if (op == OP_SOURCE) {
2019 // Emulate cairo operator source which is bound by mask!
2020 mDT->ClearRect(mRect);
2021 mDT->FillRect(mRect, GeneralPattern(this), DrawOptions(aOpacity));
2022 } else {
2023 mDT->FillRect(mRect, GeneralPattern(this), DrawOptions(aOpacity, op, state.aaMode));
2025 } else {
2026 EnsurePath();
2028 NS_ASSERTION(!state.opIsClear, "We shouldn't be clearing complex paths!");
2030 mDT->Fill(mPath, GeneralPattern(this), DrawOptions(aOpacity, op, state.aaMode));
2034 void
2035 gfxContext::PushClipsToDT(DrawTarget *aDT)
2037 // Tricky, we have to restore all clips -since the last time- the clip
2038 // was reset. If we didn't reset the clip, just popping the clips we
2039 // added was fine.
2040 unsigned int lastReset = 0;
2041 for (int i = mStateStack.Length() - 2; i > 0; i--) {
2042 if (mStateStack[i].clipWasReset) {
2043 lastReset = i;
2047 // Don't need to save the old transform, we'll be setting a new one soon!
2049 // Push all clips from the last state on the stack where the clip was
2050 // reset to the clip before ours.
2051 for (unsigned int i = lastReset; i < mStateStack.Length() - 1; i++) {
2052 for (unsigned int c = 0; c < mStateStack[i].pushedClips.Length(); c++) {
2053 aDT->SetTransform(mStateStack[i].pushedClips[c].transform * GetDeviceTransform());
2054 if (mStateStack[i].pushedClips[c].path) {
2055 aDT->PushClip(mStateStack[i].pushedClips[c].path);
2056 } else {
2057 aDT->PushClipRect(mStateStack[i].pushedClips[c].rect);
2063 CompositionOp
2064 gfxContext::GetOp()
2066 if (CurrentState().op != OP_SOURCE) {
2067 return CurrentState().op;
2070 AzureState &state = CurrentState();
2071 if (state.pattern) {
2072 if (state.pattern->IsOpaque()) {
2073 return OP_OVER;
2074 } else {
2075 return OP_SOURCE;
2077 } else if (state.sourceSurface) {
2078 if (state.sourceSurface->GetFormat() == FORMAT_B8G8R8X8) {
2079 return OP_OVER;
2080 } else {
2081 return OP_SOURCE;
2083 } else {
2084 if (state.color.a > 0.999) {
2085 return OP_OVER;
2086 } else {
2087 return OP_SOURCE;
2092 /* SVG font code can change the transform after having set the pattern on the
2093 * context. When the pattern is set it is in user space, if the transform is
2094 * changed after doing so the pattern needs to be converted back into userspace.
2095 * We just store the old pattern transform here so that we only do the work
2096 * needed here if the pattern is actually used.
2097 * We need to avoid doing this when this ChangeTransform comes from a restore,
2098 * since the current pattern and the current transform are both part of the
2099 * state we know the new CurrentState()'s values are valid. But if we assume
2100 * a change they might become invalid since patternTransformChanged is part of
2101 * the state and might be false for the restored AzureState.
2103 void
2104 gfxContext::ChangeTransform(const Matrix &aNewMatrix, bool aUpdatePatternTransform)
2106 AzureState &state = CurrentState();
2108 if (aUpdatePatternTransform && (state.pattern || state.sourceSurface)
2109 && !state.patternTransformChanged) {
2110 state.patternTransform = mTransform;
2111 state.patternTransformChanged = true;
2114 if (mPathIsRect) {
2115 Matrix invMatrix = aNewMatrix;
2117 invMatrix.Invert();
2119 Matrix toNewUS = mTransform * invMatrix;
2121 if (toNewUS.IsRectilinear()) {
2122 mRect = toNewUS.TransformBounds(mRect);
2123 mRect.NudgeToIntegers();
2124 } else {
2125 mPathBuilder = mDT->CreatePathBuilder(CurrentState().fillRule);
2127 mPathBuilder->MoveTo(toNewUS * mRect.TopLeft());
2128 mPathBuilder->LineTo(toNewUS * mRect.TopRight());
2129 mPathBuilder->LineTo(toNewUS * mRect.BottomRight());
2130 mPathBuilder->LineTo(toNewUS * mRect.BottomLeft());
2131 mPathBuilder->Close();
2133 mPathIsRect = false;
2136 // No need to consider the transform changed now!
2137 mTransformChanged = false;
2138 } else if ((mPath || mPathBuilder) && !mTransformChanged) {
2139 mTransformChanged = true;
2140 mPathTransform = mTransform;
2143 mTransform = aNewMatrix;
2145 mDT->SetTransform(GetDTTransform());
2148 Rect
2149 gfxContext::GetAzureDeviceSpaceClipBounds()
2151 unsigned int lastReset = 0;
2152 for (int i = mStateStack.Length() - 1; i > 0; i--) {
2153 if (mStateStack[i].clipWasReset) {
2154 lastReset = i;
2158 Rect rect(CurrentState().deviceOffset.x, CurrentState().deviceOffset.y,
2159 Float(mDT->GetSize().width), Float(mDT->GetSize().height));
2160 for (unsigned int i = lastReset; i < mStateStack.Length(); i++) {
2161 for (unsigned int c = 0; c < mStateStack[i].pushedClips.Length(); c++) {
2162 AzureState::PushedClip &clip = mStateStack[i].pushedClips[c];
2163 if (clip.path) {
2164 Rect bounds = clip.path->GetBounds(clip.transform);
2165 rect.IntersectRect(rect, bounds);
2166 } else {
2167 rect.IntersectRect(rect, clip.transform.TransformBounds(clip.rect));
2172 return rect;
2175 Matrix
2176 gfxContext::GetDeviceTransform() const
2178 Matrix mat;
2179 mat.Translate(-CurrentState().deviceOffset.x, -CurrentState().deviceOffset.y);
2180 return mat;
2183 Matrix
2184 gfxContext::GetDTTransform() const
2186 Matrix mat = mTransform;
2187 mat._31 -= CurrentState().deviceOffset.x;
2188 mat._32 -= CurrentState().deviceOffset.y;
2189 return mat;
2192 void
2193 gfxContext::PushNewDT(gfxASurface::gfxContentType content)
2195 Rect clipBounds = GetAzureDeviceSpaceClipBounds();
2196 clipBounds.RoundOut();
2198 clipBounds.width = std::max(1.0f, clipBounds.width);
2199 clipBounds.height = std::max(1.0f, clipBounds.height);
2201 RefPtr<DrawTarget> newDT =
2202 mDT->CreateSimilarDrawTarget(IntSize(int32_t(clipBounds.width), int32_t(clipBounds.height)),
2203 gfxPlatform::GetPlatform()->Optimal2DFormatForContent(content));
2205 Save();
2207 CurrentState().drawTarget = newDT;
2208 CurrentState().deviceOffset = clipBounds.TopLeft();
2210 mDT = newDT;
2214 * Work out whether cairo will snap inter-glyph spacing to pixels.
2216 * Layout does not align text to pixel boundaries, so, with font drawing
2217 * backends that snap glyph positions to pixels, it is important that
2218 * inter-glyph spacing within words is always an integer number of pixels.
2219 * This ensures that the drawing backend snaps all of the word's glyphs in the
2220 * same direction and so inter-glyph spacing remains the same.
2222 void
2223 gfxContext::GetRoundOffsetsToPixels(bool *aRoundX, bool *aRoundY)
2225 *aRoundX = false;
2226 // Could do something fancy here for ScaleFactors of
2227 // AxisAlignedTransforms, but we leave things simple.
2228 // Not much point rounding if a matrix will mess things up anyway.
2229 // Also return false for non-cairo contexts.
2230 if (CurrentMatrix().HasNonTranslation() || mDT) {
2231 *aRoundY = false;
2232 return;
2235 // All raster backends snap glyphs to pixels vertically.
2236 // Print backends set CAIRO_HINT_METRICS_OFF.
2237 *aRoundY = true;
2239 cairo_t *cr = GetCairo();
2240 cairo_scaled_font_t *scaled_font = cairo_get_scaled_font(cr);
2241 // Sometimes hint metrics gets set for us, most notably for printing.
2242 cairo_font_options_t *font_options = cairo_font_options_create();
2243 cairo_scaled_font_get_font_options(scaled_font, font_options);
2244 cairo_hint_metrics_t hint_metrics =
2245 cairo_font_options_get_hint_metrics(font_options);
2246 cairo_font_options_destroy(font_options);
2248 switch (hint_metrics) {
2249 case CAIRO_HINT_METRICS_OFF:
2250 *aRoundY = false;
2251 return;
2252 case CAIRO_HINT_METRICS_DEFAULT:
2253 // Here we mimic what cairo surface/font backends do. Printing
2254 // surfaces have already been handled by hint_metrics. The
2255 // fallback show_glyphs implementation composites pixel-aligned
2256 // glyph surfaces, so we just pick surface/font combinations that
2257 // override this.
2258 switch (cairo_scaled_font_get_type(scaled_font)) {
2259 #if CAIRO_HAS_DWRITE_FONT // dwrite backend is not in std cairo releases yet
2260 case CAIRO_FONT_TYPE_DWRITE:
2261 // show_glyphs is implemented on the font and so is used for
2262 // all surface types; however, it may pixel-snap depending on
2263 // the dwrite rendering mode
2264 if (!cairo_dwrite_scaled_font_get_force_GDI_classic(scaled_font) &&
2265 gfxWindowsPlatform::GetPlatform()->DWriteMeasuringMode() ==
2266 DWRITE_MEASURING_MODE_NATURAL) {
2267 return;
2269 #endif
2270 case CAIRO_FONT_TYPE_QUARTZ:
2271 // Quartz surfaces implement show_glyphs for Quartz fonts
2272 if (cairo_surface_get_type(cairo_get_target(cr)) ==
2273 CAIRO_SURFACE_TYPE_QUARTZ) {
2274 return;
2276 default:
2277 break;
2279 // fall through:
2280 case CAIRO_HINT_METRICS_ON:
2281 break;
2283 *aRoundX = true;
2284 return;