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