Created
June 3, 2014 03:37
-
-
Save creationix/5df1a30f0c1d6a4f1218 to your computer and use it in GitHub Desktop.
tedit image creator
This file contains 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
var reloadTree = require('ui/tree.js').reload; | |
var modes = require('js-git/lib/modes.js'); | |
var bodec = require('bodec.js'); | |
var fs = require('data/fs.js'); | |
/** | |
* PNG Encoder | |
* | |
* @version 1.0 | |
* @author Robert Eisele <robert@xarg.org> | |
* @copyright Copyright (c) 2010, Robert Eisele | |
* @link http://www.xarg.org/2010/03/generate-client-side-png-files-using-javascript/ | |
* @license http://www.opensource.org/licenses/bsd-license.php BSD License | |
* | |
*/ | |
(function() { | |
// helper functions for that ctx | |
function write(buffer, offs) { | |
for (var i = 2; i < arguments.length; i++) { | |
for (var j = 0; j < arguments[i].length; j++) { | |
buffer[offs++] = arguments[i].charAt(j); | |
} | |
} | |
} | |
function byte2(w) { | |
return String.fromCharCode((w >> 8) & 255, w & 255); | |
} | |
function byte4(w) { | |
return String.fromCharCode((w >> 24) & 255, (w >> 16) & 255, (w >> 8) & 255, w & 255); | |
} | |
function byte2lsb(w) { | |
return String.fromCharCode(w & 255, (w >> 8) & 255); | |
} | |
window.PNGlib = function(width,height,depth) { | |
this.width = width; | |
this.height = height; | |
this.depth = depth; | |
// pixel data and row filter identifier size | |
this.pix_size = height * (width + 1); | |
// deflate header, pix_size, block headers, adler32 checksum | |
this.data_size = 2 + this.pix_size + 5 * Math.floor((0xfffe + this.pix_size) / 0xffff) + 4; | |
// offsets and sizes of Png chunks | |
this.ihdr_offs = 0; // IHDR offset and size | |
this.ihdr_size = 4 + 4 + 13 + 4; | |
this.plte_offs = this.ihdr_offs + this.ihdr_size; // PLTE offset and size | |
this.plte_size = 4 + 4 + 3 * depth + 4; | |
this.trns_offs = this.plte_offs + this.plte_size; // tRNS offset and size | |
this.trns_size = 4 + 4 + depth + 4; | |
this.idat_offs = this.trns_offs + this.trns_size; // IDAT offset and size | |
this.idat_size = 4 + 4 + this.data_size + 4; | |
this.iend_offs = this.idat_offs + this.idat_size; // IEND offset and size | |
this.iend_size = 4 + 4 + 4; | |
this.buffer_size = this.iend_offs + this.iend_size; // total PNG size | |
this.buffer = new Array(); | |
this.palette = new Object(); | |
this.pindex = 0; | |
var _crc32 = new Array(); | |
// initialize buffer with zero bytes | |
for (var i = 0; i < this.buffer_size; i++) { | |
this.buffer[i] = "\x00"; | |
} | |
// initialize non-zero elements | |
write(this.buffer, this.ihdr_offs, byte4(this.ihdr_size - 12), 'IHDR', byte4(width), byte4(height), "\x08\x03"); | |
write(this.buffer, this.plte_offs, byte4(this.plte_size - 12), 'PLTE'); | |
write(this.buffer, this.trns_offs, byte4(this.trns_size - 12), 'tRNS'); | |
write(this.buffer, this.idat_offs, byte4(this.idat_size - 12), 'IDAT'); | |
write(this.buffer, this.iend_offs, byte4(this.iend_size - 12), 'IEND'); | |
// initialize deflate header | |
var header = ((8 + (7 << 4)) << 8) | (3 << 6); | |
header+= 31 - (header % 31); | |
write(this.buffer, this.idat_offs + 8, byte2(header)); | |
// initialize deflate block headers | |
for (var i = 0; (i << 16) - 1 < this.pix_size; i++) { | |
var size, bits; | |
if (i + 0xffff < this.pix_size) { | |
size = 0xffff; | |
bits = "\x00"; | |
} else { | |
size = this.pix_size - (i << 16) - i; | |
bits = "\x01"; | |
} | |
write(this.buffer, this.idat_offs + 8 + 2 + (i << 16) + (i << 2), bits, byte2lsb(size), byte2lsb(~size)); | |
} | |
/* Create crc32 lookup table */ | |
for (var i = 0; i < 256; i++) { | |
var c = i; | |
for (var j = 0; j < 8; j++) { | |
if (c & 1) { | |
c = -306674912 ^ ((c >> 1) & 0x7fffffff); | |
} else { | |
c = (c >> 1) & 0x7fffffff; | |
} | |
} | |
_crc32[i] = c; | |
} | |
// compute the index into a png for a given pixel | |
this.index = function(x,y) { | |
var i = y * (this.width + 1) + x + 1; | |
var j = this.idat_offs + 8 + 2 + 5 * Math.floor((i / 0xffff) + 1) + i; | |
return j; | |
} | |
// convert a color and build up the palette | |
this.color = function(red, green, blue, alpha) { | |
alpha = alpha >= 0 ? alpha : 255; | |
var color = (((((alpha << 8) | red) << 8) | green) << 8) | blue; | |
if (typeof this.palette[color] == "undefined") { | |
if (this.pindex == this.depth) return "\x00"; | |
var ndx = this.plte_offs + 8 + 3 * this.pindex; | |
this.buffer[ndx + 0] = String.fromCharCode(red); | |
this.buffer[ndx + 1] = String.fromCharCode(green); | |
this.buffer[ndx + 2] = String.fromCharCode(blue); | |
this.buffer[this.trns_offs+8+this.pindex] = String.fromCharCode(alpha); | |
this.palette[color] = String.fromCharCode(this.pindex++); | |
} | |
return this.palette[color]; | |
} | |
// output a PNG string, Base64 encoded | |
this.getBase64 = function() { | |
var s = this.getDump(); | |
var ch = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; | |
var c1, c2, c3, e1, e2, e3, e4; | |
var l = s.length; | |
var i = 0; | |
var r = ""; | |
do { | |
c1 = s.charCodeAt(i); | |
e1 = c1 >> 2; | |
c2 = s.charCodeAt(i+1); | |
e2 = ((c1 & 3) << 4) | (c2 >> 4); | |
c3 = s.charCodeAt(i+2); | |
if (l < i+2) { e3 = 64; } else { e3 = ((c2 & 0xf) << 2) | (c3 >> 6); } | |
if (l < i+3) { e4 = 64; } else { e4 = c3 & 0x3f; } | |
r+= ch.charAt(e1) + ch.charAt(e2) + ch.charAt(e3) + ch.charAt(e4); | |
} while ((i+= 3) < l); | |
return r; | |
} | |
// output a PNG string | |
this.getDump = function() { | |
// compute adler32 of output pixels + row filter bytes | |
var BASE = 65521; /* largest prime smaller than 65536 */ | |
var NMAX = 5552; /* NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 */ | |
var s1 = 1; | |
var s2 = 0; | |
var n = NMAX; | |
for (var y = 0; y < this.height; y++) { | |
for (var x = -1; x < this.width; x++) { | |
s1+= this.buffer[this.index(x, y)].charCodeAt(0); | |
s2+= s1; | |
if ((n-= 1) == 0) { | |
s1%= BASE; | |
s2%= BASE; | |
n = NMAX; | |
} | |
} | |
} | |
s1%= BASE; | |
s2%= BASE; | |
write(this.buffer, this.idat_offs + this.idat_size - 8, byte4((s2 << 16) | s1)); | |
// compute crc32 of the PNG chunks | |
function crc32(png, offs, size) { | |
var crc = -1; | |
for (var i = 4; i < size-4; i += 1) { | |
crc = _crc32[(crc ^ png[offs+i].charCodeAt(0)) & 0xff] ^ ((crc >> 8) & 0x00ffffff); | |
} | |
write(png, offs+size-4, byte4(crc ^ -1)); | |
} | |
crc32(this.buffer, this.ihdr_offs, this.ihdr_size); | |
crc32(this.buffer, this.plte_offs, this.plte_size); | |
crc32(this.buffer, this.trns_offs, this.trns_size); | |
crc32(this.buffer, this.idat_offs, this.idat_size); | |
crc32(this.buffer, this.iend_offs, this.iend_size); | |
// convert PNG to string | |
return "\211PNG\r\n\032\n"+this.buffer.join(''); | |
} | |
} | |
})(); | |
var p = new window.PNGlib(200, 200, 256); // construcor takes height, weight and color-depth | |
p.color(0, 0, 0, 0); // set the background transparent | |
for (var i = 0, num = 200 / 10; i <= num; i+=0.01) { | |
var x = i * 10; | |
var y = Math.sin(i) * Math.sin(i) * 50 + 50; | |
// use a color triad of Microsofts million dollar color | |
p.buffer[p.index(Math.floor(x), Math.floor(y - 10))] = p.color(0x00, 0x44, 0xcc); | |
p.buffer[p.index(Math.floor(x), Math.floor(y))] = p.color(0xcc, 0x00, 0x44); | |
p.buffer[p.index(Math.floor(x), Math.floor(y + 10))] = p.color(0x00, 0xcc, 0x44); | |
} | |
for (var i = 0; i < 50; i++) { | |
for (var j = 0; j < 50; j++) { | |
p.buffer[p.index(i + 90, j + 135)] = p.color(0xcc, 0x00, 0x44); | |
p.buffer[p.index(i + 80, j + 120)] = p.color(0x00, 0x44, 0xcc); | |
p.buffer[p.index(i + 100, j + 130)] = p.color(0x00, 0xcc, 0x44); | |
} | |
} | |
var buffer = bodec.fromRaw(p.getDump()); | |
fs.saveAs("", "blob", buffer, function (err, hash) { | |
if (err) throw err; | |
fs.writeEntry("test.png", { | |
mode: modes.blob, | |
hash: hash | |
}, function (err) { | |
if (err) throw err; | |
reloadTree(); | |
}); | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment