Skip to content

Instantly share code, notes, and snippets.

@lakenen
Created June 28, 2012 17:17
Show Gist options
  • Save lakenen/3012623 to your computer and use it in GitHub Desktop.
Save lakenen/3012623 to your computer and use it in GitHub Desktop.
JavaScript animated GIF detection!
function isAnimatedGif(src, cb) {
var request = new XMLHttpRequest();
request.open('GET', src, true);
request.responseType = 'arraybuffer';
request.addEventListener('load', function () {
var arr = new Uint8Array(request.response),
i, len, length = arr.length, frames = 0;
// make sure it's a gif (GIF8)
if (arr[0] !== 0x47 || arr[1] !== 0x49 ||
arr[2] !== 0x46 || arr[3] !== 0x38)
{
cb(false);
return;
}
//ported from php http://www.php.net/manual/en/function.imagecreatefromgif.php#104473
//an animated gif contains multiple "frames", with each frame having a
//header made up of:
// * a static 4-byte sequence (\x00\x21\xF9\x04)
// * 4 variable bytes
// * a static 2-byte sequence (\x00\x2C) (some variants may use \x00\x21 ?)
// We read through the file til we reach the end of the file, or we've found
// at least 2 frame headers
for (i=0, len = length - 9; i < len, frames < 2; ++i) {
if (arr[i] === 0x00 && arr[i+1] === 0x21 &&
arr[i+2] === 0xF9 && arr[i+3] === 0x04 &&
arr[i+8] === 0x00 &&
(arr[i+9] === 0x2C || arr[i+9] === 0x21))
{
frames++;
}
}
// if frame count > 1, it's animated
cb(frames > 1);
});
request.send();
}
@atk
Copy link

atk commented Jun 29, 2012

Smaller solution (inside xhr callback):

var img = request.response;
if (!/^GIF8[79]a/) { cb(false); } else {
var frames = 0;
img.replace(/\x00\x21\xF9\x04\x00[\x2c\x21]/g, function(){ frames++ });
cb(frames>1);
}

In addition, this only works if the domain origin of the image is the same as the page you are loading the script from.

@sathia-musso
Copy link

looks like it's never satisfying the for conditions if the GIF is not animated.
can't understand why

@olapeter
Copy link

It is because multiple comma-separated conditions in a for loop like this

for (i=0, len = length - 9; i < len, frames < 2; ++i)

will evaluate both checks, but use only the last one for determining whether to run the loop again. So unless more than one frame is found, i will just grow and frames < 2 never satisfy.

In this case, better to use || (or have an inner loop)

@marckubischta
Copy link

Implemented @olapeter's suggestion here, only using && since the logic is inverted between the comment and the if statement.

Also updated to support variable length extension control blocks.

@marckubischta
Copy link

N.B. the img.replace(/\x00\x21\xF9\x04\x00[\x2c\x21]/g solution suffers the same problem as the original code here -- if the ECB isn't 4 bytes this regex won't find the frame (see the wikipedia article examples).

@okramovic
Copy link

great rubust solution from @marckubischta ! thanks for that.
anyone able to come up with detecting animation for webp image format?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment