HTML5 canvas image caching/putImageData questions - javascript

I'm using the HTML5 canvas to load a single instance of an image which I then blit multiple times onto a single canvas. The image needs some slight pixel-based manipulation in order to customise it. My initial plan of attack had been to load the image, blit it to a backing canvas, draw my modifications on-top of it, and then grab the image data and cache it for future use.
Here's some code I've written to that effect:
context.drawImage(img, 0, 0);
context.fillStyle = '#ffffff';
context.fillRect(0, 0, 2, 2); // Draw a 2x2 rectangle of white pixels on the top left of the image
imageData = context.getImageData(0, 0, img.width, img.height);
cusomImage = imageData;
While this works, I've noticed that my image (which is a transparent PNG) does not maintain transparency. Instead, when using putImageData to place it onto my front-facing canvas, it is rendered with a black background. How do I maintain transparency?
Any suggestions are welcome!

putImageData() does not do what you might first expect:
http://dropshado.ws/post/1244700472/putimagedata-is-a-complete-jerk
putImageData() direct overrides the pixels of the canvas. So if you draw over something else on the same canvas it will not draw "over" it, it will instead replace the pixels of the canvas in the area with it's pixels.
I ran into this exact issue and finally found out why.
As for a solution, I haven't tried this yet but it seems promising:
Why is putImageData so slow?
[EDIT]: I tested this method and it works fine for me, my data is now displaying transparency correctly.

The canvas is black after being created. Make it transparent first with:
context.fillStyle = 'rgba(0,0,0,0)';
context.fillRect(0, 0, width, height);

Related

How to download canvas with background image?

