Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/gfx/thebes/gfxASurface.cpp
Line
Count
Source (jump to first uncovered line)
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
gfxASurface::gfxASurface()
57
 : mSurface(nullptr), mFloatingRefs(0), mBytesRecorded(0),
58
   mSurfaceValid(false)
59
0
{
60
0
    MOZ_COUNT_CTOR(gfxASurface);
61
0
}
62
63
gfxASurface::~gfxASurface()
64
0
{
65
0
    RecordMemoryFreed();
66
0
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
gfxASurface::AddRef(void)
74
0
{
75
0
    if (mSurfaceValid) {
76
0
        if (mFloatingRefs) {
77
0
            // eat a floating ref
78
0
            mFloatingRefs--;
79
0
        } else {
80
0
            cairo_surface_reference(mSurface);
81
0
        }
82
0
83
0
        return (nsrefcnt) cairo_surface_get_reference_count(mSurface);
84
0
    }
85
0
    // the surface isn't valid, but we still need to refcount
86
0
    // the gfxASurface
87
0
    return ++mFloatingRefs;
88
0
}
89
90
nsrefcnt
91
gfxASurface::Release(void)
92
0
{
93
0
    if (mSurfaceValid) {
94
0
        NS_ASSERTION(mFloatingRefs == 0, "gfxASurface::Release with floating refs still hanging around!");
95
0
96
0
        // Note that there is a destructor set on user data for mSurface,
97
0
        // which will delete this gfxASurface wrapper when the surface's refcount goes
98
0
        // out of scope.
99
0
        nsrefcnt refcnt = (nsrefcnt) cairo_surface_get_reference_count(mSurface);
100
0
        cairo_surface_destroy(mSurface);
101
0
102
0
        // |this| may not be valid any more, don't use it!
103
0
104
0
        return --refcnt;
105
0
    }
106
0
    if (--mFloatingRefs == 0) {
107
0
        delete this;
108
0
        return 0;
109
0
    }
110
0
    return mFloatingRefs;
111
0
}
112
113
void
114
0
gfxASurface::SurfaceDestroyFunc(void *data) {
115
0
    gfxASurface *surf = (gfxASurface*) data;
116
0
    // fprintf (stderr, "Deleting wrapper for %p (wrapper: %p)\n", surf->mSurface, data);
117
0
    delete surf;
118
0
}
119
120
gfxASurface*
121
gfxASurface::GetSurfaceWrapper(cairo_surface_t *csurf)
122
0
{
123
0
    if (!csurf)
124
0
        return nullptr;
125
0
    return (gfxASurface*) cairo_surface_get_user_data(csurf, &gfxasurface_pointer_key);
126
0
}
127
128
void
129
gfxASurface::SetSurfaceWrapper(cairo_surface_t *csurf, gfxASurface *asurf)
130
0
{
131
0
    if (!csurf)
132
0
        return;
133
0
    cairo_surface_set_user_data(csurf, &gfxasurface_pointer_key, asurf, SurfaceDestroyFunc);
134
0
}
135
136
already_AddRefed<gfxASurface>
137
gfxASurface::Wrap (cairo_surface_t *csurf, const IntSize& aSize)
138
0
{
139
0
    RefPtr<gfxASurface> result;
140
0
141
0
    /* Do we already have a wrapper for this surface? */
142
0
    result = GetSurfaceWrapper(csurf);
143
0
    if (result) {
144
0
        // fprintf(stderr, "Existing wrapper for %p -> %p\n", csurf, result);
145
0
        return result.forget();
146
0
    }
147
0
148
0
    /* No wrapper; figure out the surface type and create it */
149
0
    cairo_surface_type_t stype = cairo_surface_get_type(csurf);
150
0
151
0
    if (stype == CAIRO_SURFACE_TYPE_IMAGE) {
152
0
        result = new gfxImageSurface(csurf);
153
0
    }
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
0
    }
164
0
#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
0
    else {
171
0
        result = new gfxUnknownSurface(csurf, aSize);
172
0
    }
173
0
174
0
    // fprintf(stderr, "New wrapper for %p -> %p\n", csurf, result);
175
0
176
0
    return result.forget();
177
0
}
178
179
void
180
gfxASurface::Init(cairo_surface_t* surface, bool existingSurface)
181
0
{
182
0
    SetSurfaceWrapper(surface, this);
183
0
    MOZ_ASSERT(surface, "surface should be a valid pointer");
184
0
185
0
    mSurface = surface;
186
0
    mSurfaceValid = !cairo_surface_status(surface);
187
0
    if (!mSurfaceValid) {
188
0
        gfxWarning() << "ASurface Init failed with Cairo status " << cairo_surface_status(surface) << " on " << hexa(surface);
189
0
    }
190
0
191
0
    if (existingSurface || !mSurfaceValid) {
192
0
        mFloatingRefs = 0;
193
0
    } else {
194
0
        mFloatingRefs = 1;
195
0
#ifdef MOZ_TREE_CAIRO
196
0
        if (cairo_surface_get_content(surface) != CAIRO_CONTENT_COLOR) {
197
0
            cairo_surface_set_subpixel_antialiasing(surface, CAIRO_SUBPIXEL_ANTIALIASING_DISABLED);
198
0
        }
199
0
#endif
200
0
    }
201
0
}
202
203
gfxSurfaceType
204
gfxASurface::GetType() const
205
0
{
206
0
    if (!mSurfaceValid)
207
0
        return (gfxSurfaceType)-1;
208
0
209
0
    return (gfxSurfaceType)cairo_surface_get_type(mSurface);
210
0
}
211
212
gfxContentType
213
gfxASurface::GetContentType() const
214
0
{
215
0
    if (!mSurfaceValid)
216
0
        return (gfxContentType)-1;
217
0
218
0
    return (gfxContentType)cairo_surface_get_content(mSurface);
219
0
}
220
221
void
222
gfxASurface::SetDeviceOffset(const gfxPoint& offset)
223
0
{
224
0
    if (!mSurfaceValid)
225
0
        return;
226
0
    cairo_surface_set_device_offset(mSurface,
227
0
                                    offset.x, offset.y);
228
0
}
229
230
gfxPoint
231
gfxASurface::GetDeviceOffset() const
232
0
{
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
0
}
239
240
void
241
gfxASurface::Flush() const
242
0
{
243
0
    if (!mSurfaceValid)
244
0
        return;
245
0
    cairo_surface_flush(mSurface);
246
0
    gfxPlatform::ClearSourceSurfaceForSurface(const_cast<gfxASurface*>(this));
247
0
}
248
249
void
250
gfxASurface::MarkDirty()
251
0
{
252
0
    if (!mSurfaceValid)
253
0
        return;
254
0
    cairo_surface_mark_dirty(mSurface);
255
0
    gfxPlatform::ClearSourceSurfaceForSurface(this);
256
0
}
257
258
void
259
gfxASurface::MarkDirty(const gfxRect& r)
260
0
{
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
0
}
268
269
void
270
gfxASurface::SetData(const cairo_user_data_key_t *key,
271
                     void *user_data,
272
                     thebes_destroy_func_t destroy)
273
0
{
274
0
    if (!mSurfaceValid)
275
0
        return;
276
0
    cairo_surface_set_user_data(mSurface, key, user_data, destroy);
277
0
}
278
279
void *
280
gfxASurface::GetData(const cairo_user_data_key_t *key)
281
0
{
282
0
    if (!mSurfaceValid)
283
0
        return nullptr;
284
0
    return cairo_surface_get_user_data(mSurface, key);
285
0
}
286
287
void
288
gfxASurface::Finish()
289
0
{
290
0
    // null surfaces are allowed here
291
0
    cairo_surface_finish(mSurface);
292
0
}
293
294
already_AddRefed<gfxASurface>
295
gfxASurface::CreateSimilarSurface(gfxContentType aContent,
296
                                  const IntSize& aSize)
297
0
{
298
0
    if (!mSurface || !mSurfaceValid) {
299
0
      return nullptr;
300
0
    }
301
0
    
302
0
    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
0
    }
309
0
310
0
    RefPtr<gfxASurface> result = Wrap(surface, aSize);
311
0
    cairo_surface_destroy(surface);
312
0
    return result.forget();
313
0
}
314
315
already_AddRefed<gfxImageSurface>
316
gfxASurface::CopyToARGB32ImageSurface()
317
0
{
318
0
    if (!mSurface || !mSurfaceValid) {
319
0
      return nullptr;
320
0
    }
321
0
322
0
    const IntSize size = GetSize();
323
0
    RefPtr<gfxImageSurface> imgSurface =
324
0
        new gfxImageSurface(size, SurfaceFormat::A8R8G8B8_UINT32);
325
0
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
0
329
0
    dt->CopySurface(source, IntRect(0, 0, size.width, size.height), IntPoint());
330
0
331
0
    return imgSurface.forget();
332
0
}
333
334
int
335
gfxASurface::CairoStatus()
336
0
{
337
0
    if (!mSurfaceValid)
338
0
        return -1;
339
0
340
0
    return cairo_surface_status(mSurface);
341
0
}
342
343
nsresult
344
gfxASurface::BeginPrinting(const nsAString& aTitle, const nsAString& aPrintToFileName)
345
0
{
346
0
    return NS_OK;
347
0
}
348
349
nsresult
350
gfxASurface::EndPrinting()
351
0
{
352
0
    return NS_OK;
353
0
}
354
355
nsresult
356
gfxASurface::AbortPrinting()
357
0
{
358
0
    return NS_OK;
359
0
}
360
361
nsresult
362
gfxASurface::BeginPage()
363
0
{
364
0
    return NS_OK;
365
0
}
366
367
nsresult
368
gfxASurface::EndPage()
369
0
{
370
0
    return NS_OK;
371
0
}
372
373
gfxContentType
374
gfxASurface::ContentFromFormat(gfxImageFormat format)
375
{
376
    switch (format) {
377
        case SurfaceFormat::A8R8G8B8_UINT32:
378
            return gfxContentType::COLOR_ALPHA;
379
        case SurfaceFormat::X8R8G8B8_UINT32:
380
        case SurfaceFormat::R5G6B5_UINT16:
381
            return gfxContentType::COLOR;
382
        case SurfaceFormat::A8:
383
            return gfxContentType::ALPHA;
384
385
        case SurfaceFormat::UNKNOWN:
386
        default:
387
            return gfxContentType::COLOR;
388
    }
389
}
390
391
int32_t
392
gfxASurface::BytePerPixelFromFormat(gfxImageFormat format)
393
0
{
394
0
    switch (format) {
395
0
        case SurfaceFormat::A8R8G8B8_UINT32:
396
0
        case SurfaceFormat::X8R8G8B8_UINT32:
397
0
            return 4;
398
0
        case SurfaceFormat::R5G6B5_UINT16:
399
0
            return 2;
400
0
        case SurfaceFormat::A8:
401
0
            return 1;
402
0
        default:
403
0
            NS_WARNING("Unknown byte per pixel value for Image format");
404
0
    }
405
0
    return 0;
406
0
}
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
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
    static void AdjustUsedMemory(gfxSurfaceType aType, int32_t aBytes)
468
0
    {
469
0
        // A read-modify-write operation like += would require a memory barrier
470
0
        // here, which would defeat the purpose of using relaxed memory
471
0
        // ordering.  So separate out the read and write operations.
472
0
        sSurfaceMemoryUsed[size_t(aType)] = sSurfaceMemoryUsed[size_t(aType)] + aBytes;
473
0
    };
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
    NS_IMETHOD CollectReports(nsIHandleReportCallback *aHandleReport,
481
                              nsISupports *aData, bool aAnonymize) override
482
0
    {
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
0
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
0
                }
493
0
494
0
                aHandleReport->Callback(
495
0
                    EmptyCString(), nsCString(path), KIND_OTHER, UNITS_BYTES,
496
0
                    amount, nsCString(desc), aData);
497
0
            }
498
0
        }
499
0
500
0
        return NS_OK;
501
0
    }
502
};
503
504
Atomic<size_t, Relaxed> SurfaceMemoryReporter::sSurfaceMemoryUsed[size_t(gfxSurfaceType::Max)];
505
506
NS_IMPL_ISUPPORTS(SurfaceMemoryReporter, nsIMemoryReporter)
507
508
void
509
gfxASurface::RecordMemoryUsedForSurfaceType(gfxSurfaceType aType,
510
                                            int32_t aBytes)
