Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
michael@0 | 1 | /** |
michael@0 | 2 | * A handy class to calculate color values. |
michael@0 | 3 | * |
michael@0 | 4 | * @version 1.0 |
michael@0 | 5 | * @author Robert Eisele <robert@xarg.org> |
michael@0 | 6 | * @copyright Copyright (c) 2010, Robert Eisele |
michael@0 | 7 | * @link http://www.xarg.org/2010/03/generate-client-side-png-files-using-javascript/ |
michael@0 | 8 | * @license http://www.opensource.org/licenses/bsd-license.php BSD License |
michael@0 | 9 | * |
michael@0 | 10 | */ |
michael@0 | 11 | |
michael@0 | 12 | (function() { |
michael@0 | 13 | |
michael@0 | 14 | // helper functions for that ctx |
michael@0 | 15 | function write(buffer, offs) { |
michael@0 | 16 | for (var i = 2; i < arguments.length; i++) { |
michael@0 | 17 | for (var j = 0; j < arguments[i].length; j++) { |
michael@0 | 18 | buffer[offs++] = arguments[i].charAt(j); |
michael@0 | 19 | } |
michael@0 | 20 | } |
michael@0 | 21 | } |
michael@0 | 22 | |
michael@0 | 23 | function byte2(w) { |
michael@0 | 24 | return String.fromCharCode((w >> 8) & 255, w & 255); |
michael@0 | 25 | } |
michael@0 | 26 | |
michael@0 | 27 | function byte4(w) { |
michael@0 | 28 | return String.fromCharCode((w >> 24) & 255, (w >> 16) & 255, (w >> 8) & 255, w & 255); |
michael@0 | 29 | } |
michael@0 | 30 | |
michael@0 | 31 | function byte2lsb(w) { |
michael@0 | 32 | return String.fromCharCode(w & 255, (w >> 8) & 255); |
michael@0 | 33 | } |
michael@0 | 34 | |
michael@0 | 35 | window.PNGlib = function(width,height,depth) { |
michael@0 | 36 | |
michael@0 | 37 | this.width = width; |
michael@0 | 38 | this.height = height; |
michael@0 | 39 | this.depth = depth; |
michael@0 | 40 | |
michael@0 | 41 | // pixel data and row filter identifier size |
michael@0 | 42 | this.pix_size = height * (width + 1); |
michael@0 | 43 | |
michael@0 | 44 | // deflate header, pix_size, block headers, adler32 checksum |
michael@0 | 45 | this.data_size = 2 + this.pix_size + 5 * Math.floor((0xfffe + this.pix_size) / 0xffff) + 4; |
michael@0 | 46 | |
michael@0 | 47 | // offsets and sizes of Png chunks |
michael@0 | 48 | this.ihdr_offs = 0; // IHDR offset and size |
michael@0 | 49 | this.ihdr_size = 4 + 4 + 13 + 4; |
michael@0 | 50 | this.plte_offs = this.ihdr_offs + this.ihdr_size; // PLTE offset and size |
michael@0 | 51 | this.plte_size = 4 + 4 + 3 * depth + 4; |
michael@0 | 52 | this.trns_offs = this.plte_offs + this.plte_size; // tRNS offset and size |
michael@0 | 53 | this.trns_size = 4 + 4 + depth + 4; |
michael@0 | 54 | this.idat_offs = this.trns_offs + this.trns_size; // IDAT offset and size |
michael@0 | 55 | this.idat_size = 4 + 4 + this.data_size + 4; |
michael@0 | 56 | this.iend_offs = this.idat_offs + this.idat_size; // IEND offset and size |
michael@0 | 57 | this.iend_size = 4 + 4 + 4; |
michael@0 | 58 | this.buffer_size = this.iend_offs + this.iend_size; // total PNG size |
michael@0 | 59 | |
michael@0 | 60 | this.buffer = new Array(); |
michael@0 | 61 | this.palette = new Object(); |
michael@0 | 62 | this.pindex = 0; |
michael@0 | 63 | |
michael@0 | 64 | var _crc32 = new Array(); |
michael@0 | 65 | |
michael@0 | 66 | // initialize buffer with zero bytes |
michael@0 | 67 | for (var i = 0; i < this.buffer_size; i++) { |
michael@0 | 68 | this.buffer[i] = "\x00"; |
michael@0 | 69 | } |
michael@0 | 70 | |
michael@0 | 71 | // initialize non-zero elements |
michael@0 | 72 | write(this.buffer, this.ihdr_offs, byte4(this.ihdr_size - 12), 'IHDR', byte4(width), byte4(height), "\x08\x03"); |
michael@0 | 73 | write(this.buffer, this.plte_offs, byte4(this.plte_size - 12), 'PLTE'); |
michael@0 | 74 | write(this.buffer, this.trns_offs, byte4(this.trns_size - 12), 'tRNS'); |
michael@0 | 75 | write(this.buffer, this.idat_offs, byte4(this.idat_size - 12), 'IDAT'); |
michael@0 | 76 | write(this.buffer, this.iend_offs, byte4(this.iend_size - 12), 'IEND'); |
michael@0 | 77 | |
michael@0 | 78 | // initialize deflate header |
michael@0 | 79 | var header = ((8 + (7 << 4)) << 8) | (3 << 6); |
michael@0 | 80 | header+= 31 - (header % 31); |
michael@0 | 81 | |
michael@0 | 82 | write(this.buffer, this.idat_offs + 8, byte2(header)); |
michael@0 | 83 | |
michael@0 | 84 | // initialize deflate block headers |
michael@0 | 85 | for (var i = 0; (i << 16) - 1 < this.pix_size; i++) { |
michael@0 | 86 | var size, bits; |
michael@0 | 87 | if (i + 0xffff < this.pix_size) { |
michael@0 | 88 | size = 0xffff; |
michael@0 | 89 | bits = "\x00"; |
michael@0 | 90 | } else { |
michael@0 | 91 | size = this.pix_size - (i << 16) - i; |
michael@0 | 92 | bits = "\x01"; |
michael@0 | 93 | } |
michael@0 | 94 | write(this.buffer, this.idat_offs + 8 + 2 + (i << 16) + (i << 2), bits, byte2lsb(size), byte2lsb(~size)); |
michael@0 | 95 | } |
michael@0 | 96 | |
michael@0 | 97 | /* Create crc32 lookup table */ |
michael@0 | 98 | for (var i = 0; i < 256; i++) { |
michael@0 | 99 | var c = i; |
michael@0 | 100 | for (var j = 0; j < 8; j++) { |
michael@0 | 101 | if (c & 1) { |
michael@0 | 102 | c = -306674912 ^ ((c >> 1) & 0x7fffffff); |
michael@0 | 103 | } else { |
michael@0 | 104 | c = (c >> 1) & 0x7fffffff; |
michael@0 | 105 | } |
michael@0 | 106 | } |
michael@0 | 107 | _crc32[i] = c; |
michael@0 | 108 | } |
michael@0 | 109 | |
michael@0 | 110 | // compute the index into a png for a given pixel |
michael@0 | 111 | this.index = function(x,y) { |
michael@0 | 112 | var i = y * (this.width + 1) + x + 1; |
michael@0 | 113 | var j = this.idat_offs + 8 + 2 + 5 * Math.floor((i / 0xffff) + 1) + i; |
michael@0 | 114 | return j; |
michael@0 | 115 | } |
michael@0 | 116 | |
michael@0 | 117 | // convert a color and build up the palette |
michael@0 | 118 | this.color = function(red, green, blue, alpha) { |
michael@0 | 119 | |
michael@0 | 120 | alpha = alpha >= 0 ? alpha : 255; |
michael@0 | 121 | var color = (((((alpha << 8) | red) << 8) | green) << 8) | blue; |
michael@0 | 122 | |
michael@0 | 123 | if (typeof this.palette[color] == "undefined") { |
michael@0 | 124 | if (this.pindex == this.depth) return "\x00"; |
michael@0 | 125 | |
michael@0 | 126 | var ndx = this.plte_offs + 8 + 3 * this.pindex; |
michael@0 | 127 | |
michael@0 | 128 | this.buffer[ndx + 0] = String.fromCharCode(red); |
michael@0 | 129 | this.buffer[ndx + 1] = String.fromCharCode(green); |
michael@0 | 130 | this.buffer[ndx + 2] = String.fromCharCode(blue); |
michael@0 | 131 | this.buffer[this.trns_offs+8+this.pindex] = String.fromCharCode(alpha); |
michael@0 | 132 | |
michael@0 | 133 | this.palette[color] = String.fromCharCode(this.pindex++); |
michael@0 | 134 | } |
michael@0 | 135 | return this.palette[color]; |
michael@0 | 136 | } |
michael@0 | 137 | |
michael@0 | 138 | // output a PNG string, Base64 encoded |
michael@0 | 139 | this.getBase64 = function() { |
michael@0 | 140 | |
michael@0 | 141 | var s = this.getDump(); |
michael@0 | 142 | |
michael@0 | 143 | var ch = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; |
michael@0 | 144 | var c1, c2, c3, e1, e2, e3, e4; |
michael@0 | 145 | var l = s.length; |
michael@0 | 146 | var i = 0; |
michael@0 | 147 | var r = ""; |
michael@0 | 148 | |
michael@0 | 149 | do { |
michael@0 | 150 | c1 = s.charCodeAt(i); |
michael@0 | 151 | e1 = c1 >> 2; |
michael@0 | 152 | c2 = s.charCodeAt(i+1); |
michael@0 | 153 | e2 = ((c1 & 3) << 4) | (c2 >> 4); |
michael@0 | 154 | c3 = s.charCodeAt(i+2); |
michael@0 | 155 | if (l < i+2) { e3 = 64; } else { e3 = ((c2 & 0xf) << 2) | (c3 >> 6); } |
michael@0 | 156 | if (l < i+3) { e4 = 64; } else { e4 = c3 & 0x3f; } |
michael@0 | 157 | r+= ch.charAt(e1) + ch.charAt(e2) + ch.charAt(e3) + ch.charAt(e4); |
michael@0 | 158 | } while ((i+= 3) < l); |
michael@0 | 159 | return r; |
michael@0 | 160 | } |
michael@0 | 161 | |
michael@0 | 162 | // output a PNG string |
michael@0 | 163 | this.getDump = function() { |
michael@0 | 164 | |
michael@0 | 165 | // compute adler32 of output pixels + row filter bytes |
michael@0 | 166 | var BASE = 65521; /* largest prime smaller than 65536 */ |
michael@0 | 167 | var NMAX = 5552; /* NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 */ |
michael@0 | 168 | var s1 = 1; |
michael@0 | 169 | var s2 = 0; |
michael@0 | 170 | var n = NMAX; |
michael@0 | 171 | |
michael@0 | 172 | for (var y = 0; y < this.height; y++) { |
michael@0 | 173 | for (var x = -1; x < this.width; x++) { |
michael@0 | 174 | s1+= this.buffer[this.index(x, y)].charCodeAt(0); |
michael@0 | 175 | s2+= s1; |
michael@0 | 176 | if ((n-= 1) == 0) { |
michael@0 | 177 | s1%= BASE; |
michael@0 | 178 | s2%= BASE; |
michael@0 | 179 | n = NMAX; |
michael@0 | 180 | } |
michael@0 | 181 | } |
michael@0 | 182 | } |
michael@0 | 183 | s1%= BASE; |
michael@0 | 184 | s2%= BASE; |
michael@0 | 185 | write(this.buffer, this.idat_offs + this.idat_size - 8, byte4((s2 << 16) | s1)); |
michael@0 | 186 | |
michael@0 | 187 | // compute crc32 of the PNG chunks |
michael@0 | 188 | function crc32(png, offs, size) { |
michael@0 | 189 | var crc = -1; |
michael@0 | 190 | for (var i = 4; i < size-4; i += 1) { |
michael@0 | 191 | crc = _crc32[(crc ^ png[offs+i].charCodeAt(0)) & 0xff] ^ ((crc >> 8) & 0x00ffffff); |
michael@0 | 192 | } |
michael@0 | 193 | write(png, offs+size-4, byte4(crc ^ -1)); |
michael@0 | 194 | } |
michael@0 | 195 | |
michael@0 | 196 | crc32(this.buffer, this.ihdr_offs, this.ihdr_size); |
michael@0 | 197 | crc32(this.buffer, this.plte_offs, this.plte_size); |
michael@0 | 198 | crc32(this.buffer, this.trns_offs, this.trns_size); |
michael@0 | 199 | crc32(this.buffer, this.idat_offs, this.idat_size); |
michael@0 | 200 | crc32(this.buffer, this.iend_offs, this.iend_size); |
michael@0 | 201 | |
michael@0 | 202 | // convert PNG to string |
michael@0 | 203 | return "\211PNG\r\n\032\n"+this.buffer.join(''); |
michael@0 | 204 | } |
michael@0 | 205 | } |
michael@0 | 206 | |
michael@0 | 207 | })(); |