Line data Source code
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/. */
5 :
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 "mozilla/gfx/Logging.h"
16 : #include "mozilla/gfx/HelpersCairo.h"
17 : #include "gfx2DGlue.h"
18 :
19 : #include "gfxASurface.h"
20 : #include "gfxContext.h"
21 : #include "gfxImageSurface.h"
22 : #include "gfxPlatform.h"
23 : #include "gfxRect.h"
24 :
25 : #include "cairo.h"
26 : #include <algorithm>
27 :
28 : #ifdef CAIRO_HAS_WIN32_SURFACE
29 : #include "gfxWindowsSurface.h"
30 : #endif
31 :
32 : #ifdef MOZ_X11
33 : #include "gfxXlibSurface.h"
34 : #endif
35 :
36 : #ifdef CAIRO_HAS_QUARTZ_SURFACE
37 : #include "gfxQuartzSurface.h"
38 : #endif
39 :
40 : #include <stdio.h>
41 : #include <limits.h>
42 :
43 : #include "imgIEncoder.h"
44 : #include "nsComponentManagerUtils.h"
45 : #include "nsISupportsUtils.h"
46 : #include "nsCOMPtr.h"
47 : #include "nsServiceManagerUtils.h"
48 : #include "nsString.h"
49 : #include "nsIClipboardHelper.h"
50 :
51 : using namespace mozilla;
52 : using namespace mozilla::gfx;
53 :
54 : static cairo_user_data_key_t gfxasurface_pointer_key;
55 :
56 3 : gfxASurface::gfxASurface()
57 : : mSurface(nullptr), mFloatingRefs(0), mBytesRecorded(0),
58 3 : mSurfaceValid(false)
59 : {
60 3 : MOZ_COUNT_CTOR(gfxASurface);
61 3 : }
62 :
63 0 : gfxASurface::~gfxASurface()
64 : {
65 0 : RecordMemoryFreed();
66 :
67 0 : MOZ_COUNT_DTOR(gfxASurface);
68 0 : }
69 :
70 : // Surfaces use refcounting that's tied to the cairo surface refcnt, to avoid
71 : // refcount mismatch issues.
72 : nsrefcnt
73 3 : gfxASurface::AddRef(void)
74 : {
75 3 : if (mSurfaceValid) {
76 3 : if (mFloatingRefs) {
77 : // eat a floating ref
78 3 : mFloatingRefs--;
79 : } else {
80 0 : cairo_surface_reference(mSurface);
81 : }
82 :
83 3 : return (nsrefcnt) cairo_surface_get_reference_count(mSurface);
84 : }
85 : // the surface isn't valid, but we still need to refcount
86 : // the gfxASurface
87 0 : return ++mFloatingRefs;
88 : }
89 :
90 : nsrefcnt
91 0 : gfxASurface::Release(void)
92 : {
93 0 : if (mSurfaceValid) {
94 0 : NS_ASSERTION(mFloatingRefs == 0, "gfxASurface::Release with floating refs still hanging around!");
95 :
96 : // Note that there is a destructor set on user data for mSurface,
97 : // which will delete this gfxASurface wrapper when the surface's refcount goes
98 : // out of scope.
99 0 : nsrefcnt refcnt = (nsrefcnt) cairo_surface_get_reference_count(mSurface);
100 0 : cairo_surface_destroy(mSurface);
101 :
102 : // |this| may not be valid any more, don't use it!
103 :
104 0 : return --refcnt;
105 : }
106 0 : if (--mFloatingRefs == 0) {
107 0 : delete this;
108 0 : return 0;
109 : }
110 0 : return mFloatingRefs;
111 : }
112 :
113 : void
114 0 : gfxASurface::SurfaceDestroyFunc(void *data) {
115 0 : gfxASurface *surf = (gfxASurface*) data;
116 : // fprintf (stderr, "Deleting wrapper for %p (wrapper: %p)\n", surf->mSurface, data);
117 0 : delete surf;
118 0 : }
119 :
120 : gfxASurface*
121 0 : gfxASurface::GetSurfaceWrapper(cairo_surface_t *csurf)
122 : {
123 0 : if (!csurf)
124 0 : return nullptr;
125 0 : return (gfxASurface*) cairo_surface_get_user_data(csurf, &gfxasurface_pointer_key);
126 : }
127 :
128 : void
129 3 : gfxASurface::SetSurfaceWrapper(cairo_surface_t *csurf, gfxASurface *asurf)
130 : {
131 3 : if (!csurf)
132 0 : return;
133 3 : cairo_surface_set_user_data(csurf, &gfxasurface_pointer_key, asurf, SurfaceDestroyFunc);
134 : }
135 :
136 : already_AddRefed<gfxASurface>
137 0 : gfxASurface::Wrap (cairo_surface_t *csurf, const IntSize& aSize)
138 : {
139 0 : RefPtr<gfxASurface> result;
140 :
141 : /* Do we already have a wrapper for this surface? */
142 0 : result = GetSurfaceWrapper(csurf);
143 0 : if (result) {
144 : // fprintf(stderr, "Existing wrapper for %p -> %p\n", csurf, result);
145 0 : return result.forget();
146 : }
147 :
148 : /* No wrapper; figure out the surface type and create it */
149 0 : cairo_surface_type_t stype = cairo_surface_get_type(csurf);
150 :
151 0 : if (stype == CAIRO_SURFACE_TYPE_IMAGE) {
152 0 : result = new gfxImageSurface(csurf);
153 : }
154 : #ifdef CAIRO_HAS_WIN32_SURFACE
155 : else if (stype == CAIRO_SURFACE_TYPE_WIN32 ||
156 : stype == CAIRO_SURFACE_TYPE_WIN32_PRINTING) {
157 : result = new gfxWindowsSurface(csurf);
158 : }
159 : #endif
160 : #ifdef MOZ_X11
161 0 : else if (stype == CAIRO_SURFACE_TYPE_XLIB) {
162 0 : result = new gfxXlibSurface(csurf);
163 : }
164 : #endif
165 : #ifdef CAIRO_HAS_QUARTZ_SURFACE
166 : else if (stype == CAIRO_SURFACE_TYPE_QUARTZ) {
167 : result = new gfxQuartzSurface(csurf, aSize);
168 : }
169 : #endif
170 : else {
171 0 : result = new gfxUnknownSurface(csurf, aSize);
172 : }
173 :
174 : // fprintf(stderr, "New wrapper for %p -> %p\n", csurf, result);
175 :
176 0 : return result.forget();
177 : }
178 :
179 : void
180 3 : gfxASurface::Init(cairo_surface_t* surface, bool existingSurface)
181 : {
182 3 : SetSurfaceWrapper(surface, this);
183 3 : MOZ_ASSERT(surface, "surface should be a valid pointer");
184 :
185 3 : mSurface = surface;
186 3 : mSurfaceValid = !cairo_surface_status(surface);
187 3 : if (!mSurfaceValid) {
188 0 : gfxWarning() << "ASurface Init failed with Cairo status " << cairo_surface_status(surface) << " on " << hexa(surface);
189 : }
190 :
191 3 : if (existingSurface || !mSurfaceValid) {
192 0 : mFloatingRefs = 0;
193 : } else {
194 3 : mFloatingRefs = 1;
195 : #ifdef MOZ_TREE_CAIRO
196 3 : if (cairo_surface_get_content(surface) != CAIRO_CONTENT_COLOR) {
197 3 : cairo_surface_set_subpixel_antialiasing(surface, CAIRO_SUBPIXEL_ANTIALIASING_DISABLED);
198 : }
199 : #endif
200 : }
201 3 : }
202 :
203 : gfxSurfaceType
204 12 : gfxASurface::GetType() const
205 : {
206 12 : if (!mSurfaceValid)
207 0 : return (gfxSurfaceType)-1;
208 :
209 12 : return (gfxSurfaceType)cairo_surface_get_type(mSurface);
210 : }
211 :
212 : gfxContentType
213 0 : gfxASurface::GetContentType() const
214 : {
215 0 : if (!mSurfaceValid)
216 0 : return (gfxContentType)-1;
217 :
218 0 : return (gfxContentType)cairo_surface_get_content(mSurface);
219 : }
220 :
221 : void
222 0 : gfxASurface::SetDeviceOffset(const gfxPoint& offset)
223 : {
224 0 : if (!mSurfaceValid)
225 0 : return;
226 0 : cairo_surface_set_device_offset(mSurface,
227 0 : offset.x, offset.y);
228 : }
229 :
230 : gfxPoint
231 0 : gfxASurface::GetDeviceOffset() const
232 : {
233 0 : if (!mSurfaceValid)
234 0 : return gfxPoint(0.0, 0.0);
235 0 : gfxPoint pt;
236 0 : cairo_surface_get_device_offset(mSurface, &pt.x, &pt.y);
237 0 : return pt;
238 : }
239 :
240 : void
241 0 : gfxASurface::Flush() const
242 : {
243 0 : if (!mSurfaceValid)
244 0 : return;
245 0 : cairo_surface_flush(mSurface);
246 0 : gfxPlatform::ClearSourceSurfaceForSurface(const_cast<gfxASurface*>(this));
247 : }
248 :
249 : void
250 0 : gfxASurface::MarkDirty()
251 : {
252 0 : if (!mSurfaceValid)
253 0 : return;
254 0 : cairo_surface_mark_dirty(mSurface);
255 0 : gfxPlatform::ClearSourceSurfaceForSurface(this);
256 : }
257 :
258 : void
259 0 : gfxASurface::MarkDirty(const gfxRect& r)
260 : {
261 0 : if (!mSurfaceValid)
262 0 : return;
263 0 : cairo_surface_mark_dirty_rectangle(mSurface,
264 0 : (int) r.X(), (int) r.Y(),
265 0 : (int) r.Width(), (int) r.Height());
266 0 : gfxPlatform::ClearSourceSurfaceForSurface(this);
267 : }
268 :
269 : void
270 0 : gfxASurface::SetData(const cairo_user_data_key_t *key,
271 : void *user_data,
272 : thebes_destroy_func_t destroy)
273 : {
274 0 : if (!mSurfaceValid)
275 0 : return;
276 0 : cairo_surface_set_user_data(mSurface, key, user_data, destroy);
277 : }
278 :
279 : void *
280 0 : gfxASurface::GetData(const cairo_user_data_key_t *key)
281 : {
282 0 : if (!mSurfaceValid)
283 0 : return nullptr;
284 0 : return cairo_surface_get_user_data(mSurface, key);
285 : }
286 :
287 : void
288 0 : gfxASurface::Finish()
289 : {
290 : // null surfaces are allowed here
291 0 : cairo_surface_finish(mSurface);
292 0 : }
293 :
294 : already_AddRefed<gfxASurface>
295 0 : gfxASurface::CreateSimilarSurface(gfxContentType aContent,
296 : const IntSize& aSize)
297 : {
298 0 : if (!mSurface || !mSurfaceValid) {
299 0 : return nullptr;
300 : }
301 :
302 : cairo_surface_t *surface =
303 0 : cairo_surface_create_similar(mSurface, cairo_content_t(int(aContent)),
304 0 : aSize.width, aSize.height);
305 0 : if (cairo_surface_status(surface)) {
306 0 : cairo_surface_destroy(surface);
307 0 : return nullptr;
308 : }
309 :
310 0 : RefPtr<gfxASurface> result = Wrap(surface, aSize);
311 0 : cairo_surface_destroy(surface);
312 0 : return result.forget();
313 : }
314 :
315 : already_AddRefed<gfxImageSurface>
316 0 : gfxASurface::CopyToARGB32ImageSurface()
317 : {
318 0 : if (!mSurface || !mSurfaceValid) {
319 0 : return nullptr;
320 : }
321 :
322 0 : const IntSize size = GetSize();
323 : RefPtr<gfxImageSurface> imgSurface =
324 0 : new gfxImageSurface(size, SurfaceFormat::A8R8G8B8_UINT32);
325 :
326 0 : RefPtr<DrawTarget> dt = gfxPlatform::GetPlatform()->CreateDrawTargetForSurface(imgSurface, IntSize(size.width, size.height));
327 0 : RefPtr<SourceSurface> source = gfxPlatform::GetPlatform()->GetSourceSurfaceForSurface(dt, this);
328 :
329 0 : dt->CopySurface(source, IntRect(0, 0, size.width, size.height), IntPoint());
330 :
331 0 : return imgSurface.forget();
332 : }
333 :
334 : int
335 3 : gfxASurface::CairoStatus()
336 : {
337 3 : if (!mSurfaceValid)
338 0 : return -1;
339 :
340 3 : return cairo_surface_status(mSurface);
341 : }
342 :
343 : nsresult
344 0 : gfxASurface::BeginPrinting(const nsAString& aTitle, const nsAString& aPrintToFileName)
345 : {
346 0 : return NS_OK;
347 : }
348 :
349 : nsresult
350 0 : gfxASurface::EndPrinting()
351 : {
352 0 : return NS_OK;
353 : }
354 :
355 : nsresult
356 0 : gfxASurface::AbortPrinting()
357 : {
358 0 : return NS_OK;
359 : }
360 :
361 : nsresult
362 0 : gfxASurface::BeginPage()
363 : {
364 0 : return NS_OK;
365 : }
366 :
367 : nsresult
368 0 : gfxASurface::EndPage()
369 : {
370 0 : return NS_OK;
371 : }
372 :
373 : gfxContentType
374 0 : gfxASurface::ContentFromFormat(gfxImageFormat format)
375 : {
376 0 : switch (format) {
377 : case SurfaceFormat::A8R8G8B8_UINT32:
378 0 : return gfxContentType::COLOR_ALPHA;
379 : case SurfaceFormat::X8R8G8B8_UINT32:
380 : case SurfaceFormat::R5G6B5_UINT16:
381 0 : return gfxContentType::COLOR;
382 : case SurfaceFormat::A8:
383 0 : return gfxContentType::ALPHA;
384 :
385 : case SurfaceFormat::UNKNOWN:
386 : default:
387 0 : return gfxContentType::COLOR;
388 : }
389 : }
390 :
391 : int32_t
392 0 : gfxASurface::BytePerPixelFromFormat(gfxImageFormat format)
393 : {
394 0 : switch (format) {
395 : case SurfaceFormat::A8R8G8B8_UINT32:
396 : case SurfaceFormat::X8R8G8B8_UINT32:
397 0 : return 4;
398 : case SurfaceFormat::R5G6B5_UINT16:
399 0 : return 2;
400 : case SurfaceFormat::A8:
401 0 : return 1;
402 : default:
403 0 : NS_WARNING("Unknown byte per pixel value for Image format");
404 : }
405 0 : return 0;
406 : }
407 :
408 : /** Memory reporting **/
409 :
410 : static const char *sDefaultSurfaceDescription =
411 : "Memory used by gfx surface of the given type.";
412 :
413 : struct SurfaceMemoryReporterAttrs {
414 : const char *path;
415 : const char *description;
416 : };
417 :
418 : static const SurfaceMemoryReporterAttrs sSurfaceMemoryReporterAttrs[] = {
419 : {"gfx-surface-image", nullptr},
420 : {"gfx-surface-pdf", nullptr},
421 : {"gfx-surface-ps", nullptr},
422 : {"gfx-surface-xlib",
423 : "Memory used by xlib surfaces to store pixmaps. This memory lives in "
424 : "the X server's process rather than in this application, so the bytes "
425 : "accounted for here aren't counted in vsize, resident, explicit, or any of "
426 : "the other measurements on this page."},
427 : {"gfx-surface-xcb", nullptr},
428 : {"gfx-surface-glitz???", nullptr}, // should never be used
429 : {"gfx-surface-quartz", nullptr},
430 : {"gfx-surface-win32", nullptr},
431 : {"gfx-surface-beos", nullptr},
432 : {"gfx-surface-directfb???", nullptr}, // should never be used
433 : {"gfx-surface-svg", nullptr},
434 : {"gfx-surface-os2", nullptr},
435 : {"gfx-surface-win32printing", nullptr},
436 : {"gfx-surface-quartzimage", nullptr},
437 : {"gfx-surface-script", nullptr},
438 : {"gfx-surface-qpainter", nullptr},
439 : {"gfx-surface-recording", nullptr},
440 : {"gfx-surface-vg", nullptr},
441 : {"gfx-surface-gl", nullptr},
442 : {"gfx-surface-drm", nullptr},
443 : {"gfx-surface-tee", nullptr},
444 : {"gfx-surface-xml", nullptr},
445 : {"gfx-surface-skia", nullptr},
446 : {"gfx-surface-subsurface", nullptr},
447 : };
448 :
449 : static_assert(MOZ_ARRAY_LENGTH(sSurfaceMemoryReporterAttrs) ==
450 : size_t(gfxSurfaceType::Max), "sSurfaceMemoryReporterAttrs exceeds max capacity");
451 : static_assert(uint32_t(CAIRO_SURFACE_TYPE_SKIA) ==
452 : uint32_t(gfxSurfaceType::Skia), "CAIRO_SURFACE_TYPE_SKIA not equal to gfxSurfaceType::Skia");
453 :
454 : /* Surface size memory reporting */
455 :
456 3 : class SurfaceMemoryReporter final : public nsIMemoryReporter
457 : {
458 : ~SurfaceMemoryReporter() = default;
459 :
460 : // We can touch this array on several different threads, and we don't
461 : // want to introduce memory barriers when recording the memory used. To
462 : // assure dynamic race checkers like TSan that this is OK, we use
463 : // relaxed memory ordering here.
464 : static Atomic<size_t, Relaxed> sSurfaceMemoryUsed[size_t(gfxSurfaceType::Max)];
465 :
466 : public:
467 3 : static void AdjustUsedMemory(gfxSurfaceType aType, int32_t aBytes)
468 : {
469 : // A read-modify-write operation like += would require a memory barrier
470 : // here, which would defeat the purpose of using relaxed memory
471 : // ordering. So separate out the read and write operations.
472 3 : sSurfaceMemoryUsed[size_t(aType)] = sSurfaceMemoryUsed[size_t(aType)] + aBytes;
473 3 : };
474 :
475 : // This memory reporter is sometimes allocated on the compositor thread,
476 : // but always released on the main thread, so its refcounting needs to be
477 : // threadsafe.
478 : NS_DECL_THREADSAFE_ISUPPORTS
479 :
480 0 : NS_IMETHOD CollectReports(nsIHandleReportCallback *aHandleReport,
481 : nsISupports *aData, bool aAnonymize) override
482 : {
483 0 : const size_t len = ArrayLength(sSurfaceMemoryReporterAttrs);
484 0 : for (size_t i = 0; i < len; i++) {
485 0 : int64_t amount = sSurfaceMemoryUsed[i];
486 :
487 0 : if (amount != 0) {
488 0 : const char *path = sSurfaceMemoryReporterAttrs[i].path;
489 0 : const char *desc = sSurfaceMemoryReporterAttrs[i].description;
490 0 : if (!desc) {
491 0 : desc = sDefaultSurfaceDescription;
492 : }
493 :
494 0 : aHandleReport->Callback(
495 0 : EmptyCString(), nsCString(path), KIND_OTHER, UNITS_BYTES,
496 0 : amount, nsCString(desc), aData);
497 : }
498 : }
499 :
500 0 : return NS_OK;
501 : }
502 : };
503 :
504 : Atomic<size_t, Relaxed> SurfaceMemoryReporter::sSurfaceMemoryUsed[size_t(gfxSurfaceType::Max)];
505 :
506 39 : NS_IMPL_ISUPPORTS(SurfaceMemoryReporter, nsIMemoryReporter)
507 :
508 : void
509 3 : gfxASurface::RecordMemoryUsedForSurfaceType(gfxSurfaceType aType,
510 : int32_t aBytes)
511 : {
512 3 : if (int(aType) < 0 || aType >= gfxSurfaceType::Max) {
513 0 : NS_WARNING("Invalid type to RecordMemoryUsedForSurfaceType!");
514 0 : return;
515 : }
516 :
517 : static bool registered = false;
518 3 : if (!registered) {
519 3 : RegisterStrongMemoryReporter(new SurfaceMemoryReporter());
520 3 : registered = true;
521 : }
522 :
523 3 : SurfaceMemoryReporter::AdjustUsedMemory(aType, aBytes);
524 : }
525 :
526 : void
527 3 : gfxASurface::RecordMemoryUsed(int32_t aBytes)
528 : {
529 3 : RecordMemoryUsedForSurfaceType(GetType(), aBytes);
530 3 : mBytesRecorded += aBytes;
531 3 : }
532 :
533 : void
534 0 : gfxASurface::RecordMemoryFreed()
535 : {
536 0 : if (mBytesRecorded) {
537 0 : RecordMemoryUsedForSurfaceType(GetType(), -mBytesRecorded);
538 0 : mBytesRecorded = 0;
539 : }
540 0 : }
541 :
542 : size_t
543 0 : gfxASurface::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
544 : {
545 : // We don't measure mSurface because cairo doesn't allow it.
546 0 : return 0;
547 : }
548 :
549 : size_t
550 0 : gfxASurface::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
551 : {
552 0 : return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
553 : }
554 :
555 : /* static */ uint8_t
556 0 : gfxASurface::BytesPerPixel(gfxImageFormat aImageFormat)
557 : {
558 0 : switch (aImageFormat) {
559 : case SurfaceFormat::A8R8G8B8_UINT32:
560 0 : return 4;
561 : case SurfaceFormat::X8R8G8B8_UINT32:
562 0 : return 4;
563 : case SurfaceFormat::R5G6B5_UINT16:
564 0 : return 2;
565 : case SurfaceFormat::A8:
566 0 : return 1;
567 : case SurfaceFormat::UNKNOWN:
568 : default:
569 0 : NS_NOTREACHED("Not really sure what you want me to say here");
570 0 : return 0;
571 : }
572 : }
573 :
574 : void
575 0 : gfxASurface::SetOpaqueRect(const gfxRect& aRect)
576 : {
577 0 : if (aRect.IsEmpty()) {
578 0 : mOpaqueRect = nullptr;
579 0 : } else if (!!mOpaqueRect) {
580 0 : *mOpaqueRect = aRect;
581 : } else {
582 0 : mOpaqueRect = MakeUnique<gfxRect>(aRect);
583 : }
584 0 : }
585 :
586 : /* static */const gfxRect&
587 0 : gfxASurface::GetEmptyOpaqueRect()
588 : {
589 0 : static const gfxRect empty(0, 0, 0, 0);
590 0 : return empty;
591 : }
592 :
593 : const IntSize
594 0 : gfxASurface::GetSize() const
595 : {
596 0 : return IntSize(-1, -1);
597 : }
598 :
599 : SurfaceFormat
600 0 : gfxASurface::GetSurfaceFormat() const
601 : {
602 0 : if (!mSurfaceValid) {
603 0 : return SurfaceFormat::UNKNOWN;
604 : }
605 0 : return GfxFormatForCairoSurface(mSurface);
606 : }
607 :
608 : already_AddRefed<gfxImageSurface>
609 0 : gfxASurface::GetAsImageSurface()
610 : {
611 0 : return nullptr;
612 : }
|