Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/gfx/2d/DataSurfaceHelpers.cpp
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
3
/* This Source Code Form is subject to the terms of the Mozilla Public
4
 * License, v. 2.0. If a copy of the MPL was not distributed with this
5
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7
#include <cstring>
8
9
#include "2D.h"
10
#include "DataSurfaceHelpers.h"
11
#include "Logging.h"
12
#include "mozilla/MathAlgorithms.h"
13
#include "mozilla/PodOperations.h"
14
#include "Swizzle.h"
15
#include "Tools.h"
16
17
namespace mozilla {
18
namespace gfx {
19
20
int32_t
21
StrideForFormatAndWidth(SurfaceFormat aFormat, int32_t aWidth)
22
0
{
23
0
  MOZ_ASSERT(aFormat <= SurfaceFormat::UNKNOWN);
24
0
  MOZ_ASSERT(aWidth > 0);
25
0
26
0
  // There's nothing special about this alignment, other than that it's what
27
0
  // cairo_format_stride_for_width uses.
28
0
  static const int32_t alignment = sizeof(int32_t);
29
0
30
0
  const int32_t bpp = BytesPerPixel(aFormat);
31
0
32
0
  if (aWidth >= (INT32_MAX - alignment) / bpp) {
33
0
    return -1; // too big
34
0
  }
35
0
36
0
  return (bpp * aWidth + alignment-1) & ~(alignment-1);
37
0
}
38
39
already_AddRefed<DataSourceSurface>
40
CreateDataSourceSurfaceFromData(const IntSize& aSize,
41
                                SurfaceFormat aFormat,
42
                                const uint8_t* aData,
43
                                int32_t aDataStride)
44
0
{
45
0
  RefPtr<DataSourceSurface> srcSurface =
46
0
      Factory::CreateWrappingDataSourceSurface(const_cast<uint8_t*>(aData),
47
0
                                               aDataStride,
48
0
                                               aSize,
49
0
                                               aFormat);
50
0
  RefPtr<DataSourceSurface> destSurface =
51
0
      Factory::CreateDataSourceSurface(aSize, aFormat, false);
52
0
53
0
  if (!srcSurface || !destSurface) {
54
0
    return nullptr;
55
0
  }
56
0
57
0
  if (CopyRect(srcSurface,
58
0
               destSurface,
59
0
               IntRect(IntPoint(), srcSurface->GetSize()),
60
0
               IntPoint())) {
61
0
    return destSurface.forget();
62
0
  }
63
0
64
0
  return nullptr;
65
0
}
66
67
already_AddRefed<DataSourceSurface>
68
CreateDataSourceSurfaceWithStrideFromData(const IntSize &aSize,
69
                                          SurfaceFormat aFormat,
70
                                          int32_t aStride,
71
                                          const uint8_t* aData,
72
                                          int32_t aDataStride)
73
0
{
74
0
  RefPtr<DataSourceSurface> srcSurface =
75
0
      Factory::CreateWrappingDataSourceSurface(const_cast<uint8_t*>(aData),
76
0
                                               aDataStride,
77
0
                                               aSize,
78
0
                                               aFormat);
79
0
  RefPtr<DataSourceSurface> destSurface =
80
0
      Factory::CreateDataSourceSurfaceWithStride(aSize, aFormat, aStride, false);
81
0
82
0
  if (!srcSurface || !destSurface) {
83
0
    return nullptr;
84
0
  }
85
0
86
0
  if (CopyRect(srcSurface,
87
0
               destSurface,
88
0
               IntRect(IntPoint(), srcSurface->GetSize()),
89
0
               IntPoint())) {
90
0
    return destSurface.forget();
91
0
  }
92
0
93
0
  return nullptr;
94
0
}
95
96
uint8_t*
97
DataAtOffset(DataSourceSurface* aSurface,
98
             const DataSourceSurface::MappedSurface* aMap,
99
             IntPoint aPoint)
100
0
{
101
0
  if (!SurfaceContainsPoint(aSurface, aPoint)) {
102
0
    MOZ_CRASH("GFX: sample position needs to be inside surface!");
103
0
  }
104
0
105
0
  MOZ_ASSERT(Factory::CheckSurfaceSize(aSurface->GetSize()),
106
0
             "surface size overflows - this should have been prevented when the surface was created");
107
0
108
0
  uint8_t* data = aMap->mData + aPoint.y * aMap->mStride +
109
0
    aPoint.x * BytesPerPixel(aSurface->GetFormat());
110
0
111
0
  if (data < aMap->mData) {
112
0
    MOZ_CRASH("GFX: out-of-range data access");
113
0
  }
114
0
115
0
  return data;
116
0
}
117
118
// This check is safe against integer overflow.
119
bool
120
SurfaceContainsPoint(SourceSurface* aSurface, const IntPoint& aPoint)
121
0
{
122
0
  IntSize size = aSurface->GetSize();
123
0
  return aPoint.x >= 0 && aPoint.x < size.width &&
124
0
         aPoint.y >= 0 && aPoint.y < size.height;
125
0
}
126
127
void
128
CopySurfaceDataToPackedArray(uint8_t* aSrc, uint8_t* aDst, IntSize aSrcSize,
129
                             int32_t aSrcStride, int32_t aBytesPerPixel)
130
0
{
131
0
  MOZ_ASSERT(aBytesPerPixel > 0,
132
0
             "Negative stride for aDst not currently supported");
133
0
  MOZ_ASSERT(BufferSizeFromStrideAndHeight(aSrcStride, aSrcSize.height) > 0,
134
0
             "How did we end up with a surface with such a big buffer?");
135
0
136
0
  int packedStride = aSrcSize.width * aBytesPerPixel;
137
0
138
0
  if (aSrcStride == packedStride) {
139
0
    // aSrc is already packed, so we can copy with a single memcpy.
140
0
    memcpy(aDst, aSrc, packedStride * aSrcSize.height);
141
0
  } else {
142
0
    // memcpy one row at a time.
143
0
    for (int row = 0; row < aSrcSize.height; ++row) {
144
0
      memcpy(aDst, aSrc, packedStride);
145
0
      aSrc += aSrcStride;
146
0
      aDst += packedStride;
147
0
    }
148
0
  }
149
0
}
150
151
UniquePtr<uint8_t[]>
152
SurfaceToPackedBGRA(DataSourceSurface *aSurface)
153
0
{
154
0
  SurfaceFormat format = aSurface->GetFormat();
155
0
  if (format != SurfaceFormat::B8G8R8A8 && format != SurfaceFormat::B8G8R8X8) {
156
0
    return nullptr;
157
0
  }
158
0
159
0
  IntSize size = aSurface->GetSize();
160
0
  if (size.width < 0 || size.width >= INT32_MAX / 4) {
161
0
    return nullptr;
162
0
  }
163
0
  int32_t stride = size.width * 4;
164
0
  CheckedInt<size_t> bufferSize =
165
0
    CheckedInt<size_t>(stride) * CheckedInt<size_t>(size.height);
166
0
  if (!bufferSize.isValid()) {
167
0
    return nullptr;
168
0
  }
169
0
  UniquePtr<uint8_t[]> imageBuffer(new (std::nothrow) uint8_t[bufferSize.value()]);
170
0
  if (!imageBuffer) {
171
0
    return nullptr;
172
0
  }
173
0
174
0
  DataSourceSurface::MappedSurface map;
175
0
  if (!aSurface->Map(DataSourceSurface::MapType::READ, &map)) {
176
0
    return nullptr;
177
0
  }
178
0
179
0
  CopySurfaceDataToPackedArray(map.mData, imageBuffer.get(), size,
180
0
                               map.mStride, 4);
181
0
182
0
  aSurface->Unmap();
183
0
184
0
  if (format == SurfaceFormat::B8G8R8X8) {
185
0
    // Convert BGRX to BGRA by setting a to 255.
186
0
    SwizzleData(imageBuffer.get(), stride, SurfaceFormat::X8R8G8B8_UINT32,
187
0
                imageBuffer.get(), stride, SurfaceFormat::A8R8G8B8_UINT32,
188
0
                size);
189
0
  }
190
0
191
0
  return imageBuffer;
192
0
}
193
194
uint8_t*
195
SurfaceToPackedBGR(DataSourceSurface *aSurface)
196
0
{
197
0
  SurfaceFormat format = aSurface->GetFormat();
198
0
  MOZ_ASSERT(format == SurfaceFormat::B8G8R8X8, "Format not supported");
199
0
200
0
  if (format != SurfaceFormat::B8G8R8X8) {
201
0
    // To support B8G8R8A8 we'd need to un-pre-multiply alpha
202
0
    return nullptr;
203
0
  }
204
0
205
0
  IntSize size = aSurface->GetSize();
206
0
  if (size.width < 0 || size.width >= INT32_MAX / 3) {
207
0
    return nullptr;
208
0
  }
209
0
  int32_t stride = size.width * 3;
210
0
  CheckedInt<size_t> bufferSize =
211
0
    CheckedInt<size_t>(stride) * CheckedInt<size_t>(size.height);
212
0
  if (!bufferSize.isValid()) {
213
0
    return nullptr;
214
0
  }
215
0
  uint8_t* imageBuffer = new (std::nothrow) uint8_t[bufferSize.value()];
216
0
  if (!imageBuffer) {
217
0
    return nullptr;
218
0
  }
219
0
220
0
  DataSourceSurface::MappedSurface map;
221
0
  if (!aSurface->Map(DataSourceSurface::MapType::READ, &map)) {
222
0
    delete [] imageBuffer;
223
0
    return nullptr;
224
0
  }
225
0
226
0
  SwizzleData(map.mData, map.mStride, SurfaceFormat::B8G8R8X8,
227
0
              imageBuffer, stride, SurfaceFormat::B8G8R8,
228
0
              size);
229
0
230
0
  aSurface->Unmap();
231
0
232
0
  return imageBuffer;
233
0
}
234
235
void
236
ClearDataSourceSurface(DataSourceSurface *aSurface)
237
0
{
238
0
  DataSourceSurface::MappedSurface map;
239
0
  if (!aSurface->Map(DataSourceSurface::MapType::WRITE, &map)) {
240
0
    MOZ_ASSERT(false, "Failed to map DataSourceSurface");
241
0
    return;
242
0
  }
243
0
244
0
  // We avoid writing into the gaps between the rows here since we can't be
245
0
  // sure that some drivers don't use those bytes.
246
0
247
0
  uint32_t width = aSurface->GetSize().width;
248
0
  uint32_t bytesPerRow = width * BytesPerPixel(aSurface->GetFormat());
249
0
  uint8_t* row = map.mData;
250
0
  // converting to size_t here because otherwise the temporaries can overflow
251
0
  // and we can end up with |end| being a bad address!
252
0
  uint8_t* end = row + size_t(map.mStride) * size_t(aSurface->GetSize().height);
253
0
254
0
  while (row != end) {
255
0
    memset(row, 0, bytesPerRow);
256
0
    row += map.mStride;
257
0
  }
258
0
259
0
  aSurface->Unmap();
260
0
}
261
262
size_t
263
BufferSizeFromStrideAndHeight(int32_t aStride,
264
                              int32_t aHeight,
265
                              int32_t aExtraBytes)
266
0
{
267
0
  if (MOZ_UNLIKELY(aHeight <= 0) || MOZ_UNLIKELY(aStride <= 0)) {
268
0
    return 0;
269
0
  }
270
0
271
0
  // We limit the length returned to values that can be represented by int32_t
272
0
  // because we don't want to allocate buffers any bigger than that. This
273
0
  // allows for a buffer size of over 2 GiB which is already rediculously
274
0
  // large and will make the process janky. (Note the choice of the signed type
275
0
  // is deliberate because we specifically don't want the returned value to
276
0
  // overflow if someone stores the buffer length in an int32_t variable.)
277
0
278
0
  CheckedInt32 requiredBytes =
279
0
    CheckedInt32(aStride) * CheckedInt32(aHeight) + CheckedInt32(aExtraBytes);
280
0
  if (MOZ_UNLIKELY(!requiredBytes.isValid())) {
281
0
    gfxWarning() << "Buffer size too big; returning zero " << aStride << ", " << aHeight << ", " << aExtraBytes;
282
0
    return 0;
283
0
  }
284
0
  return requiredBytes.value();
285
0
}
286
287
size_t
288
BufferSizeFromDimensions(int32_t aWidth,
289
                         int32_t aHeight,
290
                         int32_t aDepth,
291
                         int32_t aExtraBytes)
292
0
{
293
0
  if (MOZ_UNLIKELY(aHeight <= 0) ||
294
0
      MOZ_UNLIKELY(aWidth <= 0) ||
295
0
      MOZ_UNLIKELY(aDepth <= 0)) {
296
0
    return 0;
297
0
  }
298
0
299
0
  // Similar to BufferSizeFromStrideAndHeight, but with an extra parameter.
300
0
301
0
  CheckedInt32 requiredBytes = CheckedInt32(aWidth) * CheckedInt32(aHeight) * CheckedInt32(aDepth) + CheckedInt32(aExtraBytes);
302
0
  if (MOZ_UNLIKELY(!requiredBytes.isValid())) {
303
0
    gfxWarning() << "Buffer size too big; returning zero " << aWidth << ", " << aHeight << ", " << aDepth << ", " << aExtraBytes;
304
0
    return 0;
305
0
  }
306
0
  return requiredBytes.value();
307
0
}
308
309
/**
310
 * aSrcRect: Rect relative to the aSrc surface
311
 * aDestPoint: Point inside aDest surface
312
 */
