Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/gfx/thebes/gfxImageSurface.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
7
#include "mozilla/MemoryReporting.h"
8
#if defined(HAVE_POSIX_MEMALIGN)
9
#include "gfxAlphaRecovery.h"
10
#endif
11
#include "gfxImageSurface.h"
12
13
#include "cairo.h"
14
#include "mozilla/gfx/2D.h"
15
#include "mozilla/gfx/HelpersCairo.h"
16
#include "gfx2DGlue.h"
17
#include <algorithm>
18
19
using namespace mozilla;
20
using namespace mozilla::gfx;
21
22
gfxImageSurface::gfxImageSurface()
23
  : mSize(0, 0),
24
    mOwnsData(false),
25
    mData(nullptr),
26
    mFormat(SurfaceFormat::UNKNOWN),
27
    mStride(0)
28
0
{
29
0
}
30
31
void
32
gfxImageSurface::InitFromSurface(cairo_surface_t *csurf)
33
0
{
34
0
    if (!csurf || cairo_surface_status(csurf)) {
35
0
        MakeInvalid();
36
0
        return;
37
0
    }
38
0
39
0
    mSize.width = cairo_image_surface_get_width(csurf);
40
0
    mSize.height = cairo_image_surface_get_height(csurf);
41
0
    mData = cairo_image_surface_get_data(csurf);
42
0
    mFormat = CairoFormatToGfxFormat(cairo_image_surface_get_format(csurf));
43
0
    mOwnsData = false;
44
0
    mStride = cairo_image_surface_get_stride(csurf);
45
0
46
0
    Init(csurf, true);
47
0
}
48
49
gfxImageSurface::gfxImageSurface(unsigned char *aData, const IntSize& aSize,
50
                                 long aStride, gfxImageFormat aFormat)
51
0
{
52
0
    InitWithData(aData, aSize, aStride, aFormat);
53
0
}
54
55
void
56
gfxImageSurface::MakeInvalid()
57
0
{
58
0
    mSize = IntSize(-1, -1);
59
0
    mData = nullptr;
60
0
    mStride = 0;
61
0
}
62
63
void
64
gfxImageSurface::InitWithData(unsigned char *aData, const IntSize& aSize,
65
                              long aStride, gfxImageFormat aFormat)
66
0
{
67
0
    mSize = aSize;
68
0
    mOwnsData = false;
69
0
    mData = aData;
70
0
    mFormat = aFormat;
71
0
    mStride = aStride;
72
0
73
0
    if (!Factory::CheckSurfaceSize(aSize))
74
0
        MakeInvalid();
75
0
76
0
    cairo_format_t cformat = GfxFormatToCairoFormat(mFormat);
77
0
    cairo_surface_t *surface =
78
0
        cairo_image_surface_create_for_data((unsigned char*)mData,
79
0
                                            cformat,
80
0
                                            mSize.width,
81
0
                                            mSize.height,
82
0
                                            mStride);
83
0
84
0
    // cairo_image_surface_create_for_data can return a 'null' surface
85
0
    // in out of memory conditions. The gfxASurface::Init call checks
86
0
    // the surface it receives to see if there is an error with the
87
0
    // surface and handles it appropriately. That is why there is
88
0
    // no check here.
89
0
    Init(surface);
90
0
}
91
92
static void*
93
TryAllocAlignedBytes(size_t aSize)
94
0
{
95
0
    // Use fallible allocators here
96
0
#if defined(HAVE_POSIX_MEMALIGN)
97
0
    void* ptr;
98
0
    // Try to align for fast alpha recovery.  This should only help
99
0
    // cairo too, can't hurt.
100
0
    return posix_memalign(&ptr,
101
0
                          1 << gfxAlphaRecovery::GoodAlignmentLog2(),
102
0
                          aSize) ?
103
0
             nullptr : ptr;
104
#else
105
    // Oh well, hope that luck is with us in the allocator
106
    return malloc(aSize);
107
#endif
108
}
109
110
gfxImageSurface::gfxImageSurface(const IntSize& size, gfxImageFormat format, bool aClear)
111
 : mSize(size), mData(nullptr), mFormat(format)
112
0
{
113
0
    AllocateAndInit(0, 0, aClear);
114
0
}
115
116
void 
117
gfxImageSurface::AllocateAndInit(long aStride, int32_t aMinimalAllocation,
118
                                 bool aClear)
119
0
{
120
0
    // The callers should set mSize and mFormat.
121
0
    MOZ_ASSERT(!mData);
122
0
    mData = nullptr;
123
0
    mOwnsData = false;
124
0
125
0
    mStride = aStride > 0 ? aStride : ComputeStride();
126
0
    if (aMinimalAllocation < mSize.height * mStride)
127
0
        aMinimalAllocation = mSize.height * mStride;
128
0
129
0
    if (!Factory::CheckSurfaceSize(mSize))
130
0
        MakeInvalid();
131
0
132
0
    // if we have a zero-sized surface, just leave mData nullptr
133
0
    if (mSize.height * mStride > 0) {
134
0
135
0
        // This can fail to allocate memory aligned as we requested,
136
0
        // or it can fail to allocate any memory at all.
137
0
        mData = (unsigned char *) TryAllocAlignedBytes(aMinimalAllocation);
138
0
        if (!mData)
139
0
            return;
140
0
        if (aClear)
141
0
            memset(mData, 0, aMinimalAllocation);
142
0
    }
143
0
144
0
    mOwnsData = true;
145
0
146
0
    cairo_format_t cformat = GfxFormatToCairoFormat(mFormat);
147
0
    cairo_surface_t *surface =
148
0
        cairo_image_surface_create_for_data((unsigned char*)mData,
149
0
                                            cformat,
150
0
                                            mSize.width,
151
0
                                            mSize.height,
152
0
                                            mStride);
153
0
154
0
    Init(surface);
155
0
156
0
    if (mSurfaceValid) {
157
0
        RecordMemoryUsed(mSize.height * ComputeStride() +
158
0
                         sizeof(gfxImageSurface));
159
0
    }
160
0
}
161
162
gfxImageSurface::gfxImageSurface(const IntSize& size, gfxImageFormat format,
163
                                 long aStride, int32_t aExtraBytes, bool aClear)
164
 : mSize(size), mData(nullptr), mFormat(format)
165
0
{
166
0
    AllocateAndInit(aStride, aExtraBytes, aClear);
167
0
}
168
169
gfxImageSurface::gfxImageSurface(cairo_surface_t *csurf)
170
0
{
171
0
    mSize.width = cairo_image_surface_get_width(csurf);
172
0
    mSize.height = cairo_image_surface_get_height(csurf);
173
0
    mData = cairo_image_surface_get_data(csurf);
174
0
    mFormat = CairoFormatToGfxFormat(cairo_image_surface_get_format(csurf));
175
0
    mOwnsData = false;
176
0
    mStride = cairo_image_surface_get_stride(csurf);
177
0
178
0
    Init(csurf, true);
179
0
}
180
181
gfxImageSurface::~gfxImageSurface()
182
0
{
183
0
    if (mOwnsData)
184
0
        free(mData);
185
0
}
186
187
/*static*/ long
188
gfxImageSurface::ComputeStride(const IntSize& aSize, gfxImageFormat aFormat)
189
0
{
190
0
    long stride;
191
0
192
0
    if (aFormat == SurfaceFormat::A8R8G8B8_UINT32)
193
0
        stride = aSize.width * 4;
194
0
    else if (aFormat == SurfaceFormat::X8R8G8B8_UINT32)
195
0
        stride = aSize.width * 4;
196
0
    else if (aFormat == SurfaceFormat::R5G6B5_UINT16)
197
0
        stride = aSize.width * 2;
198
0
    else if (aFormat == SurfaceFormat::A8)
199
0
        stride = aSize.width;
200
0
    else {
201
0
        NS_WARNING("Unknown format specified to gfxImageSurface!");
202
0
        stride = aSize.width * 4;
203
0
    }
204
0
205
0
    stride = ((stride + 3) / 4) * 4;
206
0
207
0
    return stride;
208
0
}
209
210
size_t
211
gfxImageSurface::SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
212
0
{
213
0
    size_t n = gfxASurface::SizeOfExcludingThis(aMallocSizeOf);
214
0
    if (mOwnsData) {
215
0
        n += aMallocSizeOf(mData);
216
0
    }
217
0
    return n;
218
0
}
219
220
size_t
221
gfxImageSurface::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
222
0
{
223
0
    return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
224
0
}
225
226
bool
227
gfxImageSurface::SizeOfIsMeasured() const
228
0
{
229
0
    return true;
230
0
}
231
232
// helper function for the CopyFrom methods
233
static void
234
CopyForStride(unsigned char* aDest, unsigned char* aSrc, const IntSize& aSize, long aDestStride, long aSrcStride)
235
0
{
236
0
    if (aDestStride == aSrcStride) {
237
0
        memcpy (aDest, aSrc, aSrcStride * aSize.height);
238
0
    } else {
239
0
        int lineSize = std::min(aDestStride, aSrcStride);
240
0
        for (int i = 0; i < aSize.height; i++) {
241
0
            unsigned char* src = aSrc + aSrcStride * i;
242
0
            unsigned char* dst = aDest + aDestStride * i;
243
0
244
0
            memcpy (dst, src, lineSize);
245
0
        }
246
0
    }
247
0
}
248
249
// helper function for the CopyFrom methods
250
static bool
251
FormatsAreCompatible(gfxImageFormat a1, gfxImageFormat a2)
252
0
{
253
0
    if (a1 != a2 &&
254
0
        !(a1 == SurfaceFormat::A8R8G8B8_UINT32 &&
255
0
          a2 == SurfaceFormat::X8R8G8B8_UINT32) &&
256
0
        !(a1 == SurfaceFormat::X8R8G8B8_UINT32 &&
257
0
          a2 == SurfaceFormat::A8R8G8B8_UINT32)) {
258
0
        return false;
259
0
    }
260
0
261
0
    return true;
262
0
}
263
264
bool
265
gfxImageSurface::CopyFrom (SourceSurface *aSurface)
266
0
{
267
0
    RefPtr<DataSourceSurface> data = aSurface->GetDataSurface();
268
0
269
0
    if (!data) {
270
0
        return false;
271
0
    }
272
0
273
0
    IntSize size(data->GetSize().width, data->GetSize().height);
274
0
    if (size != mSize) {
275
0
        return false;
276
0
    }
277
0
278
0
    if (!FormatsAreCompatible(SurfaceFormatToImageFormat(aSurface->GetFormat()),
279
0
                              mFormat)) {
280
0
        return false;
281
0
    }
282
0
283
0
    DataSourceSurface::ScopedMap map(data, DataSourceSurface::READ);
284
0
    CopyForStride(mData, map.GetData(), size, mStride, map.GetStride());
285
0
286
0
    return true;
287
0
}
288
289
290
bool
291
gfxImageSurface::CopyFrom(gfxImageSurface *other)
292
0
{
293
0
    if (other->mSize != mSize) {
294
0
        return false;
295
0
    }
296
0
297
0
    if (!FormatsAreCompatible(other->mFormat, mFormat)) {
298
0
        return false;
299
0
    }
300
0
301
0
    CopyForStride(mData, other->mData, mSize, mStride, other->mStride);
302
0
303
0
    return true;
304
0
}
305
306
bool
307
0
gfxImageSurface::CopyTo(SourceSurface *aSurface) {
308
0
    RefPtr<DataSourceSurface> data = aSurface->GetDataSurface();
309
0
310
0
    if (!data) {
311
0
        return false;
312
0
    }
313
0
314
0
    IntSize size(data->GetSize().width, data->GetSize().height);
315
0
    if (size != mSize) {
316
0
        return false;
317
0
    }
318
0
319
0
    if (!FormatsAreCompatible(SurfaceFormatToImageFormat(aSurface->GetFormat()),
320
0
                              mFormat)) {
321
0
        return false;
322
0
    }
323
0
324
0
    DataSourceSurface::ScopedMap map(data, DataSourceSurface::READ_WRITE);
325
0
    CopyForStride(map.GetData(), mData, size, map.GetStride(), mStride);
326
0
327
0
    return true;
328
0
}
329
330
already_AddRefed<DataSourceSurface>
331
gfxImageSurface::CopyToB8G8R8A8DataSourceSurface()
332
0
{
333
0
  RefPtr<DataSourceSurface> dataSurface =
334
0
    Factory::CreateDataSourceSurface(IntSize(GetSize().width, GetSize().height),
335
0
                                     SurfaceFormat::B8G8R8A8);
336
0
  if (dataSurface) {
337
0
    CopyTo(dataSurface);
338
0
  }
339
0
  return dataSurface.forget();
340
0
}
341
342
already_AddRefed<gfxSubimageSurface>
343
gfxImageSurface::GetSubimage(const gfxRect& aRect)
344
0
{
345
0
    gfxRect r(aRect);
346
0
    r.Round();
347
0
    MOZ_ASSERT(gfxRect(0, 0, mSize.width, mSize.height).Contains(r));
348
0
349
0
    gfxImageFormat format = Format();
350
0
351
0
    unsigned char* subData = Data() +
352
0
        (Stride() * (int)r.Y()) +
353
0
        (int)r.X() * gfxASurface::BytePerPixelFromFormat(Format());
354
0
355
0
    if (format == SurfaceFormat::A8R8G8B8_UINT32 &&
356
0
        GetOpaqueRect().Contains(aRect)) {
357
0
        format = SurfaceFormat::X8R8G8B8_UINT32;
358
0
    }
359
0
360
0
    RefPtr<gfxSubimageSurface> image =
361
0
        new gfxSubimageSurface(this, subData,
362
0
                               IntSize((int)r.Width(), (int)r.Height()),
363
0
                               format);
364
0
365
0
    return image.forget();
366
0
}
367
368
gfxSubimageSurface::gfxSubimageSurface(gfxImageSurface* aParent,
369
                                       unsigned char* aData,
370
                                       const IntSize& aSize,
371
                                       gfxImageFormat aFormat)
372
  : gfxImageSurface(aData, aSize, aParent->Stride(), aFormat)
373
  , mParent(aParent)
374
0
{
375
0
}
376
377
already_AddRefed<gfxImageSurface>
378
gfxImageSurface::GetAsImageSurface()
379
0
{
380
0
  RefPtr<gfxImageSurface> surface = this;
381
0
  return surface.forget();
382
0
}