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/. */
10 #include "DataSurfaceHelpers.h"
12 #include "mozilla/MathAlgorithms.h"
13 #include "mozilla/PodOperations.h"
20 int32_t StrideForFormatAndWidth(SurfaceFormat aFormat
, int32_t aWidth
) {
21 MOZ_ASSERT(aFormat
<= SurfaceFormat::UNKNOWN
);
22 MOZ_ASSERT(aWidth
> 0);
24 // There's nothing special about this alignment, other than that it's what
25 // cairo_format_stride_for_width uses.
26 static const int32_t alignment
= sizeof(int32_t);
28 const int32_t bpp
= BytesPerPixel(aFormat
);
30 if (aWidth
>= (INT32_MAX
- alignment
) / bpp
) {
34 return (bpp
* aWidth
+ alignment
- 1) & ~(alignment
- 1);
37 already_AddRefed
<DataSourceSurface
> CreateDataSourceSurfaceFromData(
38 const IntSize
& aSize
, SurfaceFormat aFormat
, const uint8_t* aData
,
39 int32_t aDataStride
) {
40 RefPtr
<DataSourceSurface
> srcSurface
=
41 Factory::CreateWrappingDataSourceSurface(const_cast<uint8_t*>(aData
),
42 aDataStride
, aSize
, aFormat
);
43 RefPtr
<DataSourceSurface
> destSurface
=
44 Factory::CreateDataSourceSurface(aSize
, aFormat
, false);
46 if (!srcSurface
|| !destSurface
) {
50 if (CopyRect(srcSurface
, destSurface
,
51 IntRect(IntPoint(), srcSurface
->GetSize()), IntPoint())) {
52 return destSurface
.forget();
58 already_AddRefed
<DataSourceSurface
> CreateDataSourceSurfaceWithStrideFromData(
59 const IntSize
& aSize
, SurfaceFormat aFormat
, int32_t aStride
,
60 const uint8_t* aData
, int32_t aDataStride
) {
61 RefPtr
<DataSourceSurface
> srcSurface
=
62 Factory::CreateWrappingDataSourceSurface(const_cast<uint8_t*>(aData
),
63 aDataStride
, aSize
, aFormat
);
64 RefPtr
<DataSourceSurface
> destSurface
=
65 Factory::CreateDataSourceSurfaceWithStride(aSize
, aFormat
, aStride
,
68 if (!srcSurface
|| !destSurface
) {
72 if (CopyRect(srcSurface
, destSurface
,
73 IntRect(IntPoint(), srcSurface
->GetSize()), IntPoint())) {
74 return destSurface
.forget();
80 uint8_t* DataAtOffset(DataSourceSurface
* aSurface
,
81 const DataSourceSurface::MappedSurface
* aMap
,
83 if (!SurfaceContainsPoint(aSurface
, aPoint
)) {
84 MOZ_CRASH("GFX: sample position needs to be inside surface!");
87 MOZ_ASSERT(Factory::CheckSurfaceSize(aSurface
->GetSize()),
88 "surface size overflows - this should have been prevented when "
89 "the surface was created");
92 aMap
->mData
+ size_t(aPoint
.y
) * size_t(aMap
->mStride
) +
93 size_t(aPoint
.x
) * size_t(BytesPerPixel(aSurface
->GetFormat()));
95 if (data
< aMap
->mData
) {
96 MOZ_CRASH("GFX: out-of-range data access");
102 // This check is safe against integer overflow.
103 bool SurfaceContainsPoint(SourceSurface
* aSurface
, const IntPoint
& aPoint
) {
104 IntSize size
= aSurface
->GetSize();
105 return aPoint
.x
>= 0 && aPoint
.x
< size
.width
&& aPoint
.y
>= 0 &&
106 aPoint
.y
< size
.height
;
109 void CopySurfaceDataToPackedArray(uint8_t* aSrc
, uint8_t* aDst
,
110 IntSize aSrcSize
, int32_t aSrcStride
,
111 int32_t aBytesPerPixel
) {
112 CheckedInt
<size_t> packedStride(aBytesPerPixel
);
113 packedStride
*= aSrcSize
.width
;
114 if (!packedStride
.isValid()) {
115 MOZ_ASSERT(false, "Invalid stride");
119 CheckedInt
<size_t> totalSize(aSrcStride
);
120 totalSize
*= aSrcSize
.height
;
121 if (!totalSize
.isValid()) {
122 MOZ_ASSERT(false, "Invalid surface size");
126 if (size_t(aSrcStride
) == packedStride
.value()) {
127 // aSrc is already packed, so we can copy with a single memcpy.
128 memcpy(aDst
, aSrc
, totalSize
.value());
130 // memcpy one row at a time.
131 for (int row
= 0; row
< aSrcSize
.height
; ++row
) {
132 memcpy(aDst
, aSrc
, packedStride
.value());
134 aDst
+= packedStride
.value();
139 UniquePtr
<uint8_t[]> SurfaceToPackedBGRA(DataSourceSurface
* aSurface
) {
140 SurfaceFormat format
= aSurface
->GetFormat();
141 if (format
!= SurfaceFormat::B8G8R8A8
&& format
!= SurfaceFormat::B8G8R8X8
) {
145 IntSize size
= aSurface
->GetSize();
146 if (size
.width
< 0 || size
.width
>= INT32_MAX
/ 4) {
149 int32_t stride
= size
.width
* 4;
150 CheckedInt
<size_t> bufferSize
=
151 CheckedInt
<size_t>(stride
) * CheckedInt
<size_t>(size
.height
);
152 if (!bufferSize
.isValid()) {
155 UniquePtr
<uint8_t[]> imageBuffer(new (std::nothrow
)
156 uint8_t[bufferSize
.value()]);
161 DataSourceSurface::MappedSurface map
;
162 if (!aSurface
->Map(DataSourceSurface::MapType::READ
, &map
)) {
166 CopySurfaceDataToPackedArray(map
.mData
, imageBuffer
.get(), size
, map
.mStride
,
171 if (format
== SurfaceFormat::B8G8R8X8
) {
172 // Convert BGRX to BGRA by setting a to 255.
173 SwizzleData(imageBuffer
.get(), stride
, SurfaceFormat::X8R8G8B8_UINT32
,
174 imageBuffer
.get(), stride
, SurfaceFormat::A8R8G8B8_UINT32
,
181 uint8_t* SurfaceToPackedBGR(DataSourceSurface
* aSurface
) {
182 SurfaceFormat format
= aSurface
->GetFormat();
183 MOZ_ASSERT(format
== SurfaceFormat::B8G8R8X8
, "Format not supported");
185 if (format
!= SurfaceFormat::B8G8R8X8
) {
186 // To support B8G8R8A8 we'd need to un-pre-multiply alpha
190 IntSize size
= aSurface
->GetSize();
191 if (size
.width
< 0 || size
.width
>= INT32_MAX
/ 3) {
194 int32_t stride
= size
.width
* 3;
195 CheckedInt
<size_t> bufferSize
=
196 CheckedInt
<size_t>(stride
) * CheckedInt
<size_t>(size
.height
);
197 if (!bufferSize
.isValid()) {
200 uint8_t* imageBuffer
= new (std::nothrow
) uint8_t[bufferSize
.value()];
205 DataSourceSurface::MappedSurface map
;
206 if (!aSurface
->Map(DataSourceSurface::MapType::READ
, &map
)) {
207 delete[] imageBuffer
;
211 SwizzleData(map
.mData
, map
.mStride
, SurfaceFormat::B8G8R8X8
, imageBuffer
,
212 stride
, SurfaceFormat::B8G8R8
, size
);
219 void ClearDataSourceSurface(DataSourceSurface
* aSurface
) {
220 DataSourceSurface::MappedSurface map
;
221 if (!aSurface
->Map(DataSourceSurface::MapType::WRITE
, &map
)) {
222 MOZ_ASSERT(false, "Failed to map DataSourceSurface");
226 // We avoid writing into the gaps between the rows here since we can't be
227 // sure that some drivers don't use those bytes.
229 uint32_t width
= aSurface
->GetSize().width
;
230 uint32_t bytesPerRow
= width
* BytesPerPixel(aSurface
->GetFormat());
231 uint8_t* row
= map
.mData
;
232 // converting to size_t here because otherwise the temporaries can overflow
233 // and we can end up with |end| being a bad address!
234 uint8_t* end
= row
+ size_t(map
.mStride
) * size_t(aSurface
->GetSize().height
);
237 memset(row
, 0, bytesPerRow
);
244 size_t BufferSizeFromStrideAndHeight(int32_t aStride
, int32_t aHeight
,
245 int32_t aExtraBytes
) {
246 if (MOZ_UNLIKELY(aHeight
<= 0) || MOZ_UNLIKELY(aStride
<= 0)) {
250 // We limit the length returned to values that can be represented by int32_t
251 // because we don't want to allocate buffers any bigger than that. This
252 // allows for a buffer size of over 2 GiB which is already rediculously
253 // large and will make the process janky. (Note the choice of the signed type
254 // is deliberate because we specifically don't want the returned value to
255 // overflow if someone stores the buffer length in an int32_t variable.)
257 CheckedInt32 requiredBytes
=
258 CheckedInt32(aStride
) * CheckedInt32(aHeight
) + CheckedInt32(aExtraBytes
);
259 if (MOZ_UNLIKELY(!requiredBytes
.isValid())) {
260 gfxWarning() << "Buffer size too big; returning zero " << aStride
<< ", "
261 << aHeight
<< ", " << aExtraBytes
;
264 return requiredBytes
.value();
267 size_t BufferSizeFromDimensions(int32_t aWidth
, int32_t aHeight
, int32_t aDepth
,
268 int32_t aExtraBytes
) {
269 if (MOZ_UNLIKELY(aHeight
<= 0) || MOZ_UNLIKELY(aWidth
<= 0) ||
270 MOZ_UNLIKELY(aDepth
<= 0)) {
274 // Similar to BufferSizeFromStrideAndHeight, but with an extra parameter.
276 CheckedInt32 requiredBytes
=
277 CheckedInt32(aWidth
) * CheckedInt32(aHeight
) * CheckedInt32(aDepth
) +
278 CheckedInt32(aExtraBytes
);
279 if (MOZ_UNLIKELY(!requiredBytes
.isValid())) {
280 gfxWarning() << "Buffer size too big; returning zero " << aWidth
<< ", "
281 << aHeight
<< ", " << aDepth
<< ", " << aExtraBytes
;
284 return requiredBytes
.value();
288 * aSrcRect: Rect relative to the aSrc surface
289 * aDestPoint: Point inside aDest surface
291 * aSrcRect and aDestPoint are in internal local coordinates.
292 * i.e. locations of pixels and not in the same coordinate space
295 bool CopyRect(DataSourceSurface
* aSrc
, DataSourceSurface
* aDest
,
296 IntRect aSrcRect
, IntPoint aDestPoint
) {
297 if (aSrcRect
.Overflows() ||
298 IntRect(aDestPoint
, aSrcRect
.Size()).Overflows()) {
299 MOZ_CRASH("GFX: we should never be getting invalid rects at this point");
302 MOZ_RELEASE_ASSERT(aSrc
->GetFormat() == aDest
->GetFormat(),
303 "GFX: different surface formats");
304 MOZ_RELEASE_ASSERT(IntRect(IntPoint(), aSrc
->GetSize()).Contains(aSrcRect
),
305 "GFX: source rect too big for source surface");
306 MOZ_RELEASE_ASSERT(IntRect(IntPoint(), aDest
->GetSize())
307 .Contains(IntRect(aDestPoint
, aSrcRect
.Size())),
308 "GFX: dest surface too small");
310 if (aSrcRect
.IsEmpty()) {
314 DataSourceSurface::ScopedMap
srcMap(aSrc
, DataSourceSurface::READ
);
315 DataSourceSurface::ScopedMap
destMap(aDest
, DataSourceSurface::WRITE
);
316 if (MOZ2D_WARN_IF(!srcMap
.IsMapped() || !destMap
.IsMapped())) {
320 uint8_t* sourceData
=
321 DataAtOffset(aSrc
, srcMap
.GetMappedSurface(), aSrcRect
.TopLeft());
323 DataAtOffset(aDest
, destMap
.GetMappedSurface(), aDestPoint
);
325 SwizzleData(sourceData
, srcMap
.GetStride(), aSrc
->GetFormat(), destData
,
326 destMap
.GetStride(), aDest
->GetFormat(), aSrcRect
.Size());
331 already_AddRefed
<DataSourceSurface
> CreateDataSourceSurfaceByCloning(
332 DataSourceSurface
* aSource
) {
333 RefPtr
<DataSourceSurface
> copy
= Factory::CreateDataSourceSurface(
334 aSource
->GetSize(), aSource
->GetFormat(), true);
336 CopyRect(aSource
, copy
, IntRect(IntPoint(), aSource
->GetSize()),
339 return copy
.forget();
343 } // namespace mozilla