|
1 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- |
|
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 "2D.h" |
|
7 #include "DataSurfaceHelpers.h" |
|
8 #include "Logging.h" |
|
9 #include "mozilla/CheckedInt.h" |
|
10 #include "mozilla/MathAlgorithms.h" |
|
11 |
|
12 namespace mozilla { |
|
13 namespace gfx { |
|
14 |
|
15 void |
|
16 ConvertBGRXToBGRA(uint8_t* aData, const IntSize &aSize, int32_t aStride) |
|
17 { |
|
18 uint32_t* pixel = reinterpret_cast<uint32_t*>(aData); |
|
19 |
|
20 for (int row = 0; row < aSize.height; ++row) { |
|
21 for (int column = 0; column < aSize.width; ++column) { |
|
22 #ifdef IS_BIG_ENDIAN |
|
23 pixel[column] |= 0x000000FF; |
|
24 #else |
|
25 pixel[column] |= 0xFF000000; |
|
26 #endif |
|
27 } |
|
28 pixel += (aStride/4); |
|
29 } |
|
30 } |
|
31 |
|
32 void |
|
33 CopySurfaceDataToPackedArray(uint8_t* aSrc, uint8_t* aDst, IntSize aSrcSize, |
|
34 int32_t aSrcStride, int32_t aBytesPerPixel) |
|
35 { |
|
36 MOZ_ASSERT(aBytesPerPixel > 0, |
|
37 "Negative stride for aDst not currently supported"); |
|
38 MOZ_ASSERT(BufferSizeFromStrideAndHeight(aSrcStride, aSrcSize.height) > 0, |
|
39 "How did we end up with a surface with such a big buffer?"); |
|
40 |
|
41 int packedStride = aSrcSize.width * aBytesPerPixel; |
|
42 |
|
43 if (aSrcStride == packedStride) { |
|
44 // aSrc is already packed, so we can copy with a single memcpy. |
|
45 memcpy(aDst, aSrc, packedStride * aSrcSize.height); |
|
46 } else { |
|
47 // memcpy one row at a time. |
|
48 for (int row = 0; row < aSrcSize.height; ++row) { |
|
49 memcpy(aDst, aSrc, packedStride); |
|
50 aSrc += aSrcStride; |
|
51 aDst += packedStride; |
|
52 } |
|
53 } |
|
54 } |
|
55 |
|
56 void |
|
57 CopyBGRXSurfaceDataToPackedBGRArray(uint8_t* aSrc, uint8_t* aDst, |
|
58 IntSize aSrcSize, int32_t aSrcStride) |
|
59 { |
|
60 int packedStride = aSrcSize.width * 3; |
|
61 |
|
62 uint8_t* srcPx = aSrc; |
|
63 uint8_t* dstPx = aDst; |
|
64 |
|
65 for (int row = 0; row < aSrcSize.height; ++row) { |
|
66 for (int col = 0; col < aSrcSize.height; ++col) { |
|
67 dstPx[0] = srcPx[0]; |
|
68 dstPx[1] = srcPx[1]; |
|
69 dstPx[2] = srcPx[2]; |
|
70 // srcPx[3] (unused or alpha component) dropped on floor |
|
71 srcPx += 4; |
|
72 dstPx += 3; |
|
73 } |
|
74 srcPx = aSrc += aSrcStride; |
|
75 dstPx = aDst += packedStride; |
|
76 } |
|
77 } |
|
78 |
|
79 uint8_t* |
|
80 SurfaceToPackedBGRA(DataSourceSurface *aSurface) |
|
81 { |
|
82 SurfaceFormat format = aSurface->GetFormat(); |
|
83 if (format != SurfaceFormat::B8G8R8A8 && format != SurfaceFormat::B8G8R8X8) { |
|
84 return nullptr; |
|
85 } |
|
86 |
|
87 IntSize size = aSurface->GetSize(); |
|
88 |
|
89 uint8_t* imageBuffer = new (std::nothrow) uint8_t[size.width * size.height * sizeof(uint32_t)]; |
|
90 if (!imageBuffer) { |
|
91 return nullptr; |
|
92 } |
|
93 |
|
94 DataSourceSurface::MappedSurface map; |
|
95 if (!aSurface->Map(DataSourceSurface::MapType::READ, &map)) { |
|
96 delete [] imageBuffer; |
|
97 return nullptr; |
|
98 } |
|
99 |
|
100 CopySurfaceDataToPackedArray(map.mData, imageBuffer, size, |
|
101 map.mStride, 4 * sizeof(uint8_t)); |
|
102 |
|
103 aSurface->Unmap(); |
|
104 |
|
105 if (format == SurfaceFormat::B8G8R8X8) { |
|
106 // Convert BGRX to BGRA by setting a to 255. |
|
107 ConvertBGRXToBGRA(reinterpret_cast<uint8_t *>(imageBuffer), size, size.width * sizeof(uint32_t)); |
|
108 } |
|
109 |
|
110 return imageBuffer; |
|
111 } |
|
112 |
|
113 uint8_t* |
|
114 SurfaceToPackedBGR(DataSourceSurface *aSurface) |
|
115 { |
|
116 SurfaceFormat format = aSurface->GetFormat(); |
|
117 MOZ_ASSERT(format == SurfaceFormat::B8G8R8X8, "Format not supported"); |
|
118 |
|
119 if (format != SurfaceFormat::B8G8R8X8) { |
|
120 // To support B8G8R8A8 we'd need to un-pre-multiply alpha |
|
121 return nullptr; |
|
122 } |
|
123 |
|
124 IntSize size = aSurface->GetSize(); |
|
125 |
|
126 uint8_t* imageBuffer = new (std::nothrow) uint8_t[size.width * size.height * 3 * sizeof(uint8_t)]; |
|
127 if (!imageBuffer) { |
|
128 return nullptr; |
|
129 } |
|
130 |
|
131 DataSourceSurface::MappedSurface map; |
|
132 if (!aSurface->Map(DataSourceSurface::MapType::READ, &map)) { |
|
133 delete [] imageBuffer; |
|
134 return nullptr; |
|
135 } |
|
136 |
|
137 CopyBGRXSurfaceDataToPackedBGRArray(map.mData, imageBuffer, size, |
|
138 map.mStride); |
|
139 |
|
140 aSurface->Unmap(); |
|
141 |
|
142 return imageBuffer; |
|
143 } |
|
144 |
|
145 size_t |
|
146 BufferSizeFromStrideAndHeight(int32_t aStride, |
|
147 int32_t aHeight, |
|
148 int32_t aExtraBytes) |
|
149 { |
|
150 if (MOZ_UNLIKELY(aHeight <= 0)) { |
|
151 return 0; |
|
152 } |
|
153 |
|
154 // We limit the length returned to values that can be represented by int32_t |
|
155 // because we don't want to allocate buffers any bigger than that. This |
|
156 // allows for a buffer size of over 2 GiB which is already rediculously |
|
157 // large and will make the process janky. (Note the choice of the signed type |
|
158 // is deliberate because we specifically don't want the returned value to |
|
159 // overflow if someone stores the buffer length in an int32_t variable.) |
|
160 |
|
161 CheckedInt32 requiredBytes = |
|
162 CheckedInt32(aStride) * CheckedInt32(aHeight) + CheckedInt32(aExtraBytes); |
|
163 if (MOZ_UNLIKELY(!requiredBytes.isValid())) { |
|
164 gfxWarning() << "Buffer size too big; returning zero"; |
|
165 return 0; |
|
166 } |
|
167 return requiredBytes.value(); |
|
168 } |
|
169 |
|
170 } |
|
171 } |