Bug 1874684 - Part 17: Fix uninitialised variable warnings from clang-tidy. r=allstarschh
[gecko.git] / gfx / 2d / DrawTargetCairo.cpp
blobe189fe24455f50c55a0a30a3323cdcbd67e5d31f
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"
20 #include "cairo.h"
21 #include "cairo-tee.h"
22 #include <string.h>
24 #include "Blur.h"
25 #include "Logging.h"
26 #include "Tools.h"
28 #ifdef CAIRO_HAS_QUARTZ_SURFACE
29 # include "cairo-quartz.h"
30 # ifdef MOZ_WIDGET_COCOA
31 # include <ApplicationServices/ApplicationServices.h>
32 # endif
33 #endif
35 #ifdef CAIRO_HAS_XLIB_SURFACE
36 # include "cairo-xlib.h"
37 #endif
39 #ifdef CAIRO_HAS_WIN32_SURFACE
40 # include "cairo-win32.h"
41 #endif
43 #define PIXMAN_DONT_DEFINE_STDINT
44 #include "pixman.h"
46 #include <algorithm>
48 // 2^23
49 #define CAIRO_COORD_MAX (Float(0x7fffff))
51 namespace mozilla {
52 namespace gfx {
54 cairo_surface_t* DrawTargetCairo::mDummySurface;
56 namespace {
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 {
61 public:
62 AutoPrepareForDrawing(DrawTargetCairo* dt, cairo_t* ctx) : mCtx(ctx) {
63 dt->PrepareForDrawing(ctx);
64 cairo_save(mCtx);
65 MOZ_ASSERT(cairo_status(mCtx) ||
66 dt->GetTransform().FuzzyEquals(GetTransform()));
69 AutoPrepareForDrawing(DrawTargetCairo* dt, cairo_t* ctx, const Path* path)
70 : mCtx(ctx) {
71 dt->PrepareForDrawing(ctx, path);
72 cairo_save(mCtx);
73 MOZ_ASSERT(cairo_status(mCtx) ||
74 dt->GetTransform().FuzzyEquals(GetTransform()));
77 ~AutoPrepareForDrawing() {
78 cairo_restore(mCtx);
79 cairo_status_t status = cairo_status(mCtx);
80 if (status) {
81 gfxWarning() << "DrawTargetCairo context in error state: "
82 << cairo_status_to_string(status) << "(" << status << ")";
86 private:
87 #ifdef DEBUG
88 Matrix GetTransform() {
89 cairo_matrix_t mat;
90 cairo_get_matrix(mCtx, &mat);
91 return Matrix(mat.xx, mat.yx, mat.xy, mat.yy, mat.x0, mat.y0);
93 #endif
95 cairo_t* mCtx;
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,
102 * true otherwise.
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 --
110 * false is returned.
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;
130 if (r.X() < 0.f) {
131 r.SetWidth(r.XMost());
132 if (r.Width() < 0.f) return false;
133 r.MoveToX(0.f);
136 if (r.XMost() > CAIRO_COORD_MAX) {
137 r.SetRightEdge(CAIRO_COORD_MAX);
140 if (r.Y() < 0.f) {
141 r.SetHeight(r.YMost());
142 if (r.Height() < 0.f) return false;
144 r.MoveToY(0.f);
147 if (r.YMost() > CAIRO_COORD_MAX) {
148 r.SetBottomEdge(CAIRO_COORD_MAX);
150 return true;
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:
159 return true;
160 #endif
161 #ifdef CAIRO_HAS_WIN32_SURFACE
162 case CAIRO_SURFACE_TYPE_WIN32:
163 case CAIRO_SURFACE_TYPE_WIN32_PRINTING:
164 return true;
165 #endif
166 default:
167 return false;
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;
188 default:
189 return true;
193 static cairo_user_data_key_t surfaceDataKey;
195 static void ReleaseData(void* aData) {
196 DataSourceSurface* data = static_cast<DataSourceSurface*>(aData);
197 data->Unmap();
198 data->Release();
201 static cairo_surface_t* CopyToImageSurface(unsigned char* aData,
202 const IntRect& aRect,
203 int32_t aStride,
204 SurfaceFormat aFormat) {
205 MOZ_ASSERT(aData);
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);
218 return nullptr;
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);
233 return 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) {
243 return aSurface;
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);
247 #endif
250 return nullptr;
253 static cairo_surface_t* CreateSubImageForData(unsigned char* aData,
254 const IntRect& aRect, int aStride,
255 SurfaceFormat aFormat) {
256 if (!aData) {
257 gfxWarning() << "DrawTargetCairo.CreateSubImageForData null aData";
258 return nullptr;
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(),
265 aStride);
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());
269 return image;
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);
284 if (image) {
285 image =
286 CreateSubImageForData(cairo_image_surface_get_data(image), aSubImage,
287 cairo_image_surface_get_stride(image), aFormat);
288 return image;
291 cairo_surface_t* similar = cairo_surface_create_similar(
292 aSurface, cairo_surface_get_content(aSurface), aSubImage.Width(),
293 aSubImage.Height());
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());
298 cairo_paint(ctx);
299 cairo_destroy(ctx);
301 cairo_surface_set_device_offset(similar, -aSubImage.X(), -aSubImage.Y());
302 return similar;
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()) {
315 if (!aSurface) {
316 return nullptr;
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);
331 } else {
332 surf = ExtractSubImage(surf, subimage, aSurface->GetFormat());
334 return surf;
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);
342 } else {
343 surf = ExtractSubImage(surf, subimage, aSurface->GetFormat());
345 return surf;
348 if (aExistingOnly) {
349 return nullptr;
352 RefPtr<DataSourceSurface> data = aSurface->GetDataSurface();
353 if (!data) {
354 return nullptr;
357 DataSourceSurface::MappedSurface map;
358 if (!data->Map(DataSourceSurface::READ, &map)) {
359 return nullptr;
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
374 // data.
375 cairo_surface_t* result = CopyToImageSurface(
376 map.mData, subimage, map.mStride, data->GetFormat());
377 data->Unmap();
378 return result;
380 data->Unmap();
381 return nullptr;
384 cairo_surface_set_user_data(surf, &surfaceDataKey, data.forget().take(),
385 ReleaseData);
386 return surf;
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
391 // surface.
392 class AutoClearDeviceOffset final {
393 public:
394 explicit AutoClearDeviceOffset(SourceSurface* aSurface)
395 : mSurface(nullptr), mX(0), mY(0) {
396 Init(aSurface);
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() {
409 if (mSurface) {
410 cairo_surface_set_device_offset(mSurface, mX, mY);
414 private:
415 void Init(SourceSurface* aSurface) {
416 cairo_surface_t* surface = GetCairoSurfaceForSourceSurface(aSurface, true);
417 if (surface) {
418 Init(surface);
419 cairo_surface_destroy(surface);
423 void Init(cairo_surface_t* aSurface) {
424 mSurface = aSurface;
425 cairo_surface_get_device_offset(mSurface, &mX, &mY);
426 cairo_surface_set_device_offset(mSurface, 0, 0);
429 cairo_surface_t* mSurface;
430 double mX;
431 double mY;
434 static inline void CairoPatternAddGradientStop(cairo_pattern_t* aPattern,
435 const GradientStop& aStop,
436 Float aNudge = 0) {
437 cairo_pattern_add_color_stop_rgba(aPattern, aStop.offset + aNudge,
438 aStop.color.r, aStop.color.g, aStop.color.b,
439 aStop.color.a);
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
445 // caller.
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,
450 Float aAlpha,
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,
459 color.a * aAlpha);
460 break;
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);
480 break;
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]);
502 break;
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]);
525 break;
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);
531 break;
533 default: {
534 // We should support all pattern types!
535 MOZ_ASSERT(false);
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.
542 if (matrix) {
543 cairo_matrix_t mat;
544 GfxMatrixToCairoMatrix(*matrix, mat);
545 cairo_matrix_invert(&mat);
546 cairo_pattern_set_matrix(pat, &mat);
549 return pat;
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;
560 return true;
563 DrawTargetCairo::DrawTargetCairo()
564 : mContext(nullptr),
565 mSurface(nullptr),
566 mTransformSingular(false),
567 mLockedBits(nullptr),
568 mFontOptions(nullptr) {}
570 DrawTargetCairo::~DrawTargetCairo() {
571 cairo_destroy(mContext);
572 if (mSurface) {
573 cairo_surface_destroy(mSurface);
574 mSurface = nullptr;
576 if (mFontOptions) {
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 {
589 if (mContext) {
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!");
594 MOZ_ASSERT(
595 type == cairo_surface_get_type(cairo_tee_surface_index(mSurface, 1)),
596 "What should we do here?");
598 switch (type) {
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;
632 default:
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;
653 #endif
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.
660 return;
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;) {
675 --i;
676 if (dest[i] == '\'') {
677 dest.ReplaceLiteral(i, 1, "\\'");
678 } else if (dest[i] == '\\') {
679 #ifdef XP_MACOSX
680 dest.ReplaceLiteral(i, 1, "\\\\");
681 #else
682 dest.ReplaceLiteral(i, 1, "\\\\\\\\");
683 #endif
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);
695 } else {
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.
710 return;
713 nsAutoCString dest(aDestination);
714 for (size_t i = dest.Length(); i > 0;) {
715 --i;
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() {
730 if (!IsValid()) {
731 gfxCriticalNote << "DrawTargetCairo::Snapshot with bad surface "
732 << hexa(mSurface) << ", context " << hexa(mContext)
733 << ", status "
734 << (mSurface ? cairo_surface_status(mSurface) : -1);
735 return nullptr;
737 if (mSnapshot) {
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,
752 IntPoint* aOrigin) {
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);
758 if (imgsurf) {
759 surf = imgsurf;
762 #endif
763 if (cairo_surface_get_type(surf) == CAIRO_SURFACE_TYPE_IMAGE &&
764 cairo_surface_status(surf) == CAIRO_STATUS_SUCCESS) {
765 PointDouble offset;
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
768 // bounds rect
769 IntPoint origin(int32_t(-offset.x), int32_t(-offset.y));
770 if (-PointDouble(origin) != offset || (!aOrigin && origin != IntPoint())) {
771 return false;
774 WillChange();
775 Flush();
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));
783 if (aOrigin) {
784 *aOrigin = origin;
786 return true;
789 return false;
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);
799 if (imgsurf) {
800 cairo_surface_mark_dirty(imgsurf);
803 #endif
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 */) {
814 WillChange(aPath);
817 cairo_surface_t* DrawTargetCairo::GetDummySurface() {
818 if (mDummySurface) {
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);
834 } else {
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);
840 } else {
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,
847 const Rect& aSource,
848 const DrawSurfaceOptions& aSurfOptions,
849 const DrawOptions& aOptions) {
850 if (mTransformSingular || aDest.IsEmpty()) {
851 return;
854 if (!IsValid() || !aSurface) {
855 gfxCriticalNote << "DrawSurface with bad surface "
856 << cairo_surface_status(cairo_get_group_target(mContext));
857 return;
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);
872 if (!surf) {
873 gfxWarning()
874 << "Failed to create cairo surface for DrawTargetCairo::DrawSurface";
875 return;
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
892 ? CAIRO_EXTEND_NONE
893 : CAIRO_EXTEND_PAD);
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());
905 if (needsGroup) {
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);
912 } else {
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,
932 const Point& aDest,
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));
938 return;
941 if (aSurface->GetType() != SurfaceType::CAIRO) {
942 return;
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);
960 } else {
961 blursurf = sourcesurf;
962 surf = 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));
973 WillChange();
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);
982 if (needsGroup) {
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);
998 if (needsGroup) {
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)) {
1012 return;
1015 AutoClearDeviceOffset clear(aPattern);
1017 cairo_pattern_t* pat =
1018 GfxPatternToCairoPattern(aPattern, aOptions.mAlpha, GetTransform());
1019 if (!pat) {
1020 return;
1022 if (cairo_pattern_status(pat)) {
1023 cairo_pattern_destroy(pat);
1024 gfxWarning() << "Invalid pattern";
1025 return;
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);
1043 } else {
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);
1051 } else {
1052 cairo_set_operator(mContext, GfxOpToCairoOp(aOptions.mCompositionOp));
1054 if (aDrawType == DRAW_STROKE) {
1055 SetCairoStrokeOptions(mContext, aStrokeOptions);
1056 cairo_stroke_preserve(mContext);
1057 } else {
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) {
1068 return;
1071 AutoPrepareForDrawing prep(this, mContext);
1073 bool restoreTransform = false;
1074 Matrix mat;
1075 Rect r = aRect;
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";
1092 return;
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) {
1111 SetTransform(mat);
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);
1120 return;
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(),
1133 aSource.Height());
1134 cairo_fill(mContext);
1137 void DrawTargetCairo::CopySurface(SourceSurface* aSurface,
1138 const IntRect& aSource,
1139 const IntPoint& aDest) {
1140 if (mTransformSingular) {
1141 return;
1144 AutoPrepareForDrawing prep(this, mContext);
1145 AutoClearDeviceOffset clear(aSurface);
1147 if (!aSurface) {
1148 gfxWarning() << "Unsupported surface type specified";
1149 return;
1152 cairo_surface_t* surf = GetCairoSurfaceForSourceSurface(aSurface);
1153 if (!surf) {
1154 gfxWarning() << "Unsupported surface type specified";
1155 return;
1158 CopySurfaceInternal(surf, aSource, aDest);
1159 cairo_surface_destroy(surf);
1162 void DrawTargetCairo::CopyRect(const IntRect& aSource, const IntPoint& aDest) {
1163 if (mTransformSingular) {
1164 return;
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(),
1175 aSource.Height());
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());
1179 cairo_paint(ctx);
1180 cairo_destroy(ctx);
1182 source.MoveTo(0, 0);
1183 surf = similar;
1186 CopySurfaceInternal(surf, source, aDest);
1188 if (surf != mSurface) {
1189 cairo_surface_destroy(surf);
1193 void DrawTargetCairo::ClearRect(const Rect& aRect) {
1194 if (mTransformSingular) {
1195 return;
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(),
1212 aRect.Height());
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) {
1221 return;
1224 AutoPrepareForDrawing prep(this, mContext);
1226 cairo_new_path(mContext);
1227 cairo_rectangle(mContext, aRect.X(), aRect.Y(), aRect.Width(),
1228 aRect.Height());
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) {
1238 return;
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) {
1255 return;
1258 AutoPrepareForDrawing prep(this, mContext, aPath);
1260 if (aPath->GetBackendType() != BackendType::CAIRO) return;
1262 PathCairo* path =
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) {
1272 return;
1275 AutoPrepareForDrawing prep(this, mContext, aPath);
1277 if (aPath->GetBackendType() != BackendType::CAIRO) return;
1279 PathCairo* path =
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);
1289 if (!surf) {
1290 return false;
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
1303 // system Cairo.
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) {
1313 return;
1316 if (!mFontOptions) {
1317 mFontOptions = cairo_font_options_create();
1318 if (!mFontOptions) {
1319 gfxWarning() << "Failed allocating Cairo font options";
1320 return;
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) {
1330 return;
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:
1356 return false;
1357 default:
1358 return true;
1362 void DrawTargetCairo::FillGlyphs(ScaledFont* aFont, const GlyphBuffer& aBuffer,
1363 const Pattern& aPattern,
1364 const DrawOptions& aOptions) {
1365 if (mTransformSingular) {
1366 return;
1369 if (!IsValid()) {
1370 gfxDebug() << "FillGlyphs bad surface "
1371 << cairo_surface_status(cairo_get_group_target(mContext));
1372 return;
1375 cairo_scaled_font_t* cairoScaledFont =
1376 aFont ? aFont->GetCairoScaledFont() : nullptr;
1377 if (!cairoScaledFont) {
1378 gfxDevCrash(LogReason::InvalidFont) << "Invalid scaled font";
1379 return;
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());
1389 if (!pat) return;
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.
1398 SetFontOptions(aa);
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";
1409 return;
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);
1424 } else {
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) {
1437 return;
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());
1449 if (!source) {
1450 return;
1453 cairo_pattern_t* mask =
1454 GfxPatternToCairoPattern(aMask, aOptions.mAlpha, GetTransform());
1455 if (!mask) {
1456 cairo_pattern_destroy(source);
1457 return;
1460 if (cairo_pattern_status(source) || cairo_pattern_status(mask)) {
1461 cairo_pattern_destroy(source);
1462 cairo_pattern_destroy(mask);
1463 gfxWarning() << "Invalid pattern";
1464 return;
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) {
1478 return;
1481 AutoPrepareForDrawing prep(this, mContext);
1482 AutoClearDeviceOffset clearSource(aSource);
1483 AutoClearDeviceOffset clearMask(aMask);
1485 if (!PatternIsCompatible(aSource)) {
1486 return;
1489 cairo_set_antialias(mContext,
1490 GfxAntialiasToCairoAntialias(aOptions.mAntialiasMode));
1492 cairo_pattern_t* pat =
1493 GfxPatternToCairoPattern(aSource, aOptions.mAlpha, GetTransform());
1494 if (!pat) {
1495 return;
1498 if (cairo_pattern_status(pat)) {
1499 cairo_pattern_destroy(pat);
1500 gfxWarning() << "Invalid pattern";
1501 return;
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);
1519 if (!surf) {
1520 cairo_pattern_destroy(pat);
1521 return;
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) {
1541 return;
1544 WillChange(aPath);
1545 cairo_save(mContext);
1547 PathCairo* path =
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);
1553 } else {
1554 path->SetPathOnContext(mContext);
1556 cairo_clip_preserve(mContext);
1559 void DrawTargetCairo::PushClipRect(const Rect& aRect) {
1560 WillChange();
1561 cairo_save(mContext);
1563 cairo_new_path(mContext);
1564 if (mTransformSingular) {
1565 cairo_rectangle(mContext, 0, 0, 0, 0);
1566 } else {
1567 cairo_rectangle(mContext, aRect.X(), aRect.Y(), aRect.Width(),
1568 aRect.Height());
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
1578 cairo_matrix_t mat;
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);
1615 cairo_paint(ctx);
1616 cairo_destroy(ctx);
1617 } else {
1618 cairo_push_group_with_content(mContext, content);
1621 PushedLayer layer(aOpacity, aCompositionOp, mPermitSubpixelAA);
1623 if (aMask) {
1624 cairo_surface_t* surf = GetCairoSurfaceForSourceSurface(aMask);
1625 if (surf) {
1626 layer.mMaskPattern = cairo_pattern_create_for_surface(surf);
1627 Matrix maskTransform = aMaskTransform;
1628 maskTransform.PreTranslate(aMask->GetRect().X(), aMask->GetRect().Y());
1629 cairo_matrix_t mat;
1630 GfxMatrixToCairoMatrix(maskTransform, mat);
1631 cairo_matrix_invert(&mat);
1632 cairo_pattern_set_matrix(layer.mMaskPattern, &mat);
1633 cairo_surface_destroy(surf);
1634 } else {
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);
1657 } else {
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);
1670 cairo_matrix_t mat;
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
1686 // region.
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 {
1702 if (!aData) {
1703 gfxWarning() << "DrawTargetCairo::CreateSourceSurfaceFromData null aData";
1704 return nullptr;
1707 cairo_surface_t* surf =
1708 CopyToImageSurface(aData, IntRect(IntPoint(), aSize), aStride, aFormat);
1709 if (!surf) {
1710 return nullptr;
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 {
1729 return nullptr;
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);
1747 break;
1748 #endif
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,
1754 aSize.height);
1755 break;
1757 [[fallthrough]];
1758 #endif
1759 default:
1760 similar = cairo_surface_create_similar(mSurface,
1761 GfxFormatToCairoContent(aFormat),
1762 aSize.width, aSize.height);
1763 break;
1766 if (!cairo_surface_status(similar)) {
1767 RefPtr<DrawTargetCairo> target = new DrawTargetCairo();
1768 if (target->InitAlreadyReferenced(similar, aSize)) {
1769 return target.forget();
1773 gfxCriticalError(
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 "
1778 << (int)aFormat;
1779 cairo_surface_destroy(similar);
1781 return nullptr;
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(),
1793 aBounds.Height());
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);
1801 if (dt) {
1802 result = gfx::Factory::CreateOffsetDrawTarget(
1803 dt, IntPoint(clipBounds.x, clipBounds.y));
1804 if (result) {
1805 result->SetTransform(mTransform);
1808 } else {
1809 // Everything is clipped but we still want some kind of surface
1810 result = CreateSimilarDrawTarget(IntSize(1, 1), aFormat);
1813 cairo_restore(mContext);
1814 return result;
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. "
1821 << aSize
1822 << " Cairo Status: " << cairo_surface_status(aSurface);
1823 cairo_surface_destroy(aSurface);
1824 return false;
1827 mContext = cairo_create(aSurface);
1828 mSurface = aSurface;
1829 mSize = aSize;
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);
1842 } else {
1843 SetPermitSubpixelAA(true);
1846 return 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,
1853 aSize.height);
1855 if (cairo_surface_status(similar)) {
1856 return nullptr;
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();
1865 } else {
1866 return nullptr;
1870 cairo_surface_t* blursurf =
1871 cairo_image_surface_create(CAIRO_FORMAT_A8, aSize.width, aSize.height);
1873 if (cairo_surface_status(blursurf)) {
1874 return nullptr;
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);
1881 return nullptr;
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();
1891 return nullptr;
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,
1915 aStride);
1916 return InitAlreadyReferenced(surf, aSize);
1919 void* DrawTargetCairo::GetNativeSurface(NativeSurfaceType aType) {
1920 if (aType == NativeSurfaceType::CAIRO_CONTEXT) {
1921 return mContext;
1924 return nullptr;
1927 void DrawTargetCairo::MarkSnapshotIndependent() {
1928 if (mSnapshot) {
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) {
1947 cairo_matrix_t mat;
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
1960 #ifdef MOZ_X11
1961 bool BorrowedXlibDrawable::Init(DrawTarget* aDT) {
1962 MOZ_ASSERT(aDT, "Caller should check for nullptr");
1963 MOZ_ASSERT(!mDT, "Can't initialize twice!");
1964 mDT = aDT;
1965 mDrawable = X11None;
1967 # ifdef CAIRO_HAS_XLIB_SURFACE
1968 if (aDT->GetBackendType() != BackendType::CAIRO || aDT->IsTiledDrawTarget()) {
1969 return false;
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) {
1975 return false;
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);
1992 return true;
1993 # else
1994 return false;
1995 # endif
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);
2002 if (mDrawable) {
2003 mDrawable = X11None;
2006 #endif
2008 } // namespace gfx
2009 } // namespace mozilla