-
-
Save ictrobot/bc24d73ac3515e3a856a1483e1f229d2 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// based on https://gist.github.com/jbrantly/499486 | |
var Adler32 = function() { | |
this.s1 = 1; | |
this.s2 = 0; | |
}; | |
Adler32.prototype.update = function(array, index, len) { | |
var s1 = this.s1; | |
var s2 = this.s2; | |
var NMAX = 3792; | |
var unroll = NMAX / 16; | |
while (len >= NMAX) { | |
len -= NMAX; | |
var n = unroll; | |
do { | |
s1 += (array[index++]); | |
s2 += s1; | |
s1 += (array[index++]); | |
s2 += s1; | |
s1 += (array[index++]); | |
s2 += s1; | |
s1 += (array[index++]); | |
s2 += s1; | |
s1 += (array[index++]); | |
s2 += s1; | |
s1 += (array[index++]); | |
s2 += s1; | |
s1 += (array[index++]); | |
s2 += s1; | |
s1 += (array[index++]); | |
s2 += s1; | |
s1 += (array[index++]); | |
s2 += s1; | |
s1 += (array[index++]); | |
s2 += s1; | |
s1 += (array[index++]); | |
s2 += s1; | |
s1 += (array[index++]); | |
s2 += s1; | |
s1 += (array[index++]); | |
s2 += s1; | |
s1 += (array[index++]); | |
s2 += s1; | |
s1 += (array[index++]); | |
s2 += s1; | |
s1 += (array[index++]); | |
s2 += s1; | |
} while (--n); | |
s1 = s1 % 65521; | |
s2 = s2 % 65521; | |
} | |
if (len) { | |
while (len >= 16) { | |
len -= 16; | |
s1 += (array[index++]); | |
s2 += s1; | |
s1 += (array[index++]); | |
s2 += s1; | |
s1 += (array[index++]); | |
s2 += s1; | |
s1 += (array[index++]); | |
s2 += s1; | |
s1 += (array[index++]); | |
s2 += s1; | |
s1 += (array[index++]); | |
s2 += s1; | |
s1 += (array[index++]); | |
s2 += s1; | |
s1 += (array[index++]); | |
s2 += s1; | |
s1 += (array[index++]); | |
s2 += s1; | |
s1 += (array[index++]); | |
s2 += s1; | |
s1 += (array[index++]); | |
s2 += s1; | |
s1 += (array[index++]); | |
s2 += s1; | |
s1 += (array[index++]); | |
s2 += s1; | |
s1 += (array[index++]); | |
s2 += s1; | |
s1 += (array[index++]); | |
s2 += s1; | |
s1 += (array[index++]); | |
s2 += s1; | |
} | |
while (len--) { | |
s1 += (array[index++]); | |
s2 += s1; | |
} | |
s1 = s1 % 65521; | |
s2 = s2 % 65521; | |
} | |
this.s1 = s1; | |
this.s2 = s2; | |
}; | |
var CRC = function() { | |
this.crc = 0xffffffff; | |
}; | |
CRC.table = null; | |
CRC.make_table = function() { | |
var table = []; | |
for (var n = 0; n < 256; n++) { | |
var c = n; | |
for (var k = 0; k < 8; k++) { | |
if (c & 1) { | |
c = 0xedb88320 ^ (c >>> 1); | |
} else { | |
c = c >>> 1; | |
} | |
} | |
table[n] = c; | |
} | |
CRC.table = table; | |
}; | |
CRC.prototype.update = function(array, startPos, endPos) { | |
if (CRC.table == null) { | |
CRC.make_table(); | |
} | |
var crc = this.crc; | |
var table = CRC.table; | |
len = endPos - startPos; | |
index = startPos; | |
while (len >= 8) { | |
crc = table[(crc ^ array[index++]) & 0xff] ^ (crc >>> 8) | |
crc = table[(crc ^ array[index++]) & 0xff] ^ (crc >>> 8) | |
crc = table[(crc ^ array[index++]) & 0xff] ^ (crc >>> 8) | |
crc = table[(crc ^ array[index++]) & 0xff] ^ (crc >>> 8) | |
crc = table[(crc ^ array[index++]) & 0xff] ^ (crc >>> 8) | |
crc = table[(crc ^ array[index++]) & 0xff] ^ (crc >>> 8) | |
crc = table[(crc ^ array[index++]) & 0xff] ^ (crc >>> 8) | |
crc = table[(crc ^ array[index++]) & 0xff] ^ (crc >>> 8) | |
len -= 8; | |
} | |
if (len) | |
do { | |
crc = table[(crc ^ array[index++]) & 0xff] ^ (crc >>> 8); | |
} while (--len); | |
this.crc = crc; | |
return crc; | |
}; | |
function pngBytes(b) { | |
png.set(b, pos); | |
pos += b.length; | |
} | |
function intBytes(i) { | |
return [(i >>> 24) & 0xff, (i >>> 16) & 0xff, (i >>> 8) & 0xff, i & 0xff]; | |
} | |
function encodePng(data) { | |
var width = data[0].length / 4; | |
var height = data.length; | |
var length = 8; // magic number | |
length += 8 + 13 + 4; // ihdr | |
length += 8 + idatLength(height * (1 + (width * 4))) + 4; // idat | |
length += 12; // iend | |
pos = 0; | |
png = new Uint8ClampedArray(length); | |
pngBytes([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]); | |
createPngIHDR(width, height); | |
createPngIDAT(data); | |
createPngIEND(); | |
return png; | |
}; | |
function createPngIHDR(width, height) { | |
pngBytes([0, 0, 0, 13]); | |
posBefore = pos; | |
pngBytes([0x49, 0x48, 0x44, 0x52]); | |
pngBytes(intBytes(width)); | |
pngBytes(intBytes(height)); | |
pngBytes([8, 6, 0, 0, 0]); | |
pngBytes(calcCRC32(posBefore, pos)); | |
}; | |
function createPngIDAT(data) { | |
var chunkData = []; | |
for (var i = 0, n = data.length; i < n; i++) { | |
var scanline = data[i]; | |
chunkData.push(0); | |
for (var x = 0, y = scanline.length; x < y; x += 4) { | |
chunkData.push(scanline[x], scanline[x + 1], scanline[x + 2], scanline[x + 3]); | |
} | |
} | |
chunkData = uncompressedDeflate(chunkData) | |
pngBytes(intBytes(chunkData.length)); | |
posBefore = pos; | |
pngBytes([0x49, 0x44, 0x41, 0x54]); | |
pngBytes(chunkData); | |
pngBytes(calcCRC32(posBefore, pos)); | |
}; | |
function createPngIEND() { | |
pngBytes([0, 0, 0, 0]); | |
pngBytes([0x49, 0x45, 0x4e, 0x44]); | |
pngBytes(calcCRC32(pos - 4, pos)); | |
}; | |
function calcCRC32(posBefore, posAfter) { | |
var crc = new CRC(); | |
crc.update(png, posBefore, posAfter); | |
var checksum = crc.crc ^ 0xffffffff; | |
return intBytes(checksum); | |
}; | |
function idatLength(uncompressedLength) { | |
length = 2; // Refer to RFC1950 Sect 2.2 | |
var maxBlockSize = 65535; | |
var leftOverBytes = uncompressedLength % maxBlockSize; | |
var numBlocks = (uncompressedLength - leftOverBytes) / maxBlockSize; | |
if (leftOverBytes > 0) { | |
numBlocks++; | |
} | |
length += numBlocks * 5; | |
length += uncompressedLength; | |
length += 4; //Adler 32 | |
return length; | |
} | |
function uncompressedDeflate(data) { | |
var returnData = []; | |
// Refer to RFC1950 Sect 2.2 | |
returnData.push(0x08); // CMF (CF = 8, CINFO = 0) | |
returnData.push(0x1D); // FLG (FCHECK = preset, FDICT = 0, FLEVEL = 0) | |
// Refer to RFC1951 Sects 2, 3.2.3, 3.2.4 | |
var maxBlockSize = 65535; | |
var leftOverBytes = data.length % maxBlockSize; | |
var numBlocks = (data.length - leftOverBytes) / maxBlockSize; | |
if (leftOverBytes > 0) { | |
numBlocks++; | |
} | |
var adlers1 = 1; | |
var s2 = 0; | |
for (var i = 0; i < numBlocks; i++) { | |
var lastBlock = (i == numBlocks - 1); | |
var numBytesInBlock = lastBlock ? leftOverBytes : maxBlockSize; | |
// Write block header | |
returnData.push(lastBlock ? 1 : 0); // BFINAL = lastBlock, BTYPE = 00 | |
// Write block length | |
var len1 = numBytesInBlock & 0xFF; | |
var len2 = numBytesInBlock >>> 8; | |
returnData.push(len1); | |
returnData.push(len2); | |
returnData.push(~len1 & 0xFF); | |
returnData.push(~len2 & 0xFF); | |
for (var n = maxBlockSize * i, end = n + numBytesInBlock; n < end; n++) { | |
returnData.push(data[n]); | |
} | |
} | |
// Write adler32 checksum | |
var adler32 = new Adler32(); | |
adler32.update(data, 0, data.length); | |
returnData.push((adler32.s2 >>> 8) & 0xFF); | |
returnData.push(adler32.s2 & 0xFF); | |
returnData.push((adler32.s1 >>> 8) & 0xFF); | |
returnData.push(adler32.s1 & 0xFF); | |
return returnData; | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment