Bug 1141979 - part4 - implement ImageBitmapFormatUtils; r?roc draft
authorKaku Kuo <tkuo@mozilla.com>
Tue, 22 Dec 2015 15:28:14 +0800
changeset 316830 f5b7b74dd82886c50540744d5b1b96a4f86449b3
parent 316262 46f6afe25736bce2da1476c9eef1b53f02da25ff
child 316831 bbd22a162f6351e41f03360dbe0f7f67ef95326a
push id8624
push usertkuo@mozilla.com
push dateTue, 22 Dec 2015 08:23:56 +0000
reviewersroc
bugs1141979
milestone46.0a1
Bug 1141979 - part4 - implement ImageBitmapFormatUtils; r?roc
dom/canvas/ImageBitmapUtils.cpp
dom/canvas/ImageBitmapUtils.h
dom/canvas/moz.build
new file mode 100644
--- /dev/null
+++ b/dom/canvas/ImageBitmapUtils.cpp
@@ -0,0 +1,807 @@
+/* -*- 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 "ImageBitmapUtils.h"
+#include "ImageContainer.h"
+#include "libyuv.h"
+#include "mozilla/dom/ImageBitmapBinding.h"
+#include "mozilla/gfx/2D.h"
+
+using namespace libyuv;
+using namespace mozilla::gfx;
+
+namespace mozilla {
+namespace dom {
+
+class ImageBitmapFormatUtils;
+class ImageBitmapFormatUtils_RGBA32;
+class ImageBitmapFormatUtils_BGRA32;
+class ImageBitmapFormatUtils_YUV420P;
+
+/*
+ * The UtilsUniquePtr is a UniquePtr to ImageBitmapFormatUtils with a customized
+ * deleter which does nothing. This is used as the return type of
+ * ImageBitmapFormatUtils::GetUtils to prevent users deleting the returned
+ * pointer.
+ */
+struct DoNotDelete { void operator()(void* p) {} };
+using UtilsUniquePtr = UniquePtr<ImageBitmapFormatUtils, DoNotDelete>;
+
+/*
+ * ImageBitmapFormatUtils is an abstract class which provides interfaces to
+ * extract information of each ImageBitmapFormat and interfaces to convert
+ * image data between different ImageBitmapFormats. For each kind of
+ * ImageBitmapFromat, we derive a subclass from the ImageBitmapFormatUtils to
+ * implement functionalities that are subject to the specific ImageBitmapFormat.
+ *
+ * ImageBitmapFormatUtils is an abstract class and its sub-classes are designed
+ * as singletons. The singleton instance of sub-classes could be initialized and
+ * accessed via the ImageBitmapFormatUtils::GetUtils() static method. The
+ * singleton instance is a static local variable which does not need to be
+ * released manually and, with the C++11 static initialization, the
+ * initialization is thread-safe.
+ *
+ * ImageBitmapFormatUtils and its sub-classes are designed to unify operations
+ * of ImageBitmap-extensions over different kinds of ImageBitmapFormats; they
+ * provide following functionalities:
+ *
+ * (1) Create default/customized ImagePixelLayout object of each kind of
+ *     ImageBitmapFormat.
+ * (2) Store the channel counts of each ImageBitmapFormat.
+ * (3) Calculate the needed buffer size of each kind of ImageBitmapFormat with
+ *     given width, height and stride.
+ * (4) Perform color conversion between supported ImageBitmapFormats. We use
+ *     _double dispatching_ to identify the source format and destination format
+ *     at run time. The _double dispatching_ here is mainly implemented by
+ *     overriding the _convertTo_ method over the ImageBitmapFormatUtils class
+ *     hierarchy and overloading the _convertFrom_ methods over all sub-classes
+ *     of ImageBitmapFormatUtils.
+ */
+
+class ImageBitmapFormatUtils
+{
+public:
+  // Get the singleton utility instance of the given ImageBitmapFormat.
+  static UtilsUniquePtr GetUtils(ImageBitmapFormat aFormat);
+
+  // Get the needed buffer size to store image data in the current
+  // ImageBitmapFormat with the given width and height.
+  // The current ImageBitmapFormat is the format used to implement the concrete
+  // subclass of which the current instance is initialized.
+  virtual uint32_t NeededBufferSize(uint32_t width, uint32_t height) = 0;
+
+  // Creates a default ImagePixelLayout object of the current ImageBitmapFormat
+  // with the given width, height and stride.
+  virtual UniquePtr<ImagePixelLayout>
+  CreateDefaultLayout(uint32_t aWidth, uint32_t aHeight, uint32_t aStride) = 0;
+
+  // Creates a customized ImagePixelLayout object of the current
+  // ImageBitmapFormatfrom using the information extracted from the aImage
+  // parameter.
+  virtual UniquePtr<ImagePixelLayout>
+  CreateCustomizedLayout(layers::Image* aImage) = 0;
+
+  // Convert the source image data (stored in the aSrcBuffer and described by
+  // the aSrcLayout) from the current ImageBitmapFormat to the given
+  // ImageBitmapFormat, aDstFormat.
+  // The converted image data is stored in the aDstBuffer and described by the
+  // returned ImagePixelLayout object.
+  virtual UniquePtr<ImagePixelLayout>
+  ConvertTo(ImageBitmapFormatUtils* aDstFormat, const uint8_t* aSrcBuffer, const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer) = 0;
+
+  // Convert the source RGBA32 source image data (stored in the aSrcBuffer and
+  // described by the aSrcLayout) to the current ImageBitmapFormat.
+  // The converted image data is stored in the aDstBuffer and described by the
+  // returned ImagePixelLayout object.
+  virtual UniquePtr<ImagePixelLayout>
+  ConvertFrom(ImageBitmapFormatUtils_RGBA32* aSrcFormat, const uint8_t* aSrcBuffer, const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer) = 0;
+
+  // Convert the source BGRA32 source image data (stored in the aSrcBuffer and
+  // described by the aSrcLayout) to the current ImageBitmapFormat.
+  // The converted image data is stored in the aDstBuffer and described by the
+  // returned ImagePixelLayout object.
+  virtual UniquePtr<ImagePixelLayout>
+  ConvertFrom(ImageBitmapFormatUtils_BGRA32* aSrcFormat, const uint8_t* aSrcBuffer, const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer) = 0;
+
+  // Convert the source YUV420P source image data (stored in the aSrcBuffer and
+  // described by the aSrcLayout) to the current ImageBitmapFormat.
+  // The converted image data is stored in the aDstBuffer and described by the
+  // returned ImagePixelLayout object.
+  virtual UniquePtr<ImagePixelLayout>
+  ConvertFrom(ImageBitmapFormatUtils_YUV420P* aSrcFormat, const uint8_t* aSrcBuffer, const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer) = 0;
+
+  // Check whether or not the current ImageBitmapFormat can be converted from
+  // the given ImageBitmapFormat.
+  virtual bool
+  CanConvertFrom(ImageBitmapFormat aSrcFormat) = 0;
+
+  // Get the number of channels.
+  uint8_t GetChannelCount() const
+  {
+    return mChannels;
+  }
+
+protected:
+  explicit ImageBitmapFormatUtils(uint32_t aChannels)
+  : mChannels(aChannels)
+  {
+  }
+
+  virtual ~ImageBitmapFormatUtils()
+  {
+  }
+
+  const uint8_t mChannels;
+};
+
+class ImageBitmapFormatUtils_RGBA32 : public ImageBitmapFormatUtils
+{
+private:
+  explicit ImageBitmapFormatUtils_RGBA32()
+  : ImageBitmapFormatUtils(4)
+  {
+  }
+
+  ~ImageBitmapFormatUtils_RGBA32() = default;
+  ImageBitmapFormatUtils_RGBA32(ImageBitmapFormatUtils_RGBA32 const &) = delete;
+  ImageBitmapFormatUtils_RGBA32(ImageBitmapFormatUtils_RGBA32&&) = delete;
+  ImageBitmapFormatUtils_RGBA32& operator=(ImageBitmapFormatUtils_RGBA32 const &) = delete;
+  ImageBitmapFormatUtils_RGBA32& operator=(ImageBitmapFormatUtils_RGBA32&&) = delete;
+
+public:
+  static ImageBitmapFormatUtils_RGBA32& GetInstance();
+
+  virtual uint32_t NeededBufferSize(uint32_t aWidth, uint32_t aHeight) override
+  {
+    return aWidth * aHeight * (uint32_t)mChannels;
+  }
+
+  virtual UniquePtr<ImagePixelLayout>
+  CreateDefaultLayout(uint32_t aWidth, uint32_t aHeight, uint32_t aStride) override;
+
+  virtual UniquePtr<ImagePixelLayout>
+  CreateCustomizedLayout(layers::Image* aImage) override;
+
+  virtual UniquePtr<ImagePixelLayout>
+  ConvertTo(ImageBitmapFormatUtils* aDstFormat, const uint8_t* aSrcBuffer, const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer) override
+  {
+    return aDstFormat->ConvertFrom(this, aSrcBuffer, aSrcLayout, aDstBuffer);
+  }
+
+  virtual UniquePtr<ImagePixelLayout>
+  ConvertFrom(ImageBitmapFormatUtils_RGBA32* aSrcFormat, const uint8_t* aSrcBuffer, const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer) override;
+
+  virtual UniquePtr<ImagePixelLayout>
+  ConvertFrom(ImageBitmapFormatUtils_BGRA32* aSrcFormat, const uint8_t* aSrcBuffer, const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer) override;
+
+  virtual UniquePtr<ImagePixelLayout>
+  ConvertFrom(ImageBitmapFormatUtils_YUV420P* aSrcFormat, const uint8_t* aSrcBuffer, const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer) override;
+
+  virtual bool
+  CanConvertFrom(ImageBitmapFormat aSrcFormat) override;
+};
+
+class ImageBitmapFormatUtils_BGRA32 : public ImageBitmapFormatUtils
+{
+private:
+  explicit ImageBitmapFormatUtils_BGRA32()
+  : ImageBitmapFormatUtils(4)
+  {
+  }
+
+  ~ImageBitmapFormatUtils_BGRA32() = default;
+  ImageBitmapFormatUtils_BGRA32(ImageBitmapFormatUtils_BGRA32 const &) = delete;
+  ImageBitmapFormatUtils_BGRA32(ImageBitmapFormatUtils_BGRA32&&) = delete;
+  ImageBitmapFormatUtils_BGRA32& operator=(ImageBitmapFormatUtils_BGRA32 const &) = delete;
+  ImageBitmapFormatUtils_BGRA32& operator=(ImageBitmapFormatUtils_BGRA32&&) = delete;
+
+public:
+  static ImageBitmapFormatUtils_BGRA32& GetInstance();
+
+  virtual uint32_t NeededBufferSize(uint32_t aWidth, uint32_t aHeight) override
+  {
+    return aWidth * aHeight * (uint32_t)mChannels;
+  }
+
+  virtual UniquePtr<ImagePixelLayout>
+  CreateDefaultLayout(uint32_t aWidth, uint32_t aHeight, uint32_t aStride) override;
+
+  virtual UniquePtr<ImagePixelLayout>
+  CreateCustomizedLayout(layers::Image* aImage) override;
+
+  virtual UniquePtr<ImagePixelLayout>
+  ConvertTo(ImageBitmapFormatUtils* aDstFormat, const uint8_t* aSrcBuffer, const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer) override
+  {
+    return aDstFormat->ConvertFrom(this, aSrcBuffer, aSrcLayout, aDstBuffer);
+  }
+
+  virtual UniquePtr<ImagePixelLayout>
+  ConvertFrom(ImageBitmapFormatUtils_RGBA32* aSrcFormat, const uint8_t* aSrcBuffer, const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer) override;
+
+  virtual UniquePtr<ImagePixelLayout>
+  ConvertFrom(ImageBitmapFormatUtils_BGRA32* aSrcFormat, const uint8_t* aSrcBuffer, const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer) override;
+
+  virtual UniquePtr<ImagePixelLayout>
+  ConvertFrom(ImageBitmapFormatUtils_YUV420P* aSrcFormat, const uint8_t* aSrcBuffer, const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer) override;
+
+  virtual bool
+  CanConvertFrom(ImageBitmapFormat aSrcFormat) override;
+};
+
+class ImageBitmapFormatUtils_YUV420P : public ImageBitmapFormatUtils
+{
+private:
+  explicit ImageBitmapFormatUtils_YUV420P()
+  : ImageBitmapFormatUtils(3)
+  {
+  }
+
+  ~ImageBitmapFormatUtils_YUV420P() = default;
+  ImageBitmapFormatUtils_YUV420P(ImageBitmapFormatUtils_YUV420P const &) = delete;
+  ImageBitmapFormatUtils_YUV420P(ImageBitmapFormatUtils_YUV420P&&) = delete;
+  ImageBitmapFormatUtils_YUV420P& operator=(ImageBitmapFormatUtils_YUV420P const &) = delete;
+  ImageBitmapFormatUtils_YUV420P& operator=(ImageBitmapFormatUtils_YUV420P&&) = delete;
+
+public:
+  static ImageBitmapFormatUtils_YUV420P& GetInstance();
+
+  virtual uint32_t NeededBufferSize(uint32_t aWidth, uint32_t aHeight) override
+  {
+    return aWidth * aHeight + 2 * (aWidth / 2) * (aHeight / 2);
+  }
+
+  virtual UniquePtr<ImagePixelLayout>
+  CreateDefaultLayout(uint32_t aWidth, uint32_t aHeight, uint32_t aStride) override;
+
+  virtual UniquePtr<ImagePixelLayout>
+  CreateCustomizedLayout(layers::Image* aImage) override;
+
+  virtual UniquePtr<ImagePixelLayout>
+  ConvertTo(ImageBitmapFormatUtils* aDstFormat, const uint8_t* aSrcBuffer, const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer) override
+  {
+    return aDstFormat->ConvertFrom(this, aSrcBuffer, aSrcLayout, aDstBuffer);
+  }
+
+  virtual UniquePtr<ImagePixelLayout>
+  ConvertFrom(ImageBitmapFormatUtils_RGBA32* aSrcFormat, const uint8_t* aSrcBuffer, const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer) override;
+
+  virtual UniquePtr<ImagePixelLayout>
+  ConvertFrom(ImageBitmapFormatUtils_BGRA32* aSrcFormat, const uint8_t* aSrcBuffer, const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer) override;
+
+  virtual UniquePtr<ImagePixelLayout>
+  ConvertFrom(ImageBitmapFormatUtils_YUV420P* aSrcFormat, const uint8_t* aSrcBuffer, const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer) override;
+
+  virtual bool
+  CanConvertFrom(ImageBitmapFormat aSrcFormat) override;
+};
+
+/*
+ * ImageBitmapFormatUtils.
+ */
+/* static */ UtilsUniquePtr
+ImageBitmapFormatUtils::GetUtils(ImageBitmapFormat aFormat)
+{
+  switch(aFormat)
+  {
+  case ImageBitmapFormat::RGBA32:
+    return UtilsUniquePtr(&ImageBitmapFormatUtils_RGBA32::GetInstance());
+  case ImageBitmapFormat::BGRA32:
+    return UtilsUniquePtr(&ImageBitmapFormatUtils_BGRA32::GetInstance());
+  case ImageBitmapFormat::RGB24:
+  case ImageBitmapFormat::BGR24:
+  case ImageBitmapFormat::GRAY8:
+  case ImageBitmapFormat::YUV444P:
+  case ImageBitmapFormat::YUV422P:
+    return nullptr;
+  case ImageBitmapFormat::YUV420P:
+    return UtilsUniquePtr(&ImageBitmapFormatUtils_YUV420P::GetInstance());
+  case ImageBitmapFormat::YUV420SP_NV12:
+  case ImageBitmapFormat::YUV420SP_NV21:
+  case ImageBitmapFormat::HSV:
+  case ImageBitmapFormat::Lab:
+  case ImageBitmapFormat::DEPTH:
+  default:
+    return nullptr;
+  }
+}
+
+/*
+ * ImageBitmapFormatUtils_RGBA32.
+ */
+/* static */ImageBitmapFormatUtils_RGBA32&
+ImageBitmapFormatUtils_RGBA32::GetInstance()
+{
+  static ImageBitmapFormatUtils_RGBA32 instance;
+  return instance;
+}
+
+UniquePtr<ImagePixelLayout>
+ImageBitmapFormatUtils_RGBA32::ConvertFrom(ImageBitmapFormatUtils_RGBA32* aSrcFormat,
+                                           const uint8_t* aSrcBuffer,
+                                           const ImagePixelLayout* aSrcLayout,
+                                           uint8_t* aDstBuffer)
+{
+  MOZ_ASSERT(aSrcFormat, "Convert color from a null utility object.");
+  MOZ_ASSERT(aSrcBuffer, "Convert color from a null buffer.");
+  MOZ_ASSERT(aSrcLayout, "Convert color from a null layout.");
+  MOZ_ASSERT(aDstBuffer, "Convert color to a null buffer.");
+
+  const nsTArray<ChannelPixelLayout>& channels = *aSrcLayout;
+  MOZ_ASSERT(channels.Length() == aSrcFormat->GetChannelCount(),
+             "The channel count is wrong.");
+
+  const uint32_t length = channels[0].mHeight * channels[0].mStride;
+
+  memcpy(aDstBuffer, aSrcBuffer, length);
+
+  return CreateDefaultLayout(channels[0].mWidth,
+                             channels[0].mHeight,
+                             channels[0].mStride);
+}
+
+UniquePtr<ImagePixelLayout>
+ImageBitmapFormatUtils_RGBA32::ConvertFrom(ImageBitmapFormatUtils_BGRA32* aSrcFormat,
+                                           const uint8_t* aSrcBuffer,
+                                           const ImagePixelLayout* aSrcLayout,
+                                           uint8_t* aDstBuffer)
+{
+  MOZ_ASSERT(aSrcFormat, "Convert color from a null utility object.");
+  MOZ_ASSERT(aSrcBuffer, "Convert color from a null buffer.");
+  MOZ_ASSERT(aSrcLayout, "Convert color from a null layout.");
+  MOZ_ASSERT(aDstBuffer, "Convert color to a null buffer.");
+
+  const nsTArray<ChannelPixelLayout>& channels = *aSrcLayout;
+  MOZ_ASSERT(channels.Length() == aSrcFormat->GetChannelCount(),
+             "The channel count is wrong.");
+
+  int rv = ABGRToARGB(aSrcBuffer, channels[0].mStride,
+                      aDstBuffer, channels[0].mStride,
+                      channels[0].mWidth, channels[0].mHeight);
+
+  if (NS_WARN_IF(rv != 0)) {
+    return nullptr;
+  }
+
+  return CreateDefaultLayout(channels[0].mWidth,
+                             channels[0].mHeight,
+                             channels[0].mStride);
+}
+
+UniquePtr<ImagePixelLayout>
+ImageBitmapFormatUtils_RGBA32::ConvertFrom(ImageBitmapFormatUtils_YUV420P* aSrcFormat,
+                                           const uint8_t* aSrcBuffer,
+                                           const ImagePixelLayout* aSrcLayout,
+                                           uint8_t* aDstBuffer)
+{
+  MOZ_ASSERT(aSrcFormat, "Convert color from a null utility object.");
+  MOZ_ASSERT(aSrcBuffer, "Convert color from a null buffer.");
+  MOZ_ASSERT(aSrcLayout, "Convert color from a null layout.");
+  MOZ_ASSERT(aDstBuffer, "Convert color to a null buffer.");
+
+  const nsTArray<ChannelPixelLayout>& channels = *aSrcLayout;
+  MOZ_ASSERT(channels.Length() == aSrcFormat->GetChannelCount(),
+             "The channel count is wrong.");
+
+  int rv = I420ToABGR(aSrcBuffer + channels[0].mOffset, channels[0].mStride,
+                      aSrcBuffer + channels[1].mOffset, channels[1].mStride,
+                      aSrcBuffer + channels[2].mOffset, channels[2].mStride,
+                      aDstBuffer, channels[0].mWidth * 4,
+                      channels[0].mWidth, channels[0].mHeight);
+
+  if (NS_WARN_IF(rv != 0)) {
+    return nullptr;
+  }
+
+  return CreateDefaultLayout(channels[0].mWidth,
+                             channels[0].mHeight,
+                             channels[0].mWidth * 4);
+}
+
+bool
+ImageBitmapFormatUtils_RGBA32::CanConvertFrom(ImageBitmapFormat aSrcFormat)
+{
+  if (aSrcFormat == ImageBitmapFormat::RGBA32 ||
+      aSrcFormat == ImageBitmapFormat::BGRA32 ||
+      aSrcFormat == ImageBitmapFormat::YUV420P) {
+    return true;
+  }
+
+  return false;
+}
+
+UniquePtr<ImagePixelLayout>
+ImageBitmapFormatUtils_RGBA32::CreateDefaultLayout(uint32_t aWidth,
+                                                   uint32_t aHeight,
+                                                   uint32_t aStride)
+{
+  UniquePtr<ImagePixelLayout> layout(new ImagePixelLayout(mChannels));
+
+  // set mChannels
+  for (uint8_t i = 0; i < mChannels; ++i) {
+    ChannelPixelLayout* channel = layout->AppendElement();
+    channel->mOffset = i;
+    channel->mWidth = aWidth;
+    channel->mHeight = aHeight;
+    channel->mDataType = ChannelPixelLayoutDataType::Uint8;
+    channel->mStride = aStride;
+    channel->mSkip = mChannels - 1;
+  }
+
+  return layout;
+}
+
+UniquePtr<ImagePixelLayout>
+ImageBitmapFormatUtils_RGBA32::CreateCustomizedLayout(layers::Image* aImage)
+{
+  MOZ_ASSERT(aImage, "Create layout from a null image.");
+
+  UniquePtr<ImagePixelLayout> layout(new ImagePixelLayout(mChannels));
+
+  // set mChannels
+  layers::CairoImage* src = static_cast<layers::CairoImage*> (aImage);
+  RefPtr<SourceSurface> surface = src->GetAsSourceSurface();
+  RefPtr<DataSourceSurface> dataSurface = surface->GetDataSurface();
+  gfx::DataSourceSurface::ScopedMap map(dataSurface, gfx::DataSourceSurface::READ);
+  if (!map.IsMapped()) {
+    return nullptr;
+  }
+
+  const IntSize surfaceSize = surface->GetSize();
+  const uint32_t surfaceStride = map.GetStride();
+
+  for (uint8_t i = 0; i < mChannels; ++i) {
+    ChannelPixelLayout* channel = layout->AppendElement();
+    channel->mOffset = i;
+    channel->mWidth = surfaceSize.width;
+    channel->mHeight = surfaceSize.height;
+    channel->mDataType = ChannelPixelLayoutDataType::Uint8;
+    channel->mStride = surfaceStride;
+    channel->mSkip = mChannels - 1;
+  }
+
+  return layout;
+}
+
+/*
+ * ImageBitmapFormatUtils_BGRA32.
+ */
+/* static */ImageBitmapFormatUtils_BGRA32&
+ImageBitmapFormatUtils_BGRA32::GetInstance()
+{
+  static ImageBitmapFormatUtils_BGRA32 instance;
+  return instance;
+}
+
+UniquePtr<ImagePixelLayout>
+ImageBitmapFormatUtils_BGRA32::ConvertFrom(ImageBitmapFormatUtils_RGBA32* aSrcFormat,
+                                           const uint8_t* aSrcBuffer,
+                                           const ImagePixelLayout* aSrcLayout,
+                                           uint8_t* aDstBuffer)
+{
+  MOZ_ASSERT(aSrcFormat, "Convert color from a null utility object.");
+  MOZ_ASSERT(aSrcBuffer, "Convert color from a null buffer.");
+  MOZ_ASSERT(aSrcLayout, "Convert color from a null layout.");
+  MOZ_ASSERT(aDstBuffer, "Convert color to a null buffer.");
+
+  const nsTArray<ChannelPixelLayout>& channels = *aSrcLayout;
+  MOZ_ASSERT(channels.Length() == aSrcFormat->GetChannelCount(),
+             "The channel count is wrong.");
+
+  int rv = ABGRToARGB(aSrcBuffer, channels[0].mStride,
+                      aDstBuffer, channels[0].mStride,
+                      channels[0].mWidth, channels[0].mHeight);
+
+  if (NS_WARN_IF(rv != 0)) {
+    return nullptr;
+  }
+
+  return CreateDefaultLayout(channels[0].mWidth,
+                             channels[0].mHeight,
+                             channels[0].mStride);
+}
+
+UniquePtr<ImagePixelLayout>
+ImageBitmapFormatUtils_BGRA32::ConvertFrom(ImageBitmapFormatUtils_BGRA32* aSrcFormat,
+                                           const uint8_t* aSrcBuffer,
+                                           const ImagePixelLayout* aSrcLayout,
+                                           uint8_t* aDstBuffer)
+{
+  MOZ_ASSERT(aSrcFormat, "Convert color from a null utility object.");
+  MOZ_ASSERT(aSrcBuffer, "Convert color from a null buffer.");
+  MOZ_ASSERT(aSrcLayout, "Convert color from a null layout.");
+  MOZ_ASSERT(aDstBuffer, "Convert color to a null buffer.");
+
+  const nsTArray<ChannelPixelLayout>& channels = *aSrcLayout;
+  MOZ_ASSERT(channels.Length() == aSrcFormat->GetChannelCount(),
+             "The channel count is wrong.");
+
+  const uint32_t length = channels[0].mHeight * channels[0].mStride;
+
+  memcpy(aDstBuffer, aSrcBuffer, length);
+
+  return CreateDefaultLayout(channels[0].mWidth,
+                             channels[0].mHeight,
+                             channels[0].mStride);
+}
+
+UniquePtr<ImagePixelLayout>
+ImageBitmapFormatUtils_BGRA32::ConvertFrom(ImageBitmapFormatUtils_YUV420P* aSrcFormat,
+                                           const uint8_t* aSrcBuffer,
+                                           const ImagePixelLayout* aSrcLayout,
+                                           uint8_t* aDstBuffer)
+{
+  MOZ_ASSERT(aSrcFormat, "Convert color from a null utility object.");
+  MOZ_ASSERT(aSrcBuffer, "Convert color from a null buffer.");
+  MOZ_ASSERT(aSrcLayout, "Convert color from a null layout.");
+  MOZ_ASSERT(aDstBuffer, "Convert color to a null buffer.");
+
+  const nsTArray<ChannelPixelLayout>& channels = *aSrcLayout;
+  MOZ_ASSERT(channels.Length() == aSrcFormat->GetChannelCount(),
+             "The channel count is wrong.");
+
+  int rv = I420ToARGB(aSrcBuffer + channels[0].mOffset, channels[0].mStride,
+                      aSrcBuffer + channels[1].mOffset, channels[1].mStride,
+                      aSrcBuffer + channels[2].mOffset, channels[2].mStride,
+                      aDstBuffer, channels[0].mWidth * 4,
+                      channels[0].mWidth, channels[0].mHeight);
+
+  if (NS_WARN_IF(rv != 0)) {
+    return nullptr;
+  }
+
+  return CreateDefaultLayout(channels[0].mWidth,
+                             channels[0].mHeight,
+                             channels[0].mWidth * 4);
+}
+
+bool
+ImageBitmapFormatUtils_BGRA32::CanConvertFrom(ImageBitmapFormat aSrcFormat)
+{
+  if (aSrcFormat == ImageBitmapFormat::RGBA32 ||
+      aSrcFormat == ImageBitmapFormat::BGRA32 ||
+      aSrcFormat == ImageBitmapFormat::YUV420P) {
+    return true;
+  }
+
+  return false;
+}
+
+UniquePtr<ImagePixelLayout>
+ImageBitmapFormatUtils_BGRA32::CreateDefaultLayout(uint32_t aWidth,
+                                                   uint32_t aHeight,
+                                                   uint32_t aStride)
+{
+  UtilsUniquePtr format = ImageBitmapFormatUtils::GetUtils(ImageBitmapFormat::RGBA32);
+  MOZ_ASSERT(format, "Cannot get a valid ImageBitmapFormatUtils instance.");
+
+  return format->CreateDefaultLayout(aWidth, aHeight, aStride);
+}
+
+UniquePtr<ImagePixelLayout>
+ImageBitmapFormatUtils_BGRA32::CreateCustomizedLayout(layers::Image* aImage)
+{
+  UtilsUniquePtr format = ImageBitmapFormatUtils::GetUtils(ImageBitmapFormat::RGBA32);
+  MOZ_ASSERT(format, "Cannot get a valid ImageBitmapFormatUtils instance.");
+
+  return format->CreateCustomizedLayout(aImage);
+}
+
+/*
+ * ImageBitmapFormatUtils_YUV420P.
+ */
+/* static */ImageBitmapFormatUtils_YUV420P&
+ImageBitmapFormatUtils_YUV420P::GetInstance()
+{
+  static ImageBitmapFormatUtils_YUV420P instance;
+  return instance;
+}
+
+UniquePtr<ImagePixelLayout>
+ImageBitmapFormatUtils_YUV420P::ConvertFrom(ImageBitmapFormatUtils_RGBA32* aSrcFormat,
+                                            const uint8_t* aSrcBuffer,
+                                            const ImagePixelLayout* aSrcLayout,
+                                            uint8_t* aDstBuffer)
+{
+  return nullptr;
+}
+
+UniquePtr<ImagePixelLayout>
+ImageBitmapFormatUtils_YUV420P::ConvertFrom(ImageBitmapFormatUtils_BGRA32* aSrcFormat,
+                                            const uint8_t* aSrcBuffer,
+                                            const ImagePixelLayout* aSrcLayout,
+                                            uint8_t* aDstBuffer)
+{
+  return nullptr;
+}
+
+UniquePtr<ImagePixelLayout>
+ImageBitmapFormatUtils_YUV420P::ConvertFrom(ImageBitmapFormatUtils_YUV420P* aSrcFormat,
+                                            const uint8_t* aSrcBuffer,
+                                            const ImagePixelLayout* aSrcLayout,
+                                            uint8_t* aDstBuffer)
+{
+  return nullptr;
+}
+
+bool
+ImageBitmapFormatUtils_YUV420P::CanConvertFrom(ImageBitmapFormat aSrcFormat)
+{
+  if (aSrcFormat == ImageBitmapFormat::RGBA32 ||
+      aSrcFormat == ImageBitmapFormat::BGRA32 ||
+      aSrcFormat == ImageBitmapFormat::YUV420P) {
+    return true;
+  }
+
+  return false;
+}
+
+UniquePtr<ImagePixelLayout>
+ImageBitmapFormatUtils_YUV420P::CreateDefaultLayout(uint32_t aWidth,
+                                                    uint32_t aHeight,
+                                                    uint32_t aStride)
+{
+  UniquePtr<ImagePixelLayout> layout(new ImagePixelLayout(mChannels));
+
+  // set mChannels
+  ChannelPixelLayout* ychannel = layout->AppendElement();
+  ChannelPixelLayout* uchannel = layout->AppendElement();
+  ChannelPixelLayout* vchannel = layout->AppendElement();
+  ychannel->mOffset = 0;
+  ychannel->mWidth  = aWidth;
+  ychannel->mHeight = aHeight;
+  ychannel->mDataType = ChannelPixelLayoutDataType::Uint8;
+  ychannel->mStride = aStride;
+  ychannel->mSkip   = 0; // aYSkip;
+
+  uchannel->mOffset = ychannel->mOffset + ychannel->mStride * ychannel->mHeight;
+  uchannel->mWidth  = aWidth / 2;
+  uchannel->mHeight = aHeight / 2;
+  uchannel->mDataType = ChannelPixelLayoutDataType::Uint8;
+  uchannel->mStride = aStride / 2;
+  uchannel->mSkip   = 0; // aUSkip;
+
+  vchannel->mOffset = uchannel->mOffset + uchannel->mStride * uchannel->mHeight;
+  vchannel->mWidth  = aWidth / 2;
+  vchannel->mHeight = aHeight / 2;
+  vchannel->mDataType = ChannelPixelLayoutDataType::Uint8;
+  vchannel->mStride = aStride / 2;
+  vchannel->mSkip   = 0; // aVSkip;
+
+  return layout;
+}
+
+UniquePtr<ImagePixelLayout>
+ImageBitmapFormatUtils_YUV420P::CreateCustomizedLayout(layers::Image* aImage)
+{
+  MOZ_ASSERT(aImage, "Create layout from a null image.");
+
+  UniquePtr<ImagePixelLayout> layout(new ImagePixelLayout(mChannels));
+
+  // set mChannels
+  layers::PlanarYCbCrImage* ycbcrImage = static_cast<layers::PlanarYCbCrImage*> (aImage);
+  layers::PlanarYCbCrData const *ycbcrLayout = ycbcrImage->GetData();
+  if (!ycbcrLayout) {
+    // something wrong
+    return nullptr;
+  }
+
+  ChannelPixelLayout* ychannel = layout->AppendElement();
+  ChannelPixelLayout* uchannel = layout->AppendElement();
+  ChannelPixelLayout* vchannel = layout->AppendElement();
+
+  ychannel->mOffset = 0;
+  uchannel->mOffset = ychannel->mOffset + ycbcrLayout->mYStride * ycbcrLayout->mYSize.height;
+  vchannel->mOffset = uchannel->mOffset + ycbcrLayout->mCbCrStride * ycbcrLayout->mCbCrSize.height;
+  ychannel->mWidth = ycbcrLayout->mYSize.width;
+  ychannel->mHeight = ycbcrLayout->mYSize.height;
+  ychannel->mDataType = ChannelPixelLayoutDataType::Uint8;
+  ychannel->mStride = ycbcrLayout->mYStride;
+  ychannel->mSkip = ycbcrLayout->mYSkip;
+  uchannel->mWidth = ycbcrLayout->mCbCrSize.width;
+  uchannel->mHeight = ycbcrLayout->mCbCrSize.height;
+  uchannel->mDataType = ChannelPixelLayoutDataType::Uint8;
+  uchannel->mStride = ycbcrLayout->mCbCrStride;
+  uchannel->mSkip = ycbcrLayout->mCbSkip;
+  vchannel->mWidth = ycbcrLayout->mCbCrSize.width;
+  vchannel->mHeight = ycbcrLayout->mCbCrSize.height;
+  vchannel->mDataType = ChannelPixelLayoutDataType::Uint8;
+  vchannel->mStride = ycbcrLayout->mCbCrStride;
+  vchannel->mSkip = ycbcrLayout->mCrSkip;
+
+  return layout;
+}
+
+/*
+ * Global functions.
+ */
+bool
+IsSupportedFormat(ImageBitmapFormat aFormat)
+{
+  if (aFormat == ImageBitmapFormat::RGBA32 ||
+      aFormat == ImageBitmapFormat::BGRA32) {
+    return true;
+  }
+
+  return false;
+}
+
+UniquePtr<ImagePixelLayout>
+CreateDefaultLayout(ImageBitmapFormat aFormat, uint32_t aWidth,
+                    uint32_t aHeight, uint32_t aStride)
+{
+  UtilsUniquePtr format = ImageBitmapFormatUtils::GetUtils(aFormat);
+  MOZ_ASSERT(format, "Cannot get a valid ImageBitmapFormatUtils instance.");
+
+  return format->CreateDefaultLayout(aWidth, aHeight, aStride);
+}
+
+UniquePtr<ImagePixelLayout>
+CreateCustomizedLayout(ImageBitmapFormat aFormat, layers::Image* aImage)
+{
+  MOZ_ASSERT(aImage, "Create layout from a null image.");
+
+  UtilsUniquePtr format = ImageBitmapFormatUtils::GetUtils(aFormat);
+  MOZ_ASSERT(format, "Cannot get a valid ImageBitmapFormatUtils instance.");
+
+  return format->CreateCustomizedLayout(aImage);
+}
+
+uint8_t
+GetChannelCountOfImageFormat(ImageBitmapFormat aFormat)
+{
+  UtilsUniquePtr format = ImageBitmapFormatUtils::GetUtils(aFormat);
+  MOZ_ASSERT(format, "Cannot get a valid ImageBitmapFormatUtils instance.");
+
+  return format->GetChannelCount();
+}
+
+uint32_t
+CalculateNeededBufferSize(ImageBitmapFormat aFormat,
+                          uint32_t aWidth, uint32_t aHeight)
+{
+  UtilsUniquePtr format = ImageBitmapFormatUtils::GetUtils(aFormat);
+  MOZ_ASSERT(format, "Cannot get a valid ImageBitmapFormatUtils instance.");
+
+  return format->NeededBufferSize(aWidth, aHeight);
+}
+
+UniquePtr<ImagePixelLayout>
+CopyAndConvertImageData(ImageBitmapFormat aSrcFormat,
+                        const uint8_t* aSrcBuffer,
+                        const ImagePixelLayout* aSrcLayout,
+                        ImageBitmapFormat aDstFormat,
+                        uint8_t* aDstBuffer)
+{
+  MOZ_ASSERT(aSrcBuffer, "Convert color from a null buffer.");
+  MOZ_ASSERT(aSrcLayout, "Convert color from a null layout.");
+  MOZ_ASSERT(aDstBuffer, "Convert color to a null buffer.");
+
+  UtilsUniquePtr srcFormat = ImageBitmapFormatUtils::GetUtils(aSrcFormat);
+  UtilsUniquePtr dstFormat = ImageBitmapFormatUtils::GetUtils(aDstFormat);
+  MOZ_ASSERT(srcFormat, "Cannot get a valid ImageBitmapFormatUtils instance.");
+  MOZ_ASSERT(dstFormat, "Cannot get a valid ImageBitmapFormatUtils instance.");
+
+  return srcFormat->ConvertTo(dstFormat.get(), aSrcBuffer, aSrcLayout, aDstBuffer);
+}
+
+ImageBitmapFormat
+FindBestMatchingFromat(ImageBitmapFormat aSrcFormat,
+                       const Sequence<ImageBitmapFormat>& aCandidates) {
+
+  for(auto& candidate : aCandidates) {
+    UtilsUniquePtr candidateFormat = ImageBitmapFormatUtils::GetUtils(candidate);
+    MOZ_ASSERT(candidateFormat, "Cannot get a valid ImageBitmapFormatUtils instance.");
+
+    if (candidateFormat->CanConvertFrom(aSrcFormat)) {
+      return candidate;
+    }
+  }
+
+  return ImageBitmapFormat::EndGuard_;
+}
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/canvas/ImageBitmapUtils.h
@@ -0,0 +1,99 @@
+/* -*- 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_ImageBitmapUtils_h
+#define mozilla_dom_ImageBitmapUtils_h
+
+#include "mozilla/UniquePtr.h"
+#include "nsTArrayForwardDeclare.h"
+
+namespace mozilla {
+namespace dom {
+
+struct ChannelPixelLayout;
+template<typename> class Sequence;
+
+using ImagePixelLayout = nsTArray<ChannelPixelLayout>;
+
+/*
+ * This function creates an ImagePixelLayout object which describes the
+ * default layout of the given ImageBitmapFormat with the given width, height
+ * and stride.
+ */
+UniquePtr<ImagePixelLayout>
+CreateDefaultLayout(ImageBitmapFormat aFormat, uint32_t aWidth, uint32_t aHeight, uint32_t aStride);
+
+/*
+ * This function extracts information from the aImage parameter to customize
+ * the ImagePixelLayout object, that is, this function creates a customized
+ * ImagePixelLayout object which exactly describes the pixel layout of the
+ * given aImage.
+ */
+UniquePtr<ImagePixelLayout>
+CreateCustomizedLayout(ImageBitmapFormat aFormat, layers::Image* aImage);
+
+/*
+ * Check whether or not the given ImageBitmapFormat is supported.
+ */
+bool
+IsSupportedFormat(ImageBitmapFormat aFormat);
+
+/*
+ * Get the number of channels of the given ImageBitmapFormat.
+ */
+uint8_t
+GetChannelCountOfImageFormat(ImageBitmapFormat aFormat);
+
+/*
+ * Get the needed buffer size to store the image data in the given
+ * ImageBitmapFormat with the given width and height.
+ */
+uint32_t
+CalculateNeededBufferSize(ImageBitmapFormat aFormat,
+                          uint32_t aWidth, uint32_t aHeight);
+
+/*
+ * This function always copies the image data in _aSrcBuffer_ into _aDstBuffer_
+ * and it also performs color conversion if the _aSrcFormat_ and the
+ * _aDstFormat_ are different.
+ *
+ * The source image is stored in the _aSrcBuffer_ and the corresponding pixel
+ * layout is described by the _aSrcLayout_.
+ *
+ * The copied and converted image will be stored in the _aDstBuffer_, which
+ * should be allocated with enough size before invoking this function and the
+ * needed size could be found by the ImageBitmapFormatUtils::NeededBufferSize()
+ * method.
+ *
+ * The returned ImagePixelLayout object describes the pixel layout of the result
+ * image and will be null if on failure.
+ */
+UniquePtr<ImagePixelLayout>
+CopyAndConvertImageData(ImageBitmapFormat aSrcFormat,
+                        const uint8_t* aSrcBuffer,
+                        const ImagePixelLayout* aSrcLayout,
+                        ImageBitmapFormat aDstFormat,
+                        uint8_t* aDstBuffer);
+
+/*
+ * This function tries to find the best ImageBitmapFormat, from the aCandiates,
+ * which can be converted from the aSrcFormat. The algorithm now merely returns
+ * the FIRST one, from the aCandidates, which can be converted from the
+ * aSrcFormat.
+ *
+ * TODO: The algorithm should be updated after we implement optimizations for
+ *       different platforms (different kinds of layers::Image), considering
+ *       that some conversion might be cheaper through hardware.
+ */
+ImageBitmapFormat
+FindBestMatchingFromat(ImageBitmapFormat aSrcFormat,
+                       const Sequence<ImageBitmapFormat>& aCandidates);
+
+} // namespace dom
+} // namespace mozilla
+
+
+#endif // mozilla_dom_ImageBitmapUtils_h
--- a/dom/canvas/moz.build
+++ b/dom/canvas/moz.build
@@ -45,16 +45,17 @@ EXPORTS.mozilla.dom += [
 UNIFIED_SOURCES += [
     'CanvasImageCache.cpp',
     'CanvasRenderingContext2D.cpp',
     'CanvasRenderingContextHelper.cpp',
     'CanvasUtils.cpp',
     'DocumentRendererChild.cpp',
     'DocumentRendererParent.cpp',
     'ImageBitmap.cpp',
+    'ImageBitmapUtils.cpp',
     'ImageData.cpp',
     'OffscreenCanvas.cpp',
 ]
 
 # WebGL Sources
 UNIFIED_SOURCES += [
     'MurmurHash3.cpp',
     'TexUnpackBlob.cpp',