I have a canvas element that I'm setting the background on dynamically via code. Then I'm using the Sketch library (http://intridea.github.io/sketch.js/) to draw on the canvas. - This all works.
However, whenever I try to convert the canvas using canvas.toDataURL("image/png") it's able to save the canvas drawing, however isn't saving the background. - I understand this is working as designed.
Is there a way to merge the two? I was toying around with the idea that I could set the image src to the background src after I'm done drawing and try to export that, however I'm not certain. Does anyone have any experience with this?
As you've discovered, the canvas and its background are maintained separately and toDataURL will not also capture the background.
You can combine the background with the sketch using canvas compositing.
The 'destination-over' compositing mode will let you drawImage your background behind the sketches
context.globalCompositeOperation="destination-over";
context.drawImage(backgroundImage,0,0);
Now the background pixels have been drawn behind you sketch and both will be captured with toDataURL.
Yes, You are correct. I fetch the background image along with canvas image and download.
ctx.width = 2503;
ctx.height = 250;
ctx.globalCompositeOperation="destination-over";
var background = new Image();
background.src = "http://localhost/xxxxx/xxxxx/xxxxx/xxxxx/ecg_back.png";
ctx.drawImage(background, 0, 0);
// create pattern
var ptrn = ctx.createPattern(background, 'repeat'); // Create a pattern with this image, and set it to "repeat".
ctx.fillStyle = ptrn;
ctx.fillRect(0, 0, ctx.width, ctx.height);
How are you adding the background to the canvas? Are you setting it in css as a background image? Or are you adding the image directly to the canvas? I think you'll need to do the latter, as per the example here.

Drawing a Canvas into a smaller Canvas not working

For a Project I want to take the content of a canvas (Called SAVE_CV) and display it in another, smaller canvas.
Some things that I am aware of so far that could be causing me problems: resizing a canvas clears its content, the JS-size of a canvas is different from the CSS-size.
I want the smaller canvas to be 500px wide and appropriately high.
function restoreTaggingCV() {
var cv = document.getElementById( 'taggingCV' );
var ctx = cv.getContext( "2d" );
var styleHeight = SAVE_CV.height * 500 / SAVE_CV.width;
ctx.drawImage(SAVE_CV, 0, 0, cv.width, cv.height);
}
This is my Code so far. Whenever I try to resize the smaller canvas appropriately it only gives me a blank canvas with nothing in it. I tried to set the size with "cv.height = X" and "cv.style.height = styleHeight + 'px'" but neither worked. Also I would love to set the width of the canvas using CSS.
Appreciate any help.
EDIT
I want the image in a picture because later I want the user to mark areas in the smaller version which I then want to use to create individual imaged from the big version. I want to visualise thise area to the user. I probably could do all this by using an image and putting divs over it or something but I just fell more comfident using a canvas since I am pritty new to HTML and CSS.
Try using the CanvasRenderingContext2d.prototype.scale method. It sets the scale factor of the canvas and renders anything in the current state with it's dimensions multiplied by the factor.
So before you use the drawImage function, you scale the context appropriately (in this case, down). For example:
context.save();
context.scale(0.5, 0.5);
context.drawImage(canvas, 0, 0);
context.restore();
This would render the canvas on the context at 0.5 times it's current size. See in this fiddle how I used it to mirror a larger canvas onto a smaller, separate one.
Canvas objects don't like to be resised. After drawing Your image simply convert it toDataURL() and set as image source. They You may resize image as you want.
$img.attr('src',canvas.toDataURL());

When I draw my SVG contents to a canvas, the SVG is cropped

I have a bunch of lines of SVG text that I need to draw to a canvas. I'm converting my SVG object into an SVG data URI, applying that to an image's source, and then drawing that image to the canvas, but it's clipping the text after a certain width and height for some reason.
I know the issue isn't the canvas size because I'm also drawing other images to the canvas first (that're way wider and taller than the text) with no problems. Another weird thing is, if I take the image and append it to the body, it comes out perfectly.
var imageText = new Image();
imageText.src = "data:image/svg+xml;base64," + btoa($("#text_container").html());
imageText.onload = function() { context.drawImage(imageText, 0, 0); };
#text_container is my DIV that holds all the SVG code.
EDIT: To give more detail, here's a comment I wrote below: I'm building a JS application that lets users create a custom football. You can change different colors and features of the ball and text, so it boils down to several DIVs with some SVG text (because the text goes along an arc'd path). I can take the background-images of the DIVs and draw it onto the canvas just fine, to create the football. I have problems when I try to draw the text onto the canvas because it's being cropped. Then I'm going to take that canvas element and turn it into a PNG for the user to save.
I moved the text up and to the left more so you could see the cropping better. The ball draws just fine, as you can see. http://i.imgur.com/Sngu4.png
I had this exact same problem, but I think I just found the solution.
It's not enough just to set a width and a height on your SVG element. Apparently, you also need to set the viewBox:
<svg width="720" height="400" viewBox="0 0 720 400"></svg>
This should render the image correctly on the canvas. :)
A shot in the dark.... does drawImage() have some sort of size parameters you can set? Maybe it has a default that it's drawing to in size, then clipping the rest. Just a thought...
It could also be that, since your image is dynamically created, there's some sort of image size parameters that are missing that would normally be included in say a .jpg, which the drawImage() function could be looking for.
Assuming that context is your canvas' RenderingContext ("2D"), you should include the "destiny width" and "destiny height" parameters on your context.drawImage() function:
context.drawImage(image, dx, dy, dw, dh)
If myCanvas is the id of your canvas object (and $ is jQuery's function), you can call:
var dw = $('myCanvas').width();
var dh = $('myCanvas').height();
context.drawImage(image, 0, 0, dw, dh);
This will scale the image in order to fit the whole canvas. My advice is to first "see" what the svg image is (i.e. add it to the body and measure in which coordinates the text is: sx, sy, sw and sh, in the link above), because you probably has to crop it "on purpose" in order to position it where you want:
context.drawImage(image, sx, sy, sw, sh, dx, dy, dw, dh)

Rubber Banding in HTML5

I wish to draw a rectangle on my canvas but stretching the start point. but how do i erase the previous rectangles drawn during the process. I mean if my background color is red and i want to draw a black rectangle over it. while erasing the intermediate rectangles drawn during rubber banding, i wish to retain the background.
The question is a little hard to understand, but I'm assuming what you want to do is the following using getImageData and putImageData:
// save the entire canvas (in this example, its 500 x 500) to be restored later
image = context.getImageData(0, 0, 500, 500);
function draw() {
context.clearRect(0, 0, canvas.width, canvas.height); // clear entire canvas
context.putImageData(image, 0, 0); // restore entire canvas saved previously
/** Now, draw other stuff on top of the canvas **/
}
You need to redraw the portion of the background the black rectangle previously occupied. It may be simpler to just redraw the entire background.
You simply have to keep track of every single object you want to draw and redraw every single one of them each frame.
You can optimize the process a bit, but you must keep track of each thing drawn so you can redraw.

Hit detection on non-transparent pixel

Given a PNG in a web context with some transparent pixels and some non-transparent pixels, is there a way in Javascript to determine if a user has clicked on a non-transparent pixel? A webkit-only solution would be perfectly acceptable.
1) Create HTML5 canvas of same size as your image
2) Get Canvas's context, drawImage(yourImage, 0, 0)
3) d = context.getImageData(0, 0, w of img, h of img)
4) d.data[(y*width+x)*4+3] for the alpha
canvas = document.createElement("canvas"); //Create HTML5 canvas: supported in latest firefox/chrome/safari/konquerer. Support in IE9
canvas.width = img.width; //Set width of your rendertarget
canvas.height = img.height; // \ height \ \ \
ctx = canvas.getContext("2d"); //Get the 2d context [the thing you draw on]
ctx.drawImage(img, 0, 0); //Draw the picture on it.
id = ctx.getImageData(0,0, img.width, img.height); //Get the pixelData of image
//id.data[(y*width+x)*4+3] for the alpha value of pixel at x,y, 0->255
I know these things are out of fashion these days, but HTML image maps are still valid, and can accomplish adding hit targets to nearly-arbitrary shapes within an image. If you don't actually want to reload another page on click, you could probably change the anchor in the URL with this technique and pick up the change with a Javascript interval.
Canvas is the way to go for this purpose. But also remember that older internet explorer versions will not be capable of the getImageData() function. Even if you include excanvas.
I made a small jquery plugin exactly for this purpose, maybe it will help you solving your problem without to completely reinvent the wheel. http://www.cw-internetdienste.de/pixelselection/

Categories