0

I'm working on a Chrome Extension in which I resize images (actually resize; not changing the browser display) that users right click on. When they right click on the image, I get access to the image's 'src'.

I can resize the images that aren't gifs fine; I'm using canvases to do this. You can see me do this here https://jsfiddle.net/cyqvacc6/6/.

img_url = 'https://i.imgur.com/SHo6Fub.jpg';
function get_image(image_url, emoji_name) {
    var img_el = document.createElement('img');
    img_el.onload = function () {
        canvas = img_to_canvas(img_el);
        emoji_sized_canvas = emoji_sized(canvas);
        document.body.appendChild(emoji_sized_canvas);
    };
    img_el.src = image_url;
}

function img_to_canvas(img) {
    canvas = document.createElement('canvas');
    canvas.width = img.width;
    canvas.height = img.height;
    canvas_ctx = canvas.getContext('2d');
    canvas_ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
    return canvas;
}

function emoji_sized(canvas) {
    var target_dim = emoji_dimensions(canvas.width, canvas.height);
    var factor = 2;
    var canvas_long_side = Math.max(canvas.width, canvas.height);
    var target_long_side = Math.max(target_dim.width, target_dim.height);
    new_canvas = document.createElement('canvas');
    new_canvas_ctx = new_canvas.getContext('2d');
    if ((target_long_side === canvas_long_side)) {
        // Return the image.
        return canvas;
    } else if (target_long_side > canvas_long_side * factor) {
        // Increase the size of the image and then resize the result.
        new_canvas.width = canvas.width * factor;
        new_canvas.height = canvas.height * factor;
        new_canvas_ctx.drawImage(canvas, 0, 0, new_canvas.width, new_canvas.height);
        return emoji_sized(new_canvas);
    } else if (canvas_long_side > target_long_side * factor) {
        // Half the size of the image and then resize the result.
        var width = new_canvas.width = canvas.width / factor;
        var height = new_canvas.height = canvas.height / factor;
        new_canvas_ctx.drawImage(canvas, 0, 0, new_canvas.width, new_canvas.height);
        return emoji_sized(new_canvas);
    } else {
        // Resize the image in one shot
        new_canvas.width = target_dim.width;
        new_canvas.height = target_dim.height;
        new_canvas_ctx.drawImage(canvas, 0, 0, new_canvas.width, new_canvas.height);
        return new_canvas;
    }
}

function emoji_dimensions(width, height) {
    const MAX_SIDE_LENGTH = 128;
    // Get the larger side
    long_side = Math.max(height, width);
    // Determine the scale ratio
    // If the image is between 95% to 100% of the target
    // emoji size, don't adjust it's size.
    var scale;
    if ((long_side >= 0.95 * MAX_SIDE_LENGTH) && (long_side <= MAX_SIDE_LENGTH))
    {
        scale = 1;
    } else {
        scale = MAX_SIDE_LENGTH / long_side;
    }
    return {
        'height': height * scale,
        'width': width * scale
    };
}

Unfortunately, I'm not seeing an easy way to resize gifs using canvases. When I try the same approach on gifs, the 'resized' image is no longer a gif; it's just the first frame of the gif resized.

I think I'm going to end up sending gifs to a server to resize them, but still, in order to do this, I need to know whether the image I'm working on is animated or not, which I don't know how to do.

So, how do I determine if an image is a gif? Also, is it possible to resize these gifs from the client, i.e. javascript?

For reference, I need to reduce the gifs in terms of byte size and pixel, i.e. the gif needs to be both below 128px in both height and width and less than 64k in total byte size.

1 Answers1

0

Since your question actually contains multiple questions, it's quite hard to answer it, so I'll currently don't include code in here.


First, Canvas API can only draw the first frame of any animated image passed through an <img> element. According to specs.

Specifically, when a CanvasImageSource object represents an animated image in an HTMLOrSVGImageElement, the user agent must use the default image of the animation (the one that the format defines is to be used when animation is not supported or is disabled), or, if there is no such image, the first frame of the animation, when rendering the image for CanvasRenderingContext2D APIs.

So you won't natively be able to render all your gif's frames on the canvas.

For this, you'll have to parse the file and extract every frames of your file.

Here are is an untested library that do propose this functionality :
libgif-js.

If you don't like libraries, you could also write a script yourself.
edit: I tried this lib and it's awfull... don't use it, maybe you could fork it, but it's really not meant to do image processing

Once you've got the frames, you can resize these with canvas, and then reencode them all in a final gif file. Untested either gif.js seems to be able to do that.
Tested too, little bit less awfull but it doesn't like transparency and it needs to have the js files hosted, so no online demo... Would also probably need a fork...


And finally, to answer the title question, "How to check the MIME type of a file", check this Q/A.

Basically, the steps are to extract the 4 first bits of your file and checking it against magic-numbers. 'image/gif' magic-numbers are 47 49 46 38.

Community
  • 1
  • 1
Kaiido
  • 87,051
  • 7
  • 143
  • 194