Extracting single frames from an animated GIF to canvas - javascript

I need to load all the frames of an animated GIF to an HTML5 canvas.
Please note, I don't want to "play" the animated (someone asked this before), all I want is to extract all the frames to use them as single images.

Buzzfeed have librarified the code from the repo tommitytom posted. I haven't tried it yet but it looks good.
https://github.com/buzzfeed/libgif-js

Take a look at jsgif; it downloads a GIF, parses it, and draws the individual frames of the file to a <canvas>. With a bit of digging you should be able to find the code that draws the individual frames and work from there.

Sorry, the short answer is that JavaScript has no way of controlling the current frame of an Animated GIF.
The long answer is that there are sort-of ways to do what you want with just JS, but they are very much convoluted hacks.
Example of hackish way: Create a canvas and don't add it to the DOM (so this won't be seen by anyone). In a fast loop (setTimeout), draw to this canvas constantly and collect snapshots. Compare the canvas ImageData to see if the frames have changed or not.
It would be a better use of your time, probably, to see how you can get your server to split it apart for you (with php/perl/python/etc)

When you only need the first frame of the GIF:
const image = document.getElementById('my-image');
const canvas = document.getElementById('my-canvas');
const ctx = canvas.getContext("2d");
// context.drawImage(img, x, y, width, height);
ctx.drawImage(image, 0, 0);
Credits: How to draw a GIF on a canvas?

Related

Is it possible in JavaScript to create a div, put a \u#### character in that div, then copy to canvas as a putImage?

I am working with fTelnet.js - I have gotten down to the this_Font.GetChar() code and have found all characters are from a Font Sprite, it getItemData(....) changes the color where The pixel is 0x80 and colors it foreground [r][g][b][alpha], or colors it background [r][g][b][alpha].
Since Unicode goes outside what is in its 40+ fonts - I need to cheat and .createElement("div"), .innerHTML = "\u#####"; then somehow take that as ImageData so I can patch the above logic and introduce unicode support to fTelnet's canvas routine.
Does anyone know what command(s) I am needed to do this? e.g. I have spent hours trying to resolve this via Google, HTML5 Context docs, without success... mainly not knowing what I am searching for ;-)
or as I re-read this... maybe a hidden canvas, do the ctx.strokeText("\u#####", 25, 50); and then copy from that CTX as a IMAGEDATA to the displayed? (what command would I look up then?)... I normally do not dabble in 2D/3D graphics world (obviously)...
Regards,
Generally, rendering HTML to canvas is not possible - and that seems to be by design (it would make issues like tainting much harder to navigate).
But stamping one canvas on top of another is simple - you can just use the standard #drawImage call, same as with any other image you would draw on canvas.
Actually, if you look at the API documentation and click through the definitions you will see that you can use drawImage to "stamp" any instance of svg or html image, a bitmap, offscreen canvas, video or audio tags.

How can I speed up this slow canvas DrawImage operation? WebGL? Webworkers?

I am currently combining two high resolution images into an html5 canvas element.
The first image is a JPEG and contains all color data, and the second image is an alpha mask PNG file.
I then combine the two to produce a single RGBA canvas.
The app is dealing with 2048x2048 resolution images, that need to also maintain their alpha channel. Therefore by using this method as ooposed to simply using PNG's, I have reduced the average filesize from around 2-3mb to 50-130kb plus a 10kb png alpha mask.
The method I use is as follows:
context.drawImage(alpha_mask_png, 0, 0, w, h);
context.globalCompositeOperation = 'source-in';
context.drawImage(main_image_jpeg, 0, 0, w, h);
Unfortunately this operation takes around 100-120ms. And is only carried out once for each pair of images as they are loaded. While this wouldn't normally be an issue, in this case an animation is being rendered to another main visible canvas (of which these high res images are the source art for) which suffers from a very noticable 100ms judder (mostly perceptible in firefox) whenever new source art is streamed in, loaded, and combined.
What I am looking for is a way to reduce this.
Here is what I have tried so far:
Implemented WebP for Google chrome, removing the need to combine the JPEG and PNG alpha mask altogether. Perfect in Chrome only but need a solution mostly for Firefox (IE 10/11 seems to perform the operation far quicker)
I have tried loading the images in a webworker and decoding them both, followed by combining them. All in pure javascript. This works but is far too slow to be of use.
I have also tried using WebP polyfills. Weppy is very fast and when ran in a webworker does not effect the main loop. However it does not support alpha transparency so is of no use which is a real shame as this method is very close. libwebpjs works okay within a webworker but like my manual decoding of the JPEG/PNG, is far too slow.
EDIT: To further clarify. I have tried transferring the data from my webworkers using transferrable objects and have even tried turning the result into a blob and creating an objectURL which can then be loaded by the main thread. Although there is no lag in the main thread any more, the images simply take far too long to decode.
This leaves me with WebGL. I have literally no understanding of how WebGL works other than I realise that I would need to load both the JPEG and PNG as seperate textures then combine them with a shader. But I really wouldn't know where to begin.
I have spent some time playing with the code from the following link:
Blend two canvases onto one with WebGL
But to no avail. And to be honest I am concerned that loading the images as textures might actually take longer than my original method anyway.
So to summarise, I am really looking for a way of speeding up the operation on high resolution images. (1920x1080 - 2048x2048) be it with the use of WebGL or indeed any other method.
Any help or suggestions would be greatly appreciated.

Moving objects on a canvas

I’m trying to build a game in canvas just to improve my skills set. The idea is there’s two objects on the screen that are player-controlled. I initialise the game with players, and then start a game loop that listens for player input.
At the moment, I can draw the players on the canvas (simply rectangles at this stage). However, I’m having trouble moving these objects on each “tick” of the game loop. At the moment, the rectangle is just drawn on top of the rectangle in the previous frame; I want to clear the canvas and re-draw the “players” in each tick.
How would I go about this? And is it the best way, or is there a better approach?
I’d post a code sample, but my JavaScript file is quite verbose and I’m hoping the description above is sufficient.
If the appearance of the players doesn't change from frame to frame, then I would use three separate canvas elements: one for the background and one for each player. Then you don't have to redraw anything, just change the CSS left and top values for each player canvas.
If the appearance of the players does change, then you'll need to redraw them. But you still might want to use a separate canvas for each one. That way you don't have to redraw the background. You can just draw and position each player.
Of course if the background changes or moves, you'll need to redraw it anyway. In that case you might just use a single canvas, or experiment with the separate canvases. With regard to the specific question of how to clear the background (or any canvas) before redrawing, here are a couple of ways to clear a canvas.
Either way, use requestAnimationFrame() in browsers that support it, instead of setTimeout() or setInterval(). If you search for requestAnimationFrame polyfill you will find many examples of how to do this and still support old browsers. Here's a good requestAnimationFrame polyfill.
Here is a good tutorial with a basic game loop. It renders a background, monsters and a player.
http://www.lostdecadegames.com/how-to-make-a-simple-html5-canvas-game/
// The main game loop
var main = function () {
var now = Date.now();
var delta = now - then;
update(delta / 1000);
render();
then = now;
};
You update your object positions in update and then call render.
I'm sorry, but having a separate canvas for each element in the scene, as another user sugested, is a terrible idea. And that won't improve your skills set in anyway.
Yes, you have to redraw everything on a single canvas, even the objects that don't transform from one frame to another. Clearing the canvas is simple, use the context.clearRect(0, 0, canvas.width, canvas.height) or do a fillRect to paint the background with some color, or draw an image on the entire canvas to have a static background, anything that covers the whole canvas will do.
So, you update the objects, clear the canvas, then, redraw everything. Since you just have 2 rects and the only thing that changes is their position this will be really easy to implement.
When you start making more complex things you might want to go for a more object-oriented aproach, by that I mean each object in your scene needs to "know how to draw themselves", like:
var player1 = new PlayerRect(position);
player1.draw(context);
This way you'd update it's position in the loop doing something like this:
player.position.x += 10;
In case you end up with hundreds of objects, all you have to do is a list where you could add/remove these objects and a loop calling their draw method.
I mean, that's the way I do it.

Capture/save/export an image with CSS filter effects applied

I'm tooling around to make a simple picture editor that uses CSS3 filter effects (saturation, sepia, contrast, etc.)
Making the picture editor is the easy part, however whether it is possible to save or export the image with the filters applied seems incredibly difficult..
I had originally had high hopes it would be possible with #niklasvh's html2canvas. Unfortunately, it doesn't capture most CSS3 properties, let alone filter effects.
If anybody has a solution or sadly, definitive knowledge that this just isn't possible, it would be greatly appreciated! Thanks!
You're not, that I'm aware of, able to apply CSS to graphics in the HTML5 canvas element (as they're not a part of the DOM).
However, that's OK! We can still do basic filter effects relatively easy and save them out as an image with just a few lines of JavaScript.
I found a good article that goes over applying a sepia-like effect to the canvas and saving it as an image. Rather than copying it, I'll highlight the larger takeaways from the article.
Modifying the canvas image:
var canvas = document.getElementById('canvasElementId'),
context = canvas.getContext('2d');
var imageData = context.getImageData(0, 0, canvas.width, canvas.height);
for (var i = 0; i < imageData.data.length; i+=4) {
...
}
In order to get access to some canvas API, you'll need to activate the 2d context on the canvas using the above JavaScript. MDN has some great documentation on the API that is available to you with the context object, but the important part to note here is the getImageData() call. Basically, it will grab all the pixel values in the area that you defined (in the case above, we're grabbing the whole image). Then, with this data in hand, we can iterate through all the pixels and change them as needed. In the sepia article, it's obviously making some interesting adjustments, but you can also do grayscale, blurring, or any other changes as necessary and there's an awesome canvas filters library on Github for just that.
How to save the canvas image:
var canvas = document.getElementById('canvasElementId'),
image = document.createElement("img");
image.src = canvas.toDataURL('image/jpeg');
document.body.appendChild(image);
The above script will select your canvas (assuming you've already done your drawings) and create an image element. It them uses toDataURL() to generate a url that you can use to set the source on an image element. In the example above, the image element is appended to the document body. You can view more info on MDN's canvas page.
I got your answer.
I made this program, finally it's work.
those step is :
1. upload the image (JPG/PNG)
2. convert to canvas
3. custom with css filters.
4. render using camanJS to save as image.
5. done.
you also can reset effect value by modifying value of filters to its default.
good luck!

Copy and crop images in Javascript

I'm trying to create a small 2D game in Javascript/Canvas which consists of several animated sprites. I'd like to cut down on the number of HTTP requests, so I combined each frame of animation (32px by 32px) into one image per sprite (say, 192px by 128px). Is there any way I can copy and crop these images clientside back into several smaller images? It would vastly simplify my rendering code and help reduce loading time due to network latency.
The HTML5 Canvas API provides a method called drawImage which allows you to crop the input image.
context.drawImage(image, sx, sy, sw, sh, dx, dy, dw, dh)
For more information see the spec (that image is taken directly from the spec):
http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#images
Have a look at Pixastic, specifically http://www.pixastic.com/lib/docs/actions/crop.
Just load the image in an img tag with style attribute display set to hidden. Then crop the image on an off screen canvas, then write that off screen canvas to your main canvas as required.
If you don't want to use canvas or 3rd party library, you could add the image to a div (with "overflow: hidden") of the size of the cropped version, and giving the image negative left and top margins.
Each one will carry the whole image around, but will just display a portion of it, which may -- or may not -- impact performance. I believe you may have to give the div element a position:relative as well to make IE happy.
Alternatively you could assign the image as a background of the div, and specify the backgroundPosition. I seem to remember this didn't work for something I did once, not sure why. (I think it had to do with opacity)

Categories