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