description("This test ensures that putImageData works correctly, the end result should be a 100x100px green square."); function fillRect(imageData, x, y, width, height, r, g, b, a) { var bytesPerRow = imageData.width * 4; var data =imageData.data; for (var i = 0; i < height; i++) { var rowOrigin = (y+i) * bytesPerRow; rowOrigin += x * 4; for (var j = 0; j < width; j++) { var position = rowOrigin + j * 4; data[position + 0] = r; data[position + 1] = g; data[position + 2] = b; data[position + 3] = a; } } } function dataToArray(data) { var result = new Array(data.length) for (var i = 0; i < data.length; i++) result[i] = data[i]; return result; } function getPixel(x, y) { var data = context.getImageData(x,y,1,1); if (!data) // getImageData failed, which should never happen return [-1,-1,-1,-1]; return dataToArray(data.data); } function pixelShouldBe(x, y, colour) { shouldBe("getPixel(" + [x, y] +")", "["+colour+"]"); } var canvas = document.getElementById("canvas"); var context = canvas.getContext("2d"); if (!context.createImageData) context.createImageData = function(w,h) { var data = this.getImageData(0, 0, w, h); for (var i = 0; i < data.data.length; i++) data.data[i] = 0; } var buffer = context.createImageData(100,100); // Fill top left corner fillRect(buffer, 0, 0, 50, 50, 0, 128,0,255); context.putImageData(buffer, 0, 0); pixelShouldBe( 0, 0, [0, 128,0,255]); pixelShouldBe(25, 25, [0, 128,0,255]); pixelShouldBe(49, 0, [0, 128,0,255]); pixelShouldBe( 0, 49, [0, 128,0,255]); pixelShouldBe(49, 49, [0, 128,0,255]); pixelShouldBe(50, 0, [0, 0, 0, 0]); pixelShouldBe( 0, 50, [0, 0, 0, 0]); pixelShouldBe(50, 50, [0, 0, 0, 0]); // Test positioned drawing -- make bottom right green context.putImageData(buffer, 0, 50); pixelShouldBe( 0, 50, [0, 128,0,255]); pixelShouldBe(25, 75, [0, 128,0,255]); pixelShouldBe(49, 50, [0, 128,0,255]); pixelShouldBe( 0, 99, [0, 128,0,255]); pixelShouldBe(49, 99, [0, 128,0,255]); // Test translation doesn't effect putImageData context.translate(50, -50); context.putImageData(buffer, 50, 50); pixelShouldBe(50, 50, [0, 128,0,255]); pixelShouldBe(75, 75, [0, 128,0,255]); pixelShouldBe(99, 99, [0, 128,0,255]); pixelShouldBe(50, 49, [0, 0, 0, 0]); context.translate(-50, 50); // Test dirty rect handling buffer = context.createImageData(50,50); fillRect(buffer, 0, 0, 50, 50, 0, 128, 0, 255); context.putImageData(buffer, 50, 0); fillRect(buffer, 0, 0, 50, 50, 255, 0, 0, 255); context.putImageData(buffer, 50, 0, 15, 15, 20, 20); context.fillStyle="rgb(0,128,0)"; context.fillRect(65, 15, 20, 20); var points = [0, 5, 15, 25, 35, 45]; for (var x = 0; x < points.length; x++) for (var y = 0; y < points.length; y++) pixelShouldBe(points[x] + 50, points[y], [0, 128, 0, 255]); // Test drawing outside the canvas border fillRect(buffer, 0, 0, 50, 50, 255, 0, 0, 255); context.putImageData(buffer, -50, 0); pixelShouldBe(0, 25, [0, 128,0,255]); context.putImageData(buffer, 100, 0); pixelShouldBe(99, 25, [0, 128,0,255]); context.putImageData(buffer, 0, -50); pixelShouldBe(25, 0, [0, 128,0,255]); context.putImageData(buffer, 0, 100); pixelShouldBe(25, 99, [0, 128,0,255]); // test drawing with non-intersecting dirty rect context.putImageData(buffer, 50, 0, 50, 0, 100, 100); context.putImageData(buffer, 50, 0, -50, 0, 50, 100); context.putImageData(buffer, 50, 0, 0, 50, 100, 100); context.putImageData(buffer, 50, 0, 50, -50, 100, 100); for (var x = 0; x < points.length; x++) for (var y = 0; y < points.length; y++) pixelShouldBe(points[x] + 50, points[y], [0, 128, 0, 255]); // Test drawing to region intersect edge of canvas buffer = context.createImageData(100, 100); fillRect(buffer, 0, 0, 100, 100, 0, 128, 0, 255); fillRect(buffer, 10, 10, 80, 80, 255, 0, 0, 255); //left edge context.putImageData(buffer, -90, 0); pixelShouldBe(0, 25, [0, 128,0,255]); pixelShouldBe(0, 50, [0, 128,0,255]); pixelShouldBe(0, 75, [0, 128,0,255]); //right edge context.putImageData(buffer, 90, 0); pixelShouldBe(99, 25, [0, 128,0,255]); pixelShouldBe(99, 50, [0, 128,0,255]); pixelShouldBe(99, 75, [0, 128,0,255]); //top edge context.putImageData(buffer, 0, -90); pixelShouldBe(25, 0, [0, 128,0,255]); pixelShouldBe(50, 0, [0, 128,0,255]); pixelShouldBe(75, 0, [0, 128,0,255]); //bottom edge context.putImageData(buffer, 0, 90); pixelShouldBe(25, 99, [0, 128,0,255]); pixelShouldBe(50, 99, [0, 128,0,255]); pixelShouldBe(75, 99, [0, 128,0,255]); // Test drawing with only part of the dirty region intersecting the window // left edge context.putImageData(buffer, 0, 0, -90, 0, 100, 100); pixelShouldBe(0, 25, [0, 128,0,255]); pixelShouldBe(0, 50, [0, 128,0,255]); pixelShouldBe(0, 75, [0, 128,0,255]); pixelShouldBe(10, 25, [0, 128,0,255]); pixelShouldBe(10, 50, [0, 128,0,255]); pixelShouldBe(10, 75, [0, 128,0,255]); //right edge context.putImageData(buffer, 0, 0, 90, 0, 100, 100); pixelShouldBe(99, 25, [0, 128,0,255]); pixelShouldBe(99, 50, [0, 128,0,255]); pixelShouldBe(99, 75, [0, 128,0,255]); pixelShouldBe(89, 25, [0, 128,0,255]); pixelShouldBe(89, 50, [0, 128,0,255]); pixelShouldBe(89, 75, [0, 128,0,255]); // top edge context.putImageData(buffer, 0, 0, 0, -90, 100, 100); pixelShouldBe(25, 0, [0, 128,0,255]); pixelShouldBe(50, 0, [0, 128,0,255]); pixelShouldBe(75, 0, [0, 128,0,255]); pixelShouldBe(25, 10, [0, 128,0,255]); pixelShouldBe(50, 10, [0, 128,0,255]); pixelShouldBe(75, 10, [0, 128,0,255]); //bottom edge context.putImageData(buffer, 0, 0, 0, 90, 100, 100); pixelShouldBe(25, 99, [0, 128,0,255]); pixelShouldBe(50, 99, [0, 128,0,255]); pixelShouldBe(75, 99, [0, 128,0,255]); pixelShouldBe(25, 89, [0, 128,0,255]); pixelShouldBe(50, 89, [0, 128,0,255]); pixelShouldBe(75, 89, [0, 128,0,255]); // Test clamping of dx/dy var smallbuffer = context.createImageData(10, 10); fillRect(smallbuffer, 0, 0, 10, 10, 255, 0, 0, 255); context.putImageData(smallbuffer, 1.5, 1); pixelShouldBe(11, 11, [0, 128,0,255]); fillRect(smallbuffer, 0, 0, 10, 10, 0, 128, 0, 255); context.putImageData(smallbuffer, 1.5, 1); pixelShouldBe(1, 1, [0, 128,0,255]); // test clamping of dirtyX/Y/Width/Height fillRect(smallbuffer, 0, 0, 10, 10, 0, 128, 0, 255); context.fillStyle = "red"; context.fillRect(1, 1, 9, 9); context.putImageData(smallbuffer, 1, 1, 0.5, 0.5, 8.5, 8.5); pixelShouldBe(1, 1, [0, 128,0,255]); pixelShouldBe(10, 10, [0, 128,0,255]); context.fillRect(1, 1, 9, 9); context.putImageData(smallbuffer, 1, 1, 0.25, 0.25, 9, 9); pixelShouldBe(1, 1, [0, 128,0,255]); pixelShouldBe(10, 10, [0, 128,0,255]); context.fillRect(1, 1, 8, 8); context.putImageData(smallbuffer, 1, 1, 0.0, 0.0, 8.5, 8.5); pixelShouldBe(1, 1, [0, 128,0,255]); pixelShouldBe(9, 9, [0, 128,0,255]); context.fillRect(1, 1, 8, 8); context.putImageData(smallbuffer, 1, 1, 0.0, 0.0, 8.25, 8.25); pixelShouldBe(1, 1, [0, 128,0,255]); pixelShouldBe(9, 9, [0, 128,0,255]); context.fillRect(1, 1, 7, 7); context.putImageData(smallbuffer, 1, 1, 0.5, 0.5, 7.9, 7.9); pixelShouldBe(1, 1, [0, 128,0,255]); pixelShouldBe(9, 9, [0, 128,0,255]); shouldThrowErrorName("context.putImageData({}, 0, 0)", "TypeError"); shouldThrow("context.putImageData(buffer, NaN, 0, 0, 0, 0, 0)", "'TypeError: Value NaN is outside the range [-2147483648, 2147483647]'"); shouldThrow("context.putImageData(buffer, 0, NaN, 0, 0, 0, 0)", "'TypeError: Value NaN is outside the range [-2147483648, 2147483647]'"); shouldThrow("context.putImageData(buffer, 0, 0, NaN, 0, 0, 0)", "'TypeError: Value NaN is outside the range [-2147483648, 2147483647]'"); shouldThrow("context.putImageData(buffer, 0, 0, 0, NaN, 0, 0)", "'TypeError: Value NaN is outside the range [-2147483648, 2147483647]'"); shouldThrow("context.putImageData(buffer, 0, 0, 0, 0, NaN, 0)", "'TypeError: Value NaN is outside the range [-2147483648, 2147483647]'"); shouldThrow("context.putImageData(buffer, 0, 0, 0, 0, 0, NaN)", "'TypeError: Value NaN is outside the range [-2147483648, 2147483647]'"); shouldThrow("context.putImageData(buffer, Infinity, 0, 0, 0, 0, 0)", "'TypeError: Value Infinity is outside the range [-2147483648, 2147483647]'"); shouldThrow("context.putImageData(buffer, 0, Infinity, 0, 0, 0, 0)", "'TypeError: Value Infinity is outside the range [-2147483648, 2147483647]'"); shouldThrow("context.putImageData(buffer, 0, 0, Infinity, 0, 0, 0)", "'TypeError: Value Infinity is outside the range [-2147483648, 2147483647]'"); shouldThrow("context.putImageData(buffer, 0, 0, 0, Infinity, 0, 0)", "'TypeError: Value Infinity is outside the range [-2147483648, 2147483647]'"); shouldThrow("context.putImageData(buffer, 0, 0, 0, 0, Infinity, 0)", "'TypeError: Value Infinity is outside the range [-2147483648, 2147483647]'"); shouldThrow("context.putImageData(buffer, 0, 0, 0, 0, 0, Infinity)", "'TypeError: Value Infinity is outside the range [-2147483648, 2147483647]'"); shouldThrow("context.putImageData(buffer, undefined, 0, 0, 0, 0, 0)", "'TypeError: Value NaN is outside the range [-2147483648, 2147483647]'"); shouldThrow("context.putImageData(buffer, 0, undefined, 0, 0, 0, 0)", "'TypeError: Value NaN is outside the range [-2147483648, 2147483647]'"); shouldThrow("context.putImageData(buffer, 0, 0, undefined, 0, 0, 0)", "'TypeError: Value NaN is outside the range [-2147483648, 2147483647]'"); shouldThrow("context.putImageData(buffer, 0, 0, 0, undefined, 0, 0)", "'TypeError: Value NaN is outside the range [-2147483648, 2147483647]'"); shouldThrow("context.putImageData(buffer, 0, 0, 0, 0, undefined, 0)", "'TypeError: Value NaN is outside the range [-2147483648, 2147483647]'"); shouldThrow("context.putImageData(buffer, 0, 0, 0, 0, 0, undefined)", "'TypeError: Value NaN is outside the range [-2147483648, 2147483647]'"); // Ensure we don't mess up bounds clipping checks var rectcanvas = document.createElement("canvas"); rectcanvas.width = 20; rectcanvas.height = 10; var rectbuffer = rectcanvas.getContext("2d"); rectbuffer.putImageData(smallbuffer, 10, 0); var rectcanvas = document.createElement("canvas"); rectcanvas.width = 10; rectcanvas.height = 20; var rectbuffer = rectcanvas.getContext("2d"); rectbuffer.putImageData(smallbuffer, 0, 10);