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