chromium/third_party/blink/web_tests/fast/canvas/canvas-blend-image.html

<script src="../../resources/testharness.js"></script>
<script src="../../resources/testharnessreport.js"></script>
<script>
var canvas = document.createElement('canvas');
canvas.width = 200;
canvas.height = 200;
ctx = canvas.getContext('2d', {willReadFrequently: true});

// Create the image for blending test with images.
var img = document.createElement('canvas');
img.width = 200;
img.height = 200;
var imgCtx = img.getContext('2d');
imgCtx.fillStyle = "red";
imgCtx.fillRect(0, 0, 100, 100);
imgCtx.fillStyle = "yellow";
imgCtx.fillRect(100, 0, 100, 100);
imgCtx.fillStyle = "green";
imgCtx.fillRect(100, 100, 100, 100);
imgCtx.fillStyle = "blue";
imgCtx.fillRect(0, 100, 100, 100);

TEST_POINTS = [{x: 50, y: 50}, {x: 150, y: 50}, {x: 150, y: 150}, {x: 50, y: 150}];

// Expected pixel values for various globalCompositeOperations at each testPoint
var BLEND_MODES = [
   {
      "globalCompositeOperation": "source-over",
      "expectedColors": {
         "solidOnSolid": [[255, 0, 0, 255], [255, 255, 0, 255], [0, 128, 0, 255], [0, 0, 255, 255]],
         "solidOnAlpha": [[128, 0, 127, 255], [128, 128, 127, 255], [0, 64, 127, 255], [0, 0, 255, 255]],
         "alphaOnSolid": [[255, 0, 0, 255], [255, 255, 0, 255], [0, 128, 0, 255], [0, 0, 255, 255]],
         "alphaOnAlpha": [[171, 0, 84, 191], [171, 171, 84, 191], [0, 85, 84, 191], [0, 0, 255, 191]]
      }
   }, {
      "globalCompositeOperation": "multiply",
      "expectedColors": {
         "solidOnSolid": [[0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 255, 255]],
         "solidOnAlpha": [[0, 0, 127, 255], [0, 0, 127, 255], [0, 0, 127, 255], [0, 0, 255, 255]],
         "alphaOnSolid": [[128, 0, 0, 255], [128, 128, 0, 255], [0, 64, 0, 255], [0, 0, 255, 255]],
         "alphaOnAlpha": [[85, 0, 84, 191], [85, 85, 84, 191], [0, 43, 84, 191], [0, 0, 255, 191]]
      }
   }, {
      "globalCompositeOperation": "screen",
      "expectedColors": {
         "solidOnSolid": [[255, 0, 255, 255], [255, 255, 255, 255], [0, 128, 255, 255], [0, 0, 255, 255]],
         "solidOnAlpha": [[128, 0, 255, 255], [128, 128, 255, 255], [0, 64, 255, 255], [0, 0, 255, 255]],
         "alphaOnSolid": [[255, 0, 127, 255], [255, 255, 127, 255], [0, 128, 127, 255], [0, 0, 255, 255]],
         "alphaOnAlpha": [[171, 0, 170, 191], [171, 171, 170, 191], [0, 85, 170, 191], [0, 0, 255, 191]]
      }
   }, {
      "globalCompositeOperation": "overlay",
      "expectedColors": {
         "solidOnSolid": [[0, 0, 255, 255], [0, 0, 255, 255], [0, 0, 255, 255], [0, 0, 255, 255]],
         "solidOnAlpha": [[0, 0, 255, 255], [0, 0, 255, 255], [0, 0, 255, 255], [0, 0, 255, 255]],
         "alphaOnSolid": [[128, 0, 127, 255], [128, 128, 127, 255], [0, 64, 127, 255], [0, 0, 255, 255]],
         "alphaOnAlpha": [[85, 0, 170, 191], [85, 85, 170, 191], [0, 43, 170, 191], [0, 0, 255, 191]]
      }
   }, {
      "globalCompositeOperation": "darken",
      "expectedColors": {
         "solidOnSolid": [[0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 255, 255]],
         "solidOnAlpha": [[0, 0, 127, 255], [0, 0, 127, 255], [0, 0, 127, 255], [0, 0, 255, 255]],
         "alphaOnSolid": [[128, 0, 0, 255], [128, 128, 0, 255], [0, 64, 0, 255], [0, 0, 255, 255]],
         "alphaOnAlpha": [[85, 0, 84, 191], [85, 85, 84, 191], [0, 43, 84, 191], [0, 0, 255, 191]]
      }
   }, {
      "globalCompositeOperation": "lighten",
      "expectedColors": {
         "solidOnSolid": [[255, 0, 255, 255], [255, 255, 255, 255], [0, 128, 255, 255], [0, 0, 255, 255]],
         "solidOnAlpha": [[128, 0, 255, 255], [128, 128, 255, 255], [0, 64, 255, 255], [0, 0, 255, 255]],
         "alphaOnSolid": [[255, 0, 127, 255], [255, 255, 127, 255], [0, 128, 127, 255], [0, 0, 255, 255]],
         "alphaOnAlpha": [[171, 0, 170, 191], [171, 171, 170, 191], [0, 85, 170, 191], [0, 0, 255, 191]]
      }
   }, {
      "globalCompositeOperation": "color-dodge",
      "expectedColors": {
         "solidOnSolid": [[0, 0, 255, 255], [0, 0, 255, 255], [0, 0, 255, 255], [0, 0, 255, 255]],
         "solidOnAlpha": [[0, 0, 255, 255], [0, 0, 255, 255], [0, 0, 255, 255], [0, 0, 255, 255]],
         "alphaOnSolid": [[128, 0, 127, 255], [128, 128, 127, 255], [0, 64, 127, 255], [0, 0, 255, 255]],
         "alphaOnAlpha": [[85, 0, 170, 191], [85, 85, 170, 191], [0, 43, 170, 191], [0, 0, 255, 191]]
      }
   }, {
      "globalCompositeOperation": "color-burn",
      "expectedColors": {
         "solidOnSolid": [[0, 0, 255, 255], [0, 0, 255, 255], [0, 0, 255, 255], [0, 0, 255, 255]],
         "solidOnAlpha": [[0, 0, 255, 255], [0, 0, 255, 255], [0, 0, 255, 255], [0, 0, 255, 255]],
         "alphaOnSolid": [[128, 0, 127, 255], [128, 128, 127, 255], [0, 64, 127, 255], [0, 0, 255, 255]],
         "alphaOnAlpha": [[85, 0, 170, 191], [85, 85, 170, 191], [0, 42, 170, 191], [0, 0, 255, 191]]
      }
   }, {
      "globalCompositeOperation": "hard-light",
      "expectedColors": {
         "solidOnSolid": [[255, 0, 0, 255], [255, 255, 0, 255], [0, 1, 0, 255], [0, 0, 255, 255]],
         "solidOnAlpha": [[128, 0, 127, 255], [128, 128, 127, 255], [0, 0, 127, 255], [0, 0, 255, 255]],
         "alphaOnSolid": [[255, 0, 0, 255], [255, 255, 0, 255], [0, 65, 0, 255], [0, 0, 255, 255]],
         "alphaOnAlpha": [[171, 0, 84, 191], [171, 171, 84, 191], [0, 43, 84, 191], [0, 0, 255, 191]]
      }
   }, {
      "globalCompositeOperation": "soft-light",
      "expectedColors": {
         "solidOnSolid": [[0, 0, 255, 255], [0, 0, 255, 255], [0, 0, 255, 255], [0, 0, 255, 255]],
         "solidOnAlpha": [[0, 0, 255, 255], [0, 0, 255, 255], [0, 0, 255, 255], [0, 0, 255, 255]],
         "alphaOnSolid": [[128, 0, 127, 255], [128, 128, 127, 255], [0, 64, 127, 255], [0, 0, 255, 255]],
         "alphaOnAlpha": [[85, 0, 170, 191], [85, 85, 170, 191], [0, 43, 170, 191], [0, 0, 255, 191]]
      }
   }, {
      "globalCompositeOperation": "difference",
      "expectedColors": {
         "solidOnSolid": [[255, 0, 255, 255], [255, 255, 255, 255], [0, 128, 255, 255], [0, 0, 0, 255]],
         "solidOnAlpha": [[128, 0, 255, 255], [128, 128, 255, 255], [0, 64, 255, 255], [0, 0, 127, 255]],
         "alphaOnSolid": [[255, 0, 127, 255], [255, 255, 127, 255], [0, 128, 127, 255], [0, 0, 128, 255]],
         "alphaOnAlpha": [[171, 0, 170, 191], [171, 171, 170, 191], [0, 85, 170, 191], [0, 0, 171, 191]]
      }
   }, {
      "globalCompositeOperation": "exclusion",
      "expectedColors": {
         "solidOnSolid": [[255, 0, 255, 255], [255, 255, 255, 255], [0, 128, 255, 255], [0, 0, 0, 255]],
         "solidOnAlpha": [[128, 0, 255, 255], [128, 128, 255, 255], [0, 64, 255, 255], [0, 0, 127, 255]],
         "alphaOnSolid": [[255, 0, 127, 255], [255, 255, 127, 255], [0, 128, 127, 255], [0, 0, 128, 255]],
         "alphaOnAlpha": [[171, 0, 170, 191], [171, 171, 170, 191], [0, 85, 170, 191], [0, 0, 171, 191]]
      }
   }, {
      "globalCompositeOperation": "hue",
      "expectedColors": {
         "solidOnSolid": [[93, 0, 0, 255], [31, 31, 0, 255], [0, 46, 0, 255], [0, 0, 255, 255]],
         "solidOnAlpha": [[49, 0, 127, 255], [16, 16, 127, 255], [0, 25, 127, 255], [0, 0, 255, 255]],
         "alphaOnSolid": [[175, 0, 0, 255], [144, 144, 0, 255], [0, 88, 0, 255], [0, 0, 255, 255]],
         "alphaOnAlpha": [[116, 0, 84, 191], [96, 96, 84, 191], [0, 58, 84, 191], [0, 0, 255, 191]]
      }
   }, {
      "globalCompositeOperation": "saturation",
      "expectedColors": {
         "solidOnSolid": [[0, 0, 255, 255], [0, 0, 255, 255], [14, 14, 142, 255], [0, 0, 255, 255]],
         "solidOnAlpha": [[0, 0, 255, 255], [0, 0, 255, 255], [7, 7, 198, 255], [0, 0, 255, 255]],
         "alphaOnSolid": [[128, 0, 127, 255], [128, 128, 127, 255], [7, 71, 70, 255], [0, 0, 255, 255]],
         "alphaOnAlpha": [[85, 0, 167, 191], [85, 85, 167, 191], [4, 48, 130, 191], [0, 0, 255, 191]]
      }
   }, {
      "globalCompositeOperation": "color",
      "expectedColors": {
         "solidOnSolid": [[93, 0, 0, 255], [31, 31, 0, 255], [0, 47, 0, 255], [0, 0, 255, 255]],
         "solidOnAlpha": [[49, 0, 127, 255], [16, 16, 127, 255], [0, 24, 127, 255], [0, 0, 255, 255]],
         "alphaOnSolid": [[175, 0, 0, 255], [144, 144, 0, 255], [0, 88, 0, 255], [0, 0, 255, 255]],
         "alphaOnAlpha": [[116, 0, 84, 191], [96, 96, 84, 191], [0, 58, 84, 191], [0, 0, 255, 191]]
      }
   }, {
      "globalCompositeOperation": "luminosity",
      "expectedColors": {
         "solidOnSolid": [[55, 55, 255, 255], [224, 224, 255, 255], [54, 54, 255, 255], [0, 0, 255, 255]],
         "solidOnAlpha": [[28, 28, 255, 255], [112, 112, 255, 255], [27, 27, 255, 255], [0, 0, 255, 255]],
         "alphaOnSolid": [[155, 27, 127, 255], [239, 239, 127, 255], [26, 90, 127, 255], [0, 0, 255, 255]],
         "alphaOnAlpha": [[104, 19, 167, 191], [158, 158, 167, 191], [16, 58, 167, 191], [0, 0, 255, 191]]
      }
   }
];

