Bug 1141979 - part16 - mochitest - color conversion; r=jrmuizel draft
authorKaku Kuo <tkuo@mozilla.com>
Mon, 30 May 2016 15:48:33 +0800
changeset 373855 e16b5ba233e2aa9b029f8896fae2185c436c641d
parent 373854 f6d2323fc722d73b9dbed25cf4992e3978b2cc0a
child 373856 27f415c03ed4c165e66b364d99b4cc52f0d4cda4
push id19853
push usertkuo@mozilla.com
push dateWed, 01 Jun 2016 09:17:41 +0000
reviewersjrmuizel
bugs1141979
milestone49.0a1
Bug 1141979 - part16 - mochitest - color conversion; r=jrmuizel MozReview-Commit-ID: Eiuaq6Kmeaj
dom/canvas/test/imagebitmap_extensions.html
dom/canvas/test/imagebitmap_extensions.js
dom/canvas/test/imagebitmap_extensions_data.js
dom/canvas/test/imagebitmap_extensions_on_worker.js
dom/canvas/test/mochitest.ini
--- a/dom/canvas/test/imagebitmap_extensions.html
+++ b/dom/canvas/test/imagebitmap_extensions.html
@@ -2,29 +2,31 @@
 <head>
   <title>Test ImageBitmap Extensions (Bug 1141979)</title>
   <meta charset="utf-8">
   <script src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" href="/tests/SimpleTest/test.css">
 </head>
 <body>
 <script src="imagebitmap_extensions_prepareSources.js"></script>
