summaryrefslogtreecommitdiffstats
path: root/gfx/ycbcr/YCbCrUtils.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'gfx/ycbcr/YCbCrUtils.cpp')
-rw-r--r--gfx/ycbcr/YCbCrUtils.cpp357
1 files changed, 357 insertions, 0 deletions
diff --git a/gfx/ycbcr/YCbCrUtils.cpp b/gfx/ycbcr/YCbCrUtils.cpp
new file mode 100644
index 0000000000..92f8bc3f93
--- /dev/null
+++ b/gfx/ycbcr/YCbCrUtils.cpp
@@ -0,0 +1,357 @@
+/* -*- Mode: C++; tab-width: 2; 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 "mozilla/EndianUtils.h"
+#include "gfx2DGlue.h"
+#include "mozilla/gfx/Swizzle.h"
+
+#include "YCbCrUtils.h"
+#include "yuv_convert.h"
+#include "ycbcr_to_rgb565.h"
+
+namespace mozilla {
+namespace gfx {
+
+// clang-format off
+
+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;
+
+ if (aSuggestedFormat == SurfaceFormat::R5G6B5_UINT16) {
+#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
+ aSuggestedFormat = SurfaceFormat::B8G8R8X8;
+#endif
+ }
+ else if (aSuggestedFormat != SurfaceFormat::B8G8R8X8) {
+ // No other formats are currently supported.
+ aSuggestedFormat = SurfaceFormat::B8G8R8X8;
+ }
+ if (aSuggestedFormat == SurfaceFormat::B8G8R8X8) {
+ /* 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;
+ }
+}
+
+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;
+ }
+}
+
+void
+ConvertYCbCrToRGBInternal(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. Monochrome images have 0-sized CbCr planes
+ MOZ_ASSERT(aData.mCbCrSize.width == aData.mYSize.width ||
+ aData.mCbCrSize.width == (aData.mYSize.width + 1) >> 1 ||
+ aData.mCbCrSize.width == 0);
+ MOZ_ASSERT(aData.mCbCrSize.height == aData.mYSize.height ||
+ aData.mCbCrSize.height == (aData.mYSize.height + 1) >> 1 ||
+ aData.mCbCrSize.height == 0);
+
+ // 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.mColorDepth == ColorDepth::COLOR_8 ? aData : dstData;
+
+ if (aData.mColorDepth != ColorDepth::COLOR_8) {
+ // 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.mColorDepth = ColorDepth::COLOR_8;
+
+ size_t ySize = GetAlignedStride<1>(dstData.mYStride, aData.mYSize.height);
+ size_t cbcrSize =
+ GetAlignedStride<1>(dstData.mCbCrStride, aData.mCbCrSize.height);
+ if (ySize == 0) {
+ MOZ_DIAGNOSTIC_ASSERT(cbcrSize == 0, "CbCr without Y makes no sense");
+ return;
+ }
+ yChannel = MakeUnique<uint8_t[]>(ySize);
+
+ dstData.mYChannel = yChannel.get();
+
+ int bitDepth = BitDepthForColorDepth(aData.mColorDepth);
+
+ ConvertYCbCr16to8Line(dstData.mYChannel,
+ dstData.mYStride,
+ reinterpret_cast<uint16_t*>(aData.mYChannel),
+ aData.mYStride / 2,
+ aData.mYSize.width,
+ aData.mYSize.height,
+ bitDepth);
+
+ if (cbcrSize) {
+ cbChannel = MakeUnique<uint8_t[]>(cbcrSize);
+ crChannel = MakeUnique<uint8_t[]>(cbcrSize);
+
+ dstData.mCbChannel = cbChannel.get();
+ dstData.mCrChannel = crChannel.get();
+
+ ConvertYCbCr16to8Line(dstData.mCbChannel,
+ dstData.mCbCrStride,
+ reinterpret_cast<uint16_t*>(aData.mCbChannel),
+ aData.mCbCrStride / 2,
+ aData.mCbCrSize.width,
+ aData.mCbCrSize.height,
+ bitDepth);
+
+ ConvertYCbCr16to8Line(dstData.mCrChannel,
+ dstData.mCbCrStride,
+ reinterpret_cast<uint16_t*>(aData.mCrChannel),
+ aData.mCbCrStride / 2,
+ aData.mCbCrSize.width,
+ aData.mCbCrSize.height,
+ bitDepth);
+ }
+ }
+
+ YUVType yuvtype =
+ TypeFromSize(srcData.mYSize.width,
+ srcData.mYSize.height,
+ srcData.mCbCrSize.width,
+ srcData.mCbCrSize.height);
+
+ // Convert from YCbCr to RGB now, scaling the image if needed.
+ if (aDestSize != srcData.mPicSize) {
+#if defined(HAVE_YCBCR_TO_RGB565)
+ if (aDestFormat == SurfaceFormat::R5G6B5_UINT16) {
+ ScaleYCbCrToRGB565(srcData.mYChannel,
+ srcData.mCbChannel,
+ srcData.mCrChannel,
+ aDestBuffer,
+ srcData.mPicX,
+ srcData.mPicY,
+ srcData.mPicSize.width,
+ srcData.mPicSize.height,
+ aDestSize.width,
+ aDestSize.height,
+ srcData.mYStride,
+ srcData.mCbCrStride,
+ aStride,
+ yuvtype,
+ FILTER_BILINEAR);
+ } else
+#endif
+ ScaleYCbCrToRGB32(srcData.mYChannel, //
+ srcData.mCbChannel,
+ srcData.mCrChannel,
+ aDestBuffer,
+ srcData.mPicSize.width,
+ srcData.mPicSize.height,
+ aDestSize.width,
+ aDestSize.height,
+ srcData.mYStride,
+ srcData.mCbCrStride,
+ aStride,
+ yuvtype,
+ srcData.mYUVColorSpace,
+ FILTER_BILINEAR);
+ } else { // no prescale
+#if defined(HAVE_YCBCR_TO_RGB565)
+ if (aDestFormat == SurfaceFormat::R5G6B5_UINT16) {
+ ConvertYCbCrToRGB565(srcData.mYChannel,
+ srcData.mCbChannel,
+ srcData.mCrChannel,
+ aDestBuffer,
+ srcData.mPicX,
+ srcData.mPicY,
+ srcData.mPicSize.width,
+ srcData.mPicSize.height,
+ srcData.mYStride,
+ srcData.mCbCrStride,
+ aStride,
+ yuvtype);
+ } else // aDestFormat != SurfaceFormat::R5G6B5_UINT16
+#endif
+ ConvertYCbCrToRGB32(srcData.mYChannel, //
+ srcData.mCbChannel,
+ srcData.mCrChannel,
+ aDestBuffer,
+ srcData.mPicX,
+ srcData.mPicY,
+ srcData.mPicSize.width,
+ srcData.mPicSize.height,
+ srcData.mYStride,
+ srcData.mCbCrStride,
+ aStride,
+ yuvtype,
+ srcData.mYUVColorSpace);
+ }
+}
+
+void ConvertYCbCrToRGB(const layers::PlanarYCbCrData& aData,
+ const SurfaceFormat& aDestFormat,
+ const IntSize& aDestSize, unsigned char* aDestBuffer,
+ int32_t aStride) {
+ ConvertYCbCrToRGBInternal(aData, aDestFormat, aDestSize, aDestBuffer,
+ aStride);
+#if MOZ_BIG_ENDIAN()
+ // libyuv makes endian-correct result, which needs to be swapped to BGRX
+ if (aDestFormat != SurfaceFormat::R5G6B5_UINT16)
+ gfx::SwizzleData(aDestBuffer, aStride, gfx::SurfaceFormat::X8R8G8B8,
+ aDestBuffer, aStride, gfx::SurfaceFormat::B8G8R8X8,
+ aData.mPicSize);
+#endif
+}
+
+void FillAlphaToRGBA(const uint8_t* aAlpha, const int32_t aAlphaStride,
+ uint8_t* aBuffer, const int32_t aWidth,
+ const int32_t aHeight, const gfx::SurfaceFormat& aFormat) {
+ MOZ_ASSERT(aAlphaStride >= aWidth);
+ MOZ_ASSERT(aFormat ==
+ SurfaceFormat::B8G8R8A8); // required for SurfaceFormatBit::OS_A
+
+ const int bpp = BytesPerPixel(aFormat);
+ const size_t rgbaStride = aWidth * bpp;
+ const uint8_t* src = aAlpha;
+ for (int32_t h = 0; h < aHeight; ++h) {
+ size_t offset = static_cast<size_t>(SurfaceFormatBit::OS_A) / 8;
+ for (int32_t w = 0; w < aWidth; ++w) {
+ aBuffer[offset] = src[w];
+ offset += bpp;
+ }
+ src += aAlphaStride;
+ aBuffer += rgbaStride;
+ }
+}
+
+void ConvertYCbCrAToARGB(const layers::PlanarYCbCrAData& aData,
+ const SurfaceFormat& aDestFormat,
+ const IntSize& aDestSize, unsigned char* aDestBuffer,
+ int32_t aStride) {
+ // libyuv makes endian-correct result, so the format needs to be B8G8R8A8.
+ MOZ_ASSERT(aDestFormat == SurfaceFormat::B8G8R8A8);
+
+ MOZ_ASSERT(aData.mAlphaSize == aData.mYSize);
+
+ YUVType yuvtype = TypeFromSize(aData.mYSize.width, aData.mYSize.height,
+ aData.mCbCrSize.width, aData.mCbCrSize.height);
+
+ if (yuvtype == YV12) {
+ // Currently this function only has support for I420 type
+ ConvertI420AlphaToARGB(aData.mYChannel, aData.mCbChannel, aData.mCrChannel,
+ aData.mAlphaChannel, aData.mYStride,
+ aData.mCbCrStride, aDestBuffer, aStride,
+ aData.mPicSize.width, aData.mPicSize.height);
+ return;
+ }
+
+ // This function converts non-8-bpc images to 8-bpc. (Bug 1682322)
+ ConvertYCbCrToRGBInternal(aData, aDestFormat, aDestSize, aDestBuffer,
+ aStride);
+
+ // Note alpha stride is equal to Y stride.
+ FillAlphaToRGBA(aData.mAlphaChannel, aData.mYStride, aDestBuffer,
+ aData.mPicSize.width, aData.mPicSize.height, aDestFormat);
+
+ // Do preattenuate as what ConvertI420AlphaToARGB does
+ ARGBAttenuate(aDestBuffer, aStride, aDestBuffer, aStride,
+ aData.mPicSize.width, aData.mPicSize.height);
+
+#if MOZ_BIG_ENDIAN()
+ // libyuv makes endian-correct result, which needs to be swapped to BGRA
+ gfx::SwizzleData(aDestBuffer, aStride, gfx::SurfaceFormat::A8R8G8B8,
+ aDestBuffer, aStride, gfx::SurfaceFormat::B8G8R8A8,
+ aData.mPicSize);
+#endif
+}
+
+void
+ConvertI420AlphaToARGB(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) {
+
+ ConvertI420AlphaToARGB32(aSrcY,
+ aSrcU,
+ aSrcV,
+ aSrcA,
+ aDstARGB,
+ aWidth,
+ aHeight,
+ aSrcStrideYA,
+ aSrcStrideUV,
+ aDstStrideARGB);
+#if MOZ_BIG_ENDIAN()
+ // libyuv makes endian-correct result, which needs to be swapped to BGRA
+ gfx::SwizzleData(aDstARGB, aDstStrideARGB, gfx::SurfaceFormat::A8R8G8B8,
+ aDstARGB, aDstStrideARGB, gfx::SurfaceFormat::B8G8R8A8,
+ IntSize(aWidth, aHeight));
+#endif
+}
+
+} // namespace gfx
+} // namespace mozilla