├── sample ├── build.hxml ├── output │ ├── index.html │ ├── PNGlibSample.js │ └── pnglib.js └── Sample.hx ├── circles.png ├── haxe-PNGlib.zip ├── js └── PNGlib.hx ├── haxelib.json ├── .gitignore ├── LICENSE ├── README.md ├── .gitattributes └── libs └── pnglib.js /sample/build.hxml: -------------------------------------------------------------------------------- 1 | -lib PNGlib 2 | -main Sample 3 | -js output/PNGlibSample.js -------------------------------------------------------------------------------- /circles.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hamaluik/haxe-PNGlib/master/circles.png -------------------------------------------------------------------------------- /haxe-PNGlib.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hamaluik/haxe-PNGlib/master/haxe-PNGlib.zip -------------------------------------------------------------------------------- /sample/output/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | PNGlib Sample 4 | 5 | 6 | 7 |

PNGlib Sample

8 | 9 | 10 | -------------------------------------------------------------------------------- /js/PNGlib.hx: -------------------------------------------------------------------------------- 1 | package js; 2 | 3 | @:native('PNGlib') 4 | extern class PNGlib { 5 | public function new(width:Int, height:Int, colourDepth:Int); 6 | public function index(x:Int, y:Int):Int; 7 | public function color(red:Int, green:Int, blue:Int, ?alpha:Int):Int; 8 | public function getBase64():String; 9 | public function getDump():String; 10 | public var buffer:Array; 11 | } -------------------------------------------------------------------------------- /haxelib.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "PNGlib", 3 | "url" : "https://github.com/FuzzyWuzzie/haxe-PNGlib", 4 | "license": "MIT", 5 | "tags": ["js"], 6 | "description": "Haxe externs for the pnglib.js library (for saving files from the browser).", 7 | "version": "1.0.0", 8 | "releasenote": "Initial release, everything is working correctly.", 9 | "contributors": ["FuzzyWuzzie"], 10 | "dependencies": {} 11 | } -------------------------------------------------------------------------------- /sample/output/PNGlibSample.js: -------------------------------------------------------------------------------- 1 | (function (console) { "use strict"; 2 | var Sample = function() { }; 3 | Sample.main = function() { 4 | var p = new PNGlib(256,256,256); 5 | var background = p.color(0,0,0,0); 6 | var _g = 0; 7 | while(_g < 256) { 8 | var i = _g++; 9 | var _g1 = 0; 10 | while(_g1 < 256) { 11 | var j = _g1++; 12 | var dist = Math.sqrt((i - 128) * (i - 128) + (j - 128) * (j - 128)); 13 | if(dist < 32) p.buffer[p.index(i,j)] = p.color(255,255,255); else if(dist < 64) p.buffer[p.index(i,j)] = p.color(251,199,7); else if(dist < 96) p.buffer[p.index(i,j)] = p.color(234,130,32); else if(dist < 128) p.buffer[p.index(i,j)] = p.color(168,75,56); else if(dist < 160) p.buffer[p.index(i,j)] = p.color(20,20,25); 14 | } 15 | } 16 | var base64 = p.getBase64(); 17 | window.document.write(""); 18 | }; 19 | Sample.main(); 20 | })(typeof console != "undefined" ? console : {log:function(){}}); 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ### Windows ### 2 | # Windows image file caches 3 | Thumbs.db 4 | ehthumbs.db 5 | 6 | # Folder config file 7 | Desktop.ini 8 | 9 | # Recycle Bin used on file shares 10 | $RECYCLE.BIN/ 11 | 12 | # Windows Installer files 13 | *.cab 14 | *.msi 15 | *.msm 16 | *.msp 17 | 18 | # Windows shortcuts 19 | *.lnk 20 | 21 | 22 | ### OSX ### 23 | .DS_Store 24 | .AppleDouble 25 | .LSOverride 26 | 27 | # Icon must end with two \r 28 | Icon 29 | 30 | 31 | # Thumbnails 32 | ._* 33 | 34 | # Files that might appear in the root of a volume 35 | .DocumentRevisions-V100 36 | .fseventsd 37 | .Spotlight-V100 38 | .TemporaryItems 39 | .Trashes 40 | .VolumeIcon.icns 41 | 42 | # Directories potentially created on remote AFP share 43 | .AppleDB 44 | .AppleDesktop 45 | Network Trash Folder 46 | Temporary Items 47 | .apdisk 48 | 49 | 50 | ### Linux ### 51 | *~ 52 | 53 | # KDE directory preferences 54 | .directory 55 | 56 | # Linux trash folder which might appear on any partition or disk 57 | .Trash-* -------------------------------------------------------------------------------- /sample/Sample.hx: -------------------------------------------------------------------------------- 1 | import js.PNGlib; 2 | 3 | class Sample { 4 | static public function main() { 5 | var p:PNGlib = new PNGlib(256, 256, 256); 6 | var background:Int = p.color(0, 0, 0, 0); 7 | 8 | var num:Float = 200 / 10; 9 | var i:Float = 0; 10 | while(i <= num) { 11 | var x:Float = i * 10; 12 | var y = Math.sin(i) * Math.sin(i) * 50 + 50; 13 | 14 | // use a color triad of Microsofts million dollar color 15 | p.buffer[p.index(Math.floor(x), Math.floor(y - 10))] = p.color(0x00, 0x44, 0xcc); 16 | p.buffer[p.index(Math.floor(x), Math.floor(y))] = p.color(0xcc, 0x00, 0x44); 17 | p.buffer[p.index(Math.floor(x), Math.floor(y + 10))] = p.color(0x00, 0xcc, 0x44); 18 | 19 | i += 0.01; 20 | } 21 | 22 | for(i in 0...50) { 23 | for(j in 0...50) { 24 | p.buffer[p.index(i + 90, j + 135)] = p.color(0xcc, 0x00, 0x44); 25 | p.buffer[p.index(i + 80, j + 120)] = p.color(0x00, 0x44, 0xcc); 26 | p.buffer[p.index(i + 100, j + 130)] = p.color(0x00, 0xcc, 0x44); 27 | } 28 | } 29 | 30 | var base64:String = p.getBase64(); 31 | js.Browser.document.write(''); 32 | } 33 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Kenton Hamaluik 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # haxe-PNGlib 2 | [![License](https://img.shields.io/badge/license-MIT-blue.svg?style=flat-square)](https://github.com/FuzzyWuzzie/haxe-PNGlib/blob/master/LICENSE) 3 | [![License](https://img.shields.io/badge/license-BSD-blue.svg?style=flat-square)](http://www.opensource.org/licenses/bsd-license.php) 4 | 5 | Haxe externs for the [PNGlib][1] library. 6 | 7 | ## Usage 8 | 9 | ```haxe 10 | // create a new PNG image 11 | var p:PNGlib = new PNGlib(width, height, colourDepth); 12 | 13 | // access colours in the buffer 14 | p.buffer[p.index(x, y)] = p.color(red, green, blue, alpha); 15 | 16 | // output strings describing the file 17 | var outputB64:String = p.getBase64(); 18 | var outputPNG:String = p.getDump(); 19 | ``` 20 | 21 | **NOTE**: You must import the [PNGlib][1] library into your HTML file for this to work. In case the source ever goes offline, a copy can be found here: https://github.com/FuzzyWuzzie/haxe-PNGlib/blob/master/libs/pnglib.js 22 | 23 | ## Examples 24 | 25 | ### Drawing Some Circles 26 | 27 | ```haxe 28 | var p:PNGlib = new PNGlib(256, 256, 256); 29 | var background:Int = p.color(0, 0, 0, 0); 30 | 31 | for(i in 0...256) { 32 | for(j in 0...256) { 33 | var dist:Float = Math.sqrt((i - 128)*(i - 128) + (j - 128)*(j - 128)); 34 | if(dist < 32) { 35 | p.buffer[p.index(i, j)] = p.color(255, 255, 255); 36 | } 37 | else if(dist < 64) { 38 | p.buffer[p.index(i, j)] = p.color(251,199,7); 39 | } 40 | else if(dist < 96) { 41 | p.buffer[p.index(i, j)] = p.color(234, 130, 32); 42 | } 43 | else if(dist < 128) { 44 | p.buffer[p.index(i, j)] = p.color(168,75,56); 45 | } 46 | else if(dist < 160) { 47 | p.buffer[p.index(i, j)] = p.color(20, 20, 25); 48 | } 49 | } 50 | } 51 | 52 | var base64:String = p.getBase64(); 53 | js.Browser.document.write(''); 54 | ``` 55 | 56 | Results in: 57 | 58 | ![Concentric circles](https://raw.githubusercontent.com/FuzzyWuzzie/haxe-PNGlib/master/circles.png) 59 | 60 | ### Live Sample 61 | 62 | A [live sample](http://FuzzyWuzzie.github.io/haxe-PNGlib/) is available which generates a PNG image and embeds it in a webpage. 63 | 64 | 65 | [1]: http://www.xarg.org/2010/03/generate-client-side-png-files-using-javascript/ -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Apply native OS line-endings on checkout of these files... 2 | *.boo text 3 | *.c text 4 | *.cginc text 5 | *.config text 6 | *.contentproj text 7 | *.cpp text 8 | *.cs text 9 | *.css text 10 | *.dae text 11 | *.DAE text 12 | *.dtd text 13 | *.fx text 14 | *.glsl text 15 | *.h text 16 | *.htm text 17 | *.html text 18 | *.inc text 19 | *.ini text 20 | *.js text 21 | *.JSFL text 22 | *.jsfl text 23 | *.json text 24 | *.log text 25 | *.md text 26 | *.mel text 27 | *.php text 28 | *.shader text 29 | *.txt text 30 | *.TXT text 31 | *.xaml text 32 | *.xml text 33 | *.xsd text 34 | .gitattributes text 35 | .gitignore text 36 | COPYING text 37 | INSTALL* text 38 | KEYS* text 39 | LICENSE* text 40 | NEWS* text 41 | NOTICE* text 42 | README* text 43 | TODO* text 44 | WHATSNEW* text 45 | 46 | # Apply Unix-style LF line-endings on checkout of these files... 47 | *.meta text eol=lf 48 | *.sh text eol=lf 49 | *.vspscc text eol=lf 50 | .htaccess text eol=lf 51 | 52 | # Apply Windows/DOS-style CR-LF line-endings on checkout of these files... 53 | *.bat text eol=crlf 54 | *.cmd text eol=crlf 55 | *.csproj text eol=crlf 56 | *.sln text eol=crlf 57 | *.user text eol=crlf 58 | *.vcproj text eol=crlf 59 | 60 | # No end-of-line conversions are applied (i.e., "-text -diff") to these files... 61 | *.7z binary 62 | *.ai binary 63 | *.anim binary 64 | *.apk binary 65 | *.asset binary 66 | *.bin binary 67 | *.bmp binary 68 | *.BMP binary 69 | *.com binary 70 | *.COM binary 71 | *.controller binary 72 | *.cubemap binary 73 | *.dex binary 74 | *.dll binary 75 | *.DLL binary 76 | *.dylib binary 77 | *.eps binary 78 | *.exe binary 79 | *.EXE binary 80 | *.exr binary 81 | *.fbx binary 82 | *.FBX binary 83 | *.fla binary 84 | *.flare binary 85 | *.flv binary 86 | *.gif binary 87 | *.guiskin binary 88 | *.gz binary 89 | *.ht binary 90 | *.ico binary 91 | *.jpeg binary 92 | *.jpg binary 93 | *.keystore binary 94 | *.mask binary 95 | *.mat binary 96 | *.mb binary 97 | *.mp3 binary 98 | *.mp4 binary 99 | *.mpg binary 100 | *.ogg binary 101 | *.PCX binary 102 | *.pcx binary 103 | *.pdb binary 104 | *.pdf binary 105 | *.physicMaterial binary 106 | *.physicmaterial binary 107 | *.png binary 108 | *.prefab binary 109 | *.ps binary 110 | *.psd binary 111 | *.qt binary 112 | *.so binary 113 | *.swf binary 114 | *.tga binary 115 | *.tif binary 116 | *.tiff binary 117 | *.ttf binary 118 | *.TTF binary 119 | *.unity binary 120 | *.unitypackage binary 121 | *.unityPackage binary 122 | *.wav binary 123 | *.wmv binary 124 | *.zip binary 125 | *.ZIP binary -------------------------------------------------------------------------------- /libs/pnglib.js: -------------------------------------------------------------------------------- 1 | /** 2 | * A handy class to calculate color values. 3 | * 4 | * @version 1.0 5 | * @author Robert Eisele 6 | * @copyright Copyright (c) 2010, Robert Eisele 7 | * @link http://www.xarg.org/2010/03/generate-client-side-png-files-using-javascript/ 8 | * @license http://www.opensource.org/licenses/bsd-license.php BSD License 9 | * 10 | */ 11 | 12 | (function() { 13 | 14 | // helper functions for that ctx 15 | function write(buffer, offs) { 16 | for (var i = 2; i < arguments.length; i++) { 17 | for (var j = 0; j < arguments[i].length; j++) { 18 | buffer[offs++] = arguments[i].charAt(j); 19 | } 20 | } 21 | } 22 | 23 | function byte2(w) { 24 | return String.fromCharCode((w >> 8) & 255, w & 255); 25 | } 26 | 27 | function byte4(w) { 28 | return String.fromCharCode((w >> 24) & 255, (w >> 16) & 255, (w >> 8) & 255, w & 255); 29 | } 30 | 31 | function byte2lsb(w) { 32 | return String.fromCharCode(w & 255, (w >> 8) & 255); 33 | } 34 | 35 | window.PNGlib = function(width,height,depth) { 36 | 37 | this.width = width; 38 | this.height = height; 39 | this.depth = depth; 40 | 41 | // pixel data and row filter identifier size 42 | this.pix_size = height * (width + 1); 43 | 44 | // deflate header, pix_size, block headers, adler32 checksum 45 | this.data_size = 2 + this.pix_size + 5 * Math.floor((0xfffe + this.pix_size) / 0xffff) + 4; 46 | 47 | // offsets and sizes of Png chunks 48 | this.ihdr_offs = 0; // IHDR offset and size 49 | this.ihdr_size = 4 + 4 + 13 + 4; 50 | this.plte_offs = this.ihdr_offs + this.ihdr_size; // PLTE offset and size 51 | this.plte_size = 4 + 4 + 3 * depth + 4; 52 | this.trns_offs = this.plte_offs + this.plte_size; // tRNS offset and size 53 | this.trns_size = 4 + 4 + depth + 4; 54 | this.idat_offs = this.trns_offs + this.trns_size; // IDAT offset and size 55 | this.idat_size = 4 + 4 + this.data_size + 4; 56 | this.iend_offs = this.idat_offs + this.idat_size; // IEND offset and size 57 | this.iend_size = 4 + 4 + 4; 58 | this.buffer_size = this.iend_offs + this.iend_size; // total PNG size 59 | 60 | this.buffer = new Array(); 61 | this.palette = new Object(); 62 | this.pindex = 0; 63 | 64 | var _crc32 = new Array(); 65 | 66 | // initialize buffer with zero bytes 67 | for (var i = 0; i < this.buffer_size; i++) { 68 | this.buffer[i] = "\x00"; 69 | } 70 | 71 | // initialize non-zero elements 72 | write(this.buffer, this.ihdr_offs, byte4(this.ihdr_size - 12), 'IHDR', byte4(width), byte4(height), "\x08\x03"); 73 | write(this.buffer, this.plte_offs, byte4(this.plte_size - 12), 'PLTE'); 74 | write(this.buffer, this.trns_offs, byte4(this.trns_size - 12), 'tRNS'); 75 | write(this.buffer, this.idat_offs, byte4(this.idat_size - 12), 'IDAT'); 76 | write(this.buffer, this.iend_offs, byte4(this.iend_size - 12), 'IEND'); 77 | 78 | // initialize deflate header 79 | var header = ((8 + (7 << 4)) << 8) | (3 << 6); 80 | header+= 31 - (header % 31); 81 | 82 | write(this.buffer, this.idat_offs + 8, byte2(header)); 83 | 84 | // initialize deflate block headers 85 | for (var i = 0; (i << 16) - 1 < this.pix_size; i++) { 86 | var size, bits; 87 | if (i + 0xffff < this.pix_size) { 88 | size = 0xffff; 89 | bits = "\x00"; 90 | } else { 91 | size = this.pix_size - (i << 16) - i; 92 | bits = "\x01"; 93 | } 94 | write(this.buffer, this.idat_offs + 8 + 2 + (i << 16) + (i << 2), bits, byte2lsb(size), byte2lsb(~size)); 95 | } 96 | 97 | /* Create crc32 lookup table */ 98 | for (var i = 0; i < 256; i++) { 99 | var c = i; 100 | for (var j = 0; j < 8; j++) { 101 | if (c & 1) { 102 | c = -306674912 ^ ((c >> 1) & 0x7fffffff); 103 | } else { 104 | c = (c >> 1) & 0x7fffffff; 105 | } 106 | } 107 | _crc32[i] = c; 108 | } 109 | 110 | // compute the index into a png for a given pixel 111 | this.index = function(x,y) { 112 | var i = y * (this.width + 1) + x + 1; 113 | var j = this.idat_offs + 8 + 2 + 5 * Math.floor((i / 0xffff) + 1) + i; 114 | return j; 115 | } 116 | 117 | // convert a color and build up the palette 118 | this.color = function(red, green, blue, alpha) { 119 | 120 | alpha = alpha >= 0 ? alpha : 255; 121 | var color = (((((alpha << 8) | red) << 8) | green) << 8) | blue; 122 | 123 | if (typeof this.palette[color] == "undefined") { 124 | if (this.pindex == this.depth) return "\x00"; 125 | 126 | var ndx = this.plte_offs + 8 + 3 * this.pindex; 127 | 128 | this.buffer[ndx + 0] = String.fromCharCode(red); 129 | this.buffer[ndx + 1] = String.fromCharCode(green); 130 | this.buffer[ndx + 2] = String.fromCharCode(blue); 131 | this.buffer[this.trns_offs+8+this.pindex] = String.fromCharCode(alpha); 132 | 133 | this.palette[color] = String.fromCharCode(this.pindex++); 134 | } 135 | return this.palette[color]; 136 | } 137 | 138 | // output a PNG string, Base64 encoded 139 | this.getBase64 = function() { 140 | 141 | var s = this.getDump(); 142 | 143 | var ch = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; 144 | var c1, c2, c3, e1, e2, e3, e4; 145 | var l = s.length; 146 | var i = 0; 147 | var r = ""; 148 | 149 | do { 150 | c1 = s.charCodeAt(i); 151 | e1 = c1 >> 2; 152 | c2 = s.charCodeAt(i+1); 153 | e2 = ((c1 & 3) << 4) | (c2 >> 4); 154 | c3 = s.charCodeAt(i+2); 155 | if (l < i+2) { e3 = 64; } else { e3 = ((c2 & 0xf) << 2) | (c3 >> 6); } 156 | if (l < i+3) { e4 = 64; } else { e4 = c3 & 0x3f; } 157 | r+= ch.charAt(e1) + ch.charAt(e2) + ch.charAt(e3) + ch.charAt(e4); 158 | } while ((i+= 3) < l); 159 | return r; 160 | } 161 | 162 | // output a PNG string 163 | this.getDump = function() { 164 | 165 | // compute adler32 of output pixels + row filter bytes 166 | var BASE = 65521; /* largest prime smaller than 65536 */ 167 | var NMAX = 5552; /* NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 */ 168 | var s1 = 1; 169 | var s2 = 0; 170 | var n = NMAX; 171 | 172 | for (var y = 0; y < this.height; y++) { 173 | for (var x = -1; x < this.width; x++) { 174 | s1+= this.buffer[this.index(x, y)].charCodeAt(0); 175 | s2+= s1; 176 | if ((n-= 1) == 0) { 177 | s1%= BASE; 178 | s2%= BASE; 179 | n = NMAX; 180 | } 181 | } 182 | } 183 | s1%= BASE; 184 | s2%= BASE; 185 | write(this.buffer, this.idat_offs + this.idat_size - 8, byte4((s2 << 16) | s1)); 186 | 187 | // compute crc32 of the PNG chunks 188 | function crc32(png, offs, size) { 189 | var crc = -1; 190 | for (var i = 4; i < size-4; i += 1) { 191 | crc = _crc32[(crc ^ png[offs+i].charCodeAt(0)) & 0xff] ^ ((crc >> 8) & 0x00ffffff); 192 | } 193 | write(png, offs+size-4, byte4(crc ^ -1)); 194 | } 195 | 196 | crc32(this.buffer, this.ihdr_offs, this.ihdr_size); 197 | crc32(this.buffer, this.plte_offs, this.plte_size); 198 | crc32(this.buffer, this.trns_offs, this.trns_size); 199 | crc32(this.buffer, this.idat_offs, this.idat_size); 200 | crc32(this.buffer, this.iend_offs, this.iend_size); 201 | 202 | // convert PNG to string 203 | return "\211PNG\r\n\032\n"+this.buffer.join(''); 204 | } 205 | } 206 | 207 | })(); 208 | -------------------------------------------------------------------------------- /sample/output/pnglib.js: -------------------------------------------------------------------------------- 1 | /** 2 | * A handy class to calculate color values. 3 | * 4 | * @version 1.0 5 | * @author Robert Eisele 6 | * @copyright Copyright (c) 2010, Robert Eisele 7 | * @link http://www.xarg.org/2010/03/generate-client-side-png-files-using-javascript/ 8 | * @license http://www.opensource.org/licenses/bsd-license.php BSD License 9 | * 10 | */ 11 | 12 | (function() { 13 | 14 | // helper functions for that ctx 15 | function write(buffer, offs) { 16 | for (var i = 2; i < arguments.length; i++) { 17 | for (var j = 0; j < arguments[i].length; j++) { 18 | buffer[offs++] = arguments[i].charAt(j); 19 | } 20 | } 21 | } 22 | 23 | function byte2(w) { 24 | return String.fromCharCode((w >> 8) & 255, w & 255); 25 | } 26 | 27 | function byte4(w) { 28 | return String.fromCharCode((w >> 24) & 255, (w >> 16) & 255, (w >> 8) & 255, w & 255); 29 | } 30 | 31 | function byte2lsb(w) { 32 | return String.fromCharCode(w & 255, (w >> 8) & 255); 33 | } 34 | 35 | window.PNGlib = function(width,height,depth) { 36 | 37 | this.width = width; 38 | this.height = height; 39 | this.depth = depth; 40 | 41 | // pixel data and row filter identifier size 42 | this.pix_size = height * (width + 1); 43 | 44 | // deflate header, pix_size, block headers, adler32 checksum 45 | this.data_size = 2 + this.pix_size + 5 * Math.floor((0xfffe + this.pix_size) / 0xffff) + 4; 46 | 47 | // offsets and sizes of Png chunks 48 | this.ihdr_offs = 0; // IHDR offset and size 49 | this.ihdr_size = 4 + 4 + 13 + 4; 50 | this.plte_offs = this.ihdr_offs + this.ihdr_size; // PLTE offset and size 51 | this.plte_size = 4 + 4 + 3 * depth + 4; 52 | this.trns_offs = this.plte_offs + this.plte_size; // tRNS offset and size 53 | this.trns_size = 4 + 4 + depth + 4; 54 | this.idat_offs = this.trns_offs + this.trns_size; // IDAT offset and size 55 | this.idat_size = 4 + 4 + this.data_size + 4; 56 | this.iend_offs = this.idat_offs + this.idat_size; // IEND offset and size 57 | this.iend_size = 4 + 4 + 4; 58 | this.buffer_size = this.iend_offs + this.iend_size; // total PNG size 59 | 60 | this.buffer = new Array(); 61 | this.palette = new Object(); 62 | this.pindex = 0; 63 | 64 | var _crc32 = new Array(); 65 | 66 | // initialize buffer with zero bytes 67 | for (var i = 0; i < this.buffer_size; i++) { 68 | this.buffer[i] = "\x00"; 69 | } 70 | 71 | // initialize non-zero elements 72 | write(this.buffer, this.ihdr_offs, byte4(this.ihdr_size - 12), 'IHDR', byte4(width), byte4(height), "\x08\x03"); 73 | write(this.buffer, this.plte_offs, byte4(this.plte_size - 12), 'PLTE'); 74 | write(this.buffer, this.trns_offs, byte4(this.trns_size - 12), 'tRNS'); 75 | write(this.buffer, this.idat_offs, byte4(this.idat_size - 12), 'IDAT'); 76 | write(this.buffer, this.iend_offs, byte4(this.iend_size - 12), 'IEND'); 77 | 78 | // initialize deflate header 79 | var header = ((8 + (7 << 4)) << 8) | (3 << 6); 80 | header+= 31 - (header % 31); 81 | 82 | write(this.buffer, this.idat_offs + 8, byte2(header)); 83 | 84 | // initialize deflate block headers 85 | for (var i = 0; (i << 16) - 1 < this.pix_size; i++) { 86 | var size, bits; 87 | if (i + 0xffff < this.pix_size) { 88 | size = 0xffff; 89 | bits = "\x00"; 90 | } else { 91 | size = this.pix_size - (i << 16) - i; 92 | bits = "\x01"; 93 | } 94 | write(this.buffer, this.idat_offs + 8 + 2 + (i << 16) + (i << 2), bits, byte2lsb(size), byte2lsb(~size)); 95 | } 96 | 97 | /* Create crc32 lookup table */ 98 | for (var i = 0; i < 256; i++) { 99 | var c = i; 100 | for (var j = 0; j < 8; j++) { 101 | if (c & 1) { 102 | c = -306674912 ^ ((c >> 1) & 0x7fffffff); 103 | } else { 104 | c = (c >> 1) & 0x7fffffff; 105 | } 106 | } 107 | _crc32[i] = c; 108 | } 109 | 110 | // compute the index into a png for a given pixel 111 | this.index = function(x,y) { 112 | var i = y * (this.width + 1) + x + 1; 113 | var j = this.idat_offs + 8 + 2 + 5 * Math.floor((i / 0xffff) + 1) + i; 114 | return j; 115 | } 116 | 117 | // convert a color and build up the palette 118 | this.color = function(red, green, blue, alpha) { 119 | 120 | alpha = alpha >= 0 ? alpha : 255; 121 | var color = (((((alpha << 8) | red) << 8) | green) << 8) | blue; 122 | 123 | if (typeof this.palette[color] == "undefined") { 124 | if (this.pindex == this.depth) return "\x00"; 125 | 126 | var ndx = this.plte_offs + 8 + 3 * this.pindex; 127 | 128 | this.buffer[ndx + 0] = String.fromCharCode(red); 129 | this.buffer[ndx + 1] = String.fromCharCode(green); 130 | this.buffer[ndx + 2] = String.fromCharCode(blue); 131 | this.buffer[this.trns_offs+8+this.pindex] = String.fromCharCode(alpha); 132 | 133 | this.palette[color] = String.fromCharCode(this.pindex++); 134 | } 135 | return this.palette[color]; 136 | } 137 | 138 | // output a PNG string, Base64 encoded 139 | this.getBase64 = function() { 140 | 141 | var s = this.getDump(); 142 | 143 | var ch = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; 144 | var c1, c2, c3, e1, e2, e3, e4; 145 | var l = s.length; 146 | var i = 0; 147 | var r = ""; 148 | 149 | do { 150 | c1 = s.charCodeAt(i); 151 | e1 = c1 >> 2; 152 | c2 = s.charCodeAt(i+1); 153 | e2 = ((c1 & 3) << 4) | (c2 >> 4); 154 | c3 = s.charCodeAt(i+2); 155 | if (l < i+2) { e3 = 64; } else { e3 = ((c2 & 0xf) << 2) | (c3 >> 6); } 156 | if (l < i+3) { e4 = 64; } else { e4 = c3 & 0x3f; } 157 | r+= ch.charAt(e1) + ch.charAt(e2) + ch.charAt(e3) + ch.charAt(e4); 158 | } while ((i+= 3) < l); 159 | return r; 160 | } 161 | 162 | // output a PNG string 163 | this.getDump = function() { 164 | 165 | // compute adler32 of output pixels + row filter bytes 166 | var BASE = 65521; /* largest prime smaller than 65536 */ 167 | var NMAX = 5552; /* NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 */ 168 | var s1 = 1; 169 | var s2 = 0; 170 | var n = NMAX; 171 | 172 | for (var y = 0; y < this.height; y++) { 173 | for (var x = -1; x < this.width; x++) { 174 | s1+= this.buffer[this.index(x, y)].charCodeAt(0); 175 | s2+= s1; 176 | if ((n-= 1) == 0) { 177 | s1%= BASE; 178 | s2%= BASE; 179 | n = NMAX; 180 | } 181 | } 182 | } 183 | s1%= BASE; 184 | s2%= BASE; 185 | write(this.buffer, this.idat_offs + this.idat_size - 8, byte4((s2 << 16) | s1)); 186 | 187 | // compute crc32 of the PNG chunks 188 | function crc32(png, offs, size) { 189 | var crc = -1; 190 | for (var i = 4; i < size-4; i += 1) { 191 | crc = _crc32[(crc ^ png[offs+i].charCodeAt(0)) & 0xff] ^ ((crc >> 8) & 0x00ffffff); 192 | } 193 | write(png, offs+size-4, byte4(crc ^ -1)); 194 | } 195 | 196 | crc32(this.buffer, this.ihdr_offs, this.ihdr_size); 197 | crc32(this.buffer, this.plte_offs, this.plte_size); 198 | crc32(this.buffer, this.trns_offs, this.trns_size); 199 | crc32(this.buffer, this.idat_offs, this.idat_size); 200 | crc32(this.buffer, this.iend_offs, this.iend_size); 201 | 202 | // convert PNG to string 203 | return "\211PNG\r\n\032\n"+this.buffer.join(''); 204 | } 205 | } 206 | 207 | })(); 208 | --------------------------------------------------------------------------------