<!DOCTYPE HTML>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script>

var srgbPixels =
    [[155,27,27,128, "srgbRed"],  [27,155,27,128, "srgbGreen"],
     [27,27,155,128, "srgbBlue"], [27,27,27,128, "srgbBlack"]];

var e_srgbPixels =
    [[0.607422, 0.121521, 0.121521, 0.501953, "e_sRgbRed"],
     [0.121521, 0.607422, 0.121521, 0.501953, "e_sRgbGreen"],
     [0.121521, 0.121521, 0.607422, 0.501953, "e_sRgbBlue"],
     [0.121521, 0.121521, 0.121521, 0.501953, "e_sRgbBlack"]];

var xWidth = xHeight = 2;
var colorCorrectionToleranceSRGB = 1;
// The error level in SRGB->WCG_U8->SRGB is higher than normal color correction
// operations as the pixels are clipped in WCG when U8 storage is used instead
// of F16 storage.
var colorCorrectionToleranceWCG_U8toSRGB_U8 = 6;
var colorCorrectionToleranceWCG = 0.02;

function testBlankPixels(actualData, width, height)
{
  var message = "This pixel should be transparent black";
  var blankData = new Array(4 * width * height).fill(0);
  assert_array_equals(actualData, blankData, message);
}

function testPixels(actualData, expectedPixels, tolerance)
{
  assert_equals(actualData.length, 4 * xWidth * xHeight);
  for (var i = 0; i < xWidth * xHeight; i++) {
    var message = "This pixel should be " + expectedPixels[i][4];
    for (var j = 0; j < 4; j++)
      assert_approx_equals(actualData[i*4+j], expectedPixels[i][j], tolerance,
                           message);
  }
}

function checkImageDataSettings(expectColorSetting, imageData) {
  var imageDataSettings = imageData.getSettings();
  assert_equals(expectColorSetting.colorSpace,
                imageDataSettings.colorSpace);
  assert_equals(expectColorSetting.storageFormat,
                imageDataSettings.storageFormat);
}

function checkImageDataColorValues(canvasColorSettings, imageData, isBlank = '',
  width = xWidth, height = xHeight, isWCG_U8toSRGB_U8 = '') {
  var data = imageData.data;
  if (isBlank === 'isBlank') {
    testBlankPixels(data, width, height);
  } else {
    var expectedPixels = srgbPixels;
    var tolerance = colorCorrectionToleranceSRGB;
    if (isWCG_U8toSRGB_U8 === 'isWCG_U8toSRGB_U8')
      tolerance = colorCorrectionToleranceWCG_U8toSRGB_U8;
    if (canvasColorSettings.pixelFormat != "uint8") {
      expectedPixels = e_srgbPixels;
      tolerance = colorCorrectionToleranceWCG;
    }
    testPixels(data, expectedPixels, tolerance);
  }
}

function initializeColorManagedCanvas(canvasColorSettings)
{
  var canvas = document.createElement('canvas');
  canvas.width = xWidth;
  canvas.height = xHeight;
  var ctx = canvas.getContext('2d',
    {colorSpace: canvasColorSettings.colorSpace,
     pixelFormat: canvasColorSettings.pixelFormat});
  ctx.fillStyle = "rgba(155, 27, 27, 0.5)";
  ctx.fillRect(0, 0, xWidth/2, xHeight/2);
  ctx.fillStyle = "rgba(27, 155, 27, 0.5)";
  ctx.fillRect(xWidth/2, 0, xWidth/2, xHeight/2);
  ctx.fillStyle = "rgba(27, 27, 155, 0.5)";
  ctx.fillRect(0, xHeight/2, xWidth/2, xHeight/2);
  ctx.fillStyle = "rgba(27, 27, 27, 0.5)";
  ctx.fillRect(xWidth/2, xHeight/2, xWidth/2, xHeight/2);
  return canvas;
}

