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