michael@0: /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "2D.h" michael@0: #include "DataSurfaceHelpers.h" michael@0: #include "Logging.h" michael@0: #include "mozilla/CheckedInt.h" michael@0: #include "mozilla/MathAlgorithms.h" michael@0: michael@0: namespace mozilla { michael@0: namespace gfx { michael@0: michael@0: void michael@0: ConvertBGRXToBGRA(uint8_t* aData, const IntSize &aSize, int32_t aStride) michael@0: { michael@0: uint32_t* pixel = reinterpret_cast(aData); michael@0: michael@0: for (int row = 0; row < aSize.height; ++row) { michael@0: for (int column = 0; column < aSize.width; ++column) { michael@0: #ifdef IS_BIG_ENDIAN michael@0: pixel[column] |= 0x000000FF; michael@0: #else michael@0: pixel[column] |= 0xFF000000; michael@0: #endif michael@0: } michael@0: pixel += (aStride/4); michael@0: } michael@0: } michael@0: michael@0: void michael@0: CopySurfaceDataToPackedArray(uint8_t* aSrc, uint8_t* aDst, IntSize aSrcSize, michael@0: int32_t aSrcStride, int32_t aBytesPerPixel) michael@0: { michael@0: MOZ_ASSERT(aBytesPerPixel > 0, michael@0: "Negative stride for aDst not currently supported"); michael@0: MOZ_ASSERT(BufferSizeFromStrideAndHeight(aSrcStride, aSrcSize.height) > 0, michael@0: "How did we end up with a surface with such a big buffer?"); michael@0: michael@0: int packedStride = aSrcSize.width * aBytesPerPixel; michael@0: michael@0: if (aSrcStride == packedStride) { michael@0: // aSrc is already packed, so we can copy with a single memcpy. michael@0: memcpy(aDst, aSrc, packedStride * aSrcSize.height); michael@0: } else { michael@0: // memcpy one row at a time. michael@0: for (int row = 0; row < aSrcSize.height; ++row) { michael@0: memcpy(aDst, aSrc, packedStride); michael@0: aSrc += aSrcStride; michael@0: aDst += packedStride; michael@0: } michael@0: } michael@0: } michael@0: michael@0: void michael@0: CopyBGRXSurfaceDataToPackedBGRArray(uint8_t* aSrc, uint8_t* aDst, michael@0: IntSize aSrcSize, int32_t aSrcStride) michael@0: { michael@0: int packedStride = aSrcSize.width * 3; michael@0: michael@0: uint8_t* srcPx = aSrc; michael@0: uint8_t* dstPx = aDst; michael@0: michael@0: for (int row = 0; row < aSrcSize.height; ++row) { michael@0: for (int col = 0; col < aSrcSize.height; ++col) { michael@0: dstPx[0] = srcPx[0]; michael@0: dstPx[1] = srcPx[1]; michael@0: dstPx[2] = srcPx[2]; michael@0: // srcPx[3] (unused or alpha component) dropped on floor michael@0: srcPx += 4; michael@0: dstPx += 3; michael@0: } michael@0: srcPx = aSrc += aSrcStride; michael@0: dstPx = aDst += packedStride; michael@0: } michael@0: } michael@0: michael@0: uint8_t* michael@0: SurfaceToPackedBGRA(DataSourceSurface *aSurface) michael@0: { michael@0: SurfaceFormat format = aSurface->GetFormat(); michael@0: if (format != SurfaceFormat::B8G8R8A8 && format != SurfaceFormat::B8G8R8X8) { michael@0: return nullptr; michael@0: } michael@0: michael@0: IntSize size = aSurface->GetSize(); michael@0: michael@0: uint8_t* imageBuffer = new (std::nothrow) uint8_t[size.width * size.height * sizeof(uint32_t)]; michael@0: if (!imageBuffer) { michael@0: return nullptr; michael@0: } michael@0: michael@0: DataSourceSurface::MappedSurface map; michael@0: if (!aSurface->Map(DataSourceSurface::MapType::READ, &map)) { michael@0: delete [] imageBuffer; michael@0: return nullptr; michael@0: } michael@0: michael@0: CopySurfaceDataToPackedArray(map.mData, imageBuffer, size, michael@0: map.mStride, 4 * sizeof(uint8_t)); michael@0: michael@0: aSurface->Unmap(); michael@0: michael@0: if (format == SurfaceFormat::B8G8R8X8) { michael@0: // Convert BGRX to BGRA by setting a to 255. michael@0: ConvertBGRXToBGRA(reinterpret_cast(imageBuffer), size, size.width * sizeof(uint32_t)); michael@0: } michael@0: michael@0: return imageBuffer; michael@0: } michael@0: michael@0: uint8_t* michael@0: SurfaceToPackedBGR(DataSourceSurface *aSurface) michael@0: { michael@0: SurfaceFormat format = aSurface->GetFormat(); michael@0: MOZ_ASSERT(format == SurfaceFormat::B8G8R8X8, "Format not supported"); michael@0: michael@0: if (format != SurfaceFormat::B8G8R8X8) { michael@0: // To support B8G8R8A8 we'd need to un-pre-multiply alpha michael@0: return nullptr; michael@0: } michael@0: michael@0: IntSize size = aSurface->GetSize(); michael@0: michael@0: uint8_t* imageBuffer = new (std::nothrow) uint8_t[size.width * size.height * 3 * sizeof(uint8_t)]; michael@0: if (!imageBuffer) { michael@0: return nullptr; michael@0: } michael@0: michael@0: DataSourceSurface::MappedSurface map; michael@0: if (!aSurface->Map(DataSourceSurface::MapType::READ, &map)) { michael@0: delete [] imageBuffer; michael@0: return nullptr; michael@0: } michael@0: michael@0: CopyBGRXSurfaceDataToPackedBGRArray(map.mData, imageBuffer, size, michael@0: map.mStride); michael@0: michael@0: aSurface->Unmap(); michael@0: michael@0: return imageBuffer; michael@0: } michael@0: michael@0: size_t michael@0: BufferSizeFromStrideAndHeight(int32_t aStride, michael@0: int32_t aHeight, michael@0: int32_t aExtraBytes) michael@0: { michael@0: if (MOZ_UNLIKELY(aHeight <= 0)) { michael@0: return 0; michael@0: } michael@0: michael@0: // We limit the length returned to values that can be represented by int32_t michael@0: // because we don't want to allocate buffers any bigger than that. This michael@0: // allows for a buffer size of over 2 GiB which is already rediculously michael@0: // large and will make the process janky. (Note the choice of the signed type michael@0: // is deliberate because we specifically don't want the returned value to michael@0: // overflow if someone stores the buffer length in an int32_t variable.) michael@0: michael@0: CheckedInt32 requiredBytes = michael@0: CheckedInt32(aStride) * CheckedInt32(aHeight) + CheckedInt32(aExtraBytes); michael@0: if (MOZ_UNLIKELY(!requiredBytes.isValid())) { michael@0: gfxWarning() << "Buffer size too big; returning zero"; michael@0: return 0; michael@0: } michael@0: return requiredBytes.value(); michael@0: } michael@0: michael@0: } michael@0: }