"use strict"; /* Copyright (C) 2012 by Jeremy P. White This file is part of spice-html5. spice-html5 is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. spice-html5 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with spice-html5. If not, see . */ /*---------------------------------------------------------------------------- ** crc logic from rfc2083 ported to Javascript **--------------------------------------------------------------------------*/ import { SpiceDataView } from './spicedataview.js'; var rfc2083_crc_table = Array(256); var rfc2083_crc_table_computed = 0; /* Make the table for a fast CRC. */ function rfc2083_make_crc_table() { var c; var n, k; for (n = 0; n < 256; n++) { c = n; for (k = 0; k < 8; k++) { if (c & 1) c = ((0xedb88320 ^ (c >>> 1)) >>> 0) & 0xffffffff; else c = c >>> 1; } rfc2083_crc_table[n] = c; } rfc2083_crc_table_computed = 1; } /* Update a running CRC with the bytes buf[0..len-1]--the CRC should be initialized to all 1's, and the transmitted value is the 1's complement of the final running CRC (see the crc() routine below)). */ function rfc2083_update_crc(crc, u8buf, at, len) { var c = crc; var n; if (!rfc2083_crc_table_computed) rfc2083_make_crc_table(); for (n = 0; n < len; n++) { c = rfc2083_crc_table[(c ^ u8buf[at + n]) & 0xff] ^ (c >>> 8); } return c; } function rfc2083_crc(u8buf, at, len) { return rfc2083_update_crc(0xffffffff, u8buf, at, len) ^ 0xffffffff; } function crc32(mb, at, len) { var u8 = new Uint8Array(mb); return rfc2083_crc(u8, at, len); } function PngIHDR(width, height) { this.width = width; this.height = height; this.depth = 8; this.type = 6; this.compression = 0; this.filter = 0; this.interlace = 0; } PngIHDR.prototype = { to_buffer: function(a, at) { at = at || 0; var orig = at; var dv = new SpiceDataView(a); dv.setUint32(at, this.buffer_size() - 12); at += 4; dv.setUint8(at, 'I'.charCodeAt(0)); at++; dv.setUint8(at, 'H'.charCodeAt(0)); at++; dv.setUint8(at, 'D'.charCodeAt(0)); at++; dv.setUint8(at, 'R'.charCodeAt(0)); at++; dv.setUint32(at, this.width); at += 4; dv.setUint32(at, this.height); at += 4; dv.setUint8(at, this.depth); at++; dv.setUint8(at, this.type); at++; dv.setUint8(at, this.compression); at++; dv.setUint8(at, this.filter); at++; dv.setUint8(at, this.interlace); at++; dv.setUint32(at, crc32(a, orig + 4, this.buffer_size() - 8)); at += 4; return at; }, buffer_size: function() { return 12 + 13; } } function adler() { this.s1 = 1; this.s2 = 0; } adler.prototype.update = function(b) { this.s1 += b; this.s1 %= 65521; this.s2 += this.s1; this.s2 %= 65521; } function PngIDAT(width, height, bytes) { if (bytes.byteLength > 65535) { throw new Error("Cannot handle more than 64K"); } this.data = bytes; this.width = width; this.height = height; } PngIDAT.prototype = { to_buffer: function(a, at) { at = at || 0; var orig = at; var x, y, i, j; var dv = new SpiceDataView(a); var zsum = new adler(); dv.setUint32(at, this.buffer_size() - 12); at += 4; dv.setUint8(at, 'I'.charCodeAt(0)); at++; dv.setUint8(at, 'D'.charCodeAt(0)); at++; dv.setUint8(at, 'A'.charCodeAt(0)); at++; dv.setUint8(at, 'T'.charCodeAt(0)); at++; /* zlib header. */ dv.setUint8(at, 0x78); at++; dv.setUint8(at, 0x01); at++; /* Deflate header. Specifies uncompressed, final bit */ dv.setUint8(at, 0x80); at++; dv.setUint16(at, this.data.byteLength + this.height); at += 2; dv.setUint16(at, ~(this.data.byteLength + this.height)); at += 2; var u8 = new Uint8Array(this.data); for (i = 0, y = 0; y < this.height; y++) { /* Filter type 0 - uncompressed */ dv.setUint8(at, 0); at++; zsum.update(0); for (x = 0; x < this.width && i < this.data.byteLength; x++) { zsum.update(u8[i]); dv.setUint8(at, u8[i++]); at++; zsum.update(u8[i]); dv.setUint8(at, u8[i++]); at++; zsum.update(u8[i]); dv.setUint8(at, u8[i++]); at++; zsum.update(u8[i]); dv.setUint8(at, u8[i++]); at++; } } /* zlib checksum. */ dv.setUint16(at, zsum.s2); at+=2; dv.setUint16(at, zsum.s1); at+=2; /* FIXME - something is not quite right with the zlib code; you get an error from libpng if you open the image in gimp. But it works, so it's good enough for now... */ dv.setUint32(at, crc32(a, orig + 4, this.buffer_size() - 8)); at += 4; return at; }, buffer_size: function() { return 12 + this.data.byteLength + this.height + 4 + 2 + 1 + 2 + 2; } } function PngIEND() { } PngIEND.prototype = { to_buffer: function(a, at) { at = at || 0; var orig = at; var i; var dv = new SpiceDataView(a); dv.setUint32(at, this.buffer_size() - 12); at += 4; dv.setUint8(at, 'I'.charCodeAt(0)); at++; dv.setUint8(at, 'E'.charCodeAt(0)); at++; dv.setUint8(at, 'N'.charCodeAt(0)); at++; dv.setUint8(at, 'D'.charCodeAt(0)); at++; dv.setUint32(at, crc32(a, orig + 4, this.buffer_size() - 8)); at += 4; return at; }, buffer_size: function() { return 12; } } function create_rgba_png(width, height, bytes) { var i; var ihdr = new PngIHDR(width, height); var idat = new PngIDAT(width, height, bytes); var iend = new PngIEND; var mb = new ArrayBuffer(ihdr.buffer_size() + idat.buffer_size() + iend.buffer_size()); var at = ihdr.to_buffer(mb); at = idat.to_buffer(mb, at); at = iend.to_buffer(mb, at); var u8 = new Uint8Array(mb); var str = ""; for (i = 0; i < at; i++) { str += "%"; if (u8[i] < 16) str += "0"; str += u8[i].toString(16); } return "%89PNG%0D%0A%1A%0A" + str; } export { create_rgba_png, };