Commit 18ed29dc authored by oliver@apple.com's avatar oliver@apple.com

Bug 16954: Support putImageData

Reviewed by Sam Weinig.

Implement support for HTML5's putImageData for the CG port.  All other ports
are currently just using stubs for the final blit.


git-svn-id: http://svn.webkit.org/repository/webkit/trunk@30700 268f45cc-cd09-0410-ab3c-d52691b4dbfc
parent c6ec2400
2008-03-01 Oliver Hunt <oliver@apple.com>
Reviewed by Sam Weinig.
Bug 16954: Support putImageData
Nice simple testcase for putImageData covering the exciting
little bits of behaviour present in the current spec.
* fast/canvas/canvas-putImageData-expected.txt: Added.
* fast/canvas/canvas-putImageData.html: Added.
2008-03-01 Sam Weinig <sam@webkit.org>
Reviewed by Darin Adler.
This test ensures that putImageData works correctly, the end result should be a 100x100px green square.
On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
PASS getPixel(0,0) is [0,128,0,255]
PASS getPixel(25,25) is [0,128,0,255]
PASS getPixel(49,0) is [0,128,0,255]
PASS getPixel(0,49) is [0,128,0,255]
PASS getPixel(49,49) is [0,128,0,255]
PASS getPixel(50,0) is [0,0,0,0]
PASS getPixel(0,50) is [0,0,0,0]
PASS getPixel(50,50) is [0,0,0,0]
PASS getPixel(0,50) is [0,128,0,255]
PASS getPixel(25,75) is [0,128,0,255]
PASS getPixel(49,50) is [0,128,0,255]
PASS getPixel(0,99) is [0,128,0,255]
PASS getPixel(49,99) is [0,128,0,255]
PASS getPixel(50,50) is [0,128,0,255]
PASS getPixel(75,75) is [0,128,0,255]
PASS getPixel(99,99) is [0,128,0,255]
PASS getPixel(50,49) is [0,0,0,0]
PASS getPixel(50,0) is [0,128,0,255]
PASS getPixel(50,5) is [0,128,0,255]
PASS getPixel(50,15) is [0,128,0,255]
PASS getPixel(50,25) is [0,128,0,255]
PASS getPixel(50,35) is [0,128,0,255]
PASS getPixel(50,45) is [0,128,0,255]
PASS getPixel(55,0) is [0,128,0,255]
PASS getPixel(55,5) is [0,128,0,255]
PASS getPixel(55,15) is [0,128,0,255]
PASS getPixel(55,25) is [0,128,0,255]
PASS getPixel(55,35) is [0,128,0,255]
PASS getPixel(55,45) is [0,128,0,255]
PASS getPixel(65,0) is [0,128,0,255]
PASS getPixel(65,5) is [0,128,0,255]
PASS getPixel(65,15) is [0,128,0,255]
PASS getPixel(65,25) is [0,128,0,255]
PASS getPixel(65,35) is [0,128,0,255]
PASS getPixel(65,45) is [0,128,0,255]
PASS getPixel(75,0) is [0,128,0,255]
PASS getPixel(75,5) is [0,128,0,255]
PASS getPixel(75,15) is [0,128,0,255]
PASS getPixel(75,25) is [0,128,0,255]
PASS getPixel(75,35) is [0,128,0,255]
PASS getPixel(75,45) is [0,128,0,255]
PASS getPixel(85,0) is [0,128,0,255]
PASS getPixel(85,5) is [0,128,0,255]
PASS getPixel(85,15) is [0,128,0,255]
PASS getPixel(85,25) is [0,128,0,255]
PASS getPixel(85,35) is [0,128,0,255]
PASS getPixel(85,45) is [0,128,0,255]
PASS getPixel(95,0) is [0,128,0,255]
PASS getPixel(95,5) is [0,128,0,255]
PASS getPixel(95,15) is [0,128,0,255]
PASS getPixel(95,25) is [0,128,0,255]
PASS getPixel(95,35) is [0,128,0,255]
PASS getPixel(95,45) is [0,128,0,255]
PASS getPixel(0,25) is [0,128,0,255]
PASS getPixel(99,25) is [0,128,0,255]
PASS getPixel(25,0) is [0,128,0,255]
PASS getPixel(25,99) is [0,128,0,255]
PASS getPixel(50,0) is [0,128,0,255]
PASS getPixel(50,5) is [0,128,0,255]
PASS getPixel(50,15) is [0,128,0,255]
PASS getPixel(50,25) is [0,128,0,255]
PASS getPixel(50,35) is [0,128,0,255]
PASS getPixel(50,45) is [0,128,0,255]
PASS getPixel(55,0) is [0,128,0,255]
PASS getPixel(55,5) is [0,128,0,255]
PASS getPixel(55,15) is [0,128,0,255]
PASS getPixel(55,25) is [0,128,0,255]
PASS getPixel(55,35) is [0,128,0,255]
PASS getPixel(55,45) is [0,128,0,255]
PASS getPixel(65,0) is [0,128,0,255]
PASS getPixel(65,5) is [0,128,0,255]
PASS getPixel(65,15) is [0,128,0,255]
PASS getPixel(65,25) is [0,128,0,255]
PASS getPixel(65,35) is [0,128,0,255]
PASS getPixel(65,45) is [0,128,0,255]
PASS getPixel(75,0) is [0,128,0,255]
PASS getPixel(75,5) is [0,128,0,255]
PASS getPixel(75,15) is [0,128,0,255]
PASS getPixel(75,25) is [0,128,0,255]
PASS getPixel(75,35) is [0,128,0,255]
PASS getPixel(75,45) is [0,128,0,255]
PASS getPixel(85,0) is [0,128,0,255]
PASS getPixel(85,5) is [0,128,0,255]
PASS getPixel(85,15) is [0,128,0,255]
PASS getPixel(85,25) is [0,128,0,255]
PASS getPixel(85,35) is [0,128,0,255]
PASS getPixel(85,45) is [0,128,0,255]
PASS getPixel(95,0) is [0,128,0,255]
PASS getPixel(95,5) is [0,128,0,255]
PASS getPixel(95,15) is [0,128,0,255]
PASS getPixel(95,25) is [0,128,0,255]
PASS getPixel(95,35) is [0,128,0,255]
PASS getPixel(95,45) is [0,128,0,255]
PASS getPixel(0,25) is [0,128,0,255]
PASS getPixel(0,50) is [0,128,0,255]
PASS getPixel(0,75) is [0,128,0,255]
PASS getPixel(99,25) is [0,128,0,255]
PASS getPixel(99,50) is [0,128,0,255]
PASS getPixel(99,75) is [0,128,0,255]
PASS getPixel(25,0) is [0,128,0,255]
PASS getPixel(50,0) is [0,128,0,255]
PASS getPixel(75,0) is [0,128,0,255]
PASS getPixel(25,99) is [0,128,0,255]
PASS getPixel(50,99) is [0,128,0,255]
PASS getPixel(75,99) is [0,128,0,255]
PASS getPixel(0,25) is [0,128,0,255]
PASS getPixel(0,50) is [0,128,0,255]
PASS getPixel(0,75) is [0,128,0,255]
PASS getPixel(10,25) is [0,128,0,255]
PASS getPixel(10,50) is [0,128,0,255]
PASS getPixel(10,75) is [0,128,0,255]
PASS getPixel(99,25) is [0,128,0,255]
PASS getPixel(99,50) is [0,128,0,255]
PASS getPixel(99,75) is [0,128,0,255]
PASS getPixel(89,25) is [0,128,0,255]
PASS getPixel(89,50) is [0,128,0,255]
PASS getPixel(89,75) is [0,128,0,255]
PASS getPixel(25,0) is [0,128,0,255]
PASS getPixel(50,0) is [0,128,0,255]
PASS getPixel(75,0) is [0,128,0,255]
PASS getPixel(25,10) is [0,128,0,255]
PASS getPixel(50,10) is [0,128,0,255]
PASS getPixel(75,10) is [0,128,0,255]
PASS getPixel(25,99) is [0,128,0,255]
PASS getPixel(50,99) is [0,128,0,255]
PASS getPixel(75,99) is [0,128,0,255]
PASS getPixel(25,89) is [0,128,0,255]
PASS getPixel(50,89) is [0,128,0,255]
PASS getPixel(75,89) is [0,128,0,255]
PASS getPixel(11,11) is [0,128,0,255]
PASS getPixel(1,1) is [0,128,0,255]
PASS getPixel(1,1) is [0,128,0,255]
PASS getPixel(10,10) is [0,128,0,255]
PASS getPixel(1,1) is [0,128,0,255]
PASS getPixel(10,10) is [0,128,0,255]
PASS getPixel(1,1) is [0,128,0,255]
PASS getPixel(9,9) is [0,128,0,255]
PASS getPixel(1,1) is [0,128,0,255]
PASS getPixel(9,9) is [0,128,0,255]
PASS getPixel(1,1) is [0,128,0,255]
PASS getPixel(9,9) is [0,128,0,255]
PASS context.putImageData({}, 0, 0) threw exception Error: TYPE_MISMATCH_ERR: DOM Exception 17.
PASS context.putImageData(buffer, NaN, 0, 0, 0, 0, 0) threw exception Error: INDEX_SIZE_ERR: DOM Exception 1.
PASS context.putImageData(buffer, 0, NaN, 0, 0, 0, 0) threw exception Error: INDEX_SIZE_ERR: DOM Exception 1.
PASS context.putImageData(buffer, 0, 0, NaN, 0, 0, 0) threw exception Error: INDEX_SIZE_ERR: DOM Exception 1.
PASS context.putImageData(buffer, 0, 0, 0, NaN, 0, 0) threw exception Error: INDEX_SIZE_ERR: DOM Exception 1.
PASS context.putImageData(buffer, 0, 0, 0, 0, NaN, 0) threw exception Error: INDEX_SIZE_ERR: DOM Exception 1.
PASS context.putImageData(buffer, 0, 0, 0, 0, 0, NaN) threw exception Error: INDEX_SIZE_ERR: DOM Exception 1.
PASS context.putImageData(buffer, Infinity, 0, 0, 0, 0, 0) threw exception Error: INDEX_SIZE_ERR: DOM Exception 1.
PASS context.putImageData(buffer, 0, Infinity, 0, 0, 0, 0) threw exception Error: INDEX_SIZE_ERR: DOM Exception 1.
PASS context.putImageData(buffer, 0, 0, Infinity, 0, 0, 0) threw exception Error: INDEX_SIZE_ERR: DOM Exception 1.
PASS context.putImageData(buffer, 0, 0, 0, Infinity, 0, 0) threw exception Error: INDEX_SIZE_ERR: DOM Exception 1.
PASS context.putImageData(buffer, 0, 0, 0, 0, Infinity, 0) threw exception Error: INDEX_SIZE_ERR: DOM Exception 1.
PASS context.putImageData(buffer, 0, 0, 0, 0, 0, Infinity) threw exception Error: INDEX_SIZE_ERR: DOM Exception 1.
PASS context.putImageData(buffer, undefined, 0, 0, 0, 0, 0) threw exception Error: INDEX_SIZE_ERR: DOM Exception 1.
PASS context.putImageData(buffer, 0, undefined, 0, 0, 0, 0) threw exception Error: INDEX_SIZE_ERR: DOM Exception 1.
PASS context.putImageData(buffer, 0, 0, undefined, 0, 0, 0) threw exception Error: INDEX_SIZE_ERR: DOM Exception 1.
PASS context.putImageData(buffer, 0, 0, 0, undefined, 0, 0) threw exception Error: INDEX_SIZE_ERR: DOM Exception 1.
PASS context.putImageData(buffer, 0, 0, 0, 0, undefined, 0) threw exception Error: INDEX_SIZE_ERR: DOM Exception 1.
PASS context.putImageData(buffer, 0, 0, 0, 0, 0, undefined) threw exception Error: INDEX_SIZE_ERR: DOM Exception 1.
PASS successfullyParsed is true
TEST COMPLETE
<link rel="stylesheet" href="../js/resources/js-test-style.css">
<script src="../js/resources/js-test-pre.js"></script>
<canvas id="canvas" width="100" height="100"></canvas><br/>
<p id="description"></p>
<div id="console"></div>
<script src="canvas-putImageData.js"></script>
<script src="../js/resources/js-test-post.js"></script>
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]);
shouldThrow("context.putImageData({}, 0, 0)", "'Error: TYPE_MISMATCH_ERR: DOM Exception 17'");
shouldThrow("context.putImageData(buffer, NaN, 0, 0, 0, 0, 0)", "'Error: INDEX_SIZE_ERR: DOM Exception 1'");
shouldThrow("context.putImageData(buffer, 0, NaN, 0, 0, 0, 0)", "'Error: INDEX_SIZE_ERR: DOM Exception 1'");
shouldThrow("context.putImageData(buffer, 0, 0, NaN, 0, 0, 0)", "'Error: INDEX_SIZE_ERR: DOM Exception 1'");
shouldThrow("context.putImageData(buffer, 0, 0, 0, NaN, 0, 0)", "'Error: INDEX_SIZE_ERR: DOM Exception 1'");
shouldThrow("context.putImageData(buffer, 0, 0, 0, 0, NaN, 0)", "'Error: INDEX_SIZE_ERR: DOM Exception 1'");
shouldThrow("context.putImageData(buffer, 0, 0, 0, 0, 0, NaN)", "'Error: INDEX_SIZE_ERR: DOM Exception 1'");
shouldThrow("context.putImageData(buffer, Infinity, 0, 0, 0, 0, 0)", "'Error: INDEX_SIZE_ERR: DOM Exception 1'");
shouldThrow("context.putImageData(buffer, 0, Infinity, 0, 0, 0, 0)", "'Error: INDEX_SIZE_ERR: DOM Exception 1'");
shouldThrow("context.putImageData(buffer, 0, 0, Infinity, 0, 0, 0)", "'Error: INDEX_SIZE_ERR: DOM Exception 1'");
shouldThrow("context.putImageData(buffer, 0, 0, 0, Infinity, 0, 0)", "'Error: INDEX_SIZE_ERR: DOM Exception 1'");
shouldThrow("context.putImageData(buffer, 0, 0, 0, 0, Infinity, 0)", "'Error: INDEX_SIZE_ERR: DOM Exception 1'");
shouldThrow("context.putImageData(buffer, 0, 0, 0, 0, 0, Infinity)", "'Error: INDEX_SIZE_ERR: DOM Exception 1'");
shouldThrow("context.putImageData(buffer, undefined, 0, 0, 0, 0, 0)", "'Error: INDEX_SIZE_ERR: DOM Exception 1'");
shouldThrow("context.putImageData(buffer, 0, undefined, 0, 0, 0, 0)", "'Error: INDEX_SIZE_ERR: DOM Exception 1'");
shouldThrow("context.putImageData(buffer, 0, 0, undefined, 0, 0, 0)", "'Error: INDEX_SIZE_ERR: DOM Exception 1'");
shouldThrow("context.putImageData(buffer, 0, 0, 0, undefined, 0, 0)", "'Error: INDEX_SIZE_ERR: DOM Exception 1'");
shouldThrow("context.putImageData(buffer, 0, 0, 0, 0, undefined, 0)", "'Error: INDEX_SIZE_ERR: DOM Exception 1'");
shouldThrow("context.putImageData(buffer, 0, 0, 0, 0, 0, undefined)", "'Error: INDEX_SIZE_ERR: DOM Exception 1'");
var successfullyParsed = true;
2008-03-01 Oliver Hunt <oliver@apple.com>
Reviewed by Sam Weinig.
Bug 16954: Support putImageData
Implement support for HTML5's putImageData for the CG port. All other ports
are currently just using stubs for the final blit.
Test: fast/canvas/canvas-putImageData.html
* bindings/js/JSCanvasRenderingContext2DCustom.cpp:
(WebCore::JSCanvasRenderingContext2D::putImageData):
* html/CanvasRenderingContext2D.cpp:
(WebCore::CanvasRenderingContext2D::putImageData):
* html/CanvasRenderingContext2D.h:
* platform/graphics/ImageBuffer.h:
* platform/graphics/cairo/ImageBufferCairo.cpp:
* platform/graphics/cg/ImageBufferCG.cpp:
(WebCore::ImageBuffer::putImageData):
* platform/graphics/qt/ImageBufferQt.cpp:
* platform/graphics/wx/ImageBufferWx.cpp:
2008-03-01 Jon Honeycutt <jhoneycutt@apple.com>
Reviewed by Darin.
......@@ -28,10 +28,12 @@
#include "FloatRect.h"
#include "HTMLCanvasElement.h"
#include "HTMLImageElement.h"
#include "ImageData.h"
#include "JSCanvasGradient.h"
#include "JSCanvasPattern.h"
#include "JSHTMLCanvasElement.h"
#include "JSHTMLImageElement.h"
#include "JSImageData.h"
#include "kjs_html.h"
using namespace KJS;
......@@ -335,6 +337,19 @@ JSValue* JSCanvasRenderingContext2D::createPattern(ExecState* exec, const List&
JSValue* JSCanvasRenderingContext2D::putImageData(ExecState* exec, const List& args)
{
// putImageData has two variants
// putImageData(ImageData, x, y)
// putImageData(ImageData, x, y, dirtyX, dirtyY, dirtyWidth, dirtyHeight)
CanvasRenderingContext2D* context = impl();
ExceptionCode ec = 0;
if (args.size() >= 7)
context->putImageData(toImageData(args[0]), args[1]->toFloat(exec), args[2]->toFloat(exec),
args[3]->toFloat(exec), args[4]->toFloat(exec), args[5]->toFloat(exec), args[6]->toFloat(exec), ec);
else
context->putImageData(toImageData(args[0]), args[1]->toFloat(exec), args[2]->toFloat(exec), ec);
setDOMException(exec, ec);
return jsUndefined();
}
......
......@@ -39,6 +39,7 @@ PassRefPtr<CanvasPixelArray> CanvasPixelArray::create(unsigned size)
CanvasPixelArray::CanvasPixelArray(unsigned size)
: m_data(size)
{
ASSERT((reinterpret_cast<size_t>(m_data.data()) & 3) == 0);
}
}
......@@ -1202,4 +1202,55 @@ PassRefPtr<ImageData> CanvasRenderingContext2D::getImageData(float sx, float sy,
return buffer->getImageData(scaledRect);
}
void CanvasRenderingContext2D::putImageData(ImageData* data, float dx, float dy, ExceptionCode& ec)
{
if (!data) {
ec = TYPE_MISMATCH_ERR;
return;
}
putImageData(data, dx, dy, 0, 0, data->width(), data->height(), ec);
}
void CanvasRenderingContext2D::putImageData(ImageData* data, float dx, float dy, float dirtyX, float dirtyY,
float dirtyWidth, float dirtyHeight, ExceptionCode& ec)
{
if (!data) {
ec = TYPE_MISMATCH_ERR;
return;
}
if (!isfinite(dx) || !isfinite(dy) || !isfinite(dirtyX) ||
!isfinite(dirtyY) || !isfinite(dirtyWidth) || !isfinite(dirtyHeight)) {
ec = INDEX_SIZE_ERR;
return;
}
ImageBuffer* buffer = m_canvas ? m_canvas->buffer() : 0;
if (!buffer)
return;
if (dirtyWidth < 0) {
dirtyX += dirtyWidth;
dirtyWidth = -dirtyWidth;
}
if (dirtyHeight < 0) {
dirtyY += dirtyHeight;
dirtyHeight = -dirtyHeight;
}
FloatRect clipRect(dirtyX, dirtyY, dirtyWidth, dirtyHeight);
clipRect.intersect(IntRect(0, 0, data->width(), data->height()));
IntSize destOffset(static_cast<int>(dx), static_cast<int>(dy));
IntRect sourceRect = enclosingIntRect(clipRect);
sourceRect.move(destOffset);
sourceRect.intersect(IntRect(IntPoint(), buffer->size()));
if (sourceRect.isEmpty())
return;
willDraw(sourceRect);
sourceRect.move(-destOffset);
IntPoint destPoint(destOffset.width(), destOffset.height());
buffer->putImageData(data, sourceRect, destPoint);
}
} // namespace WebCore
......@@ -166,6 +166,8 @@ namespace WebCore {
PassRefPtr<ImageData> createImageData(float width, float height) const;
PassRefPtr<ImageData> getImageData(float sx, float sy, float sw, float sh) const;
void putImageData(ImageData*, float dx, float dy, ExceptionCode&);
void putImageData(ImageData*, float dx, float dy, float dirtyX, float dirtyY, float dirtyWidth, float dirtyHeight, ExceptionCode&);
void reset();
void detachCanvas() { m_canvas = 0; }
......
......@@ -49,6 +49,7 @@ namespace WebCore {
class GraphicsContext;
class ImageData;
class IntPoint;
class IntRect;
class RenderObject;
......@@ -67,8 +68,10 @@ namespace WebCore {
#elif PLATFORM(CAIRO)
cairo_surface_t* surface() const;
#endif
PassRefPtr<ImageData> getImageData(const IntRect& rect) const;
void putImageData(ImageData* source, const IntRect& sourceRect, const IntPoint& destPoint);
private:
void* m_data;
IntSize m_size;
......
......@@ -80,4 +80,9 @@ PassRefPtr<ImageData> ImageBuffer::getImageData(const IntRect&) const
return 0;
}
void ImageBuffer::putImageData(ImageData*, const IntRect&, const IntPoint&)
{
notImplemented();
}
}
......@@ -75,6 +75,7 @@ ImageBuffer::ImageBuffer(void* imageData, const IntSize& size, auto_ptr<Graphics
, m_context(context.release())
, m_cgImage(0)
{
ASSERT((reinterpret_cast<size_t>(imageData) & 2) == 0);
}
ImageBuffer::~ImageBuffer()
......@@ -136,7 +137,7 @@ PassRefPtr<ImageData> ImageBuffer::getImageData(const IntRect& rect) const
unsigned srcBytesPerRow = 4 * m_size.width();
unsigned destBytesPerRow = 4 * rect.width();
// -originy to handle the accursed flipped y axis
// m_size.height() - originy to handle the accursed flipped y axis in CG backing store
unsigned char* srcRows = reinterpret_cast<unsigned char*>(m_data) + (m_size.height() - originy - 1) * srcBytesPerRow + originx * 4;
unsigned char* destRows = data + desty * destBytesPerRow + destx * 4;
for (int y = 0; y < numRows; ++