MacIOSurfaceHelpers.cpp (8494B)
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 "libyuv.h" 8 #include "MacIOSurfaceHelpers.h" 9 #include "mozilla/gfx/MacIOSurface.h" 10 #include "mozilla/ScopeExit.h" 11 #include "YCbCrUtils.h" 12 13 namespace mozilla { 14 15 using namespace gfx; 16 17 namespace layers { 18 19 #define ALIGNED_32(x) ((x + 31) & ~31) 20 #define ALIGNEDPTR_32(x) \ 21 reinterpret_cast<uint8_t*>((reinterpret_cast<uintptr_t>(x) + 31) & ~31) 22 23 static nsresult CopyFromLockedMacIOSurface(MacIOSurface* aSurface, 24 uint8_t* aData, int32_t aStride, 25 const IntSize& aSize, 26 SurfaceFormat aFormat) { 27 size_t bytesPerRow = aSurface->GetBytesPerRow(); 28 SurfaceFormat ioFormat = aSurface->GetFormat(); 29 30 if ((ioFormat == SurfaceFormat::NV12 || ioFormat == SurfaceFormat::YUY2) && 31 (aSize.width > PlanarYCbCrImage::MAX_DIMENSION || 32 aSize.height > PlanarYCbCrImage::MAX_DIMENSION)) { 33 return NS_ERROR_FAILURE; 34 } 35 36 if (ioFormat == SurfaceFormat::NV12) { 37 /* Extract and separate the CbCr planes */ 38 size_t cbCrStride = aSurface->GetBytesPerRow(1); 39 size_t cbCrWidth = aSurface->GetDevicePixelWidth(1); 40 size_t cbCrHeight = aSurface->GetDevicePixelHeight(1); 41 42 auto cbPlane = MakeUnique<uint8_t[]>(cbCrWidth * cbCrHeight); 43 auto crPlane = MakeUnique<uint8_t[]>(cbCrWidth * cbCrHeight); 44 45 uint8_t* src = (uint8_t*)aSurface->GetBaseAddressOfPlane(1); 46 uint8_t* cbDest = cbPlane.get(); 47 uint8_t* crDest = crPlane.get(); 48 49 for (size_t i = 0; i < cbCrHeight; i++) { 50 uint8_t* rowSrc = src + cbCrStride * i; 51 for (size_t j = 0; j < cbCrWidth; j++) { 52 *cbDest = *rowSrc; 53 cbDest++; 54 rowSrc++; 55 *crDest = *rowSrc; 56 crDest++; 57 rowSrc++; 58 } 59 } 60 61 /* Convert to RGB */ 62 PlanarYCbCrData data; 63 data.mYChannel = (uint8_t*)aSurface->GetBaseAddressOfPlane(0); 64 data.mYStride = aSurface->GetBytesPerRow(0); 65 data.mCbChannel = cbPlane.get(); 66 data.mCrChannel = crPlane.get(); 67 data.mCbCrStride = cbCrWidth; 68 data.mPictureRect = IntRect(IntPoint(0, 0), aSize); 69 data.mYUVColorSpace = aSurface->GetYUVColorSpace(); 70 data.mColorPrimaries = aSurface->mColorPrimaries; 71 data.mColorRange = aSurface->IsFullRange() ? gfx::ColorRange::FULL 72 : gfx::ColorRange::LIMITED; 73 data.mChromaSubsampling = ChromaSubsampling::HALF_WIDTH_AND_HEIGHT; 74 75 nsresult result = 76 ConvertYCbCrToRGB(data, SurfaceFormat::B8G8R8X8, aSize, aData, aStride); 77 MOZ_ASSERT(NS_SUCCEEDED(result), "Failed to convert YUV into RGB data"); 78 return result; 79 } 80 81 if (ioFormat == SurfaceFormat::YUY2) { 82 if (aSize.width == ALIGNED_32(aSize.width)) { 83 // Optimization when width is aligned to 32. 84 libyuv::ConvertToARGB((uint8_t*)aSurface->GetBaseAddress(), 85 0 /* not used */, aData, aStride, 0, 0, aSize.width, 86 aSize.height, aSize.width, aSize.height, 87 libyuv::kRotate0, libyuv::FOURCC_YUYV); 88 } else { 89 /* Convert to YV16 */ 90 size_t cbCrWidth = (aSize.width + 1) >> 1; 91 size_t cbCrHeight = aSize.height; 92 // Ensure our stride is a multiple of 32 to allow for memory aligned rows. 93 size_t cbCrStride = ALIGNED_32(cbCrWidth); 94 size_t strideDelta = cbCrStride - cbCrWidth; 95 MOZ_ASSERT(strideDelta <= 31); 96 97 auto yPlane = MakeUnique<uint8_t[]>(cbCrStride * 2 * aSize.height + 31); 98 auto cbPlane = MakeUnique<uint8_t[]>(cbCrStride * cbCrHeight + 31); 99 auto crPlane = MakeUnique<uint8_t[]>(cbCrStride * cbCrHeight + 31); 100 101 uint8_t* src = (uint8_t*)aSurface->GetBaseAddress(); 102 uint8_t* yDest = ALIGNEDPTR_32(yPlane.get()); 103 uint8_t* cbDest = ALIGNEDPTR_32(cbPlane.get()); 104 uint8_t* crDest = ALIGNEDPTR_32(crPlane.get()); 105 106 for (int32_t i = 0; i < aSize.height; i++) { 107 uint8_t* rowSrc = src + bytesPerRow * i; 108 for (size_t j = 0; j < cbCrWidth; j++) { 109 *yDest = *rowSrc; 110 yDest++; 111 rowSrc++; 112 *cbDest = *rowSrc; 113 cbDest++; 114 rowSrc++; 115 *yDest = *rowSrc; 116 yDest++; 117 rowSrc++; 118 *crDest = *rowSrc; 119 crDest++; 120 rowSrc++; 121 } 122 if (strideDelta) { 123 cbDest += strideDelta; 124 crDest += strideDelta; 125 yDest += strideDelta << 1; 126 } 127 } 128 129 /* Convert to RGB */ 130 PlanarYCbCrData data; 131 data.mYChannel = ALIGNEDPTR_32(yPlane.get()); 132 data.mYStride = cbCrStride * 2; 133 data.mCbChannel = ALIGNEDPTR_32(cbPlane.get()); 134 data.mCrChannel = ALIGNEDPTR_32(crPlane.get()); 135 data.mCbCrStride = cbCrStride; 136 data.mPictureRect = IntRect(IntPoint(0, 0), aSize); 137 data.mYUVColorSpace = aSurface->GetYUVColorSpace(); 138 data.mColorPrimaries = aSurface->mColorPrimaries; 139 data.mColorRange = aSurface->IsFullRange() ? gfx::ColorRange::FULL 140 : gfx::ColorRange::LIMITED; 141 data.mChromaSubsampling = ChromaSubsampling::HALF_WIDTH; 142 143 nsresult result = ConvertYCbCrToRGB(data, SurfaceFormat::B8G8R8X8, aSize, 144 aData, aStride); 145 MOZ_ASSERT(NS_SUCCEEDED(result), "Failed to convert YUV into RGB data"); 146 return result; 147 } 148 } else { 149 unsigned char* ioData = (unsigned char*)aSurface->GetBaseAddress(); 150 151 for (int32_t i = 0; i < aSize.height; ++i) { 152 memcpy(aData + i * aStride, ioData + i * bytesPerRow, aSize.width * 4); 153 } 154 } 155 156 return NS_OK; 157 } 158 159 already_AddRefed<SourceSurface> CreateSourceSurfaceFromMacIOSurface( 160 MacIOSurface* aSurface, gfx::DataSourceSurface* aDataSurface) { 161 if (NS_WARN_IF(!aSurface->Lock())) { 162 return nullptr; 163 } 164 165 auto scopeExit = MakeScopeExit([&]() { aSurface->Unlock(); }); 166 167 size_t ioWidth = aSurface->GetDevicePixelWidth(); 168 size_t ioHeight = aSurface->GetDevicePixelHeight(); 169 IntSize size((int32_t)ioWidth, (int32_t)ioHeight); 170 SurfaceFormat ioFormat = aSurface->GetFormat(); 171 172 SurfaceFormat format = 173 (ioFormat == SurfaceFormat::NV12 || ioFormat == SurfaceFormat::YUY2) 174 ? SurfaceFormat::B8G8R8X8 175 : SurfaceFormat::B8G8R8A8; 176 177 RefPtr<DataSourceSurface> dataSurface; 178 if (aDataSurface) { 179 MOZ_ASSERT(aDataSurface->GetSize() == size); 180 MOZ_ASSERT(aDataSurface->GetFormat() == format); 181 if (aDataSurface->GetSize() == size && 182 aDataSurface->GetFormat() == format) { 183 dataSurface = aDataSurface; 184 } 185 } 186 187 if (!dataSurface) { 188 dataSurface = Factory::CreateDataSourceSurface(size, format); 189 if (NS_WARN_IF(!dataSurface)) { 190 return nullptr; 191 } 192 } 193 194 DataSourceSurface::ScopedMap map(dataSurface, DataSourceSurface::WRITE); 195 if (NS_WARN_IF(!map.IsMapped())) { 196 return nullptr; 197 } 198 199 nsresult rv = CopyFromLockedMacIOSurface(aSurface, map.GetData(), 200 map.GetStride(), size, format); 201 if (NS_WARN_IF(NS_FAILED(rv))) { 202 return nullptr; 203 } 204 205 return dataSurface.forget(); 206 } 207 208 nsresult CreateSurfaceDescriptorBufferFromMacIOSurface( 209 MacIOSurface* aSurface, SurfaceDescriptorBuffer& aSdBuffer, 210 Image::BuildSdbFlags aFlags, 211 const std::function<MemoryOrShmem(uint32_t)>& aAllocate) { 212 if (NS_WARN_IF(!aSurface->Lock())) { 213 return NS_ERROR_FAILURE; 214 } 215 216 auto scopeExit = MakeScopeExit([&]() { aSurface->Unlock(); }); 217 218 size_t ioWidth = aSurface->GetDevicePixelWidth(); 219 size_t ioHeight = aSurface->GetDevicePixelHeight(); 220 IntSize size((int32_t)ioWidth, (int32_t)ioHeight); 221 SurfaceFormat ioFormat = aSurface->GetFormat(); 222 223 SurfaceFormat format = 224 (ioFormat == SurfaceFormat::NV12 || ioFormat == SurfaceFormat::YUY2) 225 ? SurfaceFormat::B8G8R8X8 226 : SurfaceFormat::B8G8R8A8; 227 228 uint8_t* buffer = nullptr; 229 int32_t stride = 0; 230 nsresult rv = Image::AllocateSurfaceDescriptorBufferRgb( 231 size, format, buffer, aSdBuffer, stride, aAllocate); 232 if (NS_WARN_IF(NS_FAILED(rv))) { 233 return rv; 234 } 235 236 return CopyFromLockedMacIOSurface(aSurface, buffer, stride, size, format); 237 } 238 239 } // namespace layers 240 } // namespace mozilla