I don't know the correct term, but in GTK I believe it was called a pixel buffer. You could copy all or some of the drawing area to a pixbuf, and then later dump the pixbuf back to the screen, rather than going through and rendering the entire thing all over again. I am implementing a menubar, and the menubar drops down and occludes everything underneath it. However, it takes a few seconds to draw the entire canvas, so I was wondering if there is a correct way to copy everything that will be occluded by the drop down menu, then when the drop down menu is closed, redraw it to the screen. I imagine this can be done with the context.getImageData() function, but I have read that it is extremely inefficient.
It is true, getImageData() is far too inefficient. But there's a better start for specifically what you're trying to do:
With canvas context's drawImage method, you can pass in an image but you can also pass in another canvas. So construct a temp canvas that will never be added to the page:
// only exists in javascript, not on the page
tempcanvas = document.createElement('canvas');
tempcanvas.height = (normal canvas width);
tempcanvas.width = (normal canvas height);
Then you can call tempcanvasContext.drawImage(normalCanvas, 0, 0) to take a snapshot of the current canvas right before the drop down menu is created. When the drop down menu disappears, you call normalcanvasContext.drawImage(tempcanvas, 0, 0) to draw it back.
I hope this gives a good general idea, and it should be much faster than getImageData. You can make it even more efficient by only copying the exact portions of the screen you want to redraw.
Related
I have a webgl canvas. It is being continuously updated (simulation).
Now I want to freeze the current content of the canvas. I am continuously getting updates for the simulation which I need to keep feeding to the visualizer. So my idea of achieving this is to clone the exact state of the current webgl canvas on to a new one, and hide the current one, which continues to get updated. Then I can remove the frozen one and the live simulation is being shown again.
I haven't been able to achieve this, and examples I've found on the web like this one:Any way to clone HTML5 canvas element with its content?
only apply to 2D canvases.
Google search didn't help much either.
This one:
how to copy another canvas data on the canvas with getContex('webgl')?
seemed promising but I haven't been able to figure out how to apply it.
Cloning the canvas appear to me to be an heavy and weird solution.
The simplest way to achieve what you want to do is to prevent the frame buffer to be presented (swapped, then cleared) to HTML canvas. Do do so, you simply have to avoid calling any gl.clear, gl.drawArrays or gl.drawElements during your loop.
For example suppose you have two functions, one running your simulation, the other your GL draw:
function simulate() {
// update simulation here
}
function draw() {
gl.clearColor(0.0, 0.0, 0.0, 1.0);
gl.clear(gl.COLOR_BUFFER_BIT|gl.DEPTH_BUFFER_BIT);
// do drawing stuff here
gl.drawArrays(gl.TRIANGLES, 0, 12345);
// etc...
}
From this point, if you want to "freeze" the canvas content, you simply have to stop calling the "draw" function within your global loop. For example:
function loop() {
simulate();
if(!freeze) draw();
requestAnimationFrame(loop);
}
You may uses other methods to achieve the same effect. For example, you can draw your scene to a texture, then draw the texture on the canvas. By this way, you also can control when the texture is cleared and drawn again, while it still rendered in the canvas.
However, to implements the render-to-texture method, you will have some more heavy modification to done in your code: you'll need an additionnal shader to draw the texture on screen, and take some time to play with frameBuffer and renderBuffer objects.
I've followed tutorial here: http://hashrocket.com/blog/posts/using-tiled-and-canvas-to-render-game-screens to create Tiled map on cavas. I've made some improvements to the solution, but rendering stuff remained the same:
var self = this,
layer = self._canvas.canvas.cloneNode( false );
layer = layer.getContext( "2d" );
Basically, I have somewhere reference to canvas HTML, and here I'm cloning it (just like in tutorial). Next I made some logic and draw tile on that clone:
layer.drawImage( ... );
Finally after whole drawing tiles is over, the clone is painted on main canvas:
self._canvas.drawImage( layer.canvas, 0, 0 );
My question is why? When I did same algorithm not on layer, but main canvas instead, rendered image was the same. Is there some logic behind it? Only thing that came to my mind is that we can somehome prevent rendering layer, on catched error, to canvas. Tutorial meantion only about we’ll set up a scratch canvas to render to for a slight performance improvement
You're drawing on a back buffer. This prevents the browser from trying to render the canvas to screen while drawing, and aside from the potential performance improvement also prevents potential flickering. (This applies mostly to double buffering, but this method is quite similar)
About buffering and canvas
A) As the scratch layer is memory-only there is no need for the browser to try to update the content for each monitor refresh - it is draw once only to the main canvas which then is updated in whole.
B) If you moved things around (which is typical when tiling) using drawImage() with offset/clipping and to itself, the browser does not have to create a temporary bitmap, copy the content over, then copy back to a different position, and finally destroy the temporary bitmap.
I've seen other posts about this but I can't find anything to help me out.
I'm drawing an image on the canvas which moves, when it moves it leaves a trail of itself behind which hasn't been cleared. If I draw this image on top of another I get no trail but the image on top flickers. I don't think clearRect will work for me.
Can I save the canvas state between the image animation? Or should I be using a second canvas?
I'm not quite sure how to proceed, any advice would be great
* added examples - jsfiddle.net/zE67k/2 with an image and flicker. jsfiddle.net/zE67k/3 without an image and trail. this is just an example, I'm trying to achieve this with a background image to the canvas also.
* I also tried placing one canvas on top of another and I still get the flicker, I think the problem is where I place the clearRect. It does work if I put the clearRect just before the eye is drawn, but the way this code draws and updates the eyes does so one at a time, therefore the previous eye is cleared leaving only one. I'm trying it from this tutorial http://astronautz.com/wordpress/html5-eyes-that-follow-the-mouse/
You should save the clean canvas before drawing the other image ontop and restore that saved clean state before drawing the moved image again. You can use an additional canvas to save a clean state:
// create clean buffer
var buffer = document.createElement('canvas'),
canvas = document.getElementById('myCanvas');
buffer.width = canvas.width;
buffer.height = canvas.height;
// draw "background"/clean state to canvas
drawBackground(canvas);
// save clean state
buffer.getContext('2d').drawImage(canvas);
Then whenver you want to draw / move your other image/item simply restore the clean state:
canvas.getContext('2d').drawImage(buffer);
drawObject(canvas, x, y, w, h);
I'm working on a game that allows players to click on a card and drag it across the screen in any random direction. There are a total of 64 100x80 overlapping cards on a 800x800 canvas at any one time and each one is a procedural draw. As some of you probably suspect, canvas doesn't like redrawing that entire canvas for every move. To work around this, I'm utilizing a buffer canvas to draw the card and then attempting to paint that buffer canvas to the main canvas using drawImage(). To ensure there is no drawing buildup, I clear the region of the canvas associated with where I plan to drawImage() using a clearRect().
The problem I'm experiencing is that because the (x,y) coordinates used for the clearRect() and drawImage() are coming from the location of the mouse, if the user moves too fast, the coordinates will differ from the time drawImage() was last executed to the time clearRect() is called during the next draw sequence. The result is residual draw from the last sequence - proportionate to how fast the card is being dragged.
I tried maintaining the (x,y) coordinates from the drawImage() and using those (instead of the current mouse location) for the clearRect() in the next sequence. However, now instead of residual draw being shown, residual we have residual clear (erase).
Thoughts on how I can address this?
NOTE: My animation rate is handled using RequestAnimationFrame and not SetInterval().
Assuming your canvas is static during the drag drop operation, a pretty easy way to get a good increase in performance would be to just cache the rendering.
In other words, when a drag drop operation begins, save the current canvas into another one. Stop all rendering except for the one involved with dragging the card. Now, whenever you need to repaint, simply repaint from your copy-canvas. Since you're basically just copying from one to another, it should be pretty fast.
On each processing cycle, you would take the current position of the dragged card, fill that with data from the copy, then redraw the dragged card in the new position.
Other approaches you could try would be to use some kind of a placeholder for the drag. For example, consider using a same-sized DIV which you display while dragging. This should have the benefit of not requiring to touch the canvas while dragging and thus also run a better performance for it.
My scenario is that the Canvas width / height is changed dynamically, And I dont want to reset the Canvas. Instead I use the clearRect() as I know the bounds in which the rectangle has to be cleared.
Is there a way to disable the resetting of canvas when Width/Height is set again?
Is there a way to save the previous state and just load it back to the canvas exactly without re-drawing it?
I'm afraid there's no way to disable this, it is built into the canvas spec.
Section 4.8.11:
When the canvas element is created, and subsequently whenever the width and height attributes are set (whether to a new value or to the previous value), the bitmap and any associated contexts must be cleared back to their initial state and reinitialized with the newly specified coordinate space dimensions.
Yes, and GetImageData/PutImageData is one way but it is probably much slower than the following way:
Let's say your canvas is called realCanvas. Make a second canvas (we'll call it fakeCanvas) that is as large as you ever intend your real canvas to be, but only make it in javascript code and never add it to the document (so no one will ever see it).
Then, right before you resize realCanvas, do this operation:
fakeCanvasContext.drawImage(realCanvas, 0, 0);
This will draw one entire canvas to another and it will happen very quickly from a performance perspective.
Once you are done with the resize you can draw the contents of fakeCanvas back onto your realCanvas.
realCanvasContext.drawImage(fakeCanvas, 0, 0);
And that's it!
If you want to get technical, you can speed up my way even more by doing this for the second draw:
realCanvasContext.drawImage(fakeCanvas,
0, 0, realCanvas.width, realCanvas.height,
0, 0, realCanvas.width, realCanvas.height);`
That way you are only copying the part that can fit onto realCanvas. Please note that I haven't tested the code I wrote so there might be a syntax error or two.