Bumping manifests a=b2g-bump
[gecko.git] / gfx / thebes / gfxASurface.cpp
bloba2c879173c9799144c4fd51dac9ca99d516b307a
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"
7 #include "nsMemory.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"
21 #include "gfxRect.h"
23 #include "cairo.h"
24 #include <algorithm>
26 #ifdef CAIRO_HAS_WIN32_SURFACE
27 #include "gfxWindowsSurface.h"
28 #endif
29 #ifdef CAIRO_HAS_D2D_SURFACE
30 #include "gfxD2DSurface.h"
31 #endif
33 #ifdef MOZ_X11
34 #include "gfxXlibSurface.h"
35 #endif
37 #ifdef CAIRO_HAS_QUARTZ_SURFACE
38 #include "gfxQuartzSurface.h"
39 #include "gfxQuartzImageSurface.h"
40 #endif
42 #if defined(CAIRO_HAS_QT_SURFACE) && defined(MOZ_WIDGET_QT)
43 #include "gfxQPainterSurface.h"
44 #endif
46 #include <stdio.h>
47 #include <limits.h>
49 #include "imgIEncoder.h"
50 #include "nsComponentManagerUtils.h"
51 #include "nsISupportsUtils.h"
52 #include "nsCOMPtr.h"
53 #include "nsServiceManagerUtils.h"
54 #include "nsString.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()
71 RecordMemoryFreed();
73 MOZ_COUNT_DTOR(gfxASurface);
76 // Surfaces use refcounting that's tied to the cairo surface refcnt, to avoid
77 // refcount mismatch issues.
78 nsrefcnt
79 gfxASurface::AddRef(void)
81 if (mSurfaceValid) {
82 if (mFloatingRefs) {
83 // eat a floating ref
84 mFloatingRefs--;
85 } else {
86 cairo_surface_reference(mSurface);
89 return (nsrefcnt) cairo_surface_get_reference_count(mSurface);
90 } else {
91 // the surface isn't valid, but we still need to refcount
92 // the gfxASurface
93 return ++mFloatingRefs;
97 nsrefcnt
98 gfxASurface::Release(void)
100 if (mSurfaceValid) {
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
105 // out of scope.
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!
111 return --refcnt;
112 } else {
113 if (--mFloatingRefs == 0) {
114 delete this;
115 return 0;
118 return mFloatingRefs;
122 nsrefcnt
123 gfxASurface::AddRefExternal(void)
125 return AddRef();
128 nsrefcnt
129 gfxASurface::ReleaseExternal(void)
131 return Release();
134 void
135 gfxASurface::SurfaceDestroyFunc(void *data) {
136 gfxASurface *surf = (gfxASurface*) data;
137 // fprintf (stderr, "Deleting wrapper for %p (wrapper: %p)\n", surf->mSurface, data);
138 delete surf;
141 gfxASurface*
142 gfxASurface::GetSurfaceWrapper(cairo_surface_t *csurf)
144 if (!csurf)
145 return nullptr;
146 return (gfxASurface*) cairo_surface_get_user_data(csurf, &gfxasurface_pointer_key);
149 void
150 gfxASurface::SetSurfaceWrapper(cairo_surface_t *csurf, gfxASurface *asurf)
152 if (!csurf)
153 return;
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);
164 if (result) {
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);
180 #endif
181 #ifdef CAIRO_HAS_D2D_SURFACE
182 else if (stype == CAIRO_SURFACE_TYPE_D2D) {
183 result = new gfxD2DSurface(csurf);
185 #endif
186 #ifdef MOZ_X11
187 else if (stype == CAIRO_SURFACE_TYPE_XLIB) {
188 result = new gfxXlibSurface(csurf);
190 #endif
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);
198 #endif
199 #if defined(CAIRO_HAS_QT_SURFACE) && defined(MOZ_WIDGET_QT)
200 else if (stype == CAIRO_SURFACE_TYPE_QT) {
201 result = new gfxQPainterSurface(csurf);
203 #endif
204 else {
205 result = new gfxUnknownSurface(csurf, aSize);
208 // fprintf(stderr, "New wrapper for %p -> %p\n", csurf, result);
210 return result.forget();
213 void
214 gfxASurface::Init(cairo_surface_t* surface, bool existingSurface)
216 SetSurfaceWrapper(surface, this);
218 mSurface = surface;
219 mSurfaceValid = surface && !cairo_surface_status(surface);
221 if (existingSurface || !mSurfaceValid) {
222 mFloatingRefs = 0;
223 } else {
224 mFloatingRefs = 1;
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);
229 #endif
233 gfxSurfaceType
234 gfxASurface::GetType() const
236 if (!mSurfaceValid)
237 return (gfxSurfaceType)-1;
239 return (gfxSurfaceType)cairo_surface_get_type(mSurface);
242 gfxContentType
243 gfxASurface::GetContentType() const
245 if (!mSurfaceValid)
246 return (gfxContentType)-1;
248 return (gfxContentType)cairo_surface_get_content(mSurface);
251 void
252 gfxASurface::SetDeviceOffset(const gfxPoint& offset)
254 if (!mSurfaceValid)
255 return;
256 cairo_surface_set_device_offset(mSurface,
257 offset.x, offset.y);
260 gfxPoint
261 gfxASurface::GetDeviceOffset() const
263 if (!mSurfaceValid)
264 return gfxPoint(0.0, 0.0);
265 gfxPoint pt;
266 cairo_surface_get_device_offset(mSurface, &pt.x, &pt.y);
267 return pt;
270 void
271 gfxASurface::Flush() const
273 if (!mSurfaceValid)
274 return;
275 cairo_surface_flush(mSurface);
276 gfxPlatform::ClearSourceSurfaceForSurface(const_cast<gfxASurface*>(this));
279 void
280 gfxASurface::MarkDirty()
282 if (!mSurfaceValid)
283 return;
284 cairo_surface_mark_dirty(mSurface);
285 gfxPlatform::ClearSourceSurfaceForSurface(this);
288 void
289 gfxASurface::MarkDirty(const gfxRect& r)
291 if (!mSurfaceValid)
292 return;
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);
299 void
300 gfxASurface::SetData(const cairo_user_data_key_t *key,
301 void *user_data,
302 thebes_destroy_func_t destroy)
304 if (!mSurfaceValid)
305 return;
306 cairo_surface_set_user_data(mSurface, key, user_data, destroy);
309 void *
310 gfxASurface::GetData(const cairo_user_data_key_t *key)
312 if (!mSurfaceValid)
313 return nullptr;
314 return cairo_surface_get_user_data(mSurface, key);
317 void
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) {
329 return nullptr;
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);
337 return nullptr;
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) {
359 return nullptr;
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()
377 if (!mSurfaceValid)
378 return -1;
380 return cairo_surface_status(mSurface);
383 /* static */
384 bool
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!");
389 return false;
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)!");
395 return false;
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)!");
403 return false;
405 #endif
407 // make sure the surface area doesn't overflow a int32_t
408 CheckedInt<int32_t> tmp = sz.width;
409 tmp *= sz.height;
410 if (!tmp.isValid()) {
411 NS_WARNING("Surface size too large (would overflow)!");
412 return false;
415 // assuming 4-byte stride, make sure the allocation size
416 // doesn't overflow a int32_t either
417 tmp *= 4;
418 if (!tmp.isValid()) {
419 NS_WARNING("Allocation too large (would overflow)!");
420 return false;
423 return true;
426 /* static */
427 int32_t
428 gfxASurface::FormatStrideForWidth(gfxImageFormat format, int32_t width)
430 return cairo_format_stride_for_width((cairo_format_t)(int)format, (int)width);
433 nsresult
434 gfxASurface::BeginPrinting(const nsAString& aTitle, const nsAString& aPrintToFileName)
436 return NS_OK;
439 nsresult
440 gfxASurface::EndPrinting()
442 return NS_OK;
445 nsresult
446 gfxASurface::AbortPrinting()
448 return NS_OK;
451 nsresult
452 gfxASurface::BeginPage()
454 return NS_OK;
457 nsresult
458 gfxASurface::EndPage()
460 return NS_OK;
463 gfxContentType
464 gfxASurface::ContentFromFormat(gfxImageFormat format)
466 switch (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:
477 default:
478 return gfxContentType::COLOR;
482 void
483 gfxASurface::SetSubpixelAntialiasingEnabled(bool aEnabled)
485 #ifdef MOZ_TREE_CAIRO
486 if (!mSurfaceValid)
487 return;
488 cairo_surface_set_subpixel_antialiasing(mSurface,
489 aEnabled ? CAIRO_SUBPIXEL_ANTIALIASING_ENABLED : CAIRO_SUBPIXEL_ANTIALIASING_DISABLED);
490 #endif
493 bool
494 gfxASurface::GetSubpixelAntialiasingEnabled()
496 if (!mSurfaceValid)
497 return false;
498 #ifdef MOZ_TREE_CAIRO
499 return cairo_surface_get_subpixel_antialiasing(mSurface) == CAIRO_SUBPIXEL_ANTIALIASING_ENABLED;
500 #else
501 return true;
502 #endif
505 gfxMemoryLocation
506 gfxASurface::GetMemoryLocation() const
508 return gfxMemoryLocation::IN_PROCESS_HEAP;
511 int32_t
512 gfxASurface::BytePerPixelFromFormat(gfxImageFormat format)
514 switch (format) {
515 case gfxImageFormat::ARGB32:
516 case gfxImageFormat::RGB24:
517 return 4;
518 case gfxImageFormat::RGB16_565:
519 return 2;
520 case gfxImageFormat::A8:
521 return 1;
522 default:
523 NS_WARNING("Unknown byte per pixel value for Image format");
525 return 0;
528 /** Memory reporting **/
530 static const char *sDefaultSurfaceDescription =
531 "Memory used by gfx surface of the given type.";
533 struct SurfaceMemoryReporterAttrs {
534 const char *path;
535 const char *description;
538 static const SurfaceMemoryReporterAttrs sSurfaceMemoryReporterAttrs[] = {
539 {"gfx-surface-image", nullptr},
540 {"gfx-surface-pdf", nullptr},
541 {"gfx-surface-ps", nullptr},
542 {"gfx-surface-xlib",
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));
575 #endif
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() {}
587 public:
588 NS_DECL_ISUPPORTS
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];
597 if (amount != 0) {
598 const char *path = sSurfaceMemoryReporterAttrs[i].path;
599 const char *desc = sSurfaceMemoryReporterAttrs[i].description;
600 if (!desc) {
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);
612 return NS_OK;
616 NS_IMPL_ISUPPORTS(SurfaceMemoryReporter, nsIMemoryReporter)
618 void
619 gfxASurface::RecordMemoryUsedForSurfaceType(gfxSurfaceType aType,
620 int32_t aBytes)
622 if (int(aType) < 0 || aType >= gfxSurfaceType::Max) {
623 NS_WARNING("Invalid type to RecordMemoryUsedForSurfaceType!");
624 return;
627 static bool registered = false;
628 if (!registered) {
629 RegisterStrongMemoryReporter(new SurfaceMemoryReporter());
630 registered = true;
633 gSurfaceMemoryUsed[size_t(aType)] += aBytes;
636 void
637 gfxASurface::RecordMemoryUsed(int32_t aBytes)
639 RecordMemoryUsedForSurfaceType(GetType(), aBytes);
640 mBytesRecorded += aBytes;
643 void
644 gfxASurface::RecordMemoryFreed()
646 if (mBytesRecorded) {
647 RecordMemoryUsedForSurfaceType(GetType(), -mBytesRecorded);
648 mBytesRecorded = 0;
652 size_t
653 gfxASurface::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
655 // We don't measure mSurface because cairo doesn't allow it.
656 return 0;
659 size_t
660 gfxASurface::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
662 return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
665 /* static */ uint8_t
666 gfxASurface::BytesPerPixel(gfxImageFormat aImageFormat)
668 switch (aImageFormat) {
669 case gfxImageFormat::ARGB32:
670 return 4;
671 case gfxImageFormat::RGB24:
672 return 4;
673 case gfxImageFormat::RGB16_565:
674 return 2;
675 case gfxImageFormat::A8:
676 return 1;
677 case gfxImageFormat::A1:
678 return 1; // Close enough
679 case gfxImageFormat::Unknown:
680 default:
681 NS_NOTREACHED("Not really sure what you want me to say here");
682 return 0;
686 void
687 gfxASurface::SetOpaqueRect(const gfxRect& aRect)
689 if (aRect.IsEmpty()) {
690 mOpaqueRect = nullptr;
691 } else if (!!mOpaqueRect) {
692 *mOpaqueRect = aRect;
693 } else {
694 mOpaqueRect = MakeUnique<gfxRect>(aRect);
698 /* static */const gfxRect&
699 gfxASurface::GetEmptyOpaqueRect()
701 static const gfxRect empty(0, 0, 0, 0);
702 return empty;
705 const nsIntSize
706 gfxASurface::GetSize() const
708 return nsIntSize(-1, -1);
711 already_AddRefed<gfxImageSurface>
712 gfxASurface::GetAsImageSurface()
714 return nullptr;