Bug 1874684 - Part 28: Return DateDuration from DifferenceISODateTime. r=mgaudet
[gecko.git] / gfx / thebes / gfxASurface.cpp
blobf77c836fb95f189b5a0f213989ad2564ead341fa
1 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "nsIMemoryReporter.h"
7 #include "mozilla/ArrayUtils.h"
8 #include "mozilla/Base64.h"
9 #include "mozilla/CheckedInt.h"
10 #include "mozilla/Attributes.h"
11 #include "mozilla/MemoryReporting.h"
12 #include "nsISupportsImpl.h"
13 #include "mozilla/gfx/2D.h"
14 #include "mozilla/gfx/Logging.h"
15 #include "mozilla/gfx/HelpersCairo.h"
16 #include "gfx2DGlue.h"
18 #include "gfxASurface.h"
19 #include "gfxContext.h"
20 #include "gfxImageSurface.h"
21 #include "gfxPlatform.h"
22 #include "gfxRect.h"
24 #include "cairo.h"
25 #include <algorithm>
27 #ifdef CAIRO_HAS_WIN32_SURFACE
28 # include "gfxWindowsSurface.h"
29 #endif
31 #ifdef MOZ_X11
32 # include "gfxXlibSurface.h"
33 #endif
35 #ifdef CAIRO_HAS_QUARTZ_SURFACE
36 # include "gfxQuartzSurface.h"
37 #endif
39 #include <stdio.h>
40 #include <limits.h>
42 #include "nsComponentManagerUtils.h"
43 #include "nsISupportsUtils.h"
44 #include "nsCOMPtr.h"
45 #include "nsServiceManagerUtils.h"
46 #include "nsString.h"
48 using namespace mozilla;
49 using namespace mozilla::gfx;
51 static cairo_user_data_key_t gfxasurface_pointer_key;
53 gfxASurface::gfxASurface()
54 : mSurface(nullptr),
55 mFloatingRefs(0),
56 mBytesRecorded(0),
57 mSurfaceValid(false) {
58 MOZ_COUNT_CTOR(gfxASurface);
61 gfxASurface::~gfxASurface() {
62 RecordMemoryFreed();
64 MOZ_COUNT_DTOR(gfxASurface);
67 // Surfaces use refcounting that's tied to the cairo surface refcnt, to avoid
68 // refcount mismatch issues.
69 nsrefcnt gfxASurface::AddRef(void) {
70 if (mSurfaceValid) {
71 if (mFloatingRefs) {
72 // eat a floating ref
73 mFloatingRefs--;
74 } else {
75 cairo_surface_reference(mSurface);
78 return (nsrefcnt)cairo_surface_get_reference_count(mSurface);
80 // the surface isn't valid, but we still need to refcount
81 // the gfxASurface
82 return ++mFloatingRefs;
85 nsrefcnt gfxASurface::Release(void) {
86 if (mSurfaceValid) {
87 NS_ASSERTION(
88 mFloatingRefs == 0,
89 "gfxASurface::Release with floating refs still hanging around!");
91 // Note that there is a destructor set on user data for mSurface,
92 // which will delete this gfxASurface wrapper when the surface's refcount
93 // goes out of scope.
94 nsrefcnt refcnt = (nsrefcnt)cairo_surface_get_reference_count(mSurface);
95 cairo_surface_destroy(mSurface);
97 // |this| may not be valid any more, don't use it!
99 return --refcnt;
101 if (--mFloatingRefs == 0) {
102 delete this;
103 return 0;
105 return mFloatingRefs;
108 void gfxASurface::SurfaceDestroyFunc(void* data) {
109 gfxASurface* surf = (gfxASurface*)data;
110 // fprintf (stderr, "Deleting wrapper for %p (wrapper: %p)\n", surf->mSurface,
111 // data);
112 delete surf;
115 gfxASurface* gfxASurface::GetSurfaceWrapper(cairo_surface_t* csurf) {
116 if (!csurf) return nullptr;
117 return (gfxASurface*)cairo_surface_get_user_data(csurf,
118 &gfxasurface_pointer_key);
121 void gfxASurface::SetSurfaceWrapper(cairo_surface_t* csurf,
122 gfxASurface* asurf) {
123 if (!csurf) return;
124 cairo_surface_set_user_data(csurf, &gfxasurface_pointer_key, asurf,
125 SurfaceDestroyFunc);
128 already_AddRefed<gfxASurface> gfxASurface::Wrap(cairo_surface_t* csurf,
129 const IntSize& aSize) {
130 RefPtr<gfxASurface> result;
132 /* Do we already have a wrapper for this surface? */
133 result = GetSurfaceWrapper(csurf);
134 if (result) {
135 // fprintf(stderr, "Existing wrapper for %p -> %p\n", csurf, result);
136 return result.forget();
139 /* No wrapper; figure out the surface type and create it */
140 cairo_surface_type_t stype = cairo_surface_get_type(csurf);
142 if (stype == CAIRO_SURFACE_TYPE_IMAGE) {
143 result = new gfxImageSurface(csurf);
145 #ifdef CAIRO_HAS_WIN32_SURFACE
146 else if (stype == CAIRO_SURFACE_TYPE_WIN32 ||
147 stype == CAIRO_SURFACE_TYPE_WIN32_PRINTING) {
148 result = new gfxWindowsSurface(csurf);
150 #endif
151 #ifdef MOZ_X11
152 else if (stype == CAIRO_SURFACE_TYPE_XLIB) {
153 result = new gfxXlibSurface(csurf);
155 #endif
156 #ifdef CAIRO_HAS_QUARTZ_SURFACE
157 else if (stype == CAIRO_SURFACE_TYPE_QUARTZ) {
158 result = new gfxQuartzSurface(csurf, aSize);
160 #endif
161 else {
162 result = new gfxUnknownSurface(csurf, aSize);
165 // fprintf(stderr, "New wrapper for %p -> %p\n", csurf, result);
167 return result.forget();
170 void gfxASurface::Init(cairo_surface_t* surface, bool existingSurface) {
171 SetSurfaceWrapper(surface, this);
172 MOZ_ASSERT(surface, "surface should be a valid pointer");
174 mSurface = surface;
175 mSurfaceValid = !cairo_surface_status(surface);
176 if (!mSurfaceValid) {
177 gfxWarning() << "ASurface Init failed with Cairo status "
178 << cairo_surface_status(surface) << " on " << hexa(surface);
181 if (existingSurface || !mSurfaceValid) {
182 mFloatingRefs = 0;
183 } else {
184 mFloatingRefs = 1;
185 if (cairo_surface_get_content(surface) != CAIRO_CONTENT_COLOR) {
186 cairo_surface_set_subpixel_antialiasing(
187 surface, CAIRO_SUBPIXEL_ANTIALIASING_DISABLED);
192 gfxSurfaceType gfxASurface::GetType() const {
193 if (!mSurfaceValid) return (gfxSurfaceType)-1;
195 return (gfxSurfaceType)cairo_surface_get_type(mSurface);
198 gfxContentType gfxASurface::GetContentType() const {
199 if (!mSurfaceValid) return (gfxContentType)-1;
201 return (gfxContentType)cairo_surface_get_content(mSurface);
204 void gfxASurface::SetDeviceOffset(const gfxPoint& offset) {
205 if (!mSurfaceValid) return;
206 cairo_surface_set_device_offset(mSurface, offset.x, offset.y);
209 gfxPoint gfxASurface::GetDeviceOffset() const {
210 if (!mSurfaceValid) return gfxPoint(0.0, 0.0);
211 gfxPoint pt;
212 cairo_surface_get_device_offset(mSurface, &pt.x.value, &pt.y.value);
213 return pt;
216 void gfxASurface::Flush() const {
217 if (!mSurfaceValid) return;
218 cairo_surface_flush(mSurface);
219 gfxPlatform::ClearSourceSurfaceForSurface(const_cast<gfxASurface*>(this));
222 void gfxASurface::MarkDirty() {
223 if (!mSurfaceValid) return;
224 cairo_surface_mark_dirty(mSurface);
225 gfxPlatform::ClearSourceSurfaceForSurface(this);
228 void gfxASurface::MarkDirty(const gfxRect& r) {
229 if (!mSurfaceValid) return;
230 cairo_surface_mark_dirty_rectangle(mSurface, (int)r.X(), (int)r.Y(),
231 (int)r.Width(), (int)r.Height());
232 gfxPlatform::ClearSourceSurfaceForSurface(this);
235 void gfxASurface::SetData(const cairo_user_data_key_t* key, void* user_data,
236 thebes_destroy_func_t destroy) {
237 if (!mSurfaceValid) return;
238 cairo_surface_set_user_data(mSurface, key, user_data, destroy);
241 void* gfxASurface::GetData(const cairo_user_data_key_t* key) {
242 if (!mSurfaceValid) return nullptr;
243 return cairo_surface_get_user_data(mSurface, key);
246 void gfxASurface::Finish() {
247 // null surfaces are allowed here
248 cairo_surface_finish(mSurface);
251 already_AddRefed<gfxImageSurface> gfxASurface::CopyToARGB32ImageSurface() {
252 if (!mSurface || !mSurfaceValid) {
253 return nullptr;
256 const IntSize size = GetSize();
257 RefPtr<gfxImageSurface> imgSurface =
258 new gfxImageSurface(size, SurfaceFormat::A8R8G8B8_UINT32);
260 RefPtr<DrawTarget> dt = gfxPlatform::CreateDrawTargetForSurface(
261 imgSurface, IntSize(size.width, size.height));
262 RefPtr<SourceSurface> source =
263 gfxPlatform::GetSourceSurfaceForSurface(dt, this);
265 dt->CopySurface(source, IntRect(0, 0, size.width, size.height), IntPoint());
267 return imgSurface.forget();
270 int gfxASurface::CairoStatus() {
271 if (!mSurfaceValid) return -1;
273 return cairo_surface_status(mSurface);
276 nsresult gfxASurface::BeginPrinting(const nsAString& aTitle,
277 const nsAString& aPrintToFileName) {
278 return NS_OK;
281 nsresult gfxASurface::EndPrinting() { return NS_OK; }
283 nsresult gfxASurface::AbortPrinting() { return NS_OK; }
285 nsresult gfxASurface::BeginPage() { return NS_OK; }
287 nsresult gfxASurface::EndPage() { return NS_OK; }
289 gfxContentType gfxASurface::ContentFromFormat(gfxImageFormat format) {
290 switch (format) {
291 case SurfaceFormat::A8R8G8B8_UINT32:
292 return gfxContentType::COLOR_ALPHA;
293 case SurfaceFormat::X8R8G8B8_UINT32:
294 case SurfaceFormat::R5G6B5_UINT16:
295 return gfxContentType::COLOR;
296 case SurfaceFormat::A8:
297 return gfxContentType::ALPHA;
299 case SurfaceFormat::UNKNOWN:
300 default:
301 return gfxContentType::COLOR;
305 int32_t gfxASurface::BytePerPixelFromFormat(gfxImageFormat format) {
306 switch (format) {
307 case SurfaceFormat::A8R8G8B8_UINT32:
308 case SurfaceFormat::X8R8G8B8_UINT32:
309 return 4;
310 case SurfaceFormat::R5G6B5_UINT16:
311 return 2;
312 case SurfaceFormat::A8:
313 return 1;
314 default:
315 NS_WARNING("Unknown byte per pixel value for Image format");
317 return 0;
320 /** Memory reporting **/
322 static const char* sDefaultSurfaceDescription =
323 "Memory used by gfx surface of the given type.";
325 struct SurfaceMemoryReporterAttrs {
326 const char* path;
327 const char* description;
330 static const SurfaceMemoryReporterAttrs sSurfaceMemoryReporterAttrs[] = {
331 {"gfx-surface-image", nullptr},
332 {"gfx-surface-pdf", nullptr},
333 {"gfx-surface-ps", nullptr},
334 {"gfx-surface-xlib",
335 "Memory used by xlib surfaces to store pixmaps. This memory lives in "
336 "the X server's process rather than in this application, so the bytes "
337 "accounted for here aren't counted in vsize, resident, explicit, or any "
338 "of "
339 "the other measurements on this page."},
340 {"gfx-surface-xcb", nullptr},
341 {"gfx-surface-glitz???", nullptr}, // should never be used
342 {"gfx-surface-quartz", nullptr},
343 {"gfx-surface-win32", nullptr},
344 {"gfx-surface-beos", nullptr},
345 {"gfx-surface-directfb???", nullptr}, // should never be used
346 {"gfx-surface-svg", nullptr},
347 {"gfx-surface-os2", nullptr},
348 {"gfx-surface-win32printing", nullptr},
349 {"gfx-surface-quartzimage", nullptr},
350 {"gfx-surface-script", nullptr},
351 {"gfx-surface-qpainter", nullptr},
352 {"gfx-surface-recording", nullptr},
353 {"gfx-surface-vg", nullptr},
354 {"gfx-surface-gl", nullptr},
355 {"gfx-surface-drm", nullptr},
356 {"gfx-surface-tee", nullptr},
357 {"gfx-surface-xml", nullptr},
358 {"gfx-surface-skia", nullptr},
359 {"gfx-surface-subsurface", nullptr},
362 static_assert(MOZ_ARRAY_LENGTH(sSurfaceMemoryReporterAttrs) ==
363 size_t(gfxSurfaceType::Max),
364 "sSurfaceMemoryReporterAttrs exceeds max capacity");
365 static_assert(uint32_t(CAIRO_SURFACE_TYPE_SKIA) ==
366 uint32_t(gfxSurfaceType::Skia),
367 "CAIRO_SURFACE_TYPE_SKIA not equal to gfxSurfaceType::Skia");
369 /* Surface size memory reporting */
371 class SurfaceMemoryReporter final : public nsIMemoryReporter {
372 ~SurfaceMemoryReporter() = default;
374 // We can touch this array on several different threads, and we don't
375 // want to introduce memory barriers when recording the memory used. To
376 // assure dynamic race checkers like TSan that this is OK, we use
377 // relaxed memory ordering here.
378 static Atomic<size_t, Relaxed>
379 sSurfaceMemoryUsed[size_t(gfxSurfaceType::Max)];
381 public:
382 static void AdjustUsedMemory(gfxSurfaceType aType, int32_t aBytes) {
383 // A read-modify-write operation like += would require a memory barrier
384 // here, which would defeat the purpose of using relaxed memory
385 // ordering. So separate out the read and write operations.
386 sSurfaceMemoryUsed[size_t(aType)] =
387 sSurfaceMemoryUsed[size_t(aType)] + aBytes;
390 // This memory reporter is sometimes allocated on the compositor thread,
391 // but always released on the main thread, so its refcounting needs to be
392 // threadsafe.
393 NS_DECL_THREADSAFE_ISUPPORTS
395 NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport,
396 nsISupports* aData, bool aAnonymize) override {
397 const size_t len = ArrayLength(sSurfaceMemoryReporterAttrs);
398 for (size_t i = 0; i < len; i++) {
399 int64_t amount = sSurfaceMemoryUsed[i];
401 if (amount != 0) {
402 const char* path = sSurfaceMemoryReporterAttrs[i].path;
403 const char* desc = sSurfaceMemoryReporterAttrs[i].description;
404 if (!desc) {
405 desc = sDefaultSurfaceDescription;
408 aHandleReport->Callback(""_ns, nsCString(path), KIND_OTHER, UNITS_BYTES,
409 amount, nsCString(desc), aData);
413 return NS_OK;
417 Atomic<size_t, Relaxed>
418 SurfaceMemoryReporter::sSurfaceMemoryUsed[size_t(gfxSurfaceType::Max)];
420 NS_IMPL_ISUPPORTS(SurfaceMemoryReporter, nsIMemoryReporter)
422 void gfxASurface::RecordMemoryUsedForSurfaceType(gfxSurfaceType aType,
423 int32_t aBytes) {
424 if (int(aType) < 0 || aType >= gfxSurfaceType::Max) {
425 NS_WARNING("Invalid type to RecordMemoryUsedForSurfaceType!");
426 return;
429 static bool registered = false;
430 if (!registered) {
431 RegisterStrongMemoryReporter(new SurfaceMemoryReporter());
432 registered = true;
435 SurfaceMemoryReporter::AdjustUsedMemory(aType, aBytes);
438 void gfxASurface::RecordMemoryUsed(int32_t aBytes) {
439 RecordMemoryUsedForSurfaceType(GetType(), aBytes);
440 mBytesRecorded += aBytes;
443 void gfxASurface::RecordMemoryFreed() {
444 if (mBytesRecorded) {
445 RecordMemoryUsedForSurfaceType(GetType(), -mBytesRecorded);
446 mBytesRecorded = 0;
450 size_t gfxASurface::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const {
451 // We don't measure mSurface because cairo doesn't allow it.
452 return 0;
455 size_t gfxASurface::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const {
456 return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
459 /* static */
460 uint8_t gfxASurface::BytesPerPixel(gfxImageFormat aImageFormat) {
461 switch (aImageFormat) {
462 case SurfaceFormat::A8R8G8B8_UINT32:
463 return 4;
464 case SurfaceFormat::X8R8G8B8_UINT32:
465 return 4;
466 case SurfaceFormat::R5G6B5_UINT16:
467 return 2;
468 case SurfaceFormat::A8:
469 return 1;
470 case SurfaceFormat::UNKNOWN:
471 default:
472 MOZ_ASSERT_UNREACHABLE("Not really sure what you want me to say here");
473 return 0;
477 void gfxASurface::SetOpaqueRect(const gfxRect& aRect) {
478 if (aRect.IsEmpty()) {
479 mOpaqueRect = nullptr;
480 } else if (!!mOpaqueRect) {
481 *mOpaqueRect = aRect;
482 } else {
483 mOpaqueRect = MakeUnique<gfxRect>(aRect);
487 /* static */ const gfxRect& gfxASurface::GetEmptyOpaqueRect() {
488 static const gfxRect empty(0, 0, 0, 0);
489 return empty;
492 const IntSize gfxASurface::GetSize() const { return IntSize(-1, -1); }
494 SurfaceFormat gfxASurface::GetSurfaceFormat() const {
495 if (!mSurfaceValid) {
496 return SurfaceFormat::UNKNOWN;
498 return GfxFormatForCairoSurface(mSurface);
501 already_AddRefed<gfxImageSurface> gfxASurface::GetAsImageSurface() {
502 return nullptr;