313
bool
314
CopyRect(DataSourceSurface* aSrc, DataSourceSurface* aDest,
315
         IntRect aSrcRect, IntPoint aDestPoint)
316
0
{
317
0
  if (aSrcRect.Overflows() ||
318
0
      IntRect(aDestPoint, aSrcRect.Size()).Overflows()) {
319
0
    MOZ_CRASH("GFX: we should never be getting invalid rects at this point");
320
0
  }
321
0
322
0
  MOZ_RELEASE_ASSERT(aSrc->GetFormat() == aDest->GetFormat(),
323
0
                     "GFX: different surface formats");
324
0
  MOZ_RELEASE_ASSERT(IntRect(IntPoint(), aSrc->GetSize()).Contains(aSrcRect),
325
0
                     "GFX: source rect too big for source surface");
326
0
  MOZ_RELEASE_ASSERT(IntRect(IntPoint(), aDest->GetSize()).Contains(IntRect(aDestPoint, aSrcRect.Size())),
327
0
                     "GFX: dest surface too small");
328
0
329
0
  if (aSrcRect.IsEmpty()) {
330
0
    return false;
331
0
  }
332
0
333
0
  DataSourceSurface::ScopedMap srcMap(aSrc, DataSourceSurface::READ);
334
0
  DataSourceSurface::ScopedMap destMap(aDest, DataSourceSurface::WRITE);
335
0
  if (MOZ2D_WARN_IF(!srcMap.IsMapped() || !destMap.IsMapped())) {
336
0
    return false;
337
0
  }
338
0
339
0
  uint8_t* sourceData = DataAtOffset(aSrc, srcMap.GetMappedSurface(), aSrcRect.TopLeft());
340
0
  uint8_t* destData = DataAtOffset(aDest, destMap.GetMappedSurface(), aDestPoint);
341
0
342
0
  SwizzleData(sourceData, srcMap.GetStride(), aSrc->GetFormat(),
343
0
              destData, destMap.GetStride(), aDest->GetFormat(),
344
0
              aSrcRect.Size());
345
0
346
0
  return true;
347
0
}
348
349
already_AddRefed<DataSourceSurface>
350
CreateDataSourceSurfaceByCloning(DataSourceSurface* aSource)
351
0
{
352
0
  RefPtr<DataSourceSurface> copy =
353
0
    Factory::CreateDataSourceSurface(aSource->GetSize(), aSource->GetFormat(), true);
354
0
  if (copy) {
355
0
    CopyRect(aSource, copy, IntRect(IntPoint(), aSource->GetSize()), IntPoint());
356
0
  }
357
0
  return copy.forget();
358
0
}
359
360
} // namespace gfx
361
} // namespace mozilla