TEST_SCENARIOS = [
   {"name": 'solidOnSolid', "alpha": 1, "globalAlpha": 1,},
   {"name": 'solidOnAlpha', "alpha": 1, "globalAlpha": 0.5,},
   {"name": 'alphaOnSolid', "alpha": 0.5, "globalAlpha": 1,},
   {"name": 'alphaOnAlpha', "alpha": 0.5, "globalAlpha": 0.5,}
];

// Combine every alpha combination with every globalCompositeOperation
const testsToRun = [];
BLEND_MODES.forEach((blendMode) => {
   TEST_SCENARIOS.forEach((testScenario) => {
      const parameters = Object.assign({}, blendMode, testScenario);
      const name = `${parameters.name} with globalCompositeOperation: ${parameters.globalCompositeOperation}`;
      testsToRun.push([name, parameters]);
   });
});



function pixelDataAtPoint(i) {
   return ctx.getImageData(TEST_POINTS[i].x, TEST_POINTS[i].y, 1, 1).data;
}

function checkBlendModeResult(expectedColors, sigma) {
   for (var i = 0; i < TEST_POINTS.length; i++) {
      var resultColor = pixelDataAtPoint(i);
      assert_approx_equals(resultColor[0], expectedColors[i][0], sigma);
      assert_approx_equals(resultColor[1], expectedColors[i][1], sigma);
      assert_approx_equals(resultColor[2], expectedColors[i][2], sigma);
      assert_approx_equals(resultColor[3], expectedColors[i][3], sigma);
   }
}

function testBlending(parameters) {
   ctx.clearRect(0, 0, 200, 200);
   ctx.save();

   // Draw backdrop.
   ctx.fillStyle = 'rgba(0, 0, 255, ' + parameters.alpha + ')';
   ctx.fillRect(0, 0, 200, 200);

   // Apply blend mode.
   ctx.globalCompositeOperation = parameters.globalCompositeOperation;
   ctx.globalAlpha = parameters.globalAlpha;
   ctx.drawImage(img, 0, 0);
   checkBlendModeResult(parameters.expectedColors[parameters.name], 5);
   ctx.restore();
}

test(function(t) {
   generate_tests(testBlending, testsToRun);
}, 'Series of tests to ensure correct results on applying different blend modes.');
</script>