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/. */
7 #include "ImageDataSerializer.h"
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/DebugOnly.h" // for DebugOnly
13 #include "mozilla/gfx/2D.h" // for DataSourceSurface, Factory
14 #include "mozilla/gfx/Logging.h" // for gfxDebug
15 #include "mozilla/gfx/Tools.h" // for GetAlignedStride, etc
16 #include "mozilla/gfx/Types.h"
17 #include "mozilla/mozalloc.h" // for operator delete, etc
21 namespace ImageDataSerializer
{
25 int32_t ComputeRGBStride(SurfaceFormat aFormat
, int32_t aWidth
) {
27 // Some drivers require an alignment of 32 bytes for efficient texture upload.
28 return GetAlignedStride
<32>(aWidth
, BytesPerPixel(aFormat
));
30 return GetAlignedStride
<4>(aWidth
, BytesPerPixel(aFormat
));
34 int32_t GetRGBStride(const RGBDescriptor
& aDescriptor
) {
35 return ComputeRGBStride(aDescriptor
.format(), aDescriptor
.size().width
);
38 uint32_t ComputeRGBBufferSize(IntSize aSize
, SurfaceFormat aFormat
) {
39 MOZ_ASSERT(aSize
.height
>= 0 && aSize
.width
>= 0);
41 // This takes care of checking whether there could be overflow
42 // with enough margin for the metadata.
43 if (!gfx::Factory::AllowedSurfaceSize(aSize
)) {
47 // Note we're passing height instad of the bpp parameter, but the end
48 // result is the same - and the bpp was already taken care of in the
49 // ComputeRGBStride function.
50 int32_t bufsize
= GetAlignedStride
<16>(ComputeRGBStride(aFormat
, aSize
.width
),
54 // This should not be possible thanks to Factory::AllowedSurfaceSize
61 // Minimum required shmem size in bytes
62 uint32_t ComputeYCbCrBufferSize(const gfx::IntSize
& aYSize
, int32_t aYStride
,
63 const gfx::IntSize
& aCbCrSize
,
64 int32_t aCbCrStride
) {
65 MOZ_ASSERT(aYSize
.height
>= 0 && aYSize
.width
>= 0);
67 if (aYSize
.height
< 0 || aYSize
.width
< 0 || aCbCrSize
.height
< 0 ||
68 aCbCrSize
.width
< 0 ||
69 !gfx::Factory::AllowedSurfaceSize(IntSize(aYStride
, aYSize
.height
)) ||
70 !gfx::Factory::AllowedSurfaceSize(
71 IntSize(aCbCrStride
, aCbCrSize
.height
))) {
75 // Overflow checks are performed in AllowedSurfaceSize
76 return GetAlignedStride
<4>(aYSize
.height
, aYStride
) +
77 2 * GetAlignedStride
<4>(aCbCrSize
.height
, aCbCrStride
);
80 uint32_t ComputeYCbCrBufferSize(const gfx::IntSize
& aYSize
, int32_t aYStride
,
81 const gfx::IntSize
& aCbCrSize
,
82 int32_t aCbCrStride
, uint32_t aYOffset
,
83 uint32_t aCbOffset
, uint32_t aCrOffset
) {
84 MOZ_ASSERT(aYSize
.height
>= 0 && aYSize
.width
>= 0);
86 if (aYSize
.height
< 0 || aYSize
.width
< 0 || aCbCrSize
.height
< 0 ||
87 aCbCrSize
.width
< 0 ||
88 !gfx::Factory::AllowedSurfaceSize(IntSize(aYStride
, aYSize
.height
)) ||
89 !gfx::Factory::AllowedSurfaceSize(
90 IntSize(aCbCrStride
, aCbCrSize
.height
))) {
94 uint32_t yLength
= GetAlignedStride
<4>(aYStride
, aYSize
.height
);
95 uint32_t cbCrLength
= GetAlignedStride
<4>(aCbCrStride
, aCbCrSize
.height
);
96 if (yLength
== 0 || cbCrLength
== 0) {
100 CheckedInt
<uint32_t> yEnd
= aYOffset
;
102 CheckedInt
<uint32_t> cbEnd
= aCbOffset
;
104 CheckedInt
<uint32_t> crEnd
= aCrOffset
;
107 if (!yEnd
.isValid() || !cbEnd
.isValid() || !crEnd
.isValid() ||
108 yEnd
.value() > aCbOffset
|| cbEnd
.value() > aCrOffset
) {
112 return crEnd
.value();
115 uint32_t ComputeYCbCrBufferSize(uint32_t aBufferSize
) {
116 return GetAlignedStride
<4>(aBufferSize
, 1);
119 void ComputeYCbCrOffsets(int32_t yStride
, int32_t yHeight
, int32_t cbCrStride
,
120 int32_t cbCrHeight
, uint32_t& outYOffset
,
121 uint32_t& outCbOffset
, uint32_t& outCrOffset
) {
123 outCbOffset
= outYOffset
+ GetAlignedStride
<4>(yStride
, yHeight
);
124 outCrOffset
= outCbOffset
+ GetAlignedStride
<4>(cbCrStride
, cbCrHeight
);
127 gfx::SurfaceFormat
FormatFromBufferDescriptor(
128 const BufferDescriptor
& aDescriptor
) {
129 switch (aDescriptor
.type()) {
130 case BufferDescriptor::TRGBDescriptor
:
131 return aDescriptor
.get_RGBDescriptor().format();
132 case BufferDescriptor::TYCbCrDescriptor
:
133 return gfx::SurfaceFormat::YUV420
;
135 MOZ_CRASH("GFX: FormatFromBufferDescriptor");
139 gfx::IntSize
SizeFromBufferDescriptor(const BufferDescriptor
& aDescriptor
) {
140 switch (aDescriptor
.type()) {
141 case BufferDescriptor::TRGBDescriptor
:
142 return aDescriptor
.get_RGBDescriptor().size();
143 case BufferDescriptor::TYCbCrDescriptor
: {
144 return aDescriptor
.get_YCbCrDescriptor().display().Size();
147 MOZ_CRASH("GFX: SizeFromBufferDescriptor");
151 gfx::IntRect
RectFromBufferDescriptor(const BufferDescriptor
& aDescriptor
) {
152 switch (aDescriptor
.type()) {
153 case BufferDescriptor::TRGBDescriptor
: {
154 auto size
= aDescriptor
.get_RGBDescriptor().size();
155 return gfx::IntRect(0, 0, size
.Width(), size
.Height());
157 case BufferDescriptor::TYCbCrDescriptor
:
158 return aDescriptor
.get_YCbCrDescriptor().display();
160 MOZ_CRASH("GFX: RectFromBufferDescriptor");
164 Maybe
<gfx::IntSize
> YSizeFromBufferDescriptor(
165 const BufferDescriptor
& aDescriptor
) {
166 switch (aDescriptor
.type()) {
167 case BufferDescriptor::TRGBDescriptor
:
169 case BufferDescriptor::TYCbCrDescriptor
:
170 return Some(aDescriptor
.get_YCbCrDescriptor().ySize());
172 MOZ_CRASH("GFX: YSizeFromBufferDescriptor");
176 Maybe
<gfx::IntSize
> CbCrSizeFromBufferDescriptor(
177 const BufferDescriptor
& aDescriptor
) {
178 switch (aDescriptor
.type()) {
179 case BufferDescriptor::TRGBDescriptor
:
181 case BufferDescriptor::TYCbCrDescriptor
:
182 return Some(aDescriptor
.get_YCbCrDescriptor().cbCrSize());
184 MOZ_CRASH("GFX: CbCrSizeFromBufferDescriptor");
188 Maybe
<int32_t> YStrideFromBufferDescriptor(
189 const BufferDescriptor
& aDescriptor
) {
190 switch (aDescriptor
.type()) {
191 case BufferDescriptor::TRGBDescriptor
:
193 case BufferDescriptor::TYCbCrDescriptor
:
194 return Some(aDescriptor
.get_YCbCrDescriptor().yStride());
196 MOZ_CRASH("GFX: YStrideFromBufferDescriptor");
200 Maybe
<int32_t> CbCrStrideFromBufferDescriptor(
201 const BufferDescriptor
& aDescriptor
) {
202 switch (aDescriptor
.type()) {
203 case BufferDescriptor::TRGBDescriptor
:
205 case BufferDescriptor::TYCbCrDescriptor
:
206 return Some(aDescriptor
.get_YCbCrDescriptor().cbCrStride());
208 MOZ_CRASH("GFX: CbCrStrideFromBufferDescriptor");
212 Maybe
<gfx::YUVColorSpace
> YUVColorSpaceFromBufferDescriptor(
213 const BufferDescriptor
& aDescriptor
) {
214 switch (aDescriptor
.type()) {
215 case BufferDescriptor::TRGBDescriptor
:
217 case BufferDescriptor::TYCbCrDescriptor
:
218 return Some(aDescriptor
.get_YCbCrDescriptor().yUVColorSpace());
220 MOZ_CRASH("GFX: YUVColorSpaceFromBufferDescriptor");
224 Maybe
<gfx::ColorDepth
> ColorDepthFromBufferDescriptor(
225 const BufferDescriptor
& aDescriptor
) {
226 switch (aDescriptor
.type()) {
227 case BufferDescriptor::TRGBDescriptor
:
229 case BufferDescriptor::TYCbCrDescriptor
:
230 return Some(aDescriptor
.get_YCbCrDescriptor().colorDepth());
232 MOZ_CRASH("GFX: ColorDepthFromBufferDescriptor");
236 Maybe
<gfx::ColorRange
> ColorRangeFromBufferDescriptor(
237 const BufferDescriptor
& aDescriptor
) {
238 switch (aDescriptor
.type()) {
239 case BufferDescriptor::TRGBDescriptor
:
241 case BufferDescriptor::TYCbCrDescriptor
:
242 return Some(aDescriptor
.get_YCbCrDescriptor().colorRange());
244 MOZ_CRASH("GFX: YUVFullRangeFromBufferDescriptor");
248 Maybe
<StereoMode
> StereoModeFromBufferDescriptor(
249 const BufferDescriptor
& aDescriptor
) {
250 switch (aDescriptor
.type()) {
251 case BufferDescriptor::TRGBDescriptor
:
253 case BufferDescriptor::TYCbCrDescriptor
:
254 return Some(aDescriptor
.get_YCbCrDescriptor().stereoMode());
256 MOZ_CRASH("GFX: StereoModeFromBufferDescriptor");
260 Maybe
<gfx::ChromaSubsampling
> ChromaSubsamplingFromBufferDescriptor(
261 const BufferDescriptor
& aDescriptor
) {
262 switch (aDescriptor
.type()) {
263 case BufferDescriptor::TRGBDescriptor
:
265 case BufferDescriptor::TYCbCrDescriptor
:
266 return Some(aDescriptor
.get_YCbCrDescriptor().chromaSubsampling());
268 MOZ_CRASH("GFX: ChromaSubsamplingFromBufferDescriptor");
272 uint8_t* GetYChannel(uint8_t* aBuffer
, const YCbCrDescriptor
& aDescriptor
) {
273 return aBuffer
+ aDescriptor
.yOffset();
276 uint8_t* GetCbChannel(uint8_t* aBuffer
, const YCbCrDescriptor
& aDescriptor
) {
277 return aBuffer
+ aDescriptor
.cbOffset();
280 uint8_t* GetCrChannel(uint8_t* aBuffer
, const YCbCrDescriptor
& aDescriptor
) {
281 return aBuffer
+ aDescriptor
.crOffset();
284 already_AddRefed
<DataSourceSurface
> DataSourceSurfaceFromYCbCrDescriptor(
285 uint8_t* aBuffer
, const YCbCrDescriptor
& aDescriptor
,
286 gfx::DataSourceSurface
* aSurface
) {
287 const gfx::IntRect display
= aDescriptor
.display();
288 const gfx::IntSize size
= display
.Size();
289 RefPtr
<DataSourceSurface
> result
;
291 MOZ_ASSERT(aSurface
->GetSize() == size
);
292 MOZ_ASSERT(aSurface
->GetFormat() == gfx::SurfaceFormat::B8G8R8X8
);
293 if (aSurface
->GetSize() == size
&&
294 aSurface
->GetFormat() == gfx::SurfaceFormat::B8G8R8X8
) {
301 Factory::CreateDataSourceSurface(size
, gfx::SurfaceFormat::B8G8R8X8
);
303 if (NS_WARN_IF(!result
)) {
307 DataSourceSurface::MappedSurface map
;
308 if (NS_WARN_IF(!result
->Map(DataSourceSurface::MapType::WRITE
, &map
))) {
312 layers::PlanarYCbCrData ycbcrData
;
313 ycbcrData
.mYChannel
= GetYChannel(aBuffer
, aDescriptor
);
314 ycbcrData
.mYStride
= aDescriptor
.yStride();
315 ycbcrData
.mCbChannel
= GetCbChannel(aBuffer
, aDescriptor
);
316 ycbcrData
.mCrChannel
= GetCrChannel(aBuffer
, aDescriptor
);
317 ycbcrData
.mCbCrStride
= aDescriptor
.cbCrStride();
318 ycbcrData
.mPictureRect
= aDescriptor
.display();
319 ycbcrData
.mYUVColorSpace
= aDescriptor
.yUVColorSpace();
320 ycbcrData
.mColorDepth
= aDescriptor
.colorDepth();
321 ycbcrData
.mChromaSubsampling
= aDescriptor
.chromaSubsampling();
323 if (NS_WARN_IF(NS_FAILED(
324 gfx::ConvertYCbCrToRGB(ycbcrData
, gfx::SurfaceFormat::B8G8R8X8
, size
,
325 map
.mData
, map
.mStride
)))) {
326 MOZ_ASSERT_UNREACHABLE("Failed to convert YUV into RGB data");
331 return result
.forget();
334 void ConvertAndScaleFromYCbCrDescriptor(uint8_t* aBuffer
,
335 const YCbCrDescriptor
& aDescriptor
,
336 const gfx::SurfaceFormat
& aDestFormat
,
337 const gfx::IntSize
& aDestSize
,
338 unsigned char* aDestBuffer
,
342 layers::PlanarYCbCrData ycbcrData
;
343 ycbcrData
.mYChannel
= GetYChannel(aBuffer
, aDescriptor
);
344 ycbcrData
.mYStride
= aDescriptor
.yStride();
345 ycbcrData
.mCbChannel
= GetCbChannel(aBuffer
, aDescriptor
);
346 ycbcrData
.mCrChannel
= GetCrChannel(aBuffer
, aDescriptor
);
347 ycbcrData
.mCbCrStride
= aDescriptor
.cbCrStride();
348 ycbcrData
.mPictureRect
= aDescriptor
.display();
349 ycbcrData
.mYUVColorSpace
= aDescriptor
.yUVColorSpace();
350 ycbcrData
.mColorDepth
= aDescriptor
.colorDepth();
351 ycbcrData
.mChromaSubsampling
= aDescriptor
.chromaSubsampling();
353 DebugOnly
<nsresult
> result
= gfx::ConvertYCbCrToRGB(
354 ycbcrData
, aDestFormat
, aDestSize
, aDestBuffer
, aStride
);
355 MOZ_ASSERT(NS_SUCCEEDED(result
), "Failed to convert YUV into RGB data");
358 gfx::IntSize
GetCroppedCbCrSize(const YCbCrDescriptor
& aDescriptor
) {
359 return ChromaSize(aDescriptor
.display().Size(),
360 aDescriptor
.chromaSubsampling());
363 } // namespace ImageDataSerializer
364 } // namespace layers
365 } // namespace mozilla