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 "ImageDataSerializer.h"
8 #include "gfx2DGlue.h"            // for SurfaceFormatToImageFormat
9 #include "mozilla/gfx/Point.h"    // for IntSize
10 #include "mozilla/Assertions.h"   // for MOZ_ASSERT, etc
11 #include "mozilla/gfx/2D.h"       // for DataSourceSurface, Factory
12 #include "mozilla/gfx/Logging.h"  // for gfxDebug
13 #include "mozilla/gfx/Tools.h"    // for GetAlignedStride, etc
14 #include "mozilla/gfx/Types.h"
15 #include "mozilla/mozalloc.h"  // for operator delete, etc
16 #include "YCbCrUtils.h"        // for YCbCr conversions
17 
18 namespace mozilla {
19 namespace layers {
20 namespace ImageDataSerializer {
21 
22 using namespace gfx;
23 
ComputeRGBStride(SurfaceFormat aFormat,int32_t aWidth)24 int32_t ComputeRGBStride(SurfaceFormat aFormat, int32_t aWidth) {
25   return GetAlignedStride<4>(aWidth, BytesPerPixel(aFormat));
26 }
27 
GetRGBStride(const RGBDescriptor & aDescriptor)28 int32_t GetRGBStride(const RGBDescriptor& aDescriptor) {
29   return ComputeRGBStride(aDescriptor.format(), aDescriptor.size().width);
30 }
31 
ComputeRGBBufferSize(IntSize aSize,SurfaceFormat aFormat)32 uint32_t ComputeRGBBufferSize(IntSize aSize, SurfaceFormat aFormat) {
33   MOZ_ASSERT(aSize.height >= 0 && aSize.width >= 0);
34 
35   // This takes care of checking whether there could be overflow
36   // with enough margin for the metadata.
37   if (!gfx::Factory::AllowedSurfaceSize(aSize)) {
38     return 0;
39   }
40 
41   // Note we're passing height instad of the bpp parameter, but the end
42   // result is the same - and the bpp was already taken care of in the
43   // ComputeRGBStride function.
44   int32_t bufsize = GetAlignedStride<16>(ComputeRGBStride(aFormat, aSize.width),
45                                          aSize.height);
46 
47   if (bufsize < 0) {
48     // This should not be possible thanks to Factory::AllowedSurfaceSize
49     return 0;
50   }
51 
52   return bufsize;
53 }
54 
55 // Minimum required shmem size in bytes
ComputeYCbCrBufferSize(const gfx::IntSize & aYSize,int32_t aYStride,const gfx::IntSize & aCbCrSize,int32_t aCbCrStride)56 uint32_t ComputeYCbCrBufferSize(const gfx::IntSize& aYSize, int32_t aYStride,
57                                 const gfx::IntSize& aCbCrSize,
58                                 int32_t aCbCrStride) {
59   MOZ_ASSERT(aYSize.height >= 0 && aYSize.width >= 0);
60 
61   if (aYSize.height < 0 || aYSize.width < 0 || aCbCrSize.height < 0 ||
62       aCbCrSize.width < 0 ||
63       !gfx::Factory::AllowedSurfaceSize(IntSize(aYStride, aYSize.height)) ||
64       !gfx::Factory::AllowedSurfaceSize(
65           IntSize(aCbCrStride, aCbCrSize.height))) {
66     return 0;
67   }
68 
69   // Overflow checks are performed in AllowedSurfaceSize
70   return GetAlignedStride<4>(aYSize.height, aYStride) +
71          2 * GetAlignedStride<4>(aCbCrSize.height, aCbCrStride);
72 }
73 
ComputeYCbCrBufferSize(const gfx::IntSize & aYSize,int32_t aYStride,const gfx::IntSize & aCbCrSize,int32_t aCbCrStride,uint32_t aYOffset,uint32_t aCbOffset,uint32_t aCrOffset)74 uint32_t ComputeYCbCrBufferSize(const gfx::IntSize& aYSize, int32_t aYStride,
75                                 const gfx::IntSize& aCbCrSize,
76                                 int32_t aCbCrStride, uint32_t aYOffset,
77                                 uint32_t aCbOffset, uint32_t aCrOffset) {
78   MOZ_ASSERT(aYSize.height >= 0 && aYSize.width >= 0);
79 
80   if (aYSize.height < 0 || aYSize.width < 0 || aCbCrSize.height < 0 ||
81       aCbCrSize.width < 0 ||
82       !gfx::Factory::AllowedSurfaceSize(IntSize(aYStride, aYSize.height)) ||
83       !gfx::Factory::AllowedSurfaceSize(
84           IntSize(aCbCrStride, aCbCrSize.height))) {
85     return 0;
86   }
87 
88   uint32_t yLength = GetAlignedStride<4>(aYStride, aYSize.height);
89   uint32_t cbCrLength = GetAlignedStride<4>(aCbCrStride, aCbCrSize.height);
90   if (yLength == 0 || cbCrLength == 0) {
91     return 0;
92   }
93 
94   CheckedInt<uint32_t> yEnd = aYOffset;
95   yEnd += yLength;
96   CheckedInt<uint32_t> cbEnd = aCbOffset;
97   cbEnd += cbCrLength;
98   CheckedInt<uint32_t> crEnd = aCrOffset;
99   crEnd += cbCrLength;
100 
101   if (!yEnd.isValid() || !cbEnd.isValid() || !crEnd.isValid() ||
102       yEnd.value() > aCbOffset || cbEnd.value() > aCrOffset) {
103     return 0;
104   }
105 
106   return crEnd.value();
107 }
108 
ComputeYCbCrBufferSize(uint32_t aBufferSize)109 uint32_t ComputeYCbCrBufferSize(uint32_t aBufferSize) {
110   return GetAlignedStride<4>(aBufferSize, 1);
111 }
112 
ComputeYCbCrOffsets(int32_t yStride,int32_t yHeight,int32_t cbCrStride,int32_t cbCrHeight,uint32_t & outYOffset,uint32_t & outCbOffset,uint32_t & outCrOffset)113 void ComputeYCbCrOffsets(int32_t yStride, int32_t yHeight, int32_t cbCrStride,
114                          int32_t cbCrHeight, uint32_t& outYOffset,
115                          uint32_t& outCbOffset, uint32_t& outCrOffset) {
116   outYOffset = 0;
117   outCbOffset = outYOffset + GetAlignedStride<4>(yStride, yHeight);
118   outCrOffset = outCbOffset + GetAlignedStride<4>(cbCrStride, cbCrHeight);
119 }
120 
FormatFromBufferDescriptor(const BufferDescriptor & aDescriptor)121 gfx::SurfaceFormat FormatFromBufferDescriptor(
122     const BufferDescriptor& aDescriptor) {
123   switch (aDescriptor.type()) {
124     case BufferDescriptor::TRGBDescriptor:
125       return aDescriptor.get_RGBDescriptor().format();
126     case BufferDescriptor::TYCbCrDescriptor:
127       return gfx::SurfaceFormat::YUV;
128     default:
129       MOZ_CRASH("GFX: FormatFromBufferDescriptor");
130   }
131 }
132 
SizeFromBufferDescriptor(const BufferDescriptor & aDescriptor)133 gfx::IntSize SizeFromBufferDescriptor(const BufferDescriptor& aDescriptor) {
134   switch (aDescriptor.type()) {
135     case BufferDescriptor::TRGBDescriptor:
136       return aDescriptor.get_RGBDescriptor().size();
137     case BufferDescriptor::TYCbCrDescriptor:
138       return aDescriptor.get_YCbCrDescriptor().ySize();
139     default:
140       MOZ_CRASH("GFX: SizeFromBufferDescriptor");
141   }
142 }
143 
CbCrSizeFromBufferDescriptor(const BufferDescriptor & aDescriptor)144 Maybe<gfx::IntSize> CbCrSizeFromBufferDescriptor(
145     const BufferDescriptor& aDescriptor) {
146   switch (aDescriptor.type()) {
147     case BufferDescriptor::TRGBDescriptor:
148       return Nothing();
149     case BufferDescriptor::TYCbCrDescriptor:
150       return Some(aDescriptor.get_YCbCrDescriptor().cbCrSize());
151     default:
152       MOZ_CRASH("GFX:  CbCrSizeFromBufferDescriptor");
153   }
154 }
155 
YUVColorSpaceFromBufferDescriptor(const BufferDescriptor & aDescriptor)156 Maybe<YUVColorSpace> YUVColorSpaceFromBufferDescriptor(
157     const BufferDescriptor& aDescriptor) {
158   switch (aDescriptor.type()) {
159     case BufferDescriptor::TRGBDescriptor:
160       return Nothing();
161     case BufferDescriptor::TYCbCrDescriptor:
162       return Some(aDescriptor.get_YCbCrDescriptor().yUVColorSpace());
163     default:
164       MOZ_CRASH("GFX:  YUVColorSpaceFromBufferDescriptor");
165   }
166 }
167 
BitDepthFromBufferDescriptor(const BufferDescriptor & aDescriptor)168 Maybe<uint32_t> BitDepthFromBufferDescriptor(
169     const BufferDescriptor& aDescriptor) {
170   switch (aDescriptor.type()) {
171     case BufferDescriptor::TRGBDescriptor:
172       return Nothing();
173     case BufferDescriptor::TYCbCrDescriptor:
174       return Some(aDescriptor.get_YCbCrDescriptor().bitDepth());
175     default:
176       MOZ_CRASH("GFX:  BitDepthFromBufferDescriptor");
177   }
178 }
179 
StereoModeFromBufferDescriptor(const BufferDescriptor & aDescriptor)180 Maybe<StereoMode> StereoModeFromBufferDescriptor(
181     const BufferDescriptor& aDescriptor) {
182   switch (aDescriptor.type()) {
183     case BufferDescriptor::TRGBDescriptor:
184       return Nothing();
185     case BufferDescriptor::TYCbCrDescriptor:
186       return Some(aDescriptor.get_YCbCrDescriptor().stereoMode());
187     default:
188       MOZ_CRASH("GFX:  StereoModeFromBufferDescriptor");
189   }
190 }
191 
GetYChannel(uint8_t * aBuffer,const YCbCrDescriptor & aDescriptor)192 uint8_t* GetYChannel(uint8_t* aBuffer, const YCbCrDescriptor& aDescriptor) {
193   return aBuffer + aDescriptor.yOffset();
194 }
195 
GetCbChannel(uint8_t * aBuffer,const YCbCrDescriptor & aDescriptor)196 uint8_t* GetCbChannel(uint8_t* aBuffer, const YCbCrDescriptor& aDescriptor) {
197   return aBuffer + aDescriptor.cbOffset();
198 }
199 
GetCrChannel(uint8_t * aBuffer,const YCbCrDescriptor & aDescriptor)200 uint8_t* GetCrChannel(uint8_t* aBuffer, const YCbCrDescriptor& aDescriptor) {
201   return aBuffer + aDescriptor.crOffset();
202 }
203 
DataSourceSurfaceFromYCbCrDescriptor(uint8_t * aBuffer,const YCbCrDescriptor & aDescriptor,gfx::DataSourceSurface * aSurface)204 already_AddRefed<DataSourceSurface> DataSourceSurfaceFromYCbCrDescriptor(
205     uint8_t* aBuffer, const YCbCrDescriptor& aDescriptor,
206     gfx::DataSourceSurface* aSurface) {
207   gfx::IntSize ySize = aDescriptor.ySize();
208 
209   RefPtr<DataSourceSurface> result;
210   if (aSurface) {
211     MOZ_ASSERT(aSurface->GetSize() == ySize);
212     MOZ_ASSERT(aSurface->GetFormat() == gfx::SurfaceFormat::B8G8R8X8);
213     if (aSurface->GetSize() == ySize &&
214         aSurface->GetFormat() == gfx::SurfaceFormat::B8G8R8X8) {
215       result = aSurface;
216     }
217   }
218 
219   if (!result) {
220     result =
221         Factory::CreateDataSourceSurface(ySize, gfx::SurfaceFormat::B8G8R8X8);
222   }
223   if (NS_WARN_IF(!result)) {
224     return nullptr;
225   }
226 
227   DataSourceSurface::MappedSurface map;
228   if (NS_WARN_IF(!result->Map(DataSourceSurface::MapType::WRITE, &map))) {
229     return nullptr;
230   }
231 
232   layers::PlanarYCbCrData ycbcrData;
233   ycbcrData.mYChannel = GetYChannel(aBuffer, aDescriptor);
234   ycbcrData.mYStride = aDescriptor.yStride();
235   ycbcrData.mYSize = ySize;
236   ycbcrData.mCbChannel = GetCbChannel(aBuffer, aDescriptor);
237   ycbcrData.mCrChannel = GetCrChannel(aBuffer, aDescriptor);
238   ycbcrData.mCbCrStride = aDescriptor.cbCrStride();
239   ycbcrData.mCbCrSize = aDescriptor.cbCrSize();
240   ycbcrData.mPicSize = ySize;
241   ycbcrData.mYUVColorSpace = aDescriptor.yUVColorSpace();
242   ycbcrData.mBitDepth = aDescriptor.bitDepth();
243 
244   gfx::ConvertYCbCrToRGB(ycbcrData, gfx::SurfaceFormat::B8G8R8X8, ySize,
245                          map.mData, map.mStride);
246 
247   result->Unmap();
248   return result.forget();
249 }
250 
ConvertAndScaleFromYCbCrDescriptor(uint8_t * aBuffer,const YCbCrDescriptor & aDescriptor,const gfx::SurfaceFormat & aDestFormat,const gfx::IntSize & aDestSize,unsigned char * aDestBuffer,int32_t aStride)251 void ConvertAndScaleFromYCbCrDescriptor(uint8_t* aBuffer,
252                                         const YCbCrDescriptor& aDescriptor,
253                                         const gfx::SurfaceFormat& aDestFormat,
254                                         const gfx::IntSize& aDestSize,
255                                         unsigned char* aDestBuffer,
256                                         int32_t aStride) {
257   MOZ_ASSERT(aBuffer);
258 
259   layers::PlanarYCbCrData ycbcrData;
260   ycbcrData.mYChannel = GetYChannel(aBuffer, aDescriptor);
261   ycbcrData.mYStride = aDescriptor.yStride();
262   ;
263   ycbcrData.mYSize = aDescriptor.ySize();
264   ycbcrData.mCbChannel = GetCbChannel(aBuffer, aDescriptor);
265   ycbcrData.mCrChannel = GetCrChannel(aBuffer, aDescriptor);
266   ycbcrData.mCbCrStride = aDescriptor.cbCrStride();
267   ycbcrData.mCbCrSize = aDescriptor.cbCrSize();
268   ycbcrData.mPicSize = aDescriptor.ySize();
269   ycbcrData.mYUVColorSpace = aDescriptor.yUVColorSpace();
270   ycbcrData.mBitDepth = aDescriptor.bitDepth();
271 
272   gfx::ConvertYCbCrToRGB(ycbcrData, aDestFormat, aDestSize, aDestBuffer,
273                          aStride);
274 }
275 
276 }  // namespace ImageDataSerializer
277 }  // namespace layers
278 }  // namespace mozilla
279