Bug 1141979 - part4 - Add NVImage; r=jrmuizel
MozReview-Commit-ID: 9kacGnqHVRH
--- a/gfx/layers/ImageContainer.cpp
+++ b/gfx/layers/ImageContainer.cpp
@@ -5,16 +5,17 @@
#include "ImageContainer.h"
#include <string.h> // for memcpy, memset
#include "GLImages.h" // for SurfaceTextureImage
#include "gfx2DGlue.h"
#include "gfxPlatform.h" // for gfxPlatform
#include "gfxUtils.h" // for gfxUtils
+#include "libyuv.h"
#include "mozilla/RefPtr.h" // for already_AddRefed
#include "mozilla/ipc/CrossProcessMutex.h" // for CrossProcessMutex, etc
#include "mozilla/layers/CompositorTypes.h"
#include "mozilla/layers/ImageBridgeChild.h" // for ImageBridgeChild
#include "mozilla/layers/PImageContainerChild.h"
#include "mozilla/layers/ImageClient.h" // for ImageClient
#include "mozilla/layers/LayersMessages.h"
#include "mozilla/layers/SharedPlanarYCbCrImage.h"
@@ -593,16 +594,173 @@ PlanarYCbCrImage::GetAsSourceSurface()
gfx::ConvertYCbCrToRGB(mData, format, size, mapping.GetData(), mapping.GetStride());
mSourceSurface = surface;
return surface.forget();
}
+NVImage::NVImage()
+ : Image(nullptr, ImageFormat::NV_IMAGE)
+ , mBufferSize(0)
+{
+}
+
+NVImage::~NVImage()
+{
+}
+
+IntSize
+NVImage::GetSize()
+{
+ return mSize;
+}
+
+IntRect
+NVImage::GetPictureRect()
+{
+ return mData.GetPictureRect();
+}
+
+already_AddRefed<SourceSurface>
+NVImage::GetAsSourceSurface()
+{
+ if (mSourceSurface) {
+ RefPtr<gfx::SourceSurface> surface(mSourceSurface);
+ return surface.forget();
+ }
+
+ // Convert the current NV12 or NV21 data to YUV420P so that we can follow the
+ // logics in PlanarYCbCrImage::GetAsSourceSurface().
+ const int bufferLength = mData.mYSize.height * mData.mYStride +
+ mData.mCbCrSize.height * mData.mCbCrSize.width * 2;
+ uint8_t* buffer = new uint8_t[bufferLength];
+
+ Data aData = mData;
+ aData.mCbCrStride = aData.mCbCrSize.width;
+ aData.mCbSkip = 0;
+ aData.mCrSkip = 0;
+ aData.mYChannel = buffer;
+ aData.mCbChannel = aData.mYChannel + aData.mYSize.height * aData.mYStride;
+ aData.mCrChannel = aData.mCbChannel + aData.mCbCrSize.height * aData.mCbCrStride;
+
+ if (mData.mCbChannel < mData.mCrChannel) { // NV12
+ libyuv::NV12ToI420(mData.mYChannel, mData.mYStride,
+ mData.mCbChannel, mData.mCbCrStride,
+ aData.mYChannel, aData.mYStride,
+ aData.mCbChannel, aData.mCbCrStride,
+ aData.mCrChannel, aData.mCbCrStride,
+ aData.mYSize.width, aData.mYSize.height);
+ } else { // NV21
+ libyuv::NV21ToI420(mData.mYChannel, mData.mYStride,
+ mData.mCrChannel, mData.mCbCrStride,
+ aData.mYChannel, aData.mYStride,
+ aData.mCbChannel, aData.mCbCrStride,
+ aData.mCrChannel, aData.mCbCrStride,
+ aData.mYSize.width, aData.mYSize.height);
+ }
+
+ // The logics in PlanarYCbCrImage::GetAsSourceSurface().
+ gfx::IntSize size(mSize);
+ gfx::SurfaceFormat format =
+ gfx::ImageFormatToSurfaceFormat(gfxPlatform::GetPlatform()->GetOffscreenFormat());
+ gfx::GetYCbCrToRGBDestFormatAndSize(aData, format, size);
+ if (mSize.width > PlanarYCbCrImage::MAX_DIMENSION ||
+ mSize.height > PlanarYCbCrImage::MAX_DIMENSION) {
+ NS_ERROR("Illegal image dest width or height");
+ return nullptr;
+ }
+
+ RefPtr<gfx::DataSourceSurface> surface = gfx::Factory::CreateDataSourceSurface(size, format);
+ if (NS_WARN_IF(!surface)) {
+ return nullptr;
+ }
+
+ DataSourceSurface::ScopedMap mapping(surface, DataSourceSurface::WRITE);
+ if (NS_WARN_IF(!mapping.IsMapped())) {
+ return nullptr;
+ }
+
+ gfx::ConvertYCbCrToRGB(aData, format, size, mapping.GetData(), mapping.GetStride());
+
+ mSourceSurface = surface;
+
+ // Release the temporary buffer.
+ delete[] buffer;
+
+ return surface.forget();
+}
+
+bool
+NVImage::IsValid()
+{
+ return !!mBufferSize;
+}
+
+uint32_t
+NVImage::GetBufferSize() const
+{
+ return mBufferSize;
+}
+
+NVImage*
+NVImage::AsNVImage()
+{
+ return this;
+};
+
+bool
+NVImage::SetData(const Data& aData)
+{
+ MOZ_ASSERT(aData.mCbSkip == 1 && aData.mCrSkip == 1);
+ MOZ_ASSERT((int)std::abs(aData.mCbChannel - aData.mCrChannel) == 1);
+
+ // Calculate buffer size
+ const uint32_t size = aData.mYSize.height * aData.mYStride +
+ aData.mCbCrSize.height * aData.mCbCrStride;
+
+ // Allocate a new buffer.
+ mBuffer = AllocateBuffer(size);
+ if (!mBuffer) {
+ return false;
+ }
+
+ // Update mBufferSize.
+ mBufferSize = size;
+
+ // Update mData.
+ mData = aData;
+ mData.mYChannel = mBuffer.get();
+ mData.mCbChannel = mData.mYChannel + (aData.mCbChannel - aData.mYChannel);
+ mData.mCrChannel = mData.mYChannel + (aData.mCrChannel - aData.mYChannel);
+
+ // Update mSize.
+ mSize = aData.mPicSize;
+
+ // Copy the input data into mBuffer.
+ // This copies the y-channel and the interleaving CbCr-channel.
+ memcpy(mData.mYChannel, aData.mYChannel, mBufferSize);
+
+ return true;
+}
+
+const NVImage::Data*
+NVImage::GetData() const
+{
+ return &mData;
+}
+
+UniquePtr<uint8_t>
+NVImage::AllocateBuffer(uint32_t aSize)
+{
+ UniquePtr<uint8_t> buffer(new uint8_t[aSize]);
+ return buffer;
+}
+
SourceSurfaceImage::SourceSurfaceImage(const gfx::IntSize& aSize, gfx::SourceSurface* aSourceSurface)
: Image(nullptr, ImageFormat::CAIRO_SURFACE),
mSize(aSize),
mSourceSurface(aSourceSurface)
{}
SourceSurfaceImage::SourceSurfaceImage(gfx::SourceSurface* aSourceSurface)
: Image(nullptr, ImageFormat::CAIRO_SURFACE),
--- a/gfx/layers/ImageContainer.h
+++ b/gfx/layers/ImageContainer.h
@@ -147,16 +147,17 @@ class ImageClient;
class ImageCompositeNotification;
class ImageContainerChild;
class PImageContainerChild;
class SharedPlanarYCbCrImage;
class PlanarYCbCrImage;
class TextureClient;
class CompositableClient;
class GrallocImage;
+class NVImage;
struct ImageBackendData
{
virtual ~ImageBackendData() {}
protected:
ImageBackendData() {}
};
@@ -232,16 +233,18 @@ public:
#ifdef MOZ_WIDGET_ANDROID
virtual SurfaceTextureImage* AsSurfaceTextureImage() { return nullptr; }
#endif
#ifdef XP_MACOSX
virtual MacIOSurfaceImage* AsMacIOSurfaceImage() { return nullptr; }
#endif
virtual PlanarYCbCrImage* AsPlanarYCbCrImage() { return nullptr; }
+ virtual NVImage* AsNVImage() { return nullptr; }
+
protected:
Image(void* aImplData, ImageFormat aFormat) :
mImplData(aImplData),
mSerial(++sSerialCounter),
mFormat(aFormat)
{}
// Protected destructor, to discourage deletion outside of Release():
@@ -810,16 +813,58 @@ protected:
*/
mozilla::UniquePtr<uint8_t[]> AllocateBuffer(uint32_t aSize);
RefPtr<BufferRecycleBin> mRecycleBin;
mozilla::UniquePtr<uint8_t[]> mBuffer;
};
/**
+ * NVImage is used to store YUV420SP_NV12 and YUV420SP_NV21 data natively, which
+ * are not supported by PlanarYCbCrImage. (PlanarYCbCrImage only stores YUV444P,
+ * YUV422P and YUV420P, it converts YUV420SP_NV12 and YUV420SP_NV21 data into
+ * YUV420P in its PlanarYCbCrImage::SetData() method.)
+ *
+ * PlanarYCbCrData is able to express all the YUV family and so we keep use it
+ * in NVImage.
+ */
+class NVImage: public Image {
+ typedef PlanarYCbCrData Data;
+
+public:
+ explicit NVImage();
+ virtual ~NVImage() override;
+
+ // Methods inherited from layers::Image.
+ virtual gfx::IntSize GetSize() override;
+ virtual gfx::IntRect GetPictureRect() override;
+ virtual already_AddRefed<gfx::SourceSurface> GetAsSourceSurface() override;
+ virtual bool IsValid() override;
+ virtual NVImage* AsNVImage() override;
+
+ // Methods mimic layers::PlanarYCbCrImage.
+ virtual bool SetData(const Data& aData);
+ virtual const Data* GetData() const;
+ virtual uint32_t GetBufferSize() const;
+
+protected:
+
+ /**
+ * Return a buffer to store image data in.
+ */
+ mozilla::UniquePtr<uint8_t> AllocateBuffer(uint32_t aSize);
+
+ mozilla::UniquePtr<uint8_t> mBuffer;
+ uint32_t mBufferSize;
+ gfx::IntSize mSize;
+ Data mData;
+ nsCountedRef<nsMainThreadSourceSurfaceRef> mSourceSurface;
+};
+
+/**
* Currently, the data in a SourceSurfaceImage surface is treated as being in the
* device output color space. This class is very simple as all backends
* have to know about how to deal with drawing a cairo image.
*/
class SourceSurfaceImage final : public Image {
public:
virtual already_AddRefed<gfx::SourceSurface> GetAsSourceSurface() override
{
--- a/gfx/layers/ImageTypes.h
+++ b/gfx/layers/ImageTypes.h
@@ -12,16 +12,22 @@ enum class ImageFormat {
/**
* The PLANAR_YCBCR format creates a PlanarYCbCrImage. All backends should
* support this format, because the Ogg video decoder depends on it.
* The maximum image width and height is 16384.
*/
PLANAR_YCBCR,
/**
+ * The NV_IMAGE format creates a NVImage. The PLANAR_YCBCR together with this
+ * complete the YUV format family.
+ */
+ NV_IMAGE,
+
+ /**
* The GRALLOC_PLANAR_YCBCR format creates a GrallocImage, a subtype of
* PlanarYCbCrImage. It takes a PlanarYCbCrImage data or the raw gralloc
* data and can be used as a texture by Gonk backend directly.
*/
GRALLOC_PLANAR_YCBCR,
/**
* The GONK_CAMERA_IMAGE format creates a GonkCameraImage, which contains two
--- a/gfx/layers/moz.build
+++ b/gfx/layers/moz.build
@@ -423,16 +423,17 @@ IPDL_SOURCES = [
]
include('/ipc/chromium/chromium-config.mozbuild')
LOCAL_INCLUDES += [
'/docshell/base', # for nsDocShell.h
'/layout/base', # for TouchManager.h
'/layout/generic', # for nsTextFrame.h
+ '/media/libyuv/include', # for libyuv.h
]
FINAL_LIBRARY = 'xul'
if CONFIG['MOZ_DEBUG']:
DEFINES['D3D_DEBUG_INFO'] = True
if CONFIG['MOZ_ENABLE_D3D10_LAYER']: