Bug 1141979 - part6 - implement ImageBitmapFormatUtils; r=jrmuizel
authorKaku Kuo <tkuo@mozilla.com>
Mon, 30 May 2016 15:48:02 +0800
changeset 339144 f6c8fafec1d9d2935819ce8e36a66f5e08b2b491
parent 339143 ee4b94da77fa028c846e49785e69df7c78fa6375
child 339145 7ac2b72200dd34a1de721c9b15bb05f902ee363a
push id6249
push userjlund@mozilla.com
push dateMon, 01 Aug 2016 13:59:36 +0000
treeherdermozilla-beta@bad9d4f5bf7e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjrmuizel
bugs1141979
milestone49.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1141979 - part6 - implement ImageBitmapFormatUtils; r=jrmuizel MozReview-Commit-ID: 1RyJBjP9W9Q
dom/canvas/ImageBitmapColorUtils.cpp
dom/canvas/ImageBitmapColorUtils.h
dom/canvas/ImageBitmapUtils.cpp
dom/canvas/ImageBitmapUtils.h
dom/canvas/gtest/TestImageBitmapColorUtils.cpp
dom/canvas/gtest/moz.build
dom/canvas/moz.build
new file mode 100644
--- /dev/null
+++ b/dom/canvas/ImageBitmapColorUtils.cpp
@@ -0,0 +1,2080 @@
+/* -*- 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 "ImageBitmapColorUtils.h"
+
+namespace mozilla {
+namespace dom {
+
+/*
+ * Utility function form libyuv source files.
+ */
+static __inline int32 clamp0(int32 v) {
+  return ((-(v) >> 31) & (v));
+}
+
+static __inline int32 clamp255(int32 v) {
+  return (((255 - (v)) >> 31) | (v)) & 255;
+}
+
+static __inline uint32 Clamp(int32 val) {
+  int v = clamp0(val);
+  return (uint32)(clamp255(v));
+}
+
+#define YG 74 /* (int8)(1.164 * 64 + 0.5) */
+
+#define UB 127 /* min(63,(int8)(2.018 * 64)) */
+#define UG -25 /* (int8)(-0.391 * 64 - 0.5) */
+#define UR 0
+
+#define VB 0
+#define VG -52 /* (int8)(-0.813 * 64 - 0.5) */
+#define VR 102 /* (int8)(1.596 * 64 + 0.5) */
+
+// Bias
+#define BB UB * 128 + VB * 128
+#define BG UG * 128 + VG * 128
+#define BR UR * 128 + VR * 128
+
+static __inline void
+YuvPixel(uint8 y, uint8 u, uint8 v, uint8* b, uint8* g, uint8* r)
+{
+  int32 y1 = ((int32)(y) - 16) * YG;
+  *b = Clamp((int32)((u * UB + v * VB) - (BB) + y1) >> 6);
+  *g = Clamp((int32)((u * UG + v * VG) - (BG) + y1) >> 6);
+  *r = Clamp((int32)((u * UR + v * VR) - (BR) + y1) >> 6);
+}
+
+static __inline int
+RGBToY(uint8 r, uint8 g, uint8 b)
+{
+  return (66 * r + 129 * g +  25 * b + 0x1080) >> 8;
+}
+
+static __inline int
+RGBToU(uint8 r, uint8 g, uint8 b)
+{
+  return (112 * b - 74 * g - 38 * r + 0x8080) >> 8;
+}
+
+static __inline int
+RGBToV(uint8 r, uint8 g, uint8 b)
+{
+  return (112 * r - 94 * g - 18 * b + 0x8080) >> 8;
+}
+
+/*
+ * Generic functions.
+ */
+template<int aSrcRIndex, int aSrcGIndex, int aSrcBIndex,
+         int aDstRIndex, int aDstGIndex, int aDstBIndex, int aDstAIndex>
+static int
+RGBFamilyToRGBAFamily(const uint8_t* aSrcBuffer, int aSrcStride,
+                      uint8_t* aDstBuffer, int aDstStride,
+                      int aWidth, int aHeight)
+{
+  static_assert(aSrcRIndex == 0 || aSrcRIndex == 2, "Wrong SrcR index.");
+  static_assert(aSrcGIndex == 1, "Wrong SrcG index.");
+  static_assert(aSrcBIndex == 0 || aSrcBIndex == 2, "Wrong SrcB index.");
+  static_assert(aDstRIndex == 0 || aDstRIndex == 2, "Wrong DstR index.");
+  static_assert(aDstGIndex == 1, "Wrong DstG index.");
+  static_assert(aDstBIndex == 0 || aDstBIndex == 2, "Wrong DstB index.");
+  static_assert(aDstAIndex == 3, "Wrong DstA index.");
+
+  for (int i = 0; i < aHeight; ++i) {
+    const uint8_t* srcBuffer = aSrcBuffer + aSrcStride * i;
+    uint8_t* dstBuffer = aDstBuffer + aDstStride * i;
+
+    for (int j = 0; j < aWidth; ++j) {
+      uint8_t r = *(srcBuffer + aSrcRIndex);
+      uint8_t g = *(srcBuffer + aSrcGIndex);
+      uint8_t b = *(srcBuffer + aSrcBIndex);
+      *(dstBuffer + aDstRIndex) = r;
+      *(dstBuffer + aDstGIndex) = g;
+      *(dstBuffer + aDstBIndex) = b;
+      *(dstBuffer + aDstAIndex) = 255;
+      srcBuffer += 3;
+      dstBuffer += 4;
+    }
+  }
+
+  return 0;
+}
+
+template<int aSrcRIndex, int aSrcGIndex, int aSrcBIndex,
+         int aDstRIndex, int aDstGIndex, int aDstBIndex>
+static int
+RGBAFamilyToRGBFamily(const uint8_t* aSrcBuffer, int aSrcStride,
+                      uint8_t* aDstBuffer, int aDstStride,
+                      int aWidth, int aHeight)
+{
+  static_assert(aSrcRIndex == 0 || aSrcRIndex == 2, "Wrong SrcR index.");
+  static_assert(aSrcGIndex == 1, "Wrong SrcG index.");
+  static_assert(aSrcBIndex == 0 || aSrcBIndex == 2, "Wrong SrcB index.");
+  static_assert(aDstRIndex == 0 || aDstRIndex == 2, "Wrong DstR index.");
+  static_assert(aDstGIndex == 1, "Wrong DstG index.");
+  static_assert(aDstBIndex == 0 || aDstBIndex == 2, "Wrong DstB index.");
+
+  for (int i = 0; i < aHeight; ++i) {
+    const uint8_t* srcBuffer = aSrcBuffer + aSrcStride * i;
+    uint8_t* dstBuffer = aDstBuffer + aDstStride * i;
+
+    for (int j = 0; j < aWidth; ++j) {
+      uint8_t r = *(srcBuffer + aSrcRIndex);
+      uint8_t g = *(srcBuffer + aSrcGIndex);
+      uint8_t b = *(srcBuffer + aSrcBIndex);
+      *(dstBuffer + aDstRIndex) = r;
+      *(dstBuffer + aDstGIndex) = g;
+      *(dstBuffer + aDstBIndex) = b;
+      srcBuffer += 4;
+      dstBuffer += 3;
+    }
+  }
+
+  return 0;
+}
+
+template<int aPixel1YOffset, int aPixel1UOffset, int aPixel1VOffset,
+         int aPixel2YOffset, int aPixel2UOffset, int aPixel2VOffset,
+         int aYStep, int aUStep, int aVStep,
+         int aRIndex, int aGIndex, int aBIndex>
+void
+YUVFamilyToRGBFamily_Row(const uint8_t* aYBuffer,
+                         const uint8_t* aUBuffer,
+                         const uint8_t* aVBuffer,
+                         uint8_t* aDstBuffer,
+                         int aWidth)
+{
+  static_assert(aRIndex == 0 || aRIndex == 2, "Wrong R index.");
+  static_assert(aGIndex == 1, "Wrong G index.");
+  static_assert(aBIndex == 0 || aBIndex == 2, "Wrong B index.");
+
+  for (int j = 0; j < aWidth - 1; j += 2) {
+    YuvPixel(aYBuffer[aPixel1YOffset], aUBuffer[aPixel1UOffset], aVBuffer[aPixel1VOffset],
+             aDstBuffer + aBIndex, aDstBuffer + aGIndex, aDstBuffer + aRIndex);
+    YuvPixel(aYBuffer[aPixel2YOffset], aUBuffer[aPixel2UOffset], aVBuffer[aPixel2VOffset],
+             aDstBuffer + aBIndex + 3, aDstBuffer + aGIndex + 3, aDstBuffer + aRIndex + 3);
+    aYBuffer += aYStep;
+    aUBuffer += aUStep;
+    aVBuffer += aVStep;
+    aDstBuffer += 6;
+  }
+
+  if (aWidth & 1) {
+    YuvPixel(aYBuffer[aPixel1YOffset], aUBuffer[aPixel1UOffset], aVBuffer[aPixel1VOffset],
+             aDstBuffer + aBIndex, aDstBuffer + aGIndex, aDstBuffer + aRIndex);
+  }
+}
+
+template<int aPixel1YOffset, int aPixel1UOffset, int aPixel1VOffset,
+         int aPixel2YOffset, int aPixel2UOffset, int aPixel2VOffset,
+         int aYStep, int aUStep, int aVStep,
+         int aRIndex, int aGIndex, int aBIndex, int aAIndex>
+void
+YUVFamilyToRGBAFamily_Row(const uint8_t* aYBuffer,
+                          const uint8_t* aUBuffer,
+                          const uint8_t* aVBuffer,
+                          uint8_t* aDstBuffer,
+                          int aWidth)
+{
+  static_assert(aRIndex == 0 || aRIndex == 2, "Wrong R index.");
+  static_assert(aGIndex == 1, "Wrong G index.");
+  static_assert(aBIndex == 0 || aBIndex == 2, "Wrong B index.");
+  static_assert(aAIndex == 3, "Wrong A index.");
+
+  for (int j = 0; j < aWidth - 1; j += 2) {
+    YuvPixel(aYBuffer[aPixel1YOffset], aUBuffer[aPixel1UOffset], aVBuffer[aPixel1VOffset],
+             aDstBuffer + aBIndex, aDstBuffer + aGIndex, aDstBuffer + aRIndex);
+    YuvPixel(aYBuffer[aPixel2YOffset], aUBuffer[aPixel2UOffset], aVBuffer[aPixel2VOffset],
+             aDstBuffer + aBIndex + 4, aDstBuffer + aGIndex + 4, aDstBuffer + aRIndex + 4);
+    aDstBuffer[aAIndex] = 255;
+    aDstBuffer[aAIndex + 4] = 255;
+
+    aYBuffer += aYStep;
+    aUBuffer += aUStep;
+    aVBuffer += aVStep;
+    aDstBuffer += 8;
+  }
+
+  if (aWidth & 1) {
+    YuvPixel(aYBuffer[aPixel1YOffset], aUBuffer[aPixel1UOffset], aVBuffer[aPixel1VOffset],
+             aDstBuffer + aBIndex, aDstBuffer + aGIndex, aDstBuffer + aRIndex);
+    aDstBuffer[aAIndex] = 255;
+  }
+}
+
+template< int aRIndex, int aGIndex, int aBIndex>
+static void
+RGBFamilyToY_Row(const uint8_t* aSrcBuffer, uint8_t* aYBuffer, int aWidth)
+{
+  static_assert(aRIndex == 0 || aRIndex == 2, "Wrong R index.");
+  static_assert(aGIndex == 1, "Wrong G index.");
+  static_assert(aBIndex == 0 || aBIndex == 2, "Wrong B index.");
+
+  for (int j = 0; j < aWidth - 1; j += 2) {
+    aYBuffer[0] = RGBToY(aSrcBuffer[aRIndex], aSrcBuffer[aGIndex], aSrcBuffer[aBIndex]);
+    aYBuffer[1] = RGBToY(aSrcBuffer[aRIndex + 3], aSrcBuffer[aGIndex + 3], aSrcBuffer[aBIndex + 3]);
+
+    aYBuffer += 2;
+    aSrcBuffer += 3 * 2;
+  }
+
+  if (aWidth & 1) {
+    aYBuffer[0] = RGBToY(aSrcBuffer[aRIndex], aSrcBuffer[aGIndex], aSrcBuffer[aBIndex]);
+  }
+}
+
+template< int aRIndex, int aGIndex, int aBIndex, int aUStep, int aVStep>
+static void
+RGBFamilyToUV_Row(const uint8_t* aSrcBuffer, int aSrcStride,
+                  uint8_t* aUBuffer, uint8_t* aVBuffer, int aWidth)
+{
+  static_assert(aRIndex == 0 || aRIndex == 2, "Wrong R index.");
+  static_assert(aGIndex == 1, "Wrong G index.");
+  static_assert(aBIndex == 0 || aBIndex == 2, "Wrong B index.");
+
+  uint8_t averageR = 0;
+  uint8_t averageG = 0;
+  uint8_t averageB = 0;
+
+  const uint8_t* aSrcBufferNextRow = aSrcBuffer + aSrcStride;
+  for (int j = 0; j < aWidth - 1; j += 2) {
+    averageR = (aSrcBuffer[aRIndex] + aSrcBuffer[aRIndex + 3] + aSrcBufferNextRow[aRIndex] + aSrcBufferNextRow[aRIndex + 3]) >> 2;
+    averageG = (aSrcBuffer[aGIndex] + aSrcBuffer[aGIndex + 3] + aSrcBufferNextRow[aGIndex] + aSrcBufferNextRow[aGIndex + 3]) >> 2;
+    averageB = (aSrcBuffer[aBIndex] + aSrcBuffer[aBIndex + 3] + aSrcBufferNextRow[aBIndex] + aSrcBufferNextRow[aBIndex + 3]) >> 2;
+
+    aUBuffer[0] = RGBToU(averageR, averageG, averageB);
+    aVBuffer[0] = RGBToV(averageR, averageG, averageB);
+
+    aUBuffer += aUStep;
+    aVBuffer += aVStep;
+    aSrcBuffer += 3 * 2;
+    aSrcBufferNextRow += 3 * 2;
+  }
+
+  if (aWidth & 1) {
+    averageR = (aSrcBuffer[aRIndex] + aSrcBufferNextRow[aRIndex]) >> 1;
+    averageG = (aSrcBuffer[aGIndex] + aSrcBufferNextRow[aGIndex]) >> 1;
+    averageB = (aSrcBuffer[aBIndex] + aSrcBufferNextRow[aBIndex]) >> 1;
+
+    aUBuffer[0] = RGBToU(averageR, averageG, averageB);
+    aVBuffer[0] = RGBToV(averageR, averageG, averageB);
+  }
+}
+
+template< int aRIndex, int aGIndex, int aBIndex>
+static void
+RGBAFamilyToY_Row(const uint8_t* aSrcBuffer, uint8_t* aYBuffer, int aWidth)
+{
+  static_assert(aRIndex == 0 || aRIndex == 2, "Wrong R index.");
+  static_assert(aGIndex == 1, "Wrong G index.");
+  static_assert(aBIndex == 0 || aBIndex == 2, "Wrong B index.");
+
+  for (int j = 0; j < aWidth - 1; j += 2) {
+    aYBuffer[0] = RGBToY(aSrcBuffer[aRIndex], aSrcBuffer[aGIndex], aSrcBuffer[aBIndex]);
+    aYBuffer[1] = RGBToY(aSrcBuffer[aRIndex + 4], aSrcBuffer[aGIndex + 4], aSrcBuffer[aBIndex + 4]);
+
+    aYBuffer += 2;
+    aSrcBuffer += 4 * 2;
+  }
+
+  if (aWidth & 1) {
+    aYBuffer[0] = RGBToY(aSrcBuffer[aRIndex], aSrcBuffer[aGIndex], aSrcBuffer[aBIndex]);
+  }
+}
+
+template< int aRIndex, int aGIndex, int aBIndex, int aUStep, int aVStep>
+static void
+RGBAFamilyToUV_Row(const uint8_t* aSrcBuffer, int aSrcStride,
+                   uint8_t* aUBuffer, uint8_t* aVBuffer, int aWidth)
+{
+  static_assert(aRIndex == 0 || aRIndex == 2, "Wrong R index.");
+  static_assert(aGIndex == 1, "Wrong G index.");
+  static_assert(aBIndex == 0 || aBIndex == 2, "Wrong B index.");
+
+  uint8_t averageR = 0;
+  uint8_t averageG = 0;
+  uint8_t averageB = 0;
+
+  const uint8_t* aSrcBufferNextRow = aSrcBuffer + aSrcStride;
+  for (int j = 0; j < aWidth - 1; j += 2) {
+    averageR = (aSrcBuffer[aRIndex] + aSrcBuffer[aRIndex + 4] + aSrcBufferNextRow[aRIndex] + aSrcBufferNextRow[aRIndex + 4]) >> 2;
+    averageG = (aSrcBuffer[aGIndex] + aSrcBuffer[aGIndex + 4] + aSrcBufferNextRow[aGIndex] + aSrcBufferNextRow[aGIndex + 4]) >> 2;
+    averageB = (aSrcBuffer[aBIndex] + aSrcBuffer[aBIndex + 4] + aSrcBufferNextRow[aBIndex] + aSrcBufferNextRow[aBIndex + 4]) >> 2;
+
+    aUBuffer[0] = RGBToU(averageR, averageG, averageB);
+    aVBuffer[0] = RGBToV(averageR, averageG, averageB);
+
+    aUBuffer += aUStep;
+    aVBuffer += aVStep;
+    aSrcBuffer += 4 * 2;
+    aSrcBufferNextRow += 4 * 2;
+  }
+
+  if (aWidth & 1) {
+    averageR = (aSrcBuffer[aRIndex] + aSrcBufferNextRow[aRIndex]) >> 1;
+    averageG = (aSrcBuffer[aGIndex] + aSrcBufferNextRow[aGIndex]) >> 1;
+    averageB = (aSrcBuffer[aBIndex] + aSrcBufferNextRow[aBIndex]) >> 1;
+
+    aUBuffer[0] = RGBToU(averageR, averageG, averageB);
+    aVBuffer[0] = RGBToV(averageR, averageG, averageB);
+  }
+}
+
+/*
+ * RGB family -> RGBA family.
+ */
+int
+RGB24ToRGBA32(const uint8_t* aSrcBuffer, int aSrcStride,
+              uint8_t* aDstBuffer, int aDstStride,
+              int aWidth, int aHeight)
+{
+  return RGBFamilyToRGBAFamily<0, 1, 2, 0, 1, 2, 3>(aSrcBuffer, aSrcStride,
+                                                    aDstBuffer, aDstStride,
+                                                    aWidth, aHeight);
+}
+
+int
+BGR24ToRGBA32(const uint8_t* aSrcBuffer, int aSrcStride,
+              uint8_t* aDstBuffer, int aDstStride,
+              int aWidth, int aHeight)
+{
+  return RGBFamilyToRGBAFamily<2, 1, 0, 0, 1, 2, 3>(aSrcBuffer, aSrcStride,
+                                                    aDstBuffer, aDstStride,
+                                                    aWidth, aHeight);
+}
+
+int
+RGB24ToBGRA32(const uint8_t* aSrcBuffer, int aSrcStride,
+              uint8_t* aDstBuffer, int aDstStride,
+              int aWidth, int aHeight)
+{
+  return RGBFamilyToRGBAFamily<0, 1, 2, 2, 1, 0, 3>(aSrcBuffer, aSrcStride,
+                                                    aDstBuffer, aDstStride,
+                                                    aWidth, aHeight);
+}
+
+int
+BGR24ToBGRA32(const uint8_t* aSrcBuffer, int aSrcStride,
+              uint8_t* aDstBuffer, int aDstStride,
+              int aWidth, int aHeight)
+{
+  return RGBFamilyToRGBAFamily<2, 1, 0, 2, 1, 0, 3>(aSrcBuffer, aSrcStride,
+                                                    aDstBuffer, aDstStride,
+                                                    aWidth, aHeight);
+}
+
+/*
+ * RGBA family -> RGB family.
+ */
+int
+RGBA32ToRGB24(const uint8_t* aSrcBuffer, int aSrcStride,
+              uint8_t* aDstBuffer, int aDstStride,
+              int aWidth, int aHeight)
+{
+  return RGBAFamilyToRGBFamily<0, 1, 2, 0, 1, 2>(aSrcBuffer, aSrcStride,
+                                                 aDstBuffer, aDstStride,
+                                                 aWidth, aHeight);
+}
+
+int
+BGRA32ToRGB24(const uint8_t* aSrcBuffer, int aSrcStride,
+              uint8_t* aDstBuffer, int aDstStride,
+              int aWidth, int aHeight)
+{
+  return RGBAFamilyToRGBFamily<2, 1, 0, 0, 1, 2>(aSrcBuffer, aSrcStride,
+                                                 aDstBuffer, aDstStride,
+                                                 aWidth, aHeight);
+}
+
+int
+RGBA32ToBGR24(const uint8_t* aSrcBuffer, int aSrcStride,
+              uint8_t* aDstBuffer, int aDstStride,
+              int aWidth, int aHeight)
+{
+  return RGBAFamilyToRGBFamily<0, 1, 2, 2, 1, 0>(aSrcBuffer, aSrcStride,
+                                                 aDstBuffer, aDstStride,
+                                                 aWidth, aHeight);
+}
+
+int
+BGRA32ToBGR24(const uint8_t* aSrcBuffer, int aSrcStride,
+              uint8_t* aDstBuffer, int aDstStride,
+              int aWidth, int aHeight)
+{
+  return RGBAFamilyToRGBFamily<2, 1, 0, 2, 1, 0>(aSrcBuffer, aSrcStride,
+                                                 aDstBuffer, aDstStride,
+                                                 aWidth, aHeight);
+}
+
+/*
+ * Among RGB family.
+ */
+int
+RGB24Copy(const uint8_t* aSrcBuffer, int aSrcStride,
+          uint8_t* aDstBuffer, int aDstStride,
+          int aWidth, int aHeight)
+{
+  MOZ_ASSERT(aSrcStride == aDstStride, "RGB24Copy: aSrcStride != aDstStride");
+
+  const uint32_t length = aHeight * aDstStride;
+  memcpy(aDstBuffer, aSrcBuffer, length);
+  return 0;
+}
+
+int
+RGB24ToBGR24(const uint8_t* aSrcBuffer, int aSrcStride,
+             uint8_t* aDstBuffer, int aDstStride,
+             int aWidth, int aHeight)
+{
+  for (int i = 0; i < aHeight; ++i) {
+    const uint8_t* srcBuffer = aSrcBuffer + aSrcStride * i;
+    uint8_t* dstBuffer = aDstBuffer + aDstStride * i;
+
+    for (int j = 0; j < aWidth; ++j) {
+      *(dstBuffer + 0) = *(srcBuffer + 2);
+      *(dstBuffer + 1) = *(srcBuffer + 1);
+      *(dstBuffer + 2) = *(srcBuffer + 0);
+      srcBuffer += 3;
+      dstBuffer += 3;
+    }
+  }
+
+  return 0;
+}
+
+/*
+ * YUV family -> RGB family.
+ */
+int
+YUV444PToRGB24(const uint8_t* aYBuffer, int aYStride,
+               const uint8_t* aUBuffer, int aUStride,
+               const uint8_t* aVBuffer, int aVStride,
+               uint8_t* aDstBuffer, int aDstStride,
+               int aWidth, int aHeight)
+{
+  for (int i = 0; i < aHeight; ++i) {
+    const uint8_t* yBuffer = aYBuffer + aYStride * i;
+    const uint8_t* uBuffer = aUBuffer + aUStride * i;
+    const uint8_t* vBuffer = aVBuffer + aVStride * i;
+    uint8_t* dstBuffer = aDstBuffer + aDstStride * i;
+
+    YUVFamilyToRGBFamily_Row<0, 0, 0, 1, 1, 1, 2, 2, 2, 0, 1, 2>(yBuffer,
+                                                                 uBuffer,
+                                                                 vBuffer,
+                                                                 dstBuffer,
+                                                                 aWidth);
+  }
+
+  return 0;
+}
+
+int
+YUV422PToRGB24(const uint8_t* aYBuffer, int aYStride,
+               const uint8_t* aUBuffer, int aUStride,
+               const uint8_t* aVBuffer, int aVStride,
+               uint8_t* aDstBuffer, int aDstStride,
+               int aWidth, int aHeight)
+{
+  for (int i = 0; i < aHeight; ++i) {
+    const uint8_t* yBuffer = aYBuffer + aYStride * i;
+    const uint8_t* uBuffer = aUBuffer + aUStride * i;
+    const uint8_t* vBuffer = aVBuffer + aVStride * i;
+    uint8_t* dstBuffer = aDstBuffer + aDstStride * i;
+
+    YUVFamilyToRGBFamily_Row<0, 0, 0, 1, 0, 0, 2, 1, 1, 0, 1, 2>(yBuffer,
+                                                                 uBuffer,
+                                                                 vBuffer,
+                                                                 dstBuffer,
+                                                                 aWidth);
+  }
+
+  return 0;
+}
+
+int
+YUV420PToRGB24(const uint8_t* aYBuffer, int aYStride,
+               const uint8_t* aUBuffer, int aUStride,
+               const uint8_t* aVBuffer, int aVStride,
+               uint8_t* aDstBuffer, int aDstStride,
+               int aWidth, int aHeight)
+{
+  for (int i = 0; i < aHeight; ++i) {
+    const uint8_t* yBuffer = aYBuffer + aYStride * i;
+    const uint8_t* uBuffer = aUBuffer + aUStride * (i / 2);
+    const uint8_t* vBuffer = aVBuffer + aVStride * (i / 2);
+    uint8_t* dstBuffer = aDstBuffer + aDstStride * i;
+
+    YUVFamilyToRGBFamily_Row<0, 0, 0, 1, 0, 0, 2, 1, 1, 0, 1, 2>(yBuffer,
+                                                                 uBuffer,
+                                                                 vBuffer,
+                                                                 dstBuffer,
+                                                                 aWidth);
+  }
+
+  return 0;
+}
+
+int
+NV12ToRGB24(const uint8_t* aYBuffer, int aYStride,
+            const uint8_t* aUVBuffer, int aUVStride,
+            uint8_t* aDstBuffer, int aDstStride,
+            int aWidth, int aHeight)
+{
+  for (int i = 0; i < aHeight; ++i) {
+    const uint8_t* yBuffer = aYBuffer + aYStride * i;
+    const uint8_t* uBuffer = aUVBuffer + aUVStride * (i / 2);
+    const uint8_t* vBuffer = aUVBuffer + aUVStride * (i / 2) + 1;
+    uint8_t* dstBuffer = aDstBuffer + aDstStride * i;
+
+    YUVFamilyToRGBFamily_Row<0, 0, 0, 1, 0, 0, 2, 2, 2, 0, 1, 2>(yBuffer,
+                                                                 uBuffer,
+                                                                 vBuffer,
+                                                                 dstBuffer,
+                                                                 aWidth);
+  }
+
+  return 0;
+}
+
+int
+NV21ToRGB24(const uint8_t* aYBuffer, int aYStride,
+            const uint8_t* aVUBuffer, int aVUStride,
+            uint8_t* aDstBuffer, int aDstStride,
+            int aWidth, int aHeight)
+{
+  for (int i = 0; i < aHeight; ++i) {
+    const uint8_t* yBuffer = aYBuffer + aYStride * i;
+    const uint8_t* uBuffer = aVUBuffer + aVUStride * (i / 2) + 1;
+    const uint8_t* vBuffer = aVUBuffer + aVUStride * (i / 2);
+    uint8_t* dstBuffer = aDstBuffer + aDstStride * i;
+
+    YUVFamilyToRGBFamily_Row<0, 0, 0, 1, 0, 0, 2, 2, 2, 0, 1, 2>(yBuffer,
+                                                                 uBuffer,
+                                                                 vBuffer,
+                                                                 dstBuffer,
+                                                                 aWidth);
+  }
+
+  return 0;
+}
+
+int
+YUV444PToBGR24(const uint8_t* aYBuffer, int aYStride,
+               const uint8_t* aUBuffer, int aUStride,
+               const uint8_t* aVBuffer, int aVStride,
+               uint8_t* aDstBuffer, int aDstStride,
+               int aWidth, int aHeight)
+{
+  for (int i = 0; i < aHeight; ++i) {
+    const uint8_t* yBuffer = aYBuffer + aYStride * i;
+    const uint8_t* uBuffer = aUBuffer + aUStride * i;
+    const uint8_t* vBuffer = aVBuffer + aVStride * i;
+    uint8_t* dstBuffer = aDstBuffer + aDstStride * i;
+
+    YUVFamilyToRGBFamily_Row<0, 0, 0, 1, 1, 1, 2, 2, 2, 2, 1, 0>(yBuffer,
+                                                                 uBuffer,
+                                                                 vBuffer,
+                                                                 dstBuffer,
+                                                                 aWidth);
+  }
+
+  return 0;
+}
+
+int
+YUV422PToBGR24(const uint8_t* aYBuffer, int aYStride,
+               const uint8_t* aUBuffer, int aUStride,
+               const uint8_t* aVBuffer, int aVStride,
+               uint8_t* aDstBuffer, int aDstStride,
+               int aWidth, int aHeight)
+{
+  for (int i = 0; i < aHeight; ++i) {
+    const uint8_t* yBuffer = aYBuffer + aYStride * i;
+    const uint8_t* uBuffer = aUBuffer + aUStride * i;
+    const uint8_t* vBuffer = aVBuffer + aVStride * i;
+    uint8_t* dstBuffer = aDstBuffer + aDstStride * i;
+
+    YUVFamilyToRGBFamily_Row<0, 0, 0, 1, 0, 0, 2, 1, 1, 2, 1, 0>(yBuffer,
+                                                                 uBuffer,
+                                                                 vBuffer,
+                                                                 dstBuffer,
+                                                                 aWidth);
+  }
+
+  return 0;
+}
+
+int
+YUV420PToBGR24(const uint8_t* aYBuffer, int aYStride,
+               const uint8_t* aUBuffer, int aUStride,
+               const uint8_t* aVBuffer, int aVStride,
+               uint8_t* aDstBuffer, int aDstStride,
+               int aWidth, int aHeight)
+{
+  for (int i = 0; i < aHeight; ++i) {
+    const uint8_t* yBuffer = aYBuffer + aYStride * i;
+    const uint8_t* uBuffer = aUBuffer + aUStride * (i / 2);
+    const uint8_t* vBuffer = aVBuffer + aVStride * (i / 2);
+    uint8_t* dstBuffer = aDstBuffer + aDstStride * i;
+
+    YUVFamilyToRGBFamily_Row<0, 0, 0, 1, 0, 0, 2, 1, 1, 2, 1, 0>(yBuffer,
+                                                                 uBuffer,
+                                                                 vBuffer,
+                                                                 dstBuffer,
+                                                                 aWidth);
+  }
+
+  return 0;
+}
+
+int
+NV12ToBGR24(const uint8_t* aYBuffer, int aYStride,
+            const uint8_t* aUVBuffer, int aUVStride,
+            uint8_t* aDstBuffer, int aDstStride,
+            int aWidth, int aHeight)
+{
+  for (int i = 0; i < aHeight; ++i) {
+    const uint8_t* yBuffer = aYBuffer + aYStride * i;
+    const uint8_t* uBuffer = aUVBuffer + aUVStride * (i / 2);
+    const uint8_t* vBuffer = aUVBuffer + aUVStride * (i / 2) + 1;
+    uint8_t* dstBuffer = aDstBuffer + aDstStride * i;
+
+    YUVFamilyToRGBFamily_Row<0, 0, 0, 1, 0, 0, 2, 2, 2, 2, 1, 0>(yBuffer,
+                                                                 uBuffer,
+                                                                 vBuffer,
+                                                                 dstBuffer,
+                                                                 aWidth);
+  }
+
+  return 0;
+}
+
+int
+NV21ToBGR24(const uint8_t* aYBuffer, int aYStride,
+            const uint8_t* aVUBuffer, int aVUStride,
+            uint8_t* aDstBuffer, int aDstStride,
+            int aWidth, int aHeight)
+{
+  for (int i = 0; i < aHeight; ++i) {
+    const uint8_t* yBuffer = aYBuffer + aYStride * i;
+    const uint8_t* uBuffer = aVUBuffer + aVUStride * (i / 2) + 1;
+    const uint8_t* vBuffer = aVUBuffer + aVUStride * (i / 2);
+    uint8_t* dstBuffer = aDstBuffer + aDstStride * i;
+
+    YUVFamilyToRGBFamily_Row<0, 0, 0, 1, 0, 0, 2, 2, 2, 2, 1, 0>(yBuffer,
+                                                                 uBuffer,
+                                                                 vBuffer,
+                                                                 dstBuffer,
+                                                                 aWidth);
+  }
+
+  return 0;
+}
+
+/*
+ * YUV family -> RGBA family.
+ */
+int
+YUV444PToRGBA32(const uint8_t* aYBuffer, int aYStride,
+                const uint8_t* aUBuffer, int aUStride,
+                const uint8_t* aVBuffer, int aVStride,
+                uint8_t* aDstBuffer, int aDstStride,
+                int aWidth, int aHeight)
+{
+  for (int i = 0; i < aHeight; ++i) {
+    const uint8_t* yBuffer = aYBuffer + aYStride * i;
+    const uint8_t* uBuffer = aUBuffer + aUStride * i;
+    const uint8_t* vBuffer = aVBuffer + aVStride * i;
+    uint8_t* dstBuffer = aDstBuffer + aDstStride * i;
+
+    YUVFamilyToRGBAFamily_Row<0, 0, 0, 1, 1, 1, 2, 2, 2, 0, 1, 2, 3>(yBuffer,
+                                                                     uBuffer,
+                                                                     vBuffer,
+                                                                     dstBuffer,
+                                                                     aWidth);
+  }
+
+  return 0;
+}
+
+int
+YUV422PToRGBA32(const uint8_t* aYBuffer, int aYStride,
+                const uint8_t* aUBuffer, int aUStride,
+                const uint8_t* aVBuffer, int aVStride,
+                uint8_t* aDstBuffer, int aDstStride,
+                int aWidth, int aHeight)
+{
+  for (int i = 0; i < aHeight; ++i) {
+    const uint8_t* yBuffer = aYBuffer + aYStride * i;
+    const uint8_t* uBuffer = aUBuffer + aUStride * i;
+    const uint8_t* vBuffer = aVBuffer + aVStride * i;
+    uint8_t* dstBuffer = aDstBuffer + aDstStride * i;
+
+    YUVFamilyToRGBAFamily_Row<0, 0, 0, 1, 0, 0, 2, 1, 1, 0, 1, 2, 3>(yBuffer,
+                                                                     uBuffer,
+                                                                     vBuffer,
+                                                                     dstBuffer,
+                                                                     aWidth);
+  }
+
+  return 0;
+}
+
+int
+YUV420PToRGBA32(const uint8_t* aYBuffer, int aYStride,
+                const uint8_t* aUBuffer, int aUStride,
+                const uint8_t* aVBuffer, int aVStride,
+                uint8_t* aDstBuffer, int aDstStride,
+                int aWidth, int aHeight)
+{
+  for (int i = 0; i < aHeight; ++i) {
+    const uint8_t* yBuffer = aYBuffer + aYStride * i;
+    const uint8_t* uBuffer = aUBuffer + aUStride * (i / 2);
+    const uint8_t* vBuffer = aVBuffer + aVStride * (i / 2);
+    uint8_t* dstBuffer = aDstBuffer + aDstStride * i;
+
+    YUVFamilyToRGBAFamily_Row<0, 0, 0, 1, 0, 0, 2, 1, 1, 0, 1, 2, 3>(yBuffer,
+                                                                     uBuffer,
+                                                                     vBuffer,
+                                                                     dstBuffer,
+                                                                     aWidth);
+  }
+
+  return 0;
+}
+
+int
+NV12ToRGBA32(const uint8_t* aYBuffer, int aYStride,
+             const uint8_t* aUVBuffer, int aUVStride,
+             uint8_t* aDstBuffer, int aDstStride,
+             int aWidth, int aHeight)
+{
+  for (int i = 0; i < aHeight; ++i) {
+    const uint8_t* yBuffer = aYBuffer + aYStride * i;
+    const uint8_t* uBuffer = aUVBuffer + aUVStride * (i / 2);
+    const uint8_t* vBuffer = aUVBuffer + aUVStride * (i / 2) + 1;
+    uint8_t* dstBuffer = aDstBuffer + aDstStride * i;
+
+    YUVFamilyToRGBAFamily_Row<0, 0, 0, 1, 0, 0, 2, 2, 2, 0, 1, 2, 3>(yBuffer,
+                                                                     uBuffer,
+                                                                     vBuffer,
+                                                                     dstBuffer,
+                                                                     aWidth);
+  }
+
+  return 0;
+}
+
+int
+NV21ToRGBA32(const uint8_t* aYBuffer, int aYStride,
+             const uint8_t* aVUBuffer, int aVUStride,
+             uint8_t* aDstBuffer, int aDstStride,
+             int aWidth, int aHeight)
+{
+  for (int i = 0; i < aHeight; ++i) {
+    const uint8_t* yBuffer = aYBuffer + aYStride * i;
+    const uint8_t* uBuffer = aVUBuffer + aVUStride * (i / 2) + 1;
+    const uint8_t* vBuffer = aVUBuffer + aVUStride * (i / 2);
+    uint8_t* dstBuffer = aDstBuffer + aDstStride * i;
+
+    YUVFamilyToRGBAFamily_Row<0, 0, 0, 1, 0, 0, 2, 2, 2, 0, 1, 2, 3>(yBuffer,
+                                                                     uBuffer,
+                                                                     vBuffer,
+                                                                     dstBuffer,
+                                                                     aWidth);
+  }
+
+  return 0;
+}
+
+int
+YUV444PToBGRA32(const uint8_t* aYBuffer, int aYStride,
+                const uint8_t* aUBuffer, int aUStride,
+                const uint8_t* aVBuffer, int aVStride,
+                uint8_t* aDstBuffer, int aDstStride,
+                int aWidth, int aHeight)
+{
+  for (int i = 0; i < aHeight; ++i) {
+    const uint8_t* yBuffer = aYBuffer + aYStride * i;
+    const uint8_t* uBuffer = aUBuffer + aUStride * i;
+    const uint8_t* vBuffer = aVBuffer + aVStride * i;
+    uint8_t* dstBuffer = aDstBuffer + aDstStride * i;
+
+    YUVFamilyToRGBAFamily_Row<0, 0, 0, 1, 1, 1, 2, 2, 2, 2, 1, 0, 3>(yBuffer,
+                                                                     uBuffer,
+                                                                     vBuffer,
+                                                                     dstBuffer,
+                                                                     aWidth);
+  }
+
+  return 0;
+}
+
+int
+YUV422PToBGRA32(const uint8_t* aYBuffer, int aYStride,
+                const uint8_t* aUBuffer, int aUStride,
+                const uint8_t* aVBuffer, int aVStride,
+                uint8_t* aDstBuffer, int aDstStride,
+                int aWidth, int aHeight)
+{
+  for (int i = 0; i < aHeight; ++i) {
+    const uint8_t* yBuffer = aYBuffer + aYStride * i;
+    const uint8_t* uBuffer = aUBuffer + aUStride * i;
+    const uint8_t* vBuffer = aVBuffer + aVStride * i;
+    uint8_t* dstBuffer = aDstBuffer + aDstStride * i;
+
+    YUVFamilyToRGBAFamily_Row<0, 0, 0, 1, 0, 0, 2, 1, 1, 2, 1, 0, 3>(yBuffer,
+                                                                     uBuffer,
+                                                                     vBuffer,
+                                                                     dstBuffer,
+                                                                     aWidth);
+  }
+
+  return 0;
+}
+
+int
+YUV420PToBGRA32(const uint8_t* aYBuffer, int aYStride,
+                const uint8_t* aUBuffer, int aUStride,
+                const uint8_t* aVBuffer, int aVStride,
+                uint8_t* aDstBuffer, int aDstStride,
+                int aWidth, int aHeight)
+{
+  for (int i = 0; i < aHeight; ++i) {
+    const uint8_t* yBuffer = aYBuffer + aYStride * i;
+    const uint8_t* uBuffer = aUBuffer + aUStride * (i / 2);
+    const uint8_t* vBuffer = aVBuffer + aVStride * (i / 2);
+    uint8_t* dstBuffer = aDstBuffer + aDstStride * i;
+
+    YUVFamilyToRGBAFamily_Row<0, 0, 0, 1, 0, 0, 2, 1, 1, 2, 1, 0, 3>(yBuffer,
+                                                                     uBuffer,
+                                                                     vBuffer,
+                                                                     dstBuffer,
+                                                                     aWidth);
+  }
+
+  return 0;
+}
+
+int
+NV12ToBGRA32(const uint8_t* aYBuffer, int aYStride,
+             const uint8_t* aUVBuffer, int aUVStride,
+             uint8_t* aDstBuffer, int aDstStride,
+             int aWidth, int aHeight)
+{
+  for (int i = 0; i < aHeight; ++i) {
+    const uint8_t* yBuffer = aYBuffer + aYStride * i;
+    const uint8_t* uBuffer = aUVBuffer + aUVStride * (i / 2);
+    const uint8_t* vBuffer = aUVBuffer + aUVStride * (i / 2) + 1;
+    uint8_t* dstBuffer = aDstBuffer + aDstStride * i;
+
+    YUVFamilyToRGBAFamily_Row<0, 0, 0, 1, 0, 0, 2, 2, 2, 2, 1, 0, 3>(yBuffer,
+                                                                     uBuffer,
+                                                                     vBuffer,
+                                                                     dstBuffer,
+                                                                     aWidth);
+  }
+
+  return 0;
+}
+
+int
+NV21ToBGRA32(const uint8_t* aYBuffer, int aYStride,
+             const uint8_t* aVUBuffer, int aVUStride,
+             uint8_t* aDstBuffer, int aDstStride,
+             int aWidth, int aHeight)
+{
+  for (int i = 0; i < aHeight; ++i) {
+    const uint8_t* yBuffer = aYBuffer + aYStride * i;
+    const uint8_t* uBuffer = aVUBuffer + aVUStride * (i / 2) + 1;
+    const uint8_t* vBuffer = aVUBuffer + aVUStride * (i / 2);
+    uint8_t* dstBuffer = aDstBuffer + aDstStride * i;
+
+    YUVFamilyToRGBAFamily_Row<0, 0, 0, 1, 0, 0, 2, 2, 2, 2, 1, 0, 3>(yBuffer,
+                                                                     uBuffer,
+                                                                     vBuffer,
+                                                                     dstBuffer,
+                                                                     aWidth);
+  }
+
+  return 0;
+}
+
+/*
+ * RGB family -> YUV family.
+ */
+int
+RGB24ToYUV444P(const uint8_t* aSrcBuffer, int aSrcStride,
+               uint8_t* aYBuffer, int aYStride,
+               uint8_t* aUBuffer, int aUStride,
+               uint8_t* aVBuffer, int aVStride,
+               int aWidth, int aHeight)
+{
+  for (int i = 0; i < aHeight; ++i) {
+    const uint8_t* srcBuffer = aSrcBuffer + aSrcStride * i;
+    uint8_t* yBuffer = aYBuffer + aYStride * i;
+    uint8_t* uBuffer = aUBuffer + aUStride * i;
+    uint8_t* vBuffer = aVBuffer + aVStride * i;
+
+    for (int j = 0; j < aWidth; ++j) {
+      yBuffer[0] = RGBToY(srcBuffer[0], srcBuffer[1], srcBuffer[2]);
+      uBuffer[0] = RGBToU(srcBuffer[0], srcBuffer[1], srcBuffer[2]);
+      vBuffer[0] = RGBToV(srcBuffer[0], srcBuffer[1], srcBuffer[2]);
+
+      yBuffer += 1;
+      uBuffer += 1;
+      vBuffer += 1;
+      srcBuffer += 3;
+    }
+  }
+
+  return 0;
+}
+
+int
+RGB24ToYUV422P(const uint8_t* aSrcBuffer, int aSrcStride,
+               uint8_t* aYBuffer, int aYStride,
+               uint8_t* aUBuffer, int aUStride,
+               uint8_t* aVBuffer, int aVStride,
+               int aWidth, int aHeight)
+{
+  for (int i = 0; i < aHeight; ++i) {
+    const uint8_t* srcBuffer = aSrcBuffer + aSrcStride * i;
+    uint8_t* yBuffer = aYBuffer + aYStride * i;
+    uint8_t* uBuffer = aUBuffer + aUStride * i;
+    uint8_t* vBuffer = aVBuffer + aVStride * i;
+
+    RGBFamilyToY_Row<0, 1, 2>(srcBuffer, yBuffer, aWidth);
+
+    // Pass 0 as the aSrcStride so we don't sample next row's RGB information.
+    RGBFamilyToUV_Row<0, 1, 2, 1, 1>(srcBuffer, 0, uBuffer, vBuffer, aWidth);
+  }
+
+  return 0;
+}
+
+int
+RGB24ToYUV420P(const uint8_t* aSrcBuffer, int aSrcStride,
+               uint8_t* aYBuffer, int aYStride,
+               uint8_t* aUBuffer, int aUStride,
+               uint8_t* aVBuffer, int aVStride,
+               int aWidth, int aHeight)
+{
+  for (int i = 0; i < aHeight - 1; i += 2) {
+    const uint8_t* srcBuffer = aSrcBuffer + aSrcStride * i;
+    uint8_t* yBuffer = aYBuffer + aYStride * i;
+    uint8_t* uBuffer = aUBuffer + aUStride * (i / 2);
+    uint8_t* vBuffer = aVBuffer + aVStride * (i / 2);
+
+    RGBFamilyToY_Row<0, 1, 2>(srcBuffer, yBuffer, aWidth);
+    RGBFamilyToY_Row<0, 1, 2>(srcBuffer + aSrcStride, yBuffer + aYStride, aWidth);
+    RGBFamilyToUV_Row<0, 1, 2, 1, 1>(srcBuffer, aSrcStride, uBuffer, vBuffer, aWidth);
+  }
+
+  if (aHeight & 1) {
+    const int i = aHeight - 1;
+    const uint8_t* srcBuffer = aSrcBuffer + aSrcStride * i;
+    uint8_t* yBuffer = aYBuffer + aYStride * i;
+    uint8_t* uBuffer = aUBuffer + aUStride * (i / 2);
+    uint8_t* vBuffer = aVBuffer + aVStride * (i / 2);
+
+    RGBFamilyToY_Row<0, 1, 2>(srcBuffer, yBuffer, aWidth);
+
+    // Pass 0 as the aSrcStride so we don't sample next row's RGB information.
+    RGBFamilyToUV_Row<0, 1, 2, 1, 1>(srcBuffer, 0, uBuffer, vBuffer, aWidth);
+  }
+
+  return 0;
+}
+
+int
+RGB24ToNV12(const uint8_t* aSrcBuffer, int aSrcStride,
+            uint8_t* aYBuffer, int aYStride,
+            uint8_t* aUVBuffer, int aUVStride,
+            int aWidth, int aHeight)
+{
+  for (int i = 0; i < aHeight - 1; i += 2) {
+    const uint8_t* srcBuffer = aSrcBuffer + aSrcStride * i;
+    uint8_t* yBuffer = aYBuffer + aYStride * i;
+    uint8_t* uBuffer = aUVBuffer + aUVStride * (i / 2);
+    uint8_t* vBuffer = aUVBuffer + aUVStride * (i / 2) + 1;
+
+    RGBFamilyToY_Row<0, 1, 2>(srcBuffer, yBuffer, aWidth);
+    RGBFamilyToY_Row<0, 1, 2>(srcBuffer + aSrcStride, yBuffer + aYStride, aWidth);
+    RGBFamilyToUV_Row<0, 1, 2, 2, 2>(srcBuffer, aSrcStride, uBuffer, vBuffer, aWidth);
+  }
+
+  if (aHeight & 1) {
+    const int i = aHeight - 1;
+    const uint8_t* srcBuffer = aSrcBuffer + aSrcStride * i;
+    uint8_t* yBuffer = aYBuffer + aYStride * i;
+    uint8_t* uBuffer = aUVBuffer + aUVStride * (i / 2);
+    uint8_t* vBuffer = aUVBuffer + aUVStride * (i / 2) + 1;
+
+    RGBFamilyToY_Row<0, 1, 2>(srcBuffer, yBuffer, aWidth);
+
+    // Pass 0 as the aSrcStride so we don't sample next row's RGB information.
+    RGBFamilyToUV_Row<0, 1, 2, 2, 2>(srcBuffer, 0, uBuffer, vBuffer, aWidth);
+  }
+
+  return 0;
+}
+
+int
+RGB24ToNV21(const uint8_t* aSrcBuffer, int aSrcStride,
+            uint8_t* aYBuffer, int aYStride,
+            uint8_t* aVUBuffer, int aVUStride,
+            int aWidth, int aHeight)
+{
+  for (int i = 0; i < aHeight - 1; i += 2) {
+    const uint8_t* srcBuffer = aSrcBuffer + aSrcStride * i;
+    uint8_t* yBuffer = aYBuffer + aYStride * i;
+    uint8_t* uBuffer = aVUBuffer + aVUStride * (i / 2) + 1;
+    uint8_t* vBuffer = aVUBuffer + aVUStride * (i / 2);
+
+    RGBFamilyToY_Row<0, 1, 2>(srcBuffer, yBuffer, aWidth);
+    RGBFamilyToY_Row<0, 1, 2>(srcBuffer + aSrcStride, yBuffer + aYStride, aWidth);
+    RGBFamilyToUV_Row<0, 1, 2, 2, 2>(srcBuffer, aSrcStride, uBuffer, vBuffer, aWidth);
+  }
+
+  if (aHeight & 1) {
+    const int i = aHeight - 1;
+    const uint8_t* srcBuffer = aSrcBuffer + aSrcStride * i;
+    uint8_t* yBuffer = aYBuffer + aYStride * i;
+    uint8_t* uBuffer = aVUBuffer + aVUStride * (i / 2) + 1;
+    uint8_t* vBuffer = aVUBuffer + aVUStride * (i / 2);
+
+    RGBFamilyToY_Row<0, 1, 2>(srcBuffer, yBuffer, aWidth);
+
+    // Pass 0 as the aSrcStride so we don't sample next row's RGB information.
+    RGBFamilyToUV_Row<0, 1, 2, 2, 2>(srcBuffer, 0, uBuffer, vBuffer, aWidth);
+  }
+
+  return 0;
+}
+
+int
+BGR24ToYUV444P(const uint8_t* aSrcBuffer, int aSrcStride,
+               uint8_t* aYBuffer, int aYStride,
+               uint8_t* aUBuffer, int aUStride,
+               uint8_t* aVBuffer, int aVStride,
+               int aWidth, int aHeight)
+{
+  for (int i = 0; i < aHeight; ++i) {
+    const uint8_t* srcBuffer = aSrcBuffer + aSrcStride * i;
+    uint8_t* yBuffer = aYBuffer + aYStride * i;
+    uint8_t* uBuffer = aUBuffer + aUStride * i;
+    uint8_t* vBuffer = aVBuffer + aVStride * i;
+
+    for (int j = 0; j < aWidth; ++j) {
+      yBuffer[0] = RGBToY(srcBuffer[2], srcBuffer[1], srcBuffer[0]);
+      uBuffer[0] = RGBToU(srcBuffer[2], srcBuffer[1], srcBuffer[0]);
+      vBuffer[0] = RGBToV(srcBuffer[2], srcBuffer[1], srcBuffer[0]);
+
+      yBuffer += 1;
+      uBuffer += 1;
+      vBuffer += 1;
+      srcBuffer += 3;
+    }
+  }
+
+  return 0;
+}
+
+int
+BGR24ToYUV422P(const uint8_t* aSrcBuffer, int aSrcStride,
+               uint8_t* aYBuffer, int aYStride,
+               uint8_t* aUBuffer, int aUStride,
+               uint8_t* aVBuffer, int aVStride,
+               int aWidth, int aHeight)
+{
+  for (int i = 0; i < aHeight; ++i) {
+    const uint8_t* srcBuffer = aSrcBuffer + aSrcStride * i;
+    uint8_t* yBuffer = aYBuffer + aYStride * i;
+    uint8_t* uBuffer = aUBuffer + aUStride * i;
+    uint8_t* vBuffer = aVBuffer + aVStride * i;
+
+    RGBFamilyToY_Row<2, 1, 0>(srcBuffer, yBuffer, aWidth);
+
+    // Pass 0 as the aSrcStride so we don't sample next row's RGB information.
+    RGBFamilyToUV_Row<2, 1, 0, 1, 1>(srcBuffer, 0, uBuffer, vBuffer, aWidth);
+  }
+
+  return 0;
+}
+
+int
+BGR24ToYUV420P(const uint8_t* aSrcBuffer, int aSrcStride,
+               uint8_t* aYBuffer, int aYStride,
+               uint8_t* aUBuffer, int aUStride,
+               uint8_t* aVBuffer, int aVStride,
+               int aWidth, int aHeight)
+{
+  for (int i = 0; i < aHeight - 1; i += 2) {
+    const uint8_t* srcBuffer = aSrcBuffer + aSrcStride * i;
+    uint8_t* yBuffer = aYBuffer + aYStride * i;
+    uint8_t* uBuffer = aUBuffer + aUStride * (i / 2);
+    uint8_t* vBuffer = aVBuffer + aVStride * (i / 2);
+
+    RGBFamilyToY_Row<2, 1, 0>(srcBuffer, yBuffer, aWidth);
+    RGBFamilyToY_Row<2, 1, 0>(srcBuffer + aSrcStride, yBuffer + aYStride, aWidth);
+    RGBFamilyToUV_Row<2, 1, 0, 1, 1>(srcBuffer, aSrcStride, uBuffer, vBuffer, aWidth);
+  }
+
+  if (aHeight & 1) {
+    const int i = aHeight - 1;
+    const uint8_t* srcBuffer = aSrcBuffer + aSrcStride * i;
+    uint8_t* yBuffer = aYBuffer + aYStride * i;
+    uint8_t* uBuffer = aUBuffer + aUStride * (i / 2);
+    uint8_t* vBuffer = aVBuffer + aVStride * (i / 2);
+
+    RGBFamilyToY_Row<2, 1, 0>(srcBuffer, yBuffer, aWidth);
+
+    // Pass 0 as the aSrcStride so we don't sample next row's RGB information.
+    RGBFamilyToUV_Row<2, 1, 0, 1, 1>(srcBuffer, 0, uBuffer, vBuffer, aWidth);
+  }
+
+  return 0;
+}
+
+int
+BGR24ToNV12(const uint8_t* aSrcBuffer, int aSrcStride,
+            uint8_t* aYBuffer, int aYStride,
+            uint8_t* aUVBuffer, int aUVStride,
+            int aWidth, int aHeight)
+{
+  for (int i = 0; i < aHeight - 1; i += 2) {
+    const uint8_t* srcBuffer = aSrcBuffer + aSrcStride * i;
+    uint8_t* yBuffer = aYBuffer + aYStride * i;
+    uint8_t* uBuffer = aUVBuffer + aUVStride * (i / 2);
+    uint8_t* vBuffer = aUVBuffer + aUVStride * (i / 2) + 1;
+
+    RGBFamilyToY_Row<2, 1, 0>(srcBuffer, yBuffer, aWidth);
+    RGBFamilyToY_Row<2, 1, 0>(srcBuffer + aSrcStride, yBuffer + aYStride, aWidth);
+    RGBFamilyToUV_Row<2, 1, 0, 2, 2>(srcBuffer, aSrcStride, uBuffer, vBuffer, aWidth);
+  }
+
+  if (aHeight & 1) {
+    const int i = aHeight - 1;
+    const uint8_t* srcBuffer = aSrcBuffer + aSrcStride * i;
+    uint8_t* yBuffer = aYBuffer + aYStride * i;
+    uint8_t* uBuffer = aUVBuffer + aUVStride * (i / 2);
+    uint8_t* vBuffer = aUVBuffer + aUVStride * (i / 2) + 1;
+
+    RGBFamilyToY_Row<2, 1, 0>(srcBuffer, yBuffer, aWidth);
+
+    // Pass 0 as the aSrcStride so we don't sample next row's RGB information.
+    RGBFamilyToUV_Row<2, 1, 0, 2, 2>(srcBuffer, 0, uBuffer, vBuffer, aWidth);
+  }
+
+  return 0;
+}
+
+int
+BGR24ToNV21(const uint8_t* aSrcBuffer, int aSrcStride,
+            uint8_t* aYBuffer, int aYStride,
+            uint8_t* aVUBuffer, int aVUStride,
+            int aWidth, int aHeight)
+{
+  for (int i = 0; i < aHeight - 1; i += 2) {
+    const uint8_t* srcBuffer = aSrcBuffer + aSrcStride * i;
+    uint8_t* yBuffer = aYBuffer + aYStride * i;
+    uint8_t* uBuffer = aVUBuffer + aVUStride * (i / 2) + 1;
+    uint8_t* vBuffer = aVUBuffer + aVUStride * (i / 2);
+
+    RGBFamilyToY_Row<2, 1, 0>(srcBuffer, yBuffer, aWidth);
+    RGBFamilyToY_Row<2, 1, 0>(srcBuffer + aSrcStride, yBuffer + aYStride, aWidth);
+    RGBFamilyToUV_Row<2, 1, 0, 2, 2>(srcBuffer, aSrcStride, uBuffer, vBuffer, aWidth);
+  }
+
+  if (aHeight & 1) {
+    const int i = aHeight - 1;
+    const uint8_t* srcBuffer = aSrcBuffer + aSrcStride * i;
+    uint8_t* yBuffer = aYBuffer + aYStride * i;
+    uint8_t* uBuffer = aVUBuffer + aVUStride * (i / 2) + 1;
+    uint8_t* vBuffer = aVUBuffer + aVUStride * (i / 2);
+
+    RGBFamilyToY_Row<2, 1, 0>(srcBuffer, yBuffer, aWidth);
+
+    // Pass 0 as the aSrcStride so we don't sample next row's RGB information.
+    RGBFamilyToUV_Row<2, 1, 0, 2, 2>(srcBuffer, 0, uBuffer, vBuffer, aWidth);
+  }
+
+  return 0;
+}
+
+/*
+ * RGBA family -> YUV family.
+ */
+int
+RGBA32ToYUV444P(const uint8_t* aSrcBuffer, int aSrcStride,
+               uint8_t* aYBuffer, int aYStride,
+               uint8_t* aUBuffer, int aUStride,
+               uint8_t* aVBuffer, int aVStride,
+               int aWidth, int aHeight)
+{
+  for (int i = 0; i < aHeight; ++i) {
+    const uint8_t* srcBuffer = aSrcBuffer + aSrcStride * i;
+    uint8_t* yBuffer = aYBuffer + aYStride * i;
+    uint8_t* uBuffer = aUBuffer + aUStride * i;
+    uint8_t* vBuffer = aVBuffer + aVStride * i;
+
+    for (int j = 0; j < aWidth; ++j) {
+      yBuffer[0] = RGBToY(srcBuffer[0], srcBuffer[1], srcBuffer[2]);
+      uBuffer[0] = RGBToU(srcBuffer[0], srcBuffer[1], srcBuffer[2]);
+      vBuffer[0] = RGBToV(srcBuffer[0], srcBuffer[1], srcBuffer[2]);
+
+      yBuffer += 1;
+      uBuffer += 1;
+      vBuffer += 1;
+      srcBuffer += 4;
+    }
+  }
+
+  return 0;
+}
+
+int
+RGBA32ToYUV422P(const uint8_t* aSrcBuffer, int aSrcStride,
+               uint8_t* aYBuffer, int aYStride,
+               uint8_t* aUBuffer, int aUStride,
+               uint8_t* aVBuffer, int aVStride,
+               int aWidth, int aHeight)
+{
+  for (int i = 0; i < aHeight; ++i) {
+    const uint8_t* srcBuffer = aSrcBuffer + aSrcStride * i;
+    uint8_t* yBuffer = aYBuffer + aYStride * i;
+    uint8_t* uBuffer = aUBuffer + aUStride * i;
+    uint8_t* vBuffer = aVBuffer + aVStride * i;
+
+    RGBAFamilyToY_Row<0, 1, 2>(srcBuffer, yBuffer, aWidth);
+
+    // Pass 0 as the aSrcStride so we don't sample next row's RGB information.
+    RGBAFamilyToUV_Row<0, 1, 2, 1, 1>(srcBuffer, 0, uBuffer, vBuffer, aWidth);
+  }
+
+  return 0;
+}
+
+int
+RGBA32ToYUV420P(const uint8_t* aSrcBuffer, int aSrcStride,
+               uint8_t* aYBuffer, int aYStride,
+               uint8_t* aUBuffer, int aUStride,
+               uint8_t* aVBuffer, int aVStride,
+               int aWidth, int aHeight)
+{
+  for (int i = 0; i < aHeight - 1; i += 2) {
+    const uint8_t* srcBuffer = aSrcBuffer + aSrcStride * i;
+    uint8_t* yBuffer = aYBuffer + aYStride * i;
+    uint8_t* uBuffer = aUBuffer + aUStride * (i / 2);
+    uint8_t* vBuffer = aVBuffer + aVStride * (i / 2);
+
+    RGBAFamilyToY_Row<0, 1, 2>(srcBuffer, yBuffer, aWidth);
+    RGBAFamilyToY_Row<0, 1, 2>(srcBuffer + aSrcStride, yBuffer + aYStride, aWidth);
+    RGBAFamilyToUV_Row<0, 1, 2, 1, 1>(srcBuffer, aSrcStride, uBuffer, vBuffer, aWidth);
+  }
+
+  if (aHeight & 1) {
+    const int i = aHeight - 1;
+    const uint8_t* srcBuffer = aSrcBuffer + aSrcStride * i;
+    uint8_t* yBuffer = aYBuffer + aYStride * i;
+    uint8_t* uBuffer = aUBuffer + aUStride * (i / 2);
+    uint8_t* vBuffer = aVBuffer + aVStride * (i / 2);
+
+    RGBAFamilyToY_Row<0, 1, 2>(srcBuffer, yBuffer, aWidth);
+
+    // Pass 0 as the aSrcStride so we don't sample next row's RGB information.
+    RGBAFamilyToUV_Row<0, 1, 2, 1, 1>(srcBuffer, 0, uBuffer, vBuffer, aWidth);
+  }
+
+  return 0;
+}
+
+int
+RGBA32ToNV12(const uint8_t* aSrcBuffer, int aSrcStride,
+            uint8_t* aYBuffer, int aYStride,
+            uint8_t* aUVBuffer, int aUVStride,
+            int aWidth, int aHeight)
+{
+  for (int i = 0; i < aHeight - 1; i += 2) {
+    const uint8_t* srcBuffer = aSrcBuffer + aSrcStride * i;
+    uint8_t* yBuffer = aYBuffer + aYStride * i;
+    uint8_t* uBuffer = aUVBuffer + aUVStride * (i / 2);
+    uint8_t* vBuffer = aUVBuffer + aUVStride * (i / 2) + 1;
+
+    RGBAFamilyToY_Row<0, 1, 2>(srcBuffer, yBuffer, aWidth);
+    RGBAFamilyToY_Row<0, 1, 2>(srcBuffer + aSrcStride, yBuffer + aYStride, aWidth);
+    RGBAFamilyToUV_Row<0, 1, 2, 2, 2>(srcBuffer, aSrcStride, uBuffer, vBuffer, aWidth);
+  }
+
+  if (aHeight & 1) {
+    const int i = aHeight - 1;
+    const uint8_t* srcBuffer = aSrcBuffer + aSrcStride * i;
+    uint8_t* yBuffer = aYBuffer + aYStride * i;
+    uint8_t* uBuffer = aUVBuffer + aUVStride * (i / 2);
+    uint8_t* vBuffer = aUVBuffer + aUVStride * (i / 2) + 1;
+
+    RGBAFamilyToY_Row<0, 1, 2>(srcBuffer, yBuffer, aWidth);
+
+    // Pass 0 as the aSrcStride so we don't sample next row's RGB information.
+    RGBAFamilyToUV_Row<0, 1, 2, 2, 2>(srcBuffer, 0, uBuffer, vBuffer, aWidth);
+  }
+
+  return 0;
+}
+
+int
+RGBA32ToNV21(const uint8_t* aSrcBuffer, int aSrcStride,
+            uint8_t* aYBuffer, int aYStride,
+            uint8_t* aVUBuffer, int aVUStride,
+            int aWidth, int aHeight)
+{
+  for (int i = 0; i < aHeight - 1; i += 2) {
+    const uint8_t* srcBuffer = aSrcBuffer + aSrcStride * i;
+    uint8_t* yBuffer = aYBuffer + aYStride * i;
+    uint8_t* uBuffer = aVUBuffer + aVUStride * (i / 2) + 1;
+    uint8_t* vBuffer = aVUBuffer + aVUStride * (i / 2);
+
+    RGBAFamilyToY_Row<0, 1, 2>(srcBuffer, yBuffer, aWidth);
+    RGBAFamilyToY_Row<0, 1, 2>(srcBuffer + aSrcStride, yBuffer + aYStride, aWidth);
+    RGBAFamilyToUV_Row<0, 1, 2, 2, 2>(srcBuffer, aSrcStride, uBuffer, vBuffer, aWidth);
+  }
+
+  if (aHeight & 1) {
+    const int i = aHeight - 1;
+    const uint8_t* srcBuffer = aSrcBuffer + aSrcStride * i;
+    uint8_t* yBuffer = aYBuffer + aYStride * i;
+    uint8_t* uBuffer = aVUBuffer + aVUStride * (i / 2) + 1;
+    uint8_t* vBuffer = aVUBuffer + aVUStride * (i / 2);
+
+    RGBAFamilyToY_Row<0, 1, 2>(srcBuffer, yBuffer, aWidth);
+
+    // Pass 0 as the aSrcStride so we don't sample next row's RGB information.
+    RGBAFamilyToUV_Row<0, 1, 2, 2, 2>(srcBuffer, 0, uBuffer, vBuffer, aWidth);
+  }
+
+  return 0;
+}
+
+int
+BGRA32ToYUV444P(const uint8_t* aSrcBuffer, int aSrcStride,
+               uint8_t* aYBuffer, int aYStride,
+               uint8_t* aUBuffer, int aUStride,
+               uint8_t* aVBuffer, int aVStride,
+               int aWidth, int aHeight)
+{
+  for (int i = 0; i < aHeight; ++i) {
+    const uint8_t* srcBuffer = aSrcBuffer + aSrcStride * i;
+    uint8_t* yBuffer = aYBuffer + aYStride * i;
+    uint8_t* uBuffer = aUBuffer + aUStride * i;
+    uint8_t* vBuffer = aVBuffer + aVStride * i;
+
+    for (int j = 0; j < aWidth; ++j) {
+      yBuffer[0] = RGBToY(srcBuffer[2], srcBuffer[1], srcBuffer[0]);
+      uBuffer[0] = RGBToU(srcBuffer[2], srcBuffer[1], srcBuffer[0]);
+      vBuffer[0] = RGBToV(srcBuffer[2], srcBuffer[1], srcBuffer[0]);
+
+      yBuffer += 1;
+      uBuffer += 1;
+      vBuffer += 1;
+      srcBuffer += 4;
+    }
+  }
+
+  return 0;
+}
+
+int
+BGRA32ToYUV422P(const uint8_t* aSrcBuffer, int aSrcStride,
+               uint8_t* aYBuffer, int aYStride,
+               uint8_t* aUBuffer, int aUStride,
+               uint8_t* aVBuffer, int aVStride,
+               int aWidth, int aHeight)
+{
+  for (int i = 0; i < aHeight; ++i) {
+    const uint8_t* srcBuffer = aSrcBuffer + aSrcStride * i;
+    uint8_t* yBuffer = aYBuffer + aYStride * i;
+    uint8_t* uBuffer = aUBuffer + aUStride * i;
+    uint8_t* vBuffer = aVBuffer + aVStride * i;
+
+    RGBAFamilyToY_Row<2, 1, 0>(srcBuffer, yBuffer, aWidth);
+
+    // Pass 0 as the aSrcStride so we don't sample next row's RGB information.
+    RGBAFamilyToUV_Row<2, 1, 0, 1, 1>(srcBuffer, 0, uBuffer, vBuffer, aWidth);
+  }
+
+  return 0;
+}
+
+int
+BGRA32ToYUV420P(const uint8_t* aSrcBuffer, int aSrcStride,
+               uint8_t* aYBuffer, int aYStride,
+               uint8_t* aUBuffer, int aUStride,
+               uint8_t* aVBuffer, int aVStride,
+               int aWidth, int aHeight)
+{
+  for (int i = 0; i < aHeight - 1; i += 2) {
+    const uint8_t* srcBuffer = aSrcBuffer + aSrcStride * i;
+    uint8_t* yBuffer = aYBuffer + aYStride * i;
+    uint8_t* uBuffer = aUBuffer + aUStride * (i / 2);
+    uint8_t* vBuffer = aVBuffer + aVStride * (i / 2);
+
+    RGBAFamilyToY_Row<2, 1, 0>(srcBuffer, yBuffer, aWidth);
+    RGBAFamilyToY_Row<2, 1, 0>(srcBuffer + aSrcStride, yBuffer + aYStride, aWidth);
+    RGBAFamilyToUV_Row<2, 1, 0, 1, 1>(srcBuffer, aSrcStride, uBuffer, vBuffer, aWidth);
+  }
+
+  if (aHeight & 1) {
+    const int i = aHeight - 1;
+    const uint8_t* srcBuffer = aSrcBuffer + aSrcStride * i;
+    uint8_t* yBuffer = aYBuffer + aYStride * i;
+    uint8_t* uBuffer = aUBuffer + aUStride * (i / 2);
+    uint8_t* vBuffer = aVBuffer + aVStride * (i / 2);
+
+    RGBAFamilyToY_Row<2, 1, 0>(srcBuffer, yBuffer, aWidth);
+
+    // Pass 0 as the aSrcStride so we don't sample next row's RGB information.
+    RGBAFamilyToUV_Row<2, 1, 0, 1, 1>(srcBuffer, 0, uBuffer, vBuffer, aWidth);
+  }
+
+  return 0;
+}
+
+int
+BGRA32ToNV12(const uint8_t* aSrcBuffer, int aSrcStride,
+            uint8_t* aYBuffer, int aYStride,
+            uint8_t* aUVBuffer, int aUVStride,
+            int aWidth, int aHeight)
+{
+  for (int i = 0; i < aHeight - 1; i += 2) {
+    const uint8_t* srcBuffer = aSrcBuffer + aSrcStride * i;
+    uint8_t* yBuffer = aYBuffer + aYStride * i;
+    uint8_t* uBuffer = aUVBuffer + aUVStride * (i / 2);
+    uint8_t* vBuffer = aUVBuffer + aUVStride * (i / 2) + 1;
+
+    RGBAFamilyToY_Row<2, 1, 0>(srcBuffer, yBuffer, aWidth);
+    RGBAFamilyToY_Row<2, 1, 0>(srcBuffer + aSrcStride, yBuffer + aYStride, aWidth);
+    RGBAFamilyToUV_Row<2, 1, 0, 2, 2>(srcBuffer, aSrcStride, uBuffer, vBuffer, aWidth);
+  }
+
+  if (aHeight & 1) {
+    const int i = aHeight - 1;
+    const uint8_t* srcBuffer = aSrcBuffer + aSrcStride * i;
+    uint8_t* yBuffer = aYBuffer + aYStride * i;
+    uint8_t* uBuffer = aUVBuffer + aUVStride * (i / 2);
+    uint8_t* vBuffer = aUVBuffer + aUVStride * (i / 2) + 1;
+
+    RGBAFamilyToY_Row<2, 1, 0>(srcBuffer, yBuffer, aWidth);
+
+    // Pass 0 as the aSrcStride so we don't sample next row's RGB information.
+    RGBAFamilyToUV_Row<2, 1, 0, 2, 2>(srcBuffer, 0, uBuffer, vBuffer, aWidth);
+  }
+
+  return 0;
+}
+
+int
+BGRA32ToNV21(const uint8_t* aSrcBuffer, int aSrcStride,
+            uint8_t* aYBuffer, int aYStride,
+            uint8_t* aVUBuffer, int aVUStride,
+            int aWidth, int aHeight)
+{
+  for (int i = 0; i < aHeight - 1; i += 2) {
+    const uint8_t* srcBuffer = aSrcBuffer + aSrcStride * i;
+    uint8_t* yBuffer = aYBuffer + aYStride * i;
+    uint8_t* uBuffer = aVUBuffer + aVUStride * (i / 2) + 1;
+    uint8_t* vBuffer = aVUBuffer + aVUStride * (i / 2);
+
+    RGBAFamilyToY_Row<2, 1, 0>(srcBuffer, yBuffer, aWidth);
+    RGBAFamilyToY_Row<2, 1, 0>(srcBuffer + aSrcStride, yBuffer + aYStride, aWidth);
+    RGBAFamilyToUV_Row<2, 1, 0, 2, 2>(srcBuffer, aSrcStride, uBuffer, vBuffer, aWidth);
+  }
+
+  if (aHeight & 1) {
+    const int i = aHeight - 1;
+    const uint8_t* srcBuffer = aSrcBuffer + aSrcStride * i;
+    uint8_t* yBuffer = aYBuffer + aYStride * i;
+    uint8_t* uBuffer = aVUBuffer + aVUStride * (i / 2) + 1;
+    uint8_t* vBuffer = aVUBuffer + aVUStride * (i / 2);
+
+    RGBAFamilyToY_Row<2, 1, 0>(srcBuffer, yBuffer, aWidth);
+
+    // Pass 0 as the aSrcStride so we don't sample next row's RGB information.
+    RGBAFamilyToUV_Row<2, 1, 0, 2, 2>(srcBuffer, 0, uBuffer, vBuffer, aWidth);
+  }
+
+  return 0;
+}
+
+/*
+ * RGBA/RGB family -> HSV.
+ * Reference:
+ * (1) https://en.wikipedia.org/wiki/HSL_and_HSV
+ * (2) OpenCV implementation:
+ *     http://docs.opencv.org/3.1.0/de/d25/imgproc_color_conversions.html
+ */
+const float EPSILON = 1e-10f;
+
+template<int aRIndex, int aGIndex, int aBIndex, int aSrcStep>
+int
+RGBFamilyToHSV(const uint8_t* aSrcBuffer, int aSrcStride,
+               float* aDstBuffer, int aDstStride,
+               int aWidth, int aHeight)
+{
+  for (int i = 0; i < aHeight; ++i) {
+    const uint8_t* srcBuffer = aSrcBuffer + aSrcStride * i;
+    float* dstBuffer = (float*)((uint8_t*)(aDstBuffer) + aDstStride * i);
+
+    for (int j = 0; j < aWidth; ++j) {
+      const float r = (float)(srcBuffer[aRIndex]) / 255.0f;
+      const float g = (float)(srcBuffer[aGIndex]) / 255.0f;
+      const float b = (float)(srcBuffer[aBIndex]) / 255.0f;
+      float& h = dstBuffer[0];
+      float& s = dstBuffer[1];
+      float& v = dstBuffer[2];
+
+      float min = r;
+      if (g < min) min = g;
+      if (b < min) min = b;
+
+      float max = r;
+      if (g > max) max = g;
+      if (b > max) max = b;
+
+      const float diff = max - min + EPSILON; // Prevent dividing by zero.
+
+      // Calculate v.
+      v = max;
+
+      // Calculate s.
+      if (max == 0.0f) {
+        s = 0.0f;
+      } else {
+        s = diff / v;
+      }
+
+      // Calculate h.
+      if (max == r) {
+        h = 60.0f * (g - b) / diff;
+      } else if (max == g) {
+        h = 60.0f * (b - r) / diff + 120.0f;
+      } else if (max == b) {
+        h = 60.0f * (r - g) / diff + 240.0f;
+      }
+
+      if (h < 0.0f) {
+        h += 360.0f;
+      }
+
+      // Step one pixel.
+      srcBuffer += aSrcStep;
+      dstBuffer += 3;
+    }
+  }
+
+  return 0;
+}
+
+static const int sector_data[][3]= {{0,3,1}, {2,0,1}, {1,0,3}, {1,2,0}, {3,1,0}, {0,1,2}};
+
+// If the destination is a RGB24 or BGR24, set the aAIndex to be 0, 1 or 2,
+// so that the r, g or b value will be set to 255 first than to the right value.
+template<int aRIndex, int aGIndex, int aBIndex, int aAIndex, int aDstStep>
+int
+HSVToRGBAFamily(const float* aSrcBuffer, int aSrcStride,
+                uint8_t* aDstBuffer, int aDstStride,
+                int aWidth, int aHeight)
+{
+  static_assert(aRIndex == 0 || aRIndex == 2, "Wrong R index.");
+  static_assert(aGIndex == 1, "Wrong G index.");
+  static_assert(aBIndex == 0 || aBIndex == 2, "Wrong B index.");
+  static_assert(aAIndex == 0 || aAIndex == 1 || aAIndex == 2 || aAIndex == 3, "Wrong A index.");
+
+  for (int i = 0; i < aHeight; ++i) {
+    const float* srcBuffer = (const float*)((const uint8_t*)(aSrcBuffer) + aSrcStride * i);
+    uint8_t* dstBuffer = aDstBuffer + aDstStride * i;
+
+    for (int j = 0; j < aWidth; ++j) {
+      const float h = srcBuffer[0];
+      const float s = srcBuffer[1];
+      const float v = srcBuffer[2];
+
+      // Calculate h-prime which should be in range [0, 6). -> h should be in
+      // range [0, 360).
+      float hPrime = h / 60.0f;
+      if (hPrime < 0.0f)
+          do hPrime += 6.0f; while (hPrime < 0.0f);
+      else if (hPrime >= 6.0f)
+          do hPrime -= 6.0f; while (hPrime >= 6.0f);
+      const int sector = floor(hPrime);
+      const float hMod1 = hPrime - sector;
+
+      float values[4];
+      values[0] = v;
+      values[1] = v * (1.0f - s);
+      values[2] = v * (1.0f - s * hMod1);
+      values[3] = v * (1.0f - s * (1.0f - hMod1));
+
+      dstBuffer[aAIndex] = 255;
+      dstBuffer[aRIndex] = Clamp(values[sector_data[sector][0]] * 255.0f);
+      dstBuffer[aGIndex] = Clamp(values[sector_data[sector][1]] * 255.0f);
+      dstBuffer[aBIndex] = Clamp(values[sector_data[sector][2]] * 255.0f);
+
+      // Step one pixel.
+      srcBuffer += 3;
+      dstBuffer += aDstStep;
+    }
+  }
+
+  return 0;
+}
+
+int
+RGBA32ToHSV(const uint8_t* aSrcBuffer, int aSrcStride,
+            float* aDstBuffer, int aDstStride,
+            int aWidth, int aHeight)
+{
+  return RGBFamilyToHSV<0, 1, 2, 4>(aSrcBuffer, aSrcStride,
+                                    aDstBuffer, aDstStride,
+                                    aWidth, aHeight);
+}
+
+int
+BGRA32ToHSV(const uint8_t* aSrcBuffer, int aSrcStride,
+            float* aDstBuffer, int aDstStride,
+            int aWidth, int aHeight)
+{
+  return RGBFamilyToHSV<2, 1, 0, 4>(aSrcBuffer, aSrcStride,
+                                    aDstBuffer, aDstStride,
+                                    aWidth, aHeight);
+}
+
+int
+RGB24ToHSV(const uint8_t* aSrcBuffer, int aSrcStride,
+           float* aDstBuffer, int aDstStride,
+           int aWidth, int aHeight)
+{
+  return RGBFamilyToHSV<0, 1, 2, 3>(aSrcBuffer, aSrcStride,
+                                    aDstBuffer, aDstStride,
+                                    aWidth, aHeight);
+}
+
+int
+BGR24ToHSV(const uint8_t* aSrcBuffer, int aSrcStride,
+           float* aDstBuffer, int aDstStride,
+           int aWidth, int aHeight)
+{
+  return RGBFamilyToHSV<2, 1, 0, 3>(aSrcBuffer, aSrcStride,
+                                    aDstBuffer, aDstStride,
+                                    aWidth, aHeight);
+}
+
+int
+HSVToRGBA32(const float* aSrcBuffer, int aSrcStride,
+            uint8_t* aDstBuffer, int aDstStride,
+            int aWidth, int aHeight)
+{
+  return HSVToRGBAFamily<0, 1, 2, 3, 4>(aSrcBuffer, aSrcStride,
+                                        aDstBuffer, aDstStride,
+                                        aWidth, aHeight);
+}
+
+int
+HSVToBGRA32(const float* aSrcBuffer, int aSrcStride,
+            uint8_t* aDstBuffer, int aDstStride,
+            int aWidth, int aHeight)
+{
+  return HSVToRGBAFamily<2, 1, 0, 3, 4>(aSrcBuffer, aSrcStride,
+                                        aDstBuffer, aDstStride,
+                                        aWidth, aHeight);
+}
+
+int
+HSVToRGB24(const float* aSrcBuffer, int aSrcStride,
+           uint8_t* aDstBuffer, int aDstStride,
+           int aWidth, int aHeight)
+{
+  return HSVToRGBAFamily<0, 1, 2, 0, 3>(aSrcBuffer, aSrcStride,
+                                        aDstBuffer, aDstStride,
+                                        aWidth, aHeight);
+}
+
+int
+HSVToBGR24(const float* aSrcBuffer, int aSrcStride,
+           uint8_t* aDstBuffer, int aDstStride,
+           int aWidth, int aHeight)
+{
+  return HSVToRGBAFamily<2, 1, 0, 0, 3>(aSrcBuffer, aSrcStride,
+                                        aDstBuffer, aDstStride,
+                                        aWidth, aHeight);
+}
+
+/*
+ * RGBA/RGB family -> Lab.
+ * Reference:
+ * (1) https://en.wikipedia.org/wiki/SRGB
+ * (2) https://en.wikipedia.org/wiki/Lab_color_space
+ * (3) OpenCV implementation:
+ *     http://docs.opencv.org/3.1.0/de/d25/imgproc_color_conversions.html
+ */
+static const float sRGBToXYZ_D65[] = {0.412453f, 0.357580f, 0.180423f,
+                                      0.212671f, 0.715160f, 0.072169f,
+                                      0.019334f, 0.119193f, 0.950227f};
+static const float XYZTosRGB_D65[] = {3.240479f,  -1.53715f,  -0.498535f,
+                                      -0.969256f, 1.875991f,  0.041556f,
+                                      0.055648f,  -0.204043f, 1.057311f};
+static const float whitept_D65[] = {0.950456f, 1.0f, 1.088754f};
+static const float _magic = std::pow((6.0 / 29.0), 3.0); // should be around 0.008856.
+static const float _1_3 = 1.0f / 3.0f;
+static const float _a = std::pow((29.0 / 6.0), 2.0) / 3.0; // should be around 7.787.
+static const float _b = 16.0f / 116.0f; // should be around 0.1379.
+
+template<int aRIndex, int aGIndex, int aBIndex, int aSrcStep>
+int
+RGBFamilyToLab(const uint8_t* aSrcBuffer, int aSrcStride,
+               float* aDstBuffer, int aDstStride,
+               int aWidth, int aHeight)
+{
+  static_assert(aRIndex == 0 || aRIndex == 2, "Wrong R index.");
+  static_assert(aGIndex == 1, "Wrong G index.");
+  static_assert(aBIndex == 0 || aBIndex == 2, "Wrong B index.");
+
+  const float C0 = sRGBToXYZ_D65[0] / whitept_D65[0],
+              C1 = sRGBToXYZ_D65[1] / whitept_D65[0],
+              C2 = sRGBToXYZ_D65[2] / whitept_D65[0],
+              C3 = sRGBToXYZ_D65[3] / whitept_D65[1],
+              C4 = sRGBToXYZ_D65[4] / whitept_D65[1],
+              C5 = sRGBToXYZ_D65[5] / whitept_D65[1],
+              C6 = sRGBToXYZ_D65[6] / whitept_D65[2],
+              C7 = sRGBToXYZ_D65[7] / whitept_D65[2],
+              C8 = sRGBToXYZ_D65[8] / whitept_D65[2];
+
+  for (int i = 0; i < aHeight; ++i) {
+    const uint8_t* srcBuffer = aSrcBuffer + aSrcStride * i;
+    float* dstBuffer = (float*)((uint8_t*)(aDstBuffer) + aDstStride * i);
+
+    for (int j = 0; j < aWidth; ++j) {
+      float r = (float)(srcBuffer[aRIndex]) / 255.0f;
+      float g = (float)(srcBuffer[aGIndex]) / 255.0f;
+      float b = (float)(srcBuffer[aBIndex]) / 255.0f;
+
+      // gamma correction of sRGB
+      r = r <= 0.04045f ? r / 12.92f : std::pow((r + 0.055) / 1.055, 2.4);
+      g = g <= 0.04045f ? g / 12.92f : std::pow((g + 0.055) / 1.055, 2.4);
+      b = b <= 0.04045f ? b / 12.92f : std::pow((b + 0.055) / 1.055, 2.4);
+
+      const float X = C0 * r + C1 * g + C2 * b;
+      const float Y = C3 * r + C4 * g + C5 * b;
+      const float Z = C6 * r + C7 * g + C8 * b;
+
+      const float FX = X > _magic ? std::pow(X, _1_3) : (_a * X + _b);
+      const float FY = Y > _magic ? std::pow(Y, _1_3) : (_a * Y + _b);
+      const float FZ = Z > _magic ? std::pow(Z, _1_3) : (_a * Z + _b);
+
+      dstBuffer[0] = 116.0f * FY - 16.0f;
+      dstBuffer[1] = 500.0f * (FX - FY);
+      dstBuffer[2] = 200.0f * (FY - FZ);
+
+      // Step one pixel.
+      srcBuffer += aSrcStep;
+      dstBuffer += 3;
+    }
+  }
+  return 0;
+}
+
+// If the destination is a RGB24 or BGR24, set the aAIndex to be 0, 1 or 2,
+// so that the r, g or b value will be set to 255 first than to the right value.
+template<int aRIndex, int aGIndex, int aBIndex, int aAIndex, int aDstStep>
+int
+LabToRGBAFamily(const float* aSrcBuffer, int aSrcStride,
+                uint8_t* aDstBuffer, int aDstStride,
+                int aWidth, int aHeight)
+{
+  static_assert(aRIndex == 0 || aRIndex == 2, "Wrong R index.");
+  static_assert(aGIndex == 1, "Wrong G index.");
+  static_assert(aBIndex == 0 || aBIndex == 2, "Wrong B index.");
+  static_assert(aAIndex == 0 || aAIndex == 1 || aAIndex == 2 || aAIndex == 3, "Wrong A index.");
+
+  const float C0 = XYZTosRGB_D65[0] * whitept_D65[0],
+              C1 = XYZTosRGB_D65[1] * whitept_D65[1],
+              C2 = XYZTosRGB_D65[2] * whitept_D65[2],
+              C3 = XYZTosRGB_D65[3] * whitept_D65[0],
+              C4 = XYZTosRGB_D65[4] * whitept_D65[1],
+              C5 = XYZTosRGB_D65[5] * whitept_D65[2],
+              C6 = XYZTosRGB_D65[6] * whitept_D65[0],
+              C7 = XYZTosRGB_D65[7] * whitept_D65[1],
+              C8 = XYZTosRGB_D65[8] * whitept_D65[2];
+
+  for (int i = 0; i < aHeight; ++i) {
+    const float* srcBuffer = (const float*)((const uint8_t*)(aSrcBuffer) + aSrcStride * i);
+    uint8_t* dstBuffer = aDstBuffer + aDstStride * i;
+
+    for (int j = 0; j < aWidth; ++j) {
+      const float L = srcBuffer[0];
+      const float a = srcBuffer[1];
+      const float b = srcBuffer[2];
+
+      const float FY = (L + 16.0f) / 116.0f;
+      const float FX = (a / 500.0f) + FY;
+      const float FZ = FY - (b / 200.0f);
+
+      const float X = FX > 6.0f / 29.0f ? std::pow((double)FX, 3.0) : 3.0 * std::pow((6.0 / 29.0), 2.0) * (FX - (4.0 / 29.0));
+      const float Y = FY > 6.0f / 29.0f ? std::pow((double)FY, 3.0) : 3.0 * std::pow((6.0 / 29.0), 2.0) * (FY - (4.0 / 29.0));
+      const float Z = FZ > 6.0f / 29.0f ? std::pow((double)FZ, 3.0) : 3.0 * std::pow((6.0 / 29.0), 2.0) * (FZ - (4.0 / 29.0));
+
+      const float r0 = C0 * X + C1 * Y + C2 * Z;
+      const float g0 = C3 * X + C4 * Y + C5 * Z;
+      const float b0 = C6 * X + C7 * Y + C8 * Z;
+
+      // Apply gamma curve of sRGB to the linear rgb values.
+      dstBuffer[aAIndex] = 255;
+      dstBuffer[aRIndex] = Clamp((r0 <= 0.0031308f ? r0 * 12.92f : 1.055 * std::pow((double)r0, 1.0 / 2.4) - 0.055) * 255.0);
+      dstBuffer[aGIndex] = Clamp((g0 <= 0.0031308f ? g0 * 12.92f : 1.055 * std::pow((double)g0, 1.0 / 2.4) - 0.055) * 255.0);
+      dstBuffer[aBIndex] = Clamp((b0 <= 0.0031308f ? b0 * 12.92f : 1.055 * std::pow((double)b0, 1.0 / 2.4) - 0.055) * 255.0);
+
+      // Step one pixel.
+      srcBuffer += 3;
+      dstBuffer += aDstStep;
+    }
+  }
+  return 0;
+}
+
+int
+RGBA32ToLab(const uint8_t* aSrcBuffer, int aSrcStride,
+            float* aDstBuffer, int aDstStride,
+            int aWidth, int aHeight)
+{
+  return RGBFamilyToLab<0, 1, 2, 4>(aSrcBuffer, aSrcStride,
+                                    aDstBuffer, aDstStride,
+                                    aWidth, aHeight);
+}
+
+int
+BGRA32ToLab(const uint8_t* aSrcBuffer, int aSrcStride,
+            float* aDstBuffer, int aDstStride,
+            int aWidth, int aHeight)
+{
+  return RGBFamilyToLab<2, 1, 0, 4>(aSrcBuffer, aSrcStride,
+                                    aDstBuffer, aDstStride,
+                                    aWidth, aHeight);
+}
+
+int
+RGB24ToLab(const uint8_t* aSrcBuffer, int aSrcStride,
+           float* aDstBuffer, int aDstStride,
+           int aWidth, int aHeight)
+{
+  return RGBFamilyToLab<0, 1, 2, 3>(aSrcBuffer, aSrcStride,
+                                    aDstBuffer, aDstStride,
+                                    aWidth, aHeight);
+}
+
+int
+BGR24ToLab(const uint8_t* aSrcBuffer, int aSrcStride,
+           float* aDstBuffer, int aDstStride,
+           int aWidth, int aHeight)
+{
+  return RGBFamilyToLab<2, 1, 0, 3>(aSrcBuffer, aSrcStride,
+                                    aDstBuffer, aDstStride,
+                                    aWidth, aHeight);
+}
+
+int
+LabToRGBA32(const float* aSrcBuffer, int aSrcStride,
+            uint8_t* aDstBuffer, int aDstStride,
+            int aWidth, int aHeight)
+{
+  return LabToRGBAFamily<0, 1, 2, 3, 4>(aSrcBuffer, aSrcStride,
+                                        aDstBuffer, aDstStride,
+                                        aWidth, aHeight);
+}
+
+int
+LabToBGRA32(const float* aSrcBuffer, int aSrcStride,
+            uint8_t* aDstBuffer, int aDstStride,
+            int aWidth, int aHeight)
+{
+  return LabToRGBAFamily<2, 1, 0, 3, 4>(aSrcBuffer, aSrcStride,
+                                        aDstBuffer, aDstStride,
+                                        aWidth, aHeight);
+}
+
+int
+LabToRGB24(const float* aSrcBuffer, int aSrcStride,
+           uint8_t* aDstBuffer, int aDstStride,
+           int aWidth, int aHeight)
+{
+  return LabToRGBAFamily<0, 1, 2, 0, 3>(aSrcBuffer, aSrcStride,
+                                        aDstBuffer, aDstStride,
+                                        aWidth, aHeight);
+}
+
+int
+LabToBGR24(const float* aSrcBuffer, int aSrcStride,
+           uint8_t* aDstBuffer, int aDstStride,
+           int aWidth, int aHeight)
+{
+  return LabToRGBAFamily<2, 1, 0, 0, 3>(aSrcBuffer, aSrcStride,
+                                        aDstBuffer, aDstStride,
+                                        aWidth, aHeight);
+}
+
+/*
+ * RGBA/RGB family -> Gray8.
+ * Reference:
+ * (1) OpenCV implementation:
+ * http://docs.opencv.org/3.1.0/de/d25/imgproc_color_conversions.html
+ */
+template<int aRIndex, int aGIndex, int aBIndex, int aSrcStep>
+int
+RGBFamilyToGray8(const uint8_t* aSrcBuffer, int aSrcStride,
+                 uint8_t* aDstBuffer, int aDstStride,
+                 int aWidth, int aHeight)
+{
+  static_assert(aRIndex == 0 || aRIndex == 2, "Wrong R index.");
+  static_assert(aGIndex == 1, "Wrong G index.");
+  static_assert(aBIndex == 0 || aBIndex == 2, "Wrong B index.");
+
+  for (int i = 0; i < aHeight; ++i) {
+    const uint8_t* srcBuffer = aSrcBuffer + aSrcStride * i;
+    uint8_t* dstBuffer = aDstBuffer + aDstStride * i;
+
+    for (int j = 0; j < aWidth; ++j) {
+      dstBuffer[j] = 0.299 * srcBuffer[aRIndex] +
+                     0.587 * srcBuffer[aGIndex] +
+                     0.114 * srcBuffer[aBIndex];
+      srcBuffer += aSrcStep;
+    }
+  }
+
+  return 0;
+}
+
+int
+RGB24ToGray8(const uint8_t* aSrcBuffer, int aSrcStride,
+             uint8_t* aDstBuffer, int aDstStride,
+             int aWidth, int aHeight)
+{
+  return RGBFamilyToGray8<0, 1, 2, 3>(aSrcBuffer, aSrcStride,
+                                      aDstBuffer, aDstStride,
+                                      aWidth, aHeight);
+}
+
+int
+BGR24ToGray8(const uint8_t* aSrcBuffer, int aSrcStride,
+             uint8_t* aDstBuffer, int aDstStride,
+             int aWidth, int aHeight)
+{
+  return RGBFamilyToGray8<2, 1, 0, 3>(aSrcBuffer, aSrcStride,
+                                      aDstBuffer, aDstStride,
+                                      aWidth, aHeight);
+}
+
+int
+RGBA32ToGray8(const uint8_t* aSrcBuffer, int aSrcStride,
+              uint8_t* aDstBuffer, int aDstStride,
+              int aWidth, int aHeight)
+{
+  return RGBFamilyToGray8<0, 1, 2, 4>(aSrcBuffer, aSrcStride,
+                                      aDstBuffer, aDstStride,
+                                      aWidth, aHeight);
+}
+
+int
+BGRA32ToGray8(const uint8_t* aSrcBuffer, int aSrcStride,
+              uint8_t* aDstBuffer, int aDstStride,
+              int aWidth, int aHeight)
+{
+  return RGBFamilyToGray8<2, 1, 0, 4>(aSrcBuffer, aSrcStride,
+                                      aDstBuffer, aDstStride,
+                                      aWidth, aHeight);
+}
+
+/*
+ * YUV family -> Gray8.
+ * Reference:
+ * (1) OpenCV implementation:
+ * http://docs.opencv.org/3.1.0/de/d25/imgproc_color_conversions.html
+ */
+int
+YUVFamilyToGray8(const uint8_t* aSrcYBuffer, int aSrcYStride,
+                 uint8_t* aDstBuffer, int aDstStride,
+                 int aWidth, int aHeight)
+{
+  for (int i = 0; i < aHeight; ++i) {
+    const uint8_t* srcYBuffer = aSrcYBuffer + aSrcYStride * i;
+    uint8_t* dstBuffer = aDstBuffer + aDstStride * i;
+
+    memcpy(dstBuffer, srcYBuffer, aDstStride);
+  }
+
+  return 0;
+}
+
+int
+YUV444PToGray8(const uint8_t* aYBuffer, int aYStride,
+               const uint8_t*, int,
+               const uint8_t*, int,
+               uint8_t* aDstBuffer, int aDstStride,
+               int aWidth, int aHeight)
+{
+  return YUVFamilyToGray8(aYBuffer, aYStride,
+                          aDstBuffer, aDstStride,
+                          aWidth, aHeight);
+}
+
+int
+YUV422PToGray8(const uint8_t* aYBuffer, int aYStride,
+               const uint8_t*, int,
+               const uint8_t*, int,
+               uint8_t* aDstBuffer, int aDstStride,
+               int aWidth, int aHeight)
+{
+  return YUVFamilyToGray8(aYBuffer, aYStride,
+                          aDstBuffer, aDstStride,
+                          aWidth, aHeight);
+}
+
+int
+YUV420PToGray8(const uint8_t* aYBuffer, int aYStride,
+               const uint8_t*, int,
+               const uint8_t*, int,
+               uint8_t* aDstBuffer, int aDstStride,
+               int aWidth, int aHeight)
+{
+  return YUVFamilyToGray8(aYBuffer, aYStride,
+                          aDstBuffer, aDstStride,
+                          aWidth, aHeight);
+}
+
+int
+NV12ToGray8(const uint8_t* aYBuffer, int aYStride,
+            const uint8_t*, int,
+            uint8_t* aDstBuffer, int aDstStride,
+            int aWidth, int aHeight)
+{
+  return YUVFamilyToGray8(aYBuffer, aYStride,
+                          aDstBuffer, aDstStride,
+                          aWidth, aHeight);
+}
+
+int
+NV21ToGray8(const uint8_t* aYBuffer, int aYStride,
+            const uint8_t*, int,
+            uint8_t* aDstBuffer, int aDstStride,
+            int aWidth, int aHeight)
+{
+  return YUVFamilyToGray8(aYBuffer, aYStride,
+                          aDstBuffer, aDstStride,
+                          aWidth, aHeight);
+}
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/canvas/ImageBitmapColorUtils.h
@@ -0,0 +1,501 @@
+/* -*- 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_ImageBitmapColorUtils_h
+#define mozilla_dom_ImageBitmapColorUtils_h
+
+#include "mozilla/UniquePtr.h"
+#include "nsTArrayForwardDeclare.h"
+
+namespace mozilla {
+namespace dom {
+
+/*
+ * RGB family -> RGBA family.
+ */
+int
+RGB24ToRGBA32(const uint8_t* aSrcBuffer, int aSrcStride,
+              uint8_t* aDstBuffer, int aDstStride,
+              int aWidth, int aHeight);
+
+int
+BGR24ToRGBA32(const uint8_t* aSrcBuffer, int aSrcStride,
+              uint8_t* aDstBuffer, int aDstStride,
+              int aWidth, int aHeight);
+int
+RGB24ToBGRA32(const uint8_t* aSrcBuffer, int aSrcStride,
+              uint8_t* aDstBuffer, int aDstStride,
+              int aWidth, int aHeight);
+
+int
+BGR24ToBGRA32(const uint8_t* aSrcBuffer, int aSrcStride,
+              uint8_t* aDstBuffer, int aDstStride,
+              int aWidth, int aHeight);
+
+/*
+ * RGBA family -> RGB family.
+ */
+int
+RGBA32ToRGB24(const uint8_t* aSrcBuffer, int aSrcStride,
+              uint8_t* aDstBuffer, int aDstStride,
+              int aWidth, int aHeight);
+
+int
+BGRA32ToRGB24(const uint8_t* aSrcBuffer, int aSrcStride,
+              uint8_t* aDstBuffer, int aDstStride,
+              int aWidth, int aHeight);
+int
+RGBA32ToBGR24(const uint8_t* aSrcBuffer, int aSrcStride,
+              uint8_t* aDstBuffer, int aDstStride,
+              int aWidth, int aHeight);
+
+int
+BGRA32ToBGR24(const uint8_t* aSrcBuffer, int aSrcStride,
+              uint8_t* aDstBuffer, int aDstStride,
+              int aWidth, int aHeight);
+
+/*
+ * Among RGB family.
+ */
+int
+RGB24Copy(const uint8_t* aSrcBuffer, int aSrcStride,
+          uint8_t* aDstBuffer, int aDstStride,
+          int aWidth, int aHeight);
+
+#define BGR24Copy RGB24Copy
+#define RGB24ToRGB24 RGB24Copy
+#define BGR24ToBGR24 BGR24Copy
+
+int
+RGB24ToBGR24(const uint8_t* aSrcBuffer, int aSrcStride,
+             uint8_t* aDstBuffer, int aDstStride,
+             int aWidth, int aHeight);
+
+#define BGR24ToRGB24 RGB24ToBGR24
+
+/*
+ * YUV family -> RGB family.
+ */
+int
+YUV444PToRGB24(const uint8_t* aYBuffer, int aYStride,
+               const uint8_t* aUBuffer, int aUStride,
+               const uint8_t* aVBuffer, int aVStride,
+               uint8_t* aDstBuffer, int aDstStride,
+               int aWidth, int aHeight);
+
+int
+YUV422PToRGB24(const uint8_t* aYBuffer, int aYStride,
+               const uint8_t* aUBuffer, int aUStride,
+               const uint8_t* aVBuffer, int aVStride,
+               uint8_t* aDstBuffer, int aDstStride,
+               int aWidth, int aHeight);
+
+int
+YUV420PToRGB24(const uint8_t* aYBuffer, int aYStride,
+               const uint8_t* aUBuffer, int aUStride,
+               const uint8_t* aVBuffer, int aVStride,
+               uint8_t* aDstBuffer, int aDstStride,
+               int aWidth, int aHeight);
+
+int
+NV12ToRGB24(const uint8_t* aYBuffer, int aYStride,
+            const uint8_t* aUVBuffer, int aUVStride,
+            uint8_t* aDstBuffer, int aDstStride,
+            int aWidth, int aHeight);
+
+int
+NV21ToRGB24(const uint8_t* aYBuffer, int aYStride,
+            const uint8_t* aVUBuffer, int aVUStride,
+            uint8_t* aDstBuffer, int aDstStride,
+            int aWidth, int aHeight);
+
+int
+YUV444PToBGR24(const uint8_t* aYBuffer, int aYStride,
+               const uint8_t* aUBuffer, int aUStride,
+               const uint8_t* aVBuffer, int aVStride,
+               uint8_t* aDstBuffer, int aDstStride,
+               int aWidth, int aHeight);
+
+int
+YUV422PToBGR24(const uint8_t* aYBuffer, int aYStride,
+               const uint8_t* aUBuffer, int aUStride,
+               const uint8_t* aVBuffer, int aVStride,
+               uint8_t* aDstBuffer, int aDstStride,
+               int aWidth, int aHeight);
+
+int
+YUV420PToBGR24(const uint8_t* aYBuffer, int aYStride,
+               const uint8_t* aUBuffer, int aUStride,
+               const uint8_t* aVBuffer, int aVStride,
+               uint8_t* aDstBuffer, int aDstStride,
+               int aWidth, int aHeight);
+
+int
+NV12ToBGR24(const uint8_t* aYBuffer, int aYStride,
+            const uint8_t* aUVBuffer, int aUVStride,
+            uint8_t* aDstBuffer, int aDstStride,
+            int aWidth, int aHeight);
+
+int
+NV21ToBGR24(const uint8_t* aYBuffer, int aYStride,
+            const uint8_t* aVUBuffer, int aVUStride,
+            uint8_t* aDstBuffer, int aDstStride,
+            int aWidth, int aHeight);
+
+/*
+ * YUV family -> RGBA family.
+ */
+int
+YUV444PToRGBA32(const uint8_t* aYBuffer, int aYStride,
+                const uint8_t* aUBuffer, int aUStride,
+                const uint8_t* aVBuffer, int aVStride,
+                uint8_t* aDstBuffer, int aDstStride,
+                int aWidth, int aHeight);
+
+int
+YUV422PToRGBA32(const uint8_t* aYBuffer, int aYStride,
+                const uint8_t* aUBuffer, int aUStride,
+                const uint8_t* aVBuffer, int aVStride,
+                uint8_t* aDstBuffer, int aDstStride,
+                int aWidth, int aHeight);
+
+int
+YUV420PToRGBA32(const uint8_t* aYBuffer, int aYStride,
+                const uint8_t* aUBuffer, int aUStride,
+                const uint8_t* aVBuffer, int aVStride,
+                uint8_t* aDstBuffer, int aDstStride,
+                int aWidth, int aHeight);
+
+int
+NV12ToRGBA32(const uint8_t* aYBuffer, int aYStride,
+             const uint8_t* aUVBuffer, int aUVStride,
+             uint8_t* aDstBuffer, int aDstStride,
+             int aWidth, int aHeight);
+
+int
+NV21ToRGBA32(const uint8_t* aYBuffer, int aYStride,
+             const uint8_t* aVUBuffer, int aVUStride,
+             uint8_t* aDstBuffer, int aDstStride,
+             int aWidth, int aHeight);
+
+int
+YUV444PToBGRA32(const uint8_t* aYBuffer, int aYStride,
+                const uint8_t* aUBuffer, int aUStride,
+                const uint8_t* aVBuffer, int aVStride,
+                uint8_t* aDstBuffer, int aDstStride,
+                int aWidth, int aHeight);
+
+int
+YUV422PToBGRA32(const uint8_t* aYBuffer, int aYStride,
+                const uint8_t* aUBuffer, int aUStride,
+                const uint8_t* aVBuffer, int aVStride,
+                uint8_t* aDstBuffer, int aDstStride,
+                int aWidth, int aHeight);
+
+int
+YUV420PToBGRA32(const uint8_t* aYBuffer, int aYStride,
+                const uint8_t* aUBuffer, int aUStride,
+                const uint8_t* aVBuffer, int aVStride,
+                uint8_t* aDstBuffer, int aDstStride,
+                int aWidth, int aHeight);
+
+int
+NV12ToBGRA32(const uint8_t* aYBuffer, int aYStride,
+             const uint8_t* aUVBuffer, int aUVStride,
+             uint8_t* aDstBuffer, int aDstStride,
+             int aWidth, int aHeight);
+
+int
+NV21ToBGRA32(const uint8_t* aYBuffer, int aYStride,
+             const uint8_t* aVUBuffer, int aVUStride,
+             uint8_t* aDstBuffer, int aDstStride,
+             int aWidth, int aHeight);
+
+/*
+ * RGB family -> YUV family.
+ */
+int
+RGB24ToYUV444P(const uint8_t* aSrcBuffer, int aSrcStride,
+               uint8_t* aYBuffer, int aYStride,
+               uint8_t* aUBuffer, int aUStride,
+               uint8_t* aVBuffer, int aVStride,
+               int aWidth, int aHeight);
+
+int
+RGB24ToYUV422P(const uint8_t* aSrcBuffer, int aSrcStride,
+               uint8_t* aYBuffer, int aYStride,
+               uint8_t* aUBuffer, int aUStride,
+               uint8_t* aVBuffer, int aVStride,
+               int aWidth, int aHeight);
+
+int
+RGB24ToYUV420P(const uint8_t* aSrcBuffer, int aSrcStride,
+               uint8_t* aYBuffer, int aYStride,
+               uint8_t* aUBuffer, int aUStride,
+               uint8_t* aVBuffer, int aVStride,
+               int aWidth, int aHeight);
+int
+RGB24ToNV12(const uint8_t* aSrcBuffer, int aSrcStride,
+            uint8_t* aYBuffer, int aYStride,
+            uint8_t* aUVBuffer, int aUVStride,
+            int aWidth, int aHeight);
+
+int
+RGB24ToNV21(const uint8_t* aSrcBuffer, int aSrcStride,
+            uint8_t* aYBuffer, int aYStride,
+            uint8_t* aVUBuffer, int aVUStride,
+            int aWidth, int aHeight);
+
+int
+BGR24ToYUV444P(const uint8_t* aSrcBuffer, int aSrcStride,
+               uint8_t* aYBuffer, int aYStride,
+               uint8_t* aUBuffer, int aUStride,
+               uint8_t* aVBuffer, int aVStride,
+               int aWidth, int aHeight);
+
+int
+BGR24ToYUV422P(const uint8_t* aSrcBuffer, int aSrcStride,
+               uint8_t* aYBuffer, int aYStride,
+               uint8_t* aUBuffer, int aUStride,
+               uint8_t* aVBuffer, int aVStride,
+               int aWidth, int aHeight);
+
+int
+BGR24ToYUV420P(const uint8_t* aSrcBuffer, int aSrcStride,
+               uint8_t* aYBuffer, int aYStride,
+               uint8_t* aUBuffer, int aUStride,
+               uint8_t* aVBuffer, int aVStride,
+               int aWidth, int aHeight);
+
+int
+BGR24ToNV12(const uint8_t* aSrcBuffer, int aSrcStride,
+            uint8_t* aYBuffer, int aYStride,
+            uint8_t* aUVBuffer, int aUVStride,
+            int aWidth, int aHeight);
+
+int
+BGR24ToNV21(const uint8_t* aSrcBuffer, int aSrcStride,
+            uint8_t* aYBuffer, int aYStride,
+            uint8_t* aUVBuffer, int aUVStride,
+            int aWidth, int aHeight);
+
+/*
+ * RGBA family -> YUV family.
+ */
+int
+RGBA32ToYUV444P(const uint8_t* aSrcBuffer, int aSrcStride,
+                uint8_t* aYBuffer, int aYStride,
+                uint8_t* aUBuffer, int aUStride,
+                uint8_t* aVBuffer, int aVStride,
+                int aWidth, int aHeight);
+
+int
+RGBA32ToYUV422P(const uint8_t* aSrcBuffer, int aSrcStride,
+                uint8_t* aYBuffer, int aYStride,
+                uint8_t* aUBuffer, int aUStride,
+                uint8_t* aVBuffer, int aVStride,
+                int aWidth, int aHeight);
+
+int
+RGBA32ToYUV420P(const uint8_t* aSrcBuffer, int aSrcStride,
+                uint8_t* aYBuffer, int aYStride,
+                uint8_t* aUBuffer, int aUStride,
+                uint8_t* aVBuffer, int aVStride,
+                int aWidth, int aHeight);
+int
+RGBA32ToNV12(const uint8_t* aSrcBuffer, int aSrcStride,
+             uint8_t* aYBuffer, int aYStride,
+             uint8_t* aUVBuffer, int aUVStride,
+             int aWidth, int aHeight);
+
+int
+RGBA32ToNV21(const uint8_t* aSrcBuffer, int aSrcStride,
+             uint8_t* aYBuffer, int aYStride,
+             uint8_t* aVUBuffer, int aVUStride,
+             int aWidth, int aHeight);
+
+int
+BGRA32ToYUV444P(const uint8_t* aSrcBuffer, int aSrcStride,
+                uint8_t* aYBuffer, int aYStride,
+                uint8_t* aUBuffer, int aUStride,
+                uint8_t* aVBuffer, int aVStride,
+                int aWidth, int aHeight);
+
+int
+BGRA32ToYUV422P(const uint8_t* aSrcBuffer, int aSrcStride,
+                uint8_t* aYBuffer, int aYStride,
+                uint8_t* aUBuffer, int aUStride,
+                uint8_t* aVBuffer, int aVStride,
+                int aWidth, int aHeight);
+
+int
+BGRA32ToYUV420P(const uint8_t* aSrcBuffer, int aSrcStride,
+                uint8_t* aYBuffer, int aYStride,
+                uint8_t* aUBuffer, int aUStride,
+                uint8_t* aVBuffer, int aVStride,
+                int aWidth, int aHeight);
+
+
+int
+BGRA32ToNV12(const uint8_t* aSrcBuffer, int aSrcStride,
+             uint8_t* aYBuffer, int aYStride,
+             uint8_t* aUVBuffer, int aUVStride,
+             int aWidth, int aHeight);
+
+int
+BGRA32ToNV21(const uint8_t* aSrcBuffer, int aSrcStride,
+             uint8_t* aYBuffer, int aYStride,
+             uint8_t* aUVBuffer, int aUVStride,
+             int aWidth, int aHeight);
+
+/*
+ * RGBA/RGB family <-> HSV family.
+ */
+int
+RGBA32ToHSV(const uint8_t* aSrcBuffer, int aSrcStride,
+            float* aDstBuffer, int aDstStride,
+            int aWidth, int aHeight);
+
+int
+BGRA32ToHSV(const uint8_t* aSrcBuffer, int aSrcStride,
+            float* aDstBuffer, int aDstStride,
+            int aWidth, int aHeight);
+
+int
+RGB24ToHSV(const uint8_t* aSrcBuffer, int aSrcStride,
+           float* aDstBuffer, int aDstStride,
+           int aWidth, int aHeight);
+
+int
+BGR24ToHSV(const uint8_t* aSrcBuffer, int aSrcStride,
+           float* aDstBuffer, int aDstStride,
+           int aWidth, int aHeight);
+
+int
+HSVToRGBA32(const float* aSrcBuffer, int aSrcStride,
+            uint8_t* aDstBuffer, int aDstStride,
+            int aWidth, int aHeight);
+
+int
+HSVToBGRA32(const float* aSrcBuffer, int aSrcStride,
+            uint8_t* aDstBuffer, int aDstStride,
+            int aWidth, int aHeight);
+
+int
+HSVToRGB24(const float* aSrcBuffer, int aSrcStride,
+           uint8_t* aDstBuffer, int aDstStride,
+           int aWidth, int aHeight);
+
+int
+HSVToBGR24(const float* aSrcBuffer, int aSrcStride,
+           uint8_t* aDstBuffer, int aDstStride,
+           int aWidth, int aHeight);
+
+/*
+ * RGBA/RGB family <-> Lab family.
+ */
+int
+RGBA32ToLab(const uint8_t* aSrcBuffer, int aSrcStride,
+            float* aDstBuffer, int aDstStride,
+            int aWidth, int aHeight);
+
+int
+BGRA32ToLab(const uint8_t* aSrcBuffer, int aSrcStride,
+            float* aDstBuffer, int aDstStride,
+            int aWidth, int aHeight);
+
+int
+RGB24ToLab(const uint8_t* aSrcBuffer, int aSrcStride,
+           float* aDstBuffer, int aDstStride,
+           int aWidth, int aHeight);
+
+int
+BGR24ToLab(const uint8_t* aSrcBuffer, int aSrcStride,
+           float* aDstBuffer, int aDstStride,
+           int aWidth, int aHeight);
+
+int
+LabToRGBA32(const float* aSrcBuffer, int aSrcStride,
+            uint8_t* aDstBuffer, int aDstStride,
+            int aWidth, int aHeight);
+
+int
+LabToBGRA32(const float* aSrcBuffer, int aSrcStride,
+            uint8_t* aDstBuffer, int aDstStride,
+            int aWidth, int aHeight);
+
+int
+LabToRGB24(const float* aSrcBuffer, int aSrcStride,
+           uint8_t* aDstBuffer, int aDstStride,
+           int aWidth, int aHeight);
+
+int
+LabToBGR24(const float* aSrcBuffer, int aSrcStride,
+           uint8_t* aDstBuffer, int aDstStride,
+           int aWidth, int aHeight);
+
+/*
+ * RGBA/RGB family -> Gray8.
+ */
+int
+RGB24ToGray8(const uint8_t* aSrcBuffer, int aSrcStride,
+             uint8_t* aDstBuffer, int aDstStride,
+             int aWidth, int aHeight);
+
+int
+BGR24ToGray8(const uint8_t* aSrcBuffer, int aSrcStride,
+             uint8_t* aDstBuffer, int aDstStride,
+             int aWidth, int aHeight);
+
+int
+RGBA32ToGray8(const uint8_t* aSrcBuffer, int aSrcStride,
+              uint8_t* aDstBuffer, int aDstStride,
+              int aWidth, int aHeight);
+
+int
+BGRA32ToGray8(const uint8_t* aSrcBuffer, int aSrcStride,
+              uint8_t* aDstBuffer, int aDstStride,
+              int aWidth, int aHeight);
+
+/*
+ * YUV family -> Gray8.
+ */
+int
+YUV444PToGray8(const uint8_t* aYBuffer, int aYStride,
+               const uint8_t* aUBuffer, int aUStride,
+               const uint8_t* aVBuffer, int aVStride,
+               uint8_t* aDstBuffer, int aDstStride,
+               int aWidth, int aHeight);
+
+int
+YUV422PToGray8(const uint8_t* aYBuffer, int aYStride,
+               const uint8_t* aUBuffer, int aUStride,
+               const uint8_t* aVBuffer, int aVStride,
+               uint8_t* aDstBuffer, int aDstStride,
+               int aWidth, int aHeight);
+
+int
+YUV420PToGray8(const uint8_t* aYBuffer, int aYStride,
+               const uint8_t* aUBuffer, int aUStride,
+               const uint8_t* aVBuffer, int aVStride,
+               uint8_t* aDstBuffer, int aDstStride,
+               int aWidth, int aHeight);
+
+int
+NV12ToGray8(const uint8_t* aYBuffer, int aYStride,
+            const uint8_t* aUBuffer, int aUStride,
+            uint8_t* aDstBuffer, int aDstStride,
+            int aWidth, int aHeight);
+
+int
+NV21ToGray8(const uint8_t* aYBuffer, int aYStride,
+            const uint8_t* aUBuffer, int aUStride,
+            uint8_t* aDstBuffer, int aDstStride,
+            int aWidth, int aHeight);
+
+} // namespace dom
+} // namespace mozilla
+#endif // mozilla_dom_ImageBitmapColorUtils_h
new file mode 100644
--- /dev/null
+++ b/dom/canvas/ImageBitmapUtils.cpp
@@ -0,0 +1,2778 @@
+/* -*- 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 "ImageBitmapColorUtils.h"
+#include "ImageContainer.h"
+#include "libyuv.h"
+#include "mozilla/dom/ImageBitmapBinding.h"
+#include "mozilla/Function.h"
+#include "mozilla/gfx/2D.h"
+
+using namespace libyuv;
+using namespace mozilla::gfx;
+
+namespace mozilla {
+namespace dom {
+namespace imagebitmapformat {
+
+class Utils;
+class Utils_RGBA32;
+class Utils_BGRA32;
+class Utils_RGB24;
+class Utils_BGR24;
+class Utils_Gray8;
+class Utils_YUV444P;
+class Utils_YUV422P;
+class Utils_YUV420P;
+class Utils_YUV420SP_NV12;
+class Utils_YUV420SP_NV21;
+class Utils_HSV;
+class Utils_Lab;
+class Utils_Depth;
+
+static int GetBytesPerPixelValue(ChannelPixelLayoutDataType aDataType)
+{
+  switch (aDataType)
+  {
+  case ChannelPixelLayoutDataType::Uint8:
+    return sizeof(uint8_t);
+  case ChannelPixelLayoutDataType::Int8:
+    return sizeof(int8_t);
+  case ChannelPixelLayoutDataType::Uint16:
+    return sizeof(uint16_t);
+  case ChannelPixelLayoutDataType::Int16:
+    return sizeof(int16_t);
+  case ChannelPixelLayoutDataType::Uint32:
+    return sizeof(uint32_t);
+  case ChannelPixelLayoutDataType::Int32:
+    return sizeof(int32_t);
+  case ChannelPixelLayoutDataType::Float32:
+    return sizeof(float);
+  case ChannelPixelLayoutDataType::Float64:
+    return sizeof(double);
+  default:
+    return 0;
+  }
+}
+
+/*
+ * 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<Utils, 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 Utils
+{
+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;
+
+  // 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(Utils* aDstFormat, const uint8_t* aSrcBuffer, const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer) = 0;
+
+  // ConvertFrom():
+  // Convert the source image data (which is in the aSrcFormat format, the pixel
+  // layout is described by the aSrcLayout and the raw data is stored in the
+  // aSrcBuffer) to the current ImageBitmapFormat.
+  // The converted image data is stored in the aDstBuffer and described by the
+  // returned ImagePixelLayout object.
+  virtual UniquePtr<ImagePixelLayout>
+  ConvertFrom(Utils_RGBA32* aSrcFormat, const uint8_t* aSrcBuffer, const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer) = 0;
+
+  virtual UniquePtr<ImagePixelLayout>
+  ConvertFrom(Utils_BGRA32* aSrcFormat, const uint8_t* aSrcBuffer, const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer) = 0;
+
+  virtual UniquePtr<ImagePixelLayout>
+  ConvertFrom(Utils_RGB24* aSrcFormat, const uint8_t* aSrcBuffer, const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer) = 0;
+
+  virtual UniquePtr<ImagePixelLayout>
+  ConvertFrom(Utils_BGR24* aSrcFormat, const uint8_t* aSrcBuffer, const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer) = 0;
+
+  virtual UniquePtr<ImagePixelLayout>
+  ConvertFrom(Utils_Gray8* aSrcFormat, const uint8_t* aSrcBuffer, const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer) = 0;
+
+  virtual UniquePtr<ImagePixelLayout>
+  ConvertFrom(Utils_YUV444P* aSrcFormat, const uint8_t* aSrcBuffer, const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer) = 0;
+
+  virtual UniquePtr<ImagePixelLayout>
+  ConvertFrom(Utils_YUV422P* aSrcFormat, const uint8_t* aSrcBuffer, const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer) = 0;
+
+  virtual UniquePtr<ImagePixelLayout>
+  ConvertFrom(Utils_YUV420P* aSrcFormat, const uint8_t* aSrcBuffer, const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer) = 0;
+
+  virtual UniquePtr<ImagePixelLayout>
+  ConvertFrom(Utils_YUV420SP_NV12* aSrcFormat, const uint8_t* aSrcBuffer, const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer) = 0;
+
+  virtual UniquePtr<ImagePixelLayout>
+  ConvertFrom(Utils_YUV420SP_NV21* aSrcFormat, const uint8_t* aSrcBuffer, const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer) = 0;
+
+  virtual UniquePtr<ImagePixelLayout>
+  ConvertFrom(Utils_HSV* aSrcFormat, const uint8_t* aSrcBuffer, const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer) = 0;
+
+  virtual UniquePtr<ImagePixelLayout>
+  ConvertFrom(Utils_Lab* aSrcFormat, const uint8_t* aSrcBuffer, const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer) = 0;
+
+  virtual UniquePtr<ImagePixelLayout>
+  ConvertFrom(Utils_Depth* 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:
+  Utils(uint32_t aChannels,
+                         ChannelPixelLayoutDataType aDataType)
+  : mChannels(aChannels)
+  , mBytesPerPixelValue(GetBytesPerPixelValue(aDataType))
+  , mDataType(aDataType)
+  {
+  }
+
+  virtual ~Utils()
+  {
+  }
+
+  const uint8_t mChannels;
+  const int mBytesPerPixelValue;
+  const ChannelPixelLayoutDataType mDataType;
+};
+
+#define DECLARE_Utils(NAME)                          \
+class Utils_ ## NAME : public Utils \
+{                                                                     \
+private:                                                              \
+  explicit Utils_ ## NAME ();                        \
+  ~Utils_ ## NAME () = default;                      \
+  Utils_ ## NAME (Utils_ ## NAME const &) = delete;             \
+  Utils_ ## NAME (Utils_ ## NAME &&) = delete;                  \
+  Utils_ ## NAME & operator=(Utils_ ## NAME const &) = delete;  \
+  Utils_ ## NAME & operator=(Utils_ ## NAME &&) = delete;       \
+                                                                                                  \
+public:                                                     \
+  static Utils_ ## NAME & GetInstance();   \
+                                                            \
+  virtual uint32_t NeededBufferSize(uint32_t aWidth, uint32_t aHeight) override;  \
+                                                                                  \
+  virtual UniquePtr<ImagePixelLayout>                          \
+  CreateDefaultLayout(uint32_t, uint32_t, uint32_t) override;  \
+                                                               \
+  virtual UniquePtr<ImagePixelLayout>                                                             \
+  ConvertTo(Utils*, const uint8_t*, const ImagePixelLayout*, uint8_t*) override; \
+                                                                                                  \
+  virtual UniquePtr<ImagePixelLayout>                                                                       \
+  ConvertFrom(Utils_RGBA32*, const uint8_t*, const ImagePixelLayout*, uint8_t*) override;  \
+                                                                                                            \
+  virtual UniquePtr<ImagePixelLayout>                                                                       \
+  ConvertFrom(Utils_BGRA32*, const uint8_t*, const ImagePixelLayout*, uint8_t*) override;  \
+                                                                                                            \
+  virtual UniquePtr<ImagePixelLayout>                                                                     \
+  ConvertFrom(Utils_RGB24*, const uint8_t*, const ImagePixelLayout*, uint8_t*) override; \
+                                                                                                          \
+  virtual UniquePtr<ImagePixelLayout>                                                                     \
+  ConvertFrom(Utils_BGR24*, const uint8_t*, const ImagePixelLayout*, uint8_t*) override; \
+                                                                                                          \
+  virtual UniquePtr<ImagePixelLayout>                                                                     \
+  ConvertFrom(Utils_Gray8*, const uint8_t*, const ImagePixelLayout*, uint8_t*) override; \
+                                                                                                          \
+  virtual UniquePtr<ImagePixelLayout>                                                                       \
+  ConvertFrom(Utils_YUV444P*, const uint8_t*, const ImagePixelLayout*, uint8_t*) override; \
+                                                                                                            \
+  virtual UniquePtr<ImagePixelLayout>                                                                       \
+  ConvertFrom(Utils_YUV422P*, const uint8_t*, const ImagePixelLayout*, uint8_t*) override; \
+                                                                                                            \
+  virtual UniquePtr<ImagePixelLayout>                                                                       \
+  ConvertFrom(Utils_YUV420P*, const uint8_t*, const ImagePixelLayout*, uint8_t*) override; \
+                                                                                                            \
+  virtual UniquePtr<ImagePixelLayout>                                                                             \
+  ConvertFrom(Utils_YUV420SP_NV12*, const uint8_t*, const ImagePixelLayout*, uint8_t*) override; \
+                                                                                                                  \
+  virtual UniquePtr<ImagePixelLayout>                                                                             \
+  ConvertFrom(Utils_YUV420SP_NV21*, const uint8_t*, const ImagePixelLayout*, uint8_t*) override; \
+                                                                                                                  \
+  virtual UniquePtr<ImagePixelLayout>                                                                   \
+  ConvertFrom(Utils_HSV*, const uint8_t*, const ImagePixelLayout*, uint8_t*) override; \
+                                                                                                        \
+  virtual UniquePtr<ImagePixelLayout>                                                                   \
+  ConvertFrom(Utils_Lab*, const uint8_t*, const ImagePixelLayout*, uint8_t*) override; \
+                                                                                                        \
+  virtual UniquePtr<ImagePixelLayout>                                                                     \
+  ConvertFrom(Utils_Depth*, const uint8_t*, const ImagePixelLayout*, uint8_t*) override; \
+                                                                                                          \
+  virtual bool                                  \
+  CanConvertFrom(ImageBitmapFormat) override;   \
+};
+
+DECLARE_Utils(RGBA32)
+DECLARE_Utils(BGRA32)
+DECLARE_Utils(RGB24)
+DECLARE_Utils(BGR24)
+DECLARE_Utils(Gray8)
+DECLARE_Utils(YUV444P)
+DECLARE_Utils(YUV422P)
+DECLARE_Utils(YUV420P)
+DECLARE_Utils(YUV420SP_NV12)
+DECLARE_Utils(YUV420SP_NV21)
+DECLARE_Utils(HSV)
+DECLARE_Utils(Lab)
+DECLARE_Utils(Depth)
+
+#undef DECLARE_Utils
+
+/*
+ * ImageBitmapFormatUtils.
+ */
+/* static */ UtilsUniquePtr
+Utils::GetUtils(ImageBitmapFormat aFormat)
+{
+  switch(aFormat)
+  {
+  case ImageBitmapFormat::RGBA32:
+    return UtilsUniquePtr(&Utils_RGBA32::GetInstance());
+  case ImageBitmapFormat::BGRA32:
+    return UtilsUniquePtr(&Utils_BGRA32::GetInstance());
+  case ImageBitmapFormat::RGB24:
+    return UtilsUniquePtr(&Utils_RGB24::GetInstance());
+  case ImageBitmapFormat::BGR24:
+    return UtilsUniquePtr(&Utils_BGR24::GetInstance());
+  case ImageBitmapFormat::GRAY8:
+    return UtilsUniquePtr(&Utils_Gray8::GetInstance());
+  case ImageBitmapFormat::YUV444P:
+    return UtilsUniquePtr(&Utils_YUV444P::GetInstance());
+  case ImageBitmapFormat::YUV422P:
+    return UtilsUniquePtr(&Utils_YUV422P::GetInstance());
+  case ImageBitmapFormat::YUV420P:
+    return UtilsUniquePtr(&Utils_YUV420P::GetInstance());
+  case ImageBitmapFormat::YUV420SP_NV12:
+    return UtilsUniquePtr(&Utils_YUV420SP_NV12::GetInstance());
+  case ImageBitmapFormat::YUV420SP_NV21:
+    return UtilsUniquePtr(&Utils_YUV420SP_NV21::GetInstance());
+  case ImageBitmapFormat::HSV:
+    return UtilsUniquePtr(&Utils_HSV::GetInstance());
+  case ImageBitmapFormat::Lab:
+    return UtilsUniquePtr(&Utils_Lab::GetInstance());
+  case ImageBitmapFormat::DEPTH:
+    return UtilsUniquePtr(&Utils_Depth::GetInstance());
+  default:
+    return nullptr;
+  }
+}
+
+/*
+ * Helper functions.
+ */
+template<typename SrcType, typename DstType>
+static UniquePtr<ImagePixelLayout>
+CvtSimpleImgToSimpleImg(Utils* aSrcUtils, const SrcType* aSrcBuffer,
+                        const ImagePixelLayout* aSrcLayout, DstType* aDstBuffer,
+                        ImageBitmapFormat aDstFormat, int aDstChannelCount,
+                        mozilla::function<int (const SrcType*, int, DstType*, int, int, int)> converter)
+{
+  MOZ_ASSERT(aSrcUtils, "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() == aSrcUtils->GetChannelCount(),
+             "The channel count is wrong.");
+
+  const int dstStride = channels[0].mWidth * aDstChannelCount * sizeof(DstType);
+  int rv = converter(aSrcBuffer, channels[0].mStride,
+                     aDstBuffer, dstStride,
+                     channels[0].mWidth, channels[0].mHeight);
+
+  if (NS_WARN_IF(rv != 0)) {
+    return nullptr;
+  }
+
+  return CreateDefaultPixelLayout(aDstFormat, channels[0].mWidth,
+                                  channels[0].mHeight, dstStride);
+}
+
+static UniquePtr<ImagePixelLayout>
+CvtYUVImgToSimpleImg(Utils* aSrcUtils, const uint8_t* aSrcBuffer,
+                     const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer,
+                     ImageBitmapFormat aDstFormat, int aDstChannelCount,
+                     mozilla::function<int (const uint8_t*, int, const uint8_t*, int, const uint8_t*, int, uint8_t*, int, int, int)> converter)
+{
+  MOZ_ASSERT(aSrcUtils, "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() == aSrcUtils->GetChannelCount(),
+             "The channel count is wrong.");
+
+  const int dstStride = channels[0].mWidth * aDstChannelCount * sizeof(uint8_t);
+  int rv = converter(aSrcBuffer + channels[0].mOffset, channels[0].mStride,
+                     aSrcBuffer + channels[1].mOffset, channels[1].mStride,
+                     aSrcBuffer + channels[2].mOffset, channels[2].mStride,
+                     aDstBuffer, dstStride,
+                     channels[0].mWidth, channels[0].mHeight);
+
+  if (NS_WARN_IF(rv != 0)) {
+    return nullptr;
+  }
+
+  return CreateDefaultPixelLayout(aDstFormat, channels[0].mWidth,
+                                  channels[0].mHeight, dstStride);
+}
+
+static UniquePtr<ImagePixelLayout>
+CvtNVImgToSimpleImg(Utils* aSrcUtils, const uint8_t* aSrcBuffer,
+                    const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer,
+                    ImageBitmapFormat aDstFormat, int aDstChannelCount,
+                    mozilla::function<int (const uint8_t*, int, const uint8_t*, int, uint8_t*, int, int, int)> converter)
+{
+  MOZ_ASSERT(aSrcUtils, "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() == aSrcUtils->GetChannelCount(),
+             "The channel count is wrong.");
+
+  const int dstStride = channels[0].mWidth * aDstChannelCount * sizeof(uint8_t);
+  int rv = converter(aSrcBuffer + channels[0].mOffset, channels[0].mStride,
+                     aSrcBuffer + channels[1].mOffset, channels[1].mStride,
+                     aDstBuffer, dstStride,
+                     channels[0].mWidth, channels[0].mHeight);
+
+  if (NS_WARN_IF(rv != 0)) {
+    return nullptr;
+  }
+
+  return CreateDefaultPixelLayout(aDstFormat, channels[0].mWidth,
+                                  channels[0].mHeight, dstStride);
+}
+
+static UniquePtr<ImagePixelLayout>
+CvtSimpleImgToYUVImg(Utils* aSrcUtils, const uint8_t* aSrcBuffer,
+                     const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer,
+                     ImageBitmapFormat aDstFormat,
+                     mozilla::function<int (const uint8_t*, int, uint8_t*, int, uint8_t*, int, uint8_t*, int, int, int)> converter)
+{
+  MOZ_ASSERT(aSrcUtils, "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.");
+
+  UniquePtr<ImagePixelLayout> layout =
+    CreateDefaultPixelLayout(aDstFormat, (*aSrcLayout)[0].mWidth,
+                             (*aSrcLayout)[0].mHeight, (*aSrcLayout)[0].mWidth);
+
+  MOZ_ASSERT(layout, "Cannot create a ImagePixelLayout.");
+
+  const nsTArray<ChannelPixelLayout>& channels = *layout;
+
+  int rv = converter(aSrcBuffer, (*aSrcLayout)[0].mStride,
+                     aDstBuffer + channels[0].mOffset, channels[0].mStride,
+                     aDstBuffer + channels[1].mOffset, channels[1].mStride,
+                     aDstBuffer + channels[2].mOffset, channels[2].mStride,
+                     channels[0].mWidth, channels[0].mHeight);
+
+  if (NS_WARN_IF(rv != 0)) {
+    return nullptr;
+  }
+
+  return layout;
+}
+
+static UniquePtr<ImagePixelLayout>
+CvtSimpleImgToNVImg(Utils* aSrcUtils, const uint8_t* aSrcBuffer,
+                    const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer,
+                    ImageBitmapFormat aDstFormat,
+                    mozilla::function<int (const uint8_t*, int, uint8_t*, int, uint8_t*, int, int, int)> converter)
+{
+  MOZ_ASSERT(aSrcUtils, "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.");
+
+  UniquePtr<ImagePixelLayout> layout =
+    CreateDefaultPixelLayout(aDstFormat, (*aSrcLayout)[0].mWidth,
+                             (*aSrcLayout)[0].mHeight, (*aSrcLayout)[0].mWidth);
+
+  MOZ_ASSERT(layout, "Cannot create a ImagePixelLayout.");
+
+  const nsTArray<ChannelPixelLayout>& channels = *layout;
+
+  int rv = converter(aSrcBuffer, (*aSrcLayout)[0].mStride,
+                     aDstBuffer + channels[0].mOffset, channels[0].mStride,
+                     aDstBuffer + channels[1].mOffset, channels[1].mStride,
+                     channels[0].mWidth, channels[0].mHeight);
+
+  if (NS_WARN_IF(rv != 0)) {
+    return nullptr;
+  }
+
+  return layout;
+}
+
+template<class SrcUtilsType, class DstUtilsType>
+static UniquePtr<ImagePixelLayout>
+TwoPassConversion(SrcUtilsType* aSrcUtils, const uint8_t* aSrcBuffer,
+                  const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer,
+                  ImageBitmapFormat aMiddleFormat, DstUtilsType* aDstUtils)
+{
+  MOZ_ASSERT(aSrcUtils, "Convert color from a null source utility.");
+  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.");
+
+  // I444 -> I420 -> I422
+  UtilsUniquePtr yuv420PUtils = Utils::GetUtils(aMiddleFormat);
+  UniquePtr<uint8_t> yuv420PBuffer(new uint8_t[yuv420PUtils->NeededBufferSize((*aSrcLayout)[0].mWidth, (*aSrcLayout)[0].mHeight)]);
+  UniquePtr<ImagePixelLayout> yuv420PLayout = yuv420PUtils->ConvertFrom(aSrcUtils, aSrcBuffer, aSrcLayout, yuv420PBuffer.get());
+  return yuv420PUtils->ConvertTo(aDstUtils, yuv420PBuffer.get(), yuv420PLayout.get(), aDstBuffer);
+}
+
+static UniquePtr<ImagePixelLayout>
+PureCopy(Utils* aSrcUtils, const uint8_t* aSrcBuffer,
+         const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer,
+         ImageBitmapFormat aDstFormat)
+{
+  MOZ_ASSERT(aSrcUtils, "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() == aSrcUtils->GetChannelCount(),
+             "The channel count is wrong.");
+
+
+  uint32_t length = 0;
+
+  if (aDstFormat == ImageBitmapFormat::RGBA32 ||
+      aDstFormat == ImageBitmapFormat::BGRA32 ||
+      aDstFormat == ImageBitmapFormat::RGB24 ||
+      aDstFormat == ImageBitmapFormat::BGR24 ||
+      aDstFormat == ImageBitmapFormat::GRAY8 ||
+      aDstFormat == ImageBitmapFormat::HSV ||
+      aDstFormat == ImageBitmapFormat::Lab ||
+      aDstFormat == ImageBitmapFormat::DEPTH) {
+    length = channels[0].mHeight * channels[0].mStride;
+  } else if (aDstFormat == ImageBitmapFormat::YUV444P ||
+             aDstFormat == ImageBitmapFormat::YUV422P ||
+             aDstFormat == ImageBitmapFormat::YUV420P) {
+    length = channels[0].mHeight * channels[0].mStride +
+             channels[1].mHeight * channels[1].mStride +
+             channels[2].mHeight * channels[2].mStride;
+  } else if (aDstFormat == ImageBitmapFormat::YUV420SP_NV12 ||
+             aDstFormat == ImageBitmapFormat::YUV420SP_NV21) {
+    length = channels[0].mHeight * channels[0].mStride +
+             channels[1].mHeight * channels[1].mStride;
+  }
+
+  memcpy(aDstBuffer, aSrcBuffer, length);
+
+  UniquePtr<ImagePixelLayout> layout(new ImagePixelLayout(*aSrcLayout));
+  return layout;
+}
+
+UniquePtr<ImagePixelLayout>
+CreateDefaultLayoutForSimpleImage(uint32_t aWidth, uint32_t aHeight,
+                                  uint32_t aStride, int aChannels,
+                                  int aBytesPerPixelValue,
+                                  ChannelPixelLayoutDataType aDataType)
+{
+  UniquePtr<ImagePixelLayout> layout(new ImagePixelLayout(aChannels));
+
+  // set mChannels
+  for (uint8_t i = 0; i < aChannels; ++i) {
+    ChannelPixelLayout* channel = layout->AppendElement();
+    channel->mOffset = i * aBytesPerPixelValue;
+    channel->mWidth = aWidth;
+    channel->mHeight = aHeight;
+    channel->mDataType = aDataType; //ChannelPixelLayoutDataType::Uint8;
+    channel->mStride = aStride;
+    channel->mSkip = aChannels - 1;
+  }
+
+  return layout;
+}
+
+/*
+ * Utils_RGBA32.
+ */
+/* static */Utils_RGBA32&
+Utils_RGBA32::GetInstance()
+{
+  static Utils_RGBA32 instance;
+  return instance;
+}
+
+Utils_RGBA32::Utils_RGBA32()
+: Utils(4, ChannelPixelLayoutDataType::Uint8)
+{
+}
+
+uint32_t
+Utils_RGBA32::NeededBufferSize(uint32_t aWidth, uint32_t aHeight)
+{
+  return aWidth * aHeight * (uint32_t)mChannels * mBytesPerPixelValue;
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_RGBA32::ConvertTo(Utils* aDstFormat, const uint8_t* aSrcBuffer,
+                        const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return aDstFormat->ConvertFrom(this, aSrcBuffer, aSrcLayout, aDstBuffer);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_RGBA32::ConvertFrom(Utils_RGBA32* aSrcUtils, const uint8_t* aSrcBuffer,
+                          const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return PureCopy(aSrcUtils, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::RGBA32);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_RGBA32::ConvertFrom(Utils_BGRA32* aSrcFormat, const uint8_t* aSrcBuffer,
+                          const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return CvtSimpleImgToSimpleImg<uint8_t, uint8_t>(aSrcFormat, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::RGBA32, 4, &libyuv::ABGRToARGB);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_RGBA32::ConvertFrom(Utils_RGB24* aSrcFormat, const uint8_t* aSrcBuffer,
+                          const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return CvtSimpleImgToSimpleImg<uint8_t, uint8_t>(aSrcFormat, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::RGBA32, 4, &RGB24ToRGBA32);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_RGBA32::ConvertFrom(Utils_BGR24* aSrcFormat, const uint8_t* aSrcBuffer,
+                          const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return CvtSimpleImgToSimpleImg<uint8_t, uint8_t>(aSrcFormat, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::RGBA32, 4, &BGR24ToRGBA32);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_RGBA32::ConvertFrom(Utils_Gray8* aSrcFormat, const uint8_t* aSrcBuffer,
+                          const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return nullptr;
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_RGBA32::ConvertFrom(Utils_YUV444P* aSrcUtils, const uint8_t* aSrcBuffer,
+                          const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return CvtYUVImgToSimpleImg(aSrcUtils, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::RGBA32, 4, &YUV444PToRGBA32);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_RGBA32::ConvertFrom(Utils_YUV422P* aSrcUtils, const uint8_t* aSrcBuffer,
+                          const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return CvtYUVImgToSimpleImg(aSrcUtils, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::RGBA32, 4, &YUV422PToRGBA32);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_RGBA32::ConvertFrom(Utils_YUV420P* aSrcUtils, const uint8_t* aSrcBuffer,
+                          const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return CvtYUVImgToSimpleImg(aSrcUtils, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::RGBA32, 4, &libyuv::I420ToABGR);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_RGBA32::ConvertFrom(Utils_YUV420SP_NV12* aSrcUtils, const uint8_t* aSrcBuffer,
+                          const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return CvtNVImgToSimpleImg(aSrcUtils, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::RGBA32, 4, &NV12ToRGBA32);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_RGBA32::ConvertFrom(Utils_YUV420SP_NV21* aSrcUtils, const uint8_t* aSrcBuffer,
+                          const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return CvtNVImgToSimpleImg(aSrcUtils, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::RGBA32, 4, &NV21ToRGBA32);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_RGBA32::ConvertFrom(Utils_HSV* aSrcFormat, const uint8_t* aSrcBuffer,
+                          const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return CvtSimpleImgToSimpleImg<float, uint8_t>(aSrcFormat, (const float*)aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::RGBA32, 4, &HSVToRGBA32);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_RGBA32::ConvertFrom(Utils_Lab* aSrcFormat, const uint8_t* aSrcBuffer,
+                          const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return CvtSimpleImgToSimpleImg<float, uint8_t>(aSrcFormat, (const float*)aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::RGBA32, 4, &LabToRGBA32);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_RGBA32::ConvertFrom(Utils_Depth* aSrcFormat, const uint8_t* aSrcBuffer,
+                          const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return nullptr;
+}
+
+bool
+Utils_RGBA32::CanConvertFrom(ImageBitmapFormat aSrcFormat)
+{
+  if (aSrcFormat == ImageBitmapFormat::GRAY8 ||
+      aSrcFormat == ImageBitmapFormat::DEPTH) {
+    return false;
+  }
+
+  return true;
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_RGBA32::CreateDefaultLayout(uint32_t aWidth, uint32_t aHeight, uint32_t aStride)
+{
+  return CreateDefaultLayoutForSimpleImage(aWidth, aHeight, aStride, mChannels, mBytesPerPixelValue, mDataType);
+}
+
+/*
+ * Utils_BGRA32.
+ */
+/* static */Utils_BGRA32&
+Utils_BGRA32::GetInstance()
+{
+  static Utils_BGRA32 instance;
+  return instance;
+}
+
+Utils_BGRA32::Utils_BGRA32()
+: Utils(4, ChannelPixelLayoutDataType::Uint8)
+{
+}
+
+uint32_t
+Utils_BGRA32::NeededBufferSize(uint32_t aWidth, uint32_t aHeight)
+{
+  return aWidth * aHeight * (uint32_t)mChannels * mBytesPerPixelValue;
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_BGRA32::ConvertTo(Utils* aDstFormat, const uint8_t* aSrcBuffer,
+                        const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return aDstFormat->ConvertFrom(this, aSrcBuffer, aSrcLayout, aDstBuffer);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_BGRA32::ConvertFrom(Utils_RGBA32* aSrcFormat, const uint8_t* aSrcBuffer,
+                          const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return CvtSimpleImgToSimpleImg<uint8_t, uint8_t>(aSrcFormat, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::BGRA32, 4, &libyuv::ABGRToARGB);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_BGRA32::ConvertFrom(Utils_BGRA32* aSrcUtils, const uint8_t* aSrcBuffer,
+                          const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return PureCopy(aSrcUtils, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::BGRA32);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_BGRA32::ConvertFrom(Utils_RGB24* aSrcFormat, const uint8_t* aSrcBuffer,
+                          const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return CvtSimpleImgToSimpleImg<uint8_t, uint8_t>(aSrcFormat, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::BGRA32, 4, &RGB24ToBGRA32);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_BGRA32::ConvertFrom(Utils_BGR24* aSrcFormat, const uint8_t* aSrcBuffer,
+                          const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return CvtSimpleImgToSimpleImg<uint8_t, uint8_t>(aSrcFormat, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::BGRA32, 4, &BGR24ToBGRA32);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_BGRA32::ConvertFrom(Utils_Gray8* aSrcFormat, const uint8_t* aSrcBuffer,
+                          const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return nullptr;
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_BGRA32::ConvertFrom(Utils_YUV444P* aSrcUtils, const uint8_t* aSrcBuffer,
+                          const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return CvtYUVImgToSimpleImg(aSrcUtils, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::BGRA32, 4, &YUV444PToBGRA32);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_BGRA32::ConvertFrom(Utils_YUV422P* aSrcUtils, const uint8_t* aSrcBuffer,
+                          const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return CvtYUVImgToSimpleImg(aSrcUtils, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::BGRA32, 4, &libyuv::I422ToARGB);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_BGRA32::ConvertFrom(Utils_YUV420P* aSrcUtils, const uint8_t* aSrcBuffer,
+                          const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return CvtYUVImgToSimpleImg(aSrcUtils, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::BGRA32, 4, &libyuv::I420ToARGB);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_BGRA32::ConvertFrom(Utils_YUV420SP_NV12* aSrcUtils, const uint8_t* aSrcBuffer,
+                          const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return CvtNVImgToSimpleImg(aSrcUtils, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::BGRA32, 4, &libyuv::NV12ToARGB);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_BGRA32::ConvertFrom(Utils_YUV420SP_NV21* aSrcUtils, const uint8_t* aSrcBuffer,
+                          const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return CvtNVImgToSimpleImg(aSrcUtils, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::BGRA32, 4, &libyuv::NV21ToARGB);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_BGRA32::ConvertFrom(Utils_HSV* aSrcFormat, const uint8_t* aSrcBuffer,
+                          const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return CvtSimpleImgToSimpleImg<float, uint8_t>(aSrcFormat, (const float*)aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::BGRA32, 4, &HSVToBGRA32);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_BGRA32::ConvertFrom(Utils_Lab* aSrcFormat, const uint8_t* aSrcBuffer,
+                          const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return CvtSimpleImgToSimpleImg<float, uint8_t>(aSrcFormat, (const float*)aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::BGRA32, 4, &LabToBGRA32);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_BGRA32::ConvertFrom(Utils_Depth* aSrcFormat, const uint8_t* aSrcBuffer,
+                          const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return nullptr;
+}
+
+bool
+Utils_BGRA32::CanConvertFrom(ImageBitmapFormat aSrcFormat)
+{
+  if (aSrcFormat == ImageBitmapFormat::RGBA32 ||
+      aSrcFormat == ImageBitmapFormat::BGRA32 ||
+      aSrcFormat == ImageBitmapFormat::YUV420P) {
+    return true;
+  }
+
+  return false;
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_BGRA32::CreateDefaultLayout(uint32_t aWidth, uint32_t aHeight, uint32_t aStride)
+{
+  return CreateDefaultLayoutForSimpleImage(aWidth, aHeight, aStride, mChannels, mBytesPerPixelValue, mDataType);
+}
+
+/*
+ * Utils_RGB24.
+ */
+/* static */Utils_RGB24&
+Utils_RGB24::GetInstance()
+{
+  static Utils_RGB24 instance;
+  return instance;
+}
+
+Utils_RGB24::Utils_RGB24()
+: Utils(3, ChannelPixelLayoutDataType::Uint8)
+{
+}
+
+uint32_t
+Utils_RGB24::NeededBufferSize(uint32_t aWidth, uint32_t aHeight)
+{
+  return aWidth * aHeight * (uint32_t)mChannels * mBytesPerPixelValue;
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_RGB24::ConvertTo(Utils* aDstFormat, const uint8_t* aSrcBuffer,
+                       const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return aDstFormat->ConvertFrom(this, aSrcBuffer, aSrcLayout, aDstBuffer);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_RGB24::ConvertFrom(Utils_RGBA32* aSrcFormat, const uint8_t* aSrcBuffer,
+                         const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return CvtSimpleImgToSimpleImg<uint8_t, uint8_t>(aSrcFormat, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::RGB24, 3, &RGBA32ToRGB24);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_RGB24::ConvertFrom(Utils_BGRA32* aSrcFormat, const uint8_t* aSrcBuffer,
+                         const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return CvtSimpleImgToSimpleImg<uint8_t, uint8_t>(aSrcFormat, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::RGB24, 3, &BGRA32ToRGB24);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_RGB24::ConvertFrom(Utils_RGB24* aSrcUtils, const uint8_t* aSrcBuffer,
+                         const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return PureCopy(aSrcUtils, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::RGB24);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_RGB24::ConvertFrom(Utils_BGR24* aSrcUtils, const uint8_t* aSrcBuffer,
+                         const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return CvtSimpleImgToSimpleImg<uint8_t, uint8_t>(aSrcUtils, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::RGB24, 3, &BGR24ToRGB24);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_RGB24::ConvertFrom(Utils_Gray8* aSrcFormat, const uint8_t* aSrcBuffer,
+                         const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return nullptr;
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_RGB24::ConvertFrom(Utils_YUV444P* aSrcUtils, const uint8_t* aSrcBuffer,
+                         const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return CvtYUVImgToSimpleImg(aSrcUtils, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::RGB24, 3, &YUV444PToRGB24);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_RGB24::ConvertFrom(Utils_YUV422P* aSrcUtils, const uint8_t* aSrcBuffer,
+                         const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return CvtYUVImgToSimpleImg(aSrcUtils, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::RGB24, 3, &YUV422PToRGB24);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_RGB24::ConvertFrom(Utils_YUV420P* aSrcUtils, const uint8_t* aSrcBuffer,
+                         const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return CvtYUVImgToSimpleImg(aSrcUtils, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::RGB24, 3, &YUV420PToRGB24);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_RGB24::ConvertFrom(Utils_YUV420SP_NV12* aSrcUtils, const uint8_t* aSrcBuffer,
+                         const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return CvtNVImgToSimpleImg(aSrcUtils, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::RGB24, 3, &NV12ToRGB24);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_RGB24::ConvertFrom(Utils_YUV420SP_NV21* aSrcUtils, const uint8_t* aSrcBuffer,
+                         const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return CvtNVImgToSimpleImg(aSrcUtils, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::RGB24, 3, &NV21ToRGB24);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_RGB24::ConvertFrom(Utils_HSV* aSrcFormat, const uint8_t* aSrcBuffer,
+                         const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return CvtSimpleImgToSimpleImg<float, uint8_t>(aSrcFormat, (const float*)aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::RGB24, 3, &HSVToRGB24);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_RGB24::ConvertFrom(Utils_Lab* aSrcFormat, const uint8_t* aSrcBuffer,
+                         const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return CvtSimpleImgToSimpleImg<float, uint8_t>(aSrcFormat, (const float*)aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::RGB24, 3, &LabToRGB24);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_RGB24::ConvertFrom(Utils_Depth* aSrcFormat, const uint8_t* aSrcBuffer,
+                         const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return nullptr;
+}
+
+bool
+Utils_RGB24::CanConvertFrom(ImageBitmapFormat aSrcFormat)
+{
+  if (aSrcFormat == ImageBitmapFormat::GRAY8 ||
+      aSrcFormat == ImageBitmapFormat::DEPTH) {
+    return false;
+  }
+
+  return true;
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_RGB24::CreateDefaultLayout(uint32_t aWidth, uint32_t aHeight, uint32_t aStride)
+{
+  return CreateDefaultLayoutForSimpleImage(aWidth, aHeight, aStride, mChannels, mBytesPerPixelValue, mDataType);
+}
+
+/*
+ * Utils_BGR24.
+ */
+/* static */Utils_BGR24&
+Utils_BGR24::GetInstance()
+{
+  static Utils_BGR24 instance;
+  return instance;
+}
+
+Utils_BGR24::Utils_BGR24()
+: Utils(3, ChannelPixelLayoutDataType::Uint8)
+{
+}
+
+uint32_t
+Utils_BGR24::NeededBufferSize(uint32_t aWidth, uint32_t aHeight)
+{
+  return aWidth * aHeight * (uint32_t)mChannels * mBytesPerPixelValue;
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_BGR24::ConvertTo(Utils* aDstFormat, const uint8_t* aSrcBuffer,
+                       const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return aDstFormat->ConvertFrom(this, aSrcBuffer, aSrcLayout, aDstBuffer);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_BGR24::ConvertFrom(Utils_RGBA32* aSrcFormat, const uint8_t* aSrcBuffer,
+                         const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return CvtSimpleImgToSimpleImg<uint8_t, uint8_t>(aSrcFormat, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::BGR24, 3, &RGBA32ToBGR24);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_BGR24::ConvertFrom(Utils_BGRA32* aSrcFormat, const uint8_t* aSrcBuffer,
+                         const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return CvtSimpleImgToSimpleImg<uint8_t, uint8_t>(aSrcFormat, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::BGR24, 3, &BGRA32ToBGR24);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_BGR24::ConvertFrom(Utils_RGB24* aSrcUtils, const uint8_t* aSrcBuffer,
+                         const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return CvtSimpleImgToSimpleImg<uint8_t, uint8_t>(aSrcUtils, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::BGR24, 3, &RGB24ToBGR24);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_BGR24::ConvertFrom(Utils_BGR24* aSrcUtils, const uint8_t* aSrcBuffer,
+                         const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return PureCopy(aSrcUtils, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::BGR24);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_BGR24::ConvertFrom(Utils_Gray8* aSrcFormat, const uint8_t* aSrcBuffer,
+                         const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return nullptr;
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_BGR24::ConvertFrom(Utils_YUV444P* aSrcUtils, const uint8_t* aSrcBuffer,
+                         const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return CvtYUVImgToSimpleImg(aSrcUtils, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::BGR24, 3, &YUV444PToBGR24);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_BGR24::ConvertFrom(Utils_YUV422P* aSrcUtils, const uint8_t* aSrcBuffer,
+                         const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return CvtYUVImgToSimpleImg(aSrcUtils, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::BGR24, 3, &YUV422PToBGR24);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_BGR24::ConvertFrom(Utils_YUV420P* aSrcUtils, const uint8_t* aSrcBuffer,
+                         const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return CvtYUVImgToSimpleImg(aSrcUtils, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::BGR24, 3, &YUV420PToBGR24);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_BGR24::ConvertFrom(Utils_YUV420SP_NV12* aSrcUtils, const uint8_t* aSrcBuffer,
+                         const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return CvtNVImgToSimpleImg(aSrcUtils, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::BGR24, 3, &NV12ToBGR24);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_BGR24::ConvertFrom(Utils_YUV420SP_NV21* aSrcUtils, const uint8_t* aSrcBuffer,
+                         const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return CvtNVImgToSimpleImg(aSrcUtils, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::BGR24, 3, &NV21ToBGR24);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_BGR24::ConvertFrom(Utils_HSV* aSrcFormat, const uint8_t* aSrcBuffer,
+                         const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return CvtSimpleImgToSimpleImg<float, uint8_t>(aSrcFormat, (const float*)aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::BGR24, 3, &HSVToBGR24);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_BGR24::ConvertFrom(Utils_Lab* aSrcFormat, const uint8_t* aSrcBuffer,
+                         const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return CvtSimpleImgToSimpleImg<float, uint8_t>(aSrcFormat, (const float*)aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::BGR24, 3, &LabToBGR24);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_BGR24::ConvertFrom(Utils_Depth* aSrcFormat, const uint8_t* aSrcBuffer,
+                         const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return nullptr;
+}
+
+bool
+Utils_BGR24::CanConvertFrom(ImageBitmapFormat aSrcFormat)
+{
+  if (aSrcFormat == ImageBitmapFormat::GRAY8 ||
+      aSrcFormat == ImageBitmapFormat::DEPTH) {
+    return false;
+  }
+
+  return true;
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_BGR24::CreateDefaultLayout(uint32_t aWidth, uint32_t aHeight, uint32_t aStride)
+{
+  return CreateDefaultLayoutForSimpleImage(aWidth, aHeight, aStride, mChannels, mBytesPerPixelValue, mDataType);
+}
+
+/*
+ * Utils_Gray8.
+ */
+/* static */Utils_Gray8&
+Utils_Gray8::GetInstance()
+{
+  static Utils_Gray8 instance;
+  return instance;
+}
+
+Utils_Gray8::Utils_Gray8()
+: Utils(1, ChannelPixelLayoutDataType::Uint8)
+{
+}
+
+uint32_t
+Utils_Gray8::NeededBufferSize(uint32_t aWidth, uint32_t aHeight)
+{
+  return aWidth * aHeight * (uint32_t)mChannels * mBytesPerPixelValue;
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_Gray8::ConvertTo(Utils* aDstFormat, const uint8_t* aSrcBuffer,
+                       const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return aDstFormat->ConvertFrom(this, aSrcBuffer, aSrcLayout, aDstBuffer);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_Gray8::ConvertFrom(Utils_RGBA32* aSrcFormat, const uint8_t* aSrcBuffer,
+                         const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return CvtSimpleImgToSimpleImg<uint8_t, uint8_t>(aSrcFormat, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::GRAY8, 1, &RGBA32ToGray8);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_Gray8::ConvertFrom(Utils_BGRA32* aSrcFormat, const uint8_t* aSrcBuffer,
+                         const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return CvtSimpleImgToSimpleImg<uint8_t, uint8_t>(aSrcFormat, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::GRAY8, 1, &BGRA32ToGray8);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_Gray8::ConvertFrom(Utils_RGB24* aSrcUtils, const uint8_t* aSrcBuffer,
+                         const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return CvtSimpleImgToSimpleImg<uint8_t, uint8_t>(aSrcUtils, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::GRAY8, 1, &RGB24ToGray8);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_Gray8::ConvertFrom(Utils_BGR24* aSrcUtils, const uint8_t* aSrcBuffer,
+                         const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return CvtSimpleImgToSimpleImg<uint8_t, uint8_t>(aSrcUtils, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::GRAY8, 1, &BGR24ToGray8);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_Gray8::ConvertFrom(Utils_Gray8* aSrcUtils, const uint8_t* aSrcBuffer,
+                         const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return PureCopy(aSrcUtils, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::GRAY8);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_Gray8::ConvertFrom(Utils_YUV444P* aSrcUtils, const uint8_t* aSrcBuffer,
+                         const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return CvtYUVImgToSimpleImg(aSrcUtils, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::GRAY8, 1, &YUV444PToGray8);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_Gray8::ConvertFrom(Utils_YUV422P* aSrcUtils, const uint8_t* aSrcBuffer,
+                         const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return CvtYUVImgToSimpleImg(aSrcUtils, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::GRAY8, 1, &YUV422PToGray8);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_Gray8::ConvertFrom(Utils_YUV420P* aSrcUtils, const uint8_t* aSrcBuffer,
+                         const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return CvtYUVImgToSimpleImg(aSrcUtils, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::GRAY8, 1, &YUV420PToGray8);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_Gray8::ConvertFrom(Utils_YUV420SP_NV12* aSrcUtils, const uint8_t* aSrcBuffer,
+                         const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return CvtNVImgToSimpleImg(aSrcUtils, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::GRAY8, 1, &NV12ToGray8);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_Gray8::ConvertFrom(Utils_YUV420SP_NV21* aSrcUtils, const uint8_t* aSrcBuffer,
+                         const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return CvtNVImgToSimpleImg(aSrcUtils, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::GRAY8, 1, &NV21ToGray8);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_Gray8::ConvertFrom(Utils_HSV* aSrcUtils, const uint8_t* aSrcBuffer,
+                         const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return TwoPassConversion(aSrcUtils, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::RGB24, this);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_Gray8::ConvertFrom(Utils_Lab* aSrcUtils, const uint8_t* aSrcBuffer,
+                         const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return TwoPassConversion(aSrcUtils, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::RGB24, this);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_Gray8::ConvertFrom(Utils_Depth* aSrcUtils, const uint8_t* aSrcBuffer,
+                         const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return nullptr;
+}
+
+bool
+Utils_Gray8::CanConvertFrom(ImageBitmapFormat aSrcFormat)
+{
+  if (aSrcFormat == ImageBitmapFormat::DEPTH) {
+    return false;
+  }
+
+  return true;
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_Gray8::CreateDefaultLayout(uint32_t aWidth, uint32_t aHeight, uint32_t aStride)
+{
+  return CreateDefaultLayoutForSimpleImage(aWidth, aHeight, aStride, mChannels, mBytesPerPixelValue, mDataType);
+}
+
+/*
+ * class Utils_YUV444P.
+ */
+/* static */Utils_YUV444P&
+Utils_YUV444P::GetInstance()
+{
+  static Utils_YUV444P instance;
+  return instance;
+}
+
+Utils_YUV444P::Utils_YUV444P()
+: Utils(3, ChannelPixelLayoutDataType::Uint8)
+{
+}
+
+uint32_t
+Utils_YUV444P::NeededBufferSize(uint32_t aWidth, uint32_t aHeight)
+{
+  return aWidth * aHeight * (uint32_t)mChannels * mBytesPerPixelValue;
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_YUV444P::ConvertTo(Utils* aDstFormat, const uint8_t* aSrcBuffer,
+                         const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return aDstFormat->ConvertFrom(this, aSrcBuffer, aSrcLayout, aDstBuffer);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_YUV444P::ConvertFrom(Utils_RGBA32* aSrcUtils, const uint8_t* aSrcBuffer,
+                           const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return CvtSimpleImgToYUVImg(aSrcUtils, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::YUV444P, &RGBA32ToYUV444P);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_YUV444P::ConvertFrom(Utils_BGRA32* aSrcUtils, const uint8_t* aSrcBuffer,
+                           const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return CvtSimpleImgToYUVImg(aSrcUtils, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::YUV444P, &libyuv::ARGBToI444);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_YUV444P::ConvertFrom(Utils_RGB24* aSrcUtils, const uint8_t* aSrcBuffer,
+                           const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return CvtSimpleImgToYUVImg(aSrcUtils, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::YUV444P, &RGB24ToYUV444P);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_YUV444P::ConvertFrom(Utils_BGR24* aSrcUtils, const uint8_t* aSrcBuffer,
+                           const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return CvtSimpleImgToYUVImg(aSrcUtils, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::YUV444P, &BGR24ToYUV444P);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_YUV444P::ConvertFrom(Utils_Gray8* aSrcFormat, const uint8_t* aSrcBuffer,
+                           const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return nullptr;
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_YUV444P::ConvertFrom(Utils_YUV444P* aSrcUtils, const uint8_t* aSrcBuffer,
+                           const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return PureCopy(aSrcUtils, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::YUV444P);
+}
+
+// TODO: optimize me.
+UniquePtr<ImagePixelLayout>
+Utils_YUV444P::ConvertFrom(Utils_YUV422P* aSrcUtils, const uint8_t* aSrcBuffer,
+                           const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return TwoPassConversion(aSrcUtils, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::YUV420P, this);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_YUV444P::ConvertFrom(Utils_YUV420P*, const uint8_t* aSrcBuffer,
+                           const ImagePixelLayout* aSrcLayout, 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.");
+
+  UniquePtr<ImagePixelLayout> layout =
+    CreateDefaultLayout((*aSrcLayout)[0].mWidth,
+                        (*aSrcLayout)[0].mHeight,
+                        (*aSrcLayout)[0].mWidth);
+
+  MOZ_ASSERT(layout, "Cannot create a ImagePixelLayout of YUV444P");
+
+  const nsTArray<ChannelPixelLayout>& channels = *layout;
+
+  const nsTArray<ChannelPixelLayout>& srcChannels = *aSrcLayout;
+
+  int rv = I420ToI444(aSrcBuffer + srcChannels[0].mOffset, srcChannels[0].mStride,
+                      aSrcBuffer + srcChannels[1].mOffset, srcChannels[1].mStride,
+                      aSrcBuffer + srcChannels[2].mOffset, srcChannels[2].mStride,
+                      aDstBuffer + channels[0].mOffset, channels[0].mStride,
+                      aDstBuffer + channels[1].mOffset, channels[1].mStride,
+                      aDstBuffer + channels[2].mOffset, channels[2].mStride,
+                      channels[0].mWidth, channels[0].mHeight);
+
+  if (NS_WARN_IF(rv != 0)) {
+    return nullptr;
+  }
+
+  return layout;
+}
+
+// TODO: optimize me.
+UniquePtr<ImagePixelLayout>
+Utils_YUV444P::ConvertFrom(Utils_YUV420SP_NV12* aSrcUtils, const uint8_t* aSrcBuffer,
+                           const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return TwoPassConversion(aSrcUtils, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::YUV420P, this);
+}
+
+// TODO: optimize me.
+UniquePtr<ImagePixelLayout>
+Utils_YUV444P::ConvertFrom(Utils_YUV420SP_NV21* aSrcUtils, const uint8_t* aSrcBuffer,
+                           const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return TwoPassConversion(aSrcUtils, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::YUV420P, this);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_YUV444P::ConvertFrom(Utils_HSV* aSrcUtils, const uint8_t* aSrcBuffer,
+                           const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return TwoPassConversion(aSrcUtils, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::RGB24, this);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_YUV444P::ConvertFrom(Utils_Lab* aSrcUtils, const uint8_t* aSrcBuffer,
+                           const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return TwoPassConversion(aSrcUtils, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::RGB24, this);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_YUV444P::ConvertFrom(Utils_Depth* aSrcUtils, const uint8_t* aSrcBuffer,
+                           const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return nullptr;
+}
+
+bool
+Utils_YUV444P::CanConvertFrom(ImageBitmapFormat aSrcFormat)
+{
+  if (aSrcFormat == ImageBitmapFormat::GRAY8 ||
+      aSrcFormat == ImageBitmapFormat::DEPTH) {
+    return false;
+  }
+
+  return true;
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_YUV444P::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;
+  uchannel->mHeight = aHeight;
+  uchannel->mDataType = ChannelPixelLayoutDataType::Uint8;
+  uchannel->mStride = aStride;
+  uchannel->mSkip   = 0; // aUSkip;
+
+  vchannel->mOffset = uchannel->mOffset + uchannel->mStride * uchannel->mHeight;
+  vchannel->mWidth  = aWidth;
+  vchannel->mHeight = aHeight;
+  vchannel->mDataType = ChannelPixelLayoutDataType::Uint8;
+  vchannel->mStride = aStride;
+  vchannel->mSkip   = 0; // aVSkip;
+
+  return layout;
+}
+
+/*
+ * class Utils_YUV422P.
+ */
+/* static */Utils_YUV422P&
+Utils_YUV422P::GetInstance()
+{
+  static Utils_YUV422P instance;
+  return instance;
+}
+
+Utils_YUV422P::Utils_YUV422P()
+: Utils(3, ChannelPixelLayoutDataType::Uint8)
+{
+}
+
+uint32_t
+Utils_YUV422P::NeededBufferSize(uint32_t aWidth, uint32_t aHeight)
+{
+  return aWidth * aHeight * mBytesPerPixelValue +
+         2 * ((aWidth + 1) / 2) * aHeight * mBytesPerPixelValue;
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_YUV422P::ConvertTo(Utils* aDstFormat, const uint8_t* aSrcBuffer,
+                         const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return aDstFormat->ConvertFrom(this, aSrcBuffer, aSrcLayout, aDstBuffer);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_YUV422P::ConvertFrom(Utils_RGBA32* aSrcUtils, const uint8_t* aSrcBuffer,
+                           const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return CvtSimpleImgToYUVImg(aSrcUtils, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::YUV422P, &RGBA32ToYUV422P);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_YUV422P::ConvertFrom(Utils_BGRA32* aSrcUtils, const uint8_t* aSrcBuffer,
+                           const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return CvtSimpleImgToYUVImg(aSrcUtils, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::YUV422P, &libyuv::ARGBToI422);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_YUV422P::ConvertFrom(Utils_RGB24* aSrcUtils, const uint8_t* aSrcBuffer,
+                           const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return CvtSimpleImgToYUVImg(aSrcUtils, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::YUV422P, &RGB24ToYUV422P);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_YUV422P::ConvertFrom(Utils_BGR24* aSrcUtils, const uint8_t* aSrcBuffer,
+                           const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return CvtSimpleImgToYUVImg(aSrcUtils, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::YUV422P, &BGR24ToYUV422P);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_YUV422P::ConvertFrom(Utils_Gray8* aSrcFormat, const uint8_t* aSrcBuffer,
+                           const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return nullptr;
+}
+
+// TODO: optimize me.
+UniquePtr<ImagePixelLayout>
+Utils_YUV422P::ConvertFrom(Utils_YUV444P* aSrcUtils, const uint8_t* aSrcBuffer,
+                           const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return TwoPassConversion(aSrcUtils, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::YUV420P, this);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_YUV422P::ConvertFrom(Utils_YUV422P* aSrcUtils, const uint8_t* aSrcBuffer,
+                           const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return PureCopy(aSrcUtils, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::YUV422P);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_YUV422P::ConvertFrom(Utils_YUV420P*, const uint8_t* aSrcBuffer,
+                           const ImagePixelLayout* aSrcLayout, 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.");
+
+  UniquePtr<ImagePixelLayout> layout =
+    CreateDefaultLayout((*aSrcLayout)[0].mWidth,
+                        (*aSrcLayout)[0].mHeight,
+                        (*aSrcLayout)[0].mWidth);
+
+  MOZ_ASSERT(layout, "Cannot create a ImagePixelLayout of YUV422P");
+
+  const nsTArray<ChannelPixelLayout>& channels = *layout;
+
+  const nsTArray<ChannelPixelLayout>& srcChannels = *aSrcLayout;
+
+  libyuv::I420ToI422(aSrcBuffer + srcChannels[0].mOffset, srcChannels[0].mStride,
+                     aSrcBuffer + srcChannels[1].mOffset, srcChannels[1].mStride,
+                     aSrcBuffer + srcChannels[2].mOffset, srcChannels[2].mStride,
+                     aDstBuffer + channels[0].mOffset, channels[0].mStride,
+                     aDstBuffer + channels[1].mOffset, channels[1].mStride,
+                     aDstBuffer + channels[2].mOffset, channels[2].mStride,
+                     channels[0].mWidth, channels[0].mHeight);
+
+  return layout;
+}
+
+// TODO: optimize me.
+UniquePtr<ImagePixelLayout>
+Utils_YUV422P::ConvertFrom(Utils_YUV420SP_NV12* aSrcUtils, const uint8_t* aSrcBuffer,
+                           const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return TwoPassConversion(aSrcUtils, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::YUV420P, this);
+}
+
+// TODO: optimize me.
+UniquePtr<ImagePixelLayout>
+Utils_YUV422P::ConvertFrom(Utils_YUV420SP_NV21* aSrcUtils, const uint8_t* aSrcBuffer,
+                           const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return TwoPassConversion(aSrcUtils, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::YUV420P, this);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_YUV422P::ConvertFrom(Utils_HSV* aSrcUtils, const uint8_t* aSrcBuffer,
+                           const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return TwoPassConversion(aSrcUtils, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::RGB24, this);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_YUV422P::ConvertFrom(Utils_Lab* aSrcUtils, const uint8_t* aSrcBuffer,
+                           const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return TwoPassConversion(aSrcUtils, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::RGB24, this);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_YUV422P::ConvertFrom(Utils_Depth* aSrcUtils, const uint8_t* aSrcBuffer,
+                           const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return nullptr;
+}
+
+bool
+Utils_YUV422P::CanConvertFrom(ImageBitmapFormat aSrcFormat)
+{
+  if (aSrcFormat == ImageBitmapFormat::GRAY8 ||
+      aSrcFormat == ImageBitmapFormat::DEPTH) {
+    return false;
+  }
+
+  return true;
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_YUV422P::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 + 1) / 2;
+  uchannel->mHeight = aHeight;
+  uchannel->mDataType = ChannelPixelLayoutDataType::Uint8;
+  uchannel->mStride = (aStride + 1) / 2;
+  uchannel->mSkip   = 0; // aUSkip;
+
+  vchannel->mOffset = uchannel->mOffset + uchannel->mStride * uchannel->mHeight;
+  vchannel->mWidth  = (aWidth + 1) / 2;
+  vchannel->mHeight = aHeight;
+  vchannel->mDataType = ChannelPixelLayoutDataType::Uint8;
+  vchannel->mStride = (aStride + 1) / 2;
+  vchannel->mSkip   = 0; // aVSkip;
+
+  return layout;
+}
+
+/*
+ * Utils_YUV420P.
+ */
+/* static */Utils_YUV420P&
+Utils_YUV420P::GetInstance()
+{
+  static Utils_YUV420P instance;
+  return instance;
+}
+
+Utils_YUV420P::Utils_YUV420P()
+: Utils(3, ChannelPixelLayoutDataType::Uint8)
+{
+}
+
+uint32_t
+Utils_YUV420P::NeededBufferSize(uint32_t aWidth, uint32_t aHeight)
+{
+  return aWidth * aHeight * mBytesPerPixelValue +
+         2 * ((aWidth + 1) / 2) * ((aHeight + 1) / 2) * mBytesPerPixelValue;
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_YUV420P::ConvertTo(Utils* aDstFormat, const uint8_t* aSrcBuffer,
+                         const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return aDstFormat->ConvertFrom(this, aSrcBuffer, aSrcLayout, aDstBuffer);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_YUV420P::ConvertFrom(Utils_RGBA32* aSrcUtils, const uint8_t* aSrcBuffer,
+                           const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return CvtSimpleImgToYUVImg(aSrcUtils, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::YUV420P, &libyuv::ABGRToI420);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_YUV420P::ConvertFrom(Utils_BGRA32* aSrcUtils, const uint8_t* aSrcBuffer,
+                           const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return CvtSimpleImgToYUVImg(aSrcUtils, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::YUV420P, &libyuv::ARGBToI420);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_YUV420P::ConvertFrom(Utils_RGB24* aSrcUtils, const uint8_t* aSrcBuffer,
+                           const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return CvtSimpleImgToYUVImg(aSrcUtils, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::YUV420P, &RGB24ToYUV420P);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_YUV420P::ConvertFrom(Utils_BGR24* aSrcUtils, const uint8_t* aSrcBuffer,
+                           const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return CvtSimpleImgToYUVImg(aSrcUtils, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::YUV420P, &BGR24ToYUV420P);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_YUV420P::ConvertFrom(Utils_Gray8* aSrcFormat, const uint8_t* aSrcBuffer,
+                           const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return nullptr;
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_YUV420P::ConvertFrom(Utils_YUV444P*, const uint8_t* aSrcBuffer,
+                           const ImagePixelLayout* aSrcLayout, 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.");
+
+  UniquePtr<ImagePixelLayout> layout =
+    CreateDefaultLayout((*aSrcLayout)[0].mWidth,
+                        (*aSrcLayout)[0].mHeight,
+                        (*aSrcLayout)[0].mWidth);
+
+  MOZ_ASSERT(layout, "Cannot create a ImagePixelLayout of YUV420P");
+
+  const nsTArray<ChannelPixelLayout>& channels = *layout;
+
+  const nsTArray<ChannelPixelLayout>& srcChannels = *aSrcLayout;
+
+  libyuv::I444ToI420(aSrcBuffer + srcChannels[0].mOffset, srcChannels[0].mStride,
+                     aSrcBuffer + srcChannels[1].mOffset, srcChannels[1].mStride,
+                     aSrcBuffer + srcChannels[2].mOffset, srcChannels[2].mStride,
+                     aDstBuffer + channels[0].mOffset, channels[0].mStride,
+                     aDstBuffer + channels[1].mOffset, channels[1].mStride,
+                     aDstBuffer + channels[2].mOffset, channels[2].mStride,
+                     channels[0].mWidth, channels[0].mHeight);
+
+  return layout;
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_YUV420P::ConvertFrom(Utils_YUV422P*, const uint8_t* aSrcBuffer,
+                           const ImagePixelLayout* aSrcLayout, 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.");
+
+  UniquePtr<ImagePixelLayout> layout =
+    CreateDefaultLayout((*aSrcLayout)[0].mWidth,
+                        (*aSrcLayout)[0].mHeight,
+                        (*aSrcLayout)[0].mWidth);
+
+  MOZ_ASSERT(layout, "Cannot create a ImagePixelLayout of YUV420P");
+
+  const nsTArray<ChannelPixelLayout>& channels = *layout;
+
+  const nsTArray<ChannelPixelLayout>& srcChannels = *aSrcLayout;
+
+  libyuv::I422ToI420(aSrcBuffer + srcChannels[0].mOffset, srcChannels[0].mStride,
+                     aSrcBuffer + srcChannels[1].mOffset, srcChannels[1].mStride,
+                     aSrcBuffer + srcChannels[2].mOffset, srcChannels[2].mStride,
+                     aDstBuffer + channels[0].mOffset, channels[0].mStride,
+                     aDstBuffer + channels[1].mOffset, channels[1].mStride,
+                     aDstBuffer + channels[2].mOffset, channels[2].mStride,
+                     channels[0].mWidth, channels[0].mHeight);
+
+  return layout;
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_YUV420P::ConvertFrom(Utils_YUV420P* aSrcUtils, const uint8_t* aSrcBuffer,
+                           const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return PureCopy(aSrcUtils, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::YUV420P);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_YUV420P::ConvertFrom(Utils_YUV420SP_NV12*, const uint8_t* aSrcBuffer,
+                           const ImagePixelLayout* aSrcLayout, 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.");
+
+  UniquePtr<ImagePixelLayout> layout =
+    CreateDefaultLayout((*aSrcLayout)[0].mWidth,
+                        (*aSrcLayout)[0].mHeight,
+                        (*aSrcLayout)[0].mWidth);
+
+  MOZ_ASSERT(layout, "Cannot create a ImagePixelLayout of YUV420P");
+
+  const nsTArray<ChannelPixelLayout>& channels = *layout;
+
+  const nsTArray<ChannelPixelLayout>& srcChannels = *aSrcLayout;
+
+  libyuv::NV12ToI420(aSrcBuffer + srcChannels[0].mOffset, srcChannels[0].mStride,
+                     aSrcBuffer + srcChannels[1].mOffset, srcChannels[1].mStride,
+                     aDstBuffer + channels[0].mOffset, channels[0].mStride,
+                     aDstBuffer + channels[1].mOffset, channels[1].mStride,
+                     aDstBuffer + channels[2].mOffset, channels[2].mStride,
+                     channels[0].mWidth, channels[0].mHeight);
+
+  return layout;
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_YUV420P::ConvertFrom(Utils_YUV420SP_NV21*, const uint8_t* aSrcBuffer,
+                           const ImagePixelLayout* aSrcLayout, 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.");
+
+  UniquePtr<ImagePixelLayout> layout =
+    CreateDefaultLayout((*aSrcLayout)[0].mWidth,
+                        (*aSrcLayout)[0].mHeight,
+                        (*aSrcLayout)[0].mWidth);
+
+  MOZ_ASSERT(layout, "Cannot create a ImagePixelLayout of YUV420P");
+
+  const nsTArray<ChannelPixelLayout>& channels = *layout;
+
+  const nsTArray<ChannelPixelLayout>& srcChannels = *aSrcLayout;
+
+  libyuv::NV21ToI420(aSrcBuffer + srcChannels[0].mOffset, srcChannels[0].mStride,
+                     aSrcBuffer + srcChannels[1].mOffset, srcChannels[1].mStride,
+                     aDstBuffer + channels[0].mOffset, channels[0].mStride,
+                     aDstBuffer + channels[1].mOffset, channels[1].mStride,
+                     aDstBuffer + channels[2].mOffset, channels[2].mStride,
+                     channels[0].mWidth, channels[0].mHeight);
+
+  return layout;
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_YUV420P::ConvertFrom(Utils_HSV* aSrcUtils, const uint8_t* aSrcBuffer,
+                           const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return TwoPassConversion(aSrcUtils, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::RGB24, this);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_YUV420P::ConvertFrom(Utils_Lab* aSrcUtils, const uint8_t* aSrcBuffer,
+                           const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return TwoPassConversion(aSrcUtils, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::RGB24, this);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_YUV420P::ConvertFrom(Utils_Depth* aSrcUtils, const uint8_t* aSrcBuffer,
+                           const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return nullptr;
+}
+
+bool
+Utils_YUV420P::CanConvertFrom(ImageBitmapFormat aSrcFormat)
+{
+  if (aSrcFormat == ImageBitmapFormat::GRAY8 ||
+      aSrcFormat == ImageBitmapFormat::DEPTH) {
+    return false;
+  }
+
+  return true;
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_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 + 1) / 2;
+  uchannel->mHeight = (aHeight + 1) / 2;
+  uchannel->mDataType = ChannelPixelLayoutDataType::Uint8;
+  uchannel->mStride = (aStride + 1) / 2;
+  uchannel->mSkip   = 0; // aUSkip;
+
+  vchannel->mOffset = uchannel->mOffset + uchannel->mStride * uchannel->mHeight;
+  vchannel->mWidth  = (aWidth + 1) / 2;
+  vchannel->mHeight = (aHeight + 1) / 2;
+  vchannel->mDataType = ChannelPixelLayoutDataType::Uint8;
+  vchannel->mStride = (aStride + 1) / 2;
+  vchannel->mSkip   = 0; // aVSkip;
+
+  return layout;
+}
+
+/*
+ * class Utils_YUV420SP_NV12.
+ */
+/* static */Utils_YUV420SP_NV12&
+Utils_YUV420SP_NV12::GetInstance()
+{
+  static Utils_YUV420SP_NV12 instance;
+  return instance;
+}
+
+Utils_YUV420SP_NV12::Utils_YUV420SP_NV12()
+: Utils(3, ChannelPixelLayoutDataType::Uint8)
+{
+}
+
+uint32_t
+Utils_YUV420SP_NV12::NeededBufferSize(uint32_t aWidth, uint32_t aHeight)
+{
+  return aWidth * aHeight * mBytesPerPixelValue +
+         2 * ((aWidth + 1) / 2) * ((aHeight + 1) / 2) * mBytesPerPixelValue;
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_YUV420SP_NV12::ConvertTo(Utils* aDstFormat, const uint8_t* aSrcBuffer,
+                               const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return aDstFormat->ConvertFrom(this, aSrcBuffer, aSrcLayout, aDstBuffer);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_YUV420SP_NV12::ConvertFrom(Utils_RGBA32* aSrcUtils, const uint8_t* aSrcBuffer,
+                                 const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return CvtSimpleImgToNVImg(aSrcUtils, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::YUV420SP_NV12, &RGBA32ToNV12);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_YUV420SP_NV12::ConvertFrom(Utils_BGRA32* aSrcUtils, const uint8_t* aSrcBuffer,
+                                 const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return CvtSimpleImgToNVImg(aSrcUtils, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::YUV420SP_NV12, &libyuv::ARGBToNV12);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_YUV420SP_NV12::ConvertFrom(Utils_RGB24* aSrcUtils, const uint8_t* aSrcBuffer,
+                                 const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return CvtSimpleImgToNVImg(aSrcUtils, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::YUV420SP_NV12, &RGB24ToNV12);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_YUV420SP_NV12::ConvertFrom(Utils_BGR24* aSrcUtils, const uint8_t* aSrcBuffer,
+                                 const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return CvtSimpleImgToNVImg(aSrcUtils, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::YUV420SP_NV12, &BGR24ToNV12);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_YUV420SP_NV12::ConvertFrom(Utils_Gray8* aSrcFormat, const uint8_t* aSrcBuffer,
+                                 const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return nullptr;
+}
+
+// TODO: optimize me.
+UniquePtr<ImagePixelLayout>
+Utils_YUV420SP_NV12::ConvertFrom(Utils_YUV444P* aSrcUtils, const uint8_t* aSrcBuffer,
+                                 const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return TwoPassConversion(aSrcUtils, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::YUV420P, this);
+}
+
+// TODO: optimize me.
+UniquePtr<ImagePixelLayout>
+Utils_YUV420SP_NV12::ConvertFrom(Utils_YUV422P* aSrcUtils, const uint8_t* aSrcBuffer,
+                                 const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return TwoPassConversion(aSrcUtils, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::YUV420P, this);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_YUV420SP_NV12::ConvertFrom(Utils_YUV420P*, const uint8_t* aSrcBuffer,
+                                 const ImagePixelLayout* aSrcLayout, 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.");
+
+  UniquePtr<ImagePixelLayout> layout =
+    CreateDefaultLayout((*aSrcLayout)[0].mWidth,
+                        (*aSrcLayout)[0].mHeight,
+                        (*aSrcLayout)[0].mWidth);
+
+  MOZ_ASSERT(layout, "Cannot create a ImagePixelLayout of YUV420SP_NV12");
+
+  const nsTArray<ChannelPixelLayout>& channels = *layout;
+
+  const nsTArray<ChannelPixelLayout>& srcChannels = *aSrcLayout;
+
+  libyuv::I420ToNV12(aSrcBuffer + srcChannels[0].mOffset, srcChannels[0].mStride,
+                     aSrcBuffer + srcChannels[1].mOffset, srcChannels[1].mStride,
+                     aSrcBuffer + srcChannels[2].mOffset, srcChannels[2].mStride,
+                     aDstBuffer + channels[0].mOffset, channels[0].mStride,
+                     aDstBuffer + channels[1].mOffset, channels[1].mStride,
+                     channels[0].mWidth, channels[0].mHeight);
+
+  return layout;
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_YUV420SP_NV12::ConvertFrom(Utils_YUV420SP_NV12* aSrcUtils, const uint8_t* aSrcBuffer,
+                                 const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return PureCopy(aSrcUtils, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::YUV420SP_NV12);
+}
+
+// TODO: optimize me.
+UniquePtr<ImagePixelLayout>
+Utils_YUV420SP_NV12::ConvertFrom(Utils_YUV420SP_NV21* aSrcUtils, const uint8_t* aSrcBuffer,
+                                 const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return TwoPassConversion(aSrcUtils, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::YUV420P, this);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_YUV420SP_NV12::ConvertFrom(Utils_HSV* aSrcUtils, const uint8_t* aSrcBuffer,
+                                 const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return TwoPassConversion(aSrcUtils, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::RGB24, this);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_YUV420SP_NV12::ConvertFrom(Utils_Lab* aSrcUtils, const uint8_t* aSrcBuffer,
+                                 const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return TwoPassConversion(aSrcUtils, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::RGB24, this);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_YUV420SP_NV12::ConvertFrom(Utils_Depth* aSrcUtils, const uint8_t* aSrcBuffer,
+                                 const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return nullptr;
+}
+
+bool
+Utils_YUV420SP_NV12::CanConvertFrom(ImageBitmapFormat aSrcFormat)
+{
+  if (aSrcFormat == ImageBitmapFormat::GRAY8 ||
+      aSrcFormat == ImageBitmapFormat::DEPTH) {
+    return false;
+  }
+
+  return true;
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_YUV420SP_NV12::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 + 1) / 2;
+  uchannel->mHeight = (aHeight + 1) / 2;
+  uchannel->mDataType = ChannelPixelLayoutDataType::Uint8;
+  uchannel->mStride = uchannel->mWidth * 2;
+  uchannel->mSkip   = 1; // aUSkip;
+
+  vchannel->mOffset = ychannel->mOffset + ychannel->mStride * ychannel->mHeight + 1;
+  vchannel->mWidth  = (aWidth + 1) / 2;
+  vchannel->mHeight = (aHeight + 1) / 2;
+  vchannel->mDataType = ChannelPixelLayoutDataType::Uint8;
+  vchannel->mStride = vchannel->mWidth * 2;
+  vchannel->mSkip   = 1; // aVSkip;
+
+  return layout;
+}
+
+/*
+ * class Utils_YUV420SP_NV21.
+ */
+/* static */Utils_YUV420SP_NV21&
+Utils_YUV420SP_NV21::GetInstance()
+{
+  static Utils_YUV420SP_NV21 instance;
+  return instance;
+}
+
+Utils_YUV420SP_NV21::Utils_YUV420SP_NV21()
+: Utils(3, ChannelPixelLayoutDataType::Uint8)
+{
+}
+
+uint32_t
+Utils_YUV420SP_NV21::NeededBufferSize(uint32_t aWidth, uint32_t aHeight)
+{
+  return aWidth * aHeight * mBytesPerPixelValue +
+         2 * ((aWidth + 1) / 2) * ((aHeight + 1) / 2) * mBytesPerPixelValue;
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_YUV420SP_NV21::ConvertTo(Utils* aDstFormat, const uint8_t* aSrcBuffer,
+                               const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return aDstFormat->ConvertFrom(this, aSrcBuffer, aSrcLayout, aDstBuffer);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_YUV420SP_NV21::ConvertFrom(Utils_RGBA32* aSrcUtils, const uint8_t* aSrcBuffer,
+                                 const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return CvtSimpleImgToNVImg(aSrcUtils, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::YUV420SP_NV21, &RGBA32ToNV21);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_YUV420SP_NV21::ConvertFrom(Utils_BGRA32* aSrcUtils, const uint8_t* aSrcBuffer,
+                                 const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return CvtSimpleImgToNVImg(aSrcUtils, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::YUV420SP_NV21, &libyuv::ARGBToNV21);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_YUV420SP_NV21::ConvertFrom(Utils_RGB24* aSrcUtils, const uint8_t* aSrcBuffer,
+                                 const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return CvtSimpleImgToNVImg(aSrcUtils, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::YUV420SP_NV21, &RGB24ToNV21);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_YUV420SP_NV21::ConvertFrom(Utils_BGR24* aSrcUtils, const uint8_t* aSrcBuffer,
+                                 const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return CvtSimpleImgToNVImg(aSrcUtils, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::YUV420SP_NV21, &BGR24ToNV21);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_YUV420SP_NV21::ConvertFrom(Utils_Gray8* aSrcFormat, const uint8_t* aSrcBuffer,
+                                 const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return nullptr;
+}
+
+// TODO: optimize me.
+UniquePtr<ImagePixelLayout>
+Utils_YUV420SP_NV21::ConvertFrom(Utils_YUV444P* aSrcUtils, const uint8_t* aSrcBuffer,
+                                 const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return TwoPassConversion(aSrcUtils, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::YUV420P, this);
+}
+
+// TODO: optimize me.
+UniquePtr<ImagePixelLayout>
+Utils_YUV420SP_NV21::ConvertFrom(Utils_YUV422P* aSrcUtils, const uint8_t* aSrcBuffer,
+                                 const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return TwoPassConversion(aSrcUtils, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::YUV420P, this);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_YUV420SP_NV21::ConvertFrom(Utils_YUV420P*, const uint8_t* aSrcBuffer,
+                                 const ImagePixelLayout* aSrcLayout, 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.");
+
+  UniquePtr<ImagePixelLayout> layout =
+    CreateDefaultLayout((*aSrcLayout)[0].mWidth,
+                        (*aSrcLayout)[0].mHeight,
+                        (*aSrcLayout)[0].mWidth);
+
+  MOZ_ASSERT(layout, "Cannot create a ImagePixelLayout of YUV420SP_NV21");
+
+  const nsTArray<ChannelPixelLayout>& channels = *layout;
+
+  const nsTArray<ChannelPixelLayout>& srcChannels = *aSrcLayout;
+
+  libyuv::I420ToNV21(aSrcBuffer + srcChannels[0].mOffset, srcChannels[0].mStride,
+                     aSrcBuffer + srcChannels[1].mOffset, srcChannels[1].mStride,
+                     aSrcBuffer + srcChannels[2].mOffset, srcChannels[2].mStride,
+                     aDstBuffer + channels[0].mOffset, channels[0].mStride,
+                     aDstBuffer + channels[1].mOffset, channels[1].mStride,
+                     channels[0].mWidth, channels[0].mHeight);
+
+  return layout;
+}
+
+// TODO: optimize me.
+UniquePtr<ImagePixelLayout>
+Utils_YUV420SP_NV21::ConvertFrom(Utils_YUV420SP_NV12* aSrcUtils, const uint8_t* aSrcBuffer,
+                                 const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return TwoPassConversion(aSrcUtils, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::YUV420P, this);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_YUV420SP_NV21::ConvertFrom(Utils_YUV420SP_NV21* aSrcUtils, const uint8_t* aSrcBuffer,
+                                 const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return PureCopy(aSrcUtils, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::YUV420SP_NV21);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_YUV420SP_NV21::ConvertFrom(Utils_HSV* aSrcUtils, const uint8_t* aSrcBuffer,
+                                 const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return TwoPassConversion(aSrcUtils, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::RGB24, this);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_YUV420SP_NV21::ConvertFrom(Utils_Lab* aSrcUtils, const uint8_t* aSrcBuffer,
+                                 const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return TwoPassConversion(aSrcUtils, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::RGB24, this);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_YUV420SP_NV21::ConvertFrom(Utils_Depth* aSrcUtils, const uint8_t* aSrcBuffer,
+                                 const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return nullptr;
+}
+
+bool
+Utils_YUV420SP_NV21::CanConvertFrom(ImageBitmapFormat aSrcFormat)
+{
+  if (aSrcFormat == ImageBitmapFormat::GRAY8 ||
+      aSrcFormat == ImageBitmapFormat::DEPTH) {
+    return false;
+  }
+
+  return true;
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_YUV420SP_NV21::CreateDefaultLayout(uint32_t aWidth, uint32_t aHeight, uint32_t aStride)
+{
+  UniquePtr<ImagePixelLayout> layout(new ImagePixelLayout(mChannels));
+
+  // set mChannels
+  ChannelPixelLayout* ychannel = layout->AppendElement();
+  ChannelPixelLayout* vchannel = layout->AppendElement(); // v is the 2nd channel.
+  ChannelPixelLayout* uchannel = layout->AppendElement(); // u is the 3rd channel.
+  ychannel->mOffset = 0;
+  ychannel->mWidth  = aWidth;
+  ychannel->mHeight = aHeight;
+  ychannel->mDataType = ChannelPixelLayoutDataType::Uint8;
+  ychannel->mStride = aStride;
+  ychannel->mSkip   = 0; // aYSkip;
+
+  vchannel->mOffset = ychannel->mOffset + ychannel->mStride * ychannel->mHeight;
+  vchannel->mWidth  = (aWidth + 1) / 2;
+  vchannel->mHeight = (aHeight + 1) / 2;
+  vchannel->mDataType = ChannelPixelLayoutDataType::Uint8;
+  vchannel->mStride = vchannel->mWidth * 2;
+  vchannel->mSkip   = 1; // aVSkip;
+
+  uchannel->mOffset = ychannel->mOffset + ychannel->mStride * ychannel->mHeight + 1;
+  uchannel->mWidth  = (aWidth + 1) / 2;
+  uchannel->mHeight = (aHeight + 1) / 2;
+  uchannel->mDataType = ChannelPixelLayoutDataType::Uint8;
+  uchannel->mStride = uchannel->mWidth * 2;
+  uchannel->mSkip   = 1; // aUSkip;
+
+  return layout;
+}
+
+/*
+ * Utils_HSV.
+ */
+/* static */Utils_HSV&
+Utils_HSV::GetInstance()
+{
+  static Utils_HSV instance;
+  return instance;
+}
+
+Utils_HSV::Utils_HSV()
+: Utils(3, ChannelPixelLayoutDataType::Float32)
+{
+}
+
+uint32_t
+Utils_HSV::NeededBufferSize(uint32_t aWidth, uint32_t aHeight)
+{
+  return aWidth * aHeight * (uint32_t)mChannels * mBytesPerPixelValue;
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_HSV::ConvertTo(Utils* aDstFormat, const uint8_t* aSrcBuffer,
+                     const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return aDstFormat->ConvertFrom(this, aSrcBuffer, aSrcLayout, aDstBuffer);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_HSV::ConvertFrom(Utils_RGBA32* aSrcFormat, const uint8_t* aSrcBuffer,
+                       const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return CvtSimpleImgToSimpleImg<uint8_t, float>(aSrcFormat, aSrcBuffer, aSrcLayout, (float*)aDstBuffer, ImageBitmapFormat::HSV, 3, &RGBA32ToHSV);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_HSV::ConvertFrom(Utils_BGRA32* aSrcFormat, const uint8_t* aSrcBuffer,
+                       const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return CvtSimpleImgToSimpleImg<uint8_t, float>(aSrcFormat, aSrcBuffer, aSrcLayout, (float*)aDstBuffer, ImageBitmapFormat::HSV, 3, &BGRA32ToHSV);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_HSV::ConvertFrom(Utils_RGB24* aSrcUtils, const uint8_t* aSrcBuffer,
+                       const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return CvtSimpleImgToSimpleImg<uint8_t, float>(aSrcUtils, aSrcBuffer, aSrcLayout, (float*)aDstBuffer, ImageBitmapFormat::HSV, 3, &RGB24ToHSV);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_HSV::ConvertFrom(Utils_BGR24* aSrcUtils, const uint8_t* aSrcBuffer,
+                       const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return CvtSimpleImgToSimpleImg<uint8_t, float>(aSrcUtils, aSrcBuffer, aSrcLayout, (float*)aDstBuffer, ImageBitmapFormat::HSV, 3, &BGR24ToHSV);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_HSV::ConvertFrom(Utils_Gray8* aSrcFormat, const uint8_t* aSrcBuffer,
+                       const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return nullptr;
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_HSV::ConvertFrom(Utils_YUV444P* aSrcUtils, const uint8_t* aSrcBuffer,
+                       const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return TwoPassConversion(aSrcUtils, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::RGB24, this);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_HSV::ConvertFrom(Utils_YUV422P* aSrcUtils, const uint8_t* aSrcBuffer,
+                       const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return TwoPassConversion(aSrcUtils, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::RGB24, this);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_HSV::ConvertFrom(Utils_YUV420P* aSrcUtils, const uint8_t* aSrcBuffer,
+                       const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return TwoPassConversion(aSrcUtils, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::RGB24, this);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_HSV::ConvertFrom(Utils_YUV420SP_NV12* aSrcUtils, const uint8_t* aSrcBuffer,
+                       const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return TwoPassConversion(aSrcUtils, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::RGB24, this);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_HSV::ConvertFrom(Utils_YUV420SP_NV21* aSrcUtils, const uint8_t* aSrcBuffer,
+                       const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return TwoPassConversion(aSrcUtils, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::RGB24, this);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_HSV::ConvertFrom(Utils_HSV* aSrcUtils, const uint8_t* aSrcBuffer,
+                       const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return PureCopy(aSrcUtils, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::HSV);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_HSV::ConvertFrom(Utils_Lab* aSrcUtils, const uint8_t* aSrcBuffer,
+                       const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return TwoPassConversion(aSrcUtils, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::RGB24, this);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_HSV::ConvertFrom(Utils_Depth* aSrcUtils, const uint8_t* aSrcBuffer,
+                       const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return nullptr;
+}
+
+bool
+Utils_HSV::CanConvertFrom(ImageBitmapFormat aSrcFormat)
+{
+  if (aSrcFormat == ImageBitmapFormat::GRAY8 ||
+      aSrcFormat == ImageBitmapFormat::DEPTH) {
+    return false;
+  }
+
+  return true;
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_HSV::CreateDefaultLayout(uint32_t aWidth, uint32_t aHeight, uint32_t aStride)
+{
+  return CreateDefaultLayoutForSimpleImage(aWidth, aHeight, aStride, mChannels, mBytesPerPixelValue, mDataType);
+}
+
+/*
+ * Utils_Lab.
+ */
+/* static */Utils_Lab&
+Utils_Lab::GetInstance()
+{
+  static Utils_Lab instance;
+  return instance;
+}
+
+Utils_Lab::Utils_Lab()
+: Utils(3, ChannelPixelLayoutDataType::Float32)
+{
+}
+
+uint32_t
+Utils_Lab::NeededBufferSize(uint32_t aWidth, uint32_t aHeight)
+{
+  return aWidth * aHeight * (uint32_t)mChannels * mBytesPerPixelValue;
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_Lab::ConvertTo(Utils* aDstFormat, const uint8_t* aSrcBuffer,
+                     const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return aDstFormat->ConvertFrom(this, aSrcBuffer, aSrcLayout, aDstBuffer);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_Lab::ConvertFrom(Utils_RGBA32* aSrcUtils, const uint8_t* aSrcBuffer,
+                       const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return CvtSimpleImgToSimpleImg<uint8_t, float>(aSrcUtils, aSrcBuffer, aSrcLayout, (float*)aDstBuffer, ImageBitmapFormat::Lab, 3, &RGBA32ToLab);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_Lab::ConvertFrom(Utils_BGRA32* aSrcUtils, const uint8_t* aSrcBuffer,
+                       const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return CvtSimpleImgToSimpleImg<uint8_t, float>(aSrcUtils, aSrcBuffer, aSrcLayout, (float*)aDstBuffer, ImageBitmapFormat::Lab, 3, &BGRA32ToLab);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_Lab::ConvertFrom(Utils_RGB24* aSrcUtils, const uint8_t* aSrcBuffer,
+                       const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return CvtSimpleImgToSimpleImg<uint8_t, float>(aSrcUtils, aSrcBuffer, aSrcLayout, (float*)aDstBuffer, ImageBitmapFormat::Lab, 3, &RGB24ToLab);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_Lab::ConvertFrom(Utils_BGR24* aSrcUtils, const uint8_t* aSrcBuffer,
+                       const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return CvtSimpleImgToSimpleImg<uint8_t, float>(aSrcUtils, aSrcBuffer, aSrcLayout, (float*)aDstBuffer, ImageBitmapFormat::Lab, 3, &BGR24ToLab);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_Lab::ConvertFrom(Utils_Gray8* aSrcFormat, const uint8_t* aSrcBuffer,
+                       const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return nullptr;
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_Lab::ConvertFrom(Utils_YUV444P* aSrcUtils, const uint8_t* aSrcBuffer,
+                       const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return TwoPassConversion(aSrcUtils, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::RGB24, this);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_Lab::ConvertFrom(Utils_YUV422P* aSrcUtils, const uint8_t* aSrcBuffer,
+                       const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return TwoPassConversion(aSrcUtils, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::RGB24, this);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_Lab::ConvertFrom(Utils_YUV420P* aSrcUtils, const uint8_t* aSrcBuffer,
+                       const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return TwoPassConversion(aSrcUtils, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::RGB24, this);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_Lab::ConvertFrom(Utils_YUV420SP_NV12* aSrcUtils, const uint8_t* aSrcBuffer,
+                       const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return TwoPassConversion(aSrcUtils, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::RGB24, this);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_Lab::ConvertFrom(Utils_YUV420SP_NV21* aSrcUtils, const uint8_t* aSrcBuffer,
+                       const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return TwoPassConversion(aSrcUtils, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::RGB24, this);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_Lab::ConvertFrom(Utils_HSV* aSrcUtils, const uint8_t* aSrcBuffer,
+                       const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return TwoPassConversion(aSrcUtils, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::RGB24, this);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_Lab::ConvertFrom(Utils_Lab* aSrcUtils, const uint8_t* aSrcBuffer,
+                       const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return PureCopy(aSrcUtils, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::Lab);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_Lab::ConvertFrom(Utils_Depth* aSrcUtils, const uint8_t* aSrcBuffer,
+                       const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return nullptr;
+}
+
+bool
+Utils_Lab::CanConvertFrom(ImageBitmapFormat aSrcFormat)
+{
+  if (aSrcFormat == ImageBitmapFormat::GRAY8 ||
+      aSrcFormat == ImageBitmapFormat::DEPTH) {
+    return false;
+  }
+
+  return true;
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_Lab::CreateDefaultLayout(uint32_t aWidth, uint32_t aHeight, uint32_t aStride)
+{
+  return CreateDefaultLayoutForSimpleImage(aWidth, aHeight, aStride, mChannels, mBytesPerPixelValue, mDataType);
+}
+
+/*
+ * Utils_Depth.
+ */
+/* static */Utils_Depth&
+Utils_Depth::GetInstance()
+{
+  static Utils_Depth instance;
+  return instance;
+}
+
+Utils_Depth::Utils_Depth()
+: Utils(1, ChannelPixelLayoutDataType::Uint16)
+{
+}
+
+uint32_t
+Utils_Depth::NeededBufferSize(uint32_t aWidth, uint32_t aHeight)
+{
+  return aWidth * aHeight * (uint32_t)mChannels * mBytesPerPixelValue;
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_Depth::ConvertTo(Utils* aDstFormat, const uint8_t* aSrcBuffer,
+                       const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return aDstFormat->ConvertFrom(this, aSrcBuffer, aSrcLayout, aDstBuffer);
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_Depth::ConvertFrom(Utils_RGBA32* aSrcFormat, const uint8_t* aSrcBuffer,
+                         const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return nullptr;
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_Depth::ConvertFrom(Utils_BGRA32* aSrcFormat, const uint8_t* aSrcBuffer,
+                         const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return nullptr;
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_Depth::ConvertFrom(Utils_RGB24* aSrcUtils, const uint8_t* aSrcBuffer,
+                         const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return nullptr;
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_Depth::ConvertFrom(Utils_BGR24* aSrcUtils, const uint8_t* aSrcBuffer,
+                         const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return nullptr;
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_Depth::ConvertFrom(Utils_Gray8* aSrcFormat, const uint8_t* aSrcBuffer,
+                         const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return nullptr;
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_Depth::ConvertFrom(Utils_YUV444P* aSrcUtils, const uint8_t* aSrcBuffer,
+                         const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return nullptr;
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_Depth::ConvertFrom(Utils_YUV422P* aSrcUtils, const uint8_t* aSrcBuffer,
+                         const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return nullptr;
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_Depth::ConvertFrom(Utils_YUV420P* aSrcUtils, const uint8_t* aSrcBuffer,
+                         const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return nullptr;
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_Depth::ConvertFrom(Utils_YUV420SP_NV12* aSrcUtils, const uint8_t* aSrcBuffer,
+                         const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return nullptr;
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_Depth::ConvertFrom(Utils_YUV420SP_NV21* aSrcUtils, const uint8_t* aSrcBuffer,
+                         const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return nullptr;
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_Depth::ConvertFrom(Utils_HSV* aSrcUtils, const uint8_t* aSrcBuffer,
+                         const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return nullptr;
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_Depth::ConvertFrom(Utils_Lab* aSrcUtils, const uint8_t* aSrcBuffer,
+                         const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return nullptr;
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_Depth::ConvertFrom(Utils_Depth* aSrcUtils, const uint8_t* aSrcBuffer,
+                         const ImagePixelLayout* aSrcLayout, uint8_t* aDstBuffer)
+{
+  return PureCopy(aSrcUtils, aSrcBuffer, aSrcLayout, aDstBuffer, ImageBitmapFormat::DEPTH);
+}
+
+bool
+Utils_Depth::CanConvertFrom(ImageBitmapFormat aSrcFormat)
+{
+  if (aSrcFormat == ImageBitmapFormat::DEPTH ) {
+    return true;
+  }
+
+  return false;
+}
+
+UniquePtr<ImagePixelLayout>
+Utils_Depth::CreateDefaultLayout(uint32_t aWidth, uint32_t aHeight, uint32_t aStride)
+{
+  return CreateDefaultLayoutForSimpleImage(aWidth, aHeight, aStride, mChannels, mBytesPerPixelValue, mDataType);
+}
+
+} // namespace imagebitmapformat
+
+/*
+ * Global functions.
+ */
+
+using namespace mozilla::dom::imagebitmapformat;
+
+UniquePtr<ImagePixelLayout>
+CreateDefaultPixelLayout(ImageBitmapFormat aFormat, uint32_t aWidth,
+                         uint32_t aHeight, uint32_t aStride)
+{
+  UtilsUniquePtr format = Utils::GetUtils(aFormat);
+  MOZ_ASSERT(format, "Cannot get a valid ImageBitmapFormatUtils instance.");
+
+  return format->CreateDefaultLayout(aWidth, aHeight, aStride);
+}
+
+UniquePtr<ImagePixelLayout>
+CreatePixelLayoutFromPlanarYCbCrData(const layers::PlanarYCbCrData* aData)
+{
+  if (!aData) {
+    // something wrong
+    return nullptr;
+  }
+
+  UniquePtr<ImagePixelLayout> layout(new ImagePixelLayout(3));
+
+  ChannelPixelLayout* ychannel = layout->AppendElement();
+  ChannelPixelLayout* uchannel = layout->AppendElement();
+  ChannelPixelLayout* vchannel = layout->AppendElement();
+
+  ychannel->mOffset = 0;
+
+  if (aData->mCrChannel - aData->mCbChannel > 0) {
+    uchannel->mOffset = ychannel->mOffset + (aData->mCbChannel - aData->mYChannel);
+    vchannel->mOffset = uchannel->mOffset + (aData->mCrChannel - aData->mCbChannel);
+  } else {
+    uchannel->mOffset = ychannel->mOffset + (aData->mCrChannel - aData->mYChannel);
+    vchannel->mOffset = uchannel->mOffset + (aData->mCbChannel - aData->mCrChannel);
+  }
+
+  ychannel->mWidth = aData->mYSize.width;
+  ychannel->mHeight = aData->mYSize.height;
+  ychannel->mDataType = ChannelPixelLayoutDataType::Uint8;
+  ychannel->mStride = aData->mYStride;
+  ychannel->mSkip = aData->mYSkip;
+
+  uchannel->mWidth = aData->mCbCrSize.width;
+  uchannel->mHeight = aData->mCbCrSize.height;
+  uchannel->mDataType = ChannelPixelLayoutDataType::Uint8;
+  uchannel->mStride = aData->mCbCrStride;
+  uchannel->mSkip = aData->mCbSkip;
+
+  vchannel->mWidth = aData->mCbCrSize.width;
+  vchannel->mHeight = aData->mCbCrSize.height;
+  vchannel->mDataType = ChannelPixelLayoutDataType::Uint8;
+  vchannel->mStride = aData->mCbCrStride;
+  vchannel->mSkip = aData->mCrSkip;
+
+  return layout;
+}
+
+uint8_t
+GetChannelCountOfImageFormat(ImageBitmapFormat aFormat)
+{
+  UtilsUniquePtr format = Utils::GetUtils(aFormat);
+  MOZ_ASSERT(format, "Cannot get a valid ImageBitmapFormatUtils instance.");
+
+  return format->GetChannelCount();
+}
+
+uint32_t
+CalculateImageBufferSize(ImageBitmapFormat aFormat,
+                         uint32_t aWidth, uint32_t aHeight)
+{
+  UtilsUniquePtr format = Utils::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 = Utils::GetUtils(aSrcFormat);
+  UtilsUniquePtr dstFormat = Utils::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 = Utils::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 layers {
+class Image;
+struct PlanarYCbCrData;
+}
+
+namespace dom {
+
+struct ChannelPixelLayout;
+template<typename> class Sequence;
+
+typedef nsTArray<ChannelPixelLayout> ImagePixelLayout;
+
+/*
+ * This function creates an ImagePixelLayout object which describes the
+ * default layout of the given ImageBitmapFormat with the given width, height
+ * and stride.
+ */
+UniquePtr<ImagePixelLayout>
+CreateDefaultPixelLayout(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>
+CreatePixelLayoutFromPlanarYCbCrData(const layers::PlanarYCbCrData* aData);
+
+/*
+ * 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
+CalculateImageBufferSize(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 CalculateImageBufferSize() 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
new file mode 100644
--- /dev/null
+++ b/dom/canvas/gtest/TestImageBitmapColorUtils.cpp
@@ -0,0 +1,2585 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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 "gtest/gtest.h"
+#include "ImageBitmapColorUtils.h"
+#include "mozilla/dom/ImageBitmapBinding.h"
+#include "libyuv.h"
+
+using namespace mozilla::dom;
+
+template<typename T>
+class Image {
+public:
+  explicit Image(int aChannelCount)
+  : mChannelCount(aChannelCount), mData(nullptr)
+  {
+  }
+
+  virtual ~Image() {
+    if (mData) delete[] mData;
+  }
+
+  int mChannelCount;
+  T* mData;
+};
+
+template<typename T>
+class SimpleImage : public Image<T>
+{
+public:
+  SimpleImage(int aWidth, int aHeight, int aChannelCount)
+  : Image<T>(aChannelCount)
+  , mWidth(aWidth)
+  , mHeight(aHeight)
+  , mStride(aWidth * aChannelCount * sizeof(T))
+  {
+    Image<T>::mData = new T[mHeight * mStride];
+  }
+
+  virtual ~SimpleImage() {}
+
+  T GetPixelValue(int i, int j, int c) const
+  {
+    // The unit of mStride is byte.
+    const uint8_t* data = (const uint8_t*)Image<T>::mData;
+    return *((T*)(data + i * mStride) + j * Image<T>::mChannelCount + c);
+  }
+
+  int mWidth;
+  int mHeight;
+  int mStride;
+};
+
+class RGBA32DataSinglePixel : public SimpleImage<uint8_t> {
+public:
+  RGBA32DataSinglePixel(int r, int g, int b, int a)
+  : SimpleImage(1, 1, 4)
+  {
+    mData[0] = r;
+    mData[1] = g;
+    mData[2] = b;
+    mData[3] = a;
+  }
+};
+
+class RGB24Data : public SimpleImage<uint8_t> {
+public:
+  RGB24Data()
+  : SimpleImage(3, 3, 3)
+  {
+    int i = 0;
+    mData[i + 0] = 0;   mData[i + 1] = 0;   mData[i + 2] = 0;
+    mData[i + 3] = 255; mData[i + 4] = 0;   mData[i + 5] = 0;
+    mData[i + 6] = 0;   mData[i + 7] = 255; mData[i + 8] = 0;
+
+    i = 1 * mStride;
+    mData[i + 0] = 0;   mData[i + 1] = 0;   mData[i + 2] = 255;
+    mData[i + 3] = 255; mData[i + 4] = 255; mData[i + 5] = 0;
+    mData[i + 6] = 0;   mData[i + 7] = 255; mData[i + 8] = 255;
+
+    i = 2 * mStride;
+    mData[i + 0] = 255; mData[i + 1] = 0;   mData[i + 2] = 255;
+    mData[i + 3] = 255; mData[i + 4] = 255; mData[i + 5] = 255;
+    mData[i + 6] = 128; mData[i + 7] = 128; mData[i + 8] = 128;
+  }
+};
+
+class BGR24Data : public SimpleImage<uint8_t> {
+public:
+  BGR24Data()
+  : SimpleImage(3, 3, 3)
+  {
+    int i = 0;
+    mData[i + 2] = 0;   mData[i + 1] = 0;   mData[i + 0] = 0;
+    mData[i + 5] = 255; mData[i + 4] = 0;   mData[i + 3] = 0;
+    mData[i + 8] = 0;   mData[i + 7] = 255; mData[i + 6] = 0;
+
+    i = 1 * mStride;
+    mData[i + 2] = 0;   mData[i + 1] = 0;   mData[i + 0] = 255;
+    mData[i + 5] = 255; mData[i + 4] = 255; mData[i + 3] = 0;
+    mData[i + 8] = 0;   mData[i + 7] = 255; mData[i + 6] = 255;
+
+    i = 2 * mStride;
+    mData[i + 2] = 255; mData[i + 1] = 0;   mData[i + 0] = 255;
+    mData[i + 5] = 255; mData[i + 4] = 255; mData[i + 3] = 255;
+    mData[i + 8] = 128; mData[i + 7] = 128; mData[i + 6] = 128;
+  }
+};
+
+class RGBA32Data : public SimpleImage<uint8_t> {
+public:
+  RGBA32Data()
+  : SimpleImage(3, 3, 4)
+  {
+    int i = 0;
+    mData[i + 0] = 0;   mData[i + 1] = 0;   mData[i + 2] = 0;   mData[i + 3] = 255;
+    mData[i + 4] = 255; mData[i + 5] = 0;   mData[i + 6] = 0;   mData[i + 7] = 255;
+    mData[i + 8] = 0;   mData[i + 9] = 255; mData[i + 10] = 0;  mData[i + 11] = 255;
+
+    i = 1 * mStride;
+    mData[i + 0] = 0;   mData[i + 1] = 0;   mData[i + 2] = 255;  mData[i + 3] = 255;
+    mData[i + 4] = 255; mData[i + 5] = 255; mData[i + 6] = 0;    mData[i + 7] = 255;
+    mData[i + 8] = 0;   mData[i + 9] = 255; mData[i + 10] = 255; mData[i + 11] = 255;
+
+    i = 2 * mStride;
+    mData[i + 0] = 255; mData[i + 1] = 0;   mData[i + 2] = 255;  mData[i + 3] = 255;
+    mData[i + 4] = 255; mData[i + 5] = 255; mData[i + 6] = 255;  mData[i + 7] = 255;
+    mData[i + 8] = 128; mData[i + 9] = 128; mData[i + 10] = 128; mData[i + 11] = 255;
+  }
+};
+
+class BGRA32Data : public SimpleImage<uint8_t> {
+public:
+  BGRA32Data()
+  : SimpleImage(3, 3, 4)
+  {
+    int i = 0;
+    mData[i + 2] = 0;   mData[i + 1] = 0;   mData[i + 0] = 0;   mData[i + 3] = 255;
+    mData[i + 6] = 255; mData[i + 5] = 0;   mData[i + 4] = 0;   mData[i + 7] = 255;
+    mData[i + 10] = 0;  mData[i + 9] = 255; mData[i + 8] = 0;   mData[i + 11] = 255;
+
+    i = 1 * mStride;
+    mData[i + 2] = 0;   mData[i + 1] = 0;   mData[i + 0] = 255;  mData[i + 3] = 255;
+    mData[i + 6] = 255; mData[i + 5] = 255; mData[i + 4] = 0;    mData[i + 7] = 255;
+    mData[i + 10] = 0;  mData[i + 9] = 255; mData[i + 8] = 255;  mData[i + 11] = 255;
+
+    i = 2 * mStride;
+    mData[i + 2] = 255;  mData[i + 1] = 0;   mData[i + 0] = 255; mData[i + 3] = 255;
+    mData[i + 6] = 255;  mData[i + 5] = 255; mData[i + 4] = 255; mData[i + 7] = 255;
+    mData[i + 10] = 128; mData[i + 9] = 128; mData[i + 8] = 128; mData[i + 11] = 255;
+  }
+};
+
+class RGBA32Image : public SimpleImage<uint8_t>
+{
+public:
+  RGBA32Image(int aWidth, int aHeight, int aChannelCount)
+  : SimpleImage(aWidth, aHeight, aChannelCount)
+  {}
+
+  const uint8_t* GetPixel(int x, int y) const {
+    return mData + mStride * y + x * mChannelCount;
+  }
+};
+
+class RGBA32DataFromYUV444PData : public RGBA32Image {
+public:
+  RGBA32DataFromYUV444PData()
+  : RGBA32Image(3, 3, 4)
+  {
+//    libyuv: (16, 128, 128) --> (0, 0, 0, 255)
+//    libyuv: (82, 90, 240) --> (254, 0, 0, 255)
+//    libyuv: (144, 54, 34) --> (0, 253, 1, 255)
+//    libyuv: (41, 240, 110) --> (0, 0, 251, 255)
+//    libyuv: (210, 16, 146) --> (253, 253, 2, 255)
+//    libyuv: (169, 166, 16) --> (0, 253, 252, 255)
+//    libyuv: (107, 202, 222) --> (255, 0, 252, 255)
+//    libyuv: (235, 128, 128) --> (253, 253, 253, 255)
+//    libyuv: (126, 128, 128) --> (127, 127, 127, 255)
+
+    int i = 0;
+    mData[i + 0] = 0;   mData[i + 1] = 0;   mData[i + 2] = 0;    mData[i + 3] = 255;
+    mData[i + 4] = 254; mData[i + 5] = 0;   mData[i + 6] = 0;    mData[i + 7] = 255;
+    mData[i + 8] = 0;   mData[i + 9] = 253; mData[i + 10] = 1;   mData[i + 11] = 255;
+
+    i = 1 * mStride;
+    mData[i + 0] = 0;   mData[i + 1] = 0;   mData[i + 2] = 251;  mData[i + 3] = 255;
+    mData[i + 4] = 253; mData[i + 5] = 253; mData[i + 6] = 2;    mData[i + 7] = 255;
+    mData[i + 8] = 0;   mData[i + 9] = 253; mData[i + 10] = 252; mData[i + 11] = 255;
+
+    i = 2 * mStride;
+    mData[i + 0] = 255; mData[i + 1] = 0;   mData[i + 2] = 252;  mData[i + 3] = 255;
+    mData[i + 4] = 253; mData[i + 5] = 253; mData[i + 6] = 253;  mData[i + 7] = 255;
+    mData[i + 8] = 127; mData[i + 9] = 127; mData[i + 10] = 127; mData[i + 11] = 255;
+  }
+};
+
+class RGBA32DataFromYUV422PData : public RGBA32Image {
+public:
+  RGBA32DataFromYUV422PData()
+  : RGBA32Image(3, 3, 4)
+  {
+//    libyuv: (16, 109, 184) --> (89, 0, 0, 255)
+//    libyuv: (82, 109, 184) --> (165, 38, 38, 255)
+//    libyuv: (144, 54, 34) --> (0, 253, 1, 255)
+//    libyuv: (41, 128, 128) --> (28, 28, 28, 255)
+//    libyuv: (210, 128, 128) --> (224, 224, 224, 255)
+//    libyuv: (169, 166, 16) --> (0, 253, 252, 255)
+//    libyuv: (107, 165, 175) --> (180, 52, 178, 255)
+//    libyuv: (235, 165, 175) --> (255, 200, 255, 255)
+//    libyuv: (126, 128, 128) --> (127, 127, 127, 255)
+
+    int i = 0;
+    mData[i + 0] = 89;  mData[i + 1] = 0;   mData[i + 2] = 0;    mData[i + 3] = 255;
+    mData[i + 4] = 165; mData[i + 5] = 38;  mData[i + 6] = 38;   mData[i + 7] = 255;
+    mData[i + 8] = 0;   mData[i + 9] = 253; mData[i + 10] = 1;   mData[i + 11] = 255;
+
+    i = 1 * mStride;
+    mData[i + 0] = 28;  mData[i + 1] = 28;  mData[i + 2] = 28;   mData[i + 3] = 255;
+    mData[i + 4] = 224; mData[i + 5] = 224; mData[i + 6] = 224;  mData[i + 7] = 255;
+    mData[i + 8] = 0;   mData[i + 9] = 253; mData[i + 10] = 252; mData[i + 11] = 255;
+
+    i = 2 * mStride;
+    mData[i + 0] = 180; mData[i + 1] = 52;  mData[i + 2] = 178;  mData[i + 3] = 255;
+    mData[i + 4] = 255; mData[i + 5] = 200; mData[i + 6] = 255;  mData[i + 7] = 255;
+    mData[i + 8] = 127; mData[i + 9] = 127; mData[i + 10] = 127; mData[i + 11] = 255;
+  }
+};
+
+class RGBA32DataFromYUV420PData : public RGBA32Image {
+public:
+  RGBA32DataFromYUV420PData()
+  : RGBA32Image(3, 3, 4)
+  {
+//    libyuv: (16, 119, 156) --> (44, 0, 0, 255)
+//    libyuv: (82, 119, 156) --> (120, 57, 58, 255)
+//    libyuv: (144, 110, 25) --> (0, 238, 112, 255)
+//    libyuv: (41, 119, 156) --> (73, 9, 11, 255)
+//    libyuv: (210, 119, 156) --> (255, 205, 206, 255)
+//    libyuv: (169, 110, 25) --> (12, 255, 141, 255)
+//    libyuv: (107, 165, 175) --> (180, 52, 178, 255)
+//    libyuv: (235, 165, 175) --> (255, 200, 255, 255)
+//    libyuv: (126, 128, 128) --> (127, 127, 127, 255)
+
+    int i = 0;
+    mData[i + 0] = 44;  mData[i + 1] = 0;   mData[i + 2] = 0;    mData[i + 3] = 255;
+    mData[i + 4] = 120; mData[i + 5] = 57;  mData[i + 6] = 58;   mData[i + 7] = 255;
+    mData[i + 8] = 0;   mData[i + 9] = 238; mData[i + 10] = 112;   mData[i + 11] = 255;
+
+    i = 1 * mStride;
+    mData[i + 0] = 73;  mData[i + 1] = 9;  mData[i + 2] = 11;   mData[i + 3] = 255;
+    mData[i + 4] = 255; mData[i + 5] = 205; mData[i + 6] = 206;  mData[i + 7] = 255;
+    mData[i + 8] = 12;   mData[i + 9] = 255; mData[i + 10] = 141; mData[i + 11] = 255;
+
+    i = 2 * mStride;
+    mData[i + 0] = 180; mData[i + 1] = 52;  mData[i + 2] = 178;  mData[i + 3] = 255;
+    mData[i + 4] = 255; mData[i + 5] = 200; mData[i + 6] = 255;  mData[i + 7] = 255;
+    mData[i + 8] = 127; mData[i + 9] = 127; mData[i + 10] = 127; mData[i + 11] = 255;
+  }
+};
+
+class GrayData : public SimpleImage<uint8_t> {
+public:
+  GrayData()
+  : SimpleImage(3, 3, 1)
+  {
+    int i = 0;
+    mData[i + 0] = 0;   mData[i + 1] = 76;   mData[i + 2] = 149;
+
+    i = 1 * mStride;
+    mData[i + 0] = 29;   mData[i + 1] = 225;   mData[i + 2] = 178;
+
+    i = 2 * mStride;
+    mData[i + 0] = 105; mData[i + 1] = 255;   mData[i + 2] = 127;
+  }
+};
+
+class YUVImage : public Image<uint8_t>
+{
+public:
+  YUVImage(int aYWidth, int aYHeight, int aYStride,
+           int aUWidth, int aUHeight, int aUStride,
+           int aVWidth, int aVHeight, int aVStride)
+  : Image(3)
+  , mYWidth(aYWidth), mYHeight(aYHeight), mYStride(aYStride)
+  , mUWidth(aUWidth), mUHeight(aUHeight), mUStride(aUStride)
+  , mVWidth(aVWidth), mVHeight(aVHeight), mVStride(aVStride)
+  {
+    mData = new uint8_t[mYHeight * mYStride + mUHeight * mUStride + mVHeight * mVStride];
+  }
+
+  uint8_t* YBuffer() const { return mData; }
+  uint8_t* UBuffer() const { return YBuffer() + mYHeight * mYStride; }
+  uint8_t* VBuffer() const { return UBuffer() + mUHeight * mUStride; }
+
+  int mYWidth;
+  int mYHeight;
+  int mYStride;
+  int mUWidth;
+  int mUHeight;
+  int mUStride;
+  int mVWidth;
+  int mVHeight;
+  int mVStride;
+};
+
+class NVImage : public Image<uint8_t>
+{
+public:
+  NVImage(int aYWidth, int aYHeight, int aYStride,
+          int aUVWidth, int aUVHeight, int aUVStride)
+  : Image(3)
+  , mYWidth(aYWidth), mYHeight(aYHeight), mYStride(aYStride)
+  , mUVWidth(aUVWidth), mUVHeight(aUVHeight), mUVStride(aUVStride)
+  {
+    mData = new uint8_t[mYHeight * mYStride + mUVHeight * mUVStride];
+  }
+
+  uint8_t* YBuffer() const { return mData; }
+  uint8_t* UVBuffer() const { return YBuffer() + mYHeight * mYStride; }
+
+  int mYWidth;
+  int mYHeight;
+  int mYStride;
+  int mUVWidth;
+  int mUVHeight;
+  int mUVStride;
+};
+
+class YUV444PData : public YUVImage
+{
+public:
+  YUV444PData()
+  :YUVImage(3, 3, 3, 3, 3, 3, 3, 3, 3)
+  {
+//    libyuv: (0, 0, 0) --> (16, 128, 128)
+//    libyuv: (255, 0, 0) --> (82, 90, 240)
+//    libyuv: (0, 255, 0) --> (144, 54, 34)
+//    libyuv: (0, 0, 255) --> (41, 240, 110)
+//    libyuv: (255, 255, 0) --> (210, 16, 146)
+//    libyuv: (0, 255, 255) --> (169, 166, 16)
+//    libyuv: (255, 0, 255) --> (107, 202, 222)
+//    libyuv: (255, 255, 255) --> (235, 128, 128)
+//    libyuv: (128, 128, 128) --> (126, 128, 128)
+
+    YBuffer()[0] = 16;  YBuffer()[1] = 82;  YBuffer()[2] = 144;
+    YBuffer()[3] = 41;  YBuffer()[4] = 210; YBuffer()[5] = 169;
+    YBuffer()[6] = 107; YBuffer()[7] = 235; YBuffer()[8] = 126;
+
+    UBuffer()[0] = 128; UBuffer()[1] = 90;  UBuffer()[2] = 54;
+    UBuffer()[3] = 240; UBuffer()[4] = 16;  UBuffer()[5] = 166;
+    UBuffer()[6] = 202; UBuffer()[7] = 128; UBuffer()[8] = 128;
+
+    VBuffer()[0] = 128; VBuffer()[1] = 240; VBuffer()[2] = 34;
+    VBuffer()[3] = 110; VBuffer()[4] = 146; VBuffer()[5] = 16;
+    VBuffer()[6] = 222; VBuffer()[7] = 128; VBuffer()[8] = 128;
+  }
+};
+
+class YUV422PData : public YUVImage
+{
+public:
+  YUV422PData()
+  :YUVImage(3, 3, 3, 2, 3, 2, 2, 3, 2)
+  {
+    YBuffer()[0] = 16;  YBuffer()[1] = 82;  YBuffer()[2] = 144;
+    YBuffer()[3] = 41;  YBuffer()[4] = 210; YBuffer()[5] = 169;
+    YBuffer()[6] = 107; YBuffer()[7] = 235; YBuffer()[8] = 126;
+
+    UBuffer()[0] = 109; UBuffer()[1] = 54;
+    UBuffer()[2] = 128; UBuffer()[3] = 166;
+    UBuffer()[4] = 165; UBuffer()[5] = 128;
+
+    VBuffer()[0] = 184; VBuffer()[1] = 34;
+    VBuffer()[2] = 128; VBuffer()[3] = 16;
+    VBuffer()[4] = 175; VBuffer()[5] = 128;
+  }
+};
+
+class YUV420PData : public YUVImage
+{
+public:
+  YUV420PData()
+  :YUVImage(3, 3, 3, 2, 2, 2, 2, 2, 2)
+  {
+    YBuffer()[0] = 16;  YBuffer()[1] = 82;  YBuffer()[2] = 144;
+    YBuffer()[3] = 41;  YBuffer()[4] = 210; YBuffer()[5] = 169;
+    YBuffer()[6] = 107; YBuffer()[7] = 235; YBuffer()[8] = 126;
+
+    UBuffer()[0] = 119; UBuffer()[1] = 110;
+    UBuffer()[2] = 165; UBuffer()[3] = 128;
+
+    VBuffer()[0] = 156; VBuffer()[1] = 25;
+    VBuffer()[2] = 175; VBuffer()[3] = 128;
+  }
+};
+
+class NV12Data : public NVImage
+{
+public:
+  NV12Data()
+  :NVImage(3, 3, 3, 2, 2, 4)
+  {
+    YBuffer()[0] = 16;  YBuffer()[1] = 82;  YBuffer()[2] = 144;
+    YBuffer()[3] = 41;  YBuffer()[4] = 210; YBuffer()[5] = 169;
+    YBuffer()[6] = 107; YBuffer()[7] = 235; YBuffer()[8] = 126;
+
+    // U
+    UVBuffer()[0] = 119;
+    UVBuffer()[2] = 110;
+    UVBuffer()[4] = 165;
+    UVBuffer()[6] = 128;
+
+    // V
+    UVBuffer()[1] = 156;
+    UVBuffer()[3] = 25;
+    UVBuffer()[5] = 175;
+    UVBuffer()[7] = 128;
+  }
+};
+
+class NV21Data : public NVImage
+{
+public:
+  NV21Data()
+  :NVImage(3, 3, 3, 2, 2, 4)
+  {
+    YBuffer()[0] = 16;  YBuffer()[1] = 82;  YBuffer()[2] = 144;
+    YBuffer()[3] = 41;  YBuffer()[4] = 210; YBuffer()[5] = 169;
+    YBuffer()[6] = 107; YBuffer()[7] = 235; YBuffer()[8] = 126;
+
+    // U
+    UVBuffer()[1] = 119;
+    UVBuffer()[3] = 110;
+    UVBuffer()[5] = 165;
+    UVBuffer()[7] = 128;
+
+    // V
+    UVBuffer()[0] = 156;
+    UVBuffer()[2] = 25;
+    UVBuffer()[4] = 175;
+    UVBuffer()[6] = 128;
+  }
+};
+
+class HSVData : public SimpleImage<float>
+{
+public:
+  HSVData()
+  : SimpleImage(3, 3, 3)
+  {
+    int i = 0;
+    mData[i + 0] = 0.0f;   mData[i + 1] = 0.0f; mData[i + 2] = 0.0f;
+    mData[i + 3] = 0.0f;   mData[i + 4] = 1.0f; mData[i + 5] = 1.0f;
+    mData[i + 6] = 120.0f; mData[i + 7] = 1.0f; mData[i + 8] = 1.0f;
+
+    i += mWidth * mChannelCount;
+    mData[i + 0] = 240.0f; mData[i + 1] = 1.0f; mData[i + 2] = 1.0f;
+    mData[i + 3] = 60.0f;  mData[i + 4] = 1.0f; mData[i + 5] = 1.0f;
+    mData[i + 6] = 180.0f; mData[i + 7] = 1.0f; mData[i + 8] = 1.0f;
+
+    i += mWidth * mChannelCount;
+    mData[i + 0] = 300.0f; mData[i + 1] = 1.0f; mData[i + 2] = 1.0f;
+    mData[i + 3] = 0.0f;   mData[i + 4] = 0.0f; mData[i + 5] = 1.0f;
+    mData[i + 6] = 0.0f;   mData[i + 7] = 0.0f; mData[i + 8] = 0.50196081f;
+  }
+};
+
+class LabData : public SimpleImage<float>
+{
+public:
+  LabData()
+  : SimpleImage(3, 3, 3)
+  {
+    int i = 0;
+    mData[i + 0] = 0.0f;        mData[i + 1] = 0.0f;         mData[i + 2] = 0.0f;
+    mData[i + 3] = 53.240585f;  mData[i + 4] = 80.094185f;   mData[i + 5] = 67.201538f;
+    mData[i + 6] = 87.7351f;    mData[i + 7] = -86.181252f;  mData[i + 8] = 83.177483f;
+
+    i += mWidth * mChannelCount;
+    mData[i + 0] = 32.29567f;   mData[i + 1] = 79.186989f;   mData[i + 2] = -107.86176f;
+    mData[i + 3] = 97.139511f;  mData[i + 4] = -21.552414f;  mData[i + 5] = 94.475792f;
+    mData[i + 6] = 91.113297f;  mData[i + 7] = -48.088551f;  mData[i + 8] = -14.130962f;
+
+    i += mWidth * mChannelCount;
+    mData[i + 0] = 60.323502f;  mData[i + 1] = 98.235161f;   mData[i + 2] = -60.825493f;
+    mData[i + 3] = 100.0f;      mData[i + 4] = 0.0f;         mData[i + 5] = 0.0f;
+    mData[i + 6] = 53.585014f;  mData[i + 7] = 0.0f;         mData[i + 8] = 0.0f;
+  }
+};
+
+/*
+ * From RGB24.
+ */
+
+TEST(ImageBitmapColorUtils, RGB24ToRGB24_)
+{
+  const RGB24Data srcData;
+  const RGB24Data dstData;
+
+  SimpleImage<uint8_t> result_(srcData.mWidth, srcData.mHeight, dstData.mChannelCount);
+
+  int rv = RGB24ToRGB24(srcData.mData, srcData.mStride,
+                        result_.mData, result_.mStride,
+                        result_.mWidth, result_.mHeight);
+
+  ASSERT_TRUE(rv == 0);
+
+  for (int i = 0; i < srcData.mHeight; ++i) {
+    for (int j = 0; j < srcData.mWidth; ++j) {
+      EXPECT_EQ(result_.mData[i * result_.mStride + j * result_.mChannelCount + 0],
+                dstData.mData[i * dstData.mStride + j * dstData.mChannelCount + 0]);
+      EXPECT_EQ(result_.mData[i * result_.mStride + j * result_.mChannelCount + 1],
+                dstData.mData[i * dstData.mStride + j * dstData.mChannelCount + 1]);
+      EXPECT_EQ(result_.mData[i * result_.mStride + j * result_.mChannelCount + 2],
+                dstData.mData[i * dstData.mStride + j * dstData.mChannelCount + 2]);
+    }
+  }
+}
+
+TEST(ImageBitmapColorUtils, RGB24ToBGR24_)
+{
+  const RGB24Data srcData;
+  const BGR24Data dstData;
+  SimpleImage<uint8_t> result_(srcData.mWidth, srcData.mHeight, dstData.mChannelCount);
+
+  int rv = RGB24ToBGR24(srcData.mData, srcData.mStride,
+                        result_.mData, result_.mStride,
+                        result_.mWidth, result_.mHeight);
+  ASSERT_TRUE(rv == 0);
+
+  for (int i = 0; i < srcData.mHeight; ++i) {
+    for (int j = 0; j < srcData.mWidth; ++j) {
+      EXPECT_EQ(result_.mData[i * result_.mStride + j * result_.mChannelCount + 0],
+                dstData.mData[i * dstData.mStride + j * dstData.mChannelCount + 0]);
+      EXPECT_EQ(result_.mData[i * result_.mStride + j * result_.mChannelCount + 1],
+                dstData.mData[i * dstData.mStride + j * dstData.mChannelCount + 1]);
+      EXPECT_EQ(result_.mData[i * result_.mStride + j * result_.mChannelCount + 2],
+                dstData.mData[i * dstData.mStride + j * dstData.mChannelCount + 2]);
+    }
+  }
+}
+
+TEST(ImageBitmapColorUtils, RGB24ToRGBA32)
+{
+  const RGB24Data srcData;
+  const RGBA32Data dstData;
+  SimpleImage<uint8_t> result_(srcData.mWidth, srcData.mHeight, dstData.mChannelCount);
+
+  int rv = RGB24ToRGBA32(srcData.mData, srcData.mStride,
+                         result_.mData, result_.mStride,
+                         result_.mWidth, result_.mHeight);
+  ASSERT_TRUE(rv == 0);
+
+  for (int i = 0; i < srcData.mHeight; ++i) {
+    for (int j = 0; j < srcData.mWidth; ++j) {
+      EXPECT_EQ(result_.mData[i * result_.mStride + j * result_.mChannelCount + 0],
+                dstData.mData[i * dstData.mStride + j * dstData.mChannelCount + 0]);
+      EXPECT_EQ(result_.mData[i * result_.mStride + j * result_.mChannelCount + 1],
+                dstData.mData[i * dstData.mStride + j * dstData.mChannelCount + 1]);
+      EXPECT_EQ(result_.mData[i * result_.mStride + j * result_.mChannelCount + 2],
+                dstData.mData[i * dstData.mStride + j * dstData.mChannelCount + 2]);
+      EXPECT_EQ(result_.mData[i * result_.mStride + j * result_.mChannelCount + 3],
+                255);
+    }
+  }
+}
+
+TEST(ImageBitmapColorUtils, RGB24ToBGRA32)
+{
+  const RGB24Data srcData;
+  const BGRA32Data dstData;
+
+  SimpleImage<uint8_t> result_(srcData.mWidth, srcData.mHeight, dstData.mChannelCount);
+
+  int rv = RGB24ToBGRA32(srcData.mData, srcData.mStride,
+                         result_.mData, result_.mStride,
+                         result_.mWidth, result_.mHeight);
+  ASSERT_TRUE(rv == 0);
+
+
+  for (int i = 0; i < srcData.mHeight; ++i) {
+    for (int j = 0; j < srcData.mWidth; ++j) {
+      EXPECT_EQ(result_.mData[i * result_.mStride + j * result_.mChannelCount + 0],
+                dstData.mData[i * dstData.mStride + j * dstData.mChannelCount + 0]);
+      EXPECT_EQ(result_.mData[i * result_.mStride + j * result_.mChannelCount + 1],
+                dstData.mData[i * dstData.mStride + j * dstData.mChannelCount + 1]);
+      EXPECT_EQ(result_.mData[i * result_.mStride + j * result_.mChannelCount + 2],
+                dstData.mData[i * dstData.mStride + j * dstData.mChannelCount + 2]);
+      EXPECT_EQ(result_.mData[i * result_.mStride + j * result_.mChannelCount + 3],
+                255);
+    }
+  }
+}
+
+
+//static void tryLibyuv(int r, int g, int b)
+//{
+//  const RGBA32DataSinglePixel srcData(r, g, b, 255);
+//  YUVImage result_(1, 1, 1, 1, 1, 1, 1, 1, 1);
+//
+//  int length = result_.mYHeight * result_.mYStride +
+//               result_.mUHeight * result_.mUStride +
+//               result_.mVHeight * result_.mVStride;
+//
+//  int rv = libyuv::ABGRToI420(srcData.mData, srcData.mStride,
+//                              result_.YBuffer(), result_.mYStride,
+//                              result_.UBuffer(), result_.mUStride,
+//                              result_.VBuffer(), result_.mVStride,
+//                              result_.mWidth, result_.mHeight);
+//
+//  printf_stderr("\nlibyuv: (%hhu, %hhu, %hhu) --> (%hhu, %hhu, %hhu) \n",
+//                r, g, b,
+//                result_.YBuffer()[0],
+//                result_.UBuffer()[0],
+//                result_.VBuffer()[0]);
+//}
+
+//static void tryLibyuv(int y, int u, int v)
+//{
+//  uint8_t* yuvPixel  = new uint8_t[3];
+//  uint8_t* rgbaPixel = new uint8_t[4];
+//
+//  yuvPixel[0] = y;
+//  yuvPixel[1] = u;
+//  yuvPixel[2] = v;
+//
+//  int rv = libyuv::I420ToABGR(yuvPixel + 0, 1,
+//                              yuvPixel + 1, 1,
+//                              yuvPixel + 2, 1,
+//                              rgbaPixel, 4,
+//                              1, 1);
+//
+//  printf_stderr("\nlibyuv: (%hhu, %hhu, %hhu) --> (%hhu, %hhu, %hhu, %hhu) \n",
+//                yuvPixel[0], yuvPixel[1], yuvPixel[2],
+//                rgbaPixel[0], rgbaPixel[1], rgbaPixel[2], rgbaPixel[3]);
+//}
+
+TEST(ImageBitmapColorUtils, RGB24ToYUV444P)
+{
+  const RGB24Data srcData;
+  const YUV444PData dstData;
+
+  YUVImage result_(srcData.mWidth, srcData.mHeight, srcData.mStride,
+                   srcData.mWidth, srcData.mHeight, srcData.mStride,
+                   srcData.mWidth, srcData.mHeight, srcData.mStride);
+
+  int rv = RGB24ToYUV444P(srcData.mData, srcData.mStride,
+                          result_.YBuffer(), result_.mYStride,
+                          result_.UBuffer(), result_.mUStride,
+                          result_.VBuffer(), result_.mVStride,
+                          result_.mYWidth, result_.mYHeight);
+
+  ASSERT_TRUE(rv == 0);
+
+  for (int i = 0; i < srcData.mHeight; ++i) {
+    for (int j = 0; j < srcData.mWidth; ++j) {
+      EXPECT_EQ(dstData.YBuffer()[i * dstData.mYStride + j],
+                result_.YBuffer()[i * result_.mYStride + j]);
+      EXPECT_EQ(dstData.UBuffer()[i * dstData.mUStride + j],
+                result_.UBuffer()[i * result_.mUStride + j]);
+      EXPECT_EQ(dstData.VBuffer()[i * dstData.mVStride + j],
+                result_.VBuffer()[i * result_.mVStride + j]);
+    }
+  }
+}
+
+TEST(ImageBitmapColorUtils, RGB24ToYUV422P)
+{
+  const RGB24Data srcData;
+  const YUV422PData dstData;
+
+  YUVImage result_(srcData.mWidth, srcData.mHeight, srcData.mStride,
+                   srcData.mWidth / 2 + 1, srcData.mHeight, srcData.mStride,
+                   srcData.mWidth / 2 + 1, srcData.mHeight, srcData.mStride);
+
+  int rv = RGB24ToYUV422P(srcData.mData, srcData.mStride,
+                          result_.YBuffer(), result_.mYStride,
+                          result_.UBuffer(), result_.mUStride,
+                          result_.VBuffer(), result_.mVStride,
+                          result_.mYWidth, result_.mYHeight);
+
+  ASSERT_TRUE(rv == 0);
+
+  for (int i = 0; i < dstData.mYHeight; ++i) {
+    for (int j = 0; j < dstData.mYWidth; ++j) {
+      EXPECT_EQ(dstData.YBuffer()[i * dstData.mYStride + j],
+                result_.YBuffer()[i * result_.mYStride + j]);
+    }
+  }
+
+  for (int i = 0; i < dstData.mUHeight; ++i) {
+    for (int j = 0; j < dstData.mUWidth; ++j) {
+      EXPECT_EQ(dstData.UBuffer()[i * dstData.mUStride + j],
+                result_.UBuffer()[i * result_.mUStride + j]);
+      EXPECT_EQ(dstData.VBuffer()[i * dstData.mVStride + j],
+                result_.VBuffer()[i * result_.mVStride + j]);
+    }
+  }
+}
+
+TEST(ImageBitmapColorUtils, RGB24ToYUV420P)
+{
+  const RGB24Data srcData;
+  const YUV420PData dstData;
+
+  YUVImage result_(srcData.mWidth, srcData.mHeight, srcData.mStride,
+                   srcData.mWidth / 2 + 1, srcData.mHeight / 2 + 1, srcData.mStride,
+                   srcData.mWidth / 2 + 1, srcData.mHeight / 2 + 1, srcData.mStride);
+
+  int rv = RGB24ToYUV420P(srcData.mData, srcData.mStride,
+                          result_.YBuffer(), result_.mYStride,
+                          result_.UBuffer(), result_.mUStride,
+                          result_.VBuffer(), result_.mVStride,
+                          result_.mYWidth, result_.mYHeight);
+
+  ASSERT_TRUE(rv == 0);
+
+  for (int i = 0; i < dstData.mYHeight; ++i) {
+    for (int j = 0; j < dstData.mYWidth; ++j) {
+      EXPECT_EQ(dstData.YBuffer()[i * dstData.mYStride + j],
+                result_.YBuffer()[i * result_.mYStride + j]);
+    }
+  }
+
+  for (int i = 0; i < dstData.mUHeight; ++i) {
+    for (int j = 0; j < dstData.mUWidth; ++j) {
+      EXPECT_EQ(dstData.UBuffer()[i * dstData.mUStride + j],
+                result_.UBuffer()[i * result_.mUStride + j]);
+      EXPECT_EQ(dstData.VBuffer()[i * dstData.mVStride + j],
+                result_.VBuffer()[i * result_.mVStride + j]);
+    }
+  }
+}
+
+TEST(ImageBitmapColorUtils, RGB24ToNV12)
+{
+  const RGB24Data srcData;
+  const NV12Data dstData;
+
+  NVImage result_(srcData.mWidth, srcData.mHeight, srcData.mStride,
+                  srcData.mWidth / 2 + 1, srcData.mHeight / 2 + 1, srcData.mStride + 10);
+
+  int rv = RGB24ToNV12(srcData.mData, srcData.mStride,
+                       result_.YBuffer(), result_.mYStride,
+                       result_.UVBuffer(), result_.mUVStride,
+                       result_.mYWidth, result_.mYHeight);
+
+  ASSERT_TRUE(rv == 0);
+
+  for (int i = 0; i < dstData.mYHeight; ++i) {
+    for (int j = 0; j < dstData.mYWidth; ++j) {
+      EXPECT_EQ(dstData.YBuffer()[i * dstData.mYStride + j],
+                result_.YBuffer()[i * result_.mYStride + j]);
+    }
+  }
+
+  for (int i = 0; i < dstData.mUVHeight; ++i) {
+    for (int j = 0; j < dstData.mUVWidth; ++j) {
+      EXPECT_EQ(dstData.UVBuffer()[i * dstData.mUVStride + j * 2 + 0],
+                result_.UVBuffer()[i * result_.mUVStride + j * 2 + 0]);
+      EXPECT_EQ(dstData.UVBuffer()[i * dstData.mUVStride + j * 2 + 1],
+                result_.UVBuffer()[i * result_.mUVStride + j * 2 + 1]);
+    }
+  }
+}
+
+TEST(ImageBitmapColorUtils, RGB24ToNV21)
+{
+  const RGB24Data srcData;
+  const NV21Data dstData;
+
+  NVImage result_(srcData.mWidth, srcData.mHeight, srcData.mStride,
+                  srcData.mWidth / 2 + 1, srcData.mHeight / 2 + 1, srcData.mStride + 10);
+
+  int rv = RGB24ToNV21(srcData.mData, srcData.mStride,
+                       result_.YBuffer(), result_.mYStride,
+                       result_.UVBuffer(), result_.mUVStride,
+                       result_.mYWidth, result_.mYHeight);
+
+  ASSERT_TRUE(rv == 0);
+
+  for (int i = 0; i < dstData.mYHeight; ++i) {
+    for (int j = 0; j < dstData.mYWidth; ++j) {
+      EXPECT_EQ(dstData.YBuffer()[i * dstData.mYStride + j],
+                result_.YBuffer()[i * result_.mYStride + j]);
+    }
+  }
+
+  for (int i = 0; i < dstData.mUVHeight; ++i) {
+    for (int j = 0; j < dstData.mUVWidth; ++j) {
+      EXPECT_EQ(dstData.UVBuffer()[i * dstData.mUVStride + j * 2 + 0],
+                result_.UVBuffer()[i * result_.mUVStride + j * 2 + 0]);
+      EXPECT_EQ(dstData.UVBuffer()[i * dstData.mUVStride + j * 2 + 1],
+                result_.UVBuffer()[i * result_.mUVStride + j * 2 + 1]);
+    }
+  }
+}
+
+TEST(ImageBitmapColorUtils, RGB24ToHSV)
+{
+  const RGB24Data srcData;
+  const HSVData dstData;
+
+  SimpleImage<float> result_(srcData.mWidth, srcData.mHeight, 3);
+
+  int rv = RGB24ToHSV(srcData.mData, srcData.mStride,
+                      result_.mData, result_.mStride,
+                      result_.mWidth, result_.mHeight);
+
+  ASSERT_TRUE(rv == 0);
+
+  for (int i = 0; i < dstData.mHeight; ++i) {
+    for (int j = 0; j < dstData.mWidth; ++j) {
+      for (int c = 0; c < dstData.mChannelCount; ++c) {
+        const double actualValue = result_.GetPixelValue(i, j, c);
+        const double expectedValue = dstData.GetPixelValue(i, j, c);
+        const double diff = std::abs(actualValue - expectedValue);
+        EXPECT_LT(diff, 1.0);
+      }
+    }
+  }
+}
+
+TEST(ImageBitmapColorUtils, RGB24ToLab)
+{
+  const RGB24Data srcData;
+  const LabData dstData;
+
+  SimpleImage<float> result_(srcData.mWidth, srcData.mHeight, 3);
+
+  int rv = RGB24ToLab(srcData.mData, srcData.mStride,
+                      result_.mData, result_.mStride,
+                      result_.mWidth, result_.mHeight);
+
+  ASSERT_TRUE(rv == 0);
+
+  for (int i = 0; i < dstData.mHeight; ++i) {
+    for (int j = 0; j < dstData.mWidth; ++j) {
+      for (int c = 0; c < dstData.mChannelCount; ++c) {
+        const double actualValue = result_.GetPixelValue(i, j, c);
+        const double expectedValue = dstData.GetPixelValue(i, j, c);
+        const double diff = std::abs(actualValue - expectedValue);
+        EXPECT_LT(diff, 1.0);
+      }
+    }
+  }
+}
+
+TEST(ImageBitmapColorUtils, RGB24ToGray8)
+{
+  const RGB24Data srcData;
+  const GrayData dstData;
+
+  SimpleImage<uint8_t> result_(srcData.mWidth, srcData.mHeight, 1);
+
+  int rv = RGB24ToGray8(srcData.mData, srcData.mStride,
+                        result_.mData, result_.mStride,
+                        result_.mWidth, result_.mHeight);
+
+  ASSERT_TRUE(rv == 0);
+
+  for (int i = 0; i < dstData.mHeight; ++i) {
+    for (int j = 0; j < dstData.mWidth; ++j) {
+      EXPECT_EQ(result_.mData[i * result_.mStride + j],
+                dstData.mData[i * dstData.mStride + j]);
+    }
+  }
+}
+
+/*
+ * From BGR24.
+ */
+
+TEST(ImageBitmapColorUtils, BGR24ToRGB24_)
+{
+  const BGR24Data srcData;
+  const RGB24Data dstData;
+  SimpleImage<uint8_t> result_(srcData.mWidth, srcData.mHeight, dstData.mChannelCount);
+
+  int rv = BGR24ToRGB24(srcData.mData, srcData.mStride,
+                        result_.mData, result_.mStride,
+                        result_.mWidth, result_.mHeight);
+  ASSERT_TRUE(rv == 0);
+
+  for (int i = 0; i < srcData.mHeight; ++i) {
+    for (int j = 0; j < srcData.mWidth; ++j) {
+      EXPECT_EQ(result_.mData[i * result_.mStride + j * result_.mChannelCount + 0],
+                dstData.mData[i * dstData.mStride + j * dstData.mChannelCount + 0]);
+      EXPECT_EQ(result_.mData[i * result_.mStride + j * result_.mChannelCount + 1],
+                dstData.mData[i * dstData.mStride + j * dstData.mChannelCount + 1]);
+      EXPECT_EQ(result_.mData[i * result_.mStride + j * result_.mChannelCount + 2],
+                dstData.mData[i * dstData.mStride + j * dstData.mChannelCount + 2]);
+    }
+  }
+}
+
+TEST(ImageBitmapColorUtils, BGR24ToBGR24_)
+{
+  const BGR24Data srcData;
+  const BGR24Data dstData;
+  SimpleImage<uint8_t> result_(srcData.mWidth, srcData.mHeight, dstData.mChannelCount);
+
+  int rv = BGR24ToBGR24(srcData.mData, srcData.mStride,
+                        result_.mData, result_.mStride,
+                        result_.mWidth, result_.mHeight);
+  ASSERT_TRUE(rv == 0);
+
+  for (int i = 0; i < srcData.mHeight; ++i) {
+    for (int j = 0; j < srcData.mWidth; ++j) {
+      EXPECT_EQ(result_.mData[i * result_.mStride + j * result_.mChannelCount + 0],
+                dstData.mData[i * dstData.mStride + j * dstData.mChannelCount + 0]);
+      EXPECT_EQ(result_.mData[i * result_.mStride + j * result_.mChannelCount + 1],
+                dstData.mData[i * dstData.mStride + j * dstData.mChannelCount + 1]);
+      EXPECT_EQ(result_.mData[i * result_.mStride + j * result_.mChannelCount + 2],
+                dstData.mData[i * dstData.mStride + j * dstData.mChannelCount + 2]);
+    }
+  }
+}
+
+TEST(ImageBitmapColorUtils, BGR24ToRGBA32)
+{
+  const BGR24Data srcData;
+  const RGBA32Data dstData;
+  SimpleImage<uint8_t> result_(srcData.mWidth, srcData.mHeight, dstData.mChannelCount);
+
+  int rv = BGR24ToRGBA32(srcData.mData, srcData.mStride,
+                         result_.mData, result_.mStride,
+                         result_.mWidth, result_.mHeight);
+  ASSERT_TRUE(rv == 0);
+
+  for (int i = 0; i < srcData.mHeight; ++i) {
+    for (int j = 0; j < srcData.mWidth; ++j) {
+      EXPECT_EQ(result_.mData[i * result_.mStride + j * result_.mChannelCount + 0],
+                dstData.mData[i * dstData.mStride + j * dstData.mChannelCount + 0]);
+      EXPECT_EQ(result_.mData[i * result_.mStride + j * result_.mChannelCount + 1],
+                dstData.mData[i * dstData.mStride + j * dstData.mChannelCount + 1]);
+      EXPECT_EQ(result_.mData[i * result_.mStride + j * result_.mChannelCount + 2],
+                dstData.mData[i * dstData.mStride + j * dstData.mChannelCount + 2]);
+      EXPECT_EQ(result_.mData[i * result_.mStride + j * result_.mChannelCount + 3],
+                255);
+    }
+  }
+}
+
+TEST(ImageBitmapColorUtils, BGR24ToBGRA32)
+{
+  const BGR24Data srcData;
+  const BGRA32Data dstData;
+  SimpleImage<uint8_t> result_(srcData.mWidth, srcData.mHeight, dstData.mChannelCount);
+
+  int rv = BGR24ToBGRA32(srcData.mData, srcData.mStride,
+                         result_.mData, result_.mStride,
+                         result_.mWidth, result_.mHeight);
+  ASSERT_TRUE(rv == 0);
+
+  for (int i = 0; i < srcData.mHeight; ++i) {
+    for (int j = 0; j < srcData.mWidth; ++j) {
+      EXPECT_EQ(result_.mData[i * result_.mStride + j * result_.mChannelCount + 0],
+                dstData.mData[i * dstData.mStride + j * dstData.mChannelCount + 0]);
+      EXPECT_EQ(result_.mData[i * result_.mStride + j * result_.mChannelCount + 1],
+                dstData.mData[i * dstData.mStride + j * dstData.mChannelCount + 1]);
+      EXPECT_EQ(result_.mData[i * result_.mStride + j * result_.mChannelCount + 2],
+                dstData.mData[i * dstData.mStride + j * dstData.mChannelCount + 2]);
+      EXPECT_EQ(result_.mData[i * result_.mStride + j * result_.mChannelCount + 3],
+                255);
+    }
+  }
+}
+
+TEST(ImageBitmapColorUtils, BGR24ToYUV444P)
+{
+  const BGR24Data srcData;
+  const YUV444PData dstData;
+
+  YUVImage result_(srcData.mWidth, srcData.mHeight, srcData.mStride,
+                   srcData.mWidth, srcData.mHeight, srcData.mStride,
+                   srcData.mWidth, srcData.mHeight, srcData.mStride);
+
+  int rv = BGR24ToYUV444P(srcData.mData, srcData.mStride,
+                          result_.YBuffer(), result_.mYStride,
+                          result_.UBuffer(), result_.mUStride,
+                          result_.VBuffer(), result_.mVStride,
+                          result_.mYWidth, result_.mYHeight);
+
+  ASSERT_TRUE(rv == 0);
+
+  for (int i = 0; i < srcData.mHeight; ++i) {
+    for (int j = 0; j < srcData.mWidth; ++j) {
+      EXPECT_EQ(dstData.YBuffer()[i * dstData.mYStride + j],
+                result_.YBuffer()[i * result_.mYStride + j]);
+      EXPECT_EQ(dstData.UBuffer()[i * dstData.mUStride + j],
+                result_.UBuffer()[i * result_.mUStride + j]);
+      EXPECT_EQ(dstData.VBuffer()[i * dstData.mVStride + j],
+                result_.VBuffer()[i * result_.mVStride + j]);
+    }
+  }
+}
+
+TEST(ImageBitmapColorUtils, BGR24ToYUV422P)
+{
+  const BGR24Data srcData;
+  const YUV422PData dstData;
+
+  YUVImage result_(srcData.mWidth, srcData.mHeight, srcData.mStride,
+                   srcData.mWidth / 2 + 1, srcData.mHeight, srcData.mStride,
+                   srcData.mWidth / 2 + 1, srcData.mHeight, srcData.mStride);
+
+  int rv = BGR24ToYUV422P(srcData.mData, srcData.mStride,
+                          result_.YBuffer(), result_.mYStride,
+                          result_.UBuffer(), result_.mUStride,
+                          result_.VBuffer(), result_.mVStride,
+                          result_.mYWidth, result_.mYHeight);
+
+  ASSERT_TRUE(rv == 0);
+
+  for (int i = 0; i < dstData.mYHeight; ++i) {
+    for (int j = 0; j < dstData.mYWidth; ++j) {
+      EXPECT_EQ(dstData.YBuffer()[i * dstData.mYStride + j],
+                result_.YBuffer()[i * result_.mYStride + j]);
+    }
+  }
+
+  for (int i = 0; i < dstData.mUHeight; ++i) {
+    for (int j = 0; j < dstData.mUWidth; ++j) {
+      EXPECT_EQ(dstData.UBuffer()[i * dstData.mUStride + j],
+                result_.UBuffer()[i * result_.mUStride + j]);
+      EXPECT_EQ(dstData.VBuffer()[i * dstData.mVStride + j],
+                result_.VBuffer()[i * result_.mVStride + j]);
+    }
+  }
+}
+
+TEST(ImageBitmapColorUtils, BGR24ToYUV420P)
+{
+  const BGR24Data srcData;
+  const YUV420PData dstData;
+
+  YUVImage result_(srcData.mWidth, srcData.mHeight, srcData.mStride,
+                   srcData.mWidth / 2 + 1, srcData.mHeight / 2 + 1, srcData.mStride,
+                   srcData.mWidth / 2 + 1, srcData.mHeight / 2 + 1, srcData.mStride);
+
+  int rv = BGR24ToYUV420P(srcData.mData, srcData.mStride,
+                          result_.YBuffer(), result_.mYStride,
+                          result_.UBuffer(), result_.mUStride,
+                          result_.VBuffer(), result_.mVStride,
+                          result_.mYWidth, result_.mYHeight);
+
+  ASSERT_TRUE(rv == 0);
+
+  for (int i = 0; i < dstData.mYHeight; ++i) {
+    for (int j = 0; j < dstData.mYWidth; ++j) {
+      EXPECT_EQ(dstData.YBuffer()[i * dstData.mYStride + j],
+                result_.YBuffer()[i * result_.mYStride + j]);
+    }
+  }
+
+  for (int i = 0; i < dstData.mUHeight; ++i) {
+    for (int j = 0; j < dstData.mUWidth; ++j) {
+      EXPECT_EQ(dstData.UBuffer()[i * dstData.mUStride + j],
+                result_.UBuffer()[i * result_.mUStride + j]);
+      EXPECT_EQ(dstData.VBuffer()[i * dstData.mVStride + j],
+                result_.VBuffer()[i * result_.mVStride + j]);
+    }
+  }
+}
+
+TEST(ImageBitmapColorUtils, BGR24ToNV12)
+{
+  const BGR24Data srcData;
+  const NV12Data dstData;
+
+  NVImage result_(srcData.mWidth, srcData.mHeight, srcData.mStride,
+                  srcData.mWidth / 2 + 1, srcData.mHeight / 2 + 1, srcData.mStride + 10);
+
+  int rv = BGR24ToNV12(srcData.mData, srcData.mStride,
+                       result_.YBuffer(), result_.mYStride,
+                       result_.UVBuffer(), result_.mUVStride,
+                       result_.mYWidth, result_.mYHeight);
+
+  ASSERT_TRUE(rv == 0);
+
+  for (int i = 0; i < dstData.mYHeight; ++i) {
+    for (int j = 0; j < dstData.mYWidth; ++j) {
+      EXPECT_EQ(dstData.YBuffer()[i * dstData.mYStride + j],
+                result_.YBuffer()[i * result_.mYStride + j]);
+    }
+  }
+
+  for (int i = 0; i < dstData.mUVHeight; ++i) {
+    for (int j = 0; j < dstData.mUVWidth; ++j) {
+      EXPECT_EQ(dstData.UVBuffer()[i * dstData.mUVStride + j * 2 + 0],
+                result_.UVBuffer()[i * result_.mUVStride + j * 2 + 0]);
+      EXPECT_EQ(dstData.UVBuffer()[i * dstData.mUVStride + j * 2 + 1],
+                result_.UVBuffer()[i * result_.mUVStride + j * 2 + 1]);
+    }
+  }
+}
+
+TEST(ImageBitmapColorUtils, BGR24ToNV21)
+{
+  const BGR24Data srcData;
+  const NV21Data dstData;
+
+  NVImage result_(srcData.mWidth, srcData.mHeight, srcData.mStride,
+                  srcData.mWidth / 2 + 1, srcData.mHeight / 2 + 1, srcData.mStride + 10);
+
+  int rv = BGR24ToNV21(srcData.mData, srcData.mStride,
+                       result_.YBuffer(), result_.mYStride,
+                       result_.UVBuffer(), result_.mUVStride,
+                       result_.mYWidth, result_.mYHeight);
+
+  ASSERT_TRUE(rv == 0);
+
+  for (int i = 0; i < dstData.mYHeight; ++i) {
+    for (int j = 0; j < dstData.mYWidth; ++j) {
+      EXPECT_EQ(dstData.YBuffer()[i * dstData.mYStride + j],
+                result_.YBuffer()[i * result_.mYStride + j]);
+    }
+  }
+
+  for (int i = 0; i < dstData.mUVHeight; ++i) {
+    for (int j = 0; j < dstData.mUVWidth; ++j) {
+      EXPECT_EQ(dstData.UVBuffer()[i * dstData.mUVStride + j * 2 + 0],
+                result_.UVBuffer()[i * result_.mUVStride + j * 2 + 0]);
+      EXPECT_EQ(dstData.UVBuffer()[i * dstData.mUVStride + j * 2 + 1],
+                result_.UVBuffer()[i * result_.mUVStride + j * 2 + 1]);
+    }
+  }
+}
+
+TEST(ImageBitmapColorUtils, BGR24ToHSV)
+{
+  const BGR24Data srcData;
+  const HSVData dstData;
+
+  SimpleImage<float> result_(srcData.mWidth, srcData.mHeight, 3);
+
+  int rv = BGR24ToHSV(srcData.mData, srcData.mStride,
+                      result_.mData, result_.mStride,
+                      result_.mWidth, result_.mHeight);
+
+  ASSERT_TRUE(rv == 0);
+
+  for (int i = 0; i < dstData.mHeight; ++i) {
+    for (int j = 0; j < dstData.mWidth; ++j) {
+      for (int c = 0; c < dstData.mChannelCount; ++c) {
+        const double actualValue = result_.GetPixelValue(i, j, c);
+        const double expectedValue = dstData.GetPixelValue(i, j, c);
+        const double diff = std::abs(actualValue - expectedValue);
+        EXPECT_LT(diff, 1.0);
+      }
+    }
+  }
+}
+
+TEST(ImageBitmapColorUtils, BGR24ToLab)
+{
+  const BGR24Data srcData;
+  const LabData dstData;
+
+  SimpleImage<float> result_(srcData.mWidth, srcData.mHeight, 3);
+
+  int rv = BGR24ToLab(srcData.mData, srcData.mStride,
+                      result_.mData, result_.mStride,
+                      result_.mWidth, result_.mHeight);
+
+  ASSERT_TRUE(rv == 0);
+
+  for (int i = 0; i < dstData.mHeight; ++i) {
+    for (int j = 0; j < dstData.mWidth; ++j) {
+      for (int c = 0; c < dstData.mChannelCount; ++c) {
+        const double actualValue = result_.GetPixelValue(i, j, c);
+        const double expectedValue = dstData.GetPixelValue(i, j, c);
+        const double diff = std::abs(actualValue - expectedValue);
+        EXPECT_LT(diff, 1.0);
+      }
+    }
+  }
+}
+
+TEST(ImageBitmapColorUtils, BGR24ToGray8)
+{
+  const BGR24Data srcData;
+  const GrayData dstData;
+
+  SimpleImage<uint8_t> result_(srcData.mWidth, srcData.mHeight, 1);
+
+  int rv = BGR24ToGray8(srcData.mData, srcData.mStride,
+                        result_.mData, result_.mStride,
+                        result_.mWidth, result_.mHeight);
+
+  ASSERT_TRUE(rv == 0);
+
+  for (int i = 0; i < dstData.mHeight; ++i) {
+    for (int j = 0; j < dstData.mWidth; ++j) {
+      EXPECT_EQ(result_.mData[i * result_.mStride + j],
+                dstData.mData[i * dstData.mStride + j]);
+    }
+  }
+}
+
+/*
+ * From RGBA32.
+ */
+TEST(ImageBitmapColorUtils, RGBA32ToRGB24)
+{
+  const RGBA32Data srcData;
+  const RGB24Data dstData;
+  SimpleImage<uint8_t> result_(srcData.mWidth, srcData.mHeight, dstData.mChannelCount);
+
+  int rv = RGBA32ToRGB24(srcData.mData, srcData.mStride,
+                         result_.mData, result_.mStride,
+                         result_.mWidth, result_.mHeight);
+  ASSERT_TRUE(rv == 0);
+
+  for (int i = 0; i < srcData.mHeight; ++i) {
+    for (int j = 0; j < srcData.mWidth; ++j) {
+      EXPECT_EQ(result_.mData[i * result_.mStride + j * result_.mChannelCount + 0],
+                dstData.mData[i * dstData.mStride + j * dstData.mChannelCount + 0]);
+      EXPECT_EQ(result_.mData[i * result_.mStride + j * result_.mChannelCount + 1],
+                dstData.mData[i * dstData.mStride + j * dstData.mChannelCount + 1]);
+      EXPECT_EQ(result_.mData[i * result_.mStride + j * result_.mChannelCount + 2],
+                dstData.mData[i * dstData.mStride + j * dstData.mChannelCount + 2]);
+    }
+  }
+}
+
+TEST(ImageBitmapColorUtils, RGBA32ToBGR24)
+{
+  const RGBA32Data srcData;
+  const BGR24Data dstData;
+  SimpleImage<uint8_t> result_(srcData.mWidth, srcData.mHeight, dstData.mChannelCount);
+
+  int rv = RGBA32ToBGR24(srcData.mData, srcData.mStride,
+                         result_.mData, result_.mStride,
+                         result_.mWidth, result_.mHeight);
+  ASSERT_TRUE(rv == 0);
+
+  for (int i = 0; i < srcData.mHeight; ++i) {
+    for (int j = 0; j < srcData.mWidth; ++j) {
+      EXPECT_EQ(result_.mData[i * result_.mStride + j * result_.mChannelCount + 0],
+                dstData.mData[i * dstData.mStride + j * dstData.mChannelCount + 0]);
+      EXPECT_EQ(result_.mData[i * result_.mStride + j * result_.mChannelCount + 1],
+                dstData.mData[i * dstData.mStride + j * dstData.mChannelCount + 1]);
+      EXPECT_EQ(result_.mData[i * result_.mStride + j * result_.mChannelCount + 2],
+                dstData.mData[i * dstData.mStride + j * dstData.mChannelCount + 2]);
+    }
+  }
+}
+
+TEST(ImageBitmapColorUtils, RGBA32ToYUV444P)
+{
+  const RGBA32Data srcData;
+  const YUV444PData dstData;
+
+  YUVImage result_(srcData.mWidth, srcData.mHeight, srcData.mStride,
+                   srcData.mWidth, srcData.mHeight, srcData.mStride,
+                   srcData.mWidth, srcData.mHeight, srcData.mStride);
+
+  int rv = RGBA32ToYUV444P(srcData.mData, srcData.mStride,
+                           result_.YBuffer(), result_.mYStride,
+                           result_.UBuffer(), result_.mUStride,
+                           result_.VBuffer(), result_.mVStride,
+                           result_.mYWidth, result_.mYHeight);
+
+  ASSERT_TRUE(rv == 0);
+
+  for (int i = 0; i < srcData.mHeight; ++i) {
+    for (int j = 0; j < srcData.mWidth; ++j) {
+      EXPECT_EQ(dstData.YBuffer()[i * dstData.mYStride + j],
+                result_.YBuffer()[i * result_.mYStride + j]);
+      EXPECT_EQ(dstData.UBuffer()[i * dstData.mUStride + j],
+                result_.UBuffer()[i * result_.mUStride + j]);
+      EXPECT_EQ(dstData.VBuffer()[i * dstData.mVStride + j],
+                result_.VBuffer()[i * result_.mVStride + j]);
+    }
+  }
+}
+
+TEST(ImageBitmapColorUtils, RGBA32ToYUV422P)
+{
+  const RGBA32Data srcData;
+  const YUV422PData dstData;
+
+  YUVImage result_(srcData.mWidth, srcData.mHeight, srcData.mStride,
+                   srcData.mWidth / 2 + 1, srcData.mHeight, srcData.mStride,
+                   srcData.mWidth / 2 + 1, srcData.mHeight, srcData.mStride);
+
+  int rv = RGBA32ToYUV422P(srcData.mData, srcData.mStride,
+                           result_.YBuffer(), result_.mYStride,
+                           result_.UBuffer(), result_.mUStride,
+                           result_.VBuffer(), result_.mVStride,
+                           result_.mYWidth, result_.mYHeight);
+
+  ASSERT_TRUE(rv == 0);
+
+  for (int i = 0; i < dstData.mYHeight; ++i) {
+    for (int j = 0; j < dstData.mYWidth; ++j) {
+      EXPECT_EQ(dstData.YBuffer()[i * dstData.mYStride + j],
+                result_.YBuffer()[i * result_.mYStride + j]);
+    }
+  }
+
+  for (int i = 0; i < dstData.mUHeight; ++i) {
+    for (int j = 0; j < dstData.mUWidth; ++j) {
+      EXPECT_EQ(dstData.UBuffer()[i * dstData.mUStride + j],
+                result_.UBuffer()[i * result_.mUStride + j]);
+      EXPECT_EQ(dstData.VBuffer()[i * dstData.mVStride + j],
+                result_.VBuffer()[i * result_.mVStride + j]);
+    }
+  }
+}
+
+TEST(ImageBitmapColorUtils, RGBA32ToYUV420P)
+{
+  const RGBA32Data srcData;
+  const YUV420PData dstData;
+
+  YUVImage result_(srcData.mWidth, srcData.mHeight, srcData.mStride,
+                   srcData.mWidth / 2 + 1, srcData.mHeight / 2 + 1, srcData.mStride,
+                   srcData.mWidth / 2 + 1, srcData.mHeight / 2 + 1, srcData.mStride);
+
+  int rv = RGBA32ToYUV420P(srcData.mData, srcData.mStride,
+                           result_.YBuffer(), result_.mYStride,
+                           result_.UBuffer(), result_.mUStride,
+                           result_.VBuffer(), result_.mVStride,
+                           result_.mYWidth, result_.mYHeight);
+
+  ASSERT_TRUE(rv == 0);
+
+  for (int i = 0; i < dstData.mYHeight; ++i) {
+    for (int j = 0; j < dstData.mYWidth; ++j) {
+      EXPECT_EQ(dstData.YBuffer()[i * dstData.mYStride + j],
+                result_.YBuffer()[i * result_.mYStride + j]);
+    }
+  }
+
+  for (int i = 0; i < dstData.mUHeight; ++i) {
+    for (int j = 0; j < dstData.mUWidth; ++j) {
+      EXPECT_EQ(dstData.UBuffer()[i * dstData.mUStride + j],
+                result_.UBuffer()[i * result_.mUStride + j]);
+      EXPECT_EQ(dstData.VBuffer()[i * dstData.mVStride + j],
+                result_.VBuffer()[i * result_.mVStride + j]);
+    }
+  }
+}
+
+TEST(ImageBitmapColorUtils, RGBA32ToNV12)
+{
+  const RGBA32Data srcData;
+  const NV12Data dstData;
+
+  NVImage result_(srcData.mWidth, srcData.mHeight, srcData.mStride,
+                  srcData.mWidth / 2 + 1, srcData.mHeight / 2 + 1, srcData.mStride + 10);
+
+  int rv = RGBA32ToNV12(srcData.mData, srcData.mStride,
+                        result_.YBuffer(), result_.mYStride,
+                        result_.UVBuffer(), result_.mUVStride,
+                        result_.mYWidth, result_.mYHeight);
+
+  ASSERT_TRUE(rv == 0);
+
+  for (int i = 0; i < dstData.mYHeight; ++i) {
+    for (int j = 0; j < dstData.mYWidth; ++j) {
+      EXPECT_EQ(dstData.YBuffer()[i * dstData.mYStride + j],
+                result_.YBuffer()[i * result_.mYStride + j]);
+    }
+  }
+
+  for (int i = 0; i < dstData.mUVHeight; ++i) {
+    for (int j = 0; j < dstData.mUVWidth; ++j) {
+      EXPECT_EQ(dstData.UVBuffer()[i * dstData.mUVStride + j * 2 + 0],
+                result_.UVBuffer()[i * result_.mUVStride + j * 2 + 0]);
+      EXPECT_EQ(dstData.UVBuffer()[i * dstData.mUVStride + j * 2 + 1],
+                result_.UVBuffer()[i * result_.mUVStride + j * 2 + 1]);
+    }
+  }
+}
+
+TEST(ImageBitmapColorUtils, RGBA32ToNV21)
+{
+  const RGBA32Data srcData;
+  const NV21Data dstData;
+
+  NVImage result_(srcData.mWidth, srcData.mHeight, srcData.mStride,
+                  srcData.mWidth / 2 + 1, srcData.mHeight / 2 + 1, srcData.mStride + 10);
+
+  int rv = RGBA32ToNV21(srcData.mData, srcData.mStride,
+                        result_.YBuffer(), result_.mYStride,
+                        result_.UVBuffer(), result_.mUVStride,
+                        result_.mYWidth, result_.mYHeight);
+
+  ASSERT_TRUE(rv == 0);
+
+  for (int i = 0; i < dstData.mYHeight; ++i) {
+    for (int j = 0; j < dstData.mYWidth; ++j) {
+      EXPECT_EQ(dstData.YBuffer()[i * dstData.mYStride + j],
+                result_.YBuffer()[i * result_.mYStride + j]);
+    }
+  }
+
+  for (int i = 0; i < dstData.mUVHeight; ++i) {
+    for (int j = 0; j < dstData.mUVWidth; ++j) {
+      EXPECT_EQ(dstData.UVBuffer()[i * dstData.mUVStride + j * 2 + 0],
+                result_.UVBuffer()[i * result_.mUVStride + j * 2 + 0]);
+      EXPECT_EQ(dstData.UVBuffer()[i * dstData.mUVStride + j * 2 + 1],
+                result_.UVBuffer()[i * result_.mUVStride + j * 2 + 1]);
+    }
+  }
+}
+
+TEST(ImageBitmapColorUtils, RGBA32ToHSV)
+{
+  const RGBA32Data srcData;
+  const HSVData dstData;
+
+  SimpleImage<float> result_(srcData.mWidth, srcData.mHeight, 3);
+
+  int rv = RGBA32ToHSV(srcData.mData, srcData.mStride,
+                       result_.mData, result_.mStride,
+                       result_.mWidth, result_.mHeight);
+
+  ASSERT_TRUE(rv == 0);
+
+  for (int i = 0; i < dstData.mHeight; ++i) {
+    for (int j = 0; j < dstData.mWidth; ++j) {
+      for (int c = 0; c < dstData.mChannelCount; ++c) {
+        const double actualValue = result_.GetPixelValue(i, j, c);
+        const double expectedValue = dstData.GetPixelValue(i, j, c);
+        const double diff = std::abs(actualValue - expectedValue);
+        EXPECT_LT(diff, 1.0);
+      }
+    }
+  }
+}
+
+TEST(ImageBitmapColorUtils, RGBA32ToLab)
+{
+  const RGBA32Data srcData;
+  const LabData dstData;
+
+  SimpleImage<float> result_(srcData.mWidth, srcData.mHeight, 3);
+
+  int rv = RGBA32ToLab(srcData.mData, srcData.mStride,
+                       result_.mData, result_.mStride,
+                       result_.mWidth, result_.mHeight);
+
+  ASSERT_TRUE(rv == 0);
+
+  for (int i = 0; i < dstData.mHeight; ++i) {
+    for (int j = 0; j < dstData.mWidth; ++j) {
+      for (int c = 0; c < dstData.mChannelCount; ++c) {
+        const double actualValue = result_.GetPixelValue(i, j, c);
+        const double expectedValue = dstData.GetPixelValue(i, j, c);
+        const double diff = std::abs(actualValue - expectedValue);
+        EXPECT_LT(diff, 1.0);
+      }
+    }
+  }
+}
+
+TEST(ImageBitmapColorUtils, RGBA32ToGray8)
+{
+  const RGBA32Data srcData;
+  const GrayData dstData;
+
+  SimpleImage<uint8_t> result_(srcData.mWidth, srcData.mHeight, 1);
+
+  int rv = RGBA32ToGray8(srcData.mData, srcData.mStride,
+                         result_.mData, result_.mStride,
+                         result_.mWidth, result_.mHeight);
+
+  ASSERT_TRUE(rv == 0);
+
+  for (int i = 0; i < dstData.mHeight; ++i) {
+    for (int j = 0; j < dstData.mWidth; ++j) {
+      EXPECT_EQ(result_.mData[i * result_.mStride + j],
+                dstData.mData[i * dstData.mStride + j]);
+    }
+  }
+}
+
+/*
+ * From BGRA32.
+ */
+TEST(ImageBitmapColorUtils, BGRA32ToRGB24)
+{
+  const BGRA32Data srcData;
+  const RGB24Data dstData;
+  SimpleImage<uint8_t> result_(srcData.mWidth, srcData.mHeight, dstData.mChannelCount);
+
+  int rv = BGRA32ToRGB24(srcData.mData, srcData.mStride,
+                         result_.mData, result_.mStride,
+                         result_.mWidth, result_.mHeight);
+  ASSERT_TRUE(rv == 0);
+
+  for (int i = 0; i < srcData.mHeight; ++i) {
+    for (int j = 0; j < srcData.mWidth; ++j) {
+      EXPECT_EQ(result_.mData[i * result_.mStride + j * result_.mChannelCount + 0],
+                dstData.mData[i * dstData.mStride + j * dstData.mChannelCount + 0]);
+      EXPECT_EQ(result_.mData[i * result_.mStride + j * result_.mChannelCount + 1],
+                dstData.mData[i * dstData.mStride + j * dstData.mChannelCount + 1]);
+      EXPECT_EQ(result_.mData[i * result_.mStride + j * result_.mChannelCount + 2],
+                dstData.mData[i * dstData.mStride + j * dstData.mChannelCount + 2]);
+    }
+  }
+}
+
+TEST(ImageBitmapColorUtils, BGRA32ToBGR24)
+{
+  const BGRA32Data srcData;
+  const BGR24Data dstData;
+  SimpleImage<uint8_t> result_(srcData.mWidth, srcData.mHeight, dstData.mChannelCount);
+
+  int rv = BGRA32ToBGR24(srcData.mData, srcData.mStride,
+                         result_.mData, result_.mStride,
+                         result_.mWidth, result_.mHeight);
+  ASSERT_TRUE(rv == 0);
+
+  for (int i = 0; i < srcData.mHeight; ++i) {
+    for (int j = 0; j < srcData.mWidth; ++j) {
+      EXPECT_EQ(result_.mData[i * result_.mStride + j * result_.mChannelCount + 0],
+                dstData.mData[i * dstData.mStride + j * dstData.mChannelCount + 0]);
+      EXPECT_EQ(result_.mData[i * result_.mStride + j * result_.mChannelCount + 1],
+                dstData.mData[i * dstData.mStride + j * dstData.mChannelCount + 1]);
+      EXPECT_EQ(result_.mData[i * result_.mStride + j * result_.mChannelCount + 2],
+                dstData.mData[i * dstData.mStride + j * dstData.mChannelCount + 2]);
+    }
+  }
+}
+
+TEST(ImageBitmapColorUtils, BGRA32ToYUV444P)
+{
+  const BGRA32Data srcData;
+  const YUV444PData dstData;
+
+  YUVImage result_(srcData.mWidth, srcData.mHeight, srcData.mStride,
+                   srcData.mWidth, srcData.mHeight, srcData.mStride,
+                   srcData.mWidth, srcData.mHeight, srcData.mStride);
+
+  int rv = BGRA32ToYUV444P(srcData.mData, srcData.mStride,
+                           result_.YBuffer(), result_.mYStride,
+                           result_.UBuffer(), result_.mUStride,
+                           result_.VBuffer(), result_.mVStride,
+                           result_.mYWidth, result_.mYHeight);
+
+  ASSERT_TRUE(rv == 0);
+
+  for (int i = 0; i < srcData.mHeight; ++i) {
+    for (int j = 0; j < srcData.mWidth; ++j) {
+      EXPECT_EQ(dstData.YBuffer()[i * dstData.mYStride + j],
+                result_.YBuffer()[i * result_.mYStride + j]);
+      EXPECT_EQ(dstData.UBuffer()[i * dstData.mUStride + j],
+                result_.UBuffer()[i * result_.mUStride + j]);
+      EXPECT_EQ(dstData.VBuffer()[i * dstData.mVStride + j],
+                result_.VBuffer()[i * result_.mVStride + j]);
+    }
+  }
+}
+
+TEST(ImageBitmapColorUtils, BGRA32ToYUV422P)
+{
+  const BGRA32Data srcData;
+  const YUV422PData dstData;
+
+  YUVImage result_(srcData.mWidth, srcData.mHeight, srcData.mStride,
+                   srcData.mWidth / 2 + 1, srcData.mHeight, srcData.mStride,
+                   srcData.mWidth / 2 + 1, srcData.mHeight, srcData.mStride);
+
+  int rv = BGRA32ToYUV422P(srcData.mData, srcData.mStride,
+                           result_.YBuffer(), result_.mYStride,
+                           result_.UBuffer(), result_.mUStride,
+                           result_.VBuffer(), result_.mVStride,
+                           result_.mYWidth, result_.mYHeight);
+
+  ASSERT_TRUE(rv == 0);
+
+  for (int i = 0; i < dstData.mYHeight; ++i) {
+    for (int j = 0; j < dstData.mYWidth; ++j) {
+      EXPECT_EQ(dstData.YBuffer()[i * dstData.mYStride + j],
+                result_.YBuffer()[i * result_.mYStride + j]);
+    }
+  }
+
+  for (int i = 0; i < dstData.mUHeight; ++i) {
+    for (int j = 0; j < dstData.mUWidth; ++j) {
+      EXPECT_EQ(dstData.UBuffer()[i * dstData.mUStride + j],
+                result_.UBuffer()[i * result_.mUStride + j]);
+      EXPECT_EQ(dstData.VBuffer()[i * dstData.mVStride + j],
+                result_.VBuffer()[i * result_.mVStride + j]);
+    }
+  }
+}
+
+TEST(ImageBitmapColorUtils, BGRA32ToYUV420P)
+{
+  const BGRA32Data srcData;
+  const YUV420PData dstData;
+
+  YUVImage result_(srcData.mWidth, srcData.mHeight, srcData.mStride,
+                   srcData.mWidth / 2 + 1, srcData.mHeight / 2 + 1, srcData.mStride,
+                   srcData.mWidth / 2 + 1, srcData.mHeight / 2 + 1, srcData.mStride);
+
+  int rv = BGRA32ToYUV420P(srcData.mData, srcData.mStride,
+                           result_.YBuffer(), result_.mYStride,
+                           result_.UBuffer(), result_.mUStride,
+                           result_.VBuffer(), result_.mVStride,
+                           result_.mYWidth, result_.mYHeight);
+
+  ASSERT_TRUE(rv == 0);
+
+  for (int i = 0; i < dstData.mYHeight; ++i) {
+    for (int j = 0; j < dstData.mYWidth; ++j) {
+      EXPECT_EQ(dstData.YBuffer()[i * dstData.mYStride + j],
+                result_.YBuffer()[i * result_.mYStride + j]);
+    }
+  }
+
+  for (int i = 0; i < dstData.mUHeight; ++i) {
+    for (int j = 0; j < dstData.mUWidth; ++j) {
+      EXPECT_EQ(dstData.UBuffer()[i * dstData.mUStride + j],
+                result_.UBuffer()[i * result_.mUStride + j]);
+      EXPECT_EQ(dstData.VBuffer()[i * dstData.mVStride + j],
+                result_.VBuffer()[i * result_.mVStride + j]);
+    }
+  }
+}
+
+TEST(ImageBitmapColorUtils, BGRA32ToNV12)
+{
+  const BGRA32Data srcData;
+  const NV12Data dstData;
+
+  NVImage result_(srcData.mWidth, srcData.mHeight, srcData.mStride,
+                  srcData.mWidth / 2 + 1, srcData.mHeight / 2 + 1, srcData.mStride + 10);
+
+  int rv = BGRA32ToNV12(srcData.mData, srcData.mStride,
+                        result_.YBuffer(), result_.mYStride,
+                        result_.UVBuffer(), result_.mUVStride,
+                        result_.mYWidth, result_.mYHeight);
+
+  ASSERT_TRUE(rv == 0);
+
+  for (int i = 0; i < dstData.mYHeight; ++i) {
+    for (int j = 0; j < dstData.mYWidth; ++j) {
+      EXPECT_EQ(dstData.YBuffer()[i * dstData.mYStride + j],
+                result_.YBuffer()[i * result_.mYStride + j]);
+    }
+  }
+
+  for (int i = 0; i < dstData.mUVHeight; ++i) {
+    for (int j = 0; j < dstData.mUVWidth; ++j) {
+      EXPECT_EQ(dstData.UVBuffer()[i * dstData.mUVStride + j * 2 + 0],
+                result_.UVBuffer()[i * result_.mUVStride + j * 2 + 0]);
+      EXPECT_EQ(dstData.UVBuffer()[i * dstData.mUVStride + j * 2 + 1],
+                result_.UVBuffer()[i * result_.mUVStride + j * 2 + 1]);
+    }
+  }
+}
+
+TEST(ImageBitmapColorUtils, BGRA32ToNV21)
+{
+  const BGRA32Data srcData;
+  const NV21Data dstData;
+
+  NVImage result_(srcData.mWidth, srcData.mHeight, srcData.mStride,
+                  srcData.mWidth / 2 + 1, srcData.mHeight / 2 + 1, srcData.mStride + 10);
+
+  int rv = BGRA32ToNV21(srcData.mData, srcData.mStride,
+                        result_.YBuffer(), result_.mYStride,
+                        result_.UVBuffer(), result_.mUVStride,
+                        result_.mYWidth, result_.mYHeight);
+
+  ASSERT_TRUE(rv == 0);
+
+  for (int i = 0; i < dstData.mYHeight; ++i) {
+    for (int j = 0; j < dstData.mYWidth; ++j) {
+      EXPECT_EQ(dstData.YBuffer()[i * dstData.mYStride + j],
+                result_.YBuffer()[i * result_.mYStride + j]);
+    }
+  }
+
+  for (int i = 0; i < dstData.mUVHeight; ++i) {
+    for (int j = 0; j < dstData.mUVWidth; ++j) {
+      EXPECT_EQ(dstData.UVBuffer()[i * dstData.mUVStride + j * 2 + 0],
+                result_.UVBuffer()[i * result_.mUVStride + j * 2 + 0]);
+      EXPECT_EQ(dstData.UVBuffer()[i * dstData.mUVStride + j * 2 + 1],
+                result_.UVBuffer()[i * result_.mUVStride + j * 2 + 1]);
+    }
+  }
+}
+
+TEST(ImageBitmapColorUtils, BGRA32ToHSV)
+{
+  const BGRA32Data srcData;
+  const HSVData dstData;
+
+  SimpleImage<float> result_(srcData.mWidth, srcData.mHeight, 3);
+
+  int rv = BGRA32ToHSV(srcData.mData, srcData.mStride,
+                       result_.mData, result_.mStride,
+                       result_.mWidth, result_.mHeight);
+
+  ASSERT_TRUE(rv == 0);
+
+  for (int i = 0; i < dstData.mHeight; ++i) {
+    for (int j = 0; j < dstData.mWidth; ++j) {
+      for (int c = 0; c < dstData.mChannelCount; ++c) {
+        const double actualValue = result_.GetPixelValue(i, j, c);
+        const double expectedValue = dstData.GetPixelValue(i, j, c);
+        const double diff = std::abs(actualValue - expectedValue);
+        EXPECT_LT(diff, 1.0);
+      }
+    }
+  }
+}
+
+TEST(ImageBitmapColorUtils, BGRA32ToLab)
+{
+  const BGRA32Data srcData;
+  const LabData dstData;
+
+  SimpleImage<float> result_(srcData.mWidth, srcData.mHeight, 3);
+
+  int rv = BGRA32ToLab(srcData.mData, srcData.mStride,
+                       result_.mData, result_.mStride,
+                       result_.mWidth, result_.mHeight);
+
+  ASSERT_TRUE(rv == 0);
+
+  for (int i = 0; i < dstData.mHeight; ++i) {
+    for (int j = 0; j < dstData.mWidth; ++j) {
+      for (int c = 0; c < dstData.mChannelCount; ++c) {
+        const double actualValue = result_.GetPixelValue(i, j, c);
+        const double expectedValue = dstData.GetPixelValue(i, j, c);
+        const double diff = std::abs(actualValue - expectedValue);
+        EXPECT_LT(diff, 1.0);
+      }
+    }
+  }
+}
+
+TEST(ImageBitmapColorUtils, BGRA32ToGray8)
+{
+  const BGRA32Data srcData;
+  const GrayData dstData;
+
+  SimpleImage<uint8_t> result_(srcData.mWidth, srcData.mHeight, 1);
+
+  int rv = BGRA32ToGray8(srcData.mData, srcData.mStride,
+                         result_.mData, result_.mStride,
+                         result_.mWidth, result_.mHeight);
+
+  ASSERT_TRUE(rv == 0);
+
+  for (int i = 0; i < dstData.mHeight; ++i) {
+    for (int j = 0; j < dstData.mWidth; ++j) {
+      EXPECT_EQ(result_.mData[i * result_.mStride + j],
+                dstData.mData[i * dstData.mStride + j]);
+    }
+  }
+}
+
+/*
+ * From YUV444P.
+ */
+TEST(ImageBitmapColorUtils, YUV444PToRGB24)
+{
+  const YUV444PData srcData;
+  const RGBA32DataFromYUV444PData dstData;
+  SimpleImage<uint8_t> result_(srcData.mYWidth, srcData.mYHeight, 3);
+
+  int rv = YUV444PToRGB24(srcData.YBuffer(), srcData.mYStride,
+                          srcData.UBuffer(), srcData.mUStride,
+                          srcData.VBuffer(), srcData.mVStride,
+                          result_.mData, result_.mStride,
+                          result_.mWidth, result_.mHeight);
+  ASSERT_TRUE(rv == 0);
+
+  for (int i = 0; i < srcData.mYHeight; ++i) {
+    for (int j = 0; j < srcData.mYWidth; ++j) {
+      EXPECT_EQ(result_.mData[i * result_.mStride + j * result_.mChannelCount + 0],
+                dstData.GetPixel(j, i)[0]);
+      EXPECT_EQ(result_.mData[i * result_.mStride + j * result_.mChannelCount + 1],
+                dstData.GetPixel(j, i)[1]);
+      EXPECT_EQ(result_.mData[i * result_.mStride + j * result_.mChannelCount + 2],
+                dstData.GetPixel(j, i)[2]);
+    }
+  }
+}
+
+TEST(ImageBitmapColorUtils, YUV444PToBGR24)
+{
+  const YUV444PData srcData;
+  const RGBA32DataFromYUV444PData dstData;
+  SimpleImage<uint8_t> result_(srcData.mYWidth, srcData.mYHeight, 3);
+
+  int rv = YUV444PToBGR24(srcData.YBuffer(), srcData.mYStride,
+                          srcData.UBuffer(), srcData.mUStride,
+                          srcData.VBuffer(), srcData.mVStride,
+                          result_.mData, result_.mStride,
+                          result_.mWidth, result_.mHeight);
+  ASSERT_TRUE(rv == 0);
+
+  for (int i = 0; i < srcData.mYHeight; ++i) {
+    for (int j = 0; j < srcData.mYWidth; ++j) {
+      EXPECT_EQ(result_.mData[i * result_.mStride + j * result_.mChannelCount + 0],
+                dstData.GetPixel(j, i)[2]);
+      EXPECT_EQ(result_.mData[i * result_.mStride + j * result_.mChannelCount + 1],
+                dstData.GetPixel(j, i)[1]);
+      EXPECT_EQ(result_.mData[i * result_.mStride + j * result_.mChannelCount + 2],
+                dstData.GetPixel(j, i)[0]);
+    }
+  }
+}
+
+TEST(ImageBitmapColorUtils, YUV444PToRGBA32)
+{
+  const YUV444PData srcData;
+  const RGBA32DataFromYUV444PData dstData;
+  SimpleImage<uint8_t> result_(srcData.mYWidth, srcData.mYHeight, dstData.mChannelCount);
+
+  int rv = YUV444PToRGBA32(srcData.YBuffer(), srcData.mYStride,
+                           srcData.UBuffer(), srcData.mUStride,
+                           srcData.VBuffer(), srcData.mVStride,
+                           result_.mData, result_.mStride,
+                           result_.mWidth, result_.mHeight);
+  ASSERT_TRUE(rv == 0);
+
+  for (int i = 0; i < srcData.mYHeight; ++i) {
+    for (int j = 0; j < srcData.mYWidth; ++j) {
+      EXPECT_EQ(result_.mData[i * result_.mStride + j * result_.mChannelCount + 0],
+                dstData.GetPixel(j, i)[0]);
+      EXPECT_EQ(result_.mData[i * result_.mStride + j * result_.mChannelCount + 1],
+                dstData.GetPixel(j, i)[1]);
+      EXPECT_EQ(result_.mData[i * result_.mStride + j * result_.mChannelCount + 2],
+                dstData.GetPixel(j, i)[2]);
+      EXPECT_EQ(result_.mData[i * result_.mStride + j * result_.mChannelCount + 3],
+                dstData.GetPixel(j, i)[3]);
+    }
+  }
+}
+
+TEST(ImageBitmapColorUtils, YUV444PToBGRA32)
+{
+  const YUV444PData srcData;
+  const RGBA32DataFromYUV444PData dstData;
+  SimpleImage<uint8_t> result_(srcData.mYWidth, srcData.mYHeight, dstData.mChannelCount);
+
+  int rv = YUV444PToBGRA32(srcData.YBuffer(), srcData.mYStride,
+                           srcData.UBuffer(), srcData.mUStride,
+                           srcData.VBuffer(), srcData.mVStride,
+                           result_.mData, result_.mStride,
+                           result_.mWidth, result_.mHeight);
+  ASSERT_TRUE(rv == 0);
+
+  for (int i = 0; i < srcData.mYHeight; ++i) {
+    for (int j = 0; j < srcData.mYWidth; ++j) {
+      EXPECT_EQ(result_.mData[i * result_.mStride + j * result_.mChannelCount + 0],
+                dstData.GetPixel(j, i)[2]);
+      EXPECT_EQ(result_.mData[i * result_.mStride + j * result_.mChannelCount + 1],
+                dstData.GetPixel(j, i)[1]);
+      EXPECT_EQ(result_.mData[i * result_.mStride + j * result_.mChannelCount + 2],
+                dstData.GetPixel(j, i)[0]);
+      EXPECT_EQ(result_.mData[i * result_.mStride + j * result_.mChannelCount + 3],
+                dstData.GetPixel(j, i)[3]);
+    }
+  }
+}
+
+TEST(ImageBitmapColorUtils, YUV444PToGray8)
+{
+  const YUV444PData srcData;
+  SimpleImage<uint8_t> result_(srcData.mYWidth, srcData.mYHeight, 1);
+
+  int rv = YUV444PToGray8(srcData.YBuffer(), srcData.mYStride,
+                          srcData.UBuffer(), srcData.mUStride,
+                          srcData.VBuffer(), srcData.mVStride,
+                          result_.mData, result_.mStride,
+                          result_.mWidth, result_.mHeight);
+
+  ASSERT_TRUE(rv == 0);
+
+  for (int i = 0; i < srcData.mYHeight; ++i) {
+    for (int j = 0; j < srcData.mYWidth; ++j) {
+      EXPECT_EQ(result_.mData[i * result_.mStride + j],
+                srcData.YBuffer()[i * srcData.mYStride + j]);
+    }
+  }
+}
+
+/*
+ * From YUV422P.
+ */
+TEST(ImageBitmapColorUtils, YUV422PToRGB24)
+{
+  const YUV422PData srcData;
+  const RGBA32DataFromYUV422PData dstData;
+  SimpleImage<uint8_t> result_(srcData.mYWidth, srcData.mYHeight, 3);
+
+  int rv = YUV422PToRGB24(srcData.YBuffer(), srcData.mYStride,
+                          srcData.UBuffer(), srcData.mUStride,
+                          srcData.VBuffer(), srcData.mVStride,
+                          result_.mData, result_.mStride,
+                          result_.mWidth, result_.mHeight);
+  ASSERT_TRUE(rv == 0);
+
+  for (int i = 0; i < srcData.mYHeight; ++i) {
+    for (int j = 0; j < srcData.mYWidth; ++j) {
+      EXPECT_EQ(result_.mData[i * result_.mStride + j * result_.mChannelCount + 0],
+                dstData.GetPixel(j, i)[0]);
+      EXPECT_EQ(result_.mData[i * result_.mStride + j * result_.mChannelCount + 1],
+                dstData.GetPixel(j, i)[1]);
+      EXPECT_EQ(result_.mData[i * result_.mStride + j * result_.mChannelCount + 2],
+                dstData.GetPixel(j, i)[2]);
+    }
+  }
+}
+
+TEST(ImageBitmapColorUtils, YUV422PToBGR24)
+{
+  const YUV422PData srcData;
+  const RGBA32DataFromYUV422PData dstData;
+  SimpleImage<uint8_t> result_(srcData.mYWidth, srcData.mYHeight, 3);
+
+  int rv = YUV422PToBGR24(srcData.YBuffer(), srcData.mYStride,
+                          srcData.UBuffer(), srcData.mUStride,
+                          srcData.VBuffer(), srcData.mVStride,
+                          result_.mData, result_.mStride,
+                          result_.mWidth, result_.mHeight);
+  ASSERT_TRUE(rv == 0);
+
+  for (int i = 0; i < srcData.mYHeight; ++i) {
+    for (int j = 0; j < srcData.mYWidth; ++j) {
+      EXPECT_EQ(result_.mData[i * result_.mStride + j * result_.mChannelCount + 0],
+                dstData.GetPixel(j, i)[2]);
+      EXPECT_EQ(result_.mData[i * result_.mStride + j * result_.mChannelCount + 1],
+                dstData.GetPixel(j, i)[1]);
+      EXPECT_EQ(result_.mData[i * result_.mStride + j * result_.mChannelCount + 2],
+                dstData.GetPixel(j, i)[0]);
+    }
+  }
+}
+
+TEST(ImageBitmapColorUtils, YUV422PToRGBA32)
+{
+  const YUV422PData srcData;
+  const RGBA32DataFromYUV422PData dstData;
+  SimpleImage<uint8_t> result_(srcData.mYWidth, srcData.mYHeight, dstData.mChannelCount);
+
+  int rv = YUV422PToRGBA32(srcData.YBuffer(), srcData.mYStride,
+                           srcData.UBuffer(), srcData.mUStride,
+                           srcData.VBuffer(), srcData.mVStride,
+                           result_.mData, result_.mStride,
+                           result_.mWidth, result_.mHeight);
+  ASSERT_TRUE(rv == 0);
+
+  for (int i = 0; i < srcData.mYHeight; ++i) {
+    for (int j = 0; j < srcData.mYWidth; ++j) {
+      EXPECT_EQ(result_.mData[i * result_.mStride + j * result_.mChannelCount + 0],
+                dstData.GetPixel(j, i)[0]);
+      EXPECT_EQ(result_.mData[i * result_.mStride + j * result_.mChannelCount + 1],
+                dstData.GetPixel(j, i)[1]);
+      EXPECT_EQ(result_.mData[i * result_.mStride + j * result_.mChannelCount + 2],
+                dstData.GetPixel(j, i)[2]);
+      EXPECT_EQ(result_.mData[i * result_.mStride + j * result_.mChannelCount + 3],
+                dstData.GetPixel(j, i)[3]);
+    }
+  }
+}
+
+TEST(ImageBitmapColorUtils, YUV422PToBGRA32)
+{
+  const YUV422PData srcData;
+  const RGBA32DataFromYUV422PData dstData;
+  SimpleImage<uint8_t> result_(srcData.mYWidth, srcData.mYHeight, dstData.mChannelCount);
+
+  int rv = YUV422PToBGRA32(srcData.YBuffer(), srcData.mYStride,
+                           srcData.UBuffer(), srcData.mUStride,
+                           srcData.VBuffer(), srcData.mVStride,
+                           result_.mData, result_.mStride,
+                           result_.mWidth, result_.mHeight);
+  ASSERT_TRUE(rv == 0);
+
+  for (int i = 0; i < srcData.mYHeight; ++i) {
+    for (int j = 0; j < srcData.mYWidth; ++j) {
+      EXPECT_EQ(result_.mData[i * result_.mStride + j * result_.mChannelCount + 0],
+                dstData.GetPixel(j, i)[2]);
+      EXPECT_EQ(result_.mData[i * result_.mStride + j * result_.mChannelCount + 1],
+                dstData.GetPixel(j, i)[1]);
+      EXPECT_EQ(result_.mData[i * result_.mStride + j * result_.mChannelCount + 2],
+                dstData.GetPixel(j, i)[0]);
+      EXPECT_EQ(result_.mData[i * result_.mStride + j * result_.mChannelCount + 3],
+                dstData.GetPixel(j, i)[3]);
+    }
+  }
+}
+
+TEST(ImageBitmapColorUtils, YUV422PToGray8)
+{
+  const YUV422PData srcData;
+  SimpleImage<uint8_t> result_(srcData.mYWidth, srcData.mYHeight, 1);
+
+  int rv = YUV422PToGray8(srcData.YBuffer(), srcData.mYStride,
+                          srcData.UBuffer(), srcData.mUStride,
+                          srcData.VBuffer(), srcData.mVStride,
+                          result_.mData, result_.mStride,
+                          result_.mWidth, result_.mHeight);
+
+  ASSERT_TRUE(rv == 0);
+
+  for (int i = 0; i < srcData.mYHeight; ++i) {
+    for (int j = 0; j < srcData.mYWidth; ++j) {
+      EXPECT_EQ(result_.mData[i * result_.mStride + j],
+                srcData.YBuffer()[i * srcData.mYStride + j]);
+    }
+  }
+}
+
+/*
+ * From YUV420P.
+ */
+TEST(ImageBitmapColorUtils, YUV420PToRGB24)
+{
+  const YUV420PData srcData;
+  const RGBA32DataFromYUV420PData dstData;
+  SimpleImage<uint8_t> result_(srcData.mYWidth, srcData.mYHeight, 3);
+
+  int rv = YUV420PToRGB24(srcData.YBuffer(), srcData.mYStride,
+                          srcData.UBuffer(), srcData.mUStride,
+                          srcData.VBuffer(), srcData.mVStride,
+                          result_.mData, result_.mStride,
+                          result_.mWidth, result_.mHeight);
+  ASSERT_TRUE(rv == 0);
+
+  for (int i = 0; i < srcData.mYHeight; ++i) {
+    for (int j = 0; j < srcData.mYWidth; ++j) {
+      EXPECT_EQ(result_.mData[i * result_.mStride + j * result_.mChannelCount + 0],
+                dstData.GetPixel(j, i)[0]);
+      EXPECT_EQ(result_.mData[i * result_.mStride + j * result_.mChannelCount + 1],
+                dstData.GetPixel(j, i)[1]);
+      EXPECT_EQ(result_.mData[i * result_.mStride + j * result_.mChannelCount + 2],
+                dstData.GetPixel(j, i)[2]);
+    }
+  }
+}
+
+TEST(ImageBitmapColorUtils, YUV420PToBGR24)
+{
+  const YUV420PData srcData;
+  const RGBA32DataFromYUV420PData dstData;
+  SimpleImage<uint8_t> result_(srcData.mYWidth, srcData.mYHeight, 3);
+
+  int rv = YUV420PToBGR24(srcData.YBuffer(), srcData.mYStride,
+                          srcData.UBuffer(), srcData.mUStride,
+                          srcData.VBuffer(), srcData.mVStride,
+                          result_.mData, result_.mStride,
+                          result_.mWidth, result_.mHeight);
+  ASSERT_TRUE(rv == 0);
+
+  for (int i = 0; i < srcData.mYHeight; ++i) {
+    for (int j = 0; j < srcData.mYWidth; ++j) {
+      EXPECT_EQ(result_.mData[i * result_.mStride + j * result_.mChannelCount + 0],
+                dstData.GetPixel(j, i)[2]);
+      EXPECT_EQ(result_.mData[i * result_.mStride + j * result_.mChannelCount + 1],
+                dstData.GetPixel(j, i)[1]);
+      EXPECT_EQ(result_.mData[i * result_.mStride + j * result_.mChannelCount + 2],
+                dstData.GetPixel(j, i)[0]);
+    }
+  }
+}
+
+TEST(ImageBitmapColorUtils, YUV420PToRGBA32)
+{
+  const YUV420PData srcData;
+  const RGBA32DataFromYUV420PData dstData;
+  SimpleImage<uint8_t> result_(srcData.mYWidth, srcData.mYHeight, dstData.mChannelCount);
+
+  int rv = YUV420PToRGBA32(srcData.YBuffer(), srcData.mYStride,
+                           srcData.UBuffer(), srcData.mUStride,
+                           srcData.VBuffer(), srcData.mVStride,
+                           result_.mData, result_.mStride,
+                           result_.mWidth, result_.mHeight);
+  ASSERT_TRUE(rv == 0);
+
+  for (int i = 0; i < srcData.mYHeight; ++i) {
+    for (int j = 0; j < srcData.mYWidth; ++j) {
+      EXPECT_EQ(result_.mData[i * result_.mStride + j * result_.mChannelCount + 0],
+                dstData.GetPixel(j, i)[0]);
+      EXPECT_EQ(result_.mData[i * result_.mStride + j * result_.mChannelCount + 1],
+                dstData.GetPixel(j, i)[1]);
+      EXPECT_EQ(result_.mData[i * result_.mStride + j * result_.mChannelCount + 2],
+                dstData.GetPixel(j, i)[2]);
+      EXPECT_EQ(result_.mData[i * result_.mStride + j * result_.mChannelCount + 3],
+                dstData.GetPixel(j, i)[3]);
+    }
+  }
+}
+
+TEST(ImageBitmapColorUtils, YUV420PToBGRA32)
+{
+  const YUV420PData srcData;
+  const RGBA32DataFromYUV420PData dstData;
+  SimpleImage<uint8_t> result_(srcData.mYWidth, srcData.mYHeight, dstData.mChannelCount);
+
+  int rv = YUV420PToBGRA32(srcData.YBuffer(), srcData.mYStride,
+                           srcData.UBuffer(), srcData.mUStride,
+                           srcData.VBuffer(), srcData.mVStride,
+                           result_.mData, result_.mStride,
+                           result_.mWidth, result_.mHeight);
+  ASSERT_TRUE(rv == 0);
+
+  for (int i = 0; i < srcData.mYHeight; ++i) {
+    for (int j = 0; j < srcData.mYWidth; ++j) {
+      EXPECT_EQ(result_.mData[i * result_.mStride + j * result_.mChannelCount + 0],
+                dstData.GetPixel(j, i)[2]);
+      EXPECT_EQ(result_.mData[i * result_.mStride + j * result_.mChannelCount + 1],
+                dstData.GetPixel(j, i)[1]);
+      EXPECT_EQ(result_.mData[i * result_.mStride + j * result_.mChannelCount + 2],
+                dstData.GetPixel(j, i)[0]);
+      EXPECT_EQ(result_.mData[i * result_.mStride + j * result_.mChannelCount + 3],
+                dstData.GetPixel(j, i)[3]);
+    }
+  }
+}
+
+TEST(ImageBitmapColorUtils, YUV420PToGray8)
+{
+  const YUV420PData srcData;
+  SimpleImage<uint8_t> result_(srcData.mYWidth, srcData.mYHeight, 1);
+
+  int rv = YUV420PToGray8(srcData.YBuffer(), srcData.mYStride,
+                          srcData.UBuffer(), srcData.mUStride,
+                          srcData.VBuffer(), srcData.mVStride,
+                          result_.mData, result_.mStride,
+                          result_.mWidth, result_.mHeight);
+
+  ASSERT_TRUE(rv == 0);
+
+  for (int i = 0; i < srcData.mYHeight; ++i) {
+    for (int j = 0; j < srcData.mYWidth; ++j) {
+      EXPECT_EQ(result_.mData[i * result_.mStride + j],
+                srcData.YBuffer()[i * srcData.mYStride + j]);
+    }
+  }
+}
+
+/*
+ * From NV12.
+ */
+TEST(ImageBitmapColorUtils, NV12ToRGB24)
+{
+  const NV12Data srcData;
+  const RGBA32DataFromYUV420PData dstData;
+  SimpleImage<uint8_t> result_(srcData.mYWidth, srcData.mYHeight, 3);
+
+  int rv = NV12ToRGB24(srcData.YBuffer(), srcData.mYStride,
+                          srcData.UVBuffer(), srcData.mUVStride,
+                          result_.mData, result_.mStride,
+                          result_.mWidth, result_.mHeight);
+  ASSERT_TRUE(rv == 0);
+
+  for (int i = 0; i < srcData.mYHeight; ++i) {
+    for (int j = 0; j < srcData.mYWidth; ++j) {
+      EXPECT_EQ(result_.mData[i * result_.mStride + j * result_.mChannelCount + 0],
+                dstData.GetPixel(j, i)[0]);
+      EXPECT_EQ(result_.mData[i * result_.mStride + j * result_.mChannelCount + 1],
+                dstData.GetPixel(j, i)[1]);
+      EXPECT_EQ(result_.mData[i * result_.mStride + j * result_.mChannelCount + 2],
+                dstData.GetPixel(j, i)[2]);
+    }
+  }
+}
+
+TEST(ImageBitmapColorUtils, NV12ToBGR24)
+{
+  const NV12Data srcData;
+  const RGBA32DataFromYUV420PData dstData;
+  SimpleImage<uint8_t> result_(srcData.mYWidth, srcData.mYHeight, 3);
+
+  int rv = NV12ToBGR24(srcData.YBuffer(), srcData.mYStride,
+                          srcData.UVBuffer(), srcData.mUVStride,
+                          result_.mData, result_.mStride,
+                          result_.mWidth, result_.mHeight);
+  ASSERT_TRUE(rv == 0);
+
+  for (int i = 0; i < srcData.mYHeight; ++i) {
+    for (int j = 0; j < srcData.mYWidth; ++j) {
+      EXPECT_EQ(result_.mData[i * result_.mStride + j * result_.mChannelCount + 0],
+                dstData.GetPixel(j, i)[2]);
+      EXPECT_EQ(result_.mData[i * result_.mStride + j * result_.mChannelCount + 1],
+                dstData.GetPixel(j, i)[1]);
+      EXPECT_EQ(result_.mData[i * result_.mStride + j * result_.mChannelCount + 2],
+                dstData.GetPixel(j, i)[0]);
+    }
+  }
+}
+
+TEST(ImageBitmapColorUtils, NV12ToRGBA32)
+{
+  const NV12Data srcData;
+  const RGBA32DataFromYUV420PData dstData;
+  SimpleImage<uint8_t> result_(srcData.mYWidth, srcData.mYHeight, dstData.mChannelCount);
+
+  int rv = NV12ToRGBA32(srcData.YBuffer(), srcData.mYStride,
+                           srcData.UVBuffer(), srcData.mUVStride,
+                           result_.mData, result_.mStride,
+                           result_.mWidth, result_.mHeight);
+  ASSERT_TRUE(rv == 0);
+
+  for (int i = 0; i < srcData.mYHeight; ++i) {
+    for (int j = 0; j < srcData.mYWidth; ++j) {
+      EXPECT_EQ(result_.mData[i * result_.mStride + j * result_.mChannelCount + 0],
+                dstData.GetPixel(j, i)[0]);
+      EXPECT_EQ(result_.mData[i * result_.mStride + j * result_.mChannelCount + 1],
+                dstData.GetPixel(j, i)[1]);
+      EXPECT_EQ(result_.mData[i * result_.mStride + j * result_.mChannelCount + 2],
+                dstData.GetPixel(j, i)[2]);
+      EXPECT_EQ(result_.mData[i * result_.mStride + j * result_.mChannelCount + 3],
+                dstData.GetPixel(j, i)[3]);
+    }
+  }
+}
+
+TEST(ImageBitmapColorUtils, NV12ToBGRA32)
+{
+  const NV12Data srcData;
+  const RGBA32DataFromYUV420PData dstData;
+  SimpleImage<uint8_t> result_(srcData.mYWidth, srcData.mYHeight, dstData.mChannelCount);
+
+  int rv = NV12ToBGRA32(srcData.YBuffer(), srcData.mYStride,
+                           srcData.UVBuffer(), srcData.mUVStride,
+                           result_.mData, result_.mStride,
+                           result_.mWidth, result_.mHeight);
+  ASSERT_TRUE(rv == 0);
+
+  for (int i = 0; i < srcData.mYHeight; ++i) {
+    for (int j = 0; j < srcData.mYWidth; ++j) {
+      EXPECT_EQ(result_.mData[i * result_.mStride + j * result_.mChannelCount + 0],
+                dstData.GetPixel(j, i)[2]);
+      EXPECT_EQ(result_.mData[i * result_.mStride + j * result_.mChannelCount + 1],
+                dstData.GetPixel(j, i)[1]);
+      EXPECT_EQ(result_.mData[i * result_.mStride + j * result_.mChannelCount + 2],
+                dstData.GetPixel(j, i)[0]);
+      EXPECT_EQ(result_.mData[i * result_.mStride + j * result_.mChannelCount + 3],
+                dstData.GetPixel(j, i)[3]);
+    }
+  }
+}
+
+TEST(ImageBitmapColorUtils, NV12ToGray8)
+{
+  const NV12Data srcData;
+  SimpleImage<uint8_t> result_(srcData.mYWidth, srcData.mYHeight, 1);
+
+  int rv = NV12ToGray8(srcData.YBuffer(), srcData.mYStride,
+                       srcData.UVBuffer(), srcData.mUVStride,
+                       result_.mData, result_.mStride,
+                       result_.mWidth, result_.mHeight);
+
+  ASSERT_TRUE(rv == 0);
+
+  for (int i = 0; i < srcData.mYHeight; ++i) {
+    for (int j = 0; j < srcData.mYWidth; ++j) {
+      EXPECT_EQ(result_.mData[i * result_.mStride + j],
+                srcData.YBuffer()[i * srcData.mYStride + j]);
+    }
+  }
+}
+
+/*
+ * From NV21.
+ */
+TEST(ImageBitmapColorUtils, NV21ToRGB24)
+{
+  const NV21Data srcData;
+  const RGBA32DataFromYUV420PData dstData;
+  SimpleImage<uint8_t> result_(srcData.mYWidth, srcData.mYHeight, 3);
+
+  int rv = NV21ToRGB24(srcData.YBuffer(), srcData.mYStride,
+                          srcData.UVBuffer(), srcData.mUVStride,
+                          result_.mData, result_.mStride,
+                          result_.mWidth, result_.mHeight);
+  ASSERT_TRUE(rv == 0);
+
+  for (int i = 0; i < srcData.mYHeight; ++i) {
+    for (int j = 0; j < srcData.mYWidth; ++j) {
+      EXPECT_EQ(result_.mData[i * result_.mStride + j * result_.mChannelCount + 0],
+                dstData.GetPixel(j, i)[0]);
+      EXPECT_EQ(result_.mData[i * result_.mStride + j * result_.mChannelCount + 1],
+                dstData.GetPixel(j, i)[1]);
+      EXPECT_EQ(result_.mData[i * result_.mStride + j * result_.mChannelCount + 2],
+                dstData.GetPixel(j, i)[2]);
+    }
+  }
+}
+
+TEST(ImageBitmapColorUtils, NV21ToBGR24)
+{
+  const NV21Data srcData;
+  const RGBA32DataFromYUV420PData dstData;
+  SimpleImage<uint8_t> result_(srcData.mYWidth, srcData.mYHeight, 3);
+
+  int rv = NV21ToBGR24(srcData.YBuffer(), srcData.mYStride,
+                          srcData.UVBuffer(), srcData.mUVStride,
+                          result_.mData, result_.mStride,
+                          result_.mWidth, result_.mHeight);
+  ASSERT_TRUE(rv == 0);
+
+  for (int i = 0; i < srcData.mYHeight; ++i) {
+    for (int j = 0; j < srcData.mYWidth; ++j) {
+      EXPECT_EQ(result_.mData[i * result_.mStride + j * result_.mChannelCount + 0],
+                dstData.GetPixel(j, i)[2]);
+      EXPECT_EQ(result_.mData[i * result_.mStride + j * result_.mChannelCount + 1],
+                dstData.GetPixel(j, i)[1]);
+      EXPECT_EQ(result_.mData[i * result_.mStride + j * result_.mChannelCount + 2],
+                dstData.GetPixel(j, i)[0]);
+    }
+  }
+}
+
+TEST(ImageBitmapColorUtils, NV21ToRGBA32)
+{
+  const NV21Data srcData;
+  const RGBA32DataFromYUV420PData dstData;
+  SimpleImage<uint8_t> result_(srcData.mYWidth, srcData.mYHeight, dstData.mChannelCount);
+
+  int rv = NV21ToRGBA32(srcData.YBuffer(), srcData.mYStride,
+                           srcData.UVBuffer(), srcData.mUVStride,
+                           result_.mData, result_.mStride,
+                           result_.mWidth, result_.mHeight);
+  ASSERT_TRUE(rv == 0);
+
+  for (int i = 0; i < srcData.mYHeight; ++i) {
+    for (int j = 0; j < srcData.mYWidth; ++j) {
+      EXPECT_EQ(result_.mData[i * result_.mStride + j * result_.mChannelCount + 0],
+                dstData.GetPixel(j, i)[0]);
+      EXPECT_EQ(result_.mData[i * result_.mStride + j * result_.mChannelCount + 1],
+                dstData.GetPixel(j, i)[1]);
+      EXPECT_EQ(result_.mData[i * result_.mStride + j * result_.mChannelCount + 2],
+                dstData.GetPixel(j, i)[2]);
+      EXPECT_EQ(result_.mData[i * result_.mStride + j * result_.mChannelCount + 3],
+                dstData.GetPixel(j, i)[3]);
+    }
+  }
+}
+
+TEST(ImageBitmapColorUtils, NV21ToBGRA32)
+{
+  const NV21Data srcData;
+  const RGBA32DataFromYUV420PData dstData;
+  SimpleImage<uint8_t> result_(srcData.mYWidth, srcData.mYHeight, dstData.mChannelCount);
+
+  int rv = NV21ToBGRA32(srcData.YBuffer(), srcData.mYStride,
+                           srcData.UVBuffer(), srcData.mUVStride,
+                           result_.mData, result_.mStride,
+                           result_.mWidth, result_.mHeight);
+  ASSERT_TRUE(rv == 0);
+
+  for (int i = 0; i < srcData.mYHeight; ++i) {
+    for (int j = 0; j < srcData.mYWidth; ++j) {
+      EXPECT_EQ(result_.mData[i * result_.mStride + j * result_.mChannelCount + 0],
+                dstData.GetPixel(j, i)[2]);
+      EXPECT_EQ(result_.mData[i * result_.mStride + j * result_.mChannelCount + 1],
+                dstData.GetPixel(j, i)[1]);
+      EXPECT_EQ(result_.mData[i * result_.mStride + j * result_.mChannelCount + 2],
+                dstData.GetPixel(j, i)[0]);
+      EXPECT_EQ(result_.mData[i * result_.mStride + j * result_.mChannelCount + 3],
+                dstData.GetPixel(j, i)[3]);
+    }
+  }
+}
+
+TEST(ImageBitmapColorUtils, NV21ToGray8)
+{
+  const NV21Data srcData;
+  SimpleImage<uint8_t> result_(srcData.mYWidth, srcData.mYHeight, 1);
+
+  int rv = NV21ToGray8(srcData.YBuffer(), srcData.mYStride,
+                       srcData.UVBuffer(), srcData.mUVStride,
+                       result_.mData, result_.mStride,
+                       result_.mWidth, result_.mHeight);
+
+  ASSERT_TRUE(rv == 0);
+
+  for (int i = 0; i < srcData.mYHeight; ++i) {
+    for (int j = 0; j < srcData.mYWidth; ++j) {
+      EXPECT_EQ(result_.mData[i * result_.mStride + j],
+                srcData.YBuffer()[i * srcData.mYStride + j]);
+    }
+  }
+}
+
+/*
+ * From HSV.
+ */
+TEST(ImageBitmapColorUtils, HSVToRGB24)
+{
+  const HSVData srcData;
+  const RGB24Data dstData;
+  SimpleImage<uint8_t> result_(srcData.mWidth, srcData.mHeight, dstData.mChannelCount);
+
+  int rv = HSVToRGB24(srcData.mData, srcData.mStride,
+                      result_.mData, result_.mStride,
+                      result_.mWidth, result_.mHeight);
+
+  ASSERT_TRUE(rv == 0);
+
+  for (int i = 0; i < srcData.mHeight; ++i) {
+    for (int j = 0; j < srcData.mWidth; ++j) {
+      for (int c = 0; c < dstData.mChannelCount; ++c) {
+        EXPECT_EQ(result_.GetPixelValue(i, j, c),
+                  dstData.GetPixelValue(i, j, c));
+      }
+    }
+  }
+}
+
+TEST(ImageBitmapColorUtils, HSVToBGR24)
+{
+  const HSVData srcData;
+  const BGR24Data dstData;
+  SimpleImage<uint8_t> result_(srcData.mWidth, srcData.mHeight, dstData.mChannelCount);
+
+  int rv = HSVToBGR24(srcData.mData, srcData.mStride,
+                      result_.mData, result_.mStride,
+                      result_.mWidth, result_.mHeight);
+
+  ASSERT_TRUE(rv == 0);
+
+  for (int i = 0; i < srcData.mHeight; ++i) {
+    for (int j = 0; j < srcData.mWidth; ++j) {
+      for (int c = 0; c < dstData.mChannelCount; ++c) {
+        EXPECT_EQ(result_.GetPixelValue(i, j, c),
+                  dstData.GetPixelValue(i, j, c));
+      }
+    }
+  }
+}
+
+TEST(ImageBitmapColorUtils, HSVToRGBA32)
+{
+  const HSVData srcData;
+  const RGBA32Data dstData;
+  SimpleImage<uint8_t> result_(srcData.mWidth, srcData.mHeight, dstData.mChannelCount);
+
+  int rv = HSVToRGBA32(srcData.mData, srcData.mStride,
+                       result_.mData, result_.mStride,
+                       result_.mWidth, result_.mHeight);
+
+  ASSERT_TRUE(rv == 0);
+
+  for (int i = 0; i < srcData.mHeight; ++i) {
+    for (int j = 0; j < srcData.mWidth; ++j) {
+      for (int c = 0; c < dstData.mChannelCount; ++c) {
+        EXPECT_EQ(result_.GetPixelValue(i, j, c),
+                  dstData.GetPixelValue(i, j, c));
+      }
+    }
+  }
+}
+
+TEST(ImageBitmapColorUtils, HSVToBGRA32)
+{
+  const HSVData srcData;
+  const BGRA32Data dstData;
+  SimpleImage<uint8_t> result_(srcData.mWidth, srcData.mHeight, dstData.mChannelCount);
+
+  int rv = HSVToBGRA32(srcData.mData, srcData.mStride,
+                       result_.mData, result_.mStride,
+                       result_.mWidth, result_.mHeight);
+
+  ASSERT_TRUE(rv == 0);
+
+  for (int i = 0; i < srcData.mHeight; ++i) {
+    for (int j = 0; j < srcData.mWidth; ++j) {
+      for (int c = 0; c < dstData.mChannelCount; ++c) {
+        EXPECT_EQ(result_.GetPixelValue(i, j, c),
+                  dstData.GetPixelValue(i, j, c));
+      }
+    }
+  }
+}
+
+/*
+ * From Lab.
+ */
+TEST(ImageBitmapColorUtils, LabToRGB24)
+{
+  const LabData srcData;
+  const RGB24Data dstData;
+  SimpleImage<uint8_t> result_(srcData.mWidth, srcData.mHeight, dstData.mChannelCount);
+
+  int rv = LabToRGB24(srcData.mData, srcData.mStride,
+                      result_.mData, result_.mStride,
+                      result_.mWidth, result_.mHeight);
+
+  ASSERT_TRUE(rv == 0);
+
+  for (int i = 0; i < srcData.mHeight; ++i) {
+    for (int j = 0; j < srcData.mWidth; ++j) {
+      for (int c = 0; c < dstData.mChannelCount; ++c) {
+        const uint8_t actualValue = result_.GetPixelValue(i, j, c);
+        const uint8_t expectedValue = dstData.GetPixelValue(i, j, c);
+        const int diff = std::abs(actualValue - expectedValue);
+        EXPECT_LE(diff, 1);
+      }
+    }
+  }
+}
+
+TEST(ImageBitmapColorUtils, LabToBGR24)
+{
+  const LabData srcData;
+  const BGR24Data dstData;
+  SimpleImage<uint8_t> result_(srcData.mWidth, srcData.mHeight, dstData.mChannelCount);
+
+  int rv = LabToBGR24(srcData.mData, srcData.mStride,
+                      result_.mData, result_.mStride,
+                      result_.mWidth, result_.mHeight);
+
+  ASSERT_TRUE(rv == 0);
+
+  for (int i = 0; i < srcData.mHeight; ++i) {
+    for (int j = 0; j < srcData.mWidth; ++j) {
+      for (int c = 0; c < dstData.mChannelCount; ++c) {
+        const uint8_t actualValue = result_.GetPixelValue(i, j, c);
+        const uint8_t expectedValue = dstData.GetPixelValue(i, j, c);
+        const int diff = std::abs(actualValue - expectedValue);
+        EXPECT_LE(diff, 1);
+      }
+    }
+  }
+}
+
+TEST(ImageBitmapColorUtils, LabToRGBA32)
+{
+  const LabData srcData;
+  const RGBA32Data dstData;
+  SimpleImage<uint8_t> result_(srcData.mWidth, srcData.mHeight, dstData.mChannelCount);
+
+  int rv = LabToRGBA32(srcData.mData, srcData.mStride,
+                       result_.mData, result_.mStride,
+                       result_.mWidth, result_.mHeight);
+
+  ASSERT_TRUE(rv == 0);
+
+  for (int i = 0; i < srcData.mHeight; ++i) {
+    for (int j = 0; j < srcData.mWidth; ++j) {
+      for (int c = 0; c < dstData.mChannelCount; ++c) {
+        const uint8_t actualValue = result_.GetPixelValue(i, j, c);
+        const uint8_t expectedValue = dstData.GetPixelValue(i, j, c);
+        const int diff = std::abs(actualValue - expectedValue);
+        EXPECT_LE(diff, 1);
+      }
+    }
+  }
+}
+
+TEST(ImageBitmapColorUtils, LabToBGRA32)
+{
+  const LabData srcData;
+  const BGRA32Data dstData;
+  SimpleImage<uint8_t> result_(srcData.mWidth, srcData.mHeight, dstData.mChannelCount);
+
+  int rv = LabToBGRA32(srcData.mData, srcData.mStride,
+                       result_.mData, result_.mStride,
+                       result_.mWidth, result_.mHeight);
+
+  ASSERT_TRUE(rv == 0);
+
+  for (int i = 0; i < srcData.mHeight; ++i) {
+    for (int j = 0; j < srcData.mWidth; ++j) {
+      for (int c = 0; c < dstData.mChannelCount; ++c) {
+        const uint8_t actualValue = result_.GetPixelValue(i, j, c);
+        const uint8_t expectedValue = dstData.GetPixelValue(i, j, c);
+        const int diff = std::abs(actualValue - expectedValue);
+        EXPECT_LE(diff, 1);
+      }
+    }
+  }
+}
new file mode 100644
--- /dev/null
+++ b/dom/canvas/gtest/moz.build
@@ -0,0 +1,16 @@
+# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+UNIFIED_SOURCES += [
+    'TestImageBitmapColorUtils.cpp',
+]
+
+LOCAL_INCLUDES += [
+    '/dom/canvas',
+    '/media/libyuv/include'
+]
+
+FINAL_LIBRARY = 'xul-gtest'
--- a/dom/canvas/moz.build
+++ b/dom/canvas/moz.build
@@ -1,15 +1,18 @@
 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # 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/.
 
-TEST_DIRS += ['compiledtest']
+TEST_DIRS += [
+    'compiledtest', 
+    'gtest'
+]
 
 # Change the following line to avoid bug 1081323 (clobber after changing a manifest):
 # Add test for webglcontextcreationerror.
 
 MOCHITEST_MANIFESTS += [
     'test/crash/mochitest.ini',
     'test/crossorigin/mochitest.ini',
     'test/mochitest.ini',
@@ -48,17 +51,19 @@ EXPORTS.mozilla.dom += [
 UNIFIED_SOURCES += [
     'CanvasImageCache.cpp',
     'CanvasRenderingContext2D.cpp',
     'CanvasRenderingContextHelper.cpp',
     'CanvasUtils.cpp',
     'DocumentRendererChild.cpp',
     'DocumentRendererParent.cpp',
     'ImageBitmap.cpp',
+    'ImageBitmapColorUtils.cpp',
     'ImageBitmapRenderingContext.cpp',
+    'ImageBitmapUtils.cpp',
     'ImageData.cpp',
     'OffscreenCanvas.cpp',
 ]
 
 # WebGL Sources
 UNIFIED_SOURCES += [
     'TexUnpackBlob.cpp',
     'WebGL1Context.cpp',