Bug 1141979 - part4 - Add NVImage; r=jrmuizel draft
authorKaku Kuo <tkuo@mozilla.com>
Thu, 10 Mar 2016 10:35:27 +0800
changeset 373843 d295c25ef4300d862ecdf72336e8d3d87f727699
parent 373842 2077a075e846ae2f45076a61eafd9fcc69858bfd
child 373844 7a249cc2eb0ac7749ddf75ce0d4b52d511b25424
push id19853
push usertkuo@mozilla.com
push dateWed, 01 Jun 2016 09:17:41 +0000
reviewersjrmuizel
bugs1141979
milestone49.0a1
Bug 1141979 - part4 - Add NVImage; r=jrmuizel MozReview-Commit-ID: 9kacGnqHVRH
gfx/layers/ImageContainer.cpp
gfx/layers/ImageContainer.h
gfx/layers/ImageTypes.h
gfx/layers/moz.build
--- 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']: