John Schulz continues to do my job for me.
@desandro re: [putImageData is a complete jerk] I don’t know if this has been suggested already, but use drawImage instead [http://jsfiddle.net/JFSIII/Ga5jf/]
The thing pulling it all together is converting the canvas into a data URL and using that as an image:
var img = new Image();
img.src = canvas2.toDataURL('image/png');
ctx2.drawImage(img, 30, 10);
Gonna have to give him a back rub one of these days.
Using canvas’ putImageData() method completely overwrites the pixels it replaces. Take a look.
var renderCanvasExamples = function() {
var canvas1 = document.getElementById('two-square-example'),
canvas2 = document.getElementById('put-image-data-example');
if ( !(canvas1.getContext && canvas1.getContext('2d') ) ) {
return;
}
var fillColor = 'hsla(0,100%,50%,.4)';
strokeColor = 'hsla(240,100%,50%,.4)',
// convienence function for rendering circles
circle = function( ctx, x, y, radius ) {
ctx.beginPath();
ctx.arc ( x, y, radius, 0, Math.PI*2, true);
ctx.fill();
ctx.stroke();
ctx.closePath();
};
// example 1 renders 3 circles
var ctx1 = canvas1.getContext('2d');
ctx1.fillStyle = fillColor;
ctx1.strokeStyle = strokeColor;
ctx1.lineWidth = 2;
circle( ctx1, 60, 60, 50);
circle( ctx1, 90, 70, 50);
circle( ctx1, 120, 80, 50);
// example 2 renders 1 circle,
// then uses `putImageData` to render the next 2
var ctx2 = canvas2.getContext('2d')
ctx2.fillStyle = fillColor;
ctx2.strokeStyle = strokeColor;
ctx2.lineWidth = 2;
circle( ctx2, 60, 60, 50);
// limit imgData to 100 x 100 pixels so Firefox can display
var imgData = ctx2.getImageData(0, 0, 100, 100);
ctx2.putImageData( imgData, 30, 10 );
ctx2.putImageData( imgData, 60, 20 );
};
window.addEventListener( 'DOMContentLoaded', renderCanvasExamples, false);
Each canvas has a yellow background to exhibit transparency. The first canvas has the same three circles overlaid on top of each other. As their fillStyle color has partial opacity, the red color is built up.
The second example uses getImageData() to capture a snapshot of the current canvas context. That image is then re-rendered at an offset position. Instead of three circles overlapping one another, the area of the putImageData output blows out the pixels underneath.
Neither translate() nor rotate() will have any effect on subsequent putImageData() calls.
Also of note is that Firefox with throw an error if the output of putImageData() extends outside the bounds of the canvas. In this example, I had to crop the dimension of getImageData() so the putImageData() output would fit inside the canvas. If I kept getImageData to the original canvas dimensions, Firefox returns with An invalid or illegal string was specified" code: "12.
Collectively, this is all pretty disappointing, as putImageData had tremendous potential. It would be especially useful to use putImageData as a way of reproducing Photoshop layers, or brushes, or all sorts of wondrous techniques that will have to be hacked together.
John Schulz, regarding using getImageData() on remote images:
@desandro See http://bit.ly/cTS0Vd for ideas on loading remote images. It uses jQuery, but loading img-to-json URL+cb as script should work.
From the $.getImageData page:
$.getImageData allows anyone to get an image from another domain and have pixel level access to it using the
getImageData()method. It works by sending a request with the URL of the image to google’s servers via the Google App Engine. The server then converts the image into base64 encoded data URL and sends the image back as a JSON object. This means that the image can be locally included on the website and therefore it can be edited by thecanvastag.
Information leakage can occur if scripts from one origin can access information (e.g. read pixels) from images from another origin (one that isn’t the same).
To mitigate this,
canvaselements are defined to have a flag indicating whether they are origin-clean. Allcanvaselements must start with their origin-clean set to true. The flag must be set to false if any of the following actions occur:
- The element’s 2D context’s
drawImage()method is called with anHTMLImageElementor anHTMLVideoElementwhose origin is not the same as that of theDocumentobject that owns thecanvaselement.
…
Whenever the
toDataURL()method of a canvas element whose origin-clean flag is set to false is called, the method must raise aSECURITY_ERRexception.Whenever the
getImageData()method of the 2D context of a canvas element whose origin-clean flag is set to false is called with otherwise correct arguments, the method must raise aSECURITY_ERRexception.
I was pretty jazzed to get Close Pixelate working on all over the web. Dynamically pulling in images from Flickr would have been fun. Turns out the HTML5 Spec explicitly disallows using getImageData or toDataURL() on images not hosted on the same domain of the current page.