Bug 1141979 - part6 - implement ImageBitmapFormatUtils; r=jrmuizel draft
authorKaku Kuo <tkuo@mozilla.com>
Mon, 30 May 2016 15:48:02 +0800
changeset 373845 0953c1155d0f8dadd568913279cbd2a1df007bae
parent 373844 7a249cc2eb0ac7749ddf75ce0d4b52d511b25424
child 373846 a29bb0c3de585bdc577d94c1416f044132e669fd
push id19853
push usertkuo@mozilla.com
push dateWed, 01 Jun 2016 09:17:41 +0000
reviewersjrmuizel
bugs1141979
milestone49.0a1
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',