1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "DrawTargetCairo.h"
9 #include "SourceSurfaceCairo.h"
10 #include "PathCairo.h"
11 #include "HelpersCairo.h"
12 #include "BorrowedContext.h"
13 #include "FilterNodeSoftware.h"
14 #include "mozilla/UniquePtr.h"
15 #include "mozilla/Vector.h"
16 #include "mozilla/StaticPrefs_gfx.h"
17 #include "mozilla/StaticPrefs_print.h"
18 #include "nsPrintfCString.h"
21 #include "cairo-tee.h"
28 #ifdef CAIRO_HAS_QUARTZ_SURFACE
29 # include "cairo-quartz.h"
30 # ifdef MOZ_WIDGET_COCOA
31 # include <ApplicationServices/ApplicationServices.h>
35 #ifdef CAIRO_HAS_XLIB_SURFACE
36 # include "cairo-xlib.h"
39 #ifdef CAIRO_HAS_WIN32_SURFACE
40 # include "cairo-win32.h"
43 #define PIXMAN_DONT_DEFINE_STDINT
49 #define CAIRO_COORD_MAX (Float(0x7fffff))
54 cairo_surface_t
* DrawTargetCairo::mDummySurface
;
58 // An RAII class to prepare to draw a context and optional path. Saves and
59 // restores the context on construction/destruction.
60 class AutoPrepareForDrawing
{
62 AutoPrepareForDrawing(DrawTargetCairo
* dt
, cairo_t
* ctx
) : mCtx(ctx
) {
63 dt
->PrepareForDrawing(ctx
);
65 MOZ_ASSERT(cairo_status(mCtx
) ||
66 dt
->GetTransform().FuzzyEquals(GetTransform()));
69 AutoPrepareForDrawing(DrawTargetCairo
* dt
, cairo_t
* ctx
, const Path
* path
)
71 dt
->PrepareForDrawing(ctx
, path
);
73 MOZ_ASSERT(cairo_status(mCtx
) ||
74 dt
->GetTransform().FuzzyEquals(GetTransform()));
77 ~AutoPrepareForDrawing() {
79 cairo_status_t status
= cairo_status(mCtx
);
81 gfxWarning() << "DrawTargetCairo context in error state: "
82 << cairo_status_to_string(status
) << "(" << status
<< ")";
88 Matrix
GetTransform() {
90 cairo_get_matrix(mCtx
, &mat
);
91 return Matrix(mat
.xx
, mat
.yx
, mat
.xy
, mat
.yy
, mat
.x0
, mat
.y0
);
98 /* Clamp r to (0,0) (2^23,2^23)
99 * these are to be device coordinates.
101 * Returns false if the rectangle is completely out of bounds,
104 * This function assumes that it will be called with a rectangle being
105 * drawn into a surface with an identity transformation matrix; that
106 * is, anything above or to the left of (0,0) will be offscreen.
108 * First it checks if the rectangle is entirely beyond
109 * CAIRO_COORD_MAX; if so, it can't ever appear on the screen --
112 * Then it shifts any rectangles with x/y < 0 so that x and y are = 0,
113 * and adjusts the width and height appropriately. For example, a
114 * rectangle from (0,-5) with dimensions (5,10) will become a
115 * rectangle from (0,0) with dimensions (5,5).
117 * If after negative x/y adjustment to 0, either the width or height
118 * is negative, then the rectangle is completely offscreen, and
119 * nothing is drawn -- false is returned.
121 * Finally, if x+width or y+height are greater than CAIRO_COORD_MAX,
122 * the width and height are clamped such x+width or y+height are equal
123 * to CAIRO_COORD_MAX, and true is returned.
125 static bool ConditionRect(Rect
& r
) {
126 // if either x or y is way out of bounds;
127 // note that we don't handle negative w/h here
128 if (r
.X() > CAIRO_COORD_MAX
|| r
.Y() > CAIRO_COORD_MAX
) return false;
131 r
.SetWidth(r
.XMost());
132 if (r
.Width() < 0.f
) return false;
136 if (r
.XMost() > CAIRO_COORD_MAX
) {
137 r
.SetRightEdge(CAIRO_COORD_MAX
);
141 r
.SetHeight(r
.YMost());
142 if (r
.Height() < 0.f
) return false;
147 if (r
.YMost() > CAIRO_COORD_MAX
) {
148 r
.SetBottomEdge(CAIRO_COORD_MAX
);
153 } // end anonymous namespace
155 static bool SupportsSelfCopy(cairo_surface_t
* surface
) {
156 switch (cairo_surface_get_type(surface
)) {
157 #ifdef CAIRO_HAS_QUARTZ_SURFACE
158 case CAIRO_SURFACE_TYPE_QUARTZ
:
161 #ifdef CAIRO_HAS_WIN32_SURFACE
162 case CAIRO_SURFACE_TYPE_WIN32
:
163 case CAIRO_SURFACE_TYPE_WIN32_PRINTING
:
171 static bool PatternIsCompatible(const Pattern
& aPattern
) {
172 switch (aPattern
.GetType()) {
173 case PatternType::LINEAR_GRADIENT
: {
174 const LinearGradientPattern
& pattern
=
175 static_cast<const LinearGradientPattern
&>(aPattern
);
176 return pattern
.mStops
->GetBackendType() == BackendType::CAIRO
;
178 case PatternType::RADIAL_GRADIENT
: {
179 const RadialGradientPattern
& pattern
=
180 static_cast<const RadialGradientPattern
&>(aPattern
);
181 return pattern
.mStops
->GetBackendType() == BackendType::CAIRO
;
183 case PatternType::CONIC_GRADIENT
: {
184 const ConicGradientPattern
& pattern
=
185 static_cast<const ConicGradientPattern
&>(aPattern
);
186 return pattern
.mStops
->GetBackendType() == BackendType::CAIRO
;
193 static cairo_user_data_key_t surfaceDataKey
;
195 static void ReleaseData(void* aData
) {
196 DataSourceSurface
* data
= static_cast<DataSourceSurface
*>(aData
);
201 static cairo_surface_t
* CopyToImageSurface(unsigned char* aData
,
202 const IntRect
& aRect
,
204 SurfaceFormat aFormat
) {
207 auto aRectWidth
= aRect
.Width();
208 auto aRectHeight
= aRect
.Height();
210 cairo_surface_t
* surf
= cairo_image_surface_create(
211 GfxFormatToCairoFormat(aFormat
), aRectWidth
, aRectHeight
);
212 // In certain scenarios, requesting larger than 8k image fails. Bug 803568
213 // covers the details of how to run into it, but the full detailed
214 // investigation hasn't been done to determine the underlying cause. We
215 // will just handle the failure to allocate the surface to avoid a crash.
216 if (cairo_surface_status(surf
)) {
217 gfxWarning() << "Invalid surface DTC " << cairo_surface_status(surf
);
221 unsigned char* surfData
= cairo_image_surface_get_data(surf
);
222 int surfStride
= cairo_image_surface_get_stride(surf
);
223 int32_t pixelWidth
= BytesPerPixel(aFormat
);
225 unsigned char* source
= aData
+ aRect
.Y() * aStride
+ aRect
.X() * pixelWidth
;
227 MOZ_ASSERT(aStride
>= aRectWidth
* pixelWidth
);
228 for (int32_t y
= 0; y
< aRectHeight
; ++y
) {
229 memcpy(surfData
+ y
* surfStride
, source
+ y
* aStride
,
230 aRectWidth
* pixelWidth
);
232 cairo_surface_mark_dirty(surf
);
237 * If aSurface can be represented as a surface of type
238 * CAIRO_SURFACE_TYPE_IMAGE then returns that surface. Does
239 * not add a reference.
241 static cairo_surface_t
* GetAsImageSurface(cairo_surface_t
* aSurface
) {
242 if (cairo_surface_get_type(aSurface
) == CAIRO_SURFACE_TYPE_IMAGE
) {
244 #ifdef CAIRO_HAS_WIN32_SURFACE
245 } else if (cairo_surface_get_type(aSurface
) == CAIRO_SURFACE_TYPE_WIN32
) {
246 return cairo_win32_surface_get_image(aSurface
);
253 static cairo_surface_t
* CreateSubImageForData(unsigned char* aData
,
254 const IntRect
& aRect
, int aStride
,
255 SurfaceFormat aFormat
) {
257 gfxWarning() << "DrawTargetCairo.CreateSubImageForData null aData";
260 unsigned char* data
=
261 aData
+ aRect
.Y() * aStride
+ aRect
.X() * BytesPerPixel(aFormat
);
263 cairo_surface_t
* image
= cairo_image_surface_create_for_data(
264 data
, GfxFormatToCairoFormat(aFormat
), aRect
.Width(), aRect
.Height(),
266 // Set the subimage's device offset so that in remains in the same place
267 // relative to the parent
268 cairo_surface_set_device_offset(image
, -aRect
.X(), -aRect
.Y());
273 * Returns a referenced cairo_surface_t representing the
274 * sub-image specified by aSubImage.
276 static cairo_surface_t
* ExtractSubImage(cairo_surface_t
* aSurface
,
277 const IntRect
& aSubImage
,
278 SurfaceFormat aFormat
) {
279 // No need to worry about retaining a reference to the original
280 // surface since the only caller of this function guarantees
281 // that aSurface will stay alive as long as the result
283 cairo_surface_t
* image
= GetAsImageSurface(aSurface
);
286 CreateSubImageForData(cairo_image_surface_get_data(image
), aSubImage
,
287 cairo_image_surface_get_stride(image
), aFormat
);
291 cairo_surface_t
* similar
= cairo_surface_create_similar(
292 aSurface
, cairo_surface_get_content(aSurface
), aSubImage
.Width(),
295 cairo_t
* ctx
= cairo_create(similar
);
296 cairo_set_operator(ctx
, CAIRO_OPERATOR_SOURCE
);
297 cairo_set_source_surface(ctx
, aSurface
, -aSubImage
.X(), -aSubImage
.Y());
301 cairo_surface_set_device_offset(similar
, -aSubImage
.X(), -aSubImage
.Y());
306 * Returns cairo surface for the given SourceSurface.
307 * If possible, it will use the cairo_surface associated with aSurface,
308 * otherwise, it will create a new cairo_surface.
309 * In either case, the caller must call cairo_surface_destroy on the
310 * result when it is done with it.
312 static cairo_surface_t
* GetCairoSurfaceForSourceSurface(
313 SourceSurface
* aSurface
, bool aExistingOnly
= false,
314 const IntRect
& aSubImage
= IntRect()) {
319 IntRect subimage
= IntRect(IntPoint(), aSurface
->GetSize());
320 if (!aSubImage
.IsEmpty()) {
321 MOZ_ASSERT(!aExistingOnly
);
322 MOZ_ASSERT(subimage
.Contains(aSubImage
));
323 subimage
= aSubImage
;
326 if (aSurface
->GetType() == SurfaceType::CAIRO
) {
327 cairo_surface_t
* surf
=
328 static_cast<SourceSurfaceCairo
*>(aSurface
)->GetSurface();
329 if (aSubImage
.IsEmpty()) {
330 cairo_surface_reference(surf
);
332 surf
= ExtractSubImage(surf
, subimage
, aSurface
->GetFormat());
337 if (aSurface
->GetType() == SurfaceType::CAIRO_IMAGE
) {
338 cairo_surface_t
* surf
=
339 static_cast<const DataSourceSurfaceCairo
*>(aSurface
)->GetSurface();
340 if (aSubImage
.IsEmpty()) {
341 cairo_surface_reference(surf
);
343 surf
= ExtractSubImage(surf
, subimage
, aSurface
->GetFormat());
352 RefPtr
<DataSourceSurface
> data
= aSurface
->GetDataSurface();
357 DataSourceSurface::MappedSurface map
;
358 if (!data
->Map(DataSourceSurface::READ
, &map
)) {
362 cairo_surface_t
* surf
= CreateSubImageForData(map
.mData
, subimage
,
363 map
.mStride
, data
->GetFormat());
365 // In certain scenarios, requesting larger than 8k image fails. Bug 803568
366 // covers the details of how to run into it, but the full detailed
367 // investigation hasn't been done to determine the underlying cause. We
368 // will just handle the failure to allocate the surface to avoid a crash.
369 if (!surf
|| cairo_surface_status(surf
)) {
370 if (surf
&& (cairo_surface_status(surf
) == CAIRO_STATUS_INVALID_STRIDE
)) {
371 // If we failed because of an invalid stride then copy into
372 // a new surface with a stride that cairo chooses. No need to
373 // set user data since we're not dependent on the original
375 cairo_surface_t
* result
= CopyToImageSurface(
376 map
.mData
, subimage
, map
.mStride
, data
->GetFormat());
384 cairo_surface_set_user_data(surf
, &surfaceDataKey
, data
.forget().take(),
389 // An RAII class to temporarily clear any device offset set
390 // on a surface. Note that this does not take a reference to the
392 class AutoClearDeviceOffset final
{
394 explicit AutoClearDeviceOffset(SourceSurface
* aSurface
)
395 : mSurface(nullptr), mX(0), mY(0) {
399 explicit AutoClearDeviceOffset(const Pattern
& aPattern
)
400 : mSurface(nullptr), mX(0.0), mY(0.0) {
401 if (aPattern
.GetType() == PatternType::SURFACE
) {
402 const SurfacePattern
& pattern
=
403 static_cast<const SurfacePattern
&>(aPattern
);
404 Init(pattern
.mSurface
);
408 ~AutoClearDeviceOffset() {
410 cairo_surface_set_device_offset(mSurface
, mX
, mY
);
415 void Init(SourceSurface
* aSurface
) {
416 cairo_surface_t
* surface
= GetCairoSurfaceForSourceSurface(aSurface
, true);
419 cairo_surface_destroy(surface
);
423 void Init(cairo_surface_t
* aSurface
) {
425 cairo_surface_get_device_offset(mSurface
, &mX
, &mY
);
426 cairo_surface_set_device_offset(mSurface
, 0, 0);
429 cairo_surface_t
* mSurface
;
434 static inline void CairoPatternAddGradientStop(cairo_pattern_t
* aPattern
,
435 const GradientStop
& aStop
,
437 cairo_pattern_add_color_stop_rgba(aPattern
, aStop
.offset
+ aNudge
,
438 aStop
.color
.r
, aStop
.color
.g
, aStop
.color
.b
,
442 // Never returns nullptr. As such, you must always pass in Cairo-compatible
443 // patterns, most notably gradients with a GradientStopCairo.
444 // The pattern returned must have cairo_pattern_destroy() called on it by the
446 // As the cairo_pattern_t returned may depend on the Pattern passed in, the
447 // lifetime of the cairo_pattern_t returned must not exceed the lifetime of the
448 // Pattern passed in.
449 static cairo_pattern_t
* GfxPatternToCairoPattern(const Pattern
& aPattern
,
451 const Matrix
& aTransform
) {
452 cairo_pattern_t
* pat
;
453 const Matrix
* matrix
= nullptr;
455 switch (aPattern
.GetType()) {
456 case PatternType::COLOR
: {
457 DeviceColor color
= static_cast<const ColorPattern
&>(aPattern
).mColor
;
458 pat
= cairo_pattern_create_rgba(color
.r
, color
.g
, color
.b
,
463 case PatternType::SURFACE
: {
464 const SurfacePattern
& pattern
=
465 static_cast<const SurfacePattern
&>(aPattern
);
466 cairo_surface_t
* surf
= GetCairoSurfaceForSourceSurface(
467 pattern
.mSurface
, false, pattern
.mSamplingRect
);
468 if (!surf
) return nullptr;
470 pat
= cairo_pattern_create_for_surface(surf
);
472 matrix
= &pattern
.mMatrix
;
474 cairo_pattern_set_filter(
475 pat
, GfxSamplingFilterToCairoFilter(pattern
.mSamplingFilter
));
476 cairo_pattern_set_extend(pat
,
477 GfxExtendToCairoExtend(pattern
.mExtendMode
));
479 cairo_surface_destroy(surf
);
482 case PatternType::LINEAR_GRADIENT
: {
483 const LinearGradientPattern
& pattern
=
484 static_cast<const LinearGradientPattern
&>(aPattern
);
486 pat
= cairo_pattern_create_linear(pattern
.mBegin
.x
, pattern
.mBegin
.y
,
487 pattern
.mEnd
.x
, pattern
.mEnd
.y
);
489 MOZ_ASSERT(pattern
.mStops
->GetBackendType() == BackendType::CAIRO
);
490 GradientStopsCairo
* cairoStops
=
491 static_cast<GradientStopsCairo
*>(pattern
.mStops
.get());
492 cairo_pattern_set_extend(
493 pat
, GfxExtendToCairoExtend(cairoStops
->GetExtendMode()));
495 matrix
= &pattern
.mMatrix
;
497 const std::vector
<GradientStop
>& stops
= cairoStops
->GetStops();
498 for (size_t i
= 0; i
< stops
.size(); ++i
) {
499 CairoPatternAddGradientStop(pat
, stops
[i
]);
504 case PatternType::RADIAL_GRADIENT
: {
505 const RadialGradientPattern
& pattern
=
506 static_cast<const RadialGradientPattern
&>(aPattern
);
508 pat
= cairo_pattern_create_radial(pattern
.mCenter1
.x
, pattern
.mCenter1
.y
,
509 pattern
.mRadius1
, pattern
.mCenter2
.x
,
510 pattern
.mCenter2
.y
, pattern
.mRadius2
);
512 MOZ_ASSERT(pattern
.mStops
->GetBackendType() == BackendType::CAIRO
);
513 GradientStopsCairo
* cairoStops
=
514 static_cast<GradientStopsCairo
*>(pattern
.mStops
.get());
515 cairo_pattern_set_extend(
516 pat
, GfxExtendToCairoExtend(cairoStops
->GetExtendMode()));
518 matrix
= &pattern
.mMatrix
;
520 const std::vector
<GradientStop
>& stops
= cairoStops
->GetStops();
521 for (size_t i
= 0; i
< stops
.size(); ++i
) {
522 CairoPatternAddGradientStop(pat
, stops
[i
]);
527 case PatternType::CONIC_GRADIENT
: {
528 // XXX(ntim): Bug 1617039 - Implement conic-gradient for Cairo
529 pat
= cairo_pattern_create_rgba(0.0, 0.0, 0.0, 0.0);
534 // We should support all pattern types!
539 // The pattern matrix is a matrix that transforms the pattern into user
540 // space. Cairo takes a matrix that converts from user space to pattern
541 // space. Cairo therefore needs the inverse.
544 GfxMatrixToCairoMatrix(*matrix
, mat
);
545 cairo_matrix_invert(&mat
);
546 cairo_pattern_set_matrix(pat
, &mat
);
552 static bool NeedIntermediateSurface(const Pattern
& aPattern
,
553 const DrawOptions
& aOptions
) {
554 // We pre-multiply colours' alpha by the global alpha, so we don't need to
555 // use an intermediate surface for them.
556 if (aPattern
.GetType() == PatternType::COLOR
) return false;
558 if (aOptions
.mAlpha
== 1.0) return false;
563 DrawTargetCairo::DrawTargetCairo()
566 mTransformSingular(false),
567 mLockedBits(nullptr),
568 mFontOptions(nullptr) {}
570 DrawTargetCairo::~DrawTargetCairo() {
571 cairo_destroy(mContext
);
573 cairo_surface_destroy(mSurface
);
577 cairo_font_options_destroy(mFontOptions
);
578 mFontOptions
= nullptr;
580 MOZ_ASSERT(!mLockedBits
);
583 bool DrawTargetCairo::IsValid() const {
584 return mSurface
&& !cairo_surface_status(mSurface
) && mContext
&&
585 !cairo_surface_status(cairo_get_group_target(mContext
));
588 DrawTargetType
DrawTargetCairo::GetType() const {
590 cairo_surface_type_t type
= cairo_surface_get_type(mSurface
);
591 if (type
== CAIRO_SURFACE_TYPE_TEE
) {
592 type
= cairo_surface_get_type(cairo_tee_surface_index(mSurface
, 0));
593 MOZ_ASSERT(type
!= CAIRO_SURFACE_TYPE_TEE
, "C'mon!");
595 type
== cairo_surface_get_type(cairo_tee_surface_index(mSurface
, 1)),
596 "What should we do here?");
599 case CAIRO_SURFACE_TYPE_PDF
:
600 case CAIRO_SURFACE_TYPE_PS
:
601 case CAIRO_SURFACE_TYPE_SVG
:
602 case CAIRO_SURFACE_TYPE_WIN32_PRINTING
:
603 case CAIRO_SURFACE_TYPE_XML
:
604 return DrawTargetType::VECTOR
;
606 case CAIRO_SURFACE_TYPE_VG
:
607 case CAIRO_SURFACE_TYPE_GL
:
608 case CAIRO_SURFACE_TYPE_GLITZ
:
609 case CAIRO_SURFACE_TYPE_QUARTZ
:
610 case CAIRO_SURFACE_TYPE_DIRECTFB
:
611 return DrawTargetType::HARDWARE_RASTER
;
613 case CAIRO_SURFACE_TYPE_SKIA
:
614 case CAIRO_SURFACE_TYPE_QT
:
615 MOZ_FALLTHROUGH_ASSERT(
616 "Can't determine actual DrawTargetType for DrawTargetCairo - "
617 "assuming SOFTWARE_RASTER");
618 case CAIRO_SURFACE_TYPE_IMAGE
:
619 case CAIRO_SURFACE_TYPE_XLIB
:
620 case CAIRO_SURFACE_TYPE_XCB
:
621 case CAIRO_SURFACE_TYPE_WIN32
:
622 case CAIRO_SURFACE_TYPE_BEOS
:
623 case CAIRO_SURFACE_TYPE_OS2
:
624 case CAIRO_SURFACE_TYPE_QUARTZ_IMAGE
:
625 case CAIRO_SURFACE_TYPE_SCRIPT
:
626 case CAIRO_SURFACE_TYPE_RECORDING
:
627 case CAIRO_SURFACE_TYPE_DRM
:
628 case CAIRO_SURFACE_TYPE_SUBSURFACE
:
629 case CAIRO_SURFACE_TYPE_TEE
: // included to silence warning about
630 // unhandled enum value
631 return DrawTargetType::SOFTWARE_RASTER
;
633 MOZ_CRASH("GFX: Unsupported cairo surface type");
636 MOZ_ASSERT(false, "Could not determine DrawTargetType for DrawTargetCairo");
637 return DrawTargetType::SOFTWARE_RASTER
;
640 IntSize
DrawTargetCairo::GetSize() const { return mSize
; }
642 SurfaceFormat
GfxFormatForCairoSurface(cairo_surface_t
* surface
) {
643 cairo_surface_type_t type
= cairo_surface_get_type(surface
);
644 if (type
== CAIRO_SURFACE_TYPE_IMAGE
) {
645 return CairoFormatToGfxFormat(cairo_image_surface_get_format(surface
));
647 #ifdef CAIRO_HAS_XLIB_SURFACE
648 // xlib is currently the only Cairo backend that creates 16bpp surfaces
649 if (type
== CAIRO_SURFACE_TYPE_XLIB
&&
650 cairo_xlib_surface_get_depth(surface
) == 16) {
651 return SurfaceFormat::R5G6B5_UINT16
;
654 return CairoContentToGfxFormat(cairo_surface_get_content(surface
));
657 void DrawTargetCairo::Link(const char* aDestination
, const Rect
& aRect
) {
658 if (!aDestination
|| !*aDestination
) {
659 // No destination? Just bail out.
663 // We need to \-escape any single-quotes in the destination string, in order
664 // to pass it via the attributes arg to cairo_tag_begin.
666 // We also need to escape any backslashes (bug 1748077), as per doc at
667 // https://www.cairographics.org/manual/cairo-Tags-and-Links.html#cairo-tag-begin
668 // The cairo-pdf-interchange backend (used on all platforms EXCEPT macOS)
669 // actually requires that we *doubly* escape the backslashes (this may be a
670 // cairo bug), while the quartz backend is fine with them singly-escaped.
672 // (Encoding of non-ASCII chars etc gets handled later by the PDF backend.)
673 nsAutoCString
dest(aDestination
);
674 for (size_t i
= dest
.Length(); i
> 0;) {
676 if (dest
[i
] == '\'') {
677 dest
.ReplaceLiteral(i
, 1, "\\'");
678 } else if (dest
[i
] == '\\') {
680 dest
.ReplaceLiteral(i
, 1, "\\\\");
682 dest
.ReplaceLiteral(i
, 1, "\\\\\\\\");
687 double x
= aRect
.x
, y
= aRect
.y
, w
= aRect
.width
, h
= aRect
.height
;
688 cairo_user_to_device(mContext
, &x
, &y
);
689 cairo_user_to_device_distance(mContext
, &w
, &h
);
691 nsPrintfCString
attributes("rect=[%f %f %f %f] ", x
, y
, w
, h
);
692 if (dest
[0] == '#') {
693 // The actual destination does not have a leading '#'.
694 attributes
.AppendPrintf("dest='%s'", dest
.get() + 1);
696 attributes
.AppendPrintf("uri='%s'", dest
.get());
699 // We generate a begin/end pair with no content in between, because we are
700 // using the rect attribute of the begin tag to specify the link region
701 // rather than depending on cairo to accumulate the painted area.
702 cairo_tag_begin(mContext
, CAIRO_TAG_LINK
, attributes
.get());
703 cairo_tag_end(mContext
, CAIRO_TAG_LINK
);
706 void DrawTargetCairo::Destination(const char* aDestination
,
707 const Point
& aPoint
) {
708 if (!aDestination
|| !*aDestination
) {
709 // No destination? Just bail out.
713 nsAutoCString
dest(aDestination
);
714 for (size_t i
= dest
.Length(); i
> 0;) {
716 if (dest
[i
] == '\'') {
717 dest
.ReplaceLiteral(i
, 1, "\\'");
721 double x
= aPoint
.x
, y
= aPoint
.y
;
722 cairo_user_to_device(mContext
, &x
, &y
);
724 nsPrintfCString
attributes("name='%s' x=%f y=%f internal", dest
.get(), x
, y
);
725 cairo_tag_begin(mContext
, CAIRO_TAG_DEST
, attributes
.get());
726 cairo_tag_end(mContext
, CAIRO_TAG_DEST
);
729 already_AddRefed
<SourceSurface
> DrawTargetCairo::Snapshot() {
731 gfxCriticalNote
<< "DrawTargetCairo::Snapshot with bad surface "
732 << hexa(mSurface
) << ", context " << hexa(mContext
)
734 << (mSurface
? cairo_surface_status(mSurface
) : -1);
738 RefPtr
<SourceSurface
> snapshot(mSnapshot
);
739 return snapshot
.forget();
742 IntSize size
= GetSize();
744 mSnapshot
= new SourceSurfaceCairo(mSurface
, size
,
745 GfxFormatForCairoSurface(mSurface
), this);
746 RefPtr
<SourceSurface
> snapshot(mSnapshot
);
747 return snapshot
.forget();
750 bool DrawTargetCairo::LockBits(uint8_t** aData
, IntSize
* aSize
,
751 int32_t* aStride
, SurfaceFormat
* aFormat
,
753 cairo_surface_t
* target
= cairo_get_group_target(mContext
);
754 cairo_surface_t
* surf
= target
;
755 #ifdef CAIRO_HAS_WIN32_SURFACE
756 if (cairo_surface_get_type(surf
) == CAIRO_SURFACE_TYPE_WIN32
) {
757 cairo_surface_t
* imgsurf
= cairo_win32_surface_get_image(surf
);
763 if (cairo_surface_get_type(surf
) == CAIRO_SURFACE_TYPE_IMAGE
&&
764 cairo_surface_status(surf
) == CAIRO_STATUS_SUCCESS
) {
766 cairo_surface_get_device_offset(target
, &offset
.x
.value
, &offset
.y
.value
);
767 // verify the device offset can be converted to integers suitable for a
769 IntPoint
origin(int32_t(-offset
.x
), int32_t(-offset
.y
));
770 if (-PointDouble(origin
) != offset
|| (!aOrigin
&& origin
!= IntPoint())) {
777 mLockedBits
= cairo_image_surface_get_data(surf
);
778 *aData
= mLockedBits
;
779 *aSize
= IntSize(cairo_image_surface_get_width(surf
),
780 cairo_image_surface_get_height(surf
));
781 *aStride
= cairo_image_surface_get_stride(surf
);
782 *aFormat
= CairoFormatToGfxFormat(cairo_image_surface_get_format(surf
));
792 void DrawTargetCairo::ReleaseBits(uint8_t* aData
) {
793 MOZ_ASSERT(mLockedBits
== aData
);
794 mLockedBits
= nullptr;
795 cairo_surface_t
* surf
= cairo_get_group_target(mContext
);
796 #ifdef CAIRO_HAS_WIN32_SURFACE
797 if (cairo_surface_get_type(surf
) == CAIRO_SURFACE_TYPE_WIN32
) {
798 cairo_surface_t
* imgsurf
= cairo_win32_surface_get_image(surf
);
800 cairo_surface_mark_dirty(imgsurf
);
804 cairo_surface_mark_dirty(surf
);
807 void DrawTargetCairo::Flush() {
808 cairo_surface_t
* surf
= cairo_get_group_target(mContext
);
809 cairo_surface_flush(surf
);
812 void DrawTargetCairo::PrepareForDrawing(cairo_t
* aContext
,
813 const Path
* aPath
/* = nullptr */) {
817 cairo_surface_t
* DrawTargetCairo::GetDummySurface() {
819 return mDummySurface
;
822 mDummySurface
= cairo_image_surface_create(CAIRO_FORMAT_ARGB32
, 1, 1);
824 return mDummySurface
;
827 static void PaintWithAlpha(cairo_t
* aContext
, const DrawOptions
& aOptions
) {
828 if (aOptions
.mCompositionOp
== CompositionOp::OP_SOURCE
) {
829 // Cairo treats the source operator like a lerp when alpha is < 1.
830 // Approximate the desired operator by: out = 0; out += src*alpha;
831 if (aOptions
.mAlpha
== 1) {
832 cairo_set_operator(aContext
, CAIRO_OPERATOR_SOURCE
);
833 cairo_paint(aContext
);
835 cairo_set_operator(aContext
, CAIRO_OPERATOR_CLEAR
);
836 cairo_paint(aContext
);
837 cairo_set_operator(aContext
, CAIRO_OPERATOR_ADD
);
838 cairo_paint_with_alpha(aContext
, aOptions
.mAlpha
);
841 cairo_set_operator(aContext
, GfxOpToCairoOp(aOptions
.mCompositionOp
));
842 cairo_paint_with_alpha(aContext
, aOptions
.mAlpha
);
846 void DrawTargetCairo::DrawSurface(SourceSurface
* aSurface
, const Rect
& aDest
,
848 const DrawSurfaceOptions
& aSurfOptions
,
849 const DrawOptions
& aOptions
) {
850 if (mTransformSingular
|| aDest
.IsEmpty()) {
854 if (!IsValid() || !aSurface
) {
855 gfxCriticalNote
<< "DrawSurface with bad surface "
856 << cairo_surface_status(cairo_get_group_target(mContext
));
860 AutoPrepareForDrawing
prep(this, mContext
);
861 AutoClearDeviceOffset
clear(aSurface
);
863 float sx
= aSource
.Width() / aDest
.Width();
864 float sy
= aSource
.Height() / aDest
.Height();
866 cairo_matrix_t src_mat
;
867 cairo_matrix_init_translate(&src_mat
, aSource
.X() - aSurface
->GetRect().x
,
868 aSource
.Y() - aSurface
->GetRect().y
);
869 cairo_matrix_scale(&src_mat
, sx
, sy
);
871 cairo_surface_t
* surf
= GetCairoSurfaceForSourceSurface(aSurface
);
874 << "Failed to create cairo surface for DrawTargetCairo::DrawSurface";
877 cairo_pattern_t
* pat
= cairo_pattern_create_for_surface(surf
);
878 cairo_surface_destroy(surf
);
880 cairo_pattern_set_matrix(pat
, &src_mat
);
881 cairo_pattern_set_filter(
882 pat
, GfxSamplingFilterToCairoFilter(aSurfOptions
.mSamplingFilter
));
883 // For PDF output, we avoid using EXTEND_PAD here because floating-point
884 // error accumulation may lead cairo_pdf_surface to conclude that padding
885 // is needed due to an apparent one- or two-pixel mismatch between source
886 // pattern and destination rect sizes when we're rendering a pdf.js page,
887 // and this forces undesirable fallback to the rasterization codepath
888 // instead of simply replaying the recording.
889 // (See bug 1777209.)
890 cairo_pattern_set_extend(
891 pat
, cairo_surface_get_type(mSurface
) == CAIRO_SURFACE_TYPE_PDF
895 cairo_set_antialias(mContext
,
896 GfxAntialiasToCairoAntialias(aOptions
.mAntialiasMode
));
898 // If the destination rect covers the entire clipped area, then unbounded and
899 // bounded operations are identical, and we don't need to push a group.
900 bool needsGroup
= !IsOperatorBoundByMask(aOptions
.mCompositionOp
) &&
901 !aDest
.Contains(GetUserSpaceClip());
903 cairo_translate(mContext
, aDest
.X(), aDest
.Y());
906 cairo_push_group(mContext
);
907 cairo_new_path(mContext
);
908 cairo_rectangle(mContext
, 0, 0, aDest
.Width(), aDest
.Height());
909 cairo_set_source(mContext
, pat
);
910 cairo_fill(mContext
);
911 cairo_pop_group_to_source(mContext
);
913 cairo_new_path(mContext
);
914 cairo_rectangle(mContext
, 0, 0, aDest
.Width(), aDest
.Height());
915 cairo_clip(mContext
);
916 cairo_set_source(mContext
, pat
);
919 PaintWithAlpha(mContext
, aOptions
);
921 cairo_pattern_destroy(pat
);
924 void DrawTargetCairo::DrawFilter(FilterNode
* aNode
, const Rect
& aSourceRect
,
925 const Point
& aDestPoint
,
926 const DrawOptions
& aOptions
) {
927 FilterNodeSoftware
* filter
= static_cast<FilterNodeSoftware
*>(aNode
);
928 filter
->Draw(this, aSourceRect
, aDestPoint
, aOptions
);
931 void DrawTargetCairo::DrawSurfaceWithShadow(SourceSurface
* aSurface
,
933 const ShadowOptions
& aShadow
,
934 CompositionOp aOperator
) {
935 if (!IsValid() || !aSurface
) {
936 gfxCriticalNote
<< "DrawSurfaceWithShadow with bad surface "
937 << cairo_surface_status(cairo_get_group_target(mContext
));
941 if (aSurface
->GetType() != SurfaceType::CAIRO
) {
945 AutoClearDeviceOffset
clear(aSurface
);
947 Float width
= Float(aSurface
->GetSize().width
);
948 Float height
= Float(aSurface
->GetSize().height
);
950 SourceSurfaceCairo
* source
= static_cast<SourceSurfaceCairo
*>(aSurface
);
951 cairo_surface_t
* sourcesurf
= source
->GetSurface();
952 cairo_surface_t
* blursurf
;
953 cairo_surface_t
* surf
;
955 // We only use the A8 surface for blurred shadows. Unblurred shadows can just
956 // use the RGBA surface directly.
957 if (cairo_surface_get_type(sourcesurf
) == CAIRO_SURFACE_TYPE_TEE
) {
958 blursurf
= cairo_tee_surface_index(sourcesurf
, 0);
959 surf
= cairo_tee_surface_index(sourcesurf
, 1);
961 blursurf
= sourcesurf
;
965 if (aShadow
.mSigma
!= 0.0f
) {
966 MOZ_ASSERT(cairo_surface_get_type(blursurf
) == CAIRO_SURFACE_TYPE_IMAGE
);
967 Rect
extents(0, 0, width
, height
);
968 AlphaBoxBlur
blur(extents
, cairo_image_surface_get_stride(blursurf
),
969 aShadow
.mSigma
, aShadow
.mSigma
);
970 blur
.Blur(cairo_image_surface_get_data(blursurf
));
974 ClearSurfaceForUnboundedSource(aOperator
);
976 cairo_save(mContext
);
977 cairo_set_operator(mContext
, GfxOpToCairoOp(aOperator
));
978 cairo_identity_matrix(mContext
);
979 cairo_translate(mContext
, aDest
.x
, aDest
.y
);
981 bool needsGroup
= !IsOperatorBoundByMask(aOperator
);
983 cairo_push_group(mContext
);
986 cairo_set_source_rgba(mContext
, aShadow
.mColor
.r
, aShadow
.mColor
.g
,
987 aShadow
.mColor
.b
, aShadow
.mColor
.a
);
988 cairo_mask_surface(mContext
, blursurf
, aShadow
.mOffset
.x
, aShadow
.mOffset
.y
);
990 if (blursurf
!= surf
|| aSurface
->GetFormat() != SurfaceFormat::A8
) {
991 // Now that the shadow has been drawn, we can draw the surface on top.
992 cairo_set_source_surface(mContext
, surf
, 0, 0);
993 cairo_new_path(mContext
);
994 cairo_rectangle(mContext
, 0, 0, width
, height
);
995 cairo_fill(mContext
);
999 cairo_pop_group_to_source(mContext
);
1000 cairo_paint(mContext
);
1003 cairo_restore(mContext
);
1006 void DrawTargetCairo::DrawPattern(const Pattern
& aPattern
,
1007 const StrokeOptions
& aStrokeOptions
,
1008 const DrawOptions
& aOptions
,
1009 DrawPatternType aDrawType
,
1010 bool aPathBoundsClip
) {
1011 if (!PatternIsCompatible(aPattern
)) {
1015 AutoClearDeviceOffset
clear(aPattern
);
1017 cairo_pattern_t
* pat
=
1018 GfxPatternToCairoPattern(aPattern
, aOptions
.mAlpha
, GetTransform());
1022 if (cairo_pattern_status(pat
)) {
1023 cairo_pattern_destroy(pat
);
1024 gfxWarning() << "Invalid pattern";
1028 cairo_set_source(mContext
, pat
);
1030 cairo_set_antialias(mContext
,
1031 GfxAntialiasToCairoAntialias(aOptions
.mAntialiasMode
));
1033 if (NeedIntermediateSurface(aPattern
, aOptions
) ||
1034 (!IsOperatorBoundByMask(aOptions
.mCompositionOp
) && !aPathBoundsClip
)) {
1035 cairo_push_group_with_content(mContext
, CAIRO_CONTENT_COLOR_ALPHA
);
1037 // Don't want operators to be applied twice
1038 cairo_set_operator(mContext
, CAIRO_OPERATOR_OVER
);
1040 if (aDrawType
== DRAW_STROKE
) {
1041 SetCairoStrokeOptions(mContext
, aStrokeOptions
);
1042 cairo_stroke_preserve(mContext
);
1044 cairo_fill_preserve(mContext
);
1047 cairo_pop_group_to_source(mContext
);
1049 // Now draw the content using the desired operator
1050 PaintWithAlpha(mContext
, aOptions
);
1052 cairo_set_operator(mContext
, GfxOpToCairoOp(aOptions
.mCompositionOp
));
1054 if (aDrawType
== DRAW_STROKE
) {
1055 SetCairoStrokeOptions(mContext
, aStrokeOptions
);
1056 cairo_stroke_preserve(mContext
);
1058 cairo_fill_preserve(mContext
);
1062 cairo_pattern_destroy(pat
);
1065 void DrawTargetCairo::FillRect(const Rect
& aRect
, const Pattern
& aPattern
,
1066 const DrawOptions
& aOptions
) {
1067 if (mTransformSingular
) {
1071 AutoPrepareForDrawing
prep(this, mContext
);
1073 bool restoreTransform
= false;
1077 /* Clamp coordinates to work around a design bug in cairo */
1078 if (r
.Width() > CAIRO_COORD_MAX
|| r
.Height() > CAIRO_COORD_MAX
||
1079 r
.X() < -CAIRO_COORD_MAX
|| r
.X() > CAIRO_COORD_MAX
||
1080 r
.Y() < -CAIRO_COORD_MAX
|| r
.Y() > CAIRO_COORD_MAX
) {
1081 if (!mat
.IsRectilinear()) {
1082 gfxWarning() << "DrawTargetCairo::FillRect() misdrawing huge Rect "
1083 "with non-rectilinear transform";
1086 mat
= GetTransform();
1087 r
= mat
.TransformBounds(r
);
1089 if (!ConditionRect(r
)) {
1090 gfxWarning() << "Ignoring DrawTargetCairo::FillRect() call with "
1091 "out-of-bounds Rect";
1095 restoreTransform
= true;
1096 SetTransform(Matrix());
1099 cairo_new_path(mContext
);
1100 cairo_rectangle(mContext
, r
.X(), r
.Y(), r
.Width(), r
.Height());
1102 bool pathBoundsClip
= false;
1104 if (r
.Contains(GetUserSpaceClip())) {
1105 pathBoundsClip
= true;
1108 DrawPattern(aPattern
, StrokeOptions(), aOptions
, DRAW_FILL
, pathBoundsClip
);
1110 if (restoreTransform
) {
1115 void DrawTargetCairo::CopySurfaceInternal(cairo_surface_t
* aSurface
,
1116 const IntRect
& aSource
,
1117 const IntPoint
& aDest
) {
1118 if (cairo_surface_status(aSurface
)) {
1119 gfxWarning() << "Invalid surface" << cairo_surface_status(aSurface
);
1123 cairo_identity_matrix(mContext
);
1125 cairo_set_source_surface(mContext
, aSurface
, aDest
.x
- aSource
.X(),
1126 aDest
.y
- aSource
.Y());
1127 cairo_set_operator(mContext
, CAIRO_OPERATOR_SOURCE
);
1128 cairo_set_antialias(mContext
, CAIRO_ANTIALIAS_NONE
);
1130 cairo_reset_clip(mContext
);
1131 cairo_new_path(mContext
);
1132 cairo_rectangle(mContext
, aDest
.x
, aDest
.y
, aSource
.Width(),
1134 cairo_fill(mContext
);
1137 void DrawTargetCairo::CopySurface(SourceSurface
* aSurface
,
1138 const IntRect
& aSource
,
1139 const IntPoint
& aDest
) {
1140 if (mTransformSingular
) {
1144 AutoPrepareForDrawing
prep(this, mContext
);
1145 AutoClearDeviceOffset
clear(aSurface
);
1148 gfxWarning() << "Unsupported surface type specified";
1152 cairo_surface_t
* surf
= GetCairoSurfaceForSourceSurface(aSurface
);
1154 gfxWarning() << "Unsupported surface type specified";
1158 CopySurfaceInternal(surf
, aSource
, aDest
);
1159 cairo_surface_destroy(surf
);
1162 void DrawTargetCairo::CopyRect(const IntRect
& aSource
, const IntPoint
& aDest
) {
1163 if (mTransformSingular
) {
1167 AutoPrepareForDrawing
prep(this, mContext
);
1169 IntRect source
= aSource
;
1170 cairo_surface_t
* surf
= mSurface
;
1172 if (!SupportsSelfCopy(mSurface
) && aSource
.ContainsY(aDest
.y
)) {
1173 cairo_surface_t
* similar
= cairo_surface_create_similar(
1174 mSurface
, GfxFormatToCairoContent(GetFormat()), aSource
.Width(),
1176 cairo_t
* ctx
= cairo_create(similar
);
1177 cairo_set_operator(ctx
, CAIRO_OPERATOR_SOURCE
);
1178 cairo_set_source_surface(ctx
, surf
, -aSource
.X(), -aSource
.Y());
1182 source
.MoveTo(0, 0);
1186 CopySurfaceInternal(surf
, source
, aDest
);
1188 if (surf
!= mSurface
) {
1189 cairo_surface_destroy(surf
);
1193 void DrawTargetCairo::ClearRect(const Rect
& aRect
) {
1194 if (mTransformSingular
) {
1198 AutoPrepareForDrawing
prep(this, mContext
);
1200 if (!mContext
|| aRect
.Width() < 0 || aRect
.Height() < 0 ||
1201 !std::isfinite(aRect
.X()) || !std::isfinite(aRect
.Width()) ||
1202 !std::isfinite(aRect
.Y()) || !std::isfinite(aRect
.Height())) {
1203 gfxCriticalNote
<< "ClearRect with invalid argument " << gfx::hexa(mContext
)
1204 << " with " << aRect
.Width() << "x" << aRect
.Height()
1205 << " [" << aRect
.X() << ", " << aRect
.Y() << "]";
1208 cairo_set_antialias(mContext
, CAIRO_ANTIALIAS_NONE
);
1209 cairo_new_path(mContext
);
1210 cairo_set_operator(mContext
, CAIRO_OPERATOR_CLEAR
);
1211 cairo_rectangle(mContext
, aRect
.X(), aRect
.Y(), aRect
.Width(),
1213 cairo_fill(mContext
);
1216 void DrawTargetCairo::StrokeRect(
1217 const Rect
& aRect
, const Pattern
& aPattern
,
1218 const StrokeOptions
& aStrokeOptions
/* = StrokeOptions() */,
1219 const DrawOptions
& aOptions
/* = DrawOptions() */) {
1220 if (mTransformSingular
) {
1224 AutoPrepareForDrawing
prep(this, mContext
);
1226 cairo_new_path(mContext
);
1227 cairo_rectangle(mContext
, aRect
.X(), aRect
.Y(), aRect
.Width(),
1230 DrawPattern(aPattern
, aStrokeOptions
, aOptions
, DRAW_STROKE
);
1233 void DrawTargetCairo::StrokeLine(
1234 const Point
& aStart
, const Point
& aEnd
, const Pattern
& aPattern
,
1235 const StrokeOptions
& aStrokeOptions
/* = StrokeOptions() */,
1236 const DrawOptions
& aOptions
/* = DrawOptions() */) {
1237 if (mTransformSingular
) {
1241 AutoPrepareForDrawing
prep(this, mContext
);
1243 cairo_new_path(mContext
);
1244 cairo_move_to(mContext
, aStart
.x
, aStart
.y
);
1245 cairo_line_to(mContext
, aEnd
.x
, aEnd
.y
);
1247 DrawPattern(aPattern
, aStrokeOptions
, aOptions
, DRAW_STROKE
);
1250 void DrawTargetCairo::Stroke(
1251 const Path
* aPath
, const Pattern
& aPattern
,
1252 const StrokeOptions
& aStrokeOptions
/* = StrokeOptions() */,
1253 const DrawOptions
& aOptions
/* = DrawOptions() */) {
1254 if (mTransformSingular
) {
1258 AutoPrepareForDrawing
prep(this, mContext
, aPath
);
1260 if (aPath
->GetBackendType() != BackendType::CAIRO
) return;
1263 const_cast<PathCairo
*>(static_cast<const PathCairo
*>(aPath
));
1264 path
->SetPathOnContext(mContext
);
1266 DrawPattern(aPattern
, aStrokeOptions
, aOptions
, DRAW_STROKE
);
1269 void DrawTargetCairo::Fill(const Path
* aPath
, const Pattern
& aPattern
,
1270 const DrawOptions
& aOptions
/* = DrawOptions() */) {
1271 if (mTransformSingular
) {
1275 AutoPrepareForDrawing
prep(this, mContext
, aPath
);
1277 if (aPath
->GetBackendType() != BackendType::CAIRO
) return;
1280 const_cast<PathCairo
*>(static_cast<const PathCairo
*>(aPath
));
1281 path
->SetPathOnContext(mContext
);
1283 DrawPattern(aPattern
, StrokeOptions(), aOptions
, DRAW_FILL
);
1286 bool DrawTargetCairo::IsCurrentGroupOpaque() {
1287 cairo_surface_t
* surf
= cairo_get_group_target(mContext
);
1293 return cairo_surface_get_content(surf
) == CAIRO_CONTENT_COLOR
;
1296 void DrawTargetCairo::SetFontOptions(cairo_antialias_t aAAMode
) {
1297 // This will attempt to detect if the currently set scaled font on the
1298 // context has enabled subpixel AA. If it is not permitted, then it will
1299 // downgrade to grayscale AA.
1300 // This only currently works effectively for the cairo-ft backend relative
1301 // to system defaults, as only cairo-ft reflect system defaults in the scaled
1302 // font state. However, this will work for cairo-ft on both tree Cairo and
1304 // Other backends leave the CAIRO_ANTIALIAS_DEFAULT setting untouched while
1305 // potentially interpreting it as subpixel or even other types of AA that
1306 // can't be safely equivocated with grayscale AA. For this reason we don't
1307 // try to also detect and modify the default AA setting, only explicit
1308 // subpixel AA. These other backends must instead rely on tree Cairo's
1309 // cairo_surface_set_subpixel_antialiasing extension.
1311 // If allowing subpixel AA, then leave Cairo's default AA state.
1312 if (mPermitSubpixelAA
&& aAAMode
== CAIRO_ANTIALIAS_DEFAULT
) {
1316 if (!mFontOptions
) {
1317 mFontOptions
= cairo_font_options_create();
1318 if (!mFontOptions
) {
1319 gfxWarning() << "Failed allocating Cairo font options";
1324 cairo_get_font_options(mContext
, mFontOptions
);
1325 cairo_antialias_t oldAA
= cairo_font_options_get_antialias(mFontOptions
);
1326 cairo_antialias_t newAA
=
1327 aAAMode
== CAIRO_ANTIALIAS_DEFAULT
? oldAA
: aAAMode
;
1328 // Nothing to change if switching to default AA.
1329 if (newAA
== CAIRO_ANTIALIAS_DEFAULT
) {
1332 // If the current font requests subpixel AA, force it to gray since we don't
1333 // allow subpixel AA.
1334 if (!mPermitSubpixelAA
&& newAA
== CAIRO_ANTIALIAS_SUBPIXEL
) {
1335 newAA
= CAIRO_ANTIALIAS_GRAY
;
1337 // Only override old AA with lower levels of AA.
1338 if (oldAA
== CAIRO_ANTIALIAS_DEFAULT
|| (int)newAA
< (int)oldAA
) {
1339 cairo_font_options_set_antialias(mFontOptions
, newAA
);
1340 cairo_set_font_options(mContext
, mFontOptions
);
1344 void DrawTargetCairo::SetPermitSubpixelAA(bool aPermitSubpixelAA
) {
1345 DrawTarget::SetPermitSubpixelAA(aPermitSubpixelAA
);
1346 cairo_surface_set_subpixel_antialiasing(
1347 cairo_get_group_target(mContext
),
1348 aPermitSubpixelAA
? CAIRO_SUBPIXEL_ANTIALIASING_ENABLED
1349 : CAIRO_SUBPIXEL_ANTIALIASING_DISABLED
);
1352 static bool SupportsVariationSettings(cairo_surface_t
* surface
) {
1353 switch (cairo_surface_get_type(surface
)) {
1354 case CAIRO_SURFACE_TYPE_PDF
:
1355 case CAIRO_SURFACE_TYPE_PS
:
1362 void DrawTargetCairo::FillGlyphs(ScaledFont
* aFont
, const GlyphBuffer
& aBuffer
,
1363 const Pattern
& aPattern
,
1364 const DrawOptions
& aOptions
) {
1365 if (mTransformSingular
) {
1370 gfxDebug() << "FillGlyphs bad surface "
1371 << cairo_surface_status(cairo_get_group_target(mContext
));
1375 cairo_scaled_font_t
* cairoScaledFont
=
1376 aFont
? aFont
->GetCairoScaledFont() : nullptr;
1377 if (!cairoScaledFont
) {
1378 gfxDevCrash(LogReason::InvalidFont
) << "Invalid scaled font";
1382 AutoPrepareForDrawing
prep(this, mContext
);
1383 AutoClearDeviceOffset
clear(aPattern
);
1385 cairo_set_scaled_font(mContext
, cairoScaledFont
);
1387 cairo_pattern_t
* pat
=
1388 GfxPatternToCairoPattern(aPattern
, aOptions
.mAlpha
, GetTransform());
1391 cairo_set_source(mContext
, pat
);
1392 cairo_pattern_destroy(pat
);
1394 cairo_antialias_t aa
= GfxAntialiasToCairoAntialias(aOptions
.mAntialiasMode
);
1395 cairo_set_antialias(mContext
, aa
);
1397 // Override any font-specific options as necessary.
1400 // Convert our GlyphBuffer into a vector of Cairo glyphs. This code can
1401 // execute millions of times in short periods, so we want to avoid heap
1402 // allocation whenever possible. So we use an inline vector capacity of 1024
1403 // bytes (the maximum allowed by mozilla::Vector), which gives an inline
1404 // length of 1024 / 24 = 42 elements, which is enough to typically avoid heap
1405 // allocation in ~99% of cases.
1406 Vector
<cairo_glyph_t
, 1024 / sizeof(cairo_glyph_t
)> glyphs
;
1407 if (!glyphs
.resizeUninitialized(aBuffer
.mNumGlyphs
)) {
1408 gfxDevCrash(LogReason::GlyphAllocFailedCairo
) << "glyphs allocation failed";
1411 for (uint32_t i
= 0; i
< aBuffer
.mNumGlyphs
; ++i
) {
1412 glyphs
[i
].index
= aBuffer
.mGlyphs
[i
].mIndex
;
1413 glyphs
[i
].x
= aBuffer
.mGlyphs
[i
].mPosition
.x
;
1414 glyphs
[i
].y
= aBuffer
.mGlyphs
[i
].mPosition
.y
;
1417 if (!SupportsVariationSettings(mSurface
) && aFont
->HasVariationSettings() &&
1418 StaticPrefs::print_font_variations_as_paths()) {
1419 cairo_set_fill_rule(mContext
, CAIRO_FILL_RULE_WINDING
);
1420 cairo_new_path(mContext
);
1421 cairo_glyph_path(mContext
, &glyphs
[0], aBuffer
.mNumGlyphs
);
1422 cairo_set_operator(mContext
, CAIRO_OPERATOR_OVER
);
1423 cairo_fill(mContext
);
1425 cairo_show_glyphs(mContext
, &glyphs
[0], aBuffer
.mNumGlyphs
);
1428 if (cairo_surface_status(cairo_get_group_target(mContext
))) {
1429 gfxDebug() << "Ending FillGlyphs with a bad surface "
1430 << cairo_surface_status(cairo_get_group_target(mContext
));
1434 void DrawTargetCairo::Mask(const Pattern
& aSource
, const Pattern
& aMask
,
1435 const DrawOptions
& aOptions
/* = DrawOptions() */) {
1436 if (mTransformSingular
) {
1440 AutoPrepareForDrawing
prep(this, mContext
);
1441 AutoClearDeviceOffset
clearSource(aSource
);
1442 AutoClearDeviceOffset
clearMask(aMask
);
1444 cairo_set_antialias(mContext
,
1445 GfxAntialiasToCairoAntialias(aOptions
.mAntialiasMode
));
1447 cairo_pattern_t
* source
=
1448 GfxPatternToCairoPattern(aSource
, aOptions
.mAlpha
, GetTransform());
1453 cairo_pattern_t
* mask
=
1454 GfxPatternToCairoPattern(aMask
, aOptions
.mAlpha
, GetTransform());
1456 cairo_pattern_destroy(source
);
1460 if (cairo_pattern_status(source
) || cairo_pattern_status(mask
)) {
1461 cairo_pattern_destroy(source
);
1462 cairo_pattern_destroy(mask
);
1463 gfxWarning() << "Invalid pattern";
1467 cairo_set_source(mContext
, source
);
1468 cairo_set_operator(mContext
, GfxOpToCairoOp(aOptions
.mCompositionOp
));
1469 cairo_mask(mContext
, mask
);
1471 cairo_pattern_destroy(mask
);
1472 cairo_pattern_destroy(source
);
1475 void DrawTargetCairo::MaskSurface(const Pattern
& aSource
, SourceSurface
* aMask
,
1476 Point aOffset
, const DrawOptions
& aOptions
) {
1477 if (mTransformSingular
) {
1481 AutoPrepareForDrawing
prep(this, mContext
);
1482 AutoClearDeviceOffset
clearSource(aSource
);
1483 AutoClearDeviceOffset
clearMask(aMask
);
1485 if (!PatternIsCompatible(aSource
)) {
1489 cairo_set_antialias(mContext
,
1490 GfxAntialiasToCairoAntialias(aOptions
.mAntialiasMode
));
1492 cairo_pattern_t
* pat
=
1493 GfxPatternToCairoPattern(aSource
, aOptions
.mAlpha
, GetTransform());
1498 if (cairo_pattern_status(pat
)) {
1499 cairo_pattern_destroy(pat
);
1500 gfxWarning() << "Invalid pattern";
1504 cairo_set_source(mContext
, pat
);
1506 if (NeedIntermediateSurface(aSource
, aOptions
)) {
1507 cairo_push_group_with_content(mContext
, CAIRO_CONTENT_COLOR_ALPHA
);
1509 // Don't want operators to be applied twice
1510 cairo_set_operator(mContext
, CAIRO_OPERATOR_OVER
);
1512 // Now draw the content using the desired operator
1513 cairo_paint_with_alpha(mContext
, aOptions
.mAlpha
);
1515 cairo_pop_group_to_source(mContext
);
1518 cairo_surface_t
* surf
= GetCairoSurfaceForSourceSurface(aMask
);
1520 cairo_pattern_destroy(pat
);
1523 cairo_pattern_t
* mask
= cairo_pattern_create_for_surface(surf
);
1524 cairo_matrix_t matrix
;
1526 cairo_matrix_init_translate(&matrix
, -aOffset
.x
- aMask
->GetRect().x
,
1527 -aOffset
.y
- aMask
->GetRect().y
);
1528 cairo_pattern_set_matrix(mask
, &matrix
);
1530 cairo_set_operator(mContext
, GfxOpToCairoOp(aOptions
.mCompositionOp
));
1532 cairo_mask(mContext
, mask
);
1534 cairo_surface_destroy(surf
);
1535 cairo_pattern_destroy(mask
);
1536 cairo_pattern_destroy(pat
);
1539 void DrawTargetCairo::PushClip(const Path
* aPath
) {
1540 if (aPath
->GetBackendType() != BackendType::CAIRO
) {
1545 cairo_save(mContext
);
1548 const_cast<PathCairo
*>(static_cast<const PathCairo
*>(aPath
));
1550 if (mTransformSingular
) {
1551 cairo_new_path(mContext
);
1552 cairo_rectangle(mContext
, 0, 0, 0, 0);
1554 path
->SetPathOnContext(mContext
);
1556 cairo_clip_preserve(mContext
);
1559 void DrawTargetCairo::PushClipRect(const Rect
& aRect
) {
1561 cairo_save(mContext
);
1563 cairo_new_path(mContext
);
1564 if (mTransformSingular
) {
1565 cairo_rectangle(mContext
, 0, 0, 0, 0);
1567 cairo_rectangle(mContext
, aRect
.X(), aRect
.Y(), aRect
.Width(),
1570 cairo_clip_preserve(mContext
);
1573 void DrawTargetCairo::PopClip() {
1574 // save/restore does not affect the path, so no need to call WillChange()
1576 // cairo_restore will restore the transform too and we don't want to do that
1577 // so we'll save it now and restore it after the cairo_restore
1579 cairo_get_matrix(mContext
, &mat
);
1581 cairo_restore(mContext
);
1583 cairo_set_matrix(mContext
, &mat
);
1586 void DrawTargetCairo::PushLayer(bool aOpaque
, Float aOpacity
,
1587 SourceSurface
* aMask
,
1588 const Matrix
& aMaskTransform
,
1589 const IntRect
& aBounds
, bool aCopyBackground
) {
1590 PushLayerWithBlend(aOpaque
, aOpacity
, aMask
, aMaskTransform
, aBounds
,
1591 aCopyBackground
, CompositionOp::OP_OVER
);
1594 void DrawTargetCairo::PushLayerWithBlend(bool aOpaque
, Float aOpacity
,
1595 SourceSurface
* aMask
,
1596 const Matrix
& aMaskTransform
,
1597 const IntRect
& aBounds
,
1598 bool aCopyBackground
,
1599 CompositionOp aCompositionOp
) {
1600 cairo_content_t content
= CAIRO_CONTENT_COLOR_ALPHA
;
1602 if (mFormat
== SurfaceFormat::A8
) {
1603 content
= CAIRO_CONTENT_ALPHA
;
1604 } else if (aOpaque
) {
1605 content
= CAIRO_CONTENT_COLOR
;
1608 if (aCopyBackground
) {
1609 cairo_surface_t
* source
= cairo_get_group_target(mContext
);
1610 cairo_push_group_with_content(mContext
, content
);
1611 cairo_surface_t
* dest
= cairo_get_group_target(mContext
);
1612 cairo_t
* ctx
= cairo_create(dest
);
1613 cairo_set_source_surface(ctx
, source
, 0, 0);
1614 cairo_set_operator(ctx
, CAIRO_OPERATOR_SOURCE
);
1618 cairo_push_group_with_content(mContext
, content
);
1621 PushedLayer
layer(aOpacity
, aCompositionOp
, mPermitSubpixelAA
);
1624 cairo_surface_t
* surf
= GetCairoSurfaceForSourceSurface(aMask
);
1626 layer
.mMaskPattern
= cairo_pattern_create_for_surface(surf
);
1627 Matrix maskTransform
= aMaskTransform
;
1628 maskTransform
.PreTranslate(aMask
->GetRect().X(), aMask
->GetRect().Y());
1630 GfxMatrixToCairoMatrix(maskTransform
, mat
);
1631 cairo_matrix_invert(&mat
);
1632 cairo_pattern_set_matrix(layer
.mMaskPattern
, &mat
);
1633 cairo_surface_destroy(surf
);
1635 gfxCriticalError() << "Failed to get cairo surface for mask surface!";
1639 mPushedLayers
.push_back(layer
);
1641 SetPermitSubpixelAA(aOpaque
);
1644 void DrawTargetCairo::PopLayer() {
1645 MOZ_RELEASE_ASSERT(!mPushedLayers
.empty());
1647 cairo_set_operator(mContext
, CAIRO_OPERATOR_OVER
);
1649 cairo_pop_group_to_source(mContext
);
1651 PushedLayer layer
= mPushedLayers
.back();
1652 mPushedLayers
.pop_back();
1654 if (!layer
.mMaskPattern
) {
1655 cairo_set_operator(mContext
, GfxOpToCairoOp(layer
.mCompositionOp
));
1656 cairo_paint_with_alpha(mContext
, layer
.mOpacity
);
1658 if (layer
.mOpacity
!= Float(1.0)) {
1659 cairo_push_group_with_content(mContext
, CAIRO_CONTENT_COLOR_ALPHA
);
1661 // Now draw the content using the desired operator
1662 cairo_paint_with_alpha(mContext
, layer
.mOpacity
);
1664 cairo_pop_group_to_source(mContext
);
1666 cairo_set_operator(mContext
, GfxOpToCairoOp(layer
.mCompositionOp
));
1667 cairo_mask(mContext
, layer
.mMaskPattern
);
1671 GfxMatrixToCairoMatrix(mTransform
, mat
);
1672 cairo_set_matrix(mContext
, &mat
);
1674 cairo_set_operator(mContext
, CAIRO_OPERATOR_OVER
);
1676 cairo_pattern_destroy(layer
.mMaskPattern
);
1677 SetPermitSubpixelAA(layer
.mWasPermittingSubpixelAA
);
1680 void DrawTargetCairo::ClearSurfaceForUnboundedSource(
1681 const CompositionOp
& aOperator
) {
1682 if (aOperator
!= CompositionOp::OP_SOURCE
) return;
1683 cairo_set_operator(mContext
, CAIRO_OPERATOR_CLEAR
);
1684 // It doesn't really matter what the source is here, since Paint
1685 // isn't bounded by the source and the mask covers the entire clip
1687 cairo_paint(mContext
);
1690 already_AddRefed
<GradientStops
> DrawTargetCairo::CreateGradientStops(
1691 GradientStop
* aStops
, uint32_t aNumStops
, ExtendMode aExtendMode
) const {
1692 return MakeAndAddRef
<GradientStopsCairo
>(aStops
, aNumStops
, aExtendMode
);
1695 already_AddRefed
<FilterNode
> DrawTargetCairo::CreateFilter(FilterType aType
) {
1696 return FilterNodeSoftware::Create(aType
);
1699 already_AddRefed
<SourceSurface
> DrawTargetCairo::CreateSourceSurfaceFromData(
1700 unsigned char* aData
, const IntSize
& aSize
, int32_t aStride
,
1701 SurfaceFormat aFormat
) const {
1703 gfxWarning() << "DrawTargetCairo::CreateSourceSurfaceFromData null aData";
1707 cairo_surface_t
* surf
=
1708 CopyToImageSurface(aData
, IntRect(IntPoint(), aSize
), aStride
, aFormat
);
1713 RefPtr
<SourceSurfaceCairo
> source_surf
=
1714 new SourceSurfaceCairo(surf
, aSize
, aFormat
);
1715 cairo_surface_destroy(surf
);
1717 return source_surf
.forget();
1720 already_AddRefed
<SourceSurface
> DrawTargetCairo::OptimizeSourceSurface(
1721 SourceSurface
* aSurface
) const {
1722 RefPtr
<SourceSurface
> surface(aSurface
);
1723 return surface
.forget();
1726 already_AddRefed
<SourceSurface
>
1727 DrawTargetCairo::CreateSourceSurfaceFromNativeSurface(
1728 const NativeSurface
& aSurface
) const {
1732 already_AddRefed
<DrawTarget
> DrawTargetCairo::CreateSimilarDrawTarget(
1733 const IntSize
& aSize
, SurfaceFormat aFormat
) const {
1734 if (cairo_surface_status(cairo_get_group_target(mContext
))) {
1735 RefPtr
<DrawTargetCairo
> target
= new DrawTargetCairo();
1736 if (target
->Init(aSize
, aFormat
)) {
1737 return target
.forget();
1741 cairo_surface_t
* similar
;
1742 switch (cairo_surface_get_type(mSurface
)) {
1743 #ifdef CAIRO_HAS_WIN32_SURFACE
1744 case CAIRO_SURFACE_TYPE_WIN32
:
1745 similar
= cairo_win32_surface_create_with_dib(
1746 GfxFormatToCairoFormat(aFormat
), aSize
.width
, aSize
.height
);
1749 #ifdef CAIRO_HAS_QUARTZ_SURFACE
1750 case CAIRO_SURFACE_TYPE_QUARTZ
:
1751 if (StaticPrefs::gfx_cairo_quartz_cg_layer_enabled()) {
1752 similar
= cairo_quartz_surface_create_cg_layer(
1753 mSurface
, GfxFormatToCairoContent(aFormat
), aSize
.width
,
1760 similar
= cairo_surface_create_similar(mSurface
,
1761 GfxFormatToCairoContent(aFormat
),
1762 aSize
.width
, aSize
.height
);
1766 if (!cairo_surface_status(similar
)) {
1767 RefPtr
<DrawTargetCairo
> target
= new DrawTargetCairo();
1768 if (target
->InitAlreadyReferenced(similar
, aSize
)) {
1769 return target
.forget();
1774 CriticalLog::DefaultOptions(Factory::ReasonableSurfaceSize(aSize
)))
1775 << "Failed to create similar cairo surface! Size: " << aSize
1776 << " Status: " << cairo_surface_status(similar
)
1777 << cairo_surface_status(cairo_get_group_target(mContext
)) << " format "
1779 cairo_surface_destroy(similar
);
1784 RefPtr
<DrawTarget
> DrawTargetCairo::CreateClippedDrawTarget(
1785 const Rect
& aBounds
, SurfaceFormat aFormat
) {
1786 RefPtr
<DrawTarget
> result
;
1787 // Doing this save()/restore() dance is wasteful
1788 cairo_save(mContext
);
1790 if (!aBounds
.IsEmpty()) {
1791 cairo_new_path(mContext
);
1792 cairo_rectangle(mContext
, aBounds
.X(), aBounds
.Y(), aBounds
.Width(),
1794 cairo_clip(mContext
);
1796 cairo_identity_matrix(mContext
);
1797 IntRect clipBounds
= IntRect::RoundOut(GetUserSpaceClip());
1798 if (!clipBounds
.IsEmpty()) {
1799 RefPtr
<DrawTarget
> dt
= CreateSimilarDrawTarget(
1800 IntSize(clipBounds
.width
, clipBounds
.height
), aFormat
);
1802 result
= gfx::Factory::CreateOffsetDrawTarget(
1803 dt
, IntPoint(clipBounds
.x
, clipBounds
.y
));
1805 result
->SetTransform(mTransform
);
1809 // Everything is clipped but we still want some kind of surface
1810 result
= CreateSimilarDrawTarget(IntSize(1, 1), aFormat
);
1813 cairo_restore(mContext
);
1816 bool DrawTargetCairo::InitAlreadyReferenced(cairo_surface_t
* aSurface
,
1817 const IntSize
& aSize
,
1818 SurfaceFormat
* aFormat
) {
1819 if (cairo_surface_status(aSurface
)) {
1820 gfxCriticalNote
<< "Attempt to create DrawTarget for invalid surface. "
1822 << " Cairo Status: " << cairo_surface_status(aSurface
);
1823 cairo_surface_destroy(aSurface
);
1827 mContext
= cairo_create(aSurface
);
1828 mSurface
= aSurface
;
1830 mFormat
= aFormat
? *aFormat
: GfxFormatForCairoSurface(aSurface
);
1832 // Cairo image surface have a bug where they will allocate a mask surface (for
1833 // clipping) the size of the clip extents, and don't take the surface extents
1834 // into account. Add a manual clip to the surface extents to prevent this.
1835 cairo_new_path(mContext
);
1836 cairo_rectangle(mContext
, 0, 0, mSize
.width
, mSize
.height
);
1837 cairo_clip(mContext
);
1839 if (mFormat
== SurfaceFormat::A8R8G8B8_UINT32
||
1840 mFormat
== SurfaceFormat::R8G8B8A8
) {
1841 SetPermitSubpixelAA(false);
1843 SetPermitSubpixelAA(true);
1849 already_AddRefed
<DrawTarget
> DrawTargetCairo::CreateShadowDrawTarget(
1850 const IntSize
& aSize
, SurfaceFormat aFormat
, float aSigma
) const {
1851 cairo_surface_t
* similar
= cairo_surface_create_similar(
1852 cairo_get_target(mContext
), GfxFormatToCairoContent(aFormat
), aSize
.width
,
1855 if (cairo_surface_status(similar
)) {
1859 // If we don't have a blur then we can use the RGBA mask and keep all the
1860 // operations in graphics memory.
1861 if (aSigma
== 0.0f
|| aFormat
== SurfaceFormat::A8
) {
1862 RefPtr
<DrawTargetCairo
> target
= new DrawTargetCairo();
1863 if (target
->InitAlreadyReferenced(similar
, aSize
)) {
1864 return target
.forget();
1870 cairo_surface_t
* blursurf
=
1871 cairo_image_surface_create(CAIRO_FORMAT_A8
, aSize
.width
, aSize
.height
);
1873 if (cairo_surface_status(blursurf
)) {
1877 cairo_surface_t
* tee
= cairo_tee_surface_create(blursurf
);
1878 cairo_surface_destroy(blursurf
);
1879 if (cairo_surface_status(tee
)) {
1880 cairo_surface_destroy(similar
);
1884 cairo_tee_surface_add(tee
, similar
);
1885 cairo_surface_destroy(similar
);
1887 RefPtr
<DrawTargetCairo
> target
= new DrawTargetCairo();
1888 if (target
->InitAlreadyReferenced(tee
, aSize
)) {
1889 return target
.forget();
1894 bool DrawTargetCairo::Draw3DTransformedSurface(SourceSurface
* aSurface
,
1895 const Matrix4x4
& aMatrix
) {
1896 return DrawTarget::Draw3DTransformedSurface(aSurface
, aMatrix
);
1899 bool DrawTargetCairo::Init(cairo_surface_t
* aSurface
, const IntSize
& aSize
,
1900 SurfaceFormat
* aFormat
) {
1901 cairo_surface_reference(aSurface
);
1902 return InitAlreadyReferenced(aSurface
, aSize
, aFormat
);
1905 bool DrawTargetCairo::Init(const IntSize
& aSize
, SurfaceFormat aFormat
) {
1906 cairo_surface_t
* surf
= cairo_image_surface_create(
1907 GfxFormatToCairoFormat(aFormat
), aSize
.width
, aSize
.height
);
1908 return InitAlreadyReferenced(surf
, aSize
);
1911 bool DrawTargetCairo::Init(unsigned char* aData
, const IntSize
& aSize
,
1912 int32_t aStride
, SurfaceFormat aFormat
) {
1913 cairo_surface_t
* surf
= cairo_image_surface_create_for_data(
1914 aData
, GfxFormatToCairoFormat(aFormat
), aSize
.width
, aSize
.height
,
1916 return InitAlreadyReferenced(surf
, aSize
);
1919 void* DrawTargetCairo::GetNativeSurface(NativeSurfaceType aType
) {
1920 if (aType
== NativeSurfaceType::CAIRO_CONTEXT
) {
1927 void DrawTargetCairo::MarkSnapshotIndependent() {
1929 if (mSnapshot
->refCount() > 1) {
1930 // We only need to worry about snapshots that someone else knows about
1931 mSnapshot
->DrawTargetWillChange();
1933 mSnapshot
= nullptr;
1937 void DrawTargetCairo::WillChange(const Path
* aPath
/* = nullptr */) {
1938 MarkSnapshotIndependent();
1939 MOZ_ASSERT(!mLockedBits
);
1942 void DrawTargetCairo::SetTransform(const Matrix
& aTransform
) {
1943 DrawTarget::SetTransform(aTransform
);
1945 mTransformSingular
= aTransform
.IsSingular();
1946 if (!mTransformSingular
) {
1948 GfxMatrixToCairoMatrix(mTransform
, mat
);
1949 cairo_set_matrix(mContext
, &mat
);
1953 Rect
DrawTargetCairo::GetUserSpaceClip() const {
1954 double clipX1
, clipY1
, clipX2
, clipY2
;
1955 cairo_clip_extents(mContext
, &clipX1
, &clipY1
, &clipX2
, &clipY2
);
1956 return Rect(clipX1
, clipY1
, clipX2
- clipX1
,
1957 clipY2
- clipY1
); // Narrowing of doubles to floats
1961 bool BorrowedXlibDrawable::Init(DrawTarget
* aDT
) {
1962 MOZ_ASSERT(aDT
, "Caller should check for nullptr");
1963 MOZ_ASSERT(!mDT
, "Can't initialize twice!");
1965 mDrawable
= X11None
;
1967 # ifdef CAIRO_HAS_XLIB_SURFACE
1968 if (aDT
->GetBackendType() != BackendType::CAIRO
|| aDT
->IsTiledDrawTarget()) {
1972 DrawTargetCairo
* cairoDT
= static_cast<DrawTargetCairo
*>(aDT
);
1973 cairo_surface_t
* surf
= cairo_get_group_target(cairoDT
->mContext
);
1974 if (cairo_surface_get_type(surf
) != CAIRO_SURFACE_TYPE_XLIB
) {
1977 cairo_surface_flush(surf
);
1979 cairoDT
->WillChange();
1981 mDisplay
= cairo_xlib_surface_get_display(surf
);
1982 mDrawable
= cairo_xlib_surface_get_drawable(surf
);
1983 mScreen
= cairo_xlib_surface_get_screen(surf
);
1984 mVisual
= cairo_xlib_surface_get_visual(surf
);
1985 mSize
.width
= cairo_xlib_surface_get_width(surf
);
1986 mSize
.height
= cairo_xlib_surface_get_height(surf
);
1988 double x
= 0, y
= 0;
1989 cairo_surface_get_device_offset(surf
, &x
, &y
);
1990 mOffset
= Point(x
, y
);
1998 void BorrowedXlibDrawable::Finish() {
1999 DrawTargetCairo
* cairoDT
= static_cast<DrawTargetCairo
*>(mDT
);
2000 cairo_surface_t
* surf
= cairo_get_group_target(cairoDT
->mContext
);
2001 cairo_surface_mark_dirty(surf
);
2003 mDrawable
= X11None
;
2009 } // namespace mozilla