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