511
0
{
512
0
    if (int(aType) < 0 || aType >= gfxSurfaceType::Max) {
513
0
        NS_WARNING("Invalid type to RecordMemoryUsedForSurfaceType!");
514
0
        return;
515
0
    }
516
0
517
0
    static bool registered = false;
518
0
    if (!registered) {
519
0
        RegisterStrongMemoryReporter(new SurfaceMemoryReporter());
520
0
        registered = true;
521
0
    }
522
0
523
0
    SurfaceMemoryReporter::AdjustUsedMemory(aType, aBytes);
524
0
}
525
526
void
527
gfxASurface::RecordMemoryUsed(int32_t aBytes)
528
0
{
529
0
    RecordMemoryUsedForSurfaceType(GetType(), aBytes);
530
0
    mBytesRecorded += aBytes;
531
0
}
532
533
void
534
gfxASurface::RecordMemoryFreed()
535
0
{
536
0
    if (mBytesRecorded) {
537
0
        RecordMemoryUsedForSurfaceType(GetType(), -mBytesRecorded);
538
0
        mBytesRecorded = 0;
539
0
    }
540
0
}
541
542
size_t
543
gfxASurface::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
544
0
{
545
0
    // We don't measure mSurface because cairo doesn't allow it.
546
0
    return 0;
547
0
}
548
549
size_t
550
gfxASurface::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
551
0
{
552
0
    return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
553
0
}
554
555
/* static */ uint8_t
556
gfxASurface::BytesPerPixel(gfxImageFormat aImageFormat)
557
0
{
558
0
  switch (aImageFormat) {
559
0
    case SurfaceFormat::A8R8G8B8_UINT32:
560
0
      return 4;
561
0
    case SurfaceFormat::X8R8G8B8_UINT32:
562
0
      return 4;
563
0
    case SurfaceFormat::R5G6B5_UINT16:
564
0
      return 2;
565
0
    case SurfaceFormat::A8:
566
0
      return 1;
567
0
    case SurfaceFormat::UNKNOWN:
568
0
    default:
569
0
      MOZ_ASSERT_UNREACHABLE("Not really sure what you want me to say here");
570
0
      return 0;
571
0
  }
572
0
}
573
574
void
575
gfxASurface::SetOpaqueRect(const gfxRect& aRect)
576
0
{
577
0
    if (aRect.IsEmpty()) {
578
0
        mOpaqueRect = nullptr;
579
0
    } else if (!!mOpaqueRect) {
580
0
        *mOpaqueRect = aRect;
581
0
    } else {
582
0
        mOpaqueRect = MakeUnique<gfxRect>(aRect);
583
0
    }
584
0
}
585
586
/* static */const gfxRect&
587
gfxASurface::GetEmptyOpaqueRect()
588
0
{
589
0
  static const gfxRect empty(0, 0, 0, 0);
590
0
  return empty;
591
0
}
592
593
const IntSize
594
gfxASurface::GetSize() const
595
0
{
596
0
  return IntSize(-1, -1);
597
0
}
598
599
SurfaceFormat
600
gfxASurface::GetSurfaceFormat() const
601
0
{
602
0
    if (!mSurfaceValid) {
603
0
      return SurfaceFormat::UNKNOWN;
604
0
    }
605
0
    return GfxFormatForCairoSurface(mSurface);
606
0
}
607
608
already_AddRefed<gfxImageSurface>
609
gfxASurface::GetAsImageSurface()
610
0
{
611
0
  return nullptr;
612
0
}