var canvasColorSettingsSet = [
  {name: "SRGB", colorSettings: {colorSpace: "srgb", pixelFormat: "uint8"},
  imageSetting: {colorSpace: "srgb", storageFormat: "uint8"}},
  {name: "e-SRGB", colorSettings: {colorSpace: "srgb", pixelFormat: "float16"},
  imageSetting: {colorSpace: "srgb", storageFormat: "float32"}},
];

var srgbImageDataU8, e_srgbImageDataU16, e_srgbImageDataF32;

function PreparePredefinedImageDataObjects() {
  var canvas = document.createElement('canvas');
  var ctx = canvas.getContext('2d');
  srgbImageDataU8 = ctx.createImageData(xWidth, xHeight,
    {colorSpace: "srgb", storageFormat: "uint8"});
  e_srgbImageDataU16 = ctx.createImageData(xWidth, xHeight,
    {colorSpace: "srgb", storageFormat: "uint16"});
  e_srgbImageDataF32 = ctx.createImageData(xWidth, xHeight,
    {colorSpace: "srgb", storageFormat: "float32"});

  for (var i = 0; i < xWidth * xHeight; i++)
    for (var j = 0; j < 4; j++) {
      srgbImageDataU8.data[i*4+j] = srgbPixels[i][j];
      e_srgbImageDataU16.data[i*4+j] =
          Math.round(e_srgbPixels[i][j] * 65535);
      e_srgbImageDataF32.data[i*4+j] = e_srgbPixels[i][j];
    }
}

PreparePredefinedImageDataObjects();
var imageDataSettingsSet = [
  {name: "SRGB U8", colorSettings: {colorSpace: "srgb", storageFormat: "uint8"},
      imageData: srgbImageDataU8},
  {name: "e-SRGB U16", colorSettings: {colorSpace: "srgb",
      storageFormat: "uint16"}, imageData: e_srgbImageDataU16},
  {name: "e-SRGB F32", colorSettings: {colorSpace: "srgb",
      storageFormat: "float32"}, imageData: e_srgbImageDataF32},
];

////////////////////////////////////////////////////////////////////////////////

// * ImageData imagedata = ctx.createImageData(width, height);
// No color conversion. imagedata follows the color settings of the canvas.

function runTestCreateImageDataWH(canvasColorSettings, imageSetting) {
  var canvas = initializeColorManagedCanvas(canvasColorSettings);
  var ctx = canvas.getContext('2d');
  var imageData = ctx.createImageData(xWidth, xHeight, imageSetting);
  checkImageDataSettings(imageSetting, imageData);
}

var testScenariosCreateImageDataWH = [];
for (var i = 0; i < canvasColorSettingsSet.length; i++) {
  var message = "Test createImageData(width, height) from " +
                canvasColorSettingsSet[i].name + " canvas ";
  testScenariosCreateImageDataWH.
      push([message, canvasColorSettingsSet[i].colorSettings,
      canvasColorSettingsSet[i].imageSetting]);
}

function runTestCreateImageDataWHTests() {
  for (var i = 0; i < testScenariosCreateImageDataWH.length; i++){
    var t = test(function() {
      runTestCreateImageDataWH(testScenariosCreateImageDataWH[i][1],
      testScenariosCreateImageDataWH[i][2]);
    }, testScenariosCreateImageDataWH[i][0]);
  }
}
runTestCreateImageDataWHTests();

////////////////////////////////////////////////////////////////////////////////

// * ImageData imagedata = ctx.getImageData(sx, sy, sw, sh);
// No color conversion. imagedata follows the color settings of the canvas.

function runTestGetImageDataXYWH(canvasColorSettings, imageSetting) {
  var canvas = initializeColorManagedCanvas(canvasColorSettings);
  var ctx = canvas.getContext('2d');
  var imageData = ctx.getImageData(0, 0, xWidth, xHeight, imageSetting);
  checkImageDataSettings(imageSetting, imageData);
  checkImageDataColorValues(canvasColorSettings, imageData);
}

