Bug 1141979 - part7 - implement ImageUtils; r=jrmuizel draft
authorKaku Kuo <tkuo@mozilla.com>
Mon, 21 Mar 2016 14:33:10 +0800
changeset 373846 a29bb0c3de585bdc577d94c1416f044132e669fd
parent 373845 0953c1155d0f8dadd568913279cbd2a1df007bae
child 373847 89e951fd34edfdec35707171afc690e079a28812
push id19853
push usertkuo@mozilla.com
push dateWed, 01 Jun 2016 09:17:41 +0000
reviewersjrmuizel
bugs1141979
milestone49.0a1
Bug 1141979 - part7 - implement ImageUtils; r=jrmuizel MozReview-Commit-ID: INrfdqBr4m4
dom/canvas/ImageUtils.cpp
dom/canvas/ImageUtils.h
dom/canvas/moz.build
new file mode 100644
--- /dev/null
+++ b/dom/canvas/ImageUtils.cpp
@@ -0,0 +1,291 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* 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 "ImageUtils.h"
+#include "ImageBitmapUtils.h"
+#include "ImageContainer.h"
+#include "mozilla/AlreadyAddRefed.h"
+#include "mozilla/dom/ImageBitmapBinding.h"
+#include "mozilla/ErrorResult.h"
+
+using namespace mozilla::layers;
+using namespace mozilla::gfx;
+
+namespace mozilla {
+namespace dom {
+
+static ImageBitmapFormat
+GetImageBitmapFormatFromSurfaceFromat(SurfaceFormat aSurfaceFormat)
+{
+  switch (aSurfaceFormat) {
+  case SurfaceFormat::B8G8R8A8:
+  case SurfaceFormat::B8G8R8X8:
+    return ImageBitmapFormat::BGRA32;
+  case SurfaceFormat::R8G8B8A8:
+  case SurfaceFormat::R8G8B8X8:
+    return ImageBitmapFormat::RGBA32;
+  case SurfaceFormat::R8G8B8:
+    return ImageBitmapFormat::RGB24;
+  case SurfaceFormat::B8G8R8:
+    return ImageBitmapFormat::BGR24;
+  case SurfaceFormat::HSV:
+    return ImageBitmapFormat::HSV;
+  case SurfaceFormat::Lab:
+    return ImageBitmapFormat::Lab;
+  case SurfaceFormat::Depth:
+    return ImageBitmapFormat::DEPTH;
+  case SurfaceFormat::A8:
+    return ImageBitmapFormat::GRAY8;
+  case SurfaceFormat::R5G6B5_UINT16:
+  case SurfaceFormat::YUV:
+  case SurfaceFormat::NV12:
+  case SurfaceFormat::UNKNOWN:
+  default:
+    return ImageBitmapFormat::EndGuard_;
+  }
+}
+
+static ImageBitmapFormat
+GetImageBitmapFormatFromPlanarYCbCrData(layers::PlanarYCbCrData const *aData)
+{
+  MOZ_ASSERT(aData);
+
+  if (aData->mYSkip == 0 && aData->mCbSkip == 0 && aData->mCrSkip == 0) { // Possibly three planes.
+    if (aData->mCbChannel >= aData->mYChannel + aData->mYSize.height * aData->mYStride &&
+        aData->mCrChannel >= aData->mCbChannel + aData->mCbCrSize.height * aData->mCbCrStride) { // Three planes.
+      if (aData->mYSize.height == aData->mCbCrSize.height) {
+        if (aData->mYSize.width == aData->mCbCrSize.width) {
+          return ImageBitmapFormat::YUV444P;
+        } else if (((aData->mYSize.width + 1) / 2) == aData->mCbCrSize.width) {
+          return ImageBitmapFormat::YUV422P;
+        }
+      } else if (((aData->mYSize.height + 1) / 2) == aData->mCbCrSize.height) {
+        if (((aData->mYSize.width + 1) / 2) == aData->mCbCrSize.width) {
+          return ImageBitmapFormat::YUV420P;
+        }
+      }
+    }
+  } else if (aData->mYSkip == 0 && aData->mCbSkip == 1 && aData->mCrSkip == 1) { // Possibly two planes.
+    if (aData->mCbChannel >= aData->mYChannel + aData->mYSize.height * aData->mYStride &&
+        aData->mCbChannel == aData->mCrChannel - 1) { // Two planes.
+      if (((aData->mYSize.height + 1) / 2) == aData->mCbCrSize.height &&
+          ((aData->mYSize.width + 1) / 2) == aData->mCbCrSize.width) {
+        return ImageBitmapFormat::YUV420SP_NV12;  // Y-Cb-Cr
+      }
+    } else if (aData->mCrChannel >= aData->mYChannel + aData->mYSize.height * aData->mYStride &&
+               aData->mCrChannel == aData->mCbChannel - 1) { // Two planes.
+      if (((aData->mYSize.height + 1) / 2) == aData->mCbCrSize.height &&
+          ((aData->mYSize.width + 1) / 2) == aData->mCbCrSize.width) {
+        return ImageBitmapFormat::YUV420SP_NV21;  // Y-Cr-Cb
+      }
+    }
+  }
+
+  return ImageBitmapFormat::EndGuard_;
+}
+
+// ImageUtils::Impl implements the _generic_ algorithm which always readback
+// data in RGBA format into CPU memory.
+// Since layers::CairoImage is just a warpper to a DataSourceSurface, the
+// implementation of CairoSurfaceImpl is nothing different to the generic
+// version.
+class ImageUtils::Impl
+{
+public:
+  explicit Impl(layers::Image* aImage)
+  : mImage(aImage)
+  , mSurface(nullptr)
+  {
+  }
+
+  virtual ~Impl()
+  {
+  }
+
+  virtual ImageBitmapFormat
+  GetFormat() const
+  {
+    return GetImageBitmapFormatFromSurfaceFromat(Surface()->GetFormat());
+  }
+
+  virtual uint32_t
+  GetBufferLength() const
+  {
+    const uint32_t stride = Surface()->Stride();
+    const IntSize size = Surface()->GetSize();
+    return (uint32_t)(size.height * stride);
+  }
+
+  virtual UniquePtr<ImagePixelLayout>
+  MapDataInto(uint8_t* aBuffer,
+              uint32_t aOffset,
+              uint32_t aBufferLength,
+              ImageBitmapFormat aFormat,
+              ErrorResult& aRv) const
+  {
+    DataSourceSurface::ScopedMap map(Surface(), DataSourceSurface::READ);
+    if (!map.IsMapped()) {
+      aRv.Throw(NS_ERROR_ILLEGAL_VALUE);
+      return nullptr;
+    }
+
+    // Copy or convert data.
+    UniquePtr<ImagePixelLayout> srcLayout =
+      CreateDefaultPixelLayout(GetFormat(), Surface()->GetSize().width,
+                               Surface()->GetSize().height, map.GetStride());
+
+    // Prepare destination buffer.
+    uint8_t* dstBuffer = aBuffer + aOffset;
+    UniquePtr<ImagePixelLayout> dstLayout =
+      CopyAndConvertImageData(GetFormat(), map.GetData(), srcLayout.get(),
+                              aFormat, dstBuffer);
+
+    if (!dstLayout) {
+      aRv.Throw(NS_ERROR_NOT_AVAILABLE);
+      return nullptr;
+    }
+
+    return dstLayout;
+  }
+
+protected:
+  Impl() {}
+
+  DataSourceSurface* Surface() const
+  {
+    if (!mSurface) {
+      RefPtr<SourceSurface> surface = mImage->GetAsSourceSurface();
+      MOZ_ASSERT(surface);
+
+      mSurface = surface->GetDataSurface();
+      MOZ_ASSERT(mSurface);
+    }
+
+    return mSurface.get();
+  }
+
+  RefPtr<layers::Image> mImage;
+  mutable RefPtr<DataSourceSurface> mSurface;
+};
+
+// YUVImpl is optimized for the layers::PlanarYCbCrImage and layers::NVImage.
+// This implementation does not readback data in RGBA format but keep it in YUV
+// format family.
+class YUVImpl final : public ImageUtils::Impl
+{
+public:
+  explicit YUVImpl(layers::Image* aImage)
+  : Impl(aImage)
+  {
+    MOZ_ASSERT(aImage->GetFormat() == ImageFormat::PLANAR_YCBCR ||
+               aImage->GetFormat() == ImageFormat::NV_IMAGE);
+  }
+
+  ImageBitmapFormat GetFormat() const override
+  {
+    return GetImageBitmapFormatFromPlanarYCbCrData(GetPlanarYCbCrData());
+  }
+
+  uint32_t GetBufferLength() const override
+  {
+    if (mImage->GetFormat() == ImageFormat::PLANAR_YCBCR) {
+      return mImage->AsPlanarYCbCrImage()->GetDataSize();
+    } else {
+      return mImage->AsNVImage()->GetBufferSize();
+    }
+  }
+
+  UniquePtr<ImagePixelLayout>
+  MapDataInto(uint8_t* aBuffer,
+              uint32_t aOffset,
+              uint32_t aBufferLength,
+              ImageBitmapFormat aFormat,
+              ErrorResult& aRv) const override
+  {
+    // Prepare source buffer and pixel layout.
+    const PlanarYCbCrData* data = GetPlanarYCbCrData();
+
+    UniquePtr<ImagePixelLayout> srcLayout =
+      CreatePixelLayoutFromPlanarYCbCrData(data);
+
+    // Do conversion.
+    UniquePtr<ImagePixelLayout> dstLayout =
+      CopyAndConvertImageData(GetFormat(), data->mYChannel, srcLayout.get(),
+                              aFormat, aBuffer+aOffset);
+
+    if (!dstLayout) {
+      aRv.Throw(NS_ERROR_NOT_AVAILABLE);
+      return nullptr;
+    }
+
+    return dstLayout;
+  }
+
+private:
+  const PlanarYCbCrData* GetPlanarYCbCrData() const
+  {
+    if (mImage->GetFormat() == ImageFormat::PLANAR_YCBCR) {
+      return mImage->AsPlanarYCbCrImage()->GetData();
+    } else {
+      return mImage->AsNVImage()->GetData();
+    }
+  }
+};
+
+// TODO: optimize for other platforms.
+// For GONK: implement GrallocImageImpl, GrallocPlanarYCbCrImpl and GonkCameraImpl.
+// For Windows: implement D3D9RGB32TextureImpl and D3D11ShareHandleTextureImpl.
+// Others: SharedBGRImpl, MACIOSrufaceImpl, GLImageImpl, SurfaceTextureImpl
+//         EGLImageImpl and OverlayImegImpl.
+
+ImageUtils::ImageUtils(layers::Image* aImage)
+: mImpl(nullptr)
+{
+  MOZ_ASSERT(aImage, "Create ImageUtils with nullptr.");
+  switch(aImage->GetFormat()) {
+  case mozilla::ImageFormat::PLANAR_YCBCR:
+  case mozilla::ImageFormat::NV_IMAGE:
+    mImpl = new YUVImpl(aImage);
+    break;
+  case mozilla::ImageFormat::CAIRO_SURFACE:
+  default:
+    mImpl = new Impl(aImage);
+  }
+}
+
+ImageUtils::~ImageUtils()
+{
+  if (mImpl) { delete mImpl; mImpl = nullptr; }
+}
+
+ImageBitmapFormat
+ImageUtils::GetFormat() const
+{
+  MOZ_ASSERT(mImpl);
+  return mImpl->GetFormat();
+}
+
+uint32_t
+ImageUtils::GetBufferLength() const
+{
+  MOZ_ASSERT(mImpl);
+  return mImpl->GetBufferLength();
+}
+
+UniquePtr<ImagePixelLayout>
+ImageUtils::MapDataInto(uint8_t* aBuffer,
+                        uint32_t aOffset,
+                        uint32_t aBufferLength,
+                        ImageBitmapFormat aFormat,
+                        ErrorResult& aRv) const
+{
+  MOZ_ASSERT(mImpl);
+  MOZ_ASSERT(aBuffer, "Map data into a null buffer.");
+  return mImpl->MapDataInto(aBuffer, aOffset, aBufferLength, aFormat, aRv);
+}
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/canvas/ImageUtils.h
@@ -0,0 +1,73 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* 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/. */
+
+#ifndef mozilla_dom_ImageBitmapFormatUtils_h
+#define mozilla_dom_ImageBitmapFormatUtils_h
+
+#include "mozilla/UniquePtr.h"
+#include "nsTArrayForwardDeclare.h"
+
+namespace mozilla {
+
+namespace layers {
+class Image;
+}
+
+class ErrorResult;
+
+namespace dom {
+
+struct ChannelPixelLayout;
+enum class ImageBitmapFormat : uint32_t;
+
+typedef nsTArray<ChannelPixelLayout> ImagePixelLayout;
+
+/*
+ * ImageUtils is a wrapper around layers::Image. It provides three unified
+ * methods to all sub-classes of layers::Image, which are:
+ *
+ * (1) GetFormat() converts the image's format into ImageBitmapFormat enum.
+ * (2) GetBufferLength() returns the number of bytes that are used to store
+ *     the image's underlying raw data.
+ * (3) MapDataInto() writes the image's underlying raw data into a given
+ *     ArrayBuffer in the given format. (If the given format is different from
+ *     the existing format, the ImageUtils uses the ImageBitmapFormatUtils to
+ *     performa color conversion.)
+ *
+ * In theory, the functionalities of this class could be merged into the
+ * interface of layers::Image. However, this is designed as a isolated wrapper
+ * because we don't want to pollute the layers::Image's interface with methods
+ * that are only meaningful to the ImageBitmap.
+ */
+class ImageUtils
+{
+public:
+  class Impl;
+  ImageUtils() = delete;
+  ImageUtils(const ImageUtils&) = delete;
+  ImageUtils(ImageUtils&&) = delete;
+  ImageUtils& operator=(const ImageUtils&) = delete;
+  ImageUtils& operator=(ImageUtils&&) = delete;
+
+  explicit ImageUtils(layers::Image* aImage);
+  ~ImageUtils();
+
+  ImageBitmapFormat GetFormat() const;
+
+  uint32_t GetBufferLength() const;
+
+  UniquePtr<ImagePixelLayout>
+  MapDataInto(uint8_t* aBuffer, uint32_t aOffset, uint32_t aBufferLength,
+              ImageBitmapFormat aFormat, ErrorResult& aRv) const;
+
+protected:
+  Impl* mImpl;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif /* mozilla_dom_ImageBitmapFormatUtils_h */
--- a/dom/canvas/moz.build
+++ b/dom/canvas/moz.build
@@ -58,16 +58,20 @@ UNIFIED_SOURCES += [
     'ImageBitmap.cpp',
     'ImageBitmapColorUtils.cpp',
     'ImageBitmapRenderingContext.cpp',
     'ImageBitmapUtils.cpp',
     'ImageData.cpp',
     'OffscreenCanvas.cpp',
 ]
 
+SOURCES += [
+    'ImageUtils.cpp',
+]
+
 # WebGL Sources
 UNIFIED_SOURCES += [
     'TexUnpackBlob.cpp',
     'WebGL1Context.cpp',
     'WebGL1ContextBuffers.cpp',
     'WebGL1ContextUniforms.cpp',
     'WebGL2Context.cpp',
     'WebGL2ContextBuffers.cpp',