Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Javascript proof-of-concept of GIF (re)compression

My program Precomp can be used to further compress already compressed file formats like GIF, PNG, PDF, ZIP and more. Roughly summarized, it does this by decompressing the compressed streams, recompressing them and storing the differences between the expected compressed stream and the actual compressed stream. As an example, this rotating earth picture from Wikipedia is compressed from 1429 KB to 755 KB. The process is lossless, so the original GIF file can be restored.

The algorithm for the GIF file format can be isolated and implemented relatively easy, so I was thinking about a proof-of-concept implementation in JavaScript. This would lead to the web server sending a compressed version of the GIF file (.pcf ending, essentially a bzip2 compressed file of the GIF image contents) and the client decompressing the data, recompressing to GIF and displaying it. The following things would've to be done:

  • The web site author would've to compress his GIF files using the standard version of Precomp and serve these instead of the GIF files together with a JavaScript for the client side recompression.
  • The client would decompress the bzip2 compressed file, this could be done using one of the existing bzip2 Javascript implementations.
  • The client would recompress the image content into the original GIF file.

The process is trade of bandwidth against CPU usage on the client side.

Now my questions are the following:

  • Are there any general problems with the process of loading a different file and "converting" it to GIF?
  • What would you recommend to display before the client side finishes (image placeholder)?
  • What do I have to do to make sure the .pcf file is cached? Bandwidth savings were useless if doesn't get cached.
  • Is there a way to display the original GIF if JavaScript is deactivated, but avoid loading the GIF if JavaScript is activated?
  • Can I give the users a way to configure the behaviour? E.g. on mobile devices, some might avoid bandwidth, but others might want less CPU usage.
  • Would it be possible to display interlaced GIFs as supposed (going from a rough version to the final image)? This would require updating the image content multiple times at different stages of recompression.
like image 857
schnaader Avatar asked Mar 17 '23 23:03

schnaader


1 Answers

Let's begin by answering your specific questions. Code example below.


Q&A

  1. Are there any general problems with the process of loading a different file and "converting" it to GIF?

The main problem is complication. You are effectively writing a browser addon, like those for JPEG2000.

If you are writing real browser addons, each major browsers do it differently, and change addon formats occasionally, so you have to actively maintain them.

If you are writing a JS library, it will be easier to write and maintain, but it will be unprivileged and suffer from limitations such as cross original restriction.

  1. What would you recommend to display before the client side finishes (image placeholder)?

Depends on what your format can offer. If you encode the image dimension and a small thumbnail early, you can display an accurate place-holder pretty early.

It is your format, afterall.

  1. What do I have to do to make sure the .pcf file is cached? Bandwidth savings were useless if doesn't get cached.

Nothing different from other files.

Configure the Expires and Cache-Control header on server side and they will be cached. Manifest and prefetch can also be used.

  1. Is there a way to display the original GIF if JavaScript is deactivated, but avoid loading the GIF if JavaScript is activated?

This is tricky. When JavaScript is disabled, you can only replace elements, not attributes.

This means you cannot create an image somewhere that points to the .pcf files, and ask browser to rewrite the src attribute when JS is unavailable.

I think the best solution to support no JS is outputting the images with document.write, using noscript as fall back:

<noscript>
  <img src=demo.gif width=90>
</noscript><script>
  loadPcf("demo.pcf","width=90")
</script>

(Some library or framework may make you consider <img src=demo.gif data-pcf=demo.pcf>. This will not work for you, because browsers will preload 'demo.gif' before your script kicks in, causing additional data transfer.)

Alternatively, browser addons are unaffected by "disable JS" settings, so if you make addons instead then you don't need to worry about it.

  1. Can I give the users a way to configure the behaviour? E.g. on mobile devices, some might avoid bandwidth, but others might want less CPU usage.

Perhaps. You can code a user interface and store the preference in cookie or in localStorage. Then you can detect preference and switch the logic in server code (if cookie) or in client code.