var testScenariosGetImageDataXYWH = [];
for (var i = 0; i < canvasColorSettingsSet.length; i++) {
  var message = "Test getImageData(sx, sy, sw, sh) from " +
                canvasColorSettingsSet[i].name + " canvas ";
  testScenariosGetImageDataXYWH.
      push([message, canvasColorSettingsSet[i].colorSettings,
      canvasColorSettingsSet[i].imageSetting]);
}

function runTestGetImageDataXYWHTests() {
  for (var i = 0; i < testScenariosGetImageDataXYWH.length; i++){
    var t = test(function() {
      runTestGetImageDataXYWH(testScenariosGetImageDataXYWH[i][1],
          testScenariosGetImageDataXYWH[i][2]);
    }, testScenariosGetImageDataXYWH[i][0]);
  }
}
runTestGetImageDataXYWHTests();

////////////////////////////////////////////////////////////////////////////////

// * void ctx.putImageData(imagedata, dx, dy, ...);
// Color conversion, if needed, to the color settings of the canvas.

function runTestPutImageDataDxDy(canvasColorSettings, imageData, imageSetting) {
  var canvas = document.createElement('canvas');
  canvas.width = xWidth * 2;
  canvas.height = xHeight * 2;
  var ctx = canvas.getContext('2d',
      {colorSpace: canvasColorSettings.colorSpace,
       pixelFormat: canvasColorSettings.pixelFormat});
  ctx.putImageData(imageData, xWidth/2, xHeight/2);
  var ctxImageData = ctx.getImageData(xWidth/2, xHeight/2, xWidth, xHeight, imageSetting);
  checkImageDataSettings(imageSetting, ctxImageData);
  checkImageDataColorValues(canvasColorSettings, ctxImageData, 'noBlank',
                            xWidth, xHeight, 'isWCG_U8toSRGB_U8');
}

var testScenariosPutImageDataDxDy = [];
for (var i = 0; i < canvasColorSettingsSet.length; i++) {
  for (var j = 0; j < imageDataSettingsSet.length; j++) {
    var message = "Test putImageData(imagedata, dx, dy): " +
                canvasColorSettingsSet[i].name + " canvas, " +
                imageDataSettingsSet[j].name + " ImageData";
    testScenariosPutImageDataDxDy.
        push([message, canvasColorSettingsSet[i].colorSettings,
              imageDataSettingsSet[j].imageData,
              canvasColorSettingsSet[i].imageSetting]);
  }
}

function runTestPutImageDataDxDyTests() {
  for (var i = 0; i < testScenariosPutImageDataDxDy.length; i++){
    var t = test(function() {
      runTestPutImageDataDxDy(
        testScenariosPutImageDataDxDy[i][1], testScenariosPutImageDataDxDy[i][2],
        testScenariosPutImageDataDxDy[i][3]);
    }, testScenariosPutImageDataDxDy[i][0]);
  }
}
runTestPutImageDataDxDyTests();

////////////////////////////////////////////////////////////////////////////////

// * ImageData imageData = ctx.createImageData(imagedata);
// Color conversion, if needed, to the color settings of the canvas. The
// returned imageData should be transparent black.

function runTestCreateImageDataFromImageData(canvasColorSettings, imageData) {
  var canvas = document.createElement('canvas');
  canvas.width = xWidth * 2;
  canvas.height = xHeight * 2;
  var ctx = canvas.getContext('2d',
      {colorSpace: canvasColorSettings.colorSpace,
       pixelFormat: canvasColorSettings.pixelFormat});
  var ctxImageData = ctx.createImageData(imageData);
  checkImageDataSettings(imageData.getSettings(), ctxImageData);
  checkImageDataColorValues(canvasColorSettings, ctxImageData, 'isBlank');
}

var testScenariosCreateImageDataFromImageData = [];
for (var i = 0; i < canvasColorSettingsSet.length; i++) {
  for (var j = 0; j < imageDataSettingsSet.length; j++) {
    var message = "Test createImageData(imagedata): " +
                canvasColorSettingsSet[i].name + " canvas, " +
                imageDataSettingsSet[j].name + " ImageData";
    testScenariosCreateImageDataFromImageData.
        push([message, canvasColorSettingsSet[i].colorSettings,
              imageDataSettingsSet[j].imageData,
              canvasColorSettingsSet[i].imageSetting]);
  }
}

function runTestCreateImageDataFromImageDataTests() {
  for (var i = 0; i < testScenariosCreateImageDataFromImageData.length; i++){
    var t = test(function() {
      runTestCreateImageDataFromImageData(
        testScenariosCreateImageDataFromImageData[i][1],
        testScenariosCreateImageDataFromImageData[i][2]);
    }, testScenariosCreateImageDataFromImageData[i][0]);
  }
}
runTestCreateImageDataFromImageDataTests();

////////////////////////////////////////////////////////////////////////////////

// *ImageData ctx.createImageData(width, height, imageDataSettings);
// No color conversion to the color settings of the canvas. Blank ImageData
// should be created based on imageDataSettings.

function runTestCreateImageDataWHC(canvasColorSettings, imageDataSettings) {
  var canvas = initializeColorManagedCanvas(canvasColorSettings);
  var ctx = canvas.getContext('2d');
  width = height = 3;
  var imageData = ctx.createImageData(width, height, imageDataSettings);
  var colorSettings = imageData.getSettings();
  assert_equals(colorSettings.colorSpace,
                imageDataSettings.colorSpace,
                "colorSpace should match");
  assert_equals(colorSettings.storageFormat,
                imageDataSettings.storageFormat,
                "storageFormat should match");
  var blankData = new Array(4 * width * height).fill(0);
  assert_array_equals(imageData.data, blankData,
                      "ImageData should be transparent black");
}

var testScenariosCreateImageDataWHC = [];
for (var i = 0; i < canvasColorSettingsSet.length; i++) {
  for (var j = 0; j < imageDataSettingsSet.length; j++) {
    message = "Test createImageData(width, height, imageDataSettings): " +
              canvasColorSettingsSet[i].name + " canvas, " +
              imageDataSettingsSet[j].name + " ImageData";
    testScenariosCreateImageDataWHC.
        push([message, canvasColorSettingsSet[i].colorSettings,
              imageDataSettingsSet[j].colorSettings]);
  }
}

function runTestCreateImageDataWHCTests() {
  for (var i = 0; i < testScenariosCreateImageDataWHC.length; i++){
    var t = test(function() {
      runTestCreateImageDataWHC(
        testScenariosCreateImageDataWHC[i][1],
        testScenariosCreateImageDataWHC[i][2]);
    }, testScenariosCreateImageDataWHC[i][0]);
  }
}
runTestCreateImageDataWHCTests();

////////////////////////////////////////////////////////////////////////////////

// *ImageData ctx.createImageData(data, width, height, imageDataSettings);
// No color conversion to the color settings of the canvas. ImageData is created
// from param "data" with proper storage format and is tagged with the
// CanvasColorSpace which is given in imageDataSettings.

function runTestCreateImageDataDWHC(canvasColorSettings, imageData) {
  var data = imageData.data;
  width = xWidth;
  height = xHeight;
  var colorSettings = imageData.getSettings();

  var canvas = initializeColorManagedCanvas(canvasColorSettings);
  var ctx = canvas.getContext('2d');
  var newImageData = ctx.createImageData(data, width, height, colorSettings);
  var newColorSettings = newImageData.getSettings();
  assert_equals(newColorSettings.colorSpace,
                colorSettings.colorSpace,
                "colorSpace should match");
  assert_equals(newColorSettings.storageFormat,
                colorSettings.storageFormat,
                "storageFormat should match");
  assert_array_equals(newImageData.data, imageData.data,
                      "ImageData should be transparent black");
}

////////////////////////////////////////////////////////////////////////////////

</script>
