2013-12-13 21:31:57 +04:00
|
|
|
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
|
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
|
|
|
|
#include "gfx2DGlue.h"
|
|
|
|
|
|
|
|
#include "YCbCrUtils.h"
|
|
|
|
#include "yuv_convert.h"
|
|
|
|
#include "ycbcr_to_rgb565.h"
|
|
|
|
|
|
|
|
namespace mozilla {
|
|
|
|
namespace gfx {
|
|
|
|
|
|
|
|
void
|
|
|
|
GetYCbCrToRGBDestFormatAndSize(const layers::PlanarYCbCrData& aData,
|
|
|
|
SurfaceFormat& aSuggestedFormat,
|
|
|
|
IntSize& aSuggestedSize)
|
|
|
|
{
|
|
|
|
YUVType yuvtype =
|
|
|
|
TypeFromSize(aData.mYSize.width,
|
|
|
|
aData.mYSize.height,
|
|
|
|
aData.mCbCrSize.width,
|
|
|
|
aData.mCbCrSize.height);
|
|
|
|
|
|
|
|
// 'prescale' is true if the scaling is to be done as part of the
|
|
|
|
// YCbCr to RGB conversion rather than on the RGB data when rendered.
|
|
|
|
bool prescale = aSuggestedSize.width > 0 && aSuggestedSize.height > 0 &&
|
|
|
|
aSuggestedSize != aData.mPicSize;
|
|
|
|
|
2015-10-23 09:01:31 +03:00
|
|
|
if (aSuggestedFormat == SurfaceFormat::R5G6B5_UINT16) {
|
2013-12-13 21:31:57 +04:00
|
|
|
#if defined(HAVE_YCBCR_TO_RGB565)
|
|
|
|
if (prescale &&
|
|
|
|
!IsScaleYCbCrToRGB565Fast(aData.mPicX,
|
|
|
|
aData.mPicY,
|
|
|
|
aData.mPicSize.width,
|
|
|
|
aData.mPicSize.height,
|
|
|
|
aSuggestedSize.width,
|
|
|
|
aSuggestedSize.height,
|
|
|
|
yuvtype,
|
|
|
|
FILTER_BILINEAR) &&
|
|
|
|
IsConvertYCbCrToRGB565Fast(aData.mPicX,
|
|
|
|
aData.mPicY,
|
|
|
|
aData.mPicSize.width,
|
|
|
|
aData.mPicSize.height,
|
|
|
|
yuvtype)) {
|
|
|
|
prescale = false;
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
// yuv2rgb16 function not available
|
2014-01-10 23:06:16 +04:00
|
|
|
aSuggestedFormat = SurfaceFormat::B8G8R8X8;
|
2013-12-13 21:31:57 +04:00
|
|
|
#endif
|
|
|
|
}
|
2014-01-10 23:06:16 +04:00
|
|
|
else if (aSuggestedFormat != SurfaceFormat::B8G8R8X8) {
|
2013-12-13 21:31:57 +04:00
|
|
|
// No other formats are currently supported.
|
2014-01-10 23:06:16 +04:00
|
|
|
aSuggestedFormat = SurfaceFormat::B8G8R8X8;
|
2013-12-13 21:31:57 +04:00
|
|
|
}
|
2014-01-10 23:06:16 +04:00
|
|
|
if (aSuggestedFormat == SurfaceFormat::B8G8R8X8) {
|
2013-12-13 21:31:57 +04:00
|
|
|
/* ScaleYCbCrToRGB32 does not support a picture offset, nor 4:4:4 data.
|
|
|
|
See bugs 639415 and 640073. */
|
|
|
|
if (aData.mPicX != 0 || aData.mPicY != 0 || yuvtype == YV24)
|
|
|
|
prescale = false;
|
|
|
|
}
|
|
|
|
if (!prescale) {
|
|
|
|
aSuggestedSize = aData.mPicSize;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-09-29 14:43:09 +03:00
|
|
|
static inline void
|
|
|
|
ConvertYCbCr16to8Line(uint8_t* aDst,
|
|
|
|
int aStride,
|
|
|
|
const uint16_t* aSrc,
|
|
|
|
int aStride16,
|
|
|
|
int aWidth,
|
|
|
|
int aHeight,
|
|
|
|
int aBitDepth)
|
|
|
|
{
|
|
|
|
uint16_t mask = (1 << aBitDepth) - 1;
|
|
|
|
|
|
|
|
for (int i = 0; i < aHeight; i++) {
|
|
|
|
for (int j = 0; j < aWidth; j++) {
|
|
|
|
uint16_t val = (aSrc[j] & mask) >> (aBitDepth - 8);
|
|
|
|
aDst[j] = val;
|
|
|
|
}
|
|
|
|
aDst += aStride;
|
|
|
|
aSrc += aStride16;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-12-13 21:31:57 +04:00
|
|
|
void
|
|
|
|
ConvertYCbCrToRGB(const layers::PlanarYCbCrData& aData,
|
|
|
|
const SurfaceFormat& aDestFormat,
|
|
|
|
const IntSize& aDestSize,
|
|
|
|
unsigned char* aDestBuffer,
|
|
|
|
int32_t aStride)
|
|
|
|
{
|
|
|
|
// ConvertYCbCrToRGB et al. assume the chroma planes are rounded up if the
|
|
|
|
// luma plane is odd sized.
|
|
|
|
MOZ_ASSERT((aData.mCbCrSize.width == aData.mYSize.width ||
|
|
|
|
aData.mCbCrSize.width == (aData.mYSize.width + 1) >> 1) &&
|
|
|
|
(aData.mCbCrSize.height == aData.mYSize.height ||
|
|
|
|
aData.mCbCrSize.height == (aData.mYSize.height + 1) >> 1));
|
2017-09-29 14:43:09 +03:00
|
|
|
|
|
|
|
// Used if converting to 8 bits YUV.
|
|
|
|
UniquePtr<uint8_t[]> yChannel;
|
|
|
|
UniquePtr<uint8_t[]> cbChannel;
|
|
|
|
UniquePtr<uint8_t[]> crChannel;
|
|
|
|
layers::PlanarYCbCrData dstData;
|
|
|
|
const layers::PlanarYCbCrData& srcData = aData.mBitDepth == 8 ? aData : dstData;
|
|
|
|
|
|
|
|
if (aData.mBitDepth != 8) {
|
|
|
|
MOZ_ASSERT(aData.mBitDepth > 8 && aData.mBitDepth <= 16);
|
|
|
|
// Convert to 8 bits data first.
|
|
|
|
dstData.mPicSize = aData.mPicSize;
|
|
|
|
dstData.mPicX = aData.mPicX;
|
|
|
|
dstData.mPicY = aData.mPicY;
|
|
|
|
dstData.mYSize = aData.mYSize;
|
|
|
|
// We align the destination stride to 32 bytes, so that libyuv can use
|
|
|
|
// SSE optimised code.
|
|
|
|
dstData.mYStride = (aData.mYSize.width + 31) & ~31;
|
|
|
|
dstData.mCbCrSize = aData.mCbCrSize;
|
|
|
|
dstData.mCbCrStride = (aData.mCbCrSize.width + 31) & ~31;
|
|
|
|
dstData.mYUVColorSpace = aData.mYUVColorSpace;
|
|
|
|
dstData.mBitDepth = 8;
|
|
|
|
|
|
|
|
size_t ySize = GetAlignedStride<1>(dstData.mYStride, aData.mYSize.height);
|
|
|
|
size_t cbcrSize =
|
|
|
|
GetAlignedStride<1>(dstData.mCbCrStride, aData.mCbCrSize.height);
|
|
|
|
if (ySize == 0 || cbcrSize == 0) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
yChannel = MakeUnique<uint8_t[]>(ySize);
|
|
|
|
cbChannel = MakeUnique<uint8_t[]>(cbcrSize);
|
|
|
|
crChannel = MakeUnique<uint8_t[]>(cbcrSize);
|
|
|
|
|
|
|
|
dstData.mYChannel = yChannel.get();
|
|
|
|
dstData.mCbChannel = cbChannel.get();
|
|
|
|
dstData.mCrChannel = crChannel.get();
|
|
|
|
|
|
|
|
ConvertYCbCr16to8Line(dstData.mYChannel,
|
|
|
|
dstData.mYStride,
|
|
|
|
reinterpret_cast<uint16_t*>(aData.mYChannel),
|
|
|
|
aData.mYStride / 2,
|
|
|
|
aData.mYSize.width,
|
|
|
|
aData.mYSize.height,
|
|
|
|
aData.mBitDepth);
|
|
|
|
|
|
|
|
ConvertYCbCr16to8Line(dstData.mCbChannel,
|
|
|
|
dstData.mCbCrStride,
|
|
|
|
reinterpret_cast<uint16_t*>(aData.mCbChannel),
|
|
|
|
aData.mCbCrStride / 2,
|
|
|
|
aData.mCbCrSize.width,
|
|
|
|
aData.mCbCrSize.height,
|
|
|
|
aData.mBitDepth);
|
|
|
|
|
|
|
|
ConvertYCbCr16to8Line(dstData.mCrChannel,
|
|
|
|
dstData.mCbCrStride,
|
|
|
|
reinterpret_cast<uint16_t*>(aData.mCrChannel),
|
|
|
|
aData.mCbCrStride / 2,
|
|
|
|
aData.mCbCrSize.width,
|
|
|
|
aData.mCbCrSize.height,
|
|
|
|
aData.mBitDepth);
|
|
|
|
}
|
|
|
|
|
2013-12-13 21:31:57 +04:00
|
|
|
YUVType yuvtype =
|
2017-09-29 14:43:09 +03:00
|
|
|
TypeFromSize(srcData.mYSize.width,
|
|
|
|
srcData.mYSize.height,
|
|
|
|
srcData.mCbCrSize.width,
|
|
|
|
srcData.mCbCrSize.height);
|
2013-12-13 21:31:57 +04:00
|
|
|
|
|
|
|
// Convert from YCbCr to RGB now, scaling the image if needed.
|
2017-09-29 14:43:09 +03:00
|
|
|
if (aDestSize != srcData.mPicSize) {
|
2013-12-13 21:31:57 +04:00
|
|
|
#if defined(HAVE_YCBCR_TO_RGB565)
|
2015-10-23 09:01:31 +03:00
|
|
|
if (aDestFormat == SurfaceFormat::R5G6B5_UINT16) {
|
2017-09-29 14:43:09 +03:00
|
|
|
ScaleYCbCrToRGB565(srcData.mYChannel,
|
|
|
|
srcData.mCbChannel,
|
|
|
|
srcData.mCrChannel,
|
2013-12-13 21:31:57 +04:00
|
|
|
aDestBuffer,
|
2017-09-29 14:43:09 +03:00
|
|
|
srcData.mPicX,
|
|
|
|
srcData.mPicY,
|
|
|
|
srcData.mPicSize.width,
|
|
|
|
srcData.mPicSize.height,
|
2013-12-13 21:31:57 +04:00
|
|
|
aDestSize.width,
|
|
|
|
aDestSize.height,
|
2017-09-29 14:43:09 +03:00
|
|
|
srcData.mYStride,
|
|
|
|
srcData.mCbCrStride,
|
2013-12-13 21:31:57 +04:00
|
|
|
aStride,
|
|
|
|
yuvtype,
|
|
|
|
FILTER_BILINEAR);
|
|
|
|
} else
|
|
|
|
#endif
|
2017-09-29 14:43:09 +03:00
|
|
|
ScaleYCbCrToRGB32(srcData.mYChannel, //
|
|
|
|
srcData.mCbChannel,
|
|
|
|
srcData.mCrChannel,
|
2013-12-13 21:31:57 +04:00
|
|
|
aDestBuffer,
|
2017-09-29 14:43:09 +03:00
|
|
|
srcData.mPicSize.width,
|
|
|
|
srcData.mPicSize.height,
|
2013-12-13 21:31:57 +04:00
|
|
|
aDestSize.width,
|
|
|
|
aDestSize.height,
|
2017-09-29 14:43:09 +03:00
|
|
|
srcData.mYStride,
|
|
|
|
srcData.mCbCrStride,
|
2013-12-13 21:31:57 +04:00
|
|
|
aStride,
|
|
|
|
yuvtype,
|
2017-09-29 14:43:09 +03:00
|
|
|
srcData.mYUVColorSpace,
|
2013-12-13 21:31:57 +04:00
|
|
|
FILTER_BILINEAR);
|
|
|
|
} else { // no prescale
|
|
|
|
#if defined(HAVE_YCBCR_TO_RGB565)
|
2015-10-23 09:01:31 +03:00
|
|
|
if (aDestFormat == SurfaceFormat::R5G6B5_UINT16) {
|
2017-09-29 14:43:09 +03:00
|
|
|
ConvertYCbCrToRGB565(srcData.mYChannel,
|
|
|
|
srcData.mCbChannel,
|
|
|
|
srcData.mCrChannel,
|
2013-12-13 21:31:57 +04:00
|
|
|
aDestBuffer,
|
2017-09-29 14:43:09 +03:00
|
|
|
srcData.mPicX,
|
|
|
|
srcData.mPicY,
|
|
|
|
srcData.mPicSize.width,
|
|
|
|
srcData.mPicSize.height,
|
|
|
|
srcData.mYStride,
|
|
|
|
srcData.mCbCrStride,
|
2013-12-13 21:31:57 +04:00
|
|
|
aStride,
|
|
|
|
yuvtype);
|
2016-01-08 07:57:38 +03:00
|
|
|
} else // aDestFormat != SurfaceFormat::R5G6B5_UINT16
|
2013-12-13 21:31:57 +04:00
|
|
|
#endif
|
2017-09-29 14:43:09 +03:00
|
|
|
ConvertYCbCrToRGB32(srcData.mYChannel, //
|
|
|
|
srcData.mCbChannel,
|
|
|
|
srcData.mCrChannel,
|
2013-12-13 21:31:57 +04:00
|
|
|
aDestBuffer,
|
2017-09-29 14:43:09 +03:00
|
|
|
srcData.mPicX,
|
|
|
|
srcData.mPicY,
|
|
|
|
srcData.mPicSize.width,
|
|
|
|
srcData.mPicSize.height,
|
|
|
|
srcData.mYStride,
|
|
|
|
srcData.mCbCrStride,
|
2013-12-13 21:31:57 +04:00
|
|
|
aStride,
|
2016-10-12 05:46:28 +03:00
|
|
|
yuvtype,
|
2017-09-29 14:43:09 +03:00
|
|
|
srcData.mYUVColorSpace);
|
2013-12-13 21:31:57 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-12-21 05:00:46 +03:00
|
|
|
void
|
|
|
|
ConvertYCbCrAToARGB(const uint8_t* aSrcY,
|
|
|
|
const uint8_t* aSrcU,
|
|
|
|
const uint8_t* aSrcV,
|
|
|
|
const uint8_t* aSrcA,
|
|
|
|
int aSrcStrideYA, int aSrcStrideUV,
|
|
|
|
uint8_t* aDstARGB, int aDstStrideARGB,
|
|
|
|
int aWidth, int aHeight) {
|
|
|
|
|
|
|
|
ConvertYCbCrAToARGB32(aSrcY,
|
|
|
|
aSrcU,
|
|
|
|
aSrcV,
|
|
|
|
aSrcA,
|
|
|
|
aDstARGB,
|
|
|
|
aWidth,
|
|
|
|
aHeight,
|
|
|
|
aSrcStrideYA,
|
|
|
|
aSrcStrideUV,
|
|
|
|
aDstStrideARGB);
|
|
|
|
}
|
|
|
|
|
2015-07-13 18:25:42 +03:00
|
|
|
} // namespace gfx
|
|
|
|
} // namespace mozilla
|