If you are doing addons, all browsers provide reliable preference mechanism. The problem is that, again, every browser do it differently.

  1. Would it be possible to display interlaced GIFs as supposed (going from a rough version to the final image)? This would require updating the image content multiple times at different stages of recompression.

If you hands browsers a partial image, they may think the image is corrupted and refuse to show it. In this case you have to implement your own GIF decoder AND encoder so that you can hands browser a complete placeholder image, just to be safe.

  1. (new) Can I decode image loaded from another site?

I must also repeat the warning that non-addon JS image decoding does not work with cross origin images. This means, all .pcf files must be on the same server, same port, and same protocol with the site using it.

For example you cannot share images for multiple sites or do optimisations like domain sharding.


Code Example

Here is a minimal example that creates an <img>, loads a gif, half its width, and put it back to the <img>.

To support placeholder or progressive loading, listen to onprogress instead of/in addition to onload.

  <!DOCTYPE html><html><head><meta charset="UTF-8"><script>
  function loadPcf( file, attr ) {
     var atr = attr || '', id = loadPcf.autoid = 1 + ~~loadPcf.autoid;
     document.write( '<img id=pcf'+id+' ' + atr + ' />' );
     var xhr = new XMLHttpRequest();
     xhr.responseType = 'arraybuffer'; // IE 10+ only, sorry.
     xhr.onload = function() { // I am loading gif for demo, you can load anything.
        var data = xhr.response, img = document.querySelector( '#pcf' + id );
        if ( ! img || ! data instanceof ArrayBuffer ) return;
        var buf = new DataView( data ), head = buf.getUint32(0), width = buf.getUint16(6,1);
        if ( head !== 1195984440 ) return console.log( 'Not a GIF: ' + file ); // 'GIF8' = 1195984440
        // Modify data, image width in this case, and push it to the <img> as gif.
        buf.setInt16( 6, ~~(width/2), 1 );
        img.src = URL.createObjectURL( new Blob( [ buf.buffer ], { type: "image/gif" } ) );
     };
     xhr.open( 'GET', file );
     xhr.send();
  }
  </script>
  <h1>Foo<noscript><img src=a.gif width=90></noscript><script>loadPcf("a.gif","width=90")</script>Bar</h1>

If you don't need <noscript> compatibility (and thus prevent facebook/google+ from seeing the images when the page is shared), you can put the pcf file in <img> src and use JS to handle them en mass, so that you don't need to call loadPcf for each image and will make the html much simpler.


How about <video>?

What you envisioned is mostly doable, in theory, but perhaps you should reconsider.

Judging from the questions you ask, it will be quite difficult for you to define and pull off your vision smoothly. It is perhaps better to encode your animation in WebM and use <video> instead.

  1. Better browser support back to IE 9 - just add H.264 to make it a dual format video. You need IE 10+ to modify binary data.
  2. Size: Movies has many, many options and tricks to minimise size, and what you learned can be reused in the future.
  3. Progressive: <video> have had some techs for adaptive video, and hopefully they will stabilise soon.
  4. JavaScript: <video> does not depend on JavaScript.
  5. Future-proof: Both WebM and H.264 will be supported by many programs, long after you stopped working on your special format.
  6. Cost-effective: Create a low-bandwith option using smaller or lower quality media is easier and more reliable than a custom format. This is why wikipedia and youtube offers their media in different resolutions.

For non-animations, PNG can also be colour indexed and 7z optimised (keeping the PNG format). Icon size indexed PNG is often smaller than the same GIF.

Or perhaps your vision (as described in the pcf website) is the capability to encode many different files, not only GIF. This will be more like supporting a new network protocol, and is likely beyond the scope of humble JavaScript. (e.g. how are you going to handle pdf download or streaming?)

like image 139
Sheepy Avatar answered Mar 29 '23 23:03

Sheepy