1 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "nsIMemoryReporter.h"
8 #include "mozilla/ArrayUtils.h"
9 #include "mozilla/Base64.h"
10 #include "mozilla/CheckedInt.h"
11 #include "mozilla/Attributes.h"
12 #include "mozilla/MemoryReporting.h"
13 #include "nsISupportsImpl.h"
14 #include "mozilla/gfx/2D.h"
15 #include "gfx2DGlue.h"
17 #include "gfxASurface.h"
18 #include "gfxContext.h"
19 #include "gfxImageSurface.h"
20 #include "gfxPlatform.h"
26 #ifdef CAIRO_HAS_WIN32_SURFACE
27 #include "gfxWindowsSurface.h"
29 #ifdef CAIRO_HAS_D2D_SURFACE
30 #include "gfxD2DSurface.h"
34 #include "gfxXlibSurface.h"
37 #ifdef CAIRO_HAS_QUARTZ_SURFACE
38 #include "gfxQuartzSurface.h"
39 #include "gfxQuartzImageSurface.h"
42 #if defined(CAIRO_HAS_QT_SURFACE) && defined(MOZ_WIDGET_QT)
43 #include "gfxQPainterSurface.h"
49 #include "imgIEncoder.h"
50 #include "nsComponentManagerUtils.h"
51 #include "nsISupportsUtils.h"
53 #include "nsServiceManagerUtils.h"
55 #include "nsIClipboardHelper.h"
57 using namespace mozilla
;
58 using namespace mozilla::gfx
;
60 static cairo_user_data_key_t gfxasurface_pointer_key
;
62 gfxASurface::gfxASurface()
63 : mSurface(nullptr), mFloatingRefs(0), mBytesRecorded(0),
64 mSurfaceValid(false), mAllowUseAsSource(true)
66 MOZ_COUNT_CTOR(gfxASurface
);
69 gfxASurface::~gfxASurface()
73 MOZ_COUNT_DTOR(gfxASurface
);
76 // Surfaces use refcounting that's tied to the cairo surface refcnt, to avoid
77 // refcount mismatch issues.
79 gfxASurface::AddRef(void)
86 cairo_surface_reference(mSurface
);
89 return (nsrefcnt
) cairo_surface_get_reference_count(mSurface
);
91 // the surface isn't valid, but we still need to refcount
93 return ++mFloatingRefs
;
98 gfxASurface::Release(void)
101 NS_ASSERTION(mFloatingRefs
== 0, "gfxASurface::Release with floating refs still hanging around!");
103 // Note that there is a destructor set on user data for mSurface,
104 // which will delete this gfxASurface wrapper when the surface's refcount goes
106 nsrefcnt refcnt
= (nsrefcnt
) cairo_surface_get_reference_count(mSurface
);
107 cairo_surface_destroy(mSurface
);
109 // |this| may not be valid any more, don't use it!
113 if (--mFloatingRefs
== 0) {
118 return mFloatingRefs
;
123 gfxASurface::AddRefExternal(void)
129 gfxASurface::ReleaseExternal(void)
135 gfxASurface::SurfaceDestroyFunc(void *data
) {
136 gfxASurface
*surf
= (gfxASurface
*) data
;
137 // fprintf (stderr, "Deleting wrapper for %p (wrapper: %p)\n", surf->mSurface, data);
142 gfxASurface::GetSurfaceWrapper(cairo_surface_t
*csurf
)
146 return (gfxASurface
*) cairo_surface_get_user_data(csurf
, &gfxasurface_pointer_key
);
150 gfxASurface::SetSurfaceWrapper(cairo_surface_t
*csurf
, gfxASurface
*asurf
)
154 cairo_surface_set_user_data(csurf
, &gfxasurface_pointer_key
, asurf
, SurfaceDestroyFunc
);
157 already_AddRefed
<gfxASurface
>
158 gfxASurface::Wrap (cairo_surface_t
*csurf
, const gfxIntSize
& aSize
)
160 nsRefPtr
<gfxASurface
> result
;
162 /* Do we already have a wrapper for this surface? */
163 result
= GetSurfaceWrapper(csurf
);
165 // fprintf(stderr, "Existing wrapper for %p -> %p\n", csurf, result);
166 return result
.forget();
169 /* No wrapper; figure out the surface type and create it */
170 cairo_surface_type_t stype
= cairo_surface_get_type(csurf
);
172 if (stype
== CAIRO_SURFACE_TYPE_IMAGE
) {
173 result
= new gfxImageSurface(csurf
);
175 #ifdef CAIRO_HAS_WIN32_SURFACE
176 else if (stype
== CAIRO_SURFACE_TYPE_WIN32
||
177 stype
== CAIRO_SURFACE_TYPE_WIN32_PRINTING
) {
178 result
= new gfxWindowsSurface(csurf
);
181 #ifdef CAIRO_HAS_D2D_SURFACE
182 else if (stype
== CAIRO_SURFACE_TYPE_D2D
) {
183 result
= new gfxD2DSurface(csurf
);
187 else if (stype
== CAIRO_SURFACE_TYPE_XLIB
) {
188 result
= new gfxXlibSurface(csurf
);
191 #ifdef CAIRO_HAS_QUARTZ_SURFACE
192 else if (stype
== CAIRO_SURFACE_TYPE_QUARTZ
) {
193 result
= new gfxQuartzSurface(csurf
, aSize
);
195 else if (stype
== CAIRO_SURFACE_TYPE_QUARTZ_IMAGE
) {
196 result
= new gfxQuartzImageSurface(csurf
);
199 #if defined(CAIRO_HAS_QT_SURFACE) && defined(MOZ_WIDGET_QT)
200 else if (stype
== CAIRO_SURFACE_TYPE_QT
) {
201 result
= new gfxQPainterSurface(csurf
);
205 result
= new gfxUnknownSurface(csurf
, aSize
);
208 // fprintf(stderr, "New wrapper for %p -> %p\n", csurf, result);
210 return result
.forget();
214 gfxASurface::Init(cairo_surface_t
* surface
, bool existingSurface
)
216 SetSurfaceWrapper(surface
, this);
219 mSurfaceValid
= surface
&& !cairo_surface_status(surface
);
221 if (existingSurface
|| !mSurfaceValid
) {
225 #ifdef MOZ_TREE_CAIRO
226 if (cairo_surface_get_content(surface
) != CAIRO_CONTENT_COLOR
) {
227 cairo_surface_set_subpixel_antialiasing(surface
, CAIRO_SUBPIXEL_ANTIALIASING_DISABLED
);
234 gfxASurface::GetType() const
237 return (gfxSurfaceType
)-1;
239 return (gfxSurfaceType
)cairo_surface_get_type(mSurface
);
243 gfxASurface::GetContentType() const
246 return (gfxContentType
)-1;
248 return (gfxContentType
)cairo_surface_get_content(mSurface
);
252 gfxASurface::SetDeviceOffset(const gfxPoint
& offset
)
256 cairo_surface_set_device_offset(mSurface
,
261 gfxASurface::GetDeviceOffset() const
264 return gfxPoint(0.0, 0.0);
266 cairo_surface_get_device_offset(mSurface
, &pt
.x
, &pt
.y
);
271 gfxASurface::Flush() const
275 cairo_surface_flush(mSurface
);
276 gfxPlatform::ClearSourceSurfaceForSurface(const_cast<gfxASurface
*>(this));
280 gfxASurface::MarkDirty()
284 cairo_surface_mark_dirty(mSurface
);
285 gfxPlatform::ClearSourceSurfaceForSurface(this);
289 gfxASurface::MarkDirty(const gfxRect
& r
)
293 cairo_surface_mark_dirty_rectangle(mSurface
,
294 (int) r
.X(), (int) r
.Y(),
295 (int) r
.Width(), (int) r
.Height());
296 gfxPlatform::ClearSourceSurfaceForSurface(this);
300 gfxASurface::SetData(const cairo_user_data_key_t
*key
,
302 thebes_destroy_func_t destroy
)
306 cairo_surface_set_user_data(mSurface
, key
, user_data
, destroy
);
310 gfxASurface::GetData(const cairo_user_data_key_t
*key
)
314 return cairo_surface_get_user_data(mSurface
, key
);
318 gfxASurface::Finish()
320 // null surfaces are allowed here
321 cairo_surface_finish(mSurface
);
324 already_AddRefed
<gfxASurface
>
325 gfxASurface::CreateSimilarSurface(gfxContentType aContent
,
326 const nsIntSize
& aSize
)
328 if (!mSurface
|| !mSurfaceValid
) {
332 cairo_surface_t
*surface
=
333 cairo_surface_create_similar(mSurface
, cairo_content_t(int(aContent
)),
334 aSize
.width
, aSize
.height
);
335 if (cairo_surface_status(surface
)) {
336 cairo_surface_destroy(surface
);
340 nsRefPtr
<gfxASurface
> result
= Wrap(surface
, aSize
);
341 cairo_surface_destroy(surface
);
342 return result
.forget();
345 already_AddRefed
<gfxImageSurface
>
346 gfxASurface::GetAsReadableARGB32ImageSurface()
348 nsRefPtr
<gfxImageSurface
> imgSurface
= GetAsImageSurface();
349 if (!imgSurface
|| imgSurface
->Format() != gfxImageFormat::ARGB32
) {
350 imgSurface
= CopyToARGB32ImageSurface();
352 return imgSurface
.forget();
355 already_AddRefed
<gfxImageSurface
>
356 gfxASurface::CopyToARGB32ImageSurface()
358 if (!mSurface
|| !mSurfaceValid
) {
362 const nsIntSize size
= GetSize();
363 nsRefPtr
<gfxImageSurface
> imgSurface
=
364 new gfxImageSurface(size
, gfxImageFormat::ARGB32
);
366 RefPtr
<DrawTarget
> dt
= gfxPlatform::GetPlatform()->CreateDrawTargetForSurface(imgSurface
, IntSize(size
.width
, size
.height
));
367 RefPtr
<SourceSurface
> source
= gfxPlatform::GetPlatform()->GetSourceSurfaceForSurface(dt
, this);
369 dt
->CopySurface(source
, IntRect(0, 0, size
.width
, size
.height
), IntPoint());
371 return imgSurface
.forget();
375 gfxASurface::CairoStatus()
380 return cairo_surface_status(mSurface
);
385 gfxASurface::CheckSurfaceSize(const nsIntSize
& sz
, int32_t limit
)
387 if (sz
.width
< 0 || sz
.height
< 0) {
388 NS_WARNING("Surface width or height < 0!");
392 // reject images with sides bigger than limit
393 if (limit
&& (sz
.width
> limit
|| sz
.height
> limit
)) {
394 NS_WARNING("Surface size too large (exceeds caller's limit)!");
398 #if defined(XP_MACOSX)
399 // CoreGraphics is limited to images < 32K in *height*,
400 // so clamp all surfaces on the Mac to that height
401 if (sz
.height
> SHRT_MAX
) {
402 NS_WARNING("Surface size too large (exceeds CoreGraphics limit)!");
407 // make sure the surface area doesn't overflow a int32_t
408 CheckedInt
<int32_t> tmp
= sz
.width
;
410 if (!tmp
.isValid()) {
411 NS_WARNING("Surface size too large (would overflow)!");
415 // assuming 4-byte stride, make sure the allocation size
416 // doesn't overflow a int32_t either
418 if (!tmp
.isValid()) {
419 NS_WARNING("Allocation too large (would overflow)!");
428 gfxASurface::FormatStrideForWidth(gfxImageFormat format
, int32_t width
)
430 return cairo_format_stride_for_width((cairo_format_t
)(int)format
, (int)width
);
434 gfxASurface::BeginPrinting(const nsAString
& aTitle
, const nsAString
& aPrintToFileName
)
440 gfxASurface::EndPrinting()
446 gfxASurface::AbortPrinting()
452 gfxASurface::BeginPage()
458 gfxASurface::EndPage()
464 gfxASurface::ContentFromFormat(gfxImageFormat format
)
467 case gfxImageFormat::ARGB32
:
468 return gfxContentType::COLOR_ALPHA
;
469 case gfxImageFormat::RGB24
:
470 case gfxImageFormat::RGB16_565
:
471 return gfxContentType::COLOR
;
472 case gfxImageFormat::A8
:
473 case gfxImageFormat::A1
:
474 return gfxContentType::ALPHA
;
476 case gfxImageFormat::Unknown
:
478 return gfxContentType::COLOR
;
483 gfxASurface::SetSubpixelAntialiasingEnabled(bool aEnabled
)
485 #ifdef MOZ_TREE_CAIRO
488 cairo_surface_set_subpixel_antialiasing(mSurface
,
489 aEnabled
? CAIRO_SUBPIXEL_ANTIALIASING_ENABLED
: CAIRO_SUBPIXEL_ANTIALIASING_DISABLED
);
494 gfxASurface::GetSubpixelAntialiasingEnabled()
498 #ifdef MOZ_TREE_CAIRO
499 return cairo_surface_get_subpixel_antialiasing(mSurface
) == CAIRO_SUBPIXEL_ANTIALIASING_ENABLED
;
506 gfxASurface::GetMemoryLocation() const
508 return gfxMemoryLocation::IN_PROCESS_HEAP
;
512 gfxASurface::BytePerPixelFromFormat(gfxImageFormat format
)
515 case gfxImageFormat::ARGB32
:
516 case gfxImageFormat::RGB24
:
518 case gfxImageFormat::RGB16_565
:
520 case gfxImageFormat::A8
:
523 NS_WARNING("Unknown byte per pixel value for Image format");
528 /** Memory reporting **/
530 static const char *sDefaultSurfaceDescription
=
531 "Memory used by gfx surface of the given type.";
533 struct SurfaceMemoryReporterAttrs
{
535 const char *description
;
538 static const SurfaceMemoryReporterAttrs sSurfaceMemoryReporterAttrs
[] = {
539 {"gfx-surface-image", nullptr},
540 {"gfx-surface-pdf", nullptr},
541 {"gfx-surface-ps", nullptr},
543 "Memory used by xlib surfaces to store pixmaps. This memory lives in "
544 "the X server's process rather than in this application, so the bytes "
545 "accounted for here aren't counted in vsize, resident, explicit, or any of "
546 "the other measurements on this page."},
547 {"gfx-surface-xcb", nullptr},
548 {"gfx-surface-glitz???", nullptr}, // should never be used
549 {"gfx-surface-quartz", nullptr},
550 {"gfx-surface-win32", nullptr},
551 {"gfx-surface-beos", nullptr},
552 {"gfx-surface-directfb???", nullptr}, // should never be used
553 {"gfx-surface-svg", nullptr},
554 {"gfx-surface-os2", nullptr},
555 {"gfx-surface-win32printing", nullptr},
556 {"gfx-surface-quartzimage", nullptr},
557 {"gfx-surface-script", nullptr},
558 {"gfx-surface-qpainter", nullptr},
559 {"gfx-surface-recording", nullptr},
560 {"gfx-surface-vg", nullptr},
561 {"gfx-surface-gl", nullptr},
562 {"gfx-surface-drm", nullptr},
563 {"gfx-surface-tee", nullptr},
564 {"gfx-surface-xml", nullptr},
565 {"gfx-surface-skia", nullptr},
566 {"gfx-surface-subsurface", nullptr},
567 {"gfx-surface-d2d", nullptr},
570 PR_STATIC_ASSERT(MOZ_ARRAY_LENGTH(sSurfaceMemoryReporterAttrs
) ==
571 size_t(gfxSurfaceType::Max
));
572 #ifdef CAIRO_HAS_D2D_SURFACE
573 PR_STATIC_ASSERT(uint32_t(CAIRO_SURFACE_TYPE_D2D
) ==
574 uint32_t(gfxSurfaceType::D2D
));
576 PR_STATIC_ASSERT(uint32_t(CAIRO_SURFACE_TYPE_SKIA
) ==
577 uint32_t(gfxSurfaceType::Skia
));
579 /* Surface size memory reporting */
581 static int64_t gSurfaceMemoryUsed
[size_t(gfxSurfaceType::Max
)] = { 0 };
583 class SurfaceMemoryReporter MOZ_FINAL
: public nsIMemoryReporter
585 ~SurfaceMemoryReporter() {}
590 NS_IMETHOD
CollectReports(nsIMemoryReporterCallback
*aCb
,
591 nsISupports
*aClosure
, bool aAnonymize
)
593 const size_t len
= ArrayLength(sSurfaceMemoryReporterAttrs
);
594 for (size_t i
= 0; i
< len
; i
++) {
595 int64_t amount
= gSurfaceMemoryUsed
[i
];
598 const char *path
= sSurfaceMemoryReporterAttrs
[i
].path
;
599 const char *desc
= sSurfaceMemoryReporterAttrs
[i
].description
;
601 desc
= sDefaultSurfaceDescription
;
604 nsresult rv
= aCb
->Callback(EmptyCString(), nsCString(path
),
605 KIND_OTHER
, UNITS_BYTES
,
606 gSurfaceMemoryUsed
[i
],
607 nsCString(desc
), aClosure
);
608 NS_ENSURE_SUCCESS(rv
, rv
);
616 NS_IMPL_ISUPPORTS(SurfaceMemoryReporter
, nsIMemoryReporter
)
619 gfxASurface::RecordMemoryUsedForSurfaceType(gfxSurfaceType aType
,
622 if (int(aType
) < 0 || aType
>= gfxSurfaceType::Max
) {
623 NS_WARNING("Invalid type to RecordMemoryUsedForSurfaceType!");
627 static bool registered
= false;
629 RegisterStrongMemoryReporter(new SurfaceMemoryReporter());
633 gSurfaceMemoryUsed
[size_t(aType
)] += aBytes
;
637 gfxASurface::RecordMemoryUsed(int32_t aBytes
)
639 RecordMemoryUsedForSurfaceType(GetType(), aBytes
);
640 mBytesRecorded
+= aBytes
;
644 gfxASurface::RecordMemoryFreed()
646 if (mBytesRecorded
) {
647 RecordMemoryUsedForSurfaceType(GetType(), -mBytesRecorded
);
653 gfxASurface::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf
) const
655 // We don't measure mSurface because cairo doesn't allow it.
660 gfxASurface::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf
) const
662 return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf
);
666 gfxASurface::BytesPerPixel(gfxImageFormat aImageFormat
)
668 switch (aImageFormat
) {
669 case gfxImageFormat::ARGB32
:
671 case gfxImageFormat::RGB24
:
673 case gfxImageFormat::RGB16_565
:
675 case gfxImageFormat::A8
:
677 case gfxImageFormat::A1
:
678 return 1; // Close enough
679 case gfxImageFormat::Unknown
:
681 NS_NOTREACHED("Not really sure what you want me to say here");
687 gfxASurface::SetOpaqueRect(const gfxRect
& aRect
)
689 if (aRect
.IsEmpty()) {
690 mOpaqueRect
= nullptr;
691 } else if (!!mOpaqueRect
) {
692 *mOpaqueRect
= aRect
;
694 mOpaqueRect
= MakeUnique
<gfxRect
>(aRect
);
698 /* static */const gfxRect
&
699 gfxASurface::GetEmptyOpaqueRect()
701 static const gfxRect
empty(0, 0, 0, 0);
706 gfxASurface::GetSize() const
708 return nsIntSize(-1, -1);
711 already_AddRefed
<gfxImageSurface
>
712 gfxASurface::GetAsImageSurface()