I want to do pixel-true rendering of some images on my Canvas. Right now, I obtain the images through Javascript, so my images are HTMLImageElement instances. I can draw these on the Canvas' rendering context with drawImage. However, this performs anti-aliasing on the image, which I don't want.
There appears to be a lower-level image manipulation method named putImageData, operating on ImageData objects. Does this method perform any anti-aliasing? If not, it's a fine candidate for what I'm looking for, but I haven't found out how I can convert or blit an HTMLImageElement to an ImageData instance.
Any advice would be welcome!
Edit: my original problem was solved, I accidentally had a coordinate that was fractional, which forces anti-aliasing. The conversion-to-image-data question still stands though.
The only way to convert an image into an ImageData object is to draw it to a canvas first, so you'll need to create a temporary canvas, draw the image on it, and get the image data from there.
function imageToImageData(image) {
var canvas = document.createElement("canvas");
canvas.width = image.width;
canvas.height = image.height;
var ctx = canvas.getContext("2d");
ctx.drawImage(image, 0, 0);
return ctx.getImageData(0, 0, canvas.width, canvas.height);
}
Note though, that the same-origin policy prevents you from calling getImageData if you draw an image from a different domain to the canvas, so this will only work on images from the same domain as the document. If you need to draw images from other domains, your only option is to call drawImage on the context for you main canvas directly, making sure there are no transformations that will affect the accuracy.
Related
I have a primary canvas element which I perform drawing operations on using the context. I can successfully draw images, shapes, and also transform those objects using the context.
I now need my own textbox. I want to create another canvas, render the text to that canvas, then copy that data onto the primary canvas... the catch is I want to have it transformable, almost is if I was just calling the drawImage function.
There exists the getImageData and putImageData methods, but those do not work because it does not take into consideration the transformation.
I am looking for something like this...
var tempcanvas = document.createElement("canvas");
var ctx = tempcanvas.getContext('2d');
ctx.fillRect(0,0,5,5);
then on my primary canvas which I already have, I would like to draw that as if it were an image object.
context = maincanvas.getContext('2d');
context.translate(50,50);
context.rotate(180);
context.drawImage(tempcanvas)
I have two canvases that are different sizes. My goal is copy the user's drawing from the main canvas to a second canvas as a scaled down version. So far the drawImage() and scale appear to be working, but the second canvas keeps the old version of the main drawing along with the new copy. I tried clearing it each time before calling drawImage(), but that doesn't appear to do anything. How can I copy just the current image to my secondary canvas each time the function runs?
$('#hand').dblclick(function(){
var canvas = document.getElementById('myCanvas');
var context = canvas.getContext('2d');
//var imageData = context.getImageData(0, 0, 100, 100);
var newCanvas = document.getElementById('scaledCanvas');
var destCtx = newCanvas.getContext('2d');
destCtx.clearRect(0, 0, newCanvas.width, newCanvas.height);
destCtx.scale(.5,.5);
destCtx.drawImage(canvas, 0, 0);
});
I can include more code if necessary. I also just realized that scale keeps getting called; this explains why the new copied image would get smaller each time as well, so that might be another problem.
It's quite simple actually, you're using what's called a transform (translate, rotate, or scale).
In order to use them "freshly" each time you must save and restore the canvas state each time.
$('#hand').dblclick(function(){
var canvas = document.getElementById('myCanvas');
var context = canvas.getContext('2d');
//var imageData = context.getImageData(0, 0, 100, 100);
var newCanvas = document.getElementById('scaledCanvas');
var destCtx = newCanvas.getContext('2d');
destCtx.clearRect(0, 0, newCanvas.width, newCanvas.height);
//save the current state of this canvas' drawing mode
destCtx.save();
destCtx.scale(.5,.5);
destCtx.drawImage(canvas, 0, 0);
//restore destCtx to a 1,1 scale (and also 0,0 origin and 0 rotation)
destCtx.restore();
});
It's also important to note you can push several times before calling restore, in order to perform many cool geometric tricks using recursive functions etc...
Take a look at this explanation of states and transformations:
https://developer.mozilla.org/en-US/docs/HTML/Canvas/Tutorial/Transformations
Hope this helps you understand canvas transforms a bit better.
I'm trying to use JavaScript drawImage to draw from a buffer canvas to another in Firefox; I'm calling the draw multiple times per frame using a fairly large canvas. My memory usage shoots through the roof in Firefox, but barely peaks in Chrome. I'm curious about the reason for this behavior and if there's a workaround to free the memory used (I'm assuming) by drawn images after they're no longer needed.
I need to render using globalCompositeOperation = 'source-in', so that's why I'm using this method.
Here's the basic idea:
var canvas = document.createElement('canvas');
var ctx = canvas.getContext('2d');
//set height and width of canvas to browser window
var dummyCanvas = document.createElement('canvas');
var dummyctx = dummyCanvas.getContext('2d');
dummyCanvas.width = canvas.width;
dummyCanvas.height = canvas.height;
function draw() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
//draw some stuff on normal canvas
dummyCtx.clearRect(0, 0, canvas.width, canvas.height);
//draw a polygon on buffer canvas
dummyctx.globalCompositeOperation = 'source-in';
//draw another polygon on buffer canvas
ctx.drawImage(dummyctx.canvas, 0, 0);
//draw some more stuff on normal canvas
}
Is this memory problem just a bug in Firefox? Am I doing something wrong? Are there any workarounds?
Thank you so much for any help!
I notixed that images are somehow piled on each other in a canvas, when I dra more than one. Maybe it would help clearing te canvas, before draing to it again:
context.clearRect(0, 0, canvas.width, canvas.height);
var w = canvas.width;
canvas.width = 1;
canvas.width = w;
I took this from a image resize that I dod not so long ago: http://boxed.hu/articles/html5-client-side-image-resize/
But, this is only a tip - so let me know how it worked.
I had a memory leak problem with the jquery.rotate plugin increasing the memory use on IE and found that although drawing takes up memory, the problem was when the original image was replaced by the manipulated image. Apparently the images were just piling up in memory. The line was:
p.parentNode.replaceChild(canvas, p);
so I changed to use the jQuery function replaceWith() and the memory stopped stacking up after rotating several images:
$(p).replaceWith(canvas);
Looking at the replaceWith function, it actually removes the object (probably in the end uses removeChild and appendChild) and appends the new one to the DOM. My guess is that there's a difference on how the browsers implement replaceChild().
I want to take an irregularly shaped section from an existing image and render it as a new image in Javascript using HTML5 canvases. So, only the data inside the polygon boundary will be copied. The approach I came up with involved:
Draw the polygon in a new canvas.
Create a mask using clip
Copy the data from the original canvas using getImageData (a rectangle)
Apply the data to the new canvas using putImageData
It didn't work, the entire rectangle (e.g. the stuff from the source outside the boundary) is still appearing. This question explains why:
"The spec says that putImageData will not be affected by clipping regions." Dang!
I also tried drawing the shape, setting context.globalCompositeOperation = "source-in", and then using putImageData. Same result: no mask applied. I suspect for a similar reason.
Any suggestions on how to accomplish this goal? Here's basic code for my work in progress, in case it's not clear what I'm trying to do. (Don't try too hard to debug this, it's cleaned up/extracted from code that uses a lot of functions that aren't here, just trying to show the logic).
// coords is the polygon data for the area I want
context = $('canvas')[0].getContext("2d");
context.save();
context.beginPath();
context.moveTo(coords[0], coords[1]);
for (i = 2; i < coords.length; i += 2) {
context.lineTo(coords[i], coords[i + 1]);
}
//context.closePath();
context.clip();
$img = $('#main_image');
copy_canvas = new_canvas($img); // just creates a new canvas matching dimensions of image
copy_ctx = copy.getContext("2d");
tempImage = new Image();
tempImage.src = $img.attr("src");
copy_ctx.drawImage(tempImage,0,0,tempImage.width,tempImage.height);
// returns array x,y,x,y with t/l and b/r corners for a polygon
corners = get_corners(coords)
var data = copy_ctx.getImageData(corners[0],corners[1],corners[2],corners[3]);
//context.globalCompositeOperation = "source-in";
context.putImageData(data,0,0);
context.restore();
dont use putImageData,
just make an extra in memory canvas with document.createElement to create the mask and apply that with a drawImage() and the globalCompositeOperation function (depending on the order you need to pick the right mode;
I do something similar here the code is here (mind the CasparKleijne.Canvas.GFX.Composite function)
Is there any way to create a deep copy of a canvas element with all drawn content?
Actually the correct way to copy the canvas data is to pass the old canvas to the new blank canvas. Try this function.
function cloneCanvas(oldCanvas) {
//create a new canvas
var newCanvas = document.createElement('canvas');
var context = newCanvas.getContext('2d');
//set dimensions
newCanvas.width = oldCanvas.width;
newCanvas.height = oldCanvas.height;
//apply the old canvas to the new one
context.drawImage(oldCanvas, 0, 0);
//return the new canvas
return newCanvas;
}
Using getImageData is for pixel data access, not for copying canvases. Copying with it is very slow and hard on the browser. It should be avoided.
You can call
context.getImageData(0, 0, context.canvas.width, context.canvas.height);
which will return an ImageData object. This has a property named data of type CanvasPixelArray which contains the rgb and transparency values of all the pixels. These values are not references to the canvas so can be changed without affecting the canvas.
If you also want a copy of the element, you could create a new canvas element and then copy all attributes to the new canvas element. After that you can use the
context.putImageData(imageData, 0, 0);
method to draw the ImageData object onto the new canvas element.
See this answer for more detail getPixel from HTML Canvas? on manipulating the pixels.
You might find this mozilla article useful as well https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/Canvas_tutorial/Drawing_shapes