+<script src="imagebitmap_extensions_data.js"></script>
 <script src="imagebitmap_extensions.js"></script>
 <script type="text/javascript">
 
 runTests();
 
 function ok(expect, msg) {
   window.parent.postMessage({"type": "status", status: !!expect, msg: msg}, "*");
 }
 
 function runTests() {
 
   prepareSources().
     then(testExceptions).
+    then(testColorConversions()).
     then( function() { return Promise.all([testAccessing_randomTest("Video", gVideo, 20), // video might use slightly different frames
                                            testAccessing_randomTest("Image", gImage, 0),
                                            testAccessing_randomTest("Canvas", gCanvas, 0),
                                            testAccessing_randomTest("Ctx", gCtx, 0),
                                            testAccessing_randomTest("ImageData", gImageData, 0),
                                            testAccessing_randomTest("ImageBitmap", gImageBitmap, 0),
                                            testAccessing_randomTest("PNG", gPNGBlob, 0),
                                            testAccessing_randomTest("JPEG", gJPEGBlob, 10) // JPEG loses information
--- a/dom/canvas/test/imagebitmap_extensions.js
+++ b/dom/canvas/test/imagebitmap_extensions.js
@@ -42,46 +42,218 @@ function isPixel(sourceType, bitmapForma
 function promiseThrows(p, name) {
   var didThrow;
   return p.then(function() { didThrow = false; },
                 function() { didThrow = true; })
           .then(function() { ok(didThrow, name); });
 }
 
 function testExceptions() {
+  return Promise.all([
+    promiseThrows(testColorConversion("GRAY8", "RGBA32", undefined, true), "[Exception] Cannot convert from GRAY8 to RGBA32"),
+    promiseThrows(testColorConversion("GRAY8", "BGRA32", undefined, true), "[Exception] Cannot convert from GRAY8 to BGRA32"),
+    promiseThrows(testColorConversion("GRAY8", "RGB24", undefined, true), "[Exception] Cannot convert from GRAY8 to RGB24"),
+    promiseThrows(testColorConversion("GRAY8", "BGR24", undefined, true), "[Exception] Cannot convert from GRAY8 to BGR24"),
+    promiseThrows(testColorConversion("GRAY8", "YUV444P", undefined, true), "[Exception] Cannot convert from GRAY8 to YUV444P"),
+    promiseThrows(testColorConversion("GRAY8", "YUV422P", undefined, true), "[Exception] Cannot convert from GRAY8 to YUV422P"),
+    promiseThrows(testColorConversion("GRAY8", "YUV420P", undefined, true), "[Exception] Cannot convert from GRAY8 to YUV420P"),
+    promiseThrows(testColorConversion("GRAY8", "YUV420SP_NV12", undefined, true), "[Exception] Cannot convert from GRAY8 to YUV420SP_NV12"),
+    promiseThrows(testColorConversion("GRAY8", "YUV420SP_NV21", undefined, true), "[Exception] Cannot convert from GRAY8 to YUV420SP_NV21"),
+    promiseThrows(testColorConversion("GRAY8", "HSV", undefined, true), "[Exception] Cannot convert from GRAY8 to HSV"),
+    promiseThrows(testColorConversion("GRAY8", "Lab", undefined, true), "[Exception] Cannot convert from GRAY8 to Lab"),
+    promiseThrows(testColorConversion("GRAY8", "DEPTH", undefined, true), "[Exception] Cannot convert from GRAY8 to DEPTH"),
 
-  function callMappedDataLengthWithUnsupportedFormat(format) {
-    return new Promise(function(resolve, reject) {
-      var p = createImageBitmap(gImageData);
-      p.then(
-        function(bitmap) {
-          try {
-            bitmap.mappedDataLength(format);
-            resolve();
-          }
-          catch (e) {
-            reject(e);
-          }
-        },
-        function() { resolve(); });
-    });
-  }
+    promiseThrows(testColorConversion("DEPTH", "RGBA32", undefined, true), "[Exception] Cannot convert from DEPTH to RGBA32"),
+    promiseThrows(testColorConversion("DEPTH", "BGRA32", undefined, true), "[Exception] Cannot convert from DEPTH to BGRA32"),
+    promiseThrows(testColorConversion("DEPTH", "RGB24", undefined, true), "[Exception] Cannot convert from DEPTH to RGB24"),
+    promiseThrows(testColorConversion("DEPTH", "BGR24", undefined, true), "[Exception] Cannot convert from DEPTH to BGR24"),
+    promiseThrows(testColorConversion("DEPTH", "GRAY8", undefined, true), "[Exception] Cannot convert from DEPTH to GRAY8"),
+    promiseThrows(testColorConversion("DEPTH", "YUV444P", undefined, true), "[Exception] Cannot convert from DEPTH to YUV444P"),
+    promiseThrows(testColorConversion("DEPTH", "YUV422P", undefined, true), "[Exception] Cannot convert from DEPTH to YUV422P"),
+    promiseThrows(testColorConversion("DEPTH", "YUV420P", undefined, true), "[Exception] Cannot convert from DEPTH to YUV420P"),
+    promiseThrows(testColorConversion("DEPTH", "YUV420SP_NV12", undefined, true), "[Exception] Cannot convert from DEPTH to YUV420SP_NV12"),
+    promiseThrows(testColorConversion("DEPTH", "YUV420SP_NV21", undefined, true), "[Exception] Cannot convert from DEPTH to YUV420SP_NV21"),
+    promiseThrows(testColorConversion("DEPTH", "HSV", undefined, true), "[Exception] Cannot convert from DEPTH to HSV"),
+    promiseThrows(testColorConversion("DEPTH", "Lab", undefined, true), "[Exception] Cannot convert from DEPTH to Lab"),
+
+    promiseThrows(testColorConversion("RGBA32", "DEPTH", undefined, true), "[Exception] Cannot convert from RGBA32 to DEPTH"),
+    promiseThrows(testColorConversion("BGRA32", "DEPTH", undefined, true), "[Exception] Cannot convert from BGRA32 to DEPTH"),
+    promiseThrows(testColorConversion("RGB24", "DEPTH", undefined, true), "[Exception] Cannot convert from RGB24 to DEPTH"),
+    promiseThrows(testColorConversion("BGR24", "DEPTH", undefined, true), "[Exception] Cannot convert from BGR24 to DEPTH"),
+    promiseThrows(testColorConversion("YUV444P", "DEPTH", undefined, true), "[Exception] Cannot convert from YUV444P to DEPTH"),
+    promiseThrows(testColorConversion("YUV422P", "DEPTH", undefined, true), "[Exception] Cannot convert from YUV422P to DEPTH"),
+    promiseThrows(testColorConversion("YUV420P", "DEPTH", undefined, true), "[Exception] Cannot convert from YUV420P to DEPTH"),
+    promiseThrows(testColorConversion("YUV420SP_NV12", "DEPTH", undefined, true), "[Exception] Cannot convert from YUV420SP_NV12 to DEPTH"),
+    promiseThrows(testColorConversion("YUV420SP_NV21", "DEPTH", undefined, true), "[Exception] Cannot convert from YUV420SP_NV21 to DEPTH"),
+    promiseThrows(testColorConversion("HSV", "DEPTH", undefined, true), "[Exception] Cannot convert from HSV to DEPTH"),
+    promiseThrows(testColorConversion("Lab", "DEPTH", undefined, true), "[Exception] Cannot convert from Lab to DEPTH"),
+  ]);
+}
+
+function testColorConversions() {
+  return Promise.all([// From RGBA32
+                      testColorConversion("RGBA32", "RGBA32"),
+                      testColorConversion("RGBA32", "BGRA32"),
+                      testColorConversion("RGBA32", "RGB24"),
+                      testColorConversion("RGBA32", "BGR24"),
+                      testColorConversion("RGBA32", "GRAY8"),
+                      testColorConversion("RGBA32", "YUV444P"),
+                      testColorConversion("RGBA32", "YUV422P"),
+                      testColorConversion("RGBA32", "YUV420P"),
+                      testColorConversion("RGBA32", "YUV420SP_NV12"),
+                      testColorConversion("RGBA32", "YUV420SP_NV21"),
+                      testColorConversion("RGBA32", "HSV", 0.01),
+                      testColorConversion("RGBA32", "Lab", 0.5),
+
+                      // From BGRA32
+                      testColorConversion("BGRA32", "RGBA32"),
+                      testColorConversion("BGRA32", "BGRA32"),
+                      testColorConversion("BGRA32", "RGB24"),
+                      testColorConversion("BGRA32", "BGR24"),
+                      testColorConversion("BGRA32", "GRAY8"),
+                      testColorConversion("BGRA32", "YUV444P", 3),
+                      testColorConversion("BGRA32", "YUV422P"),
+                      testColorConversion("BGRA32", "YUV420P"),
+                      testColorConversion("BGRA32", "YUV420SP_NV12"),
+                      testColorConversion("BGRA32", "YUV420SP_NV21"),
+                      testColorConversion("BGRA32", "HSV", 0.01),
+                      testColorConversion("BGRA32", "Lab", 0.5),
+
+                      // From RGB24
+                      testColorConversion("RGB24", "RGBA32"),
+                      testColorConversion("RGB24", "BGRA32"),
+                      testColorConversion("RGB24", "RGB24"),
+                      testColorConversion("RGB24", "BGR24"),
+                      testColorConversion("RGB24", "GRAY8"),
+                      testColorConversion("RGB24", "YUV444P"),
+                      testColorConversion("RGB24", "YUV422P"),
+                      testColorConversion("RGB24", "YUV420P"),
+                      testColorConversion("RGB24", "YUV420SP_NV12"),
+                      testColorConversion("RGB24", "YUV420SP_NV21"),
+                      testColorConversion("RGB24", "HSV", 0.01),
+                      testColorConversion("RGB24", "Lab", 0.5),
+
+                      // From BGR24
+                      testColorConversion("BGR24", "RGBA32"),
+                      testColorConversion("BGR24", "BGRA32"),
+                      testColorConversion("BGR24", "RGB24"),
+                      testColorConversion("BGR24", "BGR24"),
+                      testColorConversion("BGR24", "GRAY8"),
+                      testColorConversion("BGR24", "YUV444P"),
+                      testColorConversion("BGR24", "YUV422P"),
+                      testColorConversion("BGR24", "YUV420P"),
+                      testColorConversion("BGR24", "YUV420SP_NV12"),
+                      testColorConversion("BGR24", "YUV420SP_NV21"),
+                      testColorConversion("BGR24", "HSV", 0.01),
+                      testColorConversion("BGR24", "Lab", 0.5),
 
-  return Promise.all([
-    promiseThrows(callMappedDataLengthWithUnsupportedFormat("RGB24"), "[Exception] RGB24 is not supported yet."),
-    promiseThrows(callMappedDataLengthWithUnsupportedFormat("BGR24"), "[Exception] BGR24 is not supported yet."),
-    promiseThrows(callMappedDataLengthWithUnsupportedFormat("GRAY8"), "[Exception] GRAY8 is not supported yet."),
-    promiseThrows(callMappedDataLengthWithUnsupportedFormat("YUV444P"), "[Exception] GRYUV444PAY is not supported yet."),
-    promiseThrows(callMappedDataLengthWithUnsupportedFormat("YUV422P"), "[Exception] YUV422P is not supported yet."),
-    promiseThrows(callMappedDataLengthWithUnsupportedFormat("YUV420SP_NV12"), "[Exception] YUV420SP_NV12 is not supported yet."),
-    promiseThrows(callMappedDataLengthWithUnsupportedFormat("YUV420SP_NV21"), "[Exception] YUV420SP_NV21 is not supported yet."),
-    promiseThrows(callMappedDataLengthWithUnsupportedFormat("HSV"), "[Exception] HSV is not supported yet."),
-    promiseThrows(callMappedDataLengthWithUnsupportedFormat("Lab"), "[Exception] Lab is not supported yet."),
-    promiseThrows(callMappedDataLengthWithUnsupportedFormat("DEPTH"), "[Exception] DEPTH is not supported yet.")
-  ]);
+                      // From YUV444P
+                      testColorConversion("YUV444P", "RGBA32"),
+                      testColorConversion("YUV444P", "BGRA32"),
+                      testColorConversion("YUV444P", "RGB24"),
+                      testColorConversion("YUV444P", "BGR24"),
+                      testColorConversion("YUV444P", "GRAY8"),
+                      testColorConversion("YUV444P", "YUV444P"),
+                      testColorConversion("YUV444P", "YUV422P", 3),
+                      testColorConversion("YUV444P", "YUV420P", 3),
+                      testColorConversion("YUV444P", "YUV420SP_NV12", 3),
+                      testColorConversion("YUV444P", "YUV420SP_NV21", 3),
+                      testColorConversion("YUV444P", "HSV", 0.01),
+                      testColorConversion("YUV444P", "Lab", 0.01),
+
+                      // From YUV422P
+                      testColorConversion("YUV422P", "RGBA32"),
+                      testColorConversion("YUV422P", "BGRA32"),
+                      testColorConversion("YUV422P", "RGB24"),
+                      testColorConversion("YUV422P", "BGR24"),
+                      testColorConversion("YUV422P", "GRAY8"),
+                      testColorConversion("YUV422P", "YUV444P", 3),
+                      testColorConversion("YUV422P", "YUV422P"),
+                      testColorConversion("YUV422P", "YUV420P"),
+                      testColorConversion("YUV422P", "YUV420SP_NV12"),
+                      testColorConversion("YUV422P", "YUV420SP_NV21"),
+                      testColorConversion("YUV422P", "HSV", 0.01),
+                      testColorConversion("YUV422P", "Lab", 0.01),
+
+                      // From YUV420P
+                      testColorConversion("YUV420P", "RGBA32"),
+                      testColorConversion("YUV420P", "BGRA32"),
+                      testColorConversion("YUV420P", "RGB24"),
+                      testColorConversion("YUV420P", "BGR24"),
+                      testColorConversion("YUV420P", "GRAY8"),
+                      testColorConversion("YUV420P", "YUV444P", 3),
+                      testColorConversion("YUV420P", "YUV422P"),
+                      testColorConversion("YUV420P", "YUV420P"),
+                      testColorConversion("YUV420P", "YUV420SP_NV12"),
+                      testColorConversion("YUV420P", "YUV420SP_NV21"),
+                      testColorConversion("YUV420P", "HSV", 0.01),
+                      testColorConversion("YUV420P", "Lab", 0.01),
+
+                      // From NV12
+                      testColorConversion("YUV420SP_NV12", "RGBA32"),
+                      testColorConversion("YUV420SP_NV12", "BGRA32"),
+                      testColorConversion("YUV420SP_NV12", "RGB24"),
+                      testColorConversion("YUV420SP_NV12", "BGR24"),
+                      testColorConversion("YUV420SP_NV12", "GRAY8"),
+                      testColorConversion("YUV420SP_NV12", "YUV444P", 3),
+                      testColorConversion("YUV420SP_NV12", "YUV422P"),
+                      testColorConversion("YUV420SP_NV12", "YUV420P"),
+                      testColorConversion("YUV420SP_NV12", "YUV420SP_NV12"),
+                      testColorConversion("YUV420SP_NV12", "YUV420SP_NV21"),
+                      testColorConversion("YUV420SP_NV12", "HSV", 0.01),
+                      testColorConversion("YUV420SP_NV12", "Lab", 0.01),
+
+                      // From NV21
+                      testColorConversion("YUV420SP_NV21", "RGBA32"),
+                      testColorConversion("YUV420SP_NV21", "BGRA32"),
+                      testColorConversion("YUV420SP_NV21", "RGB24"),
+                      testColorConversion("YUV420SP_NV21", "BGR24"),
+                      testColorConversion("YUV420SP_NV21", "GRAY8"),
+                      testColorConversion("YUV420SP_NV21", "YUV444P", 3),
+                      testColorConversion("YUV420SP_NV21", "YUV422P"),
+                      testColorConversion("YUV420SP_NV21", "YUV420P"),
+                      testColorConversion("YUV420SP_NV21", "YUV420SP_NV12"),
+                      testColorConversion("YUV420SP_NV21", "YUV420SP_NV21"),
+                      testColorConversion("YUV420SP_NV21", "HSV", 0.01),
+                      testColorConversion("YUV420SP_NV21", "Lab", 0.01),
+
+                      // From HSV
+                      testColorConversion("HSV", "RGBA32"),
+                      testColorConversion("HSV", "BGRA32"),
+                      testColorConversion("HSV", "RGB24"),
+                      testColorConversion("HSV", "BGR24"),
+                      testColorConversion("HSV", "GRAY8"),
+                      testColorConversion("HSV", "YUV444P"),
+                      testColorConversion("HSV", "YUV422P"),
+                      testColorConversion("HSV", "YUV420P"),
+                      testColorConversion("HSV", "YUV420SP_NV12"),
+                      testColorConversion("HSV", "YUV420SP_NV21"),
+                      testColorConversion("HSV", "HSV", 0),
+                      testColorConversion("HSV", "Lab", 0.5),
+
+                      // From Lab
+                      testColorConversion("Lab", "RGBA32", 1),
+                      testColorConversion("Lab", "BGRA32", 1),
+                      testColorConversion("Lab", "RGB24", 1),
+                      testColorConversion("Lab", "BGR24", 1),
+                      testColorConversion("Lab", "GRAY8", 1),
+                      testColorConversion("Lab", "YUV444P", 1),
+                      testColorConversion("Lab", "YUV422P", 1),
+                      testColorConversion("Lab", "YUV420P", 1),
+                      testColorConversion("Lab", "YUV420SP_NV12", 1),
+                      testColorConversion("Lab", "YUV420SP_NV21", 1),
+                      testColorConversion("Lab", "HSV", 0.5),
+                      testColorConversion("Lab", "Lab", 0),
+
+                      // From GRAY8
+                      testColorConversion("GRAY8", "GRAY8"),
+
+                      // From DEPTH
+                      testColorConversion("DEPTH", "DEPTH", 0, Uint16Array),
+                     ]);
 }
 
 // Create an ImageBitmap, _bitmap_, from the _source_.
 // Read the underlying data of _bitmap_ into _bitmapBuffer_.
 // Compare the _bitmapBuffer_ with gGroundTruthImageData.
 function testAccessing_randomTest(sourceType, source, duration) {
   return new Promise(function(resolve, reject) {
     var p  = createImageBitmap(source);
@@ -163,9 +335,87 @@ function testCreateFromArrayBffer_random
                   function(ev) { failed(ev); reject(); });
               },
               function(ev) { console.log("p2 rejected!"); failed(ev); reject(); });
           },
           function(ev) { console.log("promiseMapDataInto rejected!"); failed(ev); reject(); });
       },
       function(ev) { failed(ev); reject(); });
   });
-}
\ No newline at end of file
+}
+
+function testColorConversion(sourceFromat, destinationFormat, tolerance, shouldThrow) {
+
+  tolerance = tolerance || 0;
+  shouldThrow = shouldThrow || false;
+
+  return new Promise(function(resolve, reject) {
+    var [srcData, dstData] = getTestData(sourceFromat, destinationFormat);
+
+    ok(!!srcData, "Get valid srcData of type:" + sourceFromat);
+    ok(!!dstData, "Get valid dstData of type:" + destinationFormat);
+
+    // printInfo(sourceFromat, srcData);
+    // printInfo(destinationFormat, dstData);
+
+    // Create a new ImageBitmap from an ArrayBuffer.
+    var p = createImageBitmap(srcData.buffer,
+                              0,
+                              srcData.bufferLength,
+                              srcData.format,
+                              srcData.pixelLayout);
+
+    p.then(
+      function(srcBitmap) {
+        ok(!!srcBitmap, "Should get a valid srcBitmap.");
+        ok(srcBitmap.findOptimalFormat() == sourceFromat, "srcBitmap.findOptimalFormat():" + srcBitmap.findOptimalFormat() +
+                                                          " should equal to sourceFromat:" + sourceFromat);
+
+        var dstBufferLength = srcBitmap.mappedDataLength(destinationFormat);
+        var dstBuffer = new ArrayBuffer(dstBufferLength);
+        var dstBufferView = new dstData.ArrayType(dstBuffer, 0, dstBufferLength / dstData.ArrayType.BYTES_PER_ELEMENT);
+
+        // Do color conversion here.
+        var p2 = srcBitmap.mapDataInto(destinationFormat, dstBuffer, 0);
+        p2.then(
+          function(dstPixelLayout) {
+            var dataPixalLayout = dstData.pixelLayout;
+
+            // Check pixel layout.
+            ok(dstPixelLayout.length == dstData.channelCount, "dstPixelLayout.length:" + dstPixelLayout.length +
+                                                              " should equal to dstData.channelCount:" + dstData.channelCount);
+
+            for (var c = 0; c < dstData.channelCount; ++c) {
+              var dstChannelLayout = dstPixelLayout[c];
+              var dataChannelLayout = dataPixalLayout[c];
+              ok(dstChannelLayout.width == dataChannelLayout.width, "channel[" + c + "] dstChannelLayout.width:" + dstChannelLayout.width + " should equal to dataChannelLayout.width:" + dataChannelLayout.width);
+              ok(dstChannelLayout.height == dataChannelLayout.height, "channel[" + c + "] dstChannelLayout.height:" + dstChannelLayout.height + " should equal to dataChannelLayout.height:" + dataChannelLayout.height);
+              ok(dstChannelLayout.skip == dataChannelLayout.skip, "channel[" + c + "] dstChannelLayout.skip:" + dstChannelLayout.skip + " should equal to dataChannelLayout.skip:" + dataChannelLayout.skip);
+
+              for (var i = 0; i < dstChannelLayout.height; ++i) {
+                for (var j = 0; j < dstChannelLayout.width; ++j) {
+                  var byteOffset = dstChannelLayout.offset + i * dstChannelLayout.stride + j * (dstChannelLayout.skip + 1) * dstData.ArrayType.BYTES_PER_ELEMENT;
+                  var view = new dstData.ArrayType(dstBuffer, byteOffset, 1);
+                  var dstBufferViewValue = view[0];
+                  var dstDataValue = dstData.getPixelValue(i, j, c);
+                  ok(Math.abs(dstBufferViewValue - dstDataValue) <= tolerance,
+                     "[" + sourceFromat + " -> " + destinationFormat + "] pixel(" + i + "," + j + ") channnel(" + c +
+                     "): dstBufferViewValue:" + dstBufferViewValue +
+                     " should equal to dstDataValue:" + dstDataValue);
+                }
+              }
+            }
+
+            resolve();
+          },
+          function(ev) {
+            // If the "mapDataInto" throws, the flow goes here.
+            if (!shouldThrow) { failed(ev); }
+            reject();
+          }
+        );
+      },
+      function(ev) {
+        reject(ev);
+      }
+    );
+  });
+}
new file mode 100644
--- /dev/null
+++ b/dom/canvas/test/imagebitmap_extensions_data.js
@@ -0,0 +1,1066 @@
+class Image {
+  constructor(channelCount, format, ArrayType) {
+    this.channelCount = channelCount;
+    this.format = format;
+    this.ArrayType = ArrayType;
+    this.pixelLayout = [];
+    this.buffer = undefined;
+    this.data = undefined;
+  };
+};
+
+class TypedSimpleImage extends Image {
+  constructor(width, height, channelCount, format, ArrayType) {
+    super(channelCount, format, ArrayType);
+    this.width = width;
+    this.height = height;
+    this.stride = this.width * this.channelCount * this.ArrayType.BYTES_PER_ELEMENT;
+    this.bufferLength = this.height * this.stride;
+    this.buffer = new ArrayBuffer(this.bufferLength);
+    this.data = new this.ArrayType(this.buffer, 0, this.height * this.width * this.channelCount);
+
+    // initialize pixel layout
+    for (var c = 0; c < this.channelCount; ++c) {
+      this.pixelLayout.push({offset:c * this.ArrayType.BYTES_PER_ELEMENT,
+                             width:this.width,
+                             height:this.height,
+                             dataType:"uint8",
+                             stride:this.stride,
+                             skip:(this.channelCount - 1)});
+    }
+  };
+
+  getPixelValue(i, j, c) {
+    var dataChannelLayout = this.pixelLayout[c];
+    return this.data[i * this.width * this.channelCount + j * this.channelCount + c];
+  }
+};
+
+class Uint8SimpleImage extends TypedSimpleImage {
+  constructor(width, height, channelCount, format) {
+    super(width, height, channelCount, format, Uint8ClampedArray);
+  };
+};
+
+class Uint16SimpleImage extends TypedSimpleImage {
+  constructor(width, height, channelCount, format) {
+    super(width, height, channelCount, format, Uint16Array);
+  };
+};
+
+class FloatSimpleImage extends TypedSimpleImage {
+  constructor(width, height, channelCount, format) {
+    super(width, height, channelCount, format, Float32Array);
+  };
+};
+
+class DoubleSimpleImage extends TypedSimpleImage {
+  constructor(width, height, channelCount, format) {
+    super(width, height, channelCount, format, Float64Array);
+  };
+};
+
+class RGBA32Data extends Uint8SimpleImage {
+  constructor() {
+    super(3, 3, 4, "RGBA32");
+
+    var i = 0;
+    this.data[i + 0] = 0;   this.data[i + 1] = 0;   this.data[i + 2] = 0;   this.data[i + 3] = 255;
+    this.data[i + 4] = 255; this.data[i + 5] = 0;   this.data[i + 6] = 0;   this.data[i + 7] = 255;
+    this.data[i + 8] = 0;   this.data[i + 9] = 255; this.data[i + 10] = 0;  this.data[i + 11] = 255;
+
+    i += this.stride;
+    this.data[i + 0] = 0;   this.data[i + 1] = 0;   this.data[i + 2] = 255;  this.data[i + 3] = 255;
+    this.data[i + 4] = 255; this.data[i + 5] = 255; this.data[i + 6] = 0;    this.data[i + 7] = 255;
+    this.data[i + 8] = 0;   this.data[i + 9] = 255; this.data[i + 10] = 255; this.data[i + 11] = 255;
+
+    i += this.stride;
+    this.data[i + 0] = 255;  this.data[i + 1] = 0;   this.data[i + 2] = 255;  this.data[i + 3] = 255;
+    this.data[i + 4] = 255;  this.data[i + 5] = 255; this.data[i + 6] = 255;  this.data[i + 7] = 255;
+    this.data[i + 8] = 128;  this.data[i + 9] = 128; this.data[i + 10] = 128; this.data[i + 11] = 255;
+  };
+};
+
+class BGRA32Data extends Uint8SimpleImage {
+  constructor() {
+    super(3, 3, 4, "BGRA32");
+
+    var i = 0;
+    this.data[i + 2] = 0;   this.data[i + 1] = 0;   this.data[i + 0] = 0;   this.data[i + 3] = 255;
+    this.data[i + 6] = 255; this.data[i + 5] = 0;   this.data[i + 4] = 0;   this.data[i + 7] = 255;
+    this.data[i + 10] = 0;  this.data[i + 9] = 255; this.data[i + 8] = 0;   this.data[i + 11] = 255;
+
+    i += this.stride;
+    this.data[i + 2] = 0;   this.data[i + 1] = 0;   this.data[i + 0] = 255;  this.data[i + 3] = 255;
+    this.data[i + 6] = 255; this.data[i + 5] = 255; this.data[i + 4] = 0;    this.data[i + 7] = 255;
+    this.data[i + 10] = 0;  this.data[i + 9] = 255; this.data[i + 8] = 255;  this.data[i + 11] = 255;
+
+    i += this.stride;
+    this.data[i + 2] = 255;  this.data[i + 1] = 0;   this.data[i + 0] = 255; this.data[i + 3] = 255;
+    this.data[i + 6] = 255;  this.data[i + 5] = 255; this.data[i + 4] = 255; this.data[i + 7] = 255;
+    this.data[i + 10] = 128; this.data[i + 9] = 128; this.data[i + 8] = 128; this.data[i + 11] = 255;
+  };
+};
+
+class RGB24Data extends Uint8SimpleImage {
+  constructor() {
+    super(3, 3, 3, "RGB24");
+
+    var i = 0;
+    this.data[i + 0] = 0;   this.data[i + 1] = 0;   this.data[i + 2] = 0;
+    this.data[i + 3] = 255; this.data[i + 4] = 0;   this.data[i + 5] = 0;
+    this.data[i + 6] = 0;   this.data[i + 7] = 255; this.data[i + 8] = 0;
+
+    i += this.stride;
+    this.data[i + 0] = 0;   this.data[i + 1] = 0;   this.data[i + 2] = 255;
+    this.data[i + 3] = 255; this.data[i + 4] = 255; this.data[i + 5] = 0;
+    this.data[i + 6] = 0;   this.data[i + 7] = 255; this.data[i + 8] = 255;
+
+    i += this.stride;
+    this.data[i + 0] = 255;  this.data[i + 1] = 0;   this.data[i + 2] = 255;
+    this.data[i + 3] = 255;  this.data[i + 4] = 255; this.data[i + 5] = 255;
+    this.data[i + 6] = 128;  this.data[i + 7] = 128; this.data[i + 8] = 128;
+  };
+};
+
+class BGR24Data extends Uint8SimpleImage {
+  constructor() {
+    super(3, 3, 3, "BGR24");
+
+    var i = 0;
+    this.data[i + 2] = 0;   this.data[i + 1] = 0;   this.data[i + 0] = 0;
+    this.data[i + 5] = 255; this.data[i + 4] = 0;   this.data[i + 3] = 0;
+    this.data[i + 8] = 0;   this.data[i + 7] = 255; this.data[i + 6] = 0;
+
+    i += this.stride;
+    this.data[i + 2] = 0;   this.data[i + 1] = 0;   this.data[i + 0] = 255;
+    this.data[i + 5] = 255; this.data[i + 4] = 255; this.data[i + 3] = 0;
+    this.data[i + 8] = 0;   this.data[i + 7] = 255; this.data[i + 6] = 255;
+
+    i += this.stride;
+    this.data[i + 2] = 255; this.data[i + 1] = 0;   this.data[i + 0] = 255;
+    this.data[i + 5] = 255; this.data[i + 4] = 255; this.data[i + 3] = 255;
+    this.data[i + 8] = 128; this.data[i + 7] = 128; this.data[i + 6] = 128;
+  };
+};
+
+class Gray8Data extends Uint8SimpleImage {
+  constructor() {
+    super(3, 3, 1, "GRAY8");
+
+    var i = 0;
+    this.data[i + 0] = 0;   this.data[i + 1] = 76;   this.data[i + 2] = 149;
+
+    i += this.stride;
+    this.data[i + 0] = 29;   this.data[i + 1] = 225;   this.data[i + 2] = 178;
+
+    i += this.stride;
+    this.data[i + 0] = 105; this.data[i + 1] = 255;   this.data[i + 2] = 127;
+  };
+};
+
+class RGBA32DataFromYUV444PData extends Uint8SimpleImage {
+  constructor(redIndex, greenIndex, blueIndex, alphaIndex) {
+
+    // Get the exact format.
+    var channelCount_ = !!alphaIndex ? 4 : 3;
+    var format_;
+    if (redIndex == 0) {
+      if (channelCount_ == 3) {
+        format_ = "RGBA32";
+      } else  {
+        format_ = "RGB24";
+      }
+    } else if (redIndex == 2) {
+      if (channelCount_ == 3) {
+        format_ = "BGRA32";
+      } else  {
+        format_ = "BGR24";
+      }
+    } else {
+      return undefined;
+    }
+
+    // Call parent's consturctor.
+    super(3, 3, channelCount_, format_);
+
+    // Calculate parameters for setting data.
+    var rIndex = redIndex;        // 0 or 2
+    var gIndex = 1;
+    var bIndex = redIndex ^ 2;    // 0 or 2
+    var aIndex = alphaIndex || 0; // If alphaIndex == undefined --> aIndex = 0;
+    var shift0 = 0;
+    var shift1 = channelCount_;
+    var shift2 = channelCount_ * 2;
+
+    // Set the data.
+    var i = 0;
+    this.data[i + aIndex + shift0] = 255;   this.data[i + rIndex + shift0] = 0;   this.data[i + gIndex + shift0] = 0;   this.data[i + bIndex + shift0] = 0;
+    this.data[i + aIndex + shift1] = 255;   this.data[i + rIndex + shift1] = 254; this.data[i + gIndex + shift1] = 0;   this.data[i + bIndex + shift1] = 0;
+    this.data[i + aIndex + shift2] = 255;   this.data[i + rIndex + shift2] = 0;   this.data[i + gIndex + shift2] = 253; this.data[i + bIndex + shift2] = 1;
+
+    i += this.stride;
+    this.data[i + aIndex + shift0] = 255;   this.data[i + rIndex + shift0] = 0;   this.data[i + gIndex + shift0] = 0;   this.data[i + bIndex + shift0] = 251;
+    this.data[i + aIndex + shift1] = 255;   this.data[i + rIndex + shift1] = 253; this.data[i + gIndex + shift1] = 253; this.data[i + bIndex + shift1] = 2;
+    this.data[i + aIndex + shift2] = 255;   this.data[i + rIndex + shift2] = 0;   this.data[i + gIndex + shift2] = 253; this.data[i + bIndex + shift2] = 252;
+
+    i += this.stride;
+    this.data[i + aIndex + shift0] = 255;   this.data[i + rIndex + shift0] = 255; this.data[i + gIndex + shift0] = 0;   this.data[i + bIndex + shift0] = 252;
+    this.data[i + aIndex + shift1] = 255;   this.data[i + rIndex + shift1] = 253; this.data[i + gIndex + shift1] = 253; this.data[i + bIndex + shift1] = 253;
+    this.data[i + aIndex + shift2] = 255;   this.data[i + rIndex + shift2] = 127; this.data[i + gIndex + shift2] = 127; this.data[i + bIndex + shift2] = 127;
+  };
+};
+
+class RGBA32DataFromYUV422PData extends Uint8SimpleImage {
+  constructor(redIndex, greenIndex, blueIndex, alphaIndex) {
+
+    // Get the exact format.
+    var channelCount_ = !!alphaIndex ? 4 : 3;
+    var format_;
+    if (redIndex == 0) {
+      if (channelCount_ == 3) {
+        format_ = "RGBA32";
+      } else  {
+        format_ = "RGB24";
+      }
+    } else if (redIndex == 2) {
+      if (channelCount_ == 3) {
+        format_ = "BGRA32";
+      } else  {
+        format_ = "BGR24";
+      }
+    } else {
+      return undefined;
+    }
+
+    // Call parent's consturctor.
+    super(3, 3, channelCount_, format_);
+
+    // Calculate parameters for setting data.
+    var rIndex = redIndex;        // 0 or 2
+    var gIndex = 1;
+    var bIndex = redIndex ^ 2;    // 0 or 2
+    var aIndex = alphaIndex || 0; // If alphaIndex == undefined --> aIndex = 0;
+    var shift0 = 0;
+    var shift1 = channelCount_;
+    var shift2 = channelCount_ * 2;
+
+    // Set the data.
+    var i = 0;
+    this.data[i + aIndex + shift0] = 255;   this.data[i + rIndex + shift0] = 89;  this.data[i + gIndex + shift0] = 0;   this.data[i + bIndex + shift0] = 0;
+    this.data[i + aIndex + shift1] = 255;   this.data[i + rIndex + shift1] = 165; this.data[i + gIndex + shift1] = 38;  this.data[i + bIndex + shift1] = 38;
+    this.data[i + aIndex + shift2] = 255;   this.data[i + rIndex + shift2] = 0;   this.data[i + gIndex + shift2] = 253; this.data[i + bIndex + shift2] = 1;
+
+    i += this.stride;
+    this.data[i + aIndex + shift0] = 255;   this.data[i + rIndex + shift0] = 28;  this.data[i + gIndex + shift0] = 28;  this.data[i + bIndex + shift0] = 28;
+    this.data[i + aIndex + shift1] = 255;   this.data[i + rIndex + shift1] = 224; this.data[i + gIndex + shift1] = 224; this.data[i + bIndex + shift1] = 224;
+    this.data[i + aIndex + shift2] = 255;   this.data[i + rIndex + shift2] = 0;   this.data[i + gIndex + shift2] = 253; this.data[i + bIndex + shift2] = 252;
+
+    i += this.stride;
+    this.data[i + aIndex + shift0] = 255;   this.data[i + rIndex + shift0] = 180; this.data[i + gIndex + shift0] = 52;  this.data[i + bIndex + shift0] = 178;
+    this.data[i + aIndex + shift1] = 255;   this.data[i + rIndex + shift1] = 255; this.data[i + gIndex + shift1] = 200; this.data[i + bIndex + shift1] = 255;
+    this.data[i + aIndex + shift2] = 255;   this.data[i + rIndex + shift2] = 127; this.data[i + gIndex + shift2] = 127; this.data[i + bIndex + shift2] = 127;
+  };
+};
+
+class RGBA32DataFromYUV420PData extends Uint8SimpleImage {
+  constructor(redIndex, greenIndex, blueIndex, alphaIndex) {
+
+    // Get the exact format.
+    var channelCount_ = !!alphaIndex ? 4 : 3;
+    var format_;
+    if (redIndex == 0) {
+      if (channelCount_ == 3) {
+        format_ = "RGBA32";
+      } else  {
+        format_ = "RGB24";
+      }
+    } else if (redIndex == 2) {
+      if (channelCount_ == 3) {
+        format_ = "BGRA32";
+      } else  {
+        format_ = "BGR24";
+      }
+    } else {
+      return undefined;
+    }
+
+    // Call parent's consturctor.
+    super(3, 3, channelCount_, format_);
+
+    // Calculate parameters for setting data.
+    var rIndex = redIndex;        // 0 or 2
+    var gIndex = 1;
+    var bIndex = redIndex ^ 2;    // 0 or 2
+    var aIndex = alphaIndex || 0; // If alphaIndex == undefined --> aIndex = 0;
+    var shift0 = 0;
+    var shift1 = channelCount_;
+    var shift2 = channelCount_ * 2;
+
+    // Set the data.
+    var i = 0;
+    this.data[i + aIndex + shift0] = 255;   this.data[i + rIndex + shift0] = 44;  this.data[i + gIndex + shift0] = 0;   this.data[i + bIndex + shift0] = 0;
+    this.data[i + aIndex + shift1] = 255;   this.data[i + rIndex + shift1] = 120; this.data[i + gIndex + shift1] = 57;  this.data[i + bIndex + shift1] = 58;
+    this.data[i + aIndex + shift2] = 255;   this.data[i + rIndex + shift2] = 0;   this.data[i + gIndex + shift2] = 238; this.data[i + bIndex + shift2] = 112;
+
+    i += this.stride;
+    this.data[i + aIndex + shift0] = 255;   this.data[i + rIndex + shift0] = 73;  this.data[i + gIndex + shift0] = 9;   this.data[i + bIndex + shift0] = 11;
+    this.data[i + aIndex + shift1] = 255;   this.data[i + rIndex + shift1] = 255; this.data[i + gIndex + shift1] = 205; this.data[i + bIndex + shift1] = 206;
+    this.data[i + aIndex + shift2] = 255;   this.data[i + rIndex + shift2] = 12;  this.data[i + gIndex + shift2] = 255; this.data[i + bIndex + shift2] = 141;
+
+    i += this.stride;
+    this.data[i + aIndex + shift0] = 255;   this.data[i + rIndex + shift0] = 180; this.data[i + gIndex + shift0] = 52;  this.data[i + bIndex + shift0] = 178;
+    this.data[i + aIndex + shift1] = 255;   this.data[i + rIndex + shift1] = 255; this.data[i + gIndex + shift1] = 200; this.data[i + bIndex + shift1] = 255;
+    this.data[i + aIndex + shift2] = 255;   this.data[i + rIndex + shift2] = 127; this.data[i + gIndex + shift2] = 127; this.data[i + bIndex + shift2] = 127;
+  };
+};
+
+class Gray8DataFromYUVData extends Uint8SimpleImage {
+  constructor() {
+    super(3, 3, 1, "GRAY8");
+
+    var i = 0;
+    this.data[i + 0] = 16;  this.data[i + 1] = 82;   this.data[i + 2] = 144;
+
+    i += this.stride;
+    this.data[i + 0] = 41;  this.data[i + 1] = 210;  this.data[i + 2] = 169;
+
+    i += this.stride;
+    this.data[i + 0] = 107; this.data[i + 1] = 235;  this.data[i + 2] = 126;
+  };
+};
+
+class YUVImage extends Image {
+  constructor(yWidth, yHeight, uWidth, uHeight, vWidth, vHeight, format) {
+    super(3, format, Uint8ClampedArray);
+    this.yWidth = yWidth;
+    this.yHeight = yHeight;
+    this.yStride = this.yWidth * this.ArrayType.BYTES_PER_ELEMENT;
+    this.uWidth = uWidth;
+    this.uHeight = uHeight;
+    this.uStride = this.uWidth * this.ArrayType.BYTES_PER_ELEMENT;
+    this.vWidth = vWidth;
+    this.vHeight = vHeight;
+    this.vStride = this.vWidth * this.ArrayType.BYTES_PER_ELEMENT;
+    this.bufferLength = this.yHeight * this.yStride +
+                        this.uHeight * this.uStride +
+                        this.vHeight * this.vStride
+    this.buffer = new ArrayBuffer(this.bufferLength);
+    this.data = new this.ArrayType(this.buffer, 0, this.bufferLength);
+    this.yData = new this.ArrayType(this.buffer, 0, this.yHeight * this.yStride);
+    this.uData = new this.ArrayType(this.buffer,
+                                    this.yHeight * this.yStride,
+                                    this.uHeight * this.uStride);
+    this.vData = new this.ArrayType(this.buffer,
+                                    this.yHeight * this.yStride + this.uHeight * this.uStride,
+                                    this.vHeight * this.vStride);
+
+    // Initialize pixel layout.
+    // y channel.
+    this.pixelLayout.push({offset:0,
+                           width:this.yWidth,
+                           height:this.yHeight,
+                           dataType:"uint8",
+                           stride:this.yStride,
+                           skip:0});
+
+    // u channel.
+    this.pixelLayout.push({offset:(this.yHeight * this.yStride),
+                           width:this.uWidth,
+                           height:this.uHeight,
+                           dataType:"uint8",
+                           stride:this.uStride,
+                           skip:0});
+
+    // v channel.
+    this.pixelLayout.push({offset:(this.yHeight * this.yStride + this.uHeight * this.uStride),
+                           width:this.vWidth,
+                           height:this.vHeight,
+                           dataType:"uint8",
+                           stride:this.vStride,
+                           skip:0});
+  };
+
+  getPixelValue(i, j, c) {
+    var dataChannelLayout = this.pixelLayout[c];
+    return this.data[dataChannelLayout.offset + i * dataChannelLayout.stride + j * (dataChannelLayout.skip + 1)];
+  }
+};
+
+class YUV444PData extends YUVImage {
+  constructor() {
+    super(3, 3, 3, 3, 3, 3, "YUV444P");
+
+    this.yData[0] = 16;  this.yData[1] = 82;  this.yData[2] = 144;
+    this.yData[3] = 41;  this.yData[4] = 210; this.yData[5] = 169;
+    this.yData[6] = 107; this.yData[7] = 235; this.yData[8] = 126;
+
+    this.uData[0] = 128; this.uData[1] = 90;  this.uData[2] = 54;
+    this.uData[3] = 240; this.uData[4] = 16;  this.uData[5] = 166;
+    this.uData[6] = 202; this.uData[7] = 128; this.uData[8] = 128;
+
+    this.vData[0] = 128; this.vData[1] = 240; this.vData[2] = 34;
+    this.vData[3] = 110; this.vData[4] = 146; this.vData[5] = 16;
+    this.vData[6] = 222; this.vData[7] = 128; this.vData[8] = 128;
+  }
+};
+
+class YUV444PDataFromYUV422PData extends YUVImage {
+  constructor() {
+    super(3, 3, 3, 3, 3, 3, "YUV444P");
+
+    this.yData[0] = 16;  this.yData[1] = 82;  this.yData[2] = 144;
+    this.yData[3] = 41;  this.yData[4] = 210; this.yData[5] = 169;
+    this.yData[6] = 107; this.yData[7] = 235; this.yData[8] = 126;
+
+    this.uData[0] = 108; this.uData[1] = 81;  this.uData[2] = 53;
+    this.uData[3] = 126; this.uData[4] = 112; this.uData[5] = 98;
+    this.uData[6] = 144; this.uData[7] = 144; this.uData[8] = 144;
+
+    this.vData[0] = 182; this.vData[1] = 108; this.vData[2] = 33;
+    this.vData[3] = 166; this.vData[4] = 109; this.vData[5] = 51;
+    this.vData[6] = 150; this.vData[7] = 110; this.vData[8] = 70;
+  }
+};
+
+class YUV444PDataFromYUV420PData extends YUVImage {
+  constructor() {
+    super(3, 3, 3, 3, 3, 3, "YUV444P");
+
+    this.yData[0] = 16;  this.yData[1] = 82;  this.yData[2] = 144;
+    this.yData[3] = 41;  this.yData[4] = 210; this.yData[5] = 169;
+    this.yData[6] = 107; this.yData[7] = 235; this.yData[8] = 126;
+
+    this.uData[0] = 118; this.uData[1] = 113; this.uData[2] = 109;
+    this.uData[3] = 140; this.uData[4] = 128; this.uData[5] = 117;
+    this.uData[6] = 162; this.uData[7] = 144; this.uData[8] = 126;
+
+    this.vData[0] = 154; this.vData[1] = 90;  this.vData[2] = 24;
+    this.vData[3] = 163; this.vData[4] = 119; this.vData[5] = 75;
+    this.vData[6] = 172; this.vData[7] = 149; this.vData[8] = 126;
+  }
+};
+
+class YUV422PData extends YUVImage {
+  constructor() {
+    super(3, 3, 2, 3, 2, 3, "YUV422P");
+
+    this.yData[0] = 16;  this.yData[1] = 82;  this.yData[2] = 144;
+    this.yData[3] = 41;  this.yData[4] = 210; this.yData[5] = 169;
+    this.yData[6] = 107; this.yData[7] = 235; this.yData[8] = 126;
+
+    this.uData[0] = 109; this.uData[1] = 54;
+    this.uData[2] = 128; this.uData[3] = 166;
+    this.uData[4] = 165; this.uData[5] = 128;
+
+    this.vData[0] = 184; this.vData[1] = 34;
+    this.vData[2] = 128; this.vData[3] = 16;
+    this.vData[4] = 175; this.vData[5] = 128;
+  }
+};
+
+class YUV422PDataFromYUV444PData extends YUVImage {
+  constructor() {
+    super(3, 3, 2, 3, 2, 3, "YUV422P");
+
+    this.yData[0] = 16;  this.yData[1] = 82;  this.yData[2] = 144;
+    this.yData[3] = 41;  this.yData[4] = 210; this.yData[5] = 169;
+    this.yData[6] = 107; this.yData[7] = 235; this.yData[8] = 126;
+
+    this.uData[0] = 133; this.uData[1] = 78;
+    this.uData[2] = 164; this.uData[3] = 109;
+    this.uData[4] = 180; this.uData[5] = 125;
+
+    this.vData[0] = 145; this.vData[1] = 74;
+    this.vData[2] = 165; this.vData[3] = 95;
+    this.vData[4] = 175; this.vData[5] = 105;
+  }
+};
+
+class YUV422PDataFromYUV420PData extends YUVImage {
+  constructor() {
+    super(3, 3, 2, 3, 2, 3, "YUV422P");
+
+    this.yData[0] = 16;  this.yData[1] = 82;  this.yData[2] = 144;
+    this.yData[3] = 41;  this.yData[4] = 210; this.yData[5] = 169;
+    this.yData[6] = 107; this.yData[7] = 235; this.yData[8] = 126;
+
+    this.uData[0] = 119; this.uData[1] = 110;
+    this.uData[2] = 149; this.uData[3] = 121;
+    this.uData[4] = 164; this.uData[5] = 127;
+
+    this.vData[0] = 156; this.vData[1] = 25;
+    this.vData[2] = 168; this.vData[3] = 93;
+    this.vData[4] = 174; this.vData[5] = 127;
+  }
+};
+
+class YUV420PData extends YUVImage {
+  constructor() {
+    super(3, 3, 2, 2, 2, 2, "YUV420P");
+
+    this.yData[0] = 16;  this.yData[1] = 82;  this.yData[2] = 144;
+    this.yData[3] = 41;  this.yData[4] = 210; this.yData[5] = 169;
+    this.yData[6] = 107; this.yData[7] = 235; this.yData[8] = 126;
+
+    this.uData[0] = 119; this.uData[1] = 110;
+    this.uData[2] = 165; this.uData[3] = 128;
+
+    this.vData[0] = 156; this.vData[1] = 25;
+    this.vData[2] = 175; this.vData[3] = 128;
+  }
+};
+
+class YUV420PDataFromYUV444PData extends YUVImage {
+  constructor() {
+    super(3, 3, 2, 2, 2, 2, "YUV420P");
+
+    this.yData[0] = 16;  this.yData[1] = 82;  this.yData[2] = 144;
+    this.yData[3] = 41;  this.yData[4] = 210; this.yData[5] = 169;
+    this.yData[6] = 107; this.yData[7] = 235; this.yData[8] = 126;
+
+    this.uData[0] = 133; this.uData[1] = 78;
+    this.uData[2] = 181; this.uData[3] = 126;
+
+    this.vData[0] = 145; this.vData[1] = 74;
+    this.vData[2] = 176; this.vData[3] = 106;
+  }
+};
+
+class YUV420PDataFromYUV422PData extends YUVImage {
+  constructor() {
+    super(3, 3, 2, 2, 2, 2, "YUV420P");
+
+    this.yData[0] = 16;  this.yData[1] = 82;  this.yData[2] = 144;
+    this.yData[3] = 41;  this.yData[4] = 210; this.yData[5] = 169;
+    this.yData[6] = 107; this.yData[7] = 235; this.yData[8] = 126;
+
+    this.uData[0] = 109; this.uData[1] = 54;
+    this.uData[2] = 147; this.uData[3] = 147;
+
+    this.vData[0] = 184; this.vData[1] = 34;
+    this.vData[2] = 152; this.vData[3] = 72;
+  }
+};
+
+class NVImage extends Image {
+  constructor(yWidth, yHeight, uvWidth, uvHeight, format) {
+    super(3, format, Uint8ClampedArray);
+    this.yWidth = yWidth;
+    this.yHeight = yHeight;
+    this.yStride = this.yWidth * this.ArrayType.BYTES_PER_ELEMENT;
+    this.uvWidth = uvWidth;
+    this.uvHeight = uvHeight;
+    this.uvStride = this.uvWidth * 2 * this.ArrayType.BYTES_PER_ELEMENT;
+    this.bufferLength = this.yHeight * this.yStride + this.uvHeight * this.uvStride;
+    this.buffer = new ArrayBuffer(this.bufferLength);
+    this.data = new this.ArrayType(this.buffer, 0, this.bufferLength);
+    this.yData = new this.ArrayType(this.buffer, 0, this.yHeight * this.yStride);
+    this.uvData = new this.ArrayType(this.buffer,
+                                     this.yHeight * this.yStride,
+                                     this.uvHeight * this.uvStride);
+
+    // Initialize pixel layout.
+    // y channel.
+    this.pixelLayout.push({offset:0,
+                           width:this.yWidth,
+                           height:this.yHeight,
+                           dataType:"uint8",
+                           stride:this.yStride,
+                           skip:0});
+    // v channel.
+    this.pixelLayout.push({offset:(this.yHeight * this.yStride),
+                           width:this.uvWidth,
+                           height:this.uvHeight,
+                           dataType:"uint8",
+                           stride:this.uvStride,
+                           skip:1});
+
+    // u channel.
+    this.pixelLayout.push({offset:(this.yHeight * this.yStride + this.ArrayType.BYTES_PER_ELEMENT),
+                           width:this.uvWidth,
+                           height:this.uvHeight,
+                           dataType:"uint8",
+                           stride:this.uvStride,
+                           skip:1});
+  };
+
+  getPixelValue(i, j, c) {
+    var dataChannelLayout = this.pixelLayout[c];
+    return this.data[dataChannelLayout.offset + i * dataChannelLayout.stride + j * (dataChannelLayout.skip + 1)];
+  }
+};
+
+class NV12Data extends NVImage {
+  constructor() {
+    super(3, 3, 2, 2, "YUV420SP_NV12");
+
+    this.yData[0] = 16;  this.yData[1] = 82;  this.yData[2] = 144;
+    this.yData[3] = 41;  this.yData[4] = 210; this.yData[5] = 169;
+    this.yData[6] = 107; this.yData[7] = 235; this.yData[8] = 126;
+
+    // U
+    this.uvData[0] = 119;
+    this.uvData[2] = 110;
+    this.uvData[4] = 165;
+    this.uvData[6] = 128;
+
+    // V
+    this.uvData[1] = 156;
+    this.uvData[3] = 25;
+    this.uvData[5] = 175;
+    this.uvData[7] = 128;
+  };
+};
+
+class NV12DataFromYUV444PData extends NVImage {
+  constructor() {
+    super(3, 3, 2, 2, "YUV420SP_NV12");
+
+    this.yData[0] = 16;  this.yData[1] = 82;  this.yData[2] = 144;
+    this.yData[3] = 41;  this.yData[4] = 210; this.yData[5] = 169;
+    this.yData[6] = 107; this.yData[7] = 235; this.yData[8] = 126;
+
+    // U
+    this.uvData[0] = 133;
+    this.uvData[2] = 78;
+    this.uvData[4] = 181;
+    this.uvData[6] = 126;
+
+    // V
+    this.uvData[1] = 145;
+    this.uvData[3] = 74;
+    this.uvData[5] = 176;
+    this.uvData[7] = 106;
+  };
+};
+
+class NV12DataFromYUV422PData extends NVImage {
+  constructor() {
+    super(3, 3, 2, 2, "YUV420SP_NV12");
+
+    this.yData[0] = 16;  this.yData[1] = 82;  this.yData[2] = 144;
+    this.yData[3] = 41;  this.yData[4] = 210; this.yData[5] = 169;
+    this.yData[6] = 107; this.yData[7] = 235; this.yData[8] = 126;
+
+    // U
+    this.uvData[0] = 109;
+    this.uvData[2] = 54;
+    this.uvData[4] = 147;
+    this.uvData[6] = 147;
+
+    // V
+    this.uvData[1] = 184;
+    this.uvData[3] = 34;
+    this.uvData[5] = 152;
+    this.uvData[7] = 72;
+  };
+};
+
+class NV21Data extends NVImage {
+  constructor() {
+    super(3, 3, 2, 2, "YUV420SP_NV21");
+
+    this.yData[0] = 16;  this.yData[1] = 82;  this.yData[2] = 144;
+    this.yData[3] = 41;  this.yData[4] = 210; this.yData[5] = 169;
+    this.yData[6] = 107; this.yData[7] = 235; this.yData[8] = 126;
+
+    // U
+    this.uvData[1] = 119;
+    this.uvData[3] = 110;
+    this.uvData[5] = 165;
+    this.uvData[7] = 128;
+
+    // V
+    this.uvData[0] = 156;
+    this.uvData[2] = 25;
+    this.uvData[4] = 175;
+    this.uvData[6] = 128;
+  };
+};
+
+class NV21DataFromYUV444PData extends NVImage {
+  constructor() {
+    super(3, 3, 2, 2, "YUV420SP_NV12");
+
+    this.yData[0] = 16;  this.yData[1] = 82;  this.yData[2] = 144;
+    this.yData[3] = 41;  this.yData[4] = 210; this.yData[5] = 169;
+    this.yData[6] = 107; this.yData[7] = 235; this.yData[8] = 126;
+
+    // V
+    this.uvData[1] = 133;
+    this.uvData[3] = 78;
+    this.uvData[5] = 181;
+    this.uvData[7] = 126;
+
+    // U
+    this.uvData[0] = 145;
+    this.uvData[2] = 74;
+    this.uvData[4] = 176;
+    this.uvData[6] = 106;
+  };
+};
+
+class NV21DataFromYUV422PData extends NVImage {
+  constructor() {
+    super(3, 3, 2, 2, "YUV420SP_NV12");
+
+    this.yData[0] = 16;  this.yData[1] = 82;  this.yData[2] = 144;
+    this.yData[3] = 41;  this.yData[4] = 210; this.yData[5] = 169;
+    this.yData[6] = 107; this.yData[7] = 235; this.yData[8] = 126;
+
+    // V
+    this.uvData[1] = 109;
+    this.uvData[3] = 54;
+    this.uvData[5] = 147;
+    this.uvData[7] = 147;
+
+    // U
+    this.uvData[0] = 184;
+    this.uvData[2] = 34;
+    this.uvData[4] = 152;
+    this.uvData[6] = 72;
+  };
+};
+
+class HSVData extends FloatSimpleImage {
+  constructor() {
+    super(3, 3, 3, "HSV");
+
+    var i = 0;
+    this.data[i + 0] = 0.0;   this.data[i + 1] = 0.0; this.data[i + 2] = 0.0;
+    this.data[i + 3] = 0.0;   this.data[i + 4] = 1.0; this.data[i + 5] = 1.0;
+    this.data[i + 6] = 120.0; this.data[i + 7] = 1.0; this.data[i + 8] = 1.0;
+
+    i += this.width * this.channelCount;
+    this.data[i + 0] = 240.0; this.data[i + 1] = 1.0; this.data[i + 2] = 1.0;
+    this.data[i + 3] = 60.0;  this.data[i + 4] = 1.0; this.data[i + 5] = 1.0;
+    this.data[i + 6] = 180.0; this.data[i + 7] = 1.0; this.data[i + 8] = 1.0;
+
+    i += this.width * this.channelCount;
+    this.data[i + 0] = 300.0; this.data[i + 1] = 1.0; this.data[i + 2] = 1.0;
+    this.data[i + 3] = 0.0;   this.data[i + 4] = 0.0; this.data[i + 5] = 1.0;
+    this.data[i + 6] = 0.0;   this.data[i + 7] = 0.0; this.data[i + 8] = 0.50196081;
+  };
+};
+
+class HSVDataFromLabData extends FloatSimpleImage {
+  constructor() {
+    super(3, 3, 3, "HSV");
+
+    var i = 0;
+    this.data[i + 0] = 0.0;   this.data[i + 1] = 0.0; this.data[i + 2] = 0.0;
+    this.data[i + 3] = 0.0;   this.data[i + 4] = 1.0; this.data[i + 5] = 0.996078;
+    this.data[i + 6] = 120.0; this.data[i + 7] = 1.0; this.data[i + 8] = 1.0;
+
+    i += this.width * this.channelCount;
+    this.data[i + 0] = 240.0;       this.data[i + 1] = 1.0; this.data[i + 2] = 1.0;
+    this.data[i + 3] = 60.235294;   this.data[i + 4] = 1.0; this.data[i + 5] = 1.0;
+    this.data[i + 6] = 179.764706;  this.data[i + 7] = 1.0; this.data[i + 8] = 1.0;
+
+    i += this.width * this.channelCount;
+    this.data[i + 0] = 300.0;       this.data[i + 1] = 1.0;       this.data[i + 2] = 1.0;
+    this.data[i + 3] = 0.0;         this.data[i + 4] = 0.003922;  this.data[i + 5] = 1.0;
+    this.data[i + 6] = 59.999998;   this.data[i + 7] = 0.007813;  this.data[i + 8] = 0.501961;
+  };
+};
+
+class HSVDataFromYUV444PData extends FloatSimpleImage {
+  constructor() {
+    super(3, 3, 3, "HSV");
+
+    var i = 0;
+    this.data[i + 0] = 0.0;                 this.data[i + 1] = 0.0;                 this.data[i + 2] = 0.0;
+    this.data[i + 3] = 0.0;                 this.data[i + 4] = 1.0000000001003937;  this.data[i + 5] = 0.996078431372549;
+    this.data[i + 6] = 120.23715415017372;  this.data[i + 7] = 1.0000000001007905;  this.data[i + 8] = 0.9921568627450981;
+
+    i += this.width * this.channelCount;
+    this.data[i + 0] = 240.0;               this.data[i + 1] = 1.0000000001015936;  this.data[i + 2] = 0.984313725490196;
+    this.data[i + 3] = 59.99999999390438;   this.data[i + 4] = 0.9920948617608696;  this.data[i + 5] = 0.9921568627450981;
+    this.data[i + 6] = 179.76284584377885;  this.data[i + 7] = 1.0000000001007905;  this.data[i + 8] = 0.9921568627450981;
+
+    i += this.width * this.channelCount;
+    this.data[i + 0] = 300.7058823588706;   this.data[i + 1] = 1.0000000001;        this.data[i + 2] = 1.0;
+    this.data[i + 3] = 0.0;                 this.data[i + 4] = 0.0;                 this.data[i + 5] = 0.9921568627450981;
+    this.data[i + 6] = 0.0;                 this.data[i + 7] = 0.0;                 this.data[i + 8] = 0.4980392156862745;
+  };
+};
+
+class HSVDataFromYUV422PData extends FloatSimpleImage {
+  constructor() {
+    super(3, 3, 3, "HSV");
+
+    var i = 0;
+    this.data[i + 0] = 0.0;                 this.data[i + 1] = 1.0000000002865168; this.data[i + 2] = 0.34901960784313724;
+    this.data[i + 3] = 0.0;                 this.data[i + 4] = 0.7696969698515151; this.data[i + 5] = 0.6470588235294118;
+    this.data[i + 6] = 120.23715415017372;  this.data[i + 7] = 1.0000000001007905; this.data[i + 8] = 0.9921568627450981;
+
+    i += this.width * this.channelCount;
+    this.data[i + 0] = 0;                   this.data[i + 1] = 0.0;                 this.data[i + 2] = 0.10980392156862745;
+    this.data[i + 3] = 0;                   this.data[i + 4] = 0.0;                 this.data[i + 5] = 0.8784313725490196;
+    this.data[i + 6] = 179.76284584377885;  this.data[i + 7] = 1.0000000001007905;  this.data[i + 8] = 0.9921568627450981;
+
+    i += this.width * this.channelCount;
+    this.data[i + 0] = 300.93750001176636;  this.data[i + 1] = 0.7111111112527777;  this.data[i + 2] = 0.7058823529411765;
+    this.data[i + 3] = 300.0000000278182;   this.data[i + 4] = 0.21568627460980394; this.data[i + 5] = 1.0;
+    this.data[i + 6] = 0.0;                 this.data[i + 7] = 0.0;                 this.data[i + 8] = 0.4980392156862745;
+  };
+};
+
+class HSVDataFromYUV420PData extends FloatSimpleImage {
+  constructor() {
+    super(3, 3, 3, "HSV");
+
+    var i = 0;
+    this.data[i + 0] = 0.0;                 this.data[i + 1] = 1.0000000005795455;  this.data[i + 2] = 0.17254901960784313;
+    this.data[i + 3] = 359.04761904800455;  this.data[i + 4] = 0.5250000002125;     this.data[i + 5] = 0.47058823529411764;
+    this.data[i + 6] = 148.23529411462184;  this.data[i + 7] = 1.000000000107143;   this.data[i + 8] = 0.9333333333333333;
+
+    i += this.width * this.channelCount;
+    this.data[i + 0] = 358.1250000007471;   this.data[i + 1] = 0.8767123291164385;  this.data[i + 2] = 0.28627450980392155;
+    this.data[i + 3] = 358.800000000612;    this.data[i + 4] = 0.196078431472549;   this.data[i + 5] = 1.0;
+    this.data[i + 6] = 151.85185184850937;  this.data[i + 7] = 0.9529411765705882;  this.data[i + 8] = 1.0;
+
+    i += this.width * this.channelCount;
+    this.data[i + 0] = 300.93750001176636;  this.data[i + 1] = 0.7111111112527777;  this.data[i + 2] = 0.7058823529411765;
+    this.data[i + 3] = 300.0000000278182;   this.data[i + 4] = 0.21568627460980394; this.data[i + 5] = 1.0;
+    this.data[i + 6] = 0.0;                 this.data[i + 7] = 0.0;                 this.data[i + 8] = 0.4980392156862745;
+  };
+};
+
+class LabData extends FloatSimpleImage {
+  constructor() {
+    super(3, 3, 3, "Lab");
+
+    var i = 0;
+    this.data[i + 0] = 0.0;       this.data[i + 1] = 0.0;         this.data[i + 2] = 0.0;
+    this.data[i + 3] = 53.240585; this.data[i + 4] = 80.094185;   this.data[i + 5] = 67.201538;
+    this.data[i + 6] = 87.7351;   this.data[i + 7] = -86.181252;  this.data[i + 8] = 83.177483;
+
+    i += this.width * this.channelCount;
+    this.data[i + 0] = 32.29567;  this.data[i + 1] = 79.186989;   this.data[i + 2] = -107.86176;
+    this.data[i + 3] = 97.139511; this.data[i + 4] = -21.552414;  this.data[i + 5] = 94.475792;
+    this.data[i + 6] = 91.113297; this.data[i + 7] = -48.088551;  this.data[i + 8] = -14.130962;
+
+    i += this.width * this.channelCount;
+    this.data[i + 0] = 60.323502; this.data[i + 1] = 98.235161;   this.data[i + 2] = -60.825493;
+    this.data[i + 3] = 100.0;     this.data[i + 4] = 0.0;         this.data[i + 5] = 0.0;
+    this.data[i + 6] = 53.585014; this.data[i + 7] = 0.0;         this.data[i + 8] = 0.0;
+  };
+};
+
+class LabDataFromYUV444PData extends FloatSimpleImage {
+  constructor() {
+    super(3, 3, 3, "HSV");
+
+    var i = 0;
+    this.data[i + 0] = 0.0;                 this.data[i + 1] = 0.0;                 this.data[i + 2] = 0.0;
+    this.data[i + 3] = 53.034610465388056;  this.data[i + 4] = 79.85590203914461;   this.data[i + 5] = 67.0016253024788;
+    this.data[i + 6] = 87.11875689267448;   this.data[i + 7] = -85.65429374039535;  this.data[i + 8] = 82.60623202041464;
+
+    i += this.width * this.channelCount;
+    this.data[i + 0] = 31.720345672804157;  this.data[i + 1] = 78.24367895044873;   this.data[i + 2] = -106.5768337072531;
+    this.data[i + 3] = 96.46792120648958;   this.data[i + 4] = -21.409519847697347; this.data[i + 5] = 93.77548135780542;
+    this.data[i + 6] = 90.44660871821826;   this.data[i + 7] = -48.089026724461526; this.data[i + 8] = -13.571034820412686;
+
+    i += this.width * this.channelCount;
+    this.data[i + 0] = 60.151950936932494;  this.data[i + 1] = 97.82071254270292;   this.data[i + 2] = -59.43734830934828;
+    this.data[i + 3] = 99.30958687208283;   this.data[i + 4] = 0.0;                 this.data[i + 5] = 0.0;
+    this.data[i + 6] = 53.19277745493915;   this.data[i + 7] = 0.0;                 this.data[i + 8] = 0.0;
+  };
+};
+
+class LabDataFromYUV422PData extends FloatSimpleImage {
+  constructor() {
+    super(3, 3, 3, "HSV");
+
+    var i = 0;
+    this.data[i + 0] = 16.127781199491146; this.data[i + 1] = 37.16386506574049;  this.data[i + 2] = 25.043689417354877;
+    this.data[i + 3] = 36.981683077525915; this.data[i + 4] = 50.903511613481115; this.data[i + 5] = 32.31142038484883;
+    this.data[i + 6] = 87.11875689267448;  this.data[i + 7] = -85.65429374039535; this.data[i + 8] = 82.60623202041464;
+
+    i += this.width * this.channelCount;
+    this.data[i + 0] = 10.268184311230112; this.data[i + 1] = 0.0;                 this.data[i + 2] = 0.0;
+    this.data[i + 3] = 89.1772802290269;   this.data[i + 4] = 0.0;                 this.data[i + 5] = 0.0;
+    this.data[i + 6] = 90.44660871821826;  this.data[i + 7] = -48.089026724461526; this.data[i + 8] = -13.571034820412686;
+
+    i += this.width * this.channelCount;
+    this.data[i + 0] = 46.144074613608296; this.data[i + 1] = 65.16894552944552;  this.data[i + 2] = -40.267933584999625;
+    this.data[i + 3] = 86.8938834807636;   this.data[i + 4] = 28.462923575986455; this.data[i + 5] = -19.464966592414633;
+    this.data[i + 6] = 53.19277745493915;  this.data[i + 7] = 0.0;                this.data[i + 8] = 0.0;
+  };
+};
+
+class LabDataFromYUV420PData extends FloatSimpleImage {
+  constructor() {
+    super(3, 3, 3, "HSV");
+
+    var i = 0;
+    this.data[i + 0] = 4.838519820745088;  this.data[i + 1] = 21.141105340568455; this.data[i + 2] = 7.645700032099379;
+    this.data[i + 3] = 32.31563239702677;  this.data[i + 4] = 27.57546933915833;  this.data[i + 5] = 12.300896526188554;
+    this.data[i + 6] = 83.08065258991849;  this.data[i + 7] = -73.895752859582;   this.data[i + 8] = 47.405921341516176;
+
+    i += this.width * this.channelCount;
+    this.data[i + 0] = 13.450503010545155; this.data[i + 1] = 29.406984528513203;  this.data[i + 2] = 16.333350166067607;
+    this.data[i + 3] = 86.69267491397105;  this.data[i + 4] = 17.77388194061319;   this.data[i + 5] = 6.21670853560361;
+    this.data[i + 6] = 88.693447032887;    this.data[i + 7] = -74.34828426368617;  this.data[i + 8] = 40.64106565615555;
+
+    i += this.width * this.channelCount;
+    this.data[i + 0] = 46.144074613608296; this.data[i + 1] = 65.16894552944552;  this.data[i + 2] = -40.267933584999625;
+    this.data[i + 3] = 86.8938834807636;   this.data[i + 4] = 28.462923575986455; this.data[i + 5] = -19.464966592414633;
+    this.data[i + 6] = 53.19277745493915;  this.data[i + 7] = 0.0;                this.data[i + 8] = 0.0;
+  };
+};
+
+class DepthData extends Uint16SimpleImage {
+  constructor() {
+    super(3, 3, 1, "DEPTH");
+
+    var i = 0;
+    this.data[i + 0] = 2;   this.data[i + 1] = 4;   this.data[i + 2] = 8;
+
+    i += this.width * this.channelCount;
+    this.data[i + 0] = 16;  this.data[i + 1] = 32;  this.data[i + 2] = 64;
+
+    i += this.width * this.channelCount;
+    this.data[i + 0] = 128; this.data[i + 1] = 256; this.data[i + 2] = 512;
+  };
+};
+
+function getData(format, originalFormat) {
+  if (format == "RGBA32") {
+    if (originalFormat == "YUV444P") {
+      return new RGBA32DataFromYUV444PData(0, 1, 2, 3);
+    } else if (originalFormat == "YUV422P") {
+      return new RGBA32DataFromYUV422PData(0, 1, 2, 3);
+    } else if (originalFormat == "YUV420P" ||
+               originalFormat == "YUV420SP_NV12" ||
+               originalFormat == "YUV420SP_NV21") {
+      return new RGBA32DataFromYUV420PData(0, 1, 2, 3);
+    } else {
+      return new RGBA32Data();
+    }
+  } else if (format == "BGRA32") {
+    if (originalFormat == "YUV444P") {
+      return new RGBA32DataFromYUV444PData(2, 1, 0, 3);
+    } else if (originalFormat == "YUV422P") {
+      return new RGBA32DataFromYUV422PData(2, 1, 0, 3);
+    } else if (originalFormat == "YUV420P" ||
+               originalFormat == "YUV420SP_NV12" ||
+               originalFormat == "YUV420SP_NV21") {
+      return new RGBA32DataFromYUV420PData(2, 1, 0, 3);
+    } else {
+      return new BGRA32Data();
+    }
+  } else if (format == "RGB24") {
+    if (originalFormat == "YUV444P") {
+      return new RGBA32DataFromYUV444PData(0, 1, 2);
+    } else if (originalFormat == "YUV422P") {
+      return new RGBA32DataFromYUV422PData(0, 1, 2);
+    } else if (originalFormat == "YUV420P" ||
+               originalFormat == "YUV420SP_NV12" ||
+               originalFormat == "YUV420SP_NV21") {
+      return new RGBA32DataFromYUV420PData(0, 1, 2);
+    } else {
+      return new RGB24Data();
+    }
+  } else if (format == "BGR24") {
+    if (originalFormat == "YUV444P") {
+      return new RGBA32DataFromYUV444PData(2, 1, 0);
+    } else if (originalFormat == "YUV422P") {
+      return new RGBA32DataFromYUV422PData(2, 1, 0);
+    } else if (originalFormat == "YUV420P" ||
+               originalFormat == "YUV420SP_NV12" ||
+               originalFormat == "YUV420SP_NV21") {
+      return new RGBA32DataFromYUV420PData(2, 1, 0);
+    } else {
+      return new BGR24Data();
+    }
+  } else if (format == "GRAY8") {
+    if (originalFormat == "YUV444P" ||
+        originalFormat == "YUV422P" ||
+        originalFormat == "YUV420P" ||
+        originalFormat == "YUV420SP_NV12" ||
+        originalFormat == "YUV420SP_NV21") {
+      return  new Gray8DataFromYUVData();
+    } else {
+      return new Gray8Data();
+    }
+  } else if (format == "YUV444P") {
+    if (originalFormat == "YUV422P") {
+      return new YUV444PDataFromYUV422PData();
+    } else if (originalFormat == "YUV420P" ||
+               originalFormat == "YUV420SP_NV12" ||
+               originalFormat == "YUV420SP_NV21") {
+      return new YUV444PDataFromYUV420PData();
+    } else {
+      return new YUV444PData();
+    }
+  } else if (format == "YUV422P") {
+    if (originalFormat == "YUV444P") {
+      return new YUV422PDataFromYUV444PData();
+    } else if (originalFormat == "YUV420P" ||
+               originalFormat == "YUV420SP_NV12" ||
+               originalFormat == "YUV420SP_NV21") {
+      return new YUV422PDataFromYUV420PData();
+    } else {
+      return new YUV422PData();
+    }
+  } else if (format == "YUV420P") {
+    if (originalFormat == "YUV444P") {
+      return new YUV420PDataFromYUV444PData();
+    } else if (originalFormat == "YUV422P") {
+      return new YUV420PDataFromYUV422PData();
+    } else {
+      return new YUV420PData();
+    }
+  } else if (format == "YUV420SP_NV12") {
+    if (originalFormat == "YUV444P") {
+      return new NV12DataFromYUV444PData();
+    } else if (originalFormat == "YUV422P") {
+      return new NV12DataFromYUV422PData();
+    } else {
+      return new NV12Data();
+    }
+  } else if (format == "YUV420SP_NV21") {
+    if (originalFormat == "YUV444P") {
+      return new NV21DataFromYUV444PData();
+    } else if (originalFormat == "YUV422P") {
+      return new NV21DataFromYUV422PData();
+    } else {
+      return new NV21Data();
+    }
+  } else if (format == "HSV") {
+    if (originalFormat == "YUV444P") {
+      return new HSVDataFromYUV444PData();
+    } else if (originalFormat == "YUV422P") {
+      return new HSVDataFromYUV422PData();
+    } else if (originalFormat == "YUV420P" ||
+               originalFormat == "YUV420SP_NV12" ||
+               originalFormat == "YUV420SP_NV21") {
+      return new HSVDataFromYUV420PData();
+    } else if (originalFormat == "Lab") {
+      return new HSVDataFromLabData();
+    } else {
+      return new HSVData();
+    }
+  } else if (format == "Lab") {
+    if (originalFormat == "YUV444P") {
+      return new LabDataFromYUV444PData();
+    } else if (originalFormat == "YUV422P") {
+      return new LabDataFromYUV422PData();
+    } else if (originalFormat == "YUV420P" ||
+               originalFormat == "YUV420SP_NV12" ||
+               originalFormat == "YUV420SP_NV21") {
+      return new LabDataFromYUV420PData();
+    } else {
+      return new LabData();
+    }
+  } else if (format == "DEPTH") {
+    return new DepthData();
+  }
+}
+
+function getTestData(sourceFromat, destinationFormat) {
+  return [getData(sourceFromat), getData(destinationFormat, sourceFromat)];
+}
\ No newline at end of file
--- a/dom/canvas/test/imagebitmap_extensions_on_worker.js
+++ b/dom/canvas/test/imagebitmap_extensions_on_worker.js
@@ -1,8 +1,9 @@
+importScripts("imagebitmap_extensions_data.js");
 importScripts("imagebitmap_extensions.js");
 
 var gGroundTruthImageData;
 var gImageData;
 var gImageBitmap;
 var gPNGBlob;
 var gJPEGBlob;
 
@@ -25,16 +26,17 @@ onmessage = function(event) {
 };
 
 function ok(expect, msg) {
   postMessage({"type": "status", status: !!expect, msg: msg});
 }
 
 function runTests() {
   testExceptions().
+  then(testColorConversions()).
   then( function() { return Promise.all([testAccessing_randomTest("ImageData", gImageData, 0),
                                          testAccessing_randomTest("ImageBitmap", gImageBitmap, 0),
                                          testAccessing_randomTest("PNG", gPNGBlob, 0),
                                          testAccessing_randomTest("JPEG", gJPEGBlob, 10) // JPEG loses information
                                         ]); }).
   then( function() { return Promise.all([testCreateFromArrayBffer_randomTest("ImageData", gImageData, 0),
                                          testCreateFromArrayBffer_randomTest("ImageBitmap", gImageBitmap, 0),
                                          testCreateFromArrayBffer_randomTest("PNG", gPNGBlob, 0),
--- a/dom/canvas/test/mochitest.ini
+++ b/dom/canvas/test/mochitest.ini
@@ -23,16 +23,17 @@ support-files =
   image_transparent.png
   image_transparent50.png
   image_yellow.png
   image_yellow75.png
   imagebitmap_bug1239300.js
   imagebitmap_bug1239752.js
   imagebitmap_extensions.html
   imagebitmap_extensions.js
+  imagebitmap_extensions_data.js
   imagebitmap_extensions_on_worker.js
   imagebitmap_extensions_prepareSources.js
   imagebitmap_on_worker.js
   imagebitmap_structuredclone.js
   imagebitmap_structuredclone_iframe.html
   imagebitmap_structuredclone_utils.js
   offscreencanvas.js
   offscreencanvas_mask.svg