Decoding GIF and PNG colors without a Canvas

by Admiral Potato

HTML

<script src="https://wzrd.in/standalone/[email protected]"></script>
<script src="https://wzrd.in/standalone/[email protected]"></script>
<div>
<h2>gif transparent</h2>
<img data-name="gif"...

CSS

* {
  font-family: monospace;
}

.colors {
	display: inline-block;
  background-color: #f00;
  font-size: 0;
}
.colors span {
  display: inline-block;
  width: 1px;
  height: 1px;
}

JavaScript

console.log('fast-png', window.fastPng);
console.log('omggif', window.omggif);

var images = Array.prototype.slice.apply(document.querySelectorAll('img'));

console.log('images', images);

var imageHandlerMap = {
  gif: function(data) {
    var reader = new omggif.GifReader(data);
    var info = reader.frameInfo(0);
    var buffer = new Uint8Array(
      info.width * info.height * 4
    );
    reader.decodeAndBlitFrameRGBA(0, buffer);
    console.log('GifReader', reader);
    console.log('frameInfo', info);
    return {
      info: {
        width: info.width,
        height: info.height,
        channels: 4
      },
      buffer: buffer
    };
  },
  png: function(data) {
    var decoded = fastPng.decode(data);
    console.log('fastPng', decoded);
    var info = {
      width: decoded.width,
      height: decoded.height,
      channels: decoded.channels
    };
    var buffer = decoded.data;
    if (decoded.palette) {
      info.channels = decoded.palette[0].length;
      var hasAlpha = info.channels === 4;
      var pixels = info.width * info.height;
      buffer = new Uint8Array(pixels * info.channels);
      for (var i = 0; i < pixels; i++) {
        var offset = i * info.channels;
        var colorIndex = decoded.data[i];
        var color = decoded.palette[colorIndex] || [0, 0, 0, 0];
        buffer[offset + 0] = color[0];
        buffer[offset + 1] = color[1];
        buffer[offset + 2] = color[2];
        if (hasAlpha) {
          buffer[offset + 3] = color[3];
        }
      }
    }
    return {
      info: info,
      buffer: buffer
    }
  },
};

images.forEach(function(image) {
  var base64Split = image.src.split(';base64,');
  var type = base64Split[0].split('/').pop();
  var dataString = atob(base64Split.pop());
  var handler = imageHandlerMap[type];
  var enc = new TextEncoder();
  var dataBuffer = Uint8Array.from(
    dataString,
    c => c.charCodeAt(0)
  );
  console.log('type:', type);
  console.log('imageHandlerMap:', imageHandlerMap);
  try...