diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..7d1af85e --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +armorpaint.blend1 +build_armorpaint/ diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000..e69de29b diff --git a/Bundled/brush.png b/Bundled/brush.png new file mode 100644 index 00000000..fa9c1673 Binary files /dev/null and b/Bundled/brush.png differ diff --git a/Bundled/cay_thumb.jpg b/Bundled/cay_thumb.jpg new file mode 100644 index 00000000..ff1125d5 Binary files /dev/null and b/Bundled/cay_thumb.jpg differ diff --git a/Bundled/color_wheel.png b/Bundled/color_wheel.png new file mode 100644 index 00000000..aee45c17 Binary files /dev/null and b/Bundled/color_wheel.png differ diff --git a/Bundled/default_brush.json b/Bundled/default_brush.json new file mode 100644 index 00000000..84cbe281 --- /dev/null +++ b/Bundled/default_brush.json @@ -0,0 +1 @@ +{"nodes":[{"id":0,"name":"On Brush","type":"OnBrushNode","x":335,"y":97,"color":-11959648,"inputs":[],"outputs":[{"id":0,"node_id":0,"name":"Out","type":"ACTION","color":-10238109,"default_value":null},{"id":0,"node_id":0,"name":"Position","type":"VECTOR","color":-10238109,"default_value":null}],"buttons":[]},{"id":1,"name":"Paint","type":"PaintNode","x":533,"y":75,"color":-11959648,"inputs":[{"id":1,"node_id":1,"name":"In","type":"ACTION","color":-10238109,"default_value":null},{"id":1,"node_id":1,"name":"Position","type":"VECTOR","color":-10238109,"default_value":null}],"outputs":[{"id":1,"node_id":1,"name":"Out","type":"ACTION","color":-10238109,"default_value":null}],"buttons":[]}],"links":[{"id":0,"from_id":0,"from_socket":0,"to_id":1,"to_socket":0},{"id":1,"from_id":0,"from_socket":1,"to_id":1,"to_socket":1}]} \ No newline at end of file diff --git a/Bundled/default_material.json b/Bundled/default_material.json new file mode 100644 index 00000000..84449ce1 --- /dev/null +++ b/Bundled/default_material.json @@ -0,0 +1 @@ +{"nodes":[{"id":0,"name":"Material Output","type":"OUTPUT_MATERIAL_PBR","x":307,"y":192,"color":-5025958,"inputs":[{"id":0,"node_id":0,"name":"Base Color","type":"RGBA","color":-3684567,"default_value":[0.8,0.8,0.8,1]},{"id":0,"node_id":0,"name":"Opacity","type":"VALUE","color":-6184543,"default_value":1},{"id":0,"node_id":0,"name":"Occlusion","type":"VALUE","color":-6184543,"default_value":1},{"id":0,"node_id":0,"name":"Roughness","type":"VALUE","color":-6184543,"default_value":0.1},{"id":0,"node_id":0,"name":"Metallic","type":"VALUE","color":-6184543,"default_value":0},{"id":0,"node_id":0,"name":"Normal Map","type":"VECTOR","color":-10238109,"default_value":[0.5,0.5,1]}],"outputs":[],"buttons":[]},{"id":1,"name":"RGB","type":"RGB","x":72,"y":192,"color":-5025958,"inputs":[],"outputs":[{"id":1,"node_id":1,"name":"Color","type":"RGBA","color":-3684567,"default_value":[1,1,1,1]}],"buttons":[{"name":"default_value","type":"RGBA","output":0}]}],"links":[{"id":0,"from_id":1,"from_socket":0,"to_id":0,"to_socket":0}]} \ No newline at end of file diff --git a/Bundled/mat.png b/Bundled/mat.png new file mode 100644 index 00000000..ac95036e Binary files /dev/null and b/Bundled/mat.png differ diff --git a/Bundled/matbrush.png b/Bundled/matbrush.png new file mode 100644 index 00000000..4dc9ca7a Binary files /dev/null and b/Bundled/matbrush.png differ diff --git a/Env/cay.hdr b/Env/cay.hdr new file mode 100644 index 00000000..f85f4267 Binary files /dev/null and b/Env/cay.hdr differ diff --git a/Env/license.md b/Env/license.md new file mode 100644 index 00000000..c5167eb7 --- /dev/null +++ b/Env/license.md @@ -0,0 +1,2 @@ +CC0 environment maps by hdri haven: +https://hdrihaven.com/ diff --git a/Libraries/iron_format/README.md b/Libraries/iron_format/README.md new file mode 100644 index 00000000..a605f6af --- /dev/null +++ b/Libraries/iron_format/README.md @@ -0,0 +1,6 @@ +# iron_format + +TBD + +- https://github.com/HaxeFoundation/format +- https://github.com/azrafe7/hxPako diff --git a/Libraries/iron_format/Sources/iron/format/jpg/Data.hx b/Libraries/iron_format/Sources/iron/format/jpg/Data.hx new file mode 100644 index 00000000..3f73b909 --- /dev/null +++ b/Libraries/iron_format/Sources/iron/format/jpg/Data.hx @@ -0,0 +1,37 @@ +/* + * format - haXe File Formats + * + * JPG File Format + * Copyright (C) 2007-2009 Trevor McCauley, Baluta Cristian (hx port) & Robert Sköld (format conversion) + * + * Copyright (c) 2009, The haXe Project Contributors + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE HAXE PROJECT CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE HAXE PROJECT CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + */ +package iron.format.jpg; + +typedef Data = { + var width : Int; + var height : Int; + var quality : Float; + var pixels : haxe.io.Bytes; +} diff --git a/Libraries/iron_format/Sources/iron/format/jpg/Writer.hx b/Libraries/iron_format/Sources/iron/format/jpg/Writer.hx new file mode 100644 index 00000000..9f44bbbf --- /dev/null +++ b/Libraries/iron_format/Sources/iron/format/jpg/Writer.hx @@ -0,0 +1,651 @@ +/* + * format - haXe File Formats + * + * JPG File Format + * Copyright (C) 2007-2009 Thibault Imbert, AS3-to-haXe by Michel Oster + * + * Copyright (c) 2009, The haXe Project Contributors + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE HAXE PROJECT CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE HAXE PROJECT CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + */ +package iron.format.jpg; + +class Writer { + var ZigZag: Array; + + // Static table initialization + function initZigZag() { + ZigZag = [ + 0, 1, 5, 6,14,15,27,28, + 2, 4, 7,13,16,26,29,42, + 3, 8,12,17,25,30,41,43, + 9,11,18,24,31,40,44,53, + 10,19,23,32,39,45,52,54, + 20,22,33,38,46,51,55,60, + 21,34,37,47,50,56,59,61, + 35,36,48,49,57,58,62,63 + ]; + } + + var YTable: Array; // = new Array(64); + var UVTable: Array; // = new Array(64); + var fdtbl_Y: Array; // = new Array(64); + var fdtbl_UV: Array; // = new Array(64); + + function initQuantTables(sf: Int) { + var YQT: Array = [ + 16, 11, 10, 16, 24, 40, 51, 61, + 12, 12, 14, 19, 26, 58, 60, 55, + 14, 13, 16, 24, 40, 57, 69, 56, + 14, 17, 22, 29, 51, 87, 80, 62, + 18, 22, 37, 56, 68,109,103, 77, + 24, 35, 55, 64, 81,104,113, 92, + 49, 64, 78, 87,103,121,120,101, + 72, 92, 95, 98,112,100,103, 99 + ]; + for (i in 0...64) { + var t: Int = Math.floor( (YQT[i] * sf + 50) / 100 ); + if( t < 1 ) t = 1; + else if( t > 255 ) t = 255; + YTable[ ZigZag[i] ] = t; + } + var UVQT: Array = [ + 17, 18, 24, 47, 99, 99, 99, 99, + 18, 21, 26, 66, 99, 99, 99, 99, + 24, 26, 56, 99, 99, 99, 99, 99, + 47, 66, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99 + ]; + for( j in 0...64 ) { + var u: Int = Math.floor( (UVQT[j] * sf + 50) / 100 ); + if( u < 1 ) u = 1; + else if( u > 255 ) u = 255; + UVTable[ ZigZag[j] ] = u; + } + var aasf: Array = [ + 1.0, 1.387039845, 1.306562965, 1.175875602, + 1.0, 0.785694958, 0.541196100, 0.275899379 + ]; + var k = 0; + for( row in 0...8 ) { + for( col in 0...8 ) { + fdtbl_Y[k] = (1.0 / (YTable [ZigZag[k]] * aasf[row] * aasf[col] * 8.0)); + fdtbl_UV[k] = (1.0 / (UVTable[ZigZag[k]] * aasf[row] * aasf[col] * 8.0)); + k++; + } + } + } + + var std_dc_luminance_nrcodes: Array; + var std_dc_luminance_values: haxe.io.Bytes; + var std_ac_luminance_nrcodes: Array; + var std_ac_luminance_values: haxe.io.Bytes; + + function initLuminance() { + std_dc_luminance_nrcodes = [0,0,1,5,1,1,1,1,1,1,0,0,0,0,0,0,0]; + std_dc_luminance_values = strIntsToBytes( '0,1,2,3,4,5,6,7,8,9,10,11' ); + std_ac_luminance_nrcodes = [0,0,2,1,3,3,2,4,3,5,5,4,4,0,0,1,0x7d]; + std_ac_luminance_values = strIntsToBytes( + '0x01,0x02,0x03,0x00,0x04,0x11,0x05,0x12,' + + '0x21,0x31,0x41,0x06,0x13,0x51,0x61,0x07,' + + '0x22,0x71,0x14,0x32,0x81,0x91,0xa1,0x08,' + + '0x23,0x42,0xb1,0xc1,0x15,0x52,0xd1,0xf0,' + + '0x24,0x33,0x62,0x72,0x82,0x09,0x0a,0x16,' + + '0x17,0x18,0x19,0x1a,0x25,0x26,0x27,0x28,' + + '0x29,0x2a,0x34,0x35,0x36,0x37,0x38,0x39,' + + '0x3a,0x43,0x44,0x45,0x46,0x47,0x48,0x49,' + + '0x4a,0x53,0x54,0x55,0x56,0x57,0x58,0x59,' + + '0x5a,0x63,0x64,0x65,0x66,0x67,0x68,0x69,' + + '0x6a,0x73,0x74,0x75,0x76,0x77,0x78,0x79,' + + '0x7a,0x83,0x84,0x85,0x86,0x87,0x88,0x89,' + + '0x8a,0x92,0x93,0x94,0x95,0x96,0x97,0x98,' + + '0x99,0x9a,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,' + + '0xa8,0xa9,0xaa,0xb2,0xb3,0xb4,0xb5,0xb6,' + + '0xb7,0xb8,0xb9,0xba,0xc2,0xc3,0xc4,0xc5,' + + '0xc6,0xc7,0xc8,0xc9,0xca,0xd2,0xd3,0xd4,' + + '0xd5,0xd6,0xd7,0xd8,0xd9,0xda,0xe1,0xe2,' + + '0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea,' + + '0xf1,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,' + + '0xf9,0xfa' + ); + } + + function strIntsToBytes( s: String ) { + var len = s.length; + var b = new haxe.io.BytesBuffer(); + var val = 0; + var i = 0; + for( j in 0...len ) { + if( s.charAt( j ) == ',' ) { + val = Std.parseInt( s.substr(i, j - i) ); + b.addByte( val ); + i = j + 1; + } + } + if( i < len ) { + val = Std.parseInt( s.substr(i) ); + b.addByte( val ); + } + return b.getBytes(); + } + + var std_dc_chrominance_nrcodes: Array; + var std_dc_chrominance_values: haxe.io.Bytes; + var std_ac_chrominance_nrcodes: Array; + var std_ac_chrominance_values: haxe.io.Bytes; + + function initChrominance() { + std_dc_chrominance_nrcodes = [0,0,3,1,1,1,1,1,1,1,1,1,0,0,0,0,0]; + std_dc_chrominance_values = strIntsToBytes( '0,1,2,3,4,5,6,7,8,9,10,11' ); + std_ac_chrominance_nrcodes = [0,0,2,1,2,4,4,3,4,7,5,4,4,0,1,2,0x77]; + std_ac_chrominance_values = strIntsToBytes( + '0x00,0x01,0x02,0x03,0x11,0x04,0x05,0x21,' + + '0x31,0x06,0x12,0x41,0x51,0x07,0x61,0x71,' + + '0x13,0x22,0x32,0x81,0x08,0x14,0x42,0x91,' + + '0xa1,0xb1,0xc1,0x09,0x23,0x33,0x52,0xf0,' + + '0x15,0x62,0x72,0xd1,0x0a,0x16,0x24,0x34,' + + '0xe1,0x25,0xf1,0x17,0x18,0x19,0x1a,0x26,' + + '0x27,0x28,0x29,0x2a,0x35,0x36,0x37,0x38,' + + '0x39,0x3a,0x43,0x44,0x45,0x46,0x47,0x48,' + + '0x49,0x4a,0x53,0x54,0x55,0x56,0x57,0x58,' + + '0x59,0x5a,0x63,0x64,0x65,0x66,0x67,0x68,' + + '0x69,0x6a,0x73,0x74,0x75,0x76,0x77,0x78,' + + '0x79,0x7a,0x82,0x83,0x84,0x85,0x86,0x87,' + + '0x88,0x89,0x8a,0x92,0x93,0x94,0x95,0x96,' + + '0x97,0x98,0x99,0x9a,0xa2,0xa3,0xa4,0xa5,' + + '0xa6,0xa7,0xa8,0xa9,0xaa,0xb2,0xb3,0xb4,' + + '0xb5,0xb6,0xb7,0xb8,0xb9,0xba,0xc2,0xc3,' + + '0xc4,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xd2,' + + '0xd3,0xd4,0xd5,0xd6,0xd7,0xd8,0xd9,0xda,' + + '0xe2,0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,' + + '0xea,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,' + + '0xf9,0xfa' + ); + } + + var YDC_HT: Map; + var UVDC_HT: Map; + var YAC_HT: Map; + var UVAC_HT: Map; + + function initHuffmanTbl() { + YDC_HT = computeHuffmanTbl(std_dc_luminance_nrcodes, std_dc_luminance_values); + UVDC_HT = computeHuffmanTbl(std_dc_chrominance_nrcodes, std_dc_chrominance_values); + YAC_HT = computeHuffmanTbl(std_ac_luminance_nrcodes, std_ac_luminance_values); + UVAC_HT = computeHuffmanTbl(std_ac_chrominance_nrcodes, std_ac_chrominance_values); + } + + function computeHuffmanTbl(nrcodes: Array, std_table: haxe.io.Bytes): Map { + var codevalue = 0; + var pos_in_table = 0; + var HT: Map = new Map(); + for( k in 1...17 ) { + var end = nrcodes[k]; + for( j in 0...end ) { + var idx: Int = std_table.get( pos_in_table ); + HT.set( idx, new BitString( k, codevalue ) ); + pos_in_table++; + codevalue++; + } + codevalue *= 2; + } + return HT; + } + + var bitcode: Map; + var category: Map; + + function initCategoryNumber() { + var nrlower = 1; + var nrupper = 2; + var idx: Int; + for (cat in 1...16) { + //Positive numbers + for( nr in nrlower...nrupper ) { + idx = 32767 + nr; + category.set( idx, cat ); + bitcode.set( idx, new BitString( cat, nr ) ); + } + //Negative numbers + var nrneg: Int = -(nrupper - 1); + while( nrneg <= -nrlower ) { + idx = 32767 + nrneg; + category.set( idx, cat ); + bitcode.set( idx, new BitString( cat, nrupper - 1 + nrneg ) ); + nrneg++; + } + nrlower <<= 1; + nrupper <<= 1; + } + } + + // IO functions + var byteout: haxe.io.Output; + var bytenew: Int; + var bytepos: Int; + + function writeBits(bs: BitString) { + var value: Int = bs.val; + var posval: Int = bs.len - 1; + while( posval >= 0 ) { + //if (value & uint(1 << posval) ) { + if( (value & (1 << posval)) != 0 ) { //<- CORRECT ? + //bytenew |= uint(1 << bytepos); + bytenew |= (1 << bytepos); + } + posval--; + bytepos--; + if( bytepos < 0 ) { + if( bytenew == 0xFF ) { + b(0xFF); + b(0); + } + else { + b(bytenew); + } + bytepos = 7; + bytenew = 0; + } + } + } + + function writeWord( val: Int ) { + b( (val >> 8) & 0xFF ); + b( val & 0xFF ); + } + + // DCT & quantization core + + function fDCTQuant(data: Array, fdtbl: Array): Array { + /* Pass 1: process rows. */ + var dataOff = 0; + for (i in 0...8) { + var tmp0: Float = data[dataOff + 0] + data[dataOff + 7]; + var tmp7: Float = data[dataOff + 0] - data[dataOff + 7]; + var tmp1: Float = data[dataOff + 1] + data[dataOff + 6]; + var tmp6: Float = data[dataOff + 1] - data[dataOff + 6]; + var tmp2: Float = data[dataOff + 2] + data[dataOff + 5]; + var tmp5: Float = data[dataOff + 2] - data[dataOff + 5]; + var tmp3: Float = data[dataOff + 3] + data[dataOff + 4]; + var tmp4: Float = data[dataOff + 3] - data[dataOff + 4]; + + /* Even part */ + var tmp10: Float = tmp0 + tmp3; /* phase 2 */ + var tmp13: Float = tmp0 - tmp3; + var tmp11: Float = tmp1 + tmp2; + var tmp12: Float = tmp1 - tmp2; + + data[dataOff + 0] = tmp10 + tmp11; /* phase 3 */ + data[dataOff + 4] = tmp10 - tmp11; + + var z1: Float = (tmp12 + tmp13) * 0.707106781; /* c4 */ + data[dataOff + 2] = tmp13 + z1; /* phase 5 */ + data[dataOff + 6] = tmp13 - z1; + + /* Odd part */ + tmp10 = tmp4 + tmp5; /* phase 2 */ + tmp11 = tmp5 + tmp6; + tmp12 = tmp6 + tmp7; + + /* The rotator is modified from fig 4-8 to avoid extra negations. */ + var z5: Float = (tmp10 - tmp12) * 0.382683433; /* c6 */ + var z2: Float = 0.541196100 * tmp10 + z5; /* c2-c6 */ + var z4: Float = 1.306562965 * tmp12 + z5; /* c2+c6 */ + var z3: Float = tmp11 * 0.707106781; /* c4 */ + + var z11: Float = tmp7 + z3; /* phase 5 */ + var z13: Float = tmp7 - z3; + + data[dataOff + 5] = z13 + z2; /* phase 6 */ + data[dataOff + 3] = z13 - z2; + data[dataOff + 1] = z11 + z4; + data[dataOff + 7] = z11 - z4; + + dataOff += 8; /* advance pointer to next row */ + } + + /* Pass 2: process columns. */ + dataOff = 0; + for (j in 0...8) { + var tmp0p2: Float = data[dataOff+ 0] + data[dataOff+56]; + var tmp7p2: Float = data[dataOff+ 0] - data[dataOff+56]; + var tmp1p2: Float = data[dataOff+ 8] + data[dataOff+48]; + var tmp6p2: Float = data[dataOff+ 8] - data[dataOff+48]; + var tmp2p2: Float = data[dataOff+16] + data[dataOff+40]; + var tmp5p2: Float = data[dataOff+16] - data[dataOff+40]; + var tmp3p2: Float = data[dataOff+24] + data[dataOff+32]; + var tmp4p2: Float = data[dataOff+24] - data[dataOff+32]; + + /* Even part */ + var tmp10p2: Float = tmp0p2 + tmp3p2; /* phase 2 */ + var tmp13p2: Float = tmp0p2 - tmp3p2; + var tmp11p2: Float = tmp1p2 + tmp2p2; + var tmp12p2: Float = tmp1p2 - tmp2p2; + + data[dataOff+ 0] = tmp10p2 + tmp11p2; /* phase 3 */ + data[dataOff+32] = tmp10p2 - tmp11p2; + + var z1p2: Float = (tmp12p2 + tmp13p2) * 0.707106781; /* c4 */ + data[dataOff+16] = tmp13p2 + z1p2; /* phase 5 */ + data[dataOff+48] = tmp13p2 - z1p2; + + /* Odd part */ + tmp10p2 = tmp4p2 + tmp5p2; /* phase 2 */ + tmp11p2 = tmp5p2 + tmp6p2; + tmp12p2 = tmp6p2 + tmp7p2; + + /* The rotator is modified from fig 4-8 to avoid extra negations. */ + var z5p2: Float = (tmp10p2 - tmp12p2) * 0.382683433; /* c6 */ + var z2p2: Float = 0.541196100 * tmp10p2 + z5p2; /* c2-c6 */ + var z4p2: Float = 1.306562965 * tmp12p2 + z5p2; /* c2+c6 */ + var z3p2: Float= tmp11p2 * 0.707106781; /* c4 */ + + var z11p2: Float = tmp7p2 + z3p2; /* phase 5 */ + var z13p2: Float = tmp7p2 - z3p2; + + data[dataOff+40] = z13p2 + z2p2; /* phase 6 */ + data[dataOff+24] = z13p2 - z2p2; + data[dataOff+ 8] = z11p2 + z4p2; + data[dataOff+56] = z11p2 - z4p2; + + dataOff++; /* advance pointer to next column */ + } + + // Quantize/descale the coefficients + for (k in 0...64) { + // Apply the quantization and scaling factor & Round to nearest integer + data[k] = Math.round(data[k] * fdtbl[k]); + } + return data; + } + + // Chunk writing + + inline function b(v) { + byteout.writeByte(v); + } + + function writeAPP0() { + b(0xFF); b(0xE0); //<- marker 0xFFE0 + b(0); b(16); //<- length + b("J".code); // J + b("F".code); + b("I".code); + b("F".code); + b(0); + b(1); // versionhi + b(1); // versionlo + b(0); // xyunits + b(0); b(1); // xdensity + b(0); b(1); // ydensity + b(0); // thumbnwidth + b(0); // thumbnheight + } + function writeDQT() { + b(0xFF); b(0xDB); //<- marker 0xFFDB + b(0); b(132); //<- length + b(0); + for( j in 0...64 ) + b(YTable[j]); + b(1); + for( j in 0...64 ) + b(UVTable[j]); + } + function writeSOF0(width: Int, height: Int) { + b(0xFF); b(0xC0); //<- marker 0xFFC0 + b(0); b(17); //<- length, truecolor YUV JPG + b(8); // precision + b( (height>>8) & 0xFF ); + b( height & 0xFF ); + b( (width>>8) & 0xFF ); + b( width & 0xFF ); + b(3); // nrofcomponents + b(1); // IdY + b(0x11); // HVY + b(0); // QTY + b(2); // IdU + b(0x11); // HVU + b(1); // QTU + b(3); // IdV + b(0x11); // HVV + b(1); // QTV + } + + function writeDHT() { + b(0xFF); b(0xC4); //<- marker 0xFFC4 + b(0x01); b(0xA2); //<- length + b(0); // HTYDCinfo + for( j in 1...17 ) + b(std_dc_luminance_nrcodes[j]); + byteout.write(std_dc_luminance_values); + + b(0x10); // HTYACinfo + for( j in 1...17 ) + b(std_ac_luminance_nrcodes[j]); + byteout.write(std_ac_luminance_values); + + b(1); // HTUDCinfo + for( j in 1...17 ) + b(std_dc_chrominance_nrcodes[j]); + byteout.write(std_dc_chrominance_values); + + b(0x11); // HTUACinfo + for( j in 1...17 ) + b(std_ac_chrominance_nrcodes[j]); + byteout.write(std_ac_chrominance_values); + } + + function writeSOS() { + b(0xFF); b(0xDA); //<- marker 0xFFDA + b(0); b(12); //<- length + b(3); // nrofcomponents + b(1); // IdY + b(0); // HTY + b(2); // IdU + b(0x11); // HTU + b(3); // IdV + b(0x11); // HTV + b(0); // Ss + b(0x3F); // Se + b(0); // Bf + } + + // Core processing + var DU: Array; //<- initialized in function new JPEGEncoder() + + function processDU(CDU: Array, fdtbl: Array, DC: Float, HTDC: Map, HTAC: Map): Float { + var EOB: BitString = HTAC.get( 0x00 ); + var M16zeroes: BitString = HTAC.get( 0xF0 ); + + var DU_DCT: Array = fDCTQuant(CDU, fdtbl); + //ZigZag reorder + for (i in 0...64) { + DU[ ZigZag[i] ] = DU_DCT[i]; + } + var idx: Int; + var Diff = Std.int( DU[0] - DC ); + DC = DU[0]; + //Encode DC + if( Diff == 0 ) { + writeBits( HTDC.get(0) ); // Diff might be 0 + } else { + idx = 32767 + Diff; + writeBits(HTDC.get( category.get( idx ) )); + writeBits( bitcode.get( idx ) ); + } + + //Encode ACs + var end0pos = 63; + //for (; (end0pos>0)&&(DU[end0pos]==0); end0pos--) { }; + while( (end0pos > 0) && ( DU[end0pos] == 0.0 ) ) end0pos--; + + //end0pos = first element in reverse order !=0 + if ( end0pos == 0 ) { + writeBits(EOB); + return DC; + } + var i = 1; + while ( i <= end0pos ) { + var startpos = i; + //for (; (DU[i]==0) && (i<=end0pos); i++) { }; <- it's a 'while' loop + while( ( DU[i] == 0.0 ) && ( i <= end0pos ) ) i++; + + var nrzeroes: Int = i - startpos; + if ( nrzeroes >= 16 ) { + //for (var nrmarker: Int=1; nrmarker <= nrzeroes/16; nrmarker++) { + for( nrmarker in 0...(nrzeroes >> 4) ) writeBits(M16zeroes); + nrzeroes &= 0xF; + } + idx = 32767 + Std.int( DU[i] ); //<- line added + writeBits( HTAC.get( nrzeroes * 16 + category.get( idx ) ) ); + writeBits( bitcode.get( idx ) ); + i++; + } + if( end0pos != 63 ) writeBits(EOB); + return DC; + } + + var YDU: Array; + var UDU: Array; + var VDU: Array; + + function RGB2YUV(img: haxe.io.Bytes, width : Int, xpos: Int, ypos: Int) { + var pos = 0; + for( y in 0...8 ) { + var offset = ((y + ypos) * width + xpos) << 2; + for( x in 0...8 ) { + offset++; // skip alpha + var R = img.get(offset++); + var G = img.get(offset++); + var B = img.get(offset++); + YDU[pos] = ((( 0.29900) * R + ( 0.58700) * G + ( 0.11400) * B)) -128; + UDU[pos] = (((-0.16874) * R + (-0.33126) * G + ( 0.50000) * B)); + VDU[pos] = ((( 0.50000) * R + (-0.41869) * G + (-0.08131) * B)); + pos++; + } + } + } + + public function new( out : haxe.io.Output ) { + //begin : lines added to initialize variables + YTable = new Array(); + UVTable = new Array(); + fdtbl_Y = new Array(); + fdtbl_UV = new Array(); + for (i in 0...64) { + YTable.push(0); UVTable.push(0); + fdtbl_Y.push(0.0); fdtbl_UV.push(0.0); + } + + bitcode = new Map(); //<- 65535 elements + category = new Map(); //<- 65535 elements + byteout = out; + bytenew = 0; + bytepos = 7; + + YDC_HT = new Map(); + UVDC_HT = new Map(); + YAC_HT = new Map(); + UVAC_HT = new Map(); + + YDU = new Array(); //<- 64 elements + UDU = new Array(); + VDU = new Array(); + DU = new Array(); + for (i in 0...64) { + YDU.push(0.0); UDU.push(0.0); VDU.push(0.0); DU.push(0.0); + } + initZigZag(); + initLuminance(); + initChrominance(); + //end : lines added to initialize variables + + // Create tables + initHuffmanTbl(); + initCategoryNumber(); + } + + public function write( image : Data ) { + // init quality table + var quality = image.quality; + if( quality <= 0 ) quality = 1; + if( quality > 100 ) quality = 100; + var sf = + if( quality < 50 ) Std.int( 5000 / quality ) + else Std.int( 200 - quality * 2 ); + initQuantTables(sf); + + // Initialize bit writer + bytenew = 0; + bytepos = 7; + + var width = image.width; + var height = image.height; + // Add JPEG headers + writeWord(0xFFD8); // SOI + writeAPP0(); + writeDQT(); + writeSOF0( width, height ); + writeDHT(); + writeSOS(); + + // Encode 8x8 macroblocks + var DCY = 0.0; + var DCU = 0.0; + var DCV = 0.0; + bytenew = 0; + bytepos = 7; + var ypos = 0; + while( ypos < height ) { + var xpos = 0; + while( xpos < width ) { + RGB2YUV(image.pixels, width, xpos, ypos); + DCY = processDU(YDU, fdtbl_Y, DCY, YDC_HT, YAC_HT); + DCU = processDU(UDU, fdtbl_UV, DCU, UVDC_HT, UVAC_HT); + DCV = processDU(VDU, fdtbl_UV, DCV, UVDC_HT, UVAC_HT); + xpos += 8; + } + ypos += 8; + } + + // Do the bit alignment of the EOI marker + if( bytepos >= 0 ) { + var fillbits = new BitString( bytepos + 1, ( 1 << (bytepos + 1) ) - 1 ); + writeBits(fillbits); + } + + writeWord(0xFFD9); //EOI + } +} + +private class BitString { + public var len: Int; + public var val: Int; + + public function new( l: Int, v: Int ) { + len = l; + val = v; + } +} diff --git a/Libraries/iron_format/Sources/iron/format/obj/Loader.hx b/Libraries/iron_format/Sources/iron/format/obj/Loader.hx new file mode 100644 index 00000000..57fcd7c4 --- /dev/null +++ b/Libraries/iron_format/Sources/iron/format/obj/Loader.hx @@ -0,0 +1,165 @@ +package iron.format.obj; + +class Loader { + + public var indexedVertices:Array; + public var indexedUVs:Array; + public var indexedNormals:Array; + public var index:Int; + + // public var data:Array; + public var indices:Array; + + public function new(objData:String) { + + var vertices:Array = []; + var uvs:Array = []; + var normals:Array = []; + + var vertexIndices:Array = []; + var uvIndices:Array = []; + var normalIndices:Array = []; + + var tempVertices:Array> = []; + var tempUVs:Array> = []; + var tempNormals:Array> = []; + + var lines:Array = objData.split("\n"); + + for (i in 0...lines.length) { + var words:Array = lines[i].split(" "); + + if (words[0] == "v") { + var vector:Array = []; + vector.push(Std.parseFloat(words[1])); + vector.push(Std.parseFloat(words[2])); + vector.push(Std.parseFloat(words[3])); + tempVertices.push(vector); + } + else if (words[0] == "vt") { + var vector:Array = []; + vector.push(Std.parseFloat(words[1])); + vector.push(Std.parseFloat(words[2])); + tempUVs.push(vector); + } + else if (words[0] == "vn") { + var vector:Array = []; + vector.push(Std.parseFloat(words[1])); + vector.push(Std.parseFloat(words[2])); + vector.push(Std.parseFloat(words[3])); + tempNormals.push(vector); + } + else if (words[0] == "f") { + var sec1:Array = words[1].split("/"); + var sec2:Array = words[2].split("/"); + var sec3:Array = words[3].split("/"); + + vertexIndices.push(Std.int(Std.parseFloat(sec1[0]))); + vertexIndices.push(Std.int(Std.parseFloat(sec2[0]))); + vertexIndices.push(Std.int(Std.parseFloat(sec3[0]))); + + uvIndices.push(Std.int(Std.parseFloat(sec1[1]))); + uvIndices.push(Std.int(Std.parseFloat(sec2[1]))); + uvIndices.push(Std.int(Std.parseFloat(sec3[1]))); + + normalIndices.push(Std.int(Std.parseFloat(sec1[2]))); + normalIndices.push(Std.int(Std.parseFloat(sec2[2]))); + normalIndices.push(Std.int(Std.parseFloat(sec3[2]))); + } + } + + for (i in 0...vertexIndices.length) { + var vertex:Array = tempVertices[vertexIndices[i] - 1]; + var uv:Array = tempUVs[uvIndices[i] - 1]; + var normal:Array = tempNormals[normalIndices[i] - 1]; + + vertices.push(vertex[0]); + vertices.push(vertex[1]); + vertices.push(vertex[2]); + uvs.push(uv[0]); + uvs.push(uv[1]); + normals.push(normal[0]); + normals.push(normal[1]); + normals.push(normal[2]); + } + + build(vertices, uvs, normals); + + // data = []; + // for (i in 0...Std.int(vertices.length / 3)) { + // data.push(indexedVertices[i * 3]); + // data.push(indexedVertices[i * 3 + 1]); + // data.push(indexedVertices[i * 3 + 2]); + // data.push(indexedUVs[i * 2]); + // data.push(1-indexedUVs[i * 2 + 1]); + // data.push(indexedNormals[i * 3]); + // data.push(indexedNormals[i * 3 + 1]); + // data.push(indexedNormals[i * 3 + 2]); + // } + } + + function build(vertices:Array, uvs:Array, normals:Array) { + indexedVertices = []; + indexedUVs = []; + indexedNormals = []; + indices = []; + + // For each input vertex + for (i in 0...Std.int(vertices.length / 3)) { + + // Try to find a similar vertex in out_XXXX + var found:Bool = getSimilarVertexIndex( + vertices[i * 3], vertices[i * 3 + 1], vertices[i * 3 + 2], + uvs[i * 2], uvs[i * 2 + 1], + normals[i * 3], normals[i * 3 + 1], normals[i * 3 + 2]); + + if (found) { // A similar vertex is already in the VBO, use it instead ! + indices.push(index); + } + else { // If not, it needs to be added in the output data. + indexedVertices.push(vertices[i * 3]); + indexedVertices.push(vertices[i * 3 + 1]); + indexedVertices.push(vertices[i * 3 + 2]); + indexedUVs.push(uvs[i * 2 ]); + indexedUVs.push(1-uvs[i * 2 + 1]); + indexedNormals.push(normals[i * 3]); + indexedNormals.push(normals[i * 3 + 1]); + indexedNormals.push(normals[i * 3 + 2]); + indices.push(Std.int(indexedVertices.length / 3) - 1); + } + } + } + + // Returns true if v1 can be considered equal to v2 + function isNear(v1:Float, v2:Float):Bool { + return Math.abs(v1 - v2) < 0.001; + } + + // Searches through all already-exported vertices for a similar one. + // Similar = same position + same UVs + same normal + function getSimilarVertexIndex( + vertexX:Float, vertexY:Float, vertexZ:Float, + uvX:Float, uvY:Float, + normalX:Float, normalY:Float, normalZ:Float + ):Bool { + // Lame linear search + for (i in 0...Std.int(indexedVertices.length / 3)) { + if ( + isNear(vertexX, indexedVertices[i * 3]) && + isNear(vertexY, indexedVertices[i * 3 + 1]) && + isNear(vertexZ, indexedVertices[i * 3 + 2]) && + isNear(uvX , indexedUVs [i * 2]) && + isNear(uvY , indexedUVs [i * 2 + 1]) && + isNear(normalX, indexedNormals [i * 3]) && + isNear(normalY, indexedNormals [i * 3 + 1]) && + isNear(normalZ, indexedNormals [i * 3 + 2]) + ) { + index = i; + return true; + } + } + // No other vertex could be used instead. + // Looks like we'll have to add it to the VBO. + return false; + } +} diff --git a/Libraries/iron_format/Sources/iron/format/pako/Deflate.hx b/Libraries/iron_format/Sources/iron/format/pako/Deflate.hx new file mode 100644 index 00000000..bd76121b --- /dev/null +++ b/Libraries/iron_format/Sources/iron/format/pako/Deflate.hx @@ -0,0 +1,365 @@ +package iron.format.pako; + +import haxe.io.UInt8Array; +import haxe.zip.FlushMode; +import iron.format.pako.Deflate.DeflateOptions; +import iron.format.pako.zlib.Deflate as ZlibDeflate; +import iron.format.pako.utils.Common; +import iron.format.pako.zlib.Messages; +import iron.format.pako.zlib.ZStream; +import iron.format.pako.zlib.Constants; +import iron.format.pako.zlib.Constants.CompressionLevel; +import iron.format.pako.zlib.GZHeader; + + +typedef DeflateOptions = { + @:optional var level:Int; + @:optional var method:Int; + @:optional var chunkSize:Int; + @:optional var windowBits:Int; + @:optional var memLevel:Int; + @:optional var strategy:Int; + @:optional var raw:Bool; + @:optional var gzip:Bool; + @:optional var header:GZHeader; + @:optional var dictionary:UInt8Array; + //to: '' +} + +/* Public constants ==========================================================*/ +/* ===========================================================================*/ +/* +var Z_NO_FLUSH = 0; +var Z_FINISH = 4; + +var Z_OK = 0; +var Z_STREAM_END = 1; +var Z_SYNC_FLUSH = 2; + +var Z_DEFAULT_COMPRESSION = -1; + +var Z_DEFAULT_STRATEGY = 0; + +var Z_DEFLATED = 8; + +/* ===========================================================================*/ + + +/** + * class Deflate + * + * Generic JS-style wrapper for zlib calls. If you don't need + * streaming behaviour - use more simple functions: [[deflate]], + * [[deflateRaw]] and [[gzip]]. + **/ + +/* internal + * Deflate.chunks -> Array + * + * Chunks of output data, if [[Deflate#onData]] not overriden. + **/ + +/** + * Deflate.result -> Uint8Array|Array + * + * Compressed result, generated by default [[Deflate#onData]] + * and [[Deflate#onEnd]] handlers. Filled after you push last chunk + * (call [[Deflate#push]] with `Z_FINISH` / `true` param) or if you + * push a chunk with explicit flush (call [[Deflate#push]] with + * `Z_SYNC_FLUSH` param). + **/ + +/** + * Deflate.err -> Number + * + * Error code after deflate finished. 0 (Z_OK) on success. + * You will not need it in real life, because deflate errors + * are possible only on wrong options or bad `onData` / `onEnd` + * custom handlers. + **/ + +/** + * Deflate.msg -> String + * + * Error message, if [[Deflate.err]] != 0 + **/ + + +/** + * new Deflate(options) + * - options (Object): zlib deflate options. + * + * Creates new deflator instance with specified params. Throws exception + * on bad params. Supported options: + * + * - `level` + * - `windowBits` + * - `memLevel` + * - `strategy` + * - `dictionary` + * + * [http://zlib.net/manual.html#Advanced](http://zlib.net/manual.html#Advanced) + * for more information on these. + * + * Additional options, for internal needs: + * + * - `chunkSize` - size of generated data chunks (16K by default) + * - `raw` (Boolean) - do raw deflate + * - `gzip` (Boolean) - create gzip wrapper + * - `to` (String) - if equal to 'string', then result will be "binary string" + * (each char code [0..255]) + * - `header` (Object) - custom header for gzip + * - `text` (Boolean) - true if compressed data believed to be text + * - `time` (Number) - modification time, unix timestamp + * - `os` (Number) - operation system code + * - `extra` (Array) - array of bytes with extra data (max 65536) + * - `name` (String) - file name (binary string) + * - `comment` (String) - comment (binary string) + * - `hcrc` (Boolean) - true if header crc should be added + * + * ##### Example: + * + * ```javascript + * var pako = require('pako') + * , chunk1 = Uint8Array([1,2,3,4,5,6,7,8,9]) + * , chunk2 = Uint8Array([10,11,12,13,14,15,16,17,18,19]); + * + * var deflate = new iron.format.pako.Deflate({ level: 3}); + * + * deflate.push(chunk1, false); + * deflate.push(chunk2, true); // true -> last chunk + * + * if (deflate.err) { throw new Error(deflate.err); } + * + * console.log(deflate.result); + * ``` + **/ +class Deflate +{ + static var DEFAULT_OPTIONS:DeflateOptions = { + level: CompressionLevel.Z_DEFAULT_COMPRESSION, + method: Method.Z_DEFLATED, + chunkSize: 16384, + windowBits: 15, + memLevel: 8, + strategy: Strategy.Z_DEFAULT_STRATEGY, + raw: false, + gzip: false, + header: null, + dictionary: null, + //to: '' + } + + public var options:DeflateOptions = null; + + public var err:Int = ErrorStatus.Z_OK; // error code, if happens (0 = Z_OK) + public var msg:String = ''; // error message + public var ended:Bool = false; // used to avoid multiple onEnd() calls + public var chunks:Array = []; // chunks of compressed data + + public var strm:ZStream = new ZStream(); + + public var result:UInt8Array = null; + + public function new(options:DeflateOptions = null) { + + this.options = { }; + this.options.level = (options != null && options.level != null) ? options.level : DEFAULT_OPTIONS.level; + this.options.method = (options != null && options.method != null) ? options.method : DEFAULT_OPTIONS.method; + this.options.chunkSize = (options != null && options.chunkSize != null) ? options.chunkSize : DEFAULT_OPTIONS.chunkSize; + this.options.windowBits = (options != null && options.windowBits != null) ? options.windowBits : DEFAULT_OPTIONS.windowBits; + this.options.memLevel = (options != null && options.memLevel != null) ? options.memLevel : DEFAULT_OPTIONS.memLevel; + this.options.strategy = (options != null && options.strategy != null) ? options.strategy : DEFAULT_OPTIONS.strategy; + this.options.raw = (options != null && options.raw != null) ? options.raw : DEFAULT_OPTIONS.raw; + this.options.gzip = (options != null && options.gzip != null) ? options.gzip : DEFAULT_OPTIONS.gzip; + this.options.header = (options != null && options.header != null) ? options.header : DEFAULT_OPTIONS.header; + this.options.dictionary = (options != null && options.dictionary != null) ? options.dictionary : DEFAULT_OPTIONS.dictionary; + + //NOTE(hx): both raw and gzip are false by default? + if (this.options.raw && (this.options.windowBits > 0)) { + this.options.windowBits = -this.options.windowBits; + } else if (this.options.gzip && (this.options.windowBits > 0) && (this.options.windowBits < 16)) { + this.options.windowBits += 16; + } + + this.onData = _onData; + this.onEnd = _onEnd; + + strm.avail_out = 0; + + var status = ZlibDeflate.deflateInit2( + this.strm, + this.options.level, + this.options.method, + this.options.windowBits, + this.options.memLevel, + this.options.strategy + ); + + if (status != ErrorStatus.Z_OK) { + throw Messages.get(status); + } + + if (this.options.header != null) { + ZlibDeflate.deflateSetHeader(this.strm, this.options.header); + } + + //NOTE(hx): only supporting UInt8Array + if (this.options.dictionary != null) { + status = ZlibDeflate.deflateSetDictionary(this.strm, this.options.dictionary); + } + + if (status != ErrorStatus.Z_OK) { + throw Messages.get(status); + } + } + + /** + * Deflate#push(data[, mode]) -> Boolean + * - data (Uint8Array|Array|ArrayBuffer|String): input data. Strings will be + * converted to utf8 byte sequence. + * - mode (Number|Boolean): 0..6 for corresponding Z_NO_FLUSH..Z_TREE modes. + * See constants. Skipped or `false` means Z_NO_FLUSH, `true` meansh Z_FINISH. + * + * Sends input data to deflate pipe, generating [[Deflate#onData]] calls with + * new compressed chunks. Returns `true` on success. The last data block must have + * mode Z_FINISH (or `true`). That will flush internal pending buffers and call + * [[Deflate#onEnd]]. For interim explicit flushes (without ending the stream) you + * can use mode Z_SYNC_FLUSH, keeping the compression context. + * + * On fail call [[Deflate#onEnd]] with error code and return false. + * + * We strongly recommend to use `Uint8Array` on input for best speed (output + * array format is detected automatically). Also, don't skip last param and always + * use the same type in your code (boolean or number). That will improve JS speed. + * + * For regular `Array`-s make sure all elements are [0..255]. + * + * ##### Example + * + * ```javascript + * push(chunk, false); // push one of data chunks + * ... + * push(chunk, true); // push last chunk + * ``` + **/ + public function push(data:UInt8Array, mode:Dynamic = false) { + var strm = this.strm; + var chunkSize = this.options.chunkSize; + var status, _mode:Int; + + if (this.ended) { return false; } + + //NOTE(hx): search codebase for ~~ + //_mode = (mode == ~~mode) ? mode : ((mode == true) ? Z_FINISH : Z_NO_FLUSH); + if (Std.is(mode, Int)) _mode = mode; + else if (Std.is(mode, Bool)) _mode = mode ? Flush.Z_FINISH : Flush.Z_NO_FLUSH; + else throw "Invalid mode."; + + // Convert data if needed + //NOTE(hx): only supporting UInt8Array + /*if (typeof data === 'string') { + // If we need to compress text, change encoding to utf8. + strm.input = strings.string2buf(data); + } else if (toString.call(data) === '[object ArrayBuffer]') { + strm.input = new Uint8Array(data); + } else*/ { + strm.input = data; + } + + strm.next_in = 0; + strm.avail_in = strm.input.length; + + do { + if (strm.avail_out == 0) { + strm.output = new UInt8Array(chunkSize); + strm.next_out = 0; + strm.avail_out = chunkSize; + } + status = ZlibDeflate.deflate(strm, _mode); /* no bad return value */ + + if (status != ErrorStatus.Z_STREAM_END && status != ErrorStatus.Z_OK) { + this.onEnd(status); + this.ended = true; + return false; + } + if (strm.avail_out == 0 || (strm.avail_in == 0 && (_mode == Flush.Z_FINISH || _mode == Flush.Z_SYNC_FLUSH))) { + //NOTE(hx): only supporting UInt8Array + /*if (this.options.to === 'string') { + this.onData(strings.buf2binstring(utils.shrinkBuf(strm.output, strm.next_out))); + } else */ { + //NOTE(hx): cast ok? + this.onData(Common.shrinkBuf(strm.output, strm.next_out)); + } + } + } while ((strm.avail_in > 0 || strm.avail_out == 0) && status != ErrorStatus.Z_STREAM_END); + + // Finalize on the last chunk. + if (_mode == Flush.Z_FINISH) { + status = ZlibDeflate.deflateEnd(this.strm); + this.onEnd(status); + this.ended = true; + return status == ErrorStatus.Z_OK; + } + + // callback interim results if Z_SYNC_FLUSH. + if (_mode == Flush.Z_SYNC_FLUSH) { + this.onEnd(ErrorStatus.Z_OK); + strm.avail_out = 0; + return true; + } + + return true; + } + + + /** + * Deflate#onData(chunk) -> Void + * - chunk (Uint8Array|Array|String): ouput data. Type of array depends + * on js engine support. When string output requested, each chunk + * will be string. + * + * By default, stores data blocks in `chunks[]` property and glue + * those in `onEnd`. Override this handler, if you need another behaviour. + **/ + public var onData:UInt8Array->Void; + + function _onData(chunk:UInt8Array) { + this.chunks.push(chunk); + } + + + /** + * Deflate#onEnd(status) -> Void + * - status (Number): deflate status. 0 (Z_OK) on success, + * other if not. + * + * Called once after you tell deflate that the input stream is + * complete (Z_FINISH) or should be flushed (Z_SYNC_FLUSH) + * or if an error happened. By default - join collected chunks, + * free memory and fill `results` / `err` properties. + **/ + public var onEnd:Int->Void; + + function _onEnd(status:Int) { + // On success - join + if (status == ErrorStatus.Z_OK) { + //NOTE(hx): only supporting UInt8Array + /*if (this.options.to === 'string') { + this.result = this.chunks.join(''); + } else*/ { + this.result = Common.flattenChunks(this.chunks); + } + } + this.chunks = []; + this.err = status; + this.msg = this.strm.msg; + } +} + +/* +exports.Deflate = Deflate; +exports.deflate = deflate; +exports.deflateRaw = deflateRaw; +exports.gzip = gzip; +*/ \ No newline at end of file diff --git a/Libraries/iron_format/Sources/iron/format/pako/Inflate.hx b/Libraries/iron_format/Sources/iron/format/pako/Inflate.hx new file mode 100644 index 00000000..666697ce --- /dev/null +++ b/Libraries/iron_format/Sources/iron/format/pako/Inflate.hx @@ -0,0 +1,370 @@ +package iron.format.pako; + +import haxe.io.UInt8Array; +import iron.format.pako.Inflate.InflateOptions; +import iron.format.pako.zlib.Inflate as ZlibInflate; +import iron.format.pako.utils.Common; +import iron.format.pako.zlib.Messages; +import iron.format.pako.zlib.ZStream; +import iron.format.pako.zlib.Constants; +import iron.format.pako.zlib.Constants.CompressionLevel; +import iron.format.pako.zlib.GZHeader; + + +typedef InflateOptions = { + @:optional var chunkSize:Int; + @:optional var windowBits:Int; + @:optional var raw:Bool; + @:optional var dictionary:UInt8Array; + //to: '' +} + + +/** + * class Inflate + * + * Generic JS-style wrapper for zlib calls. If you don't need + * streaming behaviour - use more simple functions: [[inflate]] + * and [[inflateRaw]]. + **/ + +/* internal + * inflate.chunks -> Array + * + * Chunks of output data, if [[Inflate#onData]] not overriden. + **/ + +/** + * Inflate.result -> Uint8Array|Array|String + * + * Uncompressed result, generated by default [[Inflate#onData]] + * and [[Inflate#onEnd]] handlers. Filled after you push last chunk + * (call [[Inflate#push]] with `Z_FINISH` / `true` param) or if you + * push a chunk with explicit flush (call [[Inflate#push]] with + * `Z_SYNC_FLUSH` param). + **/ + +/** + * Inflate.err -> Number + * + * Error code after inflate finished. 0 (Z_OK) on success. + * Should be checked if broken data possible. + **/ + +/** + * Inflate.msg -> String + * + * Error message, if [[Inflate.err]] != 0 + **/ + + +/** + * new Inflate(options) + * - options (Object): zlib inflate options. + * + * Creates new inflator instance with specified params. Throws exception + * on bad params. Supported options: + * + * - `windowBits` + * - `dictionary` + * + * [http://zlib.net/manual.html#Advanced](http://zlib.net/manual.html#Advanced) + * for more information on these. + * + * Additional options, for internal needs: + * + * - `chunkSize` - size of generated data chunks (16K by default) + * - `raw` (Boolean) - do raw inflate + * - `to` (String) - if equal to 'string', then result will be converted + * from utf8 to utf16 (javascript) string. When string output requested, + * chunk length can differ from `chunkSize`, depending on content. + * + * By default, when no options set, autodetect deflate/gzip data format via + * wrapper header. + * + * ##### Example: + * + * ```javascript + * var pako = require('pako') + * , chunk1 = Uint8Array([1,2,3,4,5,6,7,8,9]) + * , chunk2 = Uint8Array([10,11,12,13,14,15,16,17,18,19]); + * + * var inflate = new iron.format.pako.Inflate({ level: 3}); + * + * inflate.push(chunk1, false); + * inflate.push(chunk2, true); // true -> last chunk + * + * if (inflate.err) { throw new Error(inflate.err); } + * + * console.log(inflate.result); + * ``` + **/ +class Inflate +{ + static var DEFAULT_OPTIONS:InflateOptions = { + chunkSize: 16384, + windowBits: 0, + raw: false, + dictionary: null, + //to: '' + } + + public var options:InflateOptions = null; + + public var err:Int = ErrorStatus.Z_OK; // error code, if happens (0 = Z_OK) + public var msg:String = ''; // error message + public var ended:Bool = false; // used to avoid multiple onEnd() calls + public var chunks:Array = []; // chunks of compressed data + + public var strm:ZStream = new ZStream(); + + public var header:GZHeader = new GZHeader(); + + public var result:UInt8Array = null; + + public function new(options:InflateOptions = null) { + + this.options = { }; + this.options.chunkSize = (options != null && options.chunkSize != null) ? options.chunkSize : DEFAULT_OPTIONS.chunkSize; + this.options.windowBits = (options != null && options.windowBits != null) ? options.windowBits : DEFAULT_OPTIONS.windowBits; + this.options.raw = (options != null && options.raw != null) ? options.raw : DEFAULT_OPTIONS.raw; + this.options.dictionary = (options != null && options.dictionary != null) ? options.dictionary : DEFAULT_OPTIONS.dictionary; + + // Force window size for `raw` data, if not set directly, + // because we have no header for autodetect. + if (this.options.raw && (this.options.windowBits >= 0) && (this.options.windowBits < 16)) { + this.options.windowBits = -this.options.windowBits; + if (this.options.windowBits == 0) { this.options.windowBits = -15; } + } + + // If `windowBits` not defined (and mode not raw) - set autodetect flag for gzip/deflate + if ((this.options.windowBits >= 0) && (this.options.windowBits < 16) && + (options == null || options.windowBits == null)) { + this.options.windowBits += 32; + } + + // Gzip header has no info about windows size, we can do autodetect only + // for deflate. So, if window size not set, force it to max when gzip possible + if ((this.options.windowBits > 15) && (this.options.windowBits < 48)) { + // bit 3 (16) -> gzipped data + // bit 4 (32) -> autodetect gzip/deflate + if ((this.options.windowBits & 15) == 0) { + this.options.windowBits |= 15; + } + } + + this.onData = _onData; + this.onEnd = _onEnd; + + this.strm.avail_out = 0; + + var status = ZlibInflate.inflateInit2( + this.strm, + this.options.windowBits + ); + + if (status != ErrorStatus.Z_OK) { + throw Messages.get(status); + } + + ZlibInflate.inflateGetHeader(this.strm, this.header); + } + + /** + * Inflate#push(data[, mode]) -> Boolean + * - data (Uint8Array|Array|ArrayBuffer|String): input data + * - mode (Number|Boolean): 0..6 for corresponding Z_NO_FLUSH..Z_TREE modes. + * See constants. Skipped or `false` means Z_NO_FLUSH, `true` meansh Z_FINISH. + * + * Sends input data to inflate pipe, generating [[Inflate#onData]] calls with + * new output chunks. Returns `true` on success. The last data block must have + * mode Z_FINISH (or `true`). That will flush internal pending buffers and call + * [[Inflate#onEnd]]. For interim explicit flushes (without ending the stream) you + * can use mode Z_SYNC_FLUSH, keeping the decompression context. + * + * On fail call [[Inflate#onEnd]] with error code and return false. + * + * We strongly recommend to use `Uint8Array` on input for best speed (output + * format is detected automatically). Also, don't skip last param and always + * use the same type in your code (boolean or number). That will improve JS speed. + * + * For regular `Array`-s make sure all elements are [0..255]. + * + * ##### Example + * + * ```javascript + * push(chunk, false); // push one of data chunks + * ... + * push(chunk, true); // push last chunk + * ``` + **/ + public function push(data:UInt8Array, mode:Dynamic = false) { + var strm = this.strm; + var chunkSize = this.options.chunkSize; + var dictionary = this.options.dictionary; + var status, _mode:Int; + var next_out_utf8, tail, utf8str; + + // Flag to properly process Z_BUF_ERROR on testing inflate call + // when we check that all output data was flushed. + var allowBufError = false; + + if (this.ended) { return false; } + + //NOTE(hx): search codebase for ~~ + //_mode = (mode == ~~mode) ? mode : ((mode == true) ? Z_FINISH : Z_NO_FLUSH); + if (Std.is(mode, Int)) _mode = mode; + else if (Std.is(mode, Bool)) _mode = mode ? Flush.Z_FINISH : Flush.Z_NO_FLUSH; + else throw "Invalid mode."; + + // Convert data if needed + //NOTE(hx): only supporting UInt8Array + /*if (typeof data === 'string') { + // Only binary strings can be decompressed on practice + strm.input = strings.binstring2buf(data); + } else if (toString.call(data) === '[object ArrayBuffer]') { + strm.input = new Uint8Array(data); + } else*/ { + strm.input = data; + } + + strm.next_in = 0; + strm.avail_in = strm.input.length; + + do { + if (strm.avail_out == 0) { + strm.output = new UInt8Array(chunkSize); + strm.next_out = 0; + strm.avail_out = chunkSize; + } + + status = ZlibInflate.inflate(strm, Flush.Z_NO_FLUSH); /* no bad return value */ + + //NOTE(hx): only supporting UInt8Array + if (status == ErrorStatus.Z_NEED_DICT && dictionary != null) { + status = ZlibInflate.inflateSetDictionary(this.strm, dictionary); + } + + if (status == ErrorStatus.Z_BUF_ERROR && allowBufError) { + status = ErrorStatus.Z_OK; + allowBufError = false; + } + + if (status != ErrorStatus.Z_STREAM_END && status != ErrorStatus.Z_OK) { + this.onEnd(status); + this.ended = true; + return false; + } + + if (strm.next_out != 0) { + if (strm.avail_out == 0 || status == ErrorStatus.Z_STREAM_END || (strm.avail_in == 0 && (_mode == Flush.Z_FINISH || _mode == Flush.Z_SYNC_FLUSH))) { + + //NOTE(hx): only supporting UInt8Array + /*if (this.options.to === 'string') { + + next_out_utf8 = strings.utf8border(strm.output, strm.next_out); + + tail = strm.next_out - next_out_utf8; + utf8str = strings.buf2string(strm.output, next_out_utf8); + + // move tail + strm.next_out = tail; + strm.avail_out = chunkSize - tail; + if (tail) { utils.arraySet(strm.output, strm.output, next_out_utf8, tail, 0); } + + this.onData(utf8str); + + } else*/ { + this.onData(Common.shrinkBuf(strm.output, strm.next_out)); + } + } + } + + // When no more input data, we should check that internal inflate buffers + // are flushed. The only way to do it when avail_out = 0 - run one more + // inflate pass. But if output data not exists, inflate return Z_BUF_ERROR. + // Here we set flag to process this error properly. + // + // NOTE. Deflate does not return error in this case and does not needs such + // logic. + if (strm.avail_in == 0 && strm.avail_out == 0) { + allowBufError = true; + } + + } while ((strm.avail_in > 0 || strm.avail_out == 0) && status != ErrorStatus.Z_STREAM_END); + + if (status == ErrorStatus.Z_STREAM_END) { + _mode = Flush.Z_FINISH; + } + + // Finalize on the last chunk. + if (_mode == Flush.Z_FINISH) { + status = ZlibInflate.inflateEnd(this.strm); + this.onEnd(status); + this.ended = true; + return status == ErrorStatus.Z_OK; + } + + // callback interim results if Z_SYNC_FLUSH. + if (_mode == Flush.Z_SYNC_FLUSH) { + this.onEnd(ErrorStatus.Z_OK); + strm.avail_out = 0; + return true; + } + + return true; + } + + + /** + * Inflate#onData(chunk) -> Void + * - chunk (Uint8Array|Array|String): ouput data. Type of array depends + * on js engine support. When string output requested, each chunk + * will be string. + * + * By default, stores data blocks in `chunks[]` property and glue + * those in `onEnd`. Override this handler, if you need another behaviour. + **/ + public var onData:UInt8Array->Void; + + function _onData(chunk:UInt8Array) { + this.chunks.push(chunk); + } + + + /** + * Inflate#onEnd(status) -> Void + * - status (Number): inflate status. 0 (Z_OK) on success, + * other if not. + * + * Called either after you tell inflate that the input stream is + * complete (Z_FINISH) or should be flushed (Z_SYNC_FLUSH) + * or if an error happened. By default - join collected chunks, + * free memory and fill `results` / `err` properties. + **/ + public var onEnd:Int->Void; + + function _onEnd(status:Int) { + // On success - join + if (status == ErrorStatus.Z_OK) { + //NOTE(hx): only supporting UInt8Array + /*if (this.options.to === 'string') { + // Glue & convert here, until we teach pako to send + // utf8 alligned strings to onData + this.result = this.chunks.join(''); + } else*/ { + this.result = Common.flattenChunks(this.chunks); + } + } + this.chunks = []; + this.err = status; + this.msg = this.strm.msg; + } +} + +/* +exports.Inflate = Inflate; +exports.inflate = inflate; +exports.inflateRaw = inflateRaw; +exports.ungzip = inflate; +*/ \ No newline at end of file diff --git a/Libraries/iron_format/Sources/iron/format/pako/Pako.hx b/Libraries/iron_format/Sources/iron/format/pako/Pako.hx new file mode 100644 index 00000000..54458ca7 --- /dev/null +++ b/Libraries/iron_format/Sources/iron/format/pako/Pako.hx @@ -0,0 +1,180 @@ +package iron.format.pako; + +import haxe.io.UInt8Array; +import haxe.zip.FlushMode; +import iron.format.pako.Deflate.DeflateOptions; +import iron.format.pako.Inflate.InflateOptions; +import iron.format.pako.zlib.Deflate as ZlibDeflate; +import iron.format.pako.utils.Common; +import iron.format.pako.zlib.Messages; +import iron.format.pako.zlib.ZStream; +import iron.format.pako.zlib.Constants; +import iron.format.pako.zlib.Constants.CompressionLevel; +import iron.format.pako.zlib.GZHeader; + + +class Pako +{ + /** Version of ported pako lib (https://github.com/nodeca/pako). */ + inline static public var VERSION:String = "1.0.4"; + + /** + * deflate(data[, options]) -> Uint8Array|Array|String + * - data (Uint8Array|Array|String): input data to compress. + * - options (Object): zlib deflate options. + * + * Compress `data` with deflate alrorythm and `options`. + * + * Supported options are: + * + * - level + * - windowBits + * - memLevel + * - strategy + * + * [http://zlib.net/manual.html#Advanced](http://zlib.net/manual.html#Advanced) + * for more information on these. + * + * Sugar (options): + * + * - `raw` (Boolean) - say that we work with raw stream, if you don't wish to specify + * negative windowBits implicitly. + * - `to` (String) - if equal to 'string', then result will be "binary string" + * (each char code [0..255]) + * + * ##### Example: + * + * ```javascript + * var pako = require('pako') + * , data = Uint8Array([1,2,3,4,5,6,7,8,9]); + * + * console.log(iron.format.pako.deflate(data)); + * ``` + **/ + static public function deflate(input:UInt8Array, ?options:DeflateOptions) { + var deflator = new Deflate(options); + + //NOTE(hx): change here if we accept mode as Bool too + deflator.push(input, Flush.Z_FINISH); + + // This should never happen, if you don't cheat with options :) + if (deflator.err != 0) { + throw (deflator.msg != '' ? deflator.msg : Messages.get(deflator.err)); + } + + return deflator.result; + } + + + /** + * deflateRaw(data[, options]) -> Uint8Array|Array|String + * - data (Uint8Array|Array|String): input data to compress. + * - options (Object): zlib deflate options. + * + * The same as [[deflate]], but creates raw data, without wrapper + * (header and adler32 crc). + **/ + static public function deflateRaw(input:UInt8Array, ?options:DeflateOptions) { + if (options == null) options = { }; + options.raw = true; + return deflate(input, options); + } + + + /** + * gzip(data[, options]) -> Uint8Array|Array|String + * - data (Uint8Array|Array|String): input data to compress. + * - options (Object): zlib deflate options. + * + * The same as [[deflate]], but create gzip wrapper instead of + * deflate one. + **/ + static public function gzip(input:UInt8Array, ?options:DeflateOptions) { + if (options == null) options = { }; + options.gzip = true; + return deflate(input, options); + } + /** + * inflate(data[, options]) -> Uint8Array|Array|String + * - data (Uint8Array|Array|String): input data to decompress. + * - options (Object): zlib inflate options. + * + * Decompress `data` with inflate/ungzip and `options`. Autodetect + * format via wrapper header by default. That's why we don't provide + * separate `ungzip` method. + * + * Supported options are: + * + * - windowBits + * + * [http://zlib.net/manual.html#Advanced](http://zlib.net/manual.html#Advanced) + * for more information. + * + * Sugar (options): + * + * - `raw` (Boolean) - say that we work with raw stream, if you don't wish to specify + * negative windowBits implicitly. + * - `to` (String) - if equal to 'string', then result will be converted + * from utf8 to utf16 (javascript) string. When string output requested, + * chunk length can differ from `chunkSize`, depending on content. + * + * + * ##### Example: + * + * ```javascript + * var pako = require('pako') + * , input = iron.format.pako.deflate([1,2,3,4,5,6,7,8,9]) + * , output; + * + * try { + * output = iron.format.pako.inflate(input); + * } catch (err) + * console.log(err); + * } + * ``` + **/ + static public function inflate(input:UInt8Array, ?options:InflateOptions) { + var inflator = new Inflate(options); + + inflator.push(input, true); + + // This should never happen, if you don't cheat with options :) + if (inflator.err != ErrorStatus.Z_OK) { + throw (inflator.msg != '' ? inflator.msg : Messages.get(inflator.err)); + } + + return inflator.result; + } + + + /** + * inflateRaw(data[, options]) -> Uint8Array|Array|String + * - data (Uint8Array|Array|String): input data to decompress. + * - options (Object): zlib inflate options. + * + * The same as [[inflate]], but creates raw data, without wrapper + * (header and adler32 crc). + **/ + static public function inflateRaw(input:UInt8Array, ?options:InflateOptions) { + if (options == null) options = { }; + options.raw = true; + return inflate(input, options); + } + + + /** + * ungzip(data[, options]) -> Uint8Array|Array|String + * - data (Uint8Array|Array|String): input data to decompress. + * - options (Object): zlib inflate options. + * + * Just shortcut to [[inflate]], because it autodetects format + * by header.content. Done for convenience. + **/ +} + +/* +exports.Deflate = Deflate; +exports.deflate = deflate; +exports.deflateRaw = deflateRaw; +exports.gzip = gzip; +*/ \ No newline at end of file diff --git a/Libraries/iron_format/Sources/iron/format/pako/utils/Common.hx b/Libraries/iron_format/Sources/iron/format/pako/utils/Common.hx new file mode 100644 index 00000000..7a4faa8a --- /dev/null +++ b/Libraries/iron_format/Sources/iron/format/pako/utils/Common.hx @@ -0,0 +1,56 @@ +package iron.format.pako.utils; + +import haxe.io.ArrayBufferView; +import haxe.io.UInt8Array; + +class Common +{ + //NOTE(hx): blit (reset pos to respective offsets?) + static inline public function arraySet(dest:ArrayBufferView, src:ArrayBufferView, src_offs:Int, len:Int, dest_offs:Int) { + dest.buffer.blit(dest.byteOffset + dest_offs, src.buffer, src.byteOffset + src_offs, len); + } + + //NOTE(hx): moved here from Trees and Deflate + static inline public function zero(buf:ArrayBufferView) { + var start = buf.byteOffset; + var len = buf.byteLength; + #if kha_webgl + //// TODO: Pako throws Uint8Array is not a constructor in Trees.__init__ + #else + buf.buffer.fill(start, len, 0); + #end + } + + //NOTE(hx): if ArrayBufferView.EMULATED it will be a copy + // reduce buffer size, avoiding mem copy + static inline public function shrinkBuf(buf:UInt8Array, size:Int) { + if (buf.length != size) { + buf = cast buf.subarray(0, size); + } + return buf; + } + + //NOTE(hx): blit + // Join array of chunks to single array. + static public function flattenChunks(chunks:Array) { + var i, l, len, pos, chunk:UInt8Array, result:UInt8Array; + + // calculate data length + len = 0; + l = chunks.length; + for (i in 0...l) { + len += chunks[i].length; + } + + // join chunks + result = new UInt8Array(len); + pos = 0; + for (i in 0...l) { + chunk = chunks[i]; + result.view.buffer.blit(pos, chunk.view.buffer, 0, chunk.length); + pos += chunk.length; + } + + return result; + } +} \ No newline at end of file diff --git a/Libraries/iron_format/Sources/iron/format/pako/zlib/Adler32.hx b/Libraries/iron_format/Sources/iron/format/pako/zlib/Adler32.hx new file mode 100644 index 00000000..2e914dec --- /dev/null +++ b/Libraries/iron_format/Sources/iron/format/pako/zlib/Adler32.hx @@ -0,0 +1,34 @@ +package iron.format.pako.zlib; + +import haxe.io.UInt8Array; + +// Note: adler32 takes 12% for level 0 and 2% for level 6. +// It doesn't worth to make additional optimizationa as in original. +// Small size is preferable. + +class Adler32 +{ + static public function adler32(adler:Int, buf:UInt8Array, len:Int, pos:Int):Int { + var s1 = (adler & 0xffff) |0, + s2 = ((adler >>> 16) & 0xffff) |0, + n = 0; + + while (len != 0) { + // Set limit ~ twice less than 5552, to keep + // s2 in 31-bits, because we force signed ints. + // in other case %= will fail. + n = len > 2000 ? 2000 : len; + len -= n; + + do { + s1 = (s1 + buf[pos++]) |0; + s2 = (s2 + s1) |0; + } while ((--n) != 0); + + s1 %= 65521; + s2 %= 65521; + } + + return (s1 | (s2 << 16)) |0; + } +} \ No newline at end of file diff --git a/Libraries/iron_format/Sources/iron/format/pako/zlib/CRC32.hx b/Libraries/iron_format/Sources/iron/format/pako/zlib/CRC32.hx new file mode 100644 index 00000000..3b1234a4 --- /dev/null +++ b/Libraries/iron_format/Sources/iron/format/pako/zlib/CRC32.hx @@ -0,0 +1,43 @@ +package iron.format.pako.zlib; + +import haxe.ds.Vector; +import haxe.io.UInt8Array; + +// Note: we can't get significant speed boost here. +// So write code to minimize size - no pregenerated tables +// and array tools dependencies. + +class CRC32 +{ + // Use ordinary array, since untyped makes no boost here + static function makeTable():Vector { + var c, table = new Vector(256); + + for (n in 0...256) { + c = n; + for (k in 0...8) { + c = ((c&1 == 1) ? (0xEDB88320 ^ (c >>> 1)) : (c >>> 1)); + } + table[n] = c; + } + + return table; + } + + // Create table on load. Just 255 signed longs. Not a problem. + static var crcTable:Vector = makeTable(); + + + static public function crc32(crc:Int, buf:UInt8Array, len:Int, pos:Int):Int { + var t = crcTable, + end = pos + len; + + crc = crc ^ (-1); + + for (i in pos...end) { + crc = (crc >>> 8) ^ t[(crc ^ buf[i]) & 0xFF]; + } + + return (crc ^ (-1)); // >>> 0; + } +} \ No newline at end of file diff --git a/Libraries/iron_format/Sources/iron/format/pako/zlib/Constants.hx b/Libraries/iron_format/Sources/iron/format/pako/zlib/Constants.hx new file mode 100644 index 00000000..ca1628df --- /dev/null +++ b/Libraries/iron_format/Sources/iron/format/pako/zlib/Constants.hx @@ -0,0 +1,67 @@ +package iron.format.pako.zlib; + +//NOTE(hx): reconvert these to enum abstracts over Int (watching out for recursion and comparisons) + +class Constants { } + +/* Allowed flush values; see deflate() and inflate() below for details */ +class Flush +{ + static inline public var Z_NO_FLUSH:Int = 0; + static inline public var Z_PARTIAL_FLUSH:Int = 1; + static inline public var Z_SYNC_FLUSH:Int = 2; + static inline public var Z_FULL_FLUSH:Int = 3; + static inline public var Z_FINISH:Int = 4; + static inline public var Z_BLOCK:Int = 5; + static inline public var Z_TREES:Int = 6; +} + +/* Return codes for the compression/decompression functions. Negative values +* are errors, positive values are used for special but normal events. +*/ +class ErrorStatus +{ + static inline public var Z_OK:Int = 0; + static inline public var Z_STREAM_END:Int = 1; + static inline public var Z_NEED_DICT:Int = 2; + static inline public var Z_ERRNO:Int = -1; + static inline public var Z_STREAM_ERROR:Int = -2; + static inline public var Z_DATA_ERROR:Int = -3; + static inline public var Z_MEM_ERROR:Int = -4; //NOTE(hx): commented out in pako.js + static inline public var Z_BUF_ERROR:Int = -5; + static inline public var Z_VERSION_ERROR:Int = -6; //NOTE(hx): commented out in pako.js +} + +/* compression levels */ +class CompressionLevel +{ + static inline public var Z_NO_COMPRESSION:Int = 0; + static inline public var Z_BEST_SPEED:Int = 1; + static inline public var Z_BEST_COMPRESSION:Int = 9; + static inline public var Z_DEFAULT_COMPRESSION:Int = -1; +} + +class Strategy +{ + static inline public var Z_FILTERED:Int = 1; + static inline public var Z_HUFFMAN_ONLY:Int = 2; + static inline public var Z_RLE:Int = 3; + static inline public var Z_FIXED:Int = 4; + static inline public var Z_DEFAULT_STRATEGY:Int = 0; +} + +/* Possible values of the data_type field (though see inflate()) */ +class DataType +{ + static inline public var Z_BINARY:Int = 0; + static inline public var Z_TEXT:Int = 1; + //static inline public var Z_ASCII:Int = 1; // = Z_TEXT (deprecated) + static inline public var Z_UNKNOWN:Int = 2; +} + +/* The deflate compression method */ +class Method +{ + static inline public var Z_DEFLATED:Int = 8; + //static inline public var Z_NULL:Int = null; // Use -1 or null inline, depending on variable type +} diff --git a/Libraries/iron_format/Sources/iron/format/pako/zlib/Deflate.hx b/Libraries/iron_format/Sources/iron/format/pako/zlib/Deflate.hx new file mode 100644 index 00000000..56568a84 --- /dev/null +++ b/Libraries/iron_format/Sources/iron/format/pako/zlib/Deflate.hx @@ -0,0 +1,1896 @@ +package iron.format.pako.zlib; + +import haxe.Constraints.Function; +import haxe.ds.Vector; +import haxe.io.UInt16Array; +import haxe.io.UInt8Array; +import iron.format.pako.utils.Common; +import iron.format.pako.zlib.Constants; +import iron.format.pako.zlib.Trees; +import iron.format.pako.zlib.Adler32; +import iron.format.pako.zlib.CRC32; +import iron.format.pako.zlib.Messages; + +/* Public constants ==========================================================*/ +/* ===========================================================================*/ + + +/* Allowed flush values; see deflate() and inflate() below for details */ +/*var Z_NO_FLUSH = 0; +var Z_PARTIAL_FLUSH = 1; +//var Z_SYNC_FLUSH = 2; +var Z_FULL_FLUSH = 3; +var Z_FINISH = 4; +var Z_BLOCK = 5; +//var Z_TREES = 6; +*/ + +/* Return codes for the compression/decompression functions. Negative values + * are errors, positive values are used for special but normal events. + */ +/*var Z_OK = 0; +var Z_STREAM_END = 1; +//var Z_NEED_DICT = 2; +//var Z_ERRNO = -1; +var Z_STREAM_ERROR = -2; +var Z_DATA_ERROR = -3; +//var Z_MEM_ERROR = -4; +var Z_BUF_ERROR = -5; +//var Z_VERSION_ERROR = -6; +*/ + +/* compression levels */ +/*//var Z_NO_COMPRESSION = 0; +//var Z_BEST_SPEED = 1; +//var Z_BEST_COMPRESSION = 9; +var Z_DEFAULT_COMPRESSION = -1; +*/ + +/*var Z_FILTERED = 1; +var Z_HUFFMAN_ONLY = 2; +var Z_RLE = 3; +var Z_FIXED = 4; +var Z_DEFAULT_STRATEGY = 0; +*/ + +/* Possible values of the data_type field (though see inflate()) */ +/*//var Z_BINARY = 0; +//var Z_TEXT = 1; +//var Z_ASCII = 1; // = Z_TEXT +var Z_UNKNOWN = 2; +*/ + +/* The deflate compression method */ +/*var Z_DEFLATED = 8; +*/ + +/*============================================================================*/ +class Deflate +{ + static inline var deflateInfo:String = 'pako deflate (from Nodeca project)'; + + static inline var MAX_MEM_LEVEL = 9; +/* Maximum value for memLevel in deflateInit2 */ + static inline var MAX_WBITS = 15; +/* 32K LZ77 window */ + static inline var DEF_MEM_LEVEL = 8; + + + static inline var LENGTH_CODES = 29; +/* number of length codes, not counting the special END_BLOCK code */ + static inline var LITERALS = 256; +/* number of literal bytes 0..255 */ + static inline var L_CODES = LITERALS + 1 + LENGTH_CODES; +/* number of Literal or Length codes, including the END_BLOCK code */ + static inline var D_CODES = 30; +/* number of distance codes */ + static inline var BL_CODES = 19; +/* number of codes used to transfer the bit lengths */ + static inline var HEAP_SIZE = 2*L_CODES + 1; +/* maximum heap size */ + static inline var MAX_BITS = 15; +/* All codes must not exceed MAX_BITS bits */ + + static inline var MIN_MATCH = 3; + static inline var MAX_MATCH = 258; + static inline var MIN_LOOKAHEAD = (MAX_MATCH + MIN_MATCH + 1); + + static inline var PRESET_DICT = 0x20; + + static inline var INIT_STATE = 42; + static inline var EXTRA_STATE = 69; + static inline var NAME_STATE = 73; + static inline var COMMENT_STATE = 91; + static inline var HCRC_STATE = 103; + static inline var BUSY_STATE = 113; + static inline var FINISH_STATE = 666; + + static inline var BS_NEED_MORE = 1; /* block not completed, need more input or more output */ + static inline var BS_BLOCK_DONE = 2; /* block flush performed */ + static inline var BS_FINISH_STARTED = 3; /* finish started, need only more output at next deflate */ + static inline var BS_FINISH_DONE = 4; /* finish done, accept no more input or output */ + + static inline var OS_CODE = 0x03; // Unix :) . Don't detect, use this default. + + + static inline function err(strm:ZStream, errorCode) { + strm.msg = Messages.get(errorCode); + return errorCode; + } + + static inline function rank(f) { + return ((f) << 1) - ((f) > 4 ? 9 : 0); + } + + + /* ========================================================================= + * Flush as much pending output as possible. All deflate() output goes + * through this function so some applications may wish to modify it + * to avoid allocating a large strm->output buffer and copying into it. + * (See also read_buf()). + */ + static function flush_pending(strm:ZStream) { + var s = strm.deflateState; + + //_tr_flush_bits(s); + var len = s.pending; + if (len > strm.avail_out) { + len = strm.avail_out; + } + if (len == 0) { return; } + + Common.arraySet(cast strm.output, cast s.pending_buf, s.pending_out, len, strm.next_out); + strm.next_out += len; + s.pending_out += len; + strm.total_out += len; + strm.avail_out -= len; + s.pending -= len; + if (s.pending == 0) { + s.pending_out = 0; + } + } + + + static inline function flush_block_only (s:DeflateState, last) { + Trees._tr_flush_block(s, (s.block_start >= 0 ? s.block_start : -1), s.strstart - s.block_start, last); + s.block_start = s.strstart; + flush_pending(s.strm); + } + + + static inline function put_byte(s:DeflateState, b) { + s.pending_buf[s.pending++] = b; + } + + + /* ========================================================================= + * Put a short in the pending buffer. The 16-bit value is put in MSB order. + * IN assertion: the stream state is correct and there is enough room in + * pending_buf. + */ + static inline function putShortMSB(s:DeflateState, b) { + // put_byte(s, (Byte)(b >> 8)); + // put_byte(s, (Byte)(b & 0xff)); + s.pending_buf[s.pending++] = (b >>> 8) & 0xff; + s.pending_buf[s.pending++] = b & 0xff; + } + + + /* =========================================================================== + * Read a new buffer from the current input stream, update the adler32 + * and total number of bytes read. All deflate() input goes through + * this function so some applications may wish to modify it to avoid + * allocating a large strm->input buffer and copying from it. + * (See also flush_pending()). + */ + static function read_buf(strm:ZStream, buf, start, size) { + var len = strm.avail_in; + + if (len > size) { len = size; } + if (len == 0) { return 0; } + + strm.avail_in -= len; + + Common.arraySet(cast buf, cast strm.input, strm.next_in, len, start); + if (strm.deflateState.wrap == 1) { + strm.adler = Adler32.adler32(strm.adler, buf, len, start); + } + + else if (strm.deflateState.wrap == 2) { + strm.adler = CRC32.crc32(strm.adler, buf, len, start); + } + + strm.next_in += len; + strm.total_in += len; + + return len; + } + + + /* =========================================================================== + * Set match_start to the longest match starting at the given string and + * return its length. Matches shorter or equal to prev_length are discarded, + * in which case the result is equal to prev_length and match_start is + * garbage. + * IN assertions: cur_match is the head of the hash chain for the current + * string (strstart) and its distance is <= MAX_DIST, and prev_length >= 1 + * OUT assertion: the match length is not greater than s->lookahead. + */ + static function longest_match(s:DeflateState, cur_match) { + var chain_length = s.max_chain_length; /* max hash chain length */ + var scan = s.strstart; /* current string */ + var match; /* matched string */ + var len; /* length of current match */ + var best_len = s.prev_length; /* best match length so far */ + var nice_match = s.nice_match; /* stop if match long enough */ + var limit = (s.strstart > (s.w_size - MIN_LOOKAHEAD)) ? + s.strstart - (s.w_size - MIN_LOOKAHEAD) : 0/*NIL*/; + + var _win = s.window; // shortcut + + var wmask = s.w_mask; + var prev = s.prev; + + /* Stop when cur_match becomes <= limit. To simplify the code, + * we prevent matches with the string of window index 0. + */ + + var strend = s.strstart + MAX_MATCH; + var scan_end1 = _win[scan + best_len - 1]; + var scan_end = _win[scan + best_len]; + + /* The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16. + * It is easy to get rid of this optimization if necessary. + */ + // Assert(s->hash_bits >= 8 && MAX_MATCH == 258, "Code too clever"); + + /* Do not waste too much time if we already have a good match: */ + if (s.prev_length >= s.good_match) { + chain_length >>= 2; + } + /* Do not look for matches beyond the end of the input. This is necessary + * to make deflate deterministic. + */ + if (nice_match > s.lookahead) { nice_match = s.lookahead; } + + // Assert((ulg)s->strstart <= s->window_size-MIN_LOOKAHEAD, "need lookahead"); + + do { + // Assert(cur_match < s->strstart, "no future"); + match = cur_match; + + /* Skip to next match if the match length cannot increase + * or if the match length is less than 2. Note that the checks below + * for insufficient lookahead only occur occasionally for performance + * reasons. Therefore uninitialized memory will be accessed, and + * conditional jumps will be made that depend on those values. + * However the length of the match is limited to the lookahead, so + * the output of deflate is not affected by the uninitialized values. + */ + + if (_win[match + best_len] != scan_end || + _win[match + best_len - 1] != scan_end1 || + _win[match] != _win[scan] || + _win[++match] != _win[scan + 1]) { + continue; + } + + /* The check at best_len-1 can be removed because it will be made + * again later. (This heuristic is not always a win.) + * It is not necessary to compare scan[2] and match[2] since they + * are always equal when the other bytes match, given that + * the hash keys are equal and that HASH_BITS >= 8. + */ + scan += 2; + match++; + // Assert(*scan == *match, "match[2]?"); + + /* We check for insufficient lookahead only every 8th comparison; + * the 256th check will be made at strstart+258. + */ + do { + /*jshint noempty:false*/ + } while (_win[++scan] == _win[++match] && _win[++scan] == _win[++match] && + _win[++scan] == _win[++match] && _win[++scan] == _win[++match] && + _win[++scan] == _win[++match] && _win[++scan] == _win[++match] && + _win[++scan] == _win[++match] && _win[++scan] == _win[++match] && + scan < strend); + + // Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan"); + + len = MAX_MATCH - (strend - scan); + scan = strend - MAX_MATCH; + + if (len > best_len) { + s.match_start = cur_match; + best_len = len; + if (len >= nice_match) { + break; + } + scan_end1 = _win[scan + best_len - 1]; + scan_end = _win[scan + best_len]; + } + } while ((cur_match = prev[cur_match & wmask]) > limit && --chain_length != 0); + + if (best_len <= s.lookahead) { + return best_len; + } + return s.lookahead; + } + + + /* =========================================================================== + * Fill the window when the lookahead becomes insufficient. + * Updates strstart and lookahead. + * + * IN assertion: lookahead < MIN_LOOKAHEAD + * OUT assertions: strstart <= window_size-MIN_LOOKAHEAD + * At least one byte has been read, or avail_in == 0; reads are + * performed for at least two bytes (required for the zip translate_eol + * option -- not supported here). + */ + static function fill_window(s:DeflateState) { + var _w_size = s.w_size; + var p, n, m, more, str; + + //Assert(s->lookahead < MIN_LOOKAHEAD, "already enough lookahead"); + + do { + more = s.window_size - s.lookahead - s.strstart; + + // JS ints have 32 bit, block below not needed + /* Deal with !@#$% 64K limit: */ + //if (sizeof(int) <= 2) { + // if (more == 0 && s->strstart == 0 && s->lookahead == 0) { + // more = wsize; + // + // } else if (more == (unsigned)(-1)) { + // /* Very unlikely, but possible on 16 bit machine if + // * strstart == 0 && lookahead == 1 (input done a byte at time) + // */ + // more--; + // } + //} + + + /* If the window is almost full and there is insufficient lookahead, + * move the upper half to the lower one to make room in the upper half. + */ + if (s.strstart >= _w_size + (_w_size - MIN_LOOKAHEAD)) { + + Common.arraySet(cast s.window, cast s.window, _w_size, _w_size, 0); + s.match_start -= _w_size; + s.strstart -= _w_size; + /* we now have strstart >= MAX_DIST */ + s.block_start -= _w_size; + + /* Slide the hash table (could be avoided with 32 bit values + at the expense of memory usage). We slide even when level == 0 + to keep the hash table consistent if we switch back to level > 0 + later. (Using level 0 permanently is not an optimal usage of + zlib, so we don't care about this pathological case.) + */ + + n = s.hash_size; + p = n; + //NOTE(hx): check n-- + do { + m = s.head[--p]; + s.head[p] = (m >= _w_size ? m - _w_size : 0); + } while (--n != 0); + + n = _w_size; + p = n; + do { + m = s.prev[--p]; + s.prev[p] = (m >= _w_size ? m - _w_size : 0); + /* If n is not on any hash chain, prev[n] is garbage but + * its value will never be used. + */ + } while (--n != 0); + + more += _w_size; + } + if (s.strm.avail_in == 0) { + break; + } + + /* If there was no sliding: + * strstart <= WSIZE+MAX_DIST-1 && lookahead <= MIN_LOOKAHEAD - 1 && + * more == window_size - lookahead - strstart + * => more >= window_size - (MIN_LOOKAHEAD-1 + WSIZE + MAX_DIST-1) + * => more >= window_size - 2*WSIZE + 2 + * In the BIG_MEM or MMAP case (not yet supported), + * window_size == input_size + MIN_LOOKAHEAD && + * strstart + s->lookahead <= input_size => more >= MIN_LOOKAHEAD. + * Otherwise, window_size == 2*WSIZE so more >= 2. + * If there was sliding, more >= WSIZE. So in all cases, more >= 2. + */ + //Assert(more >= 2, "more < 2"); + n = read_buf(s.strm, s.window, s.strstart + s.lookahead, more); + s.lookahead += n; + + /* Initialize the hash value now that we have some input: */ + if (s.lookahead + s.insert >= MIN_MATCH) { + str = s.strstart - s.insert; + s.ins_h = s.window[str]; + + /* UPDATE_HASH(s, s->ins_h, s->window[str + 1]); */ + s.ins_h = ((s.ins_h << s.hash_shift) ^ s.window[str + 1]) & s.hash_mask; + //#if MIN_MATCH != 3 + // Call update_hash() MIN_MATCH-3 more times + //#endif + while (s.insert != 0) { + /* UPDATE_HASH(s, s->ins_h, s->window[str + MIN_MATCH-1]); */ + s.ins_h = ((s.ins_h << s.hash_shift) ^ s.window[str + MIN_MATCH-1]) & s.hash_mask; + + s.prev[str & s.w_mask] = s.head[s.ins_h]; + s.head[s.ins_h] = str; + str++; + s.insert--; + if (s.lookahead + s.insert < MIN_MATCH) { + break; + } + } + } + /* If the whole input has less than MIN_MATCH bytes, ins_h is garbage, + * but this is not important since only literal bytes will be emitted. + */ + + } while (s.lookahead < MIN_LOOKAHEAD && s.strm.avail_in != 0); + + /* If the WIN_INIT bytes after the end of the current data have never been + * written, then zero those bytes in order to avoid memory check reports of + * the use of uninitialized (or uninitialised as Julian writes) bytes by + * the longest match routines. Update the high water mark for the next + * time through here. WIN_INIT is set to MAX_MATCH since the longest match + * routines allow scanning to strstart + MAX_MATCH, ignoring lookahead. + */ + // if (s.high_water < s.window_size) { + // var curr = s.strstart + s.lookahead; + // var init = 0; + // + // if (s.high_water < curr) { + // /* Previous high water mark below current data -- zero WIN_INIT + // * bytes or up to end of window, whichever is less. + // */ + // init = s.window_size - curr; + // if (init > WIN_INIT) + // init = WIN_INIT; + // zmemzero(s->window + curr, (unsigned)init); + // s->high_water = curr + init; + // } + // else if (s->high_water < (ulg)curr + WIN_INIT) { + // /* High water mark at or above current data, but below current data + // * plus WIN_INIT -- zero out to current data plus WIN_INIT, or up + // * to end of window, whichever is less. + // */ + // init = (ulg)curr + WIN_INIT - s->high_water; + // if (init > s->window_size - s->high_water) + // init = s->window_size - s->high_water; + // zmemzero(s->window + s->high_water, (unsigned)init); + // s->high_water += init; + // } + // } + // + // Assert((ulg)s->strstart <= s->window_size - MIN_LOOKAHEAD, + // "not enough room for search"); + } + + /* =========================================================================== + * Copy without compression as much as possible from the input stream, return + * the current block state. + * This function does not insert new strings in the dictionary since + * uncompressible data is probably not useful. This function is used + * only for the level=0 compression option. + * NOTE: this function should be optimized to avoid extra copying from + * window to pending_buf. + */ + static function deflate_stored(s:DeflateState, flush:Int) { + /* Stored blocks are limited to 0xffff bytes, pending_buf is limited + * to pending_buf_size, and each stored block has a 5 byte header: + */ + var max_block_size = 0xffff; + + if (max_block_size > s.pending_buf_size - 5) { + max_block_size = s.pending_buf_size - 5; + } + + /* Copy as much as possible from input to output: */ + while (true) { + /* Fill the window as much as possible: */ + if (s.lookahead <= 1) { + + //Assert(s->strstart < s->w_size+MAX_DIST(s) || + // s->block_start >= (long)s->w_size, "slide too late"); + // if (!(s.strstart < s.w_size + (s.w_size - MIN_LOOKAHEAD) || + // s.block_start >= s.w_size)) { + // throw new Error("slide too late"); + // } + + fill_window(s); + if (s.lookahead == 0 && flush == Flush.Z_NO_FLUSH) { + return BS_NEED_MORE; + } + + if (s.lookahead == 0) { + break; + } + /* flush the current block */ + } + //Assert(s->block_start >= 0L, "block gone"); + // if (s.block_start < 0) throw new Error("block gone"); + + s.strstart += s.lookahead; + s.lookahead = 0; + + /* Emit a stored block if pending_buf will be full: */ + var max_start = s.block_start + max_block_size; + + if (s.strstart == 0 || s.strstart >= max_start) { + /* strstart == 0 is possible when wraparound on 16-bit machine */ + s.lookahead = s.strstart - max_start; + s.strstart = max_start; + /*** FLUSH_BLOCK(s, 0); ***/ + flush_block_only(s, false); + if (s.strm.avail_out == 0) { + return BS_NEED_MORE; + } + /***/ + + + } + /* Flush if we may have to slide, otherwise block_start may become + * negative and the data will be gone: + */ + if (s.strstart - s.block_start >= (s.w_size - MIN_LOOKAHEAD)) { + /*** FLUSH_BLOCK(s, 0); ***/ + flush_block_only(s, false); + if (s.strm.avail_out == 0) { + return BS_NEED_MORE; + } + /***/ + } + } + + s.insert = 0; + + if (flush == Flush.Z_FINISH) { + /*** FLUSH_BLOCK(s, 1); ***/ + flush_block_only(s, true); + if (s.strm.avail_out == 0) { + return BS_FINISH_STARTED; + } + /***/ + return BS_FINISH_DONE; + } + + if (s.strstart > s.block_start) { + /*** FLUSH_BLOCK(s, 0); ***/ + flush_block_only(s, false); + if (s.strm.avail_out == 0) { + return BS_NEED_MORE; + } + /***/ + } + + return BS_NEED_MORE; + } + + /* =========================================================================== + * Compress as much as possible from the input stream, return the current + * block state. + * This function does not perform lazy evaluation of matches and inserts + * new strings in the dictionary only for unmatched strings or for short + * matches. It is used only for the fast compression options. + */ + static function deflate_fast(s:DeflateState, flush) { + var hash_head; /* head of the hash chain */ + var bflush; /* set if current block must be flushed */ + + while (true) { + /* Make sure that we always have enough lookahead, except + * at the end of the input file. We need MAX_MATCH bytes + * for the next match, plus MIN_MATCH bytes to insert the + * string following the next match. + */ + if (s.lookahead < MIN_LOOKAHEAD) { + fill_window(s); + if (s.lookahead < MIN_LOOKAHEAD && flush == Flush.Z_NO_FLUSH) { + return BS_NEED_MORE; + } + if (s.lookahead == 0) { + break; /* flush the current block */ + } + } + + /* Insert the string window[strstart .. strstart+2] in the + * dictionary, and set hash_head to the head of the hash chain: + */ + hash_head = 0/*NIL*/; + if (s.lookahead >= MIN_MATCH) { + /*** INSERT_STRING(s, s.strstart, hash_head); ***/ + s.ins_h = ((s.ins_h << s.hash_shift) ^ s.window[s.strstart + MIN_MATCH - 1]) & s.hash_mask; + hash_head = s.prev[s.strstart & s.w_mask] = s.head[s.ins_h]; + s.head[s.ins_h] = s.strstart; + /***/ + } + + /* Find the longest match, discarding those <= prev_length. + * At this point we have always match_length < MIN_MATCH + */ + if (hash_head != 0/*NIL*/ && ((s.strstart - hash_head) <= (s.w_size - MIN_LOOKAHEAD))) { + /* To simplify the code, we prevent matches with the string + * of window index 0 (in particular we have to avoid a match + * of the string with itself at the start of the input file). + */ + s.match_length = longest_match(s, hash_head); + /* longest_match() sets match_start */ + } + if (s.match_length >= MIN_MATCH) { + // check_match(s, s.strstart, s.match_start, s.match_length); // for debug only + + /*** _tr_tally_dist(s, s.strstart - s.match_start, + s.match_length - MIN_MATCH, bflush); ***/ + bflush = Trees._tr_tally(s, s.strstart - s.match_start, s.match_length - MIN_MATCH); + + s.lookahead -= s.match_length; + + /* Insert new strings in the hash table only if the match length + * is not too large. This saves time but degrades compression. + */ + if (s.match_length <= s.max_lazy_match/*max_insert_length*/ && s.lookahead >= MIN_MATCH) { + s.match_length--; /* string at strstart already in table */ + do { + s.strstart++; + /*** INSERT_STRING(s, s.strstart, hash_head); ***/ + s.ins_h = ((s.ins_h << s.hash_shift) ^ s.window[s.strstart + MIN_MATCH - 1]) & s.hash_mask; + hash_head = s.prev[s.strstart & s.w_mask] = s.head[s.ins_h]; + s.head[s.ins_h] = s.strstart; + /***/ + /* strstart never exceeds WSIZE-MAX_MATCH, so there are + * always MIN_MATCH bytes ahead. + */ + } while (--s.match_length != 0); + s.strstart++; + } else + { + s.strstart += s.match_length; + s.match_length = 0; + s.ins_h = s.window[s.strstart]; + /* UPDATE_HASH(s, s.ins_h, s.window[s.strstart+1]); */ + s.ins_h = ((s.ins_h << s.hash_shift) ^ s.window[s.strstart + 1]) & s.hash_mask; + + //#if MIN_MATCH != 3 + // Call UPDATE_HASH() MIN_MATCH-3 more times + //#endif + /* If lookahead < MIN_MATCH, ins_h is garbage, but it does not + * matter since it will be recomputed at next deflate call. + */ + } + } else { + /* No match, output a literal byte */ + //Tracevv((stderr,"%c", s.window[s.strstart])); + /*** _tr_tally_lit(s, s.window[s.strstart], bflush); ***/ + bflush = Trees._tr_tally(s, 0, s.window[s.strstart]); + + s.lookahead--; + s.strstart++; + } + if (bflush) { + /*** FLUSH_BLOCK(s, 0); ***/ + flush_block_only(s, false); + if (s.strm.avail_out == 0) { + return BS_NEED_MORE; + } + /***/ + } + } + s.insert = ((s.strstart < (MIN_MATCH-1)) ? s.strstart : MIN_MATCH-1); + if (flush == Flush.Z_FINISH) { + /*** FLUSH_BLOCK(s, 1); ***/ + flush_block_only(s, true); + if (s.strm.avail_out == 0) { + return BS_FINISH_STARTED; + } + /***/ + return BS_FINISH_DONE; + } + if (s.last_lit != 0) { + /*** FLUSH_BLOCK(s, 0); ***/ + flush_block_only(s, false); + if (s.strm.avail_out == 0) { + return BS_NEED_MORE; + } + /***/ + } + return BS_BLOCK_DONE; + } + + /* =========================================================================== + * Same as above, but achieves better compression. We use a lazy + * evaluation for matches: a match is finally adopted only if there is + * no better match at the next window position. + */ + static function deflate_slow(s:DeflateState, flush) { + var hash_head; /* head of hash chain */ + var bflush; /* set if current block must be flushed */ + + var max_insert; + + /* Process the input block. */ + while (true) { + /* Make sure that we always have enough lookahead, except + * at the end of the input file. We need MAX_MATCH bytes + * for the next match, plus MIN_MATCH bytes to insert the + * string following the next match. + */ + if (s.lookahead < MIN_LOOKAHEAD) { + fill_window(s); + if (s.lookahead < MIN_LOOKAHEAD && flush == Flush.Z_NO_FLUSH) { + return BS_NEED_MORE; + } + if (s.lookahead == 0) { break; } /* flush the current block */ + } + + /* Insert the string window[strstart .. strstart+2] in the + * dictionary, and set hash_head to the head of the hash chain: + */ + hash_head = 0/*NIL*/; + if (s.lookahead >= MIN_MATCH) { + /*** INSERT_STRING(s, s.strstart, hash_head); ***/ + s.ins_h = ((s.ins_h << s.hash_shift) ^ s.window[s.strstart + MIN_MATCH - 1]) & s.hash_mask; + hash_head = s.prev[s.strstart & s.w_mask] = s.head[s.ins_h]; + s.head[s.ins_h] = s.strstart; + /***/ + } + + /* Find the longest match, discarding those <= prev_length. + */ + s.prev_length = s.match_length; + s.prev_match = s.match_start; + s.match_length = MIN_MATCH-1; + + if (hash_head != 0/*NIL*/ && s.prev_length < s.max_lazy_match && + s.strstart - hash_head <= (s.w_size-MIN_LOOKAHEAD)/*MAX_DIST(s)*/) { + /* To simplify the code, we prevent matches with the string + * of window index 0 (in particular we have to avoid a match + * of the string with itself at the start of the input file). + */ + s.match_length = longest_match(s, hash_head); + /* longest_match() sets match_start */ + + if (s.match_length <= 5 && + (s.strategy == Strategy.Z_FILTERED || (s.match_length == MIN_MATCH && s.strstart - s.match_start > 4096/*TOO_FAR*/))) { + + /* If prev_match is also MIN_MATCH, match_start is garbage + * but we will ignore the current match anyway. + */ + s.match_length = MIN_MATCH-1; + } + } + /* If there was a match at the previous step and the current + * match is not better, output the previous match: + */ + if (s.prev_length >= MIN_MATCH && s.match_length <= s.prev_length) { + max_insert = s.strstart + s.lookahead - MIN_MATCH; + /* Do not insert strings in hash table beyond this. */ + + //check_match(s, s.strstart-1, s.prev_match, s.prev_length); + + /***_tr_tally_dist(s, s.strstart - 1 - s.prev_match, + s.prev_length - MIN_MATCH, bflush);***/ + bflush = Trees._tr_tally(s, s.strstart - 1- s.prev_match, s.prev_length - MIN_MATCH); + /* Insert in hash table all strings up to the end of the match. + * strstart-1 and strstart are already inserted. If there is not + * enough lookahead, the last two strings are not inserted in + * the hash table. + */ + s.lookahead -= s.prev_length-1; + s.prev_length -= 2; + do { + if (++s.strstart <= max_insert) { + /*** INSERT_STRING(s, s.strstart, hash_head); ***/ + s.ins_h = ((s.ins_h << s.hash_shift) ^ s.window[s.strstart + MIN_MATCH - 1]) & s.hash_mask; + hash_head = s.prev[s.strstart & s.w_mask] = s.head[s.ins_h]; + s.head[s.ins_h] = s.strstart; + /***/ + } + } while (--s.prev_length != 0); + s.match_available = false; + s.match_length = MIN_MATCH-1; + s.strstart++; + + if (bflush) { + /*** FLUSH_BLOCK(s, 0); ***/ + flush_block_only(s, false); + if (s.strm.avail_out == 0) { + return BS_NEED_MORE; + } + /***/ + } + + } else if (s.match_available) { + /* If there was no match at the previous position, output a + * single literal. If there was a match but the current match + * is longer, truncate the previous match to a single literal. + */ + //Tracevv((stderr,"%c", s->window[s->strstart-1])); + /*** _tr_tally_lit(s, s.window[s.strstart-1], bflush); ***/ + bflush = Trees._tr_tally(s, 0, s.window[s.strstart-1]); + + if (bflush) { + /*** FLUSH_BLOCK_ONLY(s, 0) ***/ + flush_block_only(s, false); + /***/ + } + s.strstart++; + s.lookahead--; + if (s.strm.avail_out == 0) { + return BS_NEED_MORE; + } + } else { + /* There is no previous match to compare with, wait for + * the next step to decide. + */ + s.match_available = true; + s.strstart++; + s.lookahead--; + } + } + //Assert (flush != Z_NO_FLUSH, "no flush?"); + if (s.match_available) { + //Tracevv((stderr,"%c", s->window[s->strstart-1])); + /*** _tr_tally_lit(s, s.window[s.strstart-1], bflush); ***/ + bflush = Trees._tr_tally(s, 0, s.window[s.strstart-1]); + + s.match_available = false; + } + s.insert = s.strstart < MIN_MATCH-1 ? s.strstart : MIN_MATCH-1; + if (flush == Flush.Z_FINISH) { + /*** FLUSH_BLOCK(s, 1); ***/ + flush_block_only(s, true); + if (s.strm.avail_out == 0) { + return BS_FINISH_STARTED; + } + /***/ + return BS_FINISH_DONE; + } + if (s.last_lit != 0) { + /*** FLUSH_BLOCK(s, 0); ***/ + flush_block_only(s, false); + if (s.strm.avail_out == 0) { + return BS_NEED_MORE; + } + /***/ + } + + return BS_BLOCK_DONE; + } + + + /* =========================================================================== + * For Z_RLE, simply look for runs of bytes, generate matches only of distance + * one. Do not maintain a hash table. (It will be regenerated if this run of + * deflate switches away from Z_RLE.) + */ + static function deflate_rle(s:DeflateState, flush) { + var bflush; /* set if current block must be flushed */ + var prev; /* byte at distance one to match */ + var scan, strend; /* scan goes up to strend for length of run */ + + var _win = s.window; + + while (true) { + /* Make sure that we always have enough lookahead, except + * at the end of the input file. We need MAX_MATCH bytes + * for the longest run, plus one for the unrolled loop. + */ + if (s.lookahead <= MAX_MATCH) { + fill_window(s); + if (s.lookahead <= MAX_MATCH && flush == Flush.Z_NO_FLUSH) { + return BS_NEED_MORE; + } + if (s.lookahead == 0) { break; } /* flush the current block */ + } + + /* See how many times the previous byte repeats */ + s.match_length = 0; + if (s.lookahead >= MIN_MATCH && s.strstart > 0) { + scan = s.strstart - 1; + prev = _win[scan]; + if (prev == _win[++scan] && prev == _win[++scan] && prev == _win[++scan]) { + strend = s.strstart + MAX_MATCH; + do { + /*jshint noempty:false*/ + } while (prev == _win[++scan] && prev == _win[++scan] && + prev == _win[++scan] && prev == _win[++scan] && + prev == _win[++scan] && prev == _win[++scan] && + prev == _win[++scan] && prev == _win[++scan] && + scan < strend); + s.match_length = MAX_MATCH - (strend - scan); + if (s.match_length > s.lookahead) { + s.match_length = s.lookahead; + } + } + //Assert(scan <= s->window+(uInt)(s->window_size-1), "wild scan"); + } + + /* Emit match if have run of MIN_MATCH or longer, else emit literal */ + if (s.match_length >= MIN_MATCH) { + //check_match(s, s.strstart, s.strstart - 1, s.match_length); + + /*** _tr_tally_dist(s, 1, s.match_length - MIN_MATCH, bflush); ***/ + bflush = Trees._tr_tally(s, 1, s.match_length - MIN_MATCH); + + s.lookahead -= s.match_length; + s.strstart += s.match_length; + s.match_length = 0; + } else { + /* No match, output a literal byte */ + //Tracevv((stderr,"%c", s->window[s->strstart])); + /*** _tr_tally_lit(s, s.window[s.strstart], bflush); ***/ + bflush = Trees._tr_tally(s, 0, s.window[s.strstart]); + + s.lookahead--; + s.strstart++; + } + if (bflush) { + /*** FLUSH_BLOCK(s, 0); ***/ + flush_block_only(s, false); + if (s.strm.avail_out == 0) { + return BS_NEED_MORE; + } + /***/ + } + } + s.insert = 0; + if (flush == Flush.Z_FINISH) { + /*** FLUSH_BLOCK(s, 1); ***/ + flush_block_only(s, true); + if (s.strm.avail_out == 0) { + return BS_FINISH_STARTED; + } + /***/ + return BS_FINISH_DONE; + } + if (s.last_lit != 0) { + /*** FLUSH_BLOCK(s, 0); ***/ + flush_block_only(s, false); + if (s.strm.avail_out == 0) { + return BS_NEED_MORE; + } + /***/ + } + return BS_BLOCK_DONE; + } + + /* =========================================================================== + * For Z_HUFFMAN_ONLY, do not look for matches. Do not maintain a hash table. + * (It will be regenerated if this run of deflate switches away from Huffman.) + */ + static function deflate_huff(s:DeflateState, flush) { + var bflush; /* set if current block must be flushed */ + + while (true) { + /* Make sure that we have a literal to write. */ + if (s.lookahead == 0) { + fill_window(s); + if (s.lookahead == 0) { + if (flush == Flush.Z_NO_FLUSH) { + return BS_NEED_MORE; + } + break; /* flush the current block */ + } + } + + /* Output a literal byte */ + s.match_length = 0; + //Tracevv((stderr,"%c", s->window[s->strstart])); + /*** _tr_tally_lit(s, s.window[s.strstart], bflush); ***/ + bflush = Trees._tr_tally(s, 0, s.window[s.strstart]); + s.lookahead--; + s.strstart++; + if (bflush) { + /*** FLUSH_BLOCK(s, 0); ***/ + flush_block_only(s, false); + if (s.strm.avail_out == 0) { + return BS_NEED_MORE; + } + /***/ + } + } + s.insert = 0; + if (flush == Flush.Z_FINISH) { + /*** FLUSH_BLOCK(s, 1); ***/ + flush_block_only(s, true); + if (s.strm.avail_out == 0) { + return BS_FINISH_STARTED; + } + /***/ + return BS_FINISH_DONE; + } + if (s.last_lit != 0) { + /*** FLUSH_BLOCK(s, 0); ***/ + flush_block_only(s, false); + if (s.strm.avail_out == 0) { + return BS_NEED_MORE; + } + /***/ + } + return BS_BLOCK_DONE; + } + + //NOTE(hx): Config moved to end of file + + static var configuration_table:Vector; + + static function __init__() { + configuration_table = Vector.fromArrayCopy([ + /* good lazy nice chain */ + new Config(0, 0, 0, 0, deflate_stored), /* 0 store only */ + new Config(4, 4, 8, 4, deflate_fast), /* 1 max speed, no lazy matches */ + new Config(4, 5, 16, 8, deflate_fast), /* 2 */ + new Config(4, 6, 32, 32, deflate_fast), /* 3 */ + + new Config(4, 4, 16, 16, deflate_slow), /* 4 lazy matches */ + new Config(8, 16, 32, 32, deflate_slow), /* 5 */ + new Config(8, 16, 128, 128, deflate_slow), /* 6 */ + new Config(8, 32, 128, 256, deflate_slow), /* 7 */ + new Config(32, 128, 258, 1024, deflate_slow), /* 8 */ + new Config(32, 258, 258, 4096, deflate_slow) /* 9 max compression */ + ]); + } + + + /* =========================================================================== + * Initialize the "longest match" routines for a new zlib stream + */ + static function lm_init(s:DeflateState) { + s.window_size = 2 * s.w_size; + + /*** CLEAR_HASH(s); ***/ + Common.zero(cast s.head); // Fill with NIL (= 0); + + /* Set the default configuration parameters: + */ + s.max_lazy_match = configuration_table[s.level].max_lazy; + s.good_match = configuration_table[s.level].good_length; + s.nice_match = configuration_table[s.level].nice_length; + s.max_chain_length = configuration_table[s.level].max_chain; + + s.strstart = 0; + s.block_start = 0; + s.lookahead = 0; + s.insert = 0; + s.match_length = s.prev_length = MIN_MATCH - 1; + s.match_available = false; + s.ins_h = 0; + } + + //NOTE(hx): DeflateState moved to end of file + + + static public function deflateResetKeep(strm:ZStream) { + var s:DeflateState; + + if (strm == null || strm.deflateState == null) { + return err(strm, ErrorStatus.Z_STREAM_ERROR); + } + + strm.total_in = strm.total_out = 0; + strm.data_type = DataType.Z_UNKNOWN; + + s = strm.deflateState; + s.pending = 0; + s.pending_out = 0; + + if (s.wrap < 0) { + s.wrap = -s.wrap; + /* was made negative by deflate(..., Z_FINISH); */ + } + //NOTE(hx): check wrap to bool + s.status = (s.wrap != 0 ? INIT_STATE : BUSY_STATE); + strm.adler = (s.wrap == 2) ? + 0 // crc32(0, Z_NULL, 0) + : + 1; // adler32(0, Z_NULL, 0) + s.last_flush = Flush.Z_NO_FLUSH; + Trees._tr_init(s); + return ErrorStatus.Z_OK; + } + + + static public function deflateReset(strm:ZStream) { + var ret = deflateResetKeep(strm); + if (ret == ErrorStatus.Z_OK) { + lm_init(strm.deflateState); + } + return ret; + } + + + static public function deflateSetHeader(?strm:ZStream, ?head) { + if (strm == null || strm.deflateState == null) { return ErrorStatus.Z_STREAM_ERROR; } + if (strm.deflateState.wrap != 2) { return ErrorStatus.Z_STREAM_ERROR; } + strm.deflateState.gzhead = head; + return ErrorStatus.Z_OK; + } + + + static public function deflateInit2(strm:ZStream, level:Int, method:Int, windowBits, memLevel, strategy:Int) { + if (strm == null) { // == Z_NULL + return ErrorStatus.Z_STREAM_ERROR; + } + var wrap = 1; + + if (level == CompressionLevel.Z_DEFAULT_COMPRESSION) { + level = 6; + } + + if (windowBits < 0) { /* suppress zlib wrapper */ + wrap = 0; + windowBits = -windowBits; + } + + else if (windowBits > 15) { + wrap = 2; /* write gzip wrapper instead */ + windowBits -= 16; + } + + + if (memLevel < 1 || memLevel > MAX_MEM_LEVEL || method != Method.Z_DEFLATED || + windowBits < 8 || windowBits > 15 || level < 0 || level > 9 || + strategy < 0 || strategy > Strategy.Z_FIXED) { + return err(strm, ErrorStatus.Z_STREAM_ERROR); + } + + + if (windowBits == 8) { + windowBits = 9; + } + /* until 256-byte window bug fixed */ + + var s = new DeflateState(); + + strm.deflateState = s; + s.strm = strm; + + s.wrap = wrap; + s.gzhead = null; + s.w_bits = windowBits; + s.w_size = 1 << s.w_bits; + s.w_mask = s.w_size - 1; + + s.hash_bits = memLevel + 7; + s.hash_size = 1 << s.hash_bits; + s.hash_mask = s.hash_size - 1; + //NOTE(hx): division + s.hash_shift = ~(~Std.int((s.hash_bits + MIN_MATCH - 1) / MIN_MATCH)); + + s.window = new UInt8Array(s.w_size * 2); + s.head = new UInt16Array(s.hash_size); + s.prev = new UInt16Array(s.w_size); + + // Don't need mem init magic for JS. + //s.high_water = 0; /* nothing written to s->window yet */ + + s.lit_bufsize = 1 << (memLevel + 6); /* 16K elements by default */ + + + //overlay = (ushf *) ZALLOC(strm, s->lit_bufsize, sizeof(ush)+2); + //s->pending_buf = (uchf *) overlay; + s.pending_buf_size = s.lit_bufsize * 4; + + s.pending_buf = new UInt8Array(s.pending_buf_size); + + // NOTE(hx): bugfix https://github.com/nodeca/pako/commit/4a07e05823dbb110e522566c2a2fa8cb5ed6d4ff + + // It is offset from `s.pending_buf` (size is `s.lit_bufsize * 2`) + //s->d_buf = overlay + s->lit_bufsize/sizeof(ush); + s.d_buf = 1 * s.lit_bufsize; + + //s->l_buf = s->pending_buf + (1+sizeof(ush))*s->lit_bufsize; + s.l_buf = (1 + 2) * s.lit_bufsize; + + s.level = level; + s.strategy = strategy; + s.method = method; + + return deflateReset(strm); + } + + static inline public function deflateInit(?strm:ZStream, level:Int = CompressionLevel.Z_NO_COMPRESSION) { + return deflateInit2(strm, level, Method.Z_DEFLATED, MAX_WBITS, DEF_MEM_LEVEL, Strategy.Z_DEFAULT_STRATEGY); + } + + + static public function deflate(strm:ZStream, flush:Int) { + var old_flush, s:DeflateState; + var beg, val; // for gzip header write only + + //NOTE(hx): chech flush + if (strm == null || strm.deflateState == null || + flush > Flush.Z_BLOCK || flush < 0) { + return strm != null ? err(strm, ErrorStatus.Z_STREAM_ERROR) : ErrorStatus.Z_STREAM_ERROR; + } + + s = strm.deflateState; + + if (strm.output == null || + (strm.input == null && strm.avail_in != 0) || + (s.status == FINISH_STATE && flush != Flush.Z_FINISH)) { + return err(strm, (strm.avail_out == 0) ? ErrorStatus.Z_BUF_ERROR : ErrorStatus.Z_STREAM_ERROR); + } + + s.strm = strm; /* just in case */ + old_flush = s.last_flush; + s.last_flush = flush; + + /* Write the header */ + if (s.status == INIT_STATE) { + + if (s.wrap == 2) { // GZIP header + strm.adler = 0; //crc32(0L, Z_NULL, 0); + put_byte(s, 31); + put_byte(s, 139); + put_byte(s, 8); + if (s.gzhead == null) { // s->gzhead == Z_NULL + put_byte(s, 0); + put_byte(s, 0); + put_byte(s, 0); + put_byte(s, 0); + put_byte(s, 0); + //NOTE(hx): + put_byte(s, s.level == 9 ? 2 : + (s.strategy >= Strategy.Z_HUFFMAN_ONLY || s.level < 2 ? + 4 : 0)); + put_byte(s, OS_CODE); + s.status = BUSY_STATE; + } + else { + //NOTE(hx): check nulls and falsey values (and lengths) + put_byte(s, (s.gzhead.text ? 1 : 0) + + (s.gzhead.hcrc != 0? 2 : 0) + + (s.gzhead.extra == null ? 0 : 4) + + (s.gzhead.name == null || s.gzhead.name == '' ? 0 : 8) + + (s.gzhead.comment == null || s.gzhead.comment == '' ? 0 : 16) + ); + put_byte(s, s.gzhead.time & 0xff); + put_byte(s, (s.gzhead.time >> 8) & 0xff); + put_byte(s, (s.gzhead.time >> 16) & 0xff); + put_byte(s, (s.gzhead.time >> 24) & 0xff); + put_byte(s, s.level == 9 ? 2 : + (s.strategy >= Strategy.Z_HUFFMAN_ONLY || s.level < 2 ? + 4 : 0)); + put_byte(s, s.gzhead.os & 0xff); + if (s.gzhead.extra != null && s.gzhead.extra.length > 0) { + put_byte(s, s.gzhead.extra.length & 0xff); + put_byte(s, (s.gzhead.extra.length >> 8) & 0xff); + } + if (s.gzhead.hcrc != 0) { + strm.adler = CRC32.crc32(strm.adler, s.pending_buf, s.pending, 0); + } + s.gzindex = 0; + s.status = EXTRA_STATE; + } + } + else // DEFLATE header + { + var header = (Method.Z_DEFLATED + ((s.w_bits - 8) << 4)) << 8; + var level_flags = -1; + + if (s.strategy >= Strategy.Z_HUFFMAN_ONLY || s.level < 2) { + level_flags = 0; + } else if (s.level < 6) { + level_flags = 1; + } else if (s.level == 6) { + level_flags = 2; + } else { + level_flags = 3; + } + header |= (level_flags << 6); + if (s.strstart != 0) { header |= PRESET_DICT; } + header += 31 - (header % 31); + + s.status = BUSY_STATE; + putShortMSB(s, header); + + /* Save the adler32 of the preset dictionary: */ + if (s.strstart != 0) { + putShortMSB(s, strm.adler >>> 16); + putShortMSB(s, strm.adler & 0xffff); + } + strm.adler = 1; // adler32(0L, Z_NULL, 0); + } + } + + //#ifdef GZIP + if (s.status == EXTRA_STATE) { + if (s.gzhead.extra != null/* != Z_NULL*/) { + beg = s.pending; /* start of bytes to update crc */ + + while (s.gzindex < (s.gzhead.extra.length & 0xffff)) { + if (s.pending == s.pending_buf_size) { + if (s.gzhead.hcrc != 0 && s.pending > beg) { + strm.adler = CRC32.crc32(strm.adler, s.pending_buf, s.pending - beg, beg); + } + flush_pending(strm); + beg = s.pending; + if (s.pending == s.pending_buf_size) { + break; + } + } + put_byte(s, s.gzhead.extra[s.gzindex] & 0xff); + s.gzindex++; + } + if (s.gzhead.hcrc != 0 && s.pending > beg) { + strm.adler = CRC32.crc32(strm.adler, s.pending_buf, s.pending - beg, beg); + } + if (s.gzindex == s.gzhead.extra.length) { + s.gzindex = 0; + s.status = NAME_STATE; + } + } + else { + s.status = NAME_STATE; + } + } + if (s.status == NAME_STATE) { + if (s.gzhead.name != null && s.gzhead.name != ''/* != Z_NULL*/) { + beg = s.pending; /* start of bytes to update crc */ + //int val; + + do { + if (s.pending == s.pending_buf_size) { + if (s.gzhead.hcrc != 0 && s.pending > beg) { + strm.adler = CRC32.crc32(strm.adler, s.pending_buf, s.pending - beg, beg); + } + flush_pending(strm); + beg = s.pending; + if (s.pending == s.pending_buf_size) { + val = 1; + break; + } + } + // JS specific: little magic to add zero terminator to end of string + if (s.gzindex < s.gzhead.name.length) { + val = s.gzhead.name.charCodeAt(s.gzindex++) & 0xff; + } else { + val = 0; + } + put_byte(s, val); + } while (val != 0); + + if (s.gzhead.hcrc != 0 && s.pending > beg) { + strm.adler = CRC32.crc32(strm.adler, s.pending_buf, s.pending - beg, beg); + } + if (val == 0) { + s.gzindex = 0; + s.status = COMMENT_STATE; + } + } + else { + s.status = COMMENT_STATE; + } + } + if (s.status == COMMENT_STATE) { + if (s.gzhead.comment != null && s.gzhead.comment != ''/* != Z_NULL*/) { + beg = s.pending; /* start of bytes to update crc */ + //int val; + + do { + if (s.pending == s.pending_buf_size) { + if (s.gzhead.hcrc != 0 && s.pending > beg) { + strm.adler = CRC32.crc32(strm.adler, s.pending_buf, s.pending - beg, beg); + } + flush_pending(strm); + beg = s.pending; + if (s.pending == s.pending_buf_size) { + val = 1; + break; + } + } + // JS specific: little magic to add zero terminator to end of string + if (s.gzindex < s.gzhead.comment.length) { + val = s.gzhead.comment.charCodeAt(s.gzindex++) & 0xff; + } else { + val = 0; + } + put_byte(s, val); + } while (val != 0); + + if (s.gzhead.hcrc != 0 && s.pending > beg) { + strm.adler = CRC32.crc32(strm.adler, s.pending_buf, s.pending - beg, beg); + } + if (val == 0) { + s.status = HCRC_STATE; + } + } + else { + s.status = HCRC_STATE; + } + } + if (s.status == HCRC_STATE) { + if (s.gzhead.hcrc != 0) { + if (s.pending + 2 > s.pending_buf_size) { + flush_pending(strm); + } + if (s.pending + 2 <= s.pending_buf_size) { + put_byte(s, strm.adler & 0xff); + put_byte(s, (strm.adler >> 8) & 0xff); + strm.adler = 0; //crc32(0L, Z_NULL, 0); + s.status = BUSY_STATE; + } + } + else { + s.status = BUSY_STATE; + } + } + //#endif + + /* Flush as much pending output as possible */ + if (s.pending != 0) { + flush_pending(strm); + if (strm.avail_out == 0) { + /* Since avail_out is 0, deflate will be called again with + * more output space, but possibly with both pending and + * avail_in equal to zero. There won't be anything to do, + * but this is not an error situation so make sure we + * return OK instead of BUF_ERROR at next call of deflate: + */ + s.last_flush = -1; + return ErrorStatus.Z_OK; + } + + /* Make sure there is something to do and avoid duplicate consecutive + * flushes. For repeated and useless calls with Z_FINISH, we keep + * returning Z_STREAM_END instead of Z_BUF_ERROR. + */ + } else if (strm.avail_in == 0 && rank(flush) <= rank(old_flush) && + flush != Flush.Z_FINISH) { + return err(strm, ErrorStatus.Z_BUF_ERROR); + } + + /* User must not provide more input after the first FINISH: */ + if (s.status == FINISH_STATE && strm.avail_in != 0) { + return err(strm, ErrorStatus.Z_BUF_ERROR); + } + + /* Start a new block or continue the current one. + */ + if (strm.avail_in != 0 || s.lookahead != 0 || + (flush != Flush.Z_NO_FLUSH && s.status != FINISH_STATE)) { + var bstate = (s.strategy == Strategy.Z_HUFFMAN_ONLY) ? deflate_huff(s, flush) : + (s.strategy == Strategy.Z_RLE ? deflate_rle(s, flush) : + configuration_table[s.level].func(s, flush)); + + if (bstate == BS_FINISH_STARTED || bstate == BS_FINISH_DONE) { + s.status = FINISH_STATE; + } + if (bstate == BS_NEED_MORE || bstate == BS_FINISH_STARTED) { + if (strm.avail_out == 0) { + s.last_flush = -1; + /* avoid BUF_ERROR next call, see above */ + } + return ErrorStatus.Z_OK; + /* If flush != Z_NO_FLUSH && avail_out == 0, the next call + * of deflate should use the same flush parameter to make sure + * that the flush is complete. So we don't have to output an + * empty block here, this will be done at next call. This also + * ensures that for a very small output buffer, we emit at most + * one empty block. + */ + } + if (bstate == BS_BLOCK_DONE) { + if (flush == Flush.Z_PARTIAL_FLUSH) { + Trees._tr_align(s); + } + else if (flush != Flush.Z_BLOCK) { /* FULL_FLUSH or SYNC_FLUSH */ + + Trees._tr_stored_block(s, 0, 0, false); + /* For a full flush, this empty block will be recognized + * as a special marker by inflate_sync(). + */ + if (flush == Flush.Z_FULL_FLUSH) { + /*** CLEAR_HASH(s); ***/ /* forget history */ + Common.zero(cast s.head); // Fill with NIL (= 0); + + if (s.lookahead == 0) { + s.strstart = 0; + s.block_start = 0; + s.insert = 0; + } + } + } + flush_pending(strm); + if (strm.avail_out == 0) { + s.last_flush = -1; /* avoid BUF_ERROR at next call, see above */ + return ErrorStatus.Z_OK; + } + } + } + //Assert(strm->avail_out > 0, "bug2"); + //if (strm.avail_out <= 0) { throw new Error("bug2");} + + if (flush != Flush.Z_FINISH) { return ErrorStatus.Z_OK; } + if (s.wrap <= 0) { return ErrorStatus.Z_STREAM_END; } + + /* Write the trailer */ + if (s.wrap == 2) { + put_byte(s, strm.adler & 0xff); + put_byte(s, (strm.adler >> 8) & 0xff); + put_byte(s, (strm.adler >> 16) & 0xff); + put_byte(s, (strm.adler >> 24) & 0xff); + put_byte(s, strm.total_in & 0xff); + put_byte(s, (strm.total_in >> 8) & 0xff); + put_byte(s, (strm.total_in >> 16) & 0xff); + put_byte(s, (strm.total_in >> 24) & 0xff); + } + else + { + putShortMSB(s, strm.adler >>> 16); + putShortMSB(s, strm.adler & 0xffff); + } + + flush_pending(strm); + /* If avail_out is zero, the application will call deflate again + * to flush the rest. + */ + if (s.wrap > 0) { s.wrap = -s.wrap; } + /* write the trailer only once! */ + return s.pending != 0 ? ErrorStatus.Z_OK : ErrorStatus.Z_STREAM_END; + } + + static public function deflateEnd(strm:ZStream) { + var status; + + if (strm == null/*== Z_NULL*/ || strm.deflateState == null/*== Z_NULL*/) { + return ErrorStatus.Z_STREAM_ERROR; + } + + status = strm.deflateState.status; + if (status != INIT_STATE && + status != EXTRA_STATE && + status != NAME_STATE && + status != COMMENT_STATE && + status != HCRC_STATE && + status != BUSY_STATE && + status != FINISH_STATE + ) { + return err(strm, ErrorStatus.Z_STREAM_ERROR); + } + + strm.deflateState = null; + + return status == BUSY_STATE ? err(strm, ErrorStatus.Z_DATA_ERROR) : ErrorStatus.Z_OK; + } + + /* ========================================================================= + * Initializes the compression dictionary from the given byte + * sequence without producing any compressed output. + */ + static public function deflateSetDictionary(strm:ZStream, dictionary:UInt8Array) { + var dictLength = dictionary.length; + + var s:DeflateState; + var str, n:Int; + var wrap:Int; + var avail:Int; + var next:Int; + var input:UInt8Array; + var tmpDict:UInt8Array; + + if (strm == null/*== Z_NULL*/ || strm.deflateState == null/*== Z_NULL*/) { + return ErrorStatus.Z_STREAM_ERROR; + } + + s = strm.deflateState; + wrap = s.wrap; + + if (wrap == 2 || (wrap == 1 && s.status != INIT_STATE) || s.lookahead > 0) { + return ErrorStatus.Z_STREAM_ERROR; + } + + /* when using zlib wrappers, compute Adler-32 for provided dictionary */ + if (wrap == 1) { + /* adler32(strm->adler, dictionary, dictLength); */ + strm.adler = Adler32.adler32(strm.adler, dictionary, dictLength, 0); + } + + s.wrap = 0; /* avoid computing Adler-32 in read_buf */ + + /* if dictionary would fill window, just replace the history */ + if (dictLength >= s.w_size) { + if (wrap == 0) { /* already empty otherwise */ + /*** CLEAR_HASH(s); ***/ + Common.zero(cast s.head); // Fill with NIL (= 0); + s.strstart = 0; + s.block_start = 0; + s.insert = 0; + } + /* use the tail */ + // dictionary = dictionary.slice(dictLength - s.w_size); + tmpDict = new UInt8Array(s.w_size); + Common.arraySet(cast tmpDict, cast dictionary, dictLength - s.w_size, s.w_size, 0); + dictionary = tmpDict; + dictLength = s.w_size; + } + /* insert dictionary into window and hash */ + avail = strm.avail_in; + next = strm.next_in; + input = strm.input; + strm.avail_in = dictLength; + strm.next_in = 0; + strm.input = dictionary; + fill_window(s); + while (s.lookahead >= MIN_MATCH) { + str = s.strstart; + n = s.lookahead - (MIN_MATCH - 1); + do { + /* UPDATE_HASH(s, s->ins_h, s->window[str + MIN_MATCH-1]); */ + s.ins_h = ((s.ins_h << s.hash_shift) ^ s.window[str + MIN_MATCH - 1]) & s.hash_mask; + + s.prev[str & s.w_mask] = s.head[s.ins_h]; + + s.head[s.ins_h] = str; + str++; + } while (--n != 0); + s.strstart = str; + s.lookahead = MIN_MATCH - 1; + fill_window(s); + } + s.strstart += s.lookahead; + s.block_start = s.strstart; + s.insert = s.lookahead; + s.lookahead = 0; + s.match_length = s.prev_length = MIN_MATCH - 1; + s.match_available = false; + strm.next_in = next; + strm.input = input; + strm.avail_in = avail; + s.wrap = wrap; + return ErrorStatus.Z_OK; + } + +} + + +@:allow(iron.format.pako.zlib.Trees) +@:allow(iron.format.pako.zlib.Deflate) +@:access(iron.format.pako.zlib.Deflate) +class DeflateState +{ + var strm:ZStream = null; /* pointer back to this zlib stream */ + var status:Int = 0; /* as the name implies */ + var pending_buf:UInt8Array = null; /* output still pending */ + var pending_buf_size:Int = 0; /* size of pending_buf */ + var pending_out:Int = 0; /* next pending byte to output to the stream */ + var pending:Int = 0; /* nb of bytes in the pending buffer */ + var wrap:Int = 0; /* bit 0 true for zlib, bit 1 true for gzip */ + var gzhead:GZHeader = null; /* gzip header information to write */ + var gzindex:Int = 0; /* where in extra, name, or comment */ + var method:Int = Method.Z_DEFLATED; /* can only be DEFLATED */ + var last_flush:Int = -1; /* value of flush param for previous deflate call */ + + var w_size:Int = 0; /* LZ77 window size (32K by default) */ + var w_bits:Int = 0; /* log2(w_size) (8..16) */ + var w_mask:Int = 0; /* w_size - 1 */ + + var window:UInt8Array = null; + /* Sliding window. Input bytes are read into the second half of the window, + * and move to the first half later to keep a dictionary of at least wSize + * bytes. With this organization, matches are limited to a distance of + * wSize-MAX_MATCH bytes, but this ensures that IO is always + * performed with a length multiple of the block size. + */ + + var window_size:Int = 0; + /* Actual size of window: 2*wSize, except when the user input buffer + * is directly used as sliding window. + */ + + var prev:UInt16Array = null; + /* Link to older string with same hash index. To limit the size of this + * array to 64K, this link is maintained only for the last 32K strings. + * An index in this array is thus a window index modulo 32K. + */ + + var head:UInt16Array = null; /* Heads of the hash chains or NIL. */ + + var ins_h:Int = 0; /* hash index of string to be inserted */ + var hash_size:Int = 0; /* number of elements in hash table */ + var hash_bits:Int = 0; /* log2(hash_size) */ + var hash_mask:Int = 0; /* hash_size-1 */ + + var hash_shift:Int = 0; + /* Number of bits by which ins_h must be shifted at each input + * step. It must be such that after MIN_MATCH steps, the oldest + * byte no longer takes part in the hash key, that is: + * hash_shift * MIN_MATCH >= hash_bits + */ + + var block_start:Int = 0; + /* Window position at the beginning of the current output block. Gets + * negative when the window is moved backwards. + */ + + var match_length:Int = 0; /* length of best match */ + var prev_match:Int = 0; /* previous match */ + var match_available:Bool = false; /* set if previous match exists */ + var strstart:Int = 0; /* start of string to insert */ + var match_start:Int = 0; /* start of matching string */ + var lookahead:Int = 0; /* number of valid bytes ahead in window */ + + var prev_length:Int = 0; + /* Length of the best match at previous step. Matches not greater than this + * are discarded. This is used in the lazy match evaluation. + */ + + var max_chain_length:Int = 0; + /* To speed up deflation, hash chains are never searched beyond this + * length. A higher limit improves compression ratio but degrades the + * speed. + */ + + var max_lazy_match:Int = 0; + /* Attempt to find a better match only when the current match is strictly + * smaller than this value. This mechanism is used only for compression + * levels >= 4. + */ + // That's alias to max_lazy_match, don't use directly + //var max_insert_length = 0; + /* Insert new strings in the hash table only if the match length is not + * greater than this length. This saves time but degrades compression. + * max_insert_length is used only for compression levels <= 3. + */ + + var level:Int = CompressionLevel.Z_NO_COMPRESSION; /* compression level (1..9) */ + var strategy:Int = Strategy.Z_DEFAULT_STRATEGY; /* favor or force Huffman coding*/ + + var good_match:Int = 0; + /* Use a faster search when the previous match is longer than this */ + + var nice_match:Int = 0; /* Stop searching when current match exceeds this */ + + /* used by trees.c: */ + + /* Didn't use ct_data typedef below to suppress compiler warning */ + + // struct ct_data_s dyn_ltree[HEAP_SIZE]; /* literal and length tree */ + // struct ct_data_s dyn_dtree[2*D_CODES+1]; /* distance tree */ + // struct ct_data_s bl_tree[2*BL_CODES+1]; /* Huffman tree for bit lengths */ + + // Use flat array of DOUBLE size, with interleaved fata, + // because JS does not support effective + var dyn_ltree = new UInt16Array(Deflate.HEAP_SIZE * 2); + var dyn_dtree = new UInt16Array((2*Deflate.D_CODES+1) * 2); + var bl_tree = new UInt16Array((2*Deflate.BL_CODES+1) * 2); + + var l_desc:TreeDesc = null; /* desc. for literal tree */ + var d_desc:TreeDesc = null; /* desc. for distance tree */ + var bl_desc:TreeDesc = null; /* desc. for bit length tree */ + + //ush bl_count[MAX_BITS+1]; + var bl_count = new UInt16Array(Deflate.MAX_BITS+1); + /* number of codes at each bit length for an optimal tree */ + + //int heap[2*L_CODES+1]; /* heap used to build the Huffman trees */ + var heap = new UInt16Array(2*Deflate.L_CODES+1); /* heap used to build the Huffman trees */ + + var heap_len:Int = 0; /* number of elements in the heap */ + var heap_max:Int = 0; /* element of largest frequency */ + /* The sons of heap[n] are heap[2*n] and heap[2*n+1]. heap[0] is not used. + * The same heap array is used to build all trees. + */ + + var depth = new UInt16Array(2*Deflate.L_CODES+1); //uch depth[2*L_CODES+1]; + /* Depth of each subtree used as tie breaker for trees of equal frequency + */ + + var l_buf:Int = 0; /* buffer index for literals or lengths */ + + var lit_bufsize:Int = 0; + /* Size of match buffer for literals/lengths. There are 4 reasons for + * limiting lit_bufsize to 64K: + * - frequencies can be kept in 16 bit counters + * - if compression is not successful for the first block, all input + * data is still in the window so we can still emit a stored block even + * when input comes from standard input. (This can also be done for + * all blocks if lit_bufsize is not greater than 32K.) + * - if compression is not successful for a file smaller than 64K, we can + * even emit a stored file instead of a stored block (saving 5 bytes). + * This is applicable only for zip (not gzip or zlib). + * - creating new Huffman trees less frequently may not provide fast + * adaptation to changes in the input data statistics. (Take for + * example a binary file with poorly compressible code followed by + * a highly compressible string table.) Smaller buffer sizes give + * fast adaptation but have of course the overhead of transmitting + * trees more frequently. + * - I can't count above 4 + */ + + var last_lit:Int = 0; /* running index in l_buf */ + + var d_buf:Int = 0; + /* Buffer index for distances. To simplify the code, d_buf and l_buf have + * the same number of elements. To use different lengths, an extra flag + * array would be necessary. + */ + + var opt_len:Int = 0; /* bit length of current block with optimal trees */ + var static_len:Int = 0; /* bit length of current block with static trees */ + var matches:Int = 0; /* number of string matches in current block */ + var insert:Int = 0; /* bytes at end of window left to insert */ + + + var bi_buf:Int = 0; + /* Output buffer. bits are inserted starting at the bottom (least + * significant bits). + */ + var bi_valid:Int = 0; + + // Used for window memory init. We safely ignore it for JS. That makes + // sense only for pointers and memory check tools. + //this.high_water:Int = 0; + /* High water mark offset in window for initialized bytes -- bytes above + * this are set to zero in order to avoid memory check warnings when + * longest match routines access bytes past the input. This is then + * updated to the new high water mark. + */ + + + function new() { + Common.zero(cast dyn_ltree); + Common.zero(cast dyn_dtree); + Common.zero(cast bl_tree); + + Common.zero(cast this.heap); + + Common.zero(cast this.depth); + } +} + +/* Values for max_lazy_match, good_match and max_chain_length, depending on + * the desired pack level (0..9). The values given below have been tuned to + * exclude worst case performance for pathological files. Better values may be + * found for specific files. + */ +@:allow(iron.format.pako.zlib.Deflate) +class Config +{ + var good_length:Int; + var max_lazy:Int; + var nice_length:Int; + var max_chain:Int; + var func:DeflateState->Int->Int; + + function new(good_length, max_lazy, nice_length, max_chain, func:DeflateState->Int->Int) { + this.good_length = good_length; + this.max_lazy = max_lazy; + this.nice_length = nice_length; + this.max_chain = max_chain; + this.func = func; + } +} + +/* +exports.deflateInit = deflateInit; +exports.deflateInit2 = deflateInit2; +exports.deflateReset = deflateReset; +exports.deflateResetKeep = deflateResetKeep; +exports.deflateSetHeader = deflateSetHeader; +exports.deflate = deflate; +exports.deflateEnd = deflateEnd; +exports.deflateSetDictionary = deflateSetDictionary; +exports.deflateInfo = 'pako deflate (from Nodeca project)'; +*/ + +/* Not implemented +exports.deflateBound = deflateBound; +exports.deflateCopy = deflateCopy; +exports.deflateParams = deflateParams; +exports.deflatePending = deflatePending; +exports.deflatePrime = deflatePrime; +exports.deflateTune = deflateTune; +*/ diff --git a/Libraries/iron_format/Sources/iron/format/pako/zlib/GZHeader.hx b/Libraries/iron_format/Sources/iron/format/pako/zlib/GZHeader.hx new file mode 100644 index 00000000..2f7efc79 --- /dev/null +++ b/Libraries/iron_format/Sources/iron/format/pako/zlib/GZHeader.hx @@ -0,0 +1,44 @@ +package iron.format.pako.zlib; + +import haxe.io.UInt8Array; + + +class GZHeader +{ + /* true if compressed data believed to be text */ + public var text:Bool = false; + /* modification time */ + public var time:Int = 0; + /* extra flags (not used when writing a gzip file) */ + public var xflags:Int = 0; + /* operating system */ + public var os:Int = 0; + /* pointer to extra field or Z_NULL if none */ + public var extra:UInt8Array = null; + /* extra field length (valid if extra != Z_NULL) */ + public var extra_len:Int = 0; // Actually, we don't need it in JS, + // but leave for few code modifications + + // + // Setup limits is not necessary because in js we should not preallocate memory + // for inflate use constant limit in 65536 bytes + // + + /* space at extra (only when reading header) */ + // public var extra_max = 0; + /* pointer to zero-terminated file name or Z_NULL */ + public var name:String = ''; + /* space at name (only when reading header) */ + // public var name_max = 0; + /* pointer to zero-terminated comment or Z_NULL */ + public var comment:String = ''; + /* space at comment (only when reading header) */ + // public var comm_max = 0; + /* true if there was or will be a header crc */ + public var hcrc:Int = 0; + /* true when done reading gzip header (not used when writing a gzip file) */ + public var done:Bool = false; + + //NOTE(hx): change GZHeader to accept a GZOptions typedef? + public function new() { } +} \ No newline at end of file diff --git a/Libraries/iron_format/Sources/iron/format/pako/zlib/InfFast.hx b/Libraries/iron_format/Sources/iron/format/pako/zlib/InfFast.hx new file mode 100644 index 00000000..22274c1c --- /dev/null +++ b/Libraries/iron_format/Sources/iron/format/pako/zlib/InfFast.hx @@ -0,0 +1,350 @@ +package iron.format.pako.zlib; + +import haxe.io.Int32Array; +import iron.format.pako.zlib.Inflate.InflateState; +import iron.format.pako.zlib.ZStream; + +class InfFast +{ +// See state defs from inflate.js +//var BAD = 30; /* got a data error -- remain here until reset */ +//var TYPE = 12; /* i: waiting for type bits, including last-flag bit */ + +/* + Decode literal, length, and distance codes and write out the resulting + literal and match bytes until either not enough input or output is + available, an end-of-block is encountered, or a data error is encountered. + When large enough input and output buffers are supplied to inflate(), for + example, a 16K input buffer and a 64K output buffer, more than 95% of the + inflate execution time is spent in this routine. + + Entry assumptions: + + state.mode === LEN + strm.avail_in >= 6 + strm.avail_out >= 258 + start >= strm.avail_out + state.bits < 8 + + On return, state.mode is one of: + + LEN -- ran out of enough output space or enough available input + TYPE -- reached end of block code, inflate() to interpret next block + BAD -- error in block data + + Notes: + + - The maximum input bits used by a length/distance pair is 15 bits for the + length code, 5 bits for the length extra, 15 bits for the distance code, + and 13 bits for the distance extra. This totals 48 bits, or six bytes. + Therefore if strm.avail_in >= 6, then there is enough input to avoid + checking for available input while decoding. + + - The maximum bytes that a single length/distance pair can output is 258 + bytes, which is the maximum length that can be coded. inflate_fast() + requires strm.avail_out >= 258 for each loop to avoid checking for + output space. + */ + + static public function inflate_fast(strm:ZStream, start) { + var state:InflateState; + var _in; /* local strm.input */ + var last; /* have enough input while in < last */ + var _out; /* local strm.output */ + var beg; /* inflate()'s initial strm.output */ + var end; /* while out < end, enough space available */ + //#ifdef INFLATE_STRICT + var dmax; /* maximum distance from zlib header */ + //#endif + var wsize; /* window size or zero if not using window */ + var whave; /* valid bytes in the window */ + var wnext; /* window write index */ + // Use `s_window` instead `window`, avoid conflict with instrumentation tools + var s_window; /* allocated sliding window, if wsize != 0 */ + var hold; /* local strm.hold */ + var bits; /* local strm.bits */ + var lcode:Int32Array; /* local strm.lencode */ + var dcode; /* local strm.distcode */ + var lmask; /* mask for first level of length codes */ + var dmask; /* mask for first level of distance codes */ + var here; /* retrieved table entry */ + var op; /* code bits, operation, extra bits, or */ + /* window position, window bytes to copy */ + var len; /* match length, unused bytes */ + var dist; /* match distance */ + var from; /* where to copy match from */ + var from_source; + + + var input, output; // JS specific, because we have no pointers + + /* copy state to local variables */ + state = strm.inflateState; + //here = state.here; + _in = strm.next_in; + input = strm.input; + last = _in + (strm.avail_in - 5); + _out = strm.next_out; + output = strm.output; + beg = _out - (start - strm.avail_out); + end = _out + (strm.avail_out - 257); + //#ifdef INFLATE_STRICT + dmax = state.dmax; + //#endif + wsize = state.wsize; + whave = state.whave; + wnext = state.wnext; + s_window = state.window; + hold = state.hold; + bits = state.bits; + lcode = state.lencode; + dcode = state.distcode; + lmask = (1 << state.lenbits) - 1; + dmask = (1 << state.distbits) - 1; + + + /* decode literals and length/distances until end-of-block or not enough + input data or output space */ + + var exit_top = false; + //top: + do { + if (exit_top) break; + exit_top = false; + + if (bits < 15) { + hold += input[_in++] << bits; + bits += 8; + hold += input[_in++] << bits; + bits += 8; + } + + here = lcode[hold & lmask]; + + //dolen: + while (true) { // Goto emulation + op = here >>> 24/*here.bits*/; + hold >>>= op; + bits -= op; + op = (here >>> 16) & 0xff/*here.op*/; + if (op == 0) { /* literal */ + //Tracevv((stderr, here.val >= 0x20 && here.val < 0x7f ? + // "inflate: literal '%c'\n" : + // "inflate: literal 0x%02x\n", here.val)); + output[_out++] = here & 0xffff/*here.val*/; + } + else if (op & 16 != 0) { /* length base */ + len = here & 0xffff/*here.val*/; + op &= 15; /* number of extra bits */ + if (op != 0) { + if (bits < op) { + hold += input[_in++] << bits; + bits += 8; + } + len += hold & ((1 << op) - 1); + hold >>>= op; + bits -= op; + } + //Tracevv((stderr, "inflate: length %u\n", len)); + if (bits < 15) { + hold += input[_in++] << bits; + bits += 8; + hold += input[_in++] << bits; + bits += 8; + } + here = dcode[hold & dmask]; + + //dodist: + while (true) { // goto emulation + op = here >>> 24/*here.bits*/; + hold >>>= op; + bits -= op; + op = (here >>> 16) & 0xff/*here.op*/; + + if (op & 16 != 0) { /* distance base */ + dist = here & 0xffff/*here.val*/; + op &= 15; /* number of extra bits */ + if (bits < op) { + hold += input[_in++] << bits; + bits += 8; + if (bits < op) { + hold += input[_in++] << bits; + bits += 8; + } + } + dist += hold & ((1 << op) - 1); + //#ifdef INFLATE_STRICT + if (dist > dmax) { + strm.msg = 'invalid distance too far back'; + state.mode = Inflate.BAD; + exit_top = true; + break; //out of dodist + } + //#endif + hold >>>= op; + bits -= op; + //Tracevv((stderr, "inflate: distance %u\n", dist)); + op = _out - beg; /* max distance in output */ + if (dist > op) { /* see if copy from window */ + op = dist - op; /* distance back in window */ + if (op > whave) { + if (state.sane != 0) { + strm.msg = 'invalid distance too far back'; + state.mode = Inflate.BAD; + exit_top = true; + break; //out of dodist + } + + // (!) This block is disabled in zlib defailts, + // don't enable it for binary compatibility + //#ifdef INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR + // if (len <= op - whave) { + // do { + // output[_out++] = 0; + // } while (--len); + // continue top; + // } + // len -= op - whave; + // do { + // output[_out++] = 0; + // } while (--op > whave); + // if (op === 0) { + // from = _out - dist; + // do { + // output[_out++] = output[from++]; + // } while (--len); + // continue top; + // } + //#endif + } + from = 0; // window index + from_source = s_window; + if (wnext == 0) { /* very common case */ + from += wsize - op; + if (op < len) { /* some from window */ + len -= op; + do { + output[_out++] = s_window[from++]; + } while (--op != 0); + from = _out - dist; /* rest from output */ + from_source = output; + } + } + else if (wnext < op) { /* wrap around window */ + from += wsize + wnext - op; + op -= wnext; + if (op < len) { /* some from end of window */ + len -= op; + do { + output[_out++] = s_window[from++]; + } while (--op != 0); + from = 0; + if (wnext < len) { /* some from start of window */ + op = wnext; + len -= op; + do { + output[_out++] = s_window[from++]; + } while (--op != 0); + from = _out - dist; /* rest from output */ + from_source = output; + } + } + } + else { /* contiguous in window */ + from += wnext - op; + if (op < len) { /* some from window */ + len -= op; + do { + output[_out++] = s_window[from++]; + } while (--op != 0); + from = _out - dist; /* rest from output */ + from_source = output; + } + } + while (len > 2) { + output[_out++] = from_source[from++]; + output[_out++] = from_source[from++]; + output[_out++] = from_source[from++]; + len -= 3; + } + if (len != 0) { + output[_out++] = from_source[from++]; + if (len > 1) { + output[_out++] = from_source[from++]; + } + } + } + else { + from = _out - dist; /* copy direct from output */ + do { /* minimum length is three */ + output[_out++] = output[from++]; + output[_out++] = output[from++]; + output[_out++] = output[from++]; + len -= 3; + } while (len > 2); + if (len != 0) { + output[_out++] = output[from++]; + if (len > 1) { + output[_out++] = output[from++]; + } + } + } + } + else if ((op & 64) == 0) { /* 2nd level distance code */ + here = dcode[(here & 0xffff)/*here.val*/ + (hold & ((1 << op) - 1))]; + continue; //dodist + } + else { + strm.msg = 'invalid distance code'; + state.mode = Inflate.BAD; + exit_top = true; + break; //out of dodist + } + + break; // need to emulate goto via break and continue + + } // end of dodist + + if (exit_top) break; //out of dolen + } + else if ((op & 64) == 0) { /* 2nd level length code */ + here = lcode[(here & 0xffff)/*here.val*/ + (hold & ((1 << op) - 1))]; + continue; //dolen + } + else if (op & 32 != 0) { /* end-of-block */ + //Tracevv((stderr, "inflate: end of block\n")); + state.mode = Inflate.TYPE; + exit_top = true; + break; //out of dolen + } + else { + strm.msg = 'invalid literal/length code'; + state.mode = Inflate.BAD; + exit_top = true; + break; //out of dolen + } + + break; // need to emulate goto via "continue" + } // end of dolen + + if (exit_top) continue; //top + + } while (_in < last && _out < end); + // end of top + + /* return unused bytes (on entry, bits < 8, so in won't go too far back) */ + len = bits >> 3; + _in -= len; + bits -= len << 3; + hold &= (1 << bits) - 1; + + /* update state and return */ + strm.next_in = _in; + strm.next_out = _out; + strm.avail_in = (_in < last ? 5 + (last - _in) : 5 - (_in - last)); + strm.avail_out = (_out < end ? 257 + (end - _out) : 257 - (_out - end)); + state.hold = hold; + state.bits = bits; + return; + } +} \ No newline at end of file diff --git a/Libraries/iron_format/Sources/iron/format/pako/zlib/InfTrees.hx b/Libraries/iron_format/Sources/iron/format/pako/zlib/InfTrees.hx new file mode 100644 index 00000000..0866baeb --- /dev/null +++ b/Libraries/iron_format/Sources/iron/format/pako/zlib/InfTrees.hx @@ -0,0 +1,362 @@ +package iron.format.pako.zlib; + +import haxe.io.Int32Array; +import haxe.io.UInt16Array; +import iron.format.pako.zlib.Constants; + + +typedef Options = { + var bits:Int; + @:optional var table_index:Int; +} + +class InfTrees +{ + //var utils = require('../utils/common'); + + static var MAXBITS:Int = 15; + static var ENOUGH_LENS:Int = 852; + static var ENOUGH_DISTS:Int = 592; + //static var ENOUGH:Int = (ENOUGH_LENS+ENOUGH_DISTS); + + static var CODES:Int = 0; + static var LENS:Int = 1; + static var DISTS:Int = 2; + + static var lbase:UInt16Array = UInt16Array.fromArray([ /* Length codes 257..285 base */ + 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, + 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0 + ]); + + static var lext:UInt16Array = UInt16Array.fromArray([ /* Length codes 257..285 extra */ + 16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 18, 18, 18, 18, + 19, 19, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 16, 72, 78 + ]); + + static var dbase:UInt16Array = UInt16Array.fromArray([ /* Distance codes 0..29 base */ + 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, + 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, + 8193, 12289, 16385, 24577, 0, 0 + ]); + + static var dext:UInt16Array = UInt16Array.fromArray([ /* Distance codes 0..29 extra */ + 16, 16, 16, 16, 17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22, + 23, 23, 24, 24, 25, 25, 26, 26, 27, 27, + 28, 28, 29, 29, 64, 64 + ]); + + static public function inflate_table(type:Int, lens:UInt16Array, lens_index, codes, table:Int32Array, table_index, work:UInt16Array, opts:Options):Int + { + var bits:Int = 0; + + var len:Int = 0; /* a code's length in bits */ + var sym:Int = 0; /* index of code symbols */ + var min:Int = 0; /* minimum code lengths */ + var max:Int = 0; /* maximum code lengths */ + var root:Int = 0; /* number of index bits for root table */ + var curr:Int = 0; /* number of index bits for current table */ + var drop:Int = 0; /* code bits to drop for sub-table */ + var left:Int = 0; /* number of prefix codes available */ + var used:Int = 0; /* code entries in table used */ + var huff:Int = 0; /* Huffman code */ + var incr:Int = 0; /* for incrementing code, index */ + var fill:Int = 0; /* index for replicating entries */ + var low:Int = 0; /* low bits for current root entry */ + var mask:Int = 0; /* mask for low root bits */ + var next:Int = 0; /* next available space in table */ + var base:UInt16Array = null; /* base value table to use */ + var base_index:Int = 0; + // var shoextra; /* extra bits table to use */ + var end = 0; /* use base and extra for symbol > end */ + var count = new UInt16Array(MAXBITS+1); //[MAXBITS+1]; /* number of codes of each length */ + var offs = new UInt16Array(MAXBITS+1); //[MAXBITS+1]; /* offsets in table for each length */ + var extra:UInt16Array = null; + var extra_index:Int = 0; + + bits = opts.bits; + //here = opts.here; /* table entry for duplication */ + + var here_bits, here_op, here_val; + + /* + Process a set of code lengths to create a canonical Huffman code. The + code lengths are lens[0..codes-1]. Each length corresponds to the + symbols 0..codes-1. The Huffman code is generated by first sorting the + symbols by length from short to long, and retaining the symbol order + for codes with equal lengths. Then the code starts with all zero bits + for the first code of the shortest length, and the codes are integer + increments for the same length, and zeros are appended as the length + increases. For the deflate format, these bits are stored backwards + from their more natural integer increment ordering, and so when the + decoding tables are built in the large loop below, the integer codes + are incremented backwards. + + This routine assumes, but does not check, that all of the entries in + lens[] are in the range 0..MAXBITS. The caller must assure this. + 1..MAXBITS is interpreted as that code length. zero means that that + symbol does not occur in this code. + + The codes are sorted by computing a count of codes for each length, + creating from that a table of starting indices for each length in the + sorted table, and then entering the symbols in order in the sorted + table. The sorted table is work[], with that space being provided by + the caller. + + The length counts are used for other purposes as well, i.e. finding + the minimum and maximum length codes, determining if there are any + codes at all, checking for a valid set of lengths, and looking ahead + at length counts to determine sub-table sizes when building the + decoding tables. + */ + + /* accumulate lengths for codes (assumes lens[] all in 0..MAXBITS) */ + len = 0; + while (len <= MAXBITS) { + count[len] = 0; + len++; + } + sym = 0; + while (sym < codes) { + //NOTE(hx): watch out! + count[lens[lens_index + sym]]+=1; + sym++; + } + + /* bound code lengths, force root to be within code lengths */ + root = bits; + max = MAXBITS; + while (max >= 1) { + if (count[max] != 0) { break; } + max--; + } + if (root > max) { + root = max; + } + if (max == 0) { /* no symbols to code at all */ + //table.op[opts.table_index] = 64; //here.op = (var char)64; /* invalid code marker */ + //table.bits[opts.table_index] = 1; //here.bits = (var char)1; + //table.val[opts.table_index++] = 0; //here.val = (var short)0; + table[table_index++] = (1 << 24) | (64 << 16) | 0; + + + //table.op[opts.table_index] = 64; + //table.bits[opts.table_index] = 1; + //table.val[opts.table_index++] = 0; + table[table_index++] = (1 << 24) | (64 << 16) | 0; + + opts.bits = 1; + return ErrorStatus.Z_OK; /* no symbols, but wait for decoding to report error */ + } + min = 1; + while (min < max) { + if (count[min] != 0) { break; } + min++; + } + if (root < min) { + root = min; + } + + /* check for an over-subscribed or incomplete set of lengths */ + left = 1; + len = 1; + while (len <= MAXBITS) { + left <<= 1; + left -= count[len]; + if (left < 0) { + return ErrorStatus.Z_ERRNO; + } /* over-subscribed */ + len++; + } + if (left > 0 && (type == CODES || max != 1)) { + return ErrorStatus.Z_ERRNO; /* incomplete set */ + } + + /* generate offsets into symbol table for each length for sorting */ + offs[1] = 0; + len = 1; + while (len < MAXBITS) { + offs[len + 1] = offs[len] + count[len]; + len++; + } + + /* sort symbols by length, by symbol order within each length */ + sym = 0; + while (sym < codes) { + if (lens[lens_index + sym] != 0) { + //NOTE(hx): watch out! + work[offs[lens[lens_index + sym]]] = sym; + offs[lens[lens_index + sym]] += 1; + } + sym++; + } + + /* + Create and fill in decoding tables. In this loop, the table being + filled is at next and has curr index bits. The code being used is huff + with length len. That code is converted to an index by dropping drop + bits off of the bottom. For codes where len is less than drop + curr, + those top drop + curr - len bits are incremented through all values to + fill the table with replicated entries. + + root is the number of index bits for the root table. When len exceeds + root, sub-tables are created pointed to by the root entry with an index + of the low root bits of huff. This is saved in low to check for when a + new sub-table should be started. drop is zero when the root table is + being filled, and drop is root when sub-tables are being filled. + + When a new sub-table is needed, it is necessary to look ahead in the + code lengths to determine what size sub-table is needed. The length + counts are used for this, and so count[] is decremented as codes are + entered in the tables. + + used keeps track of how many table entries have been allocated from the + provided *table space. It is checked for LENS and DIST tables against + the constants ENOUGH_LENS and ENOUGH_DISTS to guard against changes in + the initial root table size constants. See the comments in inftrees.h + for more information. + + sym increments through all symbols, and the loop terminates when + all codes of length max, i.e. all codes, have been processed. This + routine permits incomplete codes, so another loop after this one fills + in the rest of the decoding tables with invalid code markers. + */ + + /* set up for code type */ + // poor man optimization - use if-else instead of switch, + // to avoid deopts in old v8 + if (type == CODES) { + base = extra = work; /* dummy value--not used */ + end = 19; + + } else if (type == LENS) { + base = lbase; + base_index -= 257; + extra = lext; + extra_index -= 257; + end = 256; + + } else { /* DISTS */ + base = dbase; + extra = dext; + end = -1; + } + + /* initialize opts for loop */ + huff = 0; /* starting code */ + sym = 0; /* starting code symbol */ + len = min; /* starting code length */ + next = table_index; /* current table to fill in */ + curr = root; /* current table index bits */ + drop = 0; /* current bits to drop from code for index */ + low = -1; /* trigger new sub-table when len > root */ + used = 1 << root; /* use root table entries */ + mask = used - 1; /* mask for comparing low */ + + /* check available table space */ + if ((type == LENS && used > ENOUGH_LENS) || + (type == DISTS && used > ENOUGH_DISTS)) { + return ErrorStatus.Z_STREAM_END; + } + + var i=0; + /* process all codes and make table entries */ + while (true) { + i++; + /* create table entry */ + here_bits = len - drop; + if (work[sym] < end) { + here_op = 0; + here_val = work[sym]; + } + else if (work[sym] > end) { + here_op = extra[extra_index + work[sym]]; + here_val = base[base_index + work[sym]]; + } + else { + here_op = 32 + 64; /* end of block */ + here_val = 0; + } + + /* replicate for those indices with low len bits equal to huff */ + incr = 1 << (len - drop); + fill = 1 << curr; + min = fill; /* save offset to next table */ + do { + fill -= incr; + table[next + (huff >> drop) + fill] = (here_bits << 24) | (here_op << 16) | here_val |0; + } while (fill != 0); + + /* backwards increment the len-bit code huff */ + incr = 1 << (len - 1); + //NOTE(hx): cond + while ((huff & incr) != 0) { + incr >>= 1; + } + if (incr != 0) { + huff &= incr - 1; + huff += incr; + } else { + huff = 0; + } + + /* go to next symbol, update count, len */ + sym++; + //NOTE(hx): assign + count[len] -= 1; + if ((count[len]) == 0) { + if (len == max) { break; } + len = lens[lens_index + work[sym]]; + } + + /* create new sub-table if needed */ + if (len > root && (huff & mask) != low) { + /* if first time, transition to sub-tables */ + if (drop == 0) { + drop = root; + } + + /* increment past last table */ + next += min; /* here min is 1 << curr */ + + /* determine length of next table */ + curr = len - drop; + left = 1 << curr; + while (curr + drop < max) { + left -= count[curr + drop]; + if (left <= 0) { break; } + curr++; + left <<= 1; + } + + /* check for enough space */ + used += 1 << curr; + if ((type == LENS && used > ENOUGH_LENS) || + (type == DISTS && used > ENOUGH_DISTS)) { + return ErrorStatus.Z_STREAM_END; + } + + /* point entry in root table to sub-table */ + low = huff & mask; + /*table.op[low] = curr; + table.bits[low] = root; + table.val[low] = next - opts.table_index;*/ + table[low] = (root << 24) | (curr << 16) | (next - table_index) |0; + } + } + + /* fill in remaining table entry if code is incomplete (guaranteed to have + at most one remaining entry, since if the code is incomplete, the + maximum code length that was allowed to get this far is one bit) */ + if (huff != 0) { + //table.op[next + huff] = 64; /* invalid code marker */ + //table.bits[next + huff] = len - drop; + //table.val[next + huff] = 0; + table[next + huff] = ((len - drop) << 24) | (64 << 16) |0; + } + + /* set return parameters */ + //opts.table_index += used; + opts.bits = root; + return ErrorStatus.Z_OK; + } +} \ No newline at end of file diff --git a/Libraries/iron_format/Sources/iron/format/pako/zlib/Inflate.hx b/Libraries/iron_format/Sources/iron/format/pako/zlib/Inflate.hx new file mode 100644 index 00000000..56b1aa8f --- /dev/null +++ b/Libraries/iron_format/Sources/iron/format/pako/zlib/Inflate.hx @@ -0,0 +1,1689 @@ +package iron.format.pako.zlib; + +import haxe.io.Int32Array; +import haxe.io.UInt16Array; +import haxe.io.UInt8Array; +import iron.format.pako.utils.Common; +import iron.format.pako.zlib.Adler32; +import iron.format.pako.zlib.CRC32; +import iron.format.pako.zlib.InfFast; +import iron.format.pako.zlib.InfTrees; +import iron.format.pako.zlib.ZStream; +import iron.format.pako.zlib.Constants; + + +@:allow(iron.format.pako.zlib.InfFast) +class Inflate +{ + static inline var inflateInfo:String = 'pako inflate (from Nodeca project)'; + + static inline var CODES = 0; + static inline var LENS = 1; + static inline var DISTS = 2; + + /* Public constants ==========================================================*/ + /* ===========================================================================*/ + + + /* Allowed flush values; see deflate() and inflate() below for details */ + /* + //var Z_NO_FLUSH = 0; + //var Z_PARTIAL_FLUSH = 1; + //var Z_SYNC_FLUSH = 2; + //var Z_FULL_FLUSH = 3; + var Z_FINISH = 4; + var Z_BLOCK = 5; + var Z_TREES = 6; + */ + + /* Return codes for the compression/decompression functions. Negative values + * are errors, positive values are used for special but normal events. + */ + /* + var Z_OK = 0; + var Z_STREAM_END = 1; + var Z_NEED_DICT = 2; + //var Z_ERRNO = -1; + var Z_STREAM_ERROR = -2; + var Z_DATA_ERROR = -3; + var Z_MEM_ERROR = -4; + var Z_BUF_ERROR = -5; + //var Z_VERSION_ERROR = -6; + */ + + /* The deflate compression method */ + //var Z_DEFLATED = 8; + + + /* STATES ====================================================================*/ + /* ===========================================================================*/ + + + static inline var HEAD = 1; /* i: waiting for magic header */ + static inline var FLAGS = 2; /* i: waiting for method and flags (gzip) */ + static inline var TIME = 3; /* i: waiting for modification time (gzip) */ + static inline var OS = 4; /* i: waiting for extra flags and operating system (gzip) */ + static inline var EXLEN = 5; /* i: waiting for extra length (gzip) */ + static inline var EXTRA = 6; /* i: waiting for extra bytes (gzip) */ + static inline var NAME = 7; /* i: waiting for end of file name (gzip) */ + static inline var COMMENT = 8; /* i: waiting for end of comment (gzip) */ + static inline var HCRC = 9; /* i: waiting for header crc (gzip) */ + static inline var DICTID = 10; /* i: waiting for dictionary check value */ + static inline var DICT = 11; /* waiting for inflateSetDictionary() call */ + static inline var TYPE = 12; /* i: waiting for type bits, including last-flag bit */ + static inline var TYPEDO = 13; /* i: same, but skip check to exit inflate on new block */ + static inline var STORED = 14; /* i: waiting for stored size (length and complement) */ + static inline var COPY_ = 15; /* i/o: same as COPY below, but only first time in */ + static inline var COPY = 16; /* i/o: waiting for input or output to copy stored block */ + static inline var TABLE = 17; /* i: waiting for dynamic block table lengths */ + static inline var LENLENS = 18; /* i: waiting for code length code lengths */ + static inline var CODELENS = 19; /* i: waiting for length/lit and distance code lengths */ + static inline var LEN_ = 20; /* i: same as LEN below, but only first time in */ + static inline var LEN = 21; /* i: waiting for length/lit/eob code */ + static inline var LENEXT = 22; /* i: waiting for length extra bits */ + static inline var DIST = 23; /* i: waiting for distance code */ + static inline var DISTEXT = 24; /* i: waiting for distance extra bits */ + static inline var MATCH = 25; /* o: waiting for output space to copy string */ + static inline var LIT = 26; /* o: waiting for output space to write literal */ + static inline var CHECK = 27; /* i: waiting for 32-bit check value */ + static inline var LENGTH = 28; /* i: waiting for 32-bit length (gzip) */ + static inline var DONE = 29; /* finished check, done -- remain here until reset */ + static inline var BAD = 30; /* got a data error -- remain here until reset */ + static inline var MEM = 31; /* got an inflate() memory error -- remain here until reset */ + static inline var SYNC = 32; /* looking for synchronization bytes to restart inflate() */ + + /* ===========================================================================*/ + + + + static inline var ENOUGH_LENS = 852; + static inline var ENOUGH_DISTS = 592; + //static inline var ENOUGH = (ENOUGH_LENS+ENOUGH_DISTS); + + static inline var MAX_WBITS = 15; + /* 32K LZ77 window */ + static inline var DEF_WBITS = MAX_WBITS; + + + static inline function ZSWAP32(q:Int):Int { + return (((q >>> 24) & 0xff) + + ((q >>> 8) & 0xff00) + + ((q & 0xff00) << 8) + + ((q & 0xff) << 24)); + } + + //NOTE(hx): InflateState moved to end of file + + static public function inflateResetKeep(strm:ZStream) { + var state:InflateState; + + if (strm == null || strm.inflateState == null) { return ErrorStatus.Z_STREAM_ERROR; } + state = strm.inflateState; + strm.total_in = strm.total_out = state.total = 0; + strm.msg = ''; /*Z_NULL*/ + if (state.wrap != 0) { /* to support ill-conceived Java test suite */ + strm.adler = state.wrap & 1; + } + state.mode = HEAD; + state.last = false; + state.havedict = false; + state.dmax = 32768; + state.head = null/*Z_NULL*/; + state.hold = 0; + state.bits = 0; + //state.lencode = state.distcode = state.next = state.codes; + state.lencode = state.lendyn = new Int32Array(ENOUGH_LENS); + state.distcode = state.distdyn = new Int32Array(ENOUGH_DISTS); + + state.sane = 1; + state.back = -1; + //Tracev((stderr, "inflate: reset\n")); + return ErrorStatus.Z_OK; + } + + static public function inflateReset(strm:ZStream) { + var state; + + if (strm == null || strm.inflateState == null) { return ErrorStatus.Z_STREAM_ERROR; } + state = strm.inflateState; + state.wsize = 0; + state.whave = 0; + state.wnext = 0; + return inflateResetKeep(strm); + + } + + static public function inflateReset2(strm:ZStream, windowBits) { + var wrap; + var state:InflateState; + + /* get the state */ + if (strm == null || strm.inflateState == null) { return ErrorStatus.Z_STREAM_ERROR; } + state = strm.inflateState; + + /* extract wrap request from windowBits parameter */ + if (windowBits < 0) { + wrap = 0; + windowBits = -windowBits; + } + else { + wrap = (windowBits >> 4) + 1; + if (windowBits < 48) { + windowBits &= 15; + } + } + + /* set number of window bits, free window if different */ + if (windowBits != 0 && (windowBits < 8 || windowBits > 15)) { + return ErrorStatus.Z_STREAM_ERROR; + } + if (state.window != null && state.wbits != windowBits) { + state.window = null; + } + + /* update state and reset the rest of it */ + state.wrap = wrap; + state.wbits = windowBits; + return inflateReset(strm); + } + + static public function inflateInit2(strm:ZStream, windowBits) { + var ret; + var state:InflateState; + + if (strm == null) { return ErrorStatus.Z_STREAM_ERROR; } + //strm.msg = Z_NULL; /* in case we return an error */ + + state = new InflateState(); + + //if (state === Z_NULL) return Z_MEM_ERROR; + //Tracev((stderr, "inflate: allocated\n")); + strm.inflateState = state; + state.window = null/*Z_NULL*/; + ret = inflateReset2(strm, windowBits); + if (ret != ErrorStatus.Z_OK) { + strm.inflateState = null/*Z_NULL*/; + } + return ret; + } + + static inline public function inflateInit(strm:ZStream) { + return inflateInit2(strm, DEF_WBITS); + } + + + /* + Return state with length and distance decoding tables and index sizes set to + fixed code decoding. Normally this returns fixed tables from inffixed.h. + If BUILDFIXED is defined, then instead this routine builds the tables the + first time it's called, and returns those tables the first time and + thereafter. This reduces the size of the code by about 2K bytes, in + exchange for a little execution time. However, BUILDFIXED should not be + used for threaded applications, since the rewriting of the tables and virgin + may not be thread-safe. + */ + static var virgin:Bool = true; + + // We have no pointers in JS, so keep tables separate + static var lenfix; + static var distfix; + + static function fixedtables(state:InflateState) { + /* build fixed huffman tables if first call (may not be thread safe) */ + if (virgin) { + var sym; + + lenfix = new Int32Array(512); + distfix = new Int32Array(32); + + /* literal/length table */ + sym = 0; + while (sym < 144) { state.lens[sym++] = 8; } + while (sym < 256) { state.lens[sym++] = 9; } + while (sym < 280) { state.lens[sym++] = 7; } + while (sym < 288) { state.lens[sym++] = 8; } + + InfTrees.inflate_table(LENS, state.lens, 0, 288, lenfix, 0, state.work, {bits: 9}); + + /* distance table */ + sym = 0; + while (sym < 32) { state.lens[sym++] = 5; } + + InfTrees.inflate_table(DISTS, state.lens, 0, 32, distfix, 0, state.work, {bits: 5}); + + /* do this just once */ + virgin = false; + } + + state.lencode = lenfix; + state.lenbits = 9; + state.distcode = distfix; + state.distbits = 5; + } + + + /* + Update the window with the last wsize (normally 32K) bytes written before + returning. If window does not exist yet, create it. This is only called + when a window is already in use, or when output has been written during this + inflate call, but the end of the deflate stream has not been reached yet. + It is also called to create a window for dictionary data when a dictionary + is loaded. + + Providing output buffers larger than 32K to inflate() should provide a speed + advantage, since only the last 32K of output is copied to the sliding window + upon return from inflate(), and since all distances after the first 32K of + output will fall in the output data, making match copies simpler and faster. + The advantage may be dependent on the size of the processor's data caches. + */ + static function updatewindow(strm:ZStream, src:UInt8Array, end, copy) { + var dist; + var state = strm.inflateState; + + /* if it hasn't been done already, allocate space for the window */ + if (state.window == null) { + state.wsize = 1 << state.wbits; + state.wnext = 0; + state.whave = 0; + + state.window = new UInt8Array(state.wsize); + } + + /* copy state->wsize or less output bytes into the circular window */ + if (copy >= state.wsize) { + Common.arraySet(cast state.window,cast src, end - state.wsize, state.wsize, 0); + state.wnext = 0; + state.whave = state.wsize; + } + else { + dist = state.wsize - state.wnext; + if (dist > copy) { + dist = copy; + } + //zmemcpy(state->window + state->wnext, end - copy, dist); + Common.arraySet(cast state.window,cast src, end - copy, dist, state.wnext); + copy -= dist; + if (copy != 0) { + //zmemcpy(state->window, end - copy, copy); + Common.arraySet(cast state.window,cast src, end - copy, copy, 0); + state.wnext = copy; + state.whave = state.wsize; + } + else { + state.wnext += dist; + if (state.wnext == state.wsize) { state.wnext = 0; } + if (state.whave < state.wsize) { state.whave += dist; } + } + } + return 0; + } + + static public function inflate(?strm:ZStream, flush:Int):Int { + var state:InflateState; + var input, output; // input/output buffers + var next; /* next input INDEX */ + var put; /* next output INDEX */ + var have, left; /* available input and output */ + var hold = 0; /* bit buffer */ + var bits = 0; /* bits in bit buffer */ + var _in, _out; /* save starting available input and output */ + var copy = 0; /* number of stored or match bytes to copy */ + var from; /* where to copy match bytes from */ + var from_source; + var here = 0; /* current decoding table entry */ + var here_bits = 0, here_op = 0, here_val = 0; // paked "here" denormalized (JS specific) + //var last; /* parent table entry */ + var last_bits, last_op, last_val; // paked "last" denormalized (JS specific) + var len = 0; /* length to copy for repeats, bits to drop */ + var ret:Int; /* return code */ + var hbuf = new UInt8Array(4); /* buffer for gzip header crc calculation */ + var opts; + + var n; // temporary var for NEED_BITS + + var order = /* permutation of code lengths */ + [16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15]; + + + if (strm == null || strm.inflateState == null || strm.output == null || + (strm.input == null && strm.avail_in != 0)) { + return ErrorStatus.Z_STREAM_ERROR; + } + + state = strm.inflateState; + if (state.mode == TYPE) { state.mode = TYPEDO; } /* skip check */ + + + //--- LOAD() --- + put = strm.next_out; + output = strm.output; + left = strm.avail_out; + next = strm.next_in; + input = strm.input; + have = strm.avail_in; + hold = state.hold; + bits = state.bits; + //--- + + _in = have; + _out = left; + ret = ErrorStatus.Z_OK; + + //NOTE(hx): this huge block is a state machine implemented with continue to emulate gotos. + //A major difference with the js version is that, in haxe, switch cases don't have break, + //so it's worth double checking this section + var inf_leave = false; + //inf_leave: // goto emulation + while (true) { + if (inf_leave) break; + inf_leave = false; + switch (state.mode) { + case HEAD: + if (state.wrap == 0) { + state.mode = TYPEDO; + continue; // inf_leave + } + //=== NEEDBITS(16); + while (bits < 16) { + if (have == 0) { + inf_leave = true; + break; // out of this while + } + have--; + hold += input[next++] << bits; + bits += 8; + } + if (inf_leave) break; //inf_leave + //===// + if ((state.wrap & 2 != 0) && hold == 0x8b1f) { /* gzip header */ + state.check = 0/*crc32(0L, Z_NULL, 0)*/; + //=== CRC2(state.check, hold); + hbuf[0] = hold & 0xff; + hbuf[1] = (hold >>> 8) & 0xff; + state.check = CRC32.crc32(state.check, hbuf, 2, 0); + //===// + + //=== INITBITS(); + hold = 0; + bits = 0; + //===// + state.mode = FLAGS; + continue; //inf_leave + } + state.flags = 0; /* expect zlib header */ + if (state.head != null) { + state.head.done = false; + } + //NOTE(hx): check bitwise ops + if (!(state.wrap & 1 == 1) || /* check if zlib header allowed */ + ((((hold & 0xff)/*BITS(8)*/ << 8) + (hold >> 8)) % 31) != 0) { + strm.msg = 'incorrect header check'; + state.mode = BAD; + continue; //inf_leave + } + if ((hold & 0x0f)/*BITS(4)*/ != Method.Z_DEFLATED) { + strm.msg = 'unknown compression method'; + state.mode = BAD; + continue; //inf_leave + } + //--- DROPBITS(4) ---// + hold >>>= 4; + bits -= 4; + //---// + len = (hold & 0x0f)/*BITS(4)*/ + 8; + if (state.wbits == 0) { + state.wbits = len; + } + else if (len > state.wbits) { + strm.msg = 'invalid window size'; + state.mode = BAD; + continue; //inf_leave + } + state.dmax = 1 << len; + //Tracev((stderr, "inflate: zlib header ok\n")); + strm.adler = state.check = 1/*adler32(0L, Z_NULL, 0)*/; + state.mode = hold & 0x200 != 0 ? DICTID : TYPE; + //=== INITBITS(); + hold = 0; + bits = 0; + //===// + case FLAGS: + //=== NEEDBITS(16); */ + while (bits < 16) { + if (have == 0) { + inf_leave = true; + break; // out of this while + } + have--; + hold += input[next++] << bits; + bits += 8; + } + if (inf_leave) break; //inf_leave + //===// + state.flags = hold; + if ((state.flags & 0xff) != Method.Z_DEFLATED) { + strm.msg = 'unknown compression method'; + state.mode = BAD; + continue; //inf_leave + } + if (state.flags & 0xe000 != 0) { + strm.msg = 'unknown header flags set'; + state.mode = BAD; + continue; //inf_leave + } + if (state.head != null) { + state.head.text = ((hold >> 8) & 1 == 1); + } + if (state.flags & 0x0200 != 0) { + //=== CRC2(state.check, hold); + hbuf[0] = hold & 0xff; + hbuf[1] = (hold >>> 8) & 0xff; + state.check = CRC32.crc32(state.check, hbuf, 2, 0); + //===// + } + //=== INITBITS(); + hold = 0; + bits = 0; + //===// + state.mode = TIME; + /* falls through */ + case TIME: + //=== NEEDBITS(32); */ + while (bits < 32) { + if (have == 0) { + inf_leave = true; + break; // out of this while + } + have--; + hold += input[next++] << bits; + bits += 8; + } + if (inf_leave) break; //inf_leave + //===// + if (state.head != null) { + state.head.time = hold; + } + if (state.flags & 0x0200 != 0) { + //=== CRC4(state.check, hold) + hbuf[0] = hold & 0xff; + hbuf[1] = (hold >>> 8) & 0xff; + hbuf[2] = (hold >>> 16) & 0xff; + hbuf[3] = (hold >>> 24) & 0xff; + state.check = CRC32.crc32(state.check, hbuf, 4, 0); + //=== + } + //=== INITBITS(); + hold = 0; + bits = 0; + //===// + state.mode = OS; + /* falls through */ + case OS: + //=== NEEDBITS(16); */ + while (bits < 16) { + if (have == 0) { + inf_leave = true; + break; // out of this while + } + have--; + hold += input[next++] << bits; + bits += 8; + } + if (inf_leave) break; //inf_leave + //===// + if (state.head != null) { + state.head.xflags = (hold & 0xff); + state.head.os = (hold >> 8); + } + if (state.flags & 0x0200 != 0) { + //=== CRC2(state.check, hold); + hbuf[0] = hold & 0xff; + hbuf[1] = (hold >>> 8) & 0xff; + state.check = CRC32.crc32(state.check, hbuf, 2, 0); + //===// + } + //=== INITBITS(); + hold = 0; + bits = 0; + //===// + state.mode = EXLEN; + /* falls through */ + case EXLEN: + if (state.flags & 0x0400 != 0) { + //=== NEEDBITS(16); */ + while (bits < 16) { + if (have == 0) { + inf_leave = true; + break; // out of this while + } + have--; + hold += input[next++] << bits; + bits += 8; + } + if (inf_leave) break; //inf_leave + //===// + state.length = hold; + if (state.head != null) { + state.head.extra_len = hold; + } + if (state.flags & 0x0200 != 0) { + //=== CRC2(state.check, hold); + hbuf[0] = hold & 0xff; + hbuf[1] = (hold >>> 8) & 0xff; + state.check = CRC32.crc32(state.check, hbuf, 2, 0); + //===// + } + //=== INITBITS(); + hold = 0; + bits = 0; + //===// + } + else if (state.head != null) { + state.head.extra = null/*Z_NULL*/; + } + state.mode = EXTRA; + /* falls through */ + case EXTRA: + if (state.flags & 0x0400 != 0) { + copy = state.length; + if (copy > have) { copy = have; } + if (copy != 0) { + if (state.head != null) { + len = state.head.extra_len - state.length; + if (state.head.extra == null) { + // Use untyped array for more conveniend processing later + state.head.extra = new UInt8Array(state.head.extra_len); + } + Common.arraySet( + cast state.head.extra, + cast input, + next, + // extra field is limited to 65536 bytes + // - no need for additional size check + copy, + /*len + copy > state.head.extra_max - len ? state.head.extra_max : copy,*/ + len + ); + //zmemcpy(state.head.extra + len, next, + // len + copy > state.head.extra_max ? + // state.head.extra_max - len : copy); + } + if (state.flags & 0x0200 != 0) { + state.check = CRC32.crc32(state.check, input, copy, next); + } + have -= copy; + next += copy; + state.length -= copy; + } + if (state.length != 0) { + inf_leave = true; + break; //inf_leave + } + } + state.length = 0; + state.mode = NAME; + /* falls through */ + case NAME: + if (state.flags & 0x0800 != 0) { + if (have == 0) { + inf_leave = true; + break; //inf_leave + } + copy = 0; + do { + // TODO: 2 or 1 bytes? + len = input[next + copy++]; + /* use constant limit because in js we should not preallocate memory */ + if (state.head != null && len != 0 && + (state.length < 65536 /*state.head.name_max*/)) { + state.head.name += String.fromCharCode(len); + } + } while (len != 0 && copy < have); + + if (state.flags & 0x0200 != 0) { + state.check = CRC32.crc32(state.check, input, copy, next); + } + have -= copy; + next += copy; + if (len != 0) { + inf_leave = true; + break; //inf_leave + } + } + else if (state.head != null) { + state.head.name = null; + } + state.length = 0; + state.mode = COMMENT; + /* falls through */ + case COMMENT: + if (state.flags & 0x1000 != 0) { + if (have == 0) { + inf_leave = true; + break; //inf_leave + } + copy = 0; + do { + len = input[next + copy++]; + /* use constant limit because in js we should not preallocate memory */ + if (state.head != null && len != 0 && + (state.length < 65536 /*state.head.comm_max*/)) { + state.head.comment += String.fromCharCode(len); + } + } while (len != 0 && copy < have); + if (state.flags & 0x0200 != 0) { + state.check = CRC32.crc32(state.check, input, copy, next); + } + have -= copy; + next += copy; + if (len != 0) { + inf_leave = true; + break; //inf_leave + } + } + else if (state.head != null) { + state.head.comment = null; + } + state.mode = HCRC; + /* falls through */ + case HCRC: + if (state.flags & 0x0200 != 0) { + //=== NEEDBITS(16); */ + while (bits < 16) { + if (have == 0) { + inf_leave = true; + break; //out of this while + } + have--; + hold += input[next++] << bits; + bits += 8; + } + if (inf_leave) break; //inf_leave + //===// + if (hold != (state.check & 0xffff)) { + strm.msg = 'header crc mismatch'; + state.mode = BAD; + continue; //inf_leave + } + //=== INITBITS(); + hold = 0; + bits = 0; + //===// + } + if (state.head != null) { + state.head.hcrc = ((state.flags >> 9) & 1); + state.head.done = true; + } + strm.adler = state.check = 0 /*crc32(0L, Z_NULL, 0)*/; + state.mode = TYPE; + case DICTID: + //=== NEEDBITS(32); */ + while (bits < 32) { + if (have == 0) { + inf_leave = true; + break; //out of this while + } + have--; + hold += input[next++] << bits; + bits += 8; + } + if (inf_leave) break; //inf_leave + //===// + strm.adler = state.check = ZSWAP32(hold); + //=== INITBITS(); + hold = 0; + bits = 0; + //===// + state.mode = DICT; + /* falls through */ + case DICT: + if (!state.havedict) { + //--- RESTORE() --- + strm.next_out = put; + strm.avail_out = left; + strm.next_in = next; + strm.avail_in = have; + state.hold = hold; + state.bits = bits; + //--- + return ErrorStatus.Z_NEED_DICT; + } + strm.adler = state.check = 1/*adler32(0L, Z_NULL, 0)*/; + state.mode = TYPE; + /* falls through */ + case TYPE: + if (flush == Flush.Z_BLOCK || flush == Flush.Z_TREES) continue; //inf_leave + state.mode = TYPEDO; + /* falls through */ + case TYPEDO: + if (state.last) { + //--- BYTEBITS() ---// + hold >>>= bits & 7; + bits -= bits & 7; + //---// + state.mode = CHECK; + continue; //inf_leave + } + //=== NEEDBITS(3); */ + while (bits < 3) { + if (have == 0) { + inf_leave = true; + break; //out of this while + } + have--; + hold += input[next++] << bits; + bits += 8; + } + if (inf_leave) break; //inf_leave + //===// + state.last = (hold & 0x01) == 1/*BITS(1)*/; + //--- DROPBITS(1) ---// + hold >>>= 1; + bits -= 1; + //---// + + switch ((hold & 0x03)/*BITS(2)*/) { + case 0: /* stored block */ + //Tracev((stderr, "inflate: stored block%s\n", + // state.last ? " (last)" : "")); + state.mode = STORED; + case 1: /* fixed block */ + fixedtables(state); + //Tracev((stderr, "inflate: fixed codes block%s\n", + // state.last ? " (last)" : "")); + state.mode = LEN_; /* decode codes */ + if (flush == Flush.Z_TREES) { + //--- DROPBITS(2) ---// + hold >>>= 2; + bits -= 2; + //---// + inf_leave = true; + break; //inf_leave + } + case 2: /* dynamic block */ + //Tracev((stderr, "inflate: dynamic codes block%s\n", + // state.last ? " (last)" : "")); + state.mode = TABLE; + case 3: + strm.msg = 'invalid block type'; + state.mode = BAD; + } + //--- DROPBITS(2) ---// + hold >>>= 2; + bits -= 2; + //---// + case STORED: + //--- BYTEBITS() ---// /* go to byte boundary */ + hold >>>= bits & 7; + bits -= bits & 7; + //---// + //=== NEEDBITS(32); */ + while (bits < 32) { + if (have == 0) { + inf_leave = true; + break; //out of this while + } + have--; + hold += input[next++] << bits; + bits += 8; + } + if (inf_leave) break; //inf_leave + //===// + if ((hold & 0xffff) != ((hold >>> 16) ^ 0xffff)) { + strm.msg = 'invalid stored block lengths'; + state.mode = BAD; + continue; //inf_leave + } + state.length = hold & 0xffff; + //Tracev((stderr, "inflate: stored length %u\n", + // state.length)); + //=== INITBITS(); + hold = 0; + bits = 0; + //===// + state.mode = COPY_; + if (flush == Flush.Z_TREES) { + inf_leave = true; + break; //inf_leave + } + /* falls through */ + case COPY_: + state.mode = COPY; + /* falls through */ + case COPY: + copy = state.length; + if (copy != 0) { + if (copy > have) { copy = have; } + if (copy > left) { copy = left; } + if (copy == 0) { + inf_leave = true; + break; //inf_leave + } + //--- zmemcpy(put, next, copy); --- + Common.arraySet(cast output, cast input, next, copy, put); + //---// + have -= copy; + next += copy; + left -= copy; + put += copy; + state.length -= copy; + continue; //inf_leave + } + //Tracev((stderr, "inflate: stored end\n")); + state.mode = TYPE; + case TABLE: + //=== NEEDBITS(14); */ + while (bits < 14) { + if (have == 0) { + inf_leave = true; + break; //out of this while + } + have--; + hold += input[next++] << bits; + bits += 8; + } + if (inf_leave) break; //inf_leave + //===// + state.nlen = (hold & 0x1f)/*BITS(5)*/ + 257; + //--- DROPBITS(5) ---// + hold >>>= 5; + bits -= 5; + //---// + state.ndist = (hold & 0x1f)/*BITS(5)*/ + 1; + //--- DROPBITS(5) ---// + hold >>>= 5; + bits -= 5; + //---// + state.ncode = (hold & 0x0f)/*BITS(4)*/ + 4; + //--- DROPBITS(4) ---// + hold >>>= 4; + bits -= 4; + //---// + //#ifndef PKZIP_BUG_WORKAROUND + if (state.nlen > 286 || state.ndist > 30) { + strm.msg = 'too many length or distance symbols'; + state.mode = BAD; + continue; //inf_leave + } + //#endif + //Tracev((stderr, "inflate: table sizes ok\n")); + state.have = 0; + state.mode = LENLENS; + /* falls through */ + case LENLENS: + while (state.have < state.ncode) { + //=== NEEDBITS(3); + while (bits < 3) { + if (have == 0) { + inf_leave = true; + break; //out of inner while + } + have--; + hold += input[next++] << bits; + bits += 8; + } + if (inf_leave) break; //out of outer while + //===// + state.lens[order[state.have++]] = (hold & 0x07);//BITS(3); + //--- DROPBITS(3) ---// + hold >>>= 3; + bits -= 3; + //---// + } + if (inf_leave) break; //inf_leave + while (state.have < 19) { + state.lens[order[state.have++]] = 0; + } + // We have separate tables & no pointers. 2 commented lines below not needed. + //state.next = state.codes; + //state.lencode = state.next; + // Switch to use dynamic table + state.lencode = state.lendyn; + state.lenbits = 7; + + opts = {bits: state.lenbits}; + ret = InfTrees.inflate_table(CODES, state.lens, 0, 19, state.lencode, 0, state.work, opts); + state.lenbits = opts.bits; + + if (ret != ErrorStatus.Z_OK) { + strm.msg = 'invalid code lengths set'; + state.mode = BAD; + continue; //inf_leave + } + //Tracev((stderr, "inflate: code lengths ok\n")); + state.have = 0; + state.mode = CODELENS; + /* falls through */ + case CODELENS: + while (state.have < state.nlen + state.ndist) { + while (true) { + here = state.lencode[hold & ((1 << state.lenbits) - 1)];/*BITS(state.lenbits)*/ + here_bits = here >>> 24; + here_op = (here >>> 16) & 0xff; + here_val = here & 0xffff; + + if ((here_bits) <= bits) break; //out of inner while + //--- PULLBYTE() ---// + if (have == 0) { + inf_leave = true; + break; //out of inner while + } + have--; + hold += input[next++] << bits; + bits += 8; + //---// + } + if (inf_leave) break; //out of outer while + if (here_val < 16) { + //--- DROPBITS(here.bits) ---// + hold = hold >>> here_bits; + bits -= here_bits; + //---// + state.lens[state.have++] = here_val; + } + else { + if (here_val == 16) { + //=== NEEDBITS(here.bits + 2); + n = here_bits + 2; + while (bits < n) { + if (have == 0) { + inf_leave = true; + break; //out of this while + } + have--; + hold += input[next++] << bits; + bits += 8; + } + if (inf_leave) break; //out of outer while + //===// + //--- DROPBITS(here.bits) ---// + hold >>>= here_bits; + bits -= here_bits; + //---// + if (state.have == 0) { + strm.msg = 'invalid bit length repeat'; + state.mode = BAD; + break; //out of outer while + } + len = state.lens[state.have - 1]; + copy = 3 + (hold & 0x03);//BITS(2); + //--- DROPBITS(2) ---// + hold >>>= 2; + bits -= 2; + //---// + } + else if (here_val == 17) { + //=== NEEDBITS(here.bits + 3); + n = here_bits + 3; + while (bits < n) { + if (have == 0) { + inf_leave = true; + break; //out of this while + } + have--; + hold += input[next++] << bits; + bits += 8; + } + if (inf_leave) break; //out of outer while + //===// + //--- DROPBITS(here.bits) ---// + hold >>>= here_bits; + bits -= here_bits; + //---// + len = 0; + copy = 3 + (hold & 0x07);//BITS(3); + //--- DROPBITS(3) ---// + hold >>>= 3; + bits -= 3; + //---// + } + else { + //=== NEEDBITS(here.bits + 7); + n = here_bits + 7; + while (bits < n) { + if (have == 0) { + inf_leave = true; + break; //out of this while + } + have--; + hold += input[next++] << bits; + bits += 8; + } + if (inf_leave) break; //out of outer while + //===// + //--- DROPBITS(here.bits) ---// + hold >>>= here_bits; + bits -= here_bits; + //---// + len = 0; + copy = 11 + (hold & 0x7f);//BITS(7); + //--- DROPBITS(7) ---// + hold >>>= 7; + bits -= 7; + //---// + } + if (state.have + copy > state.nlen + state.ndist) { + strm.msg = 'invalid bit length repeat'; + state.mode = BAD; + break; //out of outer while + } + while (copy-- != 0) { + state.lens[state.have++] = len; + } + } + } + + /* handle error breaks in while */ + if (inf_leave || state.mode == BAD) continue; //inf_leave + + /* check for end-of-block code (better have one) */ + if (state.lens[256] == 0) { + strm.msg = 'invalid code -- missing end-of-block'; + state.mode = BAD; + continue; //inf_leave + } + + /* build code tables -- note: do not change the lenbits or distbits + values here (9 and 6) without reading the comments in inftrees.h + concerning the ENOUGH constants, which depend on those values */ + state.lenbits = 9; + + opts = {bits: state.lenbits}; + ret = InfTrees.inflate_table(LENS, state.lens, 0, state.nlen, state.lencode, 0, state.work, opts); + // We have separate tables & no pointers. 2 commented lines below not needed. + // state.next_index = opts.table_index; + state.lenbits = opts.bits; + // state.lencode = state.next; + + if (ret != ErrorStatus.Z_OK) { + strm.msg = 'invalid literal/lengths set'; + state.mode = BAD; + continue; //inf_leave + } + + state.distbits = 6; + //state.distcode.copy(state.codes); + // Switch to use dynamic table + state.distcode = state.distdyn; + opts = {bits: state.distbits}; + ret = InfTrees.inflate_table(DISTS, state.lens, state.nlen, state.ndist, state.distcode, 0, state.work, opts); + // We have separate tables & no pointers. 2 commented lines below not needed. + // state.next_index = opts.table_index; + state.distbits = opts.bits; + // state.distcode = state.next; + + if (ret != ErrorStatus.Z_OK) { + strm.msg = 'invalid distances set'; + state.mode = BAD; + continue; //inf_leave + } + //Tracev((stderr, 'inflate: codes ok\n')); + state.mode = LEN_; + if (flush == Flush.Z_TREES) { + inf_leave = true; + continue; //inf_leave + } + /* falls through */ + case LEN_: + state.mode = LEN; + /* falls through */ + case LEN: + if (have >= 6 && left >= 258) { + //--- RESTORE() --- + strm.next_out = put; + strm.avail_out = left; + strm.next_in = next; + strm.avail_in = have; + state.hold = hold; + state.bits = bits; + //--- + InfFast.inflate_fast(strm, _out); + //--- LOAD() --- + put = strm.next_out; + output = strm.output; + left = strm.avail_out; + next = strm.next_in; + input = strm.input; + have = strm.avail_in; + hold = state.hold; + bits = state.bits; + //--- + + if (state.mode == TYPE) { + state.back = -1; + } + continue; //inf_leave + } + state.back = 0; + while (true) { + here = state.lencode[hold & ((1 << state.lenbits) -1)]; /*BITS(state.lenbits)*/ + here_bits = here >>> 24; + here_op = (here >>> 16) & 0xff; + here_val = here & 0xffff; + + if (here_bits <= bits) { break; } + //--- PULLBYTE() ---// + if (have == 0) { + inf_leave = true; + break; //out of this while + } + have--; + hold += input[next++] << bits; + bits += 8; + //---// + } + if (inf_leave) continue; //inf_leave + if (here_op != 0 && (here_op & 0xf0) == 0) { + last_bits = here_bits; + last_op = here_op; + last_val = here_val; + while (true) { + here = state.lencode[last_val + + ((hold & ((1 << (last_bits + last_op)) -1))/*BITS(last.bits + last.op)*/ >> last_bits)]; + here_bits = here >>> 24; + here_op = (here >>> 16) & 0xff; + here_val = here & 0xffff; + + if ((last_bits + here_bits) <= bits) { break; } + //--- PULLBYTE() ---// + if (have == 0) { + inf_leave = true; + break; //out of this while + } + have--; + hold += input[next++] << bits; + bits += 8; + //---// + } + if (inf_leave) continue; //inf_leave + //--- DROPBITS(last.bits) ---// + hold >>>= last_bits; + bits -= last_bits; + //---// + state.back += last_bits; + } + //--- DROPBITS(here.bits) ---// + hold >>>= here_bits; + bits -= here_bits; + //---// + state.back += here_bits; + state.length = here_val; + if (here_op == 0) { + //Tracevv((stderr, here.val >= 0x20 && here.val < 0x7f ? + // "inflate: literal '%c'\n" : + // "inflate: literal 0x%02x\n", here.val)); + state.mode = LIT; + continue; //inf_leave + } + if (here_op & 32 != 0) { + //Tracevv((stderr, "inflate: end of block\n")); + state.back = -1; + state.mode = TYPE; + continue; //inf_leave + } + if (here_op & 64 != 0) { + strm.msg = 'invalid literal/length code'; + state.mode = BAD; + continue; //inf_leave + } + state.extra = here_op & 15; + state.mode = LENEXT; + /* falls through */ + case LENEXT: + if (state.extra != 0) { + //=== NEEDBITS(state.extra); + n = state.extra; + while (bits < n) { + if (have == 0) { + inf_leave = true; + break; //out of this while + } + have--; + hold += input[next++] << bits; + bits += 8; + } + if (inf_leave) continue; //inf_leave + //===// + state.length += hold & ((1 << state.extra) -1)/*BITS(state.extra)*/; + //--- DROPBITS(state.extra) ---// + hold >>>= state.extra; + bits -= state.extra; + //---// + state.back += state.extra; + } + //Tracevv((stderr, "inflate: length %u\n", state.length)); + state.was = state.length; + state.mode = DIST; + /* falls through */ + case DIST: + while (true) { + here = state.distcode[hold & ((1 << state.distbits) -1)];/*BITS(state.distbits)*/ + here_bits = here >>> 24; + here_op = (here >>> 16) & 0xff; + here_val = here & 0xffff; + + if ((here_bits) <= bits) { break; } + //--- PULLBYTE() ---// + if (have == 0) { + inf_leave = true; + break; //out of this while + } + have--; + hold += input[next++] << bits; + bits += 8; + //---// + } + if (inf_leave) continue; //inf_leave + if ((here_op & 0xf0) == 0) { + last_bits = here_bits; + last_op = here_op; + last_val = here_val; + while (true) { + here = state.distcode[last_val + + ((hold & ((1 << (last_bits + last_op)) -1))/*BITS(last.bits + last.op)*/ >> last_bits)]; + here_bits = here >>> 24; + here_op = (here >>> 16) & 0xff; + here_val = here & 0xffff; + + if ((last_bits + here_bits) <= bits) { break; } + //--- PULLBYTE() ---// + if (have == 0) { + inf_leave = true; + break; //out of this while + } + have--; + hold += input[next++] << bits; + bits += 8; + //---// + } + if (inf_leave) continue; //inf_leave + //--- DROPBITS(last.bits) ---// + hold >>>= last_bits; + bits -= last_bits; + //---// + state.back += last_bits; + } + //--- DROPBITS(here.bits) ---// + hold >>>= here_bits; + bits -= here_bits; + //---// + state.back += here_bits; + if (here_op & 64 != 0) { + strm.msg = 'invalid distance code'; + state.mode = BAD; + continue; //inf_leave + } + state.offset = here_val; + state.extra = (here_op) & 15; + state.mode = DISTEXT; + /* falls through */ + case DISTEXT: + if (state.extra != 0) { + //=== NEEDBITS(state.extra); + n = state.extra; + while (bits < n) { + if (have == 0) { + inf_leave = true; + break; //out of this while + } + have--; + hold += input[next++] << bits; + bits += 8; + } + if (inf_leave) continue; //inf_leave + //===// + state.offset += hold & ((1 << state.extra) -1)/*BITS(state.extra)*/; + //--- DROPBITS(state.extra) ---// + hold >>>= state.extra; + bits -= state.extra; + //---// + state.back += state.extra; + } + //#ifdef INFLATE_STRICT + if (state.offset > state.dmax) { + strm.msg = 'invalid distance too far back'; + state.mode = BAD; + continue; //inf_leave + } + //#endif + //Tracevv((stderr, "inflate: distance %u\n", state.offset)); + state.mode = MATCH; + /* falls through */ + case MATCH: + if (left == 0) { + inf_leave = true; + continue; //inf_leave + } + copy = _out - left; + if (state.offset > copy) { /* copy from window */ + copy = state.offset - copy; + if (copy > state.whave) { + if (state.sane != 0) { + strm.msg = 'invalid distance too far back'; + state.mode = BAD; + continue; //inf_leave + } + // (!) This block is disabled in zlib defailts, + // don't enable it for binary compatibility + //#ifdef INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR + // Trace((stderr, "inflate.c too far\n")); + // copy -= state.whave; + // if (copy > state.length) { copy = state.length; } + // if (copy > left) { copy = left; } + // left -= copy; + // state.length -= copy; + // do { + // output[put++] = 0; + // } while (--copy); + // if (state.length === 0) { state.mode = LEN; } + // break; + //#endif + } + if (copy > state.wnext) { + copy -= state.wnext; + from = state.wsize - copy; + } + else { + from = state.wnext - copy; + } + if (copy > state.length) { copy = state.length; } + from_source = state.window; + } + else { /* copy from output */ + from_source = output; + from = put - state.offset; + copy = state.length; + } + if (copy > left) { copy = left; } + left -= copy; + state.length -= copy; + do { + output[put++] = from_source[from++]; + } while (--copy != 0); + if (state.length == 0) { state.mode = LEN; } + case LIT: + if (left == 0) { + inf_leave = true; + continue; //inf_leave + } + output[put++] = state.length; + left--; + state.mode = LEN; + case CHECK: + if (state.wrap != 0) { + //=== NEEDBITS(32); + while (bits < 32) { + if (have == 0) { + inf_leave = true; + break; //out of this while + } + have--; + // Use '|' insdead of '+' to make sure that result is signed + hold |= input[next++] << bits; + bits += 8; + } + if (inf_leave) continue; //inf_leave + //===// + _out -= left; + strm.total_out += _out; + state.total += _out; + if (_out != 0) { + strm.adler = state.check = + /*UPDATE(state.check, put - _out, _out);*/ + (state.flags != 0 ? CRC32.crc32(state.check, output, _out, put - _out) : Adler32.adler32(state.check, output, _out, put - _out)); + + } + _out = left; + //NOTE(hx): force-clamp to 32bits (needed f.e. on python) + hold = @:privateAccess haxe.Int32.clamp(hold); + // NB: crc32 stored as signed 32-bit int, ZSWAP32 returns signed too + if ((state.flags != 0 ? hold : ZSWAP32(hold)) != state.check) { + strm.msg = 'incorrect data check'; + state.mode = BAD; + continue; //inf_leave + } + //=== INITBITS(); + hold = 0; + bits = 0; + //===// + //Tracev((stderr, "inflate: check matches trailer\n")); + } + state.mode = LENGTH; + /* falls through */ + case LENGTH: + if (state.wrap != 0 && state.flags != 0) { + //=== NEEDBITS(32); + while (bits < 32) { + if (have == 0) { + inf_leave = true; + break; //out of this while + } + have--; + hold += input[next++] << bits; + bits += 8; + } + if (inf_leave) continue; //inf_leave + //===// + if (hold != (state.total & 0xffffffff)) { + strm.msg = 'incorrect length check'; + state.mode = BAD; + continue; //inf_leave + } + //=== INITBITS(); + hold = 0; + bits = 0; + //===// + //Tracev((stderr, "inflate: length matches trailer\n")); + } + state.mode = DONE; + /* falls through */ + case DONE: + ret = ErrorStatus.Z_STREAM_END; + inf_leave = true; + continue; //inf_leave + case BAD: + ret = ErrorStatus.Z_DATA_ERROR; + inf_leave = true; + continue; //inf_leave + case MEM: + return ErrorStatus.Z_MEM_ERROR; + case SYNC: + return ErrorStatus.Z_STREAM_ERROR; + default: + return ErrorStatus.Z_STREAM_ERROR; + } + } + + // inf_leave <- here is real place for "goto inf_leave", emulated via inline and returns + + /* + Return from inflate(), updating the total counts and the check value. + If there was no progress during the inflate() call, return a buffer + error. Call updatewindow() to create and/or update the window state. + Note: a memory error from inflate() is non-recoverable. + */ + + //--- RESTORE() --- + strm.next_out = put; + strm.avail_out = left; + strm.next_in = next; + strm.avail_in = have; + state.hold = hold; + state.bits = bits; + //--- + + if (state.wsize != 0 || (_out != strm.avail_out && state.mode < BAD && + (state.mode < CHECK || flush != Flush.Z_FINISH))) { + if (updatewindow(strm, strm.output, strm.next_out, _out - strm.avail_out) != 0) { + state.mode = MEM; + return ErrorStatus.Z_MEM_ERROR; + } + } + _in -= strm.avail_in; + _out -= strm.avail_out; + strm.total_in += _in; + strm.total_out += _out; + state.total += _out; + if (state.wrap != 0 && _out != 0) { + strm.adler = state.check = /*UPDATE(state.check, strm.next_out - _out, _out);*/ + (state.flags != 0 ? CRC32.crc32(state.check, output, _out, strm.next_out - _out) : Adler32.adler32(state.check, output, _out, strm.next_out - _out)); + } + strm.data_type = state.bits + (state.last ? 64 : 0) + + (state.mode == TYPE ? 128 : 0) + + (state.mode == LEN_ || state.mode == COPY_ ? 256 : 0); + if (((_in == 0 && _out == 0) || flush == Flush.Z_FINISH) && ret == ErrorStatus.Z_OK) { + ret = ErrorStatus.Z_BUF_ERROR; + } + return ret; + } + + static public function inflateEnd(?strm:ZStream) { + + if (strm == null || strm.inflateState == null /*|| strm->zfree == (free_func)0*/) { + return ErrorStatus.Z_STREAM_ERROR; + } + + var state = strm.inflateState; + if (state.window != null) { + state.window = null; + } + strm.inflateState = null; + return ErrorStatus.Z_OK; + } + + static public function inflateGetHeader(strm:ZStream, head) { + var state:InflateState; + + /* check state */ + if (strm == null || strm.inflateState == null) { return ErrorStatus.Z_STREAM_ERROR; } + state = strm.inflateState; + if ((state.wrap & 2) == 0) { return ErrorStatus.Z_STREAM_ERROR; } + + /* save header structure */ + state.head = head; + head.done = false; + return ErrorStatus.Z_OK; + } + + static public function inflateSetDictionary(strm:ZStream, dictionary:UInt8Array) { + var dictLength = dictionary.length; + + var state:InflateState; + var dictid:Int; + var ret; + + /* check state */ + if (strm == null/* == Z_NULL */ || strm.inflateState == null/* == Z_NULL */) { return ErrorStatus.Z_STREAM_ERROR; } + state = strm.inflateState; + + if (state.wrap != 0 && state.mode != DICT) { + return ErrorStatus.Z_STREAM_ERROR; + } + + /* check for correct dictionary identifier */ + if (state.mode == DICT) { + dictid = 1; /* adler32(0, null, 0)*/ + /* dictid = adler32(dictid, dictionary, dictLength); */ + dictid = Adler32.adler32(dictid, dictionary, dictLength, 0); + if (dictid != state.check) { + return ErrorStatus.Z_DATA_ERROR; + } + } + /* copy dictionary to window using updatewindow(), which will amend the + existing dictionary if appropriate */ + ret = updatewindow(strm, dictionary, dictLength, dictLength); + if (ret != 0) { + state.mode = MEM; + return ErrorStatus.Z_MEM_ERROR; + } + state.havedict = true; + // Tracev((stderr, "inflate: dictionary set\n")); + return ErrorStatus.Z_OK; + } +} + + +@:allow(iron.format.pako.zlib.Inflate) +@:allow(iron.format.pako.zlib.InfFast) +class InflateState +{ + var mode:Int = 0; /* current inflate mode */ + var last:Bool = false; /* true if processing last block */ + var wrap:Int = 0; /* bit 0 true for zlib, bit 1 true for gzip */ + var havedict:Bool = false; /* true if dictionary provided */ + var flags:Int = 0; /* gzip header method and flags (0 if zlib) */ + var dmax:Int = 0; /* zlib header max distance (INFLATE_STRICT) */ + var check:Int = 0; /* protected copy of check value */ + var total:Int = 0; /* protected copy of output count */ + // TODO: may be {} + var head:GZHeader = null; /* where to save gzip header information */ + + /* sliding window */ + var wbits:Int = 0; /* log base 2 of requested window size */ + var wsize:Int = 0; /* window size or zero if not using window */ + var whave:Int = 0; /* valid bytes in the window */ + var wnext:Int = 0; /* window write index */ + var window:UInt8Array= null; /* allocated sliding window, if needed */ + + /* bit accumulator */ + var hold:Int = 0; /* input bit accumulator */ + var bits:Int = 0; /* number of bits in "in" */ + + /* for string and stored block copying */ + var length:Int = 0; /* literal or length of data to copy */ + var offset:Int = 0; /* distance back to copy string from */ + + /* for table and code decoding */ + var extra:Int = 0; /* extra bits needed */ + + /* fixed and dynamic code tables */ + var lencode:Int32Array = null; /* starting table for length/literal codes */ + var distcode:Int32Array = null; /* starting table for distance codes */ + var lenbits:Int = 0; /* index bits for lencode */ + var distbits:Int = 0; /* index bits for distcode */ + + /* dynamic table building */ + var ncode:Int = 0; /* number of code length code lengths */ + var nlen:Int = 0; /* number of length code lengths */ + var ndist:Int = 0; /* number of distance code lengths */ + var have = 0; /* number of code lengths in lens[] */ + var next = null; /* next available space in codes[] */ + + var lens:UInt16Array = new UInt16Array(320); /* temporary storage for code lengths */ + var work:UInt16Array = new UInt16Array(288); /* work area for code table building */ + + /* + because we don't have pointers in js, we use lencode and distcode directly + as buffers so we don't need codes + */ + //var codes = new Common.Buf32(ENOUGH); /* space for code tables */ + var lendyn:Int32Array = null; /* dynamic table for length/literal codes (JS specific) */ + var distdyn:Int32Array = null; /* dynamic table for distance codes (JS specific) */ + var sane:Int = 0; /* if false, allow invalid distance too far */ + var back:Int = 0; /* bits back of last unprocessed length/lit */ + var was:Int = 0; /* initial length of match */ + + function new() { } +} + +/*exports.inflateReset = inflateReset; +exports.inflateReset2 = inflateReset2; +exports.inflateResetKeep = inflateResetKeep; +exports.inflateInit = inflateInit; +exports.inflateInit2 = inflateInit2; +exports.inflate = inflate; +exports.inflateEnd = inflateEnd; +exports.inflateGetHeader = inflateGetHeader; +exports.inflateSetDictionary = inflateSetDictionary; +exports.inflateInfo = 'pako inflate (from Nodeca project)'; +*/ + +/* Not implemented +exports.inflateCopy = inflateCopy; +exports.inflateGetDictionary = inflateGetDictionary; +exports.inflateMark = inflateMark; +exports.inflatePrime = inflatePrime; +exports.inflateSync = inflateSync; +exports.inflateSyncPoint = inflateSyncPoint; +exports.inflateUndermine = inflateUndermine; +*/ diff --git a/Libraries/iron_format/Sources/iron/format/pako/zlib/Messages.hx b/Libraries/iron_format/Sources/iron/format/pako/zlib/Messages.hx new file mode 100644 index 00000000..53999f69 --- /dev/null +++ b/Libraries/iron_format/Sources/iron/format/pako/zlib/Messages.hx @@ -0,0 +1,24 @@ +package iron.format.pako.zlib; + +import haxe.ds.IntMap; +import iron.format.pako.zlib.Constants; + +//NOTE(hx): might as well refactor Error to be over string +class Messages +{ + static var map:IntMap = [ + ErrorStatus.Z_NEED_DICT => 'need dictionary', /* Z_NEED_DICT 2 */ + ErrorStatus.Z_STREAM_END => 'stream end', /* Z_STREAM_END 1 */ + ErrorStatus.Z_OK => '', /* Z_OK 0 */ + ErrorStatus.Z_ERRNO => 'file error', /* Z_ERRNO (-1) */ + ErrorStatus.Z_STREAM_ERROR => 'stream error', /* Z_STREAM_ERROR (-2) */ + ErrorStatus.Z_DATA_ERROR => 'data error', /* Z_DATA_ERROR (-3) */ + ErrorStatus.Z_MEM_ERROR => 'insufficient memory', /* Z_MEM_ERROR (-4) */ + ErrorStatus.Z_BUF_ERROR => 'buffer error', /* Z_BUF_ERROR (-5) */ + ErrorStatus.Z_VERSION_ERROR => 'incompatible version' /* Z_VERSION_ERROR (-6) */ + ]; + + static public function get(error:Int) { + return "ERROR: " + map.get(error); + } +} diff --git a/Libraries/iron_format/Sources/iron/format/pako/zlib/Trees.hx b/Libraries/iron_format/Sources/iron/format/pako/zlib/Trees.hx new file mode 100644 index 00000000..87283b75 --- /dev/null +++ b/Libraries/iron_format/Sources/iron/format/pako/zlib/Trees.hx @@ -0,0 +1,1249 @@ +package iron.format.pako.zlib; + +import haxe.ds.Vector; +import haxe.io.ArrayBufferView; +import haxe.io.UInt16Array; +import iron.format.pako.zlib.Constants; +import iron.format.pako.utils.Common; +import iron.format.pako.zlib.Deflate.DeflateState; + + +/* Public constants ==========================================================*/ +/* ===========================================================================*/ + +/* +//var Z_FILTERED = 1; +//var Z_HUFFMAN_ONLY = 2; +//var Z_RLE = 3; +var Z_FIXED = 4; +//var Z_DEFAULT_STRATEGY = 0; +*/ + +/* Possible values of the data_type field (though see inflate()) */ +/*var Z_BINARY = 0; +var Z_TEXT = 1; +//var Z_ASCII = 1; // = Z_TEXT +var Z_UNKNOWN = 2; +*/ +/*============================================================================*/ + + +// From zutil.h + +/* The minimum and maximum match lengths */ +@:enum abstract MatchLength(Int) to Int +{ + var MIN_MATCH = 3; + var MAX_MATCH = 258; +} + +// From deflate.h +/* =========================================================================== + * Internal compression state. + */ + +class Trees +{ + /* The three kinds of block type */ + static inline public var STORED_BLOCK:Int = 0; + static inline public var STATIC_TREES:Int = 1; + static inline public var DYN_TREES:Int = 2; + + static inline var LENGTH_CODES = 29; + /* number of length codes, not counting the special END_BLOCK code */ + + static inline var LITERALS = 256; + /* number of literal bytes 0..255 */ + + static inline var L_CODES = LITERALS + 1 + LENGTH_CODES; + /* number of Literal or Length codes, including the END_BLOCK code */ + + static inline var D_CODES = 30; + /* number of distance codes */ + + static inline var BL_CODES = 19; + /* number of codes used to transfer the bit lengths */ + + static inline var HEAP_SIZE = 2*L_CODES + 1; + /* maximum heap size */ + + static inline var MAX_BITS = 15; + /* All codes must not exceed MAX_BITS bits */ + + static inline var Buf_size = 16; + /* size of bit buffer in bi_buf */ + + + /* =========================================================================== + * Constants + */ + + static inline var MAX_BL_BITS = 7; + /* Bit length codes must not exceed MAX_BL_BITS bits */ + + static inline var END_BLOCK = 256; + /* end of block literal code */ + + static inline var REP_3_6 = 16; + /* repeat previous bit length 3-6 times (2 bits of repeat count) */ + + static inline var REPZ_3_10 = 17; + /* repeat a zero length 3-10 times (3 bits of repeat count) */ + + static inline var REPZ_11_138 = 18; + /* repeat a zero length 11-138 times (7 bits of repeat count) */ + + static var extra_lbits = /* extra bits for each length code */ + UInt16Array.fromArray([0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0]); + + static var extra_dbits = /* extra bits for each distance code */ + UInt16Array.fromArray([0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13]); + + static var extra_blbits = /* extra bits for each bit length code */ + UInt16Array.fromArray([0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,7]); + + static var bl_order = + UInt16Array.fromArray([16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15]); + /* The lengths of the bit length codes are sent in order of decreasing + * probability, to avoid transmitting the lengths for unused bit length codes. + */ + + /* =========================================================================== + * Local data. These are initialized only once. + */ + + // We pre-fill arrays with 0 to avoid uninitialized gaps + + static inline var DIST_CODE_LEN = 512; /* see definition of array dist_code below */ + + // !!!! Use flat array insdead of structure, Freq = i*2, Len = i*2+1 + static var static_ltree:UInt16Array; + /* The static literal tree. Since the bit lengths are imposed, there is no + * need for the L_CODES extra codes used during heap construction. However + * The codes 286 and 287 are needed to build a canonical tree (see _tr_init + * below). + */ + + static var static_dtree:UInt16Array; + /* The static distance tree. (Actually a trivial tree since all codes use + * 5 bits.) + */ + + static var _dist_code:UInt16Array; + /* Distance codes. The first 256 values correspond to the distances + * 3 .. 258, the last 256 values correspond to the top 8 bits of + * the 15 bit distances. + */ + + static var _length_code:UInt16Array; + /* length code for each normalized match length (0 == MIN_MATCH) */ + + static var base_length:UInt16Array; + /* First normalized length for each code (0 = MIN_MATCH) */ + + static var base_dist:UInt16Array; + /* First normalized distance for each code (0 = distance of 1) */ + + static function __init__() { + static_ltree = new UInt16Array((L_CODES + 2) * 2); + static_dtree = new UInt16Array(D_CODES * 2); + _dist_code = new UInt16Array(DIST_CODE_LEN); + _length_code = new UInt16Array(MAX_MATCH - MIN_MATCH + 1); + base_length = new UInt16Array(LENGTH_CODES); + base_dist = new UInt16Array(D_CODES); + + Common.zero(cast static_ltree); + Common.zero(cast static_dtree); + Common.zero(cast _dist_code); + Common.zero(cast _length_code); + Common.zero(cast base_length); + Common.zero(cast base_dist); + } + + //NOTE(hx): StaticTreeDesc and TreeDesc moved to end of file + + static var static_l_desc:StaticTreeDesc; + static var static_d_desc:StaticTreeDesc; + static var static_bl_desc:StaticTreeDesc; + + + static inline function d_code(dist) { + return dist < 256 ? _dist_code[dist] : _dist_code[256 + (dist >>> 7)]; + } + + + /* =========================================================================== + * Output a short LSB first on the stream. + * IN assertion: there is enough room in pendingBuf. + */ + static inline function put_short (s:DeflateState, w:Int) { + // put_byte(s, (uch)((w) & 0xff)); + // put_byte(s, (uch)((ush)(w) >> 8)); + s.pending_buf[s.pending++] = (w) & 0xff; + s.pending_buf[s.pending++] = (w >>> 8) & 0xff; + } + + + /* =========================================================================== + * Send a value on a given number of bits. + * IN assertion: length <= 16 and value fits in length bits. + */ + static function send_bits(s:DeflateState, value:Int, length:Int) { + if (s.bi_valid > (Buf_size - length)) { + s.bi_buf |= (value << s.bi_valid) & 0xffff; + put_short(s, s.bi_buf); + s.bi_buf = value >> (Buf_size - s.bi_valid); + s.bi_valid += length - Buf_size; + } else { + s.bi_buf |= (value << s.bi_valid) & 0xffff; + s.bi_valid += length; + } + } + + + static inline function send_code(s:DeflateState, c, tree:UInt16Array) { + send_bits(s, tree[c*2]/*.Code*/, tree[c*2 + 1]/*.Len*/); + } + + + /* =========================================================================== + * Reverse the first len bits of a code, using straightforward code (a faster + * method would use a table) + * IN assertion: 1 <= len <= 15 + */ + static function bi_reverse(code:Int, len:Int):Int { + var res = 0; + do { + res |= code & 1; + code >>>= 1; + res <<= 1; + } while (--len > 0); + return res >>> 1; + } + + + /* =========================================================================== + * Flush the bit buffer, keeping at most 7 bits in it. + */ + static function bi_flush(s:DeflateState) { + if (s.bi_valid == 16) { + put_short(s, s.bi_buf); + s.bi_buf = 0; + s.bi_valid = 0; + + } else if (s.bi_valid >= 8) { + s.pending_buf[s.pending++] = s.bi_buf & 0xff; + s.bi_buf >>= 8; + s.bi_valid -= 8; + } + } + + + /* =========================================================================== + * Compute the optimal bit lengths for a tree and update the total bit length + * for the current block. + * IN assertion: the fields freq and dad are set, heap[heap_max] and + * above are the tree nodes sorted by increasing frequency. + * OUT assertions: the field len is set to the optimal bit length, the + * array bl_count contains the frequencies for each bit length. + * The length opt_len is updated; static_len is also updated if stree is + * not null. + */ + static function gen_bitlen(s:DeflateState, desc:TreeDesc) + // deflate_state *s; + // tree_desc *desc; /* the tree descriptor */ + { + var tree:UInt16Array = desc.dyn_tree; + var max_code = desc.max_code; + var stree:UInt16Array = desc.stat_desc.static_tree; + var has_stree = desc.stat_desc.has_stree; + var extra:UInt16Array = desc.stat_desc.extra_bits; + var base = desc.stat_desc.extra_base; + var max_length = desc.stat_desc.max_length; + var h; /* heap index */ + var n, m; /* iterate over the tree elements */ + var bits; /* bit length */ + var xbits; /* extra bits */ + var f; /* frequency */ + var overflow = 0; /* number of elements with bit length too large */ + + for (bits in 0...MAX_BITS + 1) { + s.bl_count[bits] = 0; + } + + /* In a first pass, compute the optimal bit lengths (which may + * overflow in the case of the bit length tree). + */ + tree[s.heap[s.heap_max]*2 + 1]/*.Len*/ = 0; /* root of the heap */ + + for (h in s.heap_max+1...HEAP_SIZE) { + n = s.heap[h]; + bits = tree[tree[n*2 +1]/*.Dad*/ * 2 + 1]/*.Len*/ + 1; + if (bits > max_length) { + bits = max_length; + overflow++; + } + tree[n*2 + 1]/*.Len*/ = bits; + /* We overwrite tree[n].Dad which is no longer needed */ + + if (n > max_code) { continue; } /* not a leaf node */ + + s.bl_count[bits]+=1; + xbits = 0; + if (n >= base) { + xbits = extra[n-base]; + } + f = tree[n * 2]/*.Freq*/; + s.opt_len += f * (bits + xbits); + if (has_stree) { + s.static_len += f * (stree[n*2 + 1]/*.Len*/ + xbits); + } + } + if (overflow == 0) { return; } + h = HEAP_SIZE; // NOTE(hx): set h to last value + + // Trace((stderr,"\nbit length overflow\n")); + /* This happens for example on obj2 and pic of the Calgary corpus */ + + /* Find the first bit length which could increase: */ + do { + bits = max_length-1; + while (s.bl_count[bits] == 0) { bits--; } + s.bl_count[bits]-=1; /* move one leaf down the tree */ + s.bl_count[bits+1] += 2; /* move one overflow item as its brother */ + s.bl_count[max_length]-=1; + /* The brother of the overflow item also moves one step up, + * but this does not affect bl_count[max_length] + */ + overflow -= 2; + } while (overflow > 0); + + /* Now recompute all bit lengths, scanning in increasing frequency. + * h is still equal to HEAP_SIZE. (It is simpler to reconstruct all + * lengths instead of fixing only the wrong ones. This idea is taken + * from 'ar' written by Haruhiko Okumura.) + */ + //NOTE(hx): check loop and continue + bits = max_length; + while (bits != 0) { + n = s.bl_count[bits]; + while (n != 0) { + m = s.heap[--h]; + if (m > max_code) { continue; } + if (tree[m*2 + 1]/*.Len*/ != bits) { + // Trace((stderr,"code %d bits %d->%d\n", m, tree[m].Len, bits)); + s.opt_len += (bits - tree[m*2 + 1]/*.Len*/)*tree[m*2]/*.Freq*/; + tree[m*2 + 1]/*.Len*/ = bits; + } + n--; + } + bits--; + } + } + + + /* =========================================================================== + * Generate the codes for a given tree and bit counts (which need not be + * optimal). + * IN assertion: the array bl_count contains the bit length statistics for + * the given tree and the field len is set for all tree elements. + * OUT assertion: the field code is set for all tree elements of non + * zero code length. + */ + static function gen_codes(tree:UInt16Array, max_code:Int, bl_count:UInt16Array) + // ct_data *tree; /* the tree to decorate */ + // int max_code; /* largest code with non zero frequency */ + // ushf *bl_count; /* number of codes at each bit length */ + { + var next_code = new Vector(MAX_BITS+1); /* next code value for each bit length */ + var code:Int = 0; /* running code value */ + var bits:Int; /* bit index */ + var n:Int; /* code index */ + + /* The distribution counts are first used to generate the code values + * without bit reversal. + */ + for (bits in 1...MAX_BITS + 1) { + next_code[bits] = code = (code + bl_count[bits-1]) << 1; + } + /* Check that the bit counts in bl_count are consistent. The last code + * must be all ones. + */ + //Assert (code + bl_count[MAX_BITS]-1 == (1< length code (0..28) */ + length = 0; + for (code in 0...LENGTH_CODES-1) { + base_length[code] = length; + for (n in 0...(1< dist code (0..29) */ + dist = 0; + for (code in 0...16) { + base_dist[code] = dist; + for (n in 0...(1<>= 7; /* from now on, all distances are divided by 128 */ + //NOTE(hx): check usage of prev code value + for (code in 16...D_CODES) { + base_dist[code] = dist << 7; + for (n in 0...(1<<(extra_dbits[code]-7))) { + _dist_code[256 + dist++] = code; + } + } + //Assert (dist == 256, "tr_static_init: 256+dist != 512"); + + /* Construct the codes of the static literal tree */ + for (bits in 0...MAX_BITS + 1) { + bl_count[bits] = 0; + } + + n = 0; + while (n <= 143) { + static_ltree[n*2 + 1]/*.Len*/ = 8; + n++; + bl_count[8]+=1; + } + while (n <= 255) { + static_ltree[n*2 + 1]/*.Len*/ = 9; + n++; + bl_count[9]+=1; + } + while (n <= 279) { + static_ltree[n*2 + 1]/*.Len*/ = 7; + n++; + bl_count[7]+=1; + } + while (n <= 287) { + static_ltree[n*2 + 1]/*.Len*/ = 8; + n++; + bl_count[8]+=1; + } + /* Codes 286 and 287 do not exist, but we must include them in the + * tree construction to get a canonical Huffman tree (longest code + * all ones) + */ + gen_codes(static_ltree, L_CODES+1, bl_count); + + /* The static distance tree is trivial: */ + for (n in 0...D_CODES) { + static_dtree[n*2 + 1]/*.Len*/ = 5; + static_dtree[n*2]/*.Code*/ = bi_reverse(n, 5); + } + + // Now data ready and we can init static trees + static_l_desc = new StaticTreeDesc(static_ltree, extra_lbits, LITERALS+1, L_CODES, MAX_BITS); + static_d_desc = new StaticTreeDesc(static_dtree, extra_dbits, 0, D_CODES, MAX_BITS); + //NOTE(hx): Array(0) ? + static_bl_desc = new StaticTreeDesc(new UInt16Array(0), extra_blbits, 0, BL_CODES, MAX_BL_BITS); + + //static_init_done = true; + } + + + /* =========================================================================== + * Initialize a new block. + */ + static function init_block(s:DeflateState) { + var n; /* iterates over tree elements */ + + /* Initialize the trees. */ + for (n in 0...L_CODES) { s.dyn_ltree[n*2]/*.Freq*/ = 0; } + for (n in 0...D_CODES) { s.dyn_dtree[n*2]/*.Freq*/ = 0; } + for (n in 0...BL_CODES) { s.bl_tree[n*2]/*.Freq*/ = 0; } + + s.dyn_ltree[END_BLOCK*2]/*.Freq*/ = 1; + s.opt_len = s.static_len = 0; + s.last_lit = s.matches = 0; + } + + + /* =========================================================================== + * Flush the bit buffer and align the output on a byte boundary + */ + static function bi_windup(s:DeflateState) + { + if (s.bi_valid > 8) { + put_short(s, s.bi_buf); + } else if (s.bi_valid > 0) { + //put_byte(s, (Byte)s->bi_buf); + s.pending_buf[s.pending++] = s.bi_buf; + } + s.bi_buf = 0; + s.bi_valid = 0; + } + + /* =========================================================================== + * Copy a stored block, storing first the length and its + * one's complement if requested. + */ + static inline function copy_block(s:DeflateState, buf, len, header) + //DeflateState *s; + //charf *buf; /* the input data */ + //unsigned len; /* its length */ + //int header; /* true if block header must be written */ + { + bi_windup(s); /* align on byte boundary */ + + if (header) { + put_short(s, len); + put_short(s, ~len); + } + // while (len--) { + // put_byte(s, *buf++); + // } + Common.arraySet(cast s.pending_buf, cast s.window, buf, len, s.pending); + s.pending += len; + } + + /* =========================================================================== + * Compares to subtrees, using the tree depth as tie breaker when + * the subtrees have equal frequency. This minimizes the worst case length. + */ + static inline function smaller(tree:UInt16Array, n, m, depth:UInt16Array) { + var _n2 = n*2; + var _m2 = m*2; + return (tree[_n2]/*.Freq*/ < tree[_m2]/*.Freq*/ || + (tree[_n2]/*.Freq*/ == tree[_m2]/*.Freq*/ && depth[n] <= depth[m])); + } + + /* =========================================================================== + * Restore the heap property by moving down the tree starting at node k, + * exchanging a node with the smallest of its two sons if necessary, stopping + * when the heap property is re-established (each father smaller than its + * two sons). + */ + static function pqdownheap(s:DeflateState, tree:UInt16Array, k) + // deflate_state *s; + // ct_data *tree; /* the tree to restore */ + // int k; /* node to move down */ + { + var v = s.heap[k]; + var j = k << 1; /* left son of k */ + while (j <= s.heap_len) { + /* Set j to the smallest of the two sons: */ + if (j < s.heap_len && + smaller(tree, s.heap[j+1], s.heap[j], s.depth)) { + j++; + } + /* Exit if v is smaller than both sons */ + if (smaller(tree, v, s.heap[j], s.depth)) { break; } + + /* Exchange v with the smallest son */ + s.heap[k] = s.heap[j]; + k = j; + + /* And continue down the tree, setting j to the left son of k */ + j <<= 1; + } + s.heap[k] = v; + } + + + // inlined manually + // var SMALLEST = 1; + + /* =========================================================================== + * Send the block data compressed using the given Huffman trees + */ + static function compress_block(s:DeflateState, ltree:UInt16Array, dtree:UInt16Array) + // deflate_state *s; + // const ct_data *ltree; /* literal tree */ + // const ct_data *dtree; /* distance tree */ + { + var dist; /* distance of matched string */ + var lc; /* match length or unmatched char (if dist == 0) */ + var lx = 0; /* running index in l_buf */ + var code; /* the code to send */ + var extra; /* number of extra bits to send */ + + if (s.last_lit != 0) { + do { + dist = (s.pending_buf[s.d_buf + lx*2] << 8) | (s.pending_buf[s.d_buf + lx*2 + 1]); + lc = s.pending_buf[s.l_buf + lx]; + lx++; + + if (dist == 0) { + send_code(s, lc, ltree); /* send a literal byte */ + //Tracecv(isgraph(lc), (stderr," '%c' ", lc)); + } else { + /* Here, lc is the match length - MIN_MATCH */ + code = _length_code[lc]; + send_code(s, code+LITERALS+1, ltree); /* send the length code */ + extra = extra_lbits[code]; + if (extra != 0) { + lc -= base_length[code]; + send_bits(s, lc, extra); /* send the extra length bits */ + } + dist--; /* dist is now the match distance - 1 */ + code = d_code(dist); + //Assert (code < D_CODES, "bad d_code"); + + send_code(s, code, dtree); /* send the distance code */ + extra = extra_dbits[code]; + if (extra != 0) { + dist -= base_dist[code]; + send_bits(s, dist, extra); /* send the extra distance bits */ + } + } /* literal or match pair ? */ + + /* Check that the overlay between pending_buf and d_buf+l_buf is ok: */ + //Assert((uInt)(s->pending) < s->lit_bufsize + 2*lx, + // "pendingBuf overflow"); + + } while (lx < s.last_lit); + } + + send_code(s, END_BLOCK, ltree); + } + + + /* =========================================================================== + * Construct one Huffman tree and assigns the code bit strings and lengths. + * Update the total bit length for the current block. + * IN assertion: the field freq is set for all tree elements. + * OUT assertions: the fields len and code are set to the optimal bit length + * and corresponding code. The length opt_len is updated; static_len is + * also updated if stree is not null. The field max_code is set. + */ + static function build_tree(s:DeflateState, desc:TreeDesc) + // deflate_state *s; + // tree_desc *desc; /* the tree descriptor */ + { + var tree = desc.dyn_tree; + var stree = desc.stat_desc.static_tree; + var has_stree = desc.stat_desc.has_stree; + var elems = desc.stat_desc.elems; + var n, m; /* iterate over heap elements */ + var max_code = -1; /* largest code with non zero frequency */ + var node; /* new node being created */ + + /* Construct the initial heap, with least frequent element in + * heap[SMALLEST]. The sons of heap[n] are heap[2*n] and heap[2*n+1]. + * heap[0] is not used. + */ + s.heap_len = 0; + s.heap_max = HEAP_SIZE; + + for (n in 0...elems) { + if (tree[n * 2]/*.Freq*/ != 0) { + s.heap[++s.heap_len] = max_code = n; + s.depth[n] = 0; + + } else { + tree[n*2 + 1]/*.Len*/ = 0; + } + } + + /* The pkzip format requires that at least one distance code exists, + * and that at least one bit should be sent even if there is only one + * possible code. So to avoid special checks later on we force at least + * two codes of non zero frequency. + */ + while (s.heap_len < 2) { + node = s.heap[++s.heap_len] = (max_code < 2 ? ++max_code : 0); + tree[node * 2]/*.Freq*/ = 1; + s.depth[node] = 0; + s.opt_len--; + + if (has_stree) { + s.static_len -= stree[node*2 + 1]/*.Len*/; + } + /* node is 0 or 1 so it does not have extra bits */ + } + desc.max_code = max_code; + + /* The elements heap[heap_len/2+1 .. heap_len] are leaves of the tree, + * establish sub-heaps of increasing lengths: + */ + var n = (s.heap_len >> 1/*int /2*/); + while (n >= 1) { + pqdownheap(s, tree, n); + n--; + } + + /* Construct the Huffman tree by repeatedly combining the least two + * frequent nodes. + */ + node = elems; /* next internal node of the tree */ + do { + //pqremove(s, tree, n); /* n = node of least frequency */ + /*** pqremove ***/ + n = s.heap[1/*SMALLEST*/]; + s.heap[1/*SMALLEST*/] = s.heap[s.heap_len--]; + pqdownheap(s, tree, 1/*SMALLEST*/); + /***/ + + m = s.heap[1/*SMALLEST*/]; /* m = node of next least frequency */ + + s.heap[--s.heap_max] = n; /* keep the nodes sorted by frequency */ + s.heap[--s.heap_max] = m; + + /* Create a new node father of n and m */ + tree[node * 2]/*.Freq*/ = tree[n * 2]/*.Freq*/ + tree[m * 2]/*.Freq*/; + s.depth[node] = (s.depth[n] >= s.depth[m] ? s.depth[n] : s.depth[m]) + 1; + tree[n*2 + 1]/*.Dad*/ = tree[m*2 + 1]/*.Dad*/ = node; + + /* and insert the new node in the heap */ + s.heap[1/*SMALLEST*/] = node++; + pqdownheap(s, tree, 1/*SMALLEST*/); + + } while (s.heap_len >= 2); + + s.heap[--s.heap_max] = s.heap[1/*SMALLEST*/]; + + /* At this point, the fields freq and dad are set. We can now + * generate the bit lengths. + */ + gen_bitlen(s, desc); + + /* The field len is now set, we can generate the bit codes */ + gen_codes(tree, max_code, s.bl_count); + } + + + /* =========================================================================== + * Scan a literal or distance tree to determine the frequencies of the codes + * in the bit length tree. + */ + static function scan_tree(s:DeflateState, tree:UInt16Array, max_code:Int) + // deflate_state *s; + // ct_data *tree; /* the tree to be scanned */ + // int max_code; /* and its largest code of non zero frequency */ + { + var n; /* iterates over all tree elements */ + var prevlen = -1; /* last emitted length */ + var curlen; /* length of current code */ + + var nextlen = tree[0*2 + 1]/*.Len*/; /* length of next code */ + + var count = 0; /* repeat count of the current code */ + var max_count = 7; /* max repeat count */ + var min_count = 4; /* min repeat count */ + + if (nextlen == 0) { + max_count = 138; + min_count = 3; + } + tree[(max_code+1)*2 + 1]/*.Len*/ = 0xffff; /* guard */ + + for (n in 0...max_code + 1) { + curlen = nextlen; + nextlen = tree[(n+1)*2 + 1]/*.Len*/; + + if (++count < max_count && curlen == nextlen) { + continue; + + } else if (count < min_count) { + s.bl_tree[curlen * 2]/*.Freq*/ += count; + + } else if (curlen != 0) { + + if (curlen != prevlen) { s.bl_tree[curlen * 2]/*.Freq*/+=1; } + s.bl_tree[REP_3_6*2]/*.Freq*/+=1; + + } else if (count <= 10) { + s.bl_tree[REPZ_3_10*2]/*.Freq*/+=1; + + } else { + s.bl_tree[REPZ_11_138*2]/*.Freq*/+=1; + } + + count = 0; + prevlen = curlen; + + if (nextlen == 0) { + max_count = 138; + min_count = 3; + + } else if (curlen == nextlen) { + max_count = 6; + min_count = 3; + + } else { + max_count = 7; + min_count = 4; + } + } + } + + + /* =========================================================================== + * Send a literal or distance tree in compressed form, using the codes in + * bl_tree. + */ + static function send_tree(s:DeflateState, tree:UInt16Array, max_code:Int) + // deflate_state *s; + // ct_data *tree; /* the tree to be scanned */ + // int max_code; /* and its largest code of non zero frequency */ + { + var n; /* iterates over all tree elements */ + var prevlen = -1; /* last emitted length */ + var curlen; /* length of current code */ + + var nextlen = tree[0*2 + 1]/*.Len*/; /* length of next code */ + + var count = 0; /* repeat count of the current code */ + var max_count = 7; /* max repeat count */ + var min_count = 4; /* min repeat count */ + + /* tree[max_code+1].Len = -1; */ /* guard already set */ + if (nextlen == 0) { + max_count = 138; + min_count = 3; + } + + for (n in 0...max_code + 1) { + curlen = nextlen; + nextlen = tree[(n+1)*2 + 1]/*.Len*/; + + if (++count < max_count && curlen == nextlen) { + continue; + + } else if (count < min_count) { + do { send_code(s, curlen, s.bl_tree); } while (--count != 0); + + } else if (curlen != 0) { + if (curlen != prevlen) { + send_code(s, curlen, s.bl_tree); + count--; + } + //Assert(count >= 3 && count <= 6, " 3_6?"); + send_code(s, REP_3_6, s.bl_tree); + send_bits(s, count-3, 2); + + } else if (count <= 10) { + send_code(s, REPZ_3_10, s.bl_tree); + send_bits(s, count-3, 3); + + } else { + send_code(s, REPZ_11_138, s.bl_tree); + send_bits(s, count-11, 7); + } + + count = 0; + prevlen = curlen; + if (nextlen == 0) { + max_count = 138; + min_count = 3; + + } else if (curlen == nextlen) { + max_count = 6; + min_count = 3; + + } else { + max_count = 7; + min_count = 4; + } + } + } + + + /* =========================================================================== + * Construct the Huffman tree for the bit lengths and return the index in + * bl_order of the last bit length code to send. + */ + static function build_bl_tree(s:DeflateState) { + var max_blindex; /* index of last bit length code of non zero freq */ + + /* Determine the bit length frequencies for literal and distance trees */ + scan_tree(s, s.dyn_ltree, s.l_desc.max_code); + scan_tree(s, s.dyn_dtree, s.d_desc.max_code); + + /* Build the bit length tree: */ + build_tree(s, s.bl_desc); + /* opt_len now includes the length of the tree representations, except + * the lengths of the bit lengths codes and the 5+5+4 bits for the counts. + */ + + /* Determine the number of bit length codes to send. The pkzip format + * requires that at least 4 bit length codes be sent. (appnote.txt says + * 3 but the actual value used is 4.) + */ + var max_blindex = BL_CODES-1; + while (max_blindex >= 3) { + if (s.bl_tree[bl_order[max_blindex]*2 + 1]/*.Len*/ != 0) { + break; + } + max_blindex--; + } + /* Update opt_len to include the bit length tree and counts */ + s.opt_len += 3*(max_blindex+1) + 5+5+4; + //Tracev((stderr, "\ndyn trees: dyn %ld, stat %ld", + // s->opt_len, s->static_len)); + + return max_blindex; + } + + + /* =========================================================================== + * Send the header for a block using dynamic Huffman trees: the counts, the + * lengths of the bit length codes, the literal tree and the distance tree. + * IN assertion: lcodes >= 257, dcodes >= 1, blcodes >= 4. + */ + static function send_all_trees(s:DeflateState, lcodes, dcodes, blcodes) + // deflate_state *s; + // int lcodes, dcodes, blcodes; /* number of codes for each tree */ + { + var rank; /* index in bl_order */ + + //Assert (lcodes >= 257 && dcodes >= 1 && blcodes >= 4, "not enough codes"); + //Assert (lcodes <= L_CODES && dcodes <= D_CODES && blcodes <= BL_CODES, + // "too many codes"); + //Tracev((stderr, "\nbl counts: ")); + send_bits(s, lcodes-257, 5); /* not +255 as stated in appnote.txt */ + send_bits(s, dcodes-1, 5); + send_bits(s, blcodes-4, 4); /* not -3 as stated in appnote.txt */ + for (rank in 0...blcodes) { + //Tracev((stderr, "\nbl code %2d ", bl_order[rank])); + send_bits(s, s.bl_tree[bl_order[rank]*2 + 1]/*.Len*/, 3); + } + //Tracev((stderr, "\nbl tree: sent %ld", s->bits_sent)); + + send_tree(s, s.dyn_ltree, lcodes-1); /* literal tree */ + //Tracev((stderr, "\nlit tree: sent %ld", s->bits_sent)); + + send_tree(s, s.dyn_dtree, dcodes-1); /* distance tree */ + //Tracev((stderr, "\ndist tree: sent %ld", s->bits_sent)); + } + + + /* =========================================================================== + * Check if the data type is TEXT or BINARY, using the following algorithm: + * - TEXT if the two conditions below are satisfied: + * a) There are no non-portable control characters belonging to the + * "black list" (0..6, 14..25, 28..31). + * b) There is at least one printable character belonging to the + * "white list" (9 {TAB}, 10 {LF}, 13 {CR}, 32..255). + * - BINARY otherwise. + * - The following partially-portable control characters form a + * "gray list" that is ignored in this detection algorithm: + * (7 {BEL}, 8 {BS}, 11 {VT}, 12 {FF}, 26 {SUB}, 27 {ESC}). + * IN assertion: the fields Freq of dyn_ltree are set. + */ + static function detect_data_type(s:DeflateState) { + /* black_mask is the bit mask of black-listed bytes + * set bits 0..6, 14..25, and 28..31 + * 0xf3ffc07f = binary 11110011111111111100000001111111 + */ + var black_mask = 0xf3ffc07f; + var n = 0; + + /* Check for non-textual ("black-listed") bytes. */ + //NOTE(hx): check loop with two inc clauses + while (n <= 31) { + if ((black_mask & 1 == 1) && (s.dyn_ltree[n*2]/*.Freq*/ != 0)) { + return DataType.Z_BINARY; + } + n++; + black_mask >>>= 1; + } + + /* Check for textual ("white-listed") bytes. */ + if (s.dyn_ltree[9 * 2]/*.Freq*/ != 0 || s.dyn_ltree[10 * 2]/*.Freq*/ != 0 || + s.dyn_ltree[13 * 2]/*.Freq*/ != 0) { + return DataType.Z_TEXT; + } + for (n in 32...LITERALS) { + if (s.dyn_ltree[n * 2]/*.Freq*/ != 0) { + return DataType.Z_TEXT; + } + } + + /* There are no "black-listed" or "white-listed" bytes: + * this stream either is empty or has tolerated ("gray-listed") bytes only. + */ + return DataType.Z_BINARY; + } + + + static var static_init_done = false; + + /* =========================================================================== + * Initialize the tree data structures for a new zlib stream. + */ + static public function _tr_init(s:DeflateState) + { + + if (!static_init_done) { + tr_static_init(); + static_init_done = true; + } + + s.l_desc = new TreeDesc(s.dyn_ltree, static_l_desc); + s.d_desc = new TreeDesc(s.dyn_dtree, static_d_desc); + s.bl_desc = new TreeDesc(s.bl_tree, static_bl_desc); + + s.bi_buf = 0; + s.bi_valid = 0; + + /* Initialize the first block of the first file: */ + init_block(s); + } + + + /* =========================================================================== + * Send a stored block + */ + static inline public function _tr_stored_block(s:DeflateState, buf, stored_len, last) + //DeflateState *s; + //charf *buf; /* input block */ + //ulg stored_len; /* length of input block */ + //int last; /* one if this is the last block for a file */ + { + send_bits(s, (STORED_BLOCK<<1)+(last ? 1 : 0), 3); /* send block type */ + copy_block(s, buf, stored_len, true); /* with header */ + } + + + /* =========================================================================== + * Send one empty static block to give enough lookahead for inflate. + * This takes 10 bits, of which 7 may remain in the bit buffer. + */ + static inline public function _tr_align(s:DeflateState) { + send_bits(s, STATIC_TREES<<1, 3); + send_code(s, END_BLOCK, static_ltree); + bi_flush(s); + } + + + /* =========================================================================== + * Determine the best encoding for the current block: dynamic trees, static + * trees or store, and output the encoded block to the zip file. + */ + static public function _tr_flush_block(s:DeflateState, buf, stored_len, last) + //DeflateState *s; + //charf *buf; /* input block, or NULL if too old */ + //ulg stored_len; /* length of input block */ + //int last; /* one if this is the last block for a file */ + { + var opt_lenb, static_lenb; /* opt_len and static_len in bytes */ + var max_blindex = 0; /* index of last bit length code of non zero freq */ + + /* Build the Huffman trees unless a stored block is forced */ + if (s.level > 0) { + + /* Check if the file is binary or text */ + if (s.strm.data_type == DataType.Z_UNKNOWN) { + s.strm.data_type = detect_data_type(s); + } + + /* Construct the literal and distance trees */ + build_tree(s, s.l_desc); + // Tracev((stderr, "\nlit data: dyn %ld, stat %ld", s->opt_len, + // s->static_len)); + + build_tree(s, s.d_desc); + // Tracev((stderr, "\ndist data: dyn %ld, stat %ld", s->opt_len, + // s->static_len)); + /* At this point, opt_len and static_len are the total bit lengths of + * the compressed block data, excluding the tree representations. + */ + + /* Build the bit length tree for the above two trees, and get the index + * in bl_order of the last bit length code to send. + */ + max_blindex = build_bl_tree(s); + + /* Determine the best encoding. Compute the block lengths in bytes. */ + opt_lenb = (s.opt_len+3+7) >>> 3; + static_lenb = (s.static_len+3+7) >>> 3; + + // Tracev((stderr, "\nopt %lu(%lu) stat %lu(%lu) stored %lu lit %u ", + // opt_lenb, s->opt_len, static_lenb, s->static_len, stored_len, + // s->last_lit)); + + if (static_lenb <= opt_lenb) { opt_lenb = static_lenb; } + + } else { + // Assert(buf != (char*)0, "lost buf"); + opt_lenb = static_lenb = stored_len + 5; /* force a stored block */ + } + + if ((stored_len+4 <= opt_lenb) && (buf != -1)) { + /* 4: two words for the lengths */ + + /* The test buf != NULL is only necessary if LIT_BUFSIZE > WSIZE. + * Otherwise we can't have processed more than WSIZE input bytes since + * the last block flush, because compression would have been + * successful. If LIT_BUFSIZE <= WSIZE, it is never too late to + * transform a block into a stored block. + */ + _tr_stored_block(s, buf, stored_len, last); + + } else if (s.strategy == Strategy.Z_FIXED || static_lenb == opt_lenb) { + + send_bits(s, (STATIC_TREES<<1) + (last ? 1 : 0), 3); + compress_block(s, static_ltree, static_dtree); + + } else { + send_bits(s, (DYN_TREES<<1) + (last ? 1 : 0), 3); + send_all_trees(s, s.l_desc.max_code+1, s.d_desc.max_code+1, max_blindex+1); + compress_block(s, s.dyn_ltree, s.dyn_dtree); + } + // Assert (s->compressed_len == s->bits_sent, "bad compressed size"); + /* The above check is made mod 2^32, for files larger than 512 MB + * and uLong implemented on 32 bits. + */ + init_block(s); + + if (last) { + bi_windup(s); + } + // Tracev((stderr,"\ncomprlen %lu(%lu) ", s->compressed_len>>3, + // s->compressed_len-7*last)); + } + + /* =========================================================================== + * Save the match info and tally the frequency counts. Return true if + * the current block must be flushed. + */ + static inline public function _tr_tally(s:DeflateState, dist, lc) + // deflate_state *s; + // unsigned dist; /* distance of matched string */ + // unsigned lc; /* match length-MIN_MATCH or unmatched char (if dist==0) */ + { + //var out_length, in_length, dcode; + + s.pending_buf[s.d_buf + s.last_lit * 2] = (dist >>> 8) & 0xff; + s.pending_buf[s.d_buf + s.last_lit * 2 + 1] = dist & 0xff; + + s.pending_buf[s.l_buf + s.last_lit] = lc & 0xff; + s.last_lit++; + + if (dist == 0) { + /* lc is the unmatched char */ + s.dyn_ltree[lc*2]/*.Freq*/+=1; + } else { + s.matches++; + /* Here, lc is the match length - MIN_MATCH */ + dist--; /* dist = match distance - 1 */ + //Assert((ush)dist < (ush)MAX_DIST(s) && + // (ush)lc <= (ush)(MAX_MATCH-MIN_MATCH) && + // (ush)d_code(dist) < (ush)D_CODES, "_tr_tally: bad match"); + + s.dyn_ltree[(_length_code[lc]+LITERALS+1) * 2]/*.Freq*/+=1; + s.dyn_dtree[d_code(dist) * 2]/*.Freq*/+=1; + } + + // (!) This block is disabled in zlib defailts, + // don't enable it for binary compatibility + + //#ifdef TRUNCATE_BLOCK + // /* Try to guess if it is profitable to stop the current block here */ + // if ((s.last_lit & 0x1fff) === 0 && s.level > 2) { + // /* Compute an upper bound for the compressed length */ + // out_length = s.last_lit*8; + // in_length = s.strstart - s.block_start; + // + // for (dcode = 0; dcode < D_CODES; dcode++) { + // out_length += s.dyn_dtree[dcode*2]/*.Freq*/ * (5 + extra_dbits[dcode]); + // } + // out_length >>>= 3; + // //Tracev((stderr,"\nlast_lit %u, in %ld, out ~%ld(%ld%%) ", + // // s->last_lit, in_length, out_length, + // // 100L - out_length*100L/in_length)); + // if (s.matches < (s.last_lit>>1)/*int /2*/ && out_length < (in_length>>1)/*int /2*/) { + // return true; + // } + // } + //#endif + + return (s.last_lit == s.lit_bufsize-1); + /* We avoid equality with lit_bufsize because of wraparound at 64K + * on 16 bit machines and because stored blocks are restricted to + * 64K-1 bytes. + */ + } +} + +@:allow(iron.format.pako.zlib.Trees) +class StaticTreeDesc +{ + var static_tree:UInt16Array; + var extra_bits:UInt16Array; + var extra_base:Int; + var elems:Int; + var max_length:Int; + + var has_stree:Bool; + + function new(static_tree:UInt16Array, extra_bits:UInt16Array, extra_base, elems, max_length) { + this.static_tree = static_tree; /* static tree or NULL */ + this.extra_bits = extra_bits; /* extra bits for each code or NULL */ + this.extra_base = extra_base; /* base index for extra_bits */ + this.elems = elems; /* max number of elements in the tree */ + this.max_length = max_length; /* max bit length for the codes */ + + // show if `static_tree` has data or dummy - needed for monomorphic objects + this.has_stree = static_tree != null && static_tree.length > 0; + } +} + +@:allow(iron.format.pako.zlib.Trees) +class TreeDesc +{ + var dyn_tree:UInt16Array; + var max_code:Int; + var stat_desc:StaticTreeDesc; + + function new(dyn_tree, stat_desc) { + this.dyn_tree = dyn_tree; /* the dynamic tree */ + this.max_code = 0; /* largest code with non zero frequency */ + this.stat_desc = stat_desc; /* the corresponding static tree */ + } +} \ No newline at end of file diff --git a/Libraries/iron_format/Sources/iron/format/pako/zlib/ZStream.hx b/Libraries/iron_format/Sources/iron/format/pako/zlib/ZStream.hx new file mode 100644 index 00000000..74377d9d --- /dev/null +++ b/Libraries/iron_format/Sources/iron/format/pako/zlib/ZStream.hx @@ -0,0 +1,34 @@ +package iron.format.pako.zlib; + +import haxe.io.UInt8Array; +import iron.format.pako.zlib.Deflate.DeflateState; +import iron.format.pako.zlib.Inflate.InflateState; + +class ZStream +{ + /* next input byte */ + public var input:UInt8Array = null; // JS specific, because we have no pointers + public var next_in:Int = 0; + /* number of bytes available at input */ + public var avail_in:Int = 0; + /* total number of input bytes read so far */ + public var total_in:Int = 0; + /* next output byte should be put there */ + public var output:UInt8Array = null; // JS specific, because we have no pointers + public var next_out:Int = 0; + /* remaining free space at output */ + public var avail_out:Int = 0; + /* total number of bytes output so far */ + public var total_out:Int = 0; + /* last error message, NULL if no error */ + public var msg = ''/*Z_NULL*/; + /* not visible by applications */ + public var deflateState:DeflateState = null; + public var inflateState:InflateState = null; + /* best guess about the data type: binary or text */ + public var data_type:Int = Constants.DataType.Z_UNKNOWN /*Z_UNKNOWN*/; + /* adler32 value of the uncompressed data */ + public var adler:Int = 0; + + public function new():Void { } +} \ No newline at end of file diff --git a/Libraries/iron_format/Sources/iron/format/png/Data.hx b/Libraries/iron_format/Sources/iron/format/png/Data.hx new file mode 100644 index 00000000..223e36f7 --- /dev/null +++ b/Libraries/iron_format/Sources/iron/format/png/Data.hx @@ -0,0 +1,51 @@ +/* + * format - haXe File Formats + * + * Copyright (c) 2008-2009, The haXe Project Contributors + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE HAXE PROJECT CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE HAXE PROJECT CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + */ +package iron.format.png; + +enum Color { + ColGrey( alpha : Bool ) ; // 1|2|4|8|16 without alpha , 8|16 with alpha + ColTrue( alpha : Bool ); // 8|16 + ColIndexed; // 1|2|4|8 +} + +typedef Header = { + var width : Int; + var height : Int; + var colbits : Int; + var color : Color; + var interlaced : Bool; +} + +enum Chunk { + CEnd; + CHeader( h : Header ); + CData( b : haxe.io.Bytes ); + CPalette( b : haxe.io.Bytes ); + CUnknown( id : String, data : haxe.io.Bytes ); +} + +typedef Data = List; diff --git a/Libraries/iron_format/Sources/iron/format/png/Tools.hx b/Libraries/iron_format/Sources/iron/format/png/Tools.hx new file mode 100644 index 00000000..f7cdd6fb --- /dev/null +++ b/Libraries/iron_format/Sources/iron/format/png/Tools.hx @@ -0,0 +1,739 @@ +/* + * format - haXe File Formats + * + * Copyright (c) 2008-2009, The haXe Project Contributors + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE HAXE PROJECT CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE HAXE PROJECT CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + */ +package iron.format.png; +import iron.format.png.Data; + +class Tools { + + /** + Returns the PNG header informations. Throws an exception if no header found. + **/ + public static function getHeader( d : Data ) : Header { + for( c in d ) + switch( c ) { + case CHeader(h): return h; + default: + } + throw "Header not found"; + } + + /** + Return the PNG palette colors, or null if no palette chunk was found + **/ + public static function getPalette( d : Data ) : haxe.io.Bytes { + for( c in d ) + switch( c ) { + case CPalette(b): return b; + default: + } + return null; + } + + static inline function filter( data : #if flash10 iron.format.tools.MemoryBytes #else haxe.io.Bytes #end, x, y, stride, prev, p, numChannels=4 ) { + var b = y == 0 ? 0 : data.get(p - stride); + var c = x == 0 || y == 0 ? 0 : data.get(p - stride - numChannels); + var k = prev + b - c; + var pa = k - prev; if( pa < 0 ) pa = -pa; + var pb = k - b; if( pb < 0 ) pb = -pb; + var pc = k - c; if( pc < 0 ) pc = -pc; + return (pa <= pb && pa <= pc) ? prev : (pb <= pc ? b : c); + } + + /** + Converts from BGRA to ARGB and the other way by reversing bytes. + **/ + public static function reverseBytes( b : haxe.io.Bytes ) { + #if flash10 + var bytes = b.getData(); + if( bytes.length < 1024 ) bytes.length = 1024; + flash.Memory.select(bytes); + #end + inline function bget(p) { + #if flash10 + return flash.Memory.getByte(p); + #else + return b.get(p); + #end + } + inline function bset(p,v) { + #if flash10 + flash.Memory.setByte(p,v); + #else + return b.set(p,v); + #end + } + var p = 0; + for( i in 0...b.length >> 2 ) { + var b = bget(p); + var g = bget(p + 1); + var r = bget(p + 2); + var a = bget(p + 3); + bset(p++, a); + bset(p++, r); + bset(p++, g); + bset(p++, b); + } + } + + /** + Decode the greyscale PNG data and apply filters, extracting only the grey channel if alpha is present. + **/ + @:noDebug + public static function extractGrey( d : Data ) : haxe.io.Bytes { + var h = getHeader(d); + var grey = haxe.io.Bytes.alloc(h.width * h.height); + var data = null; + var fullData : haxe.io.BytesBuffer = null; + for( c in d ) + switch( c ) { + case CData(b): + if( fullData != null ) + fullData.add(b); + else if( data == null ) + data = b; + else { + fullData = new haxe.io.BytesBuffer(); + fullData.add(data); + fullData.add(b); + data = null; + } + default: + } + if( fullData != null ) + data = fullData.getBytes(); + if( data == null ) + throw "Data not found"; + data = iron.format.tools.Inflate.run(data); + var r = 0, w = 0; + switch( h.color ) { + default: + throw "Unsupported color mode"; + case ColGrey(alpha): + if( h.colbits != 8 ) + throw "Unsupported color mode"; + var width = h.width; + var stride = (alpha ? 2 : 1) * width + 1; + if( data.length < h.height * stride ) throw "Not enough data"; + + #if flash10 + var bytes = data.getData(); + var start = h.height * stride; + bytes.length = start + h.width * h.height; + if( bytes.length < 1024 ) bytes.length = 1024; + flash.Memory.select(bytes); + var realData = data, realGrey = grey; + var data = iron.format.tools.MemoryBytes.make(0); + var grey = iron.format.tools.MemoryBytes.make(start); + #end + + var rinc = (alpha ? 2 : 1); + for( y in 0...h.height ) { + var f = data.get(r++); + switch( f ) { + case 0: + for( x in 0...width ) { + var v = data.get(r); r += rinc; + grey.set(w++,v); + } + case 1: + var cv = 0; + for( x in 0...width ) { + cv += data.get(r); r += rinc; + grey.set(w++,cv); + } + case 2: + var stride = y == 0 ? 0 : width; + for( x in 0...width ) { + var v = data.get(r) + grey.get(w - stride); r += rinc; + grey.set(w++, v); + } + case 3: + var cv = 0; + var stride = y == 0 ? 0 : width; + for( x in 0...width ) { + cv = (data.get(r) + ((cv + grey.get(w - stride)) >> 1)) & 0xFF; r += rinc; + grey.set(w++,cv); + } + case 4: + var stride = width; + var cv = 0; + for( x in 0...width ) { + cv = (filter(grey, x, y, stride, cv, w, 1) + data.get(r)) & 0xFF; r += rinc; + grey.set(w++, cv); + } + default: + throw "Invalid filter "+f; + } + } + + #if flash10 + var b = realGrey.getData(); + b.position = 0; + b.writeBytes(realData.getData(), start, h.width * h.height); + #end + } + return grey; + } + /** + Decode the PNG data and apply filters. By default this will output BGRA low-endian format. You can use the [reverseBytes] function to inverse the bytes to ARGB big-endian format. + **/ + @:noDebug + public static function extract32( d : Data, ?bytes, ?flipY ) : haxe.io.Bytes { + var h = getHeader(d); + var bgra = bytes == null ? haxe.io.Bytes.alloc(h.width * h.height * 4) : bytes; + var data = null; + var fullData : haxe.io.BytesBuffer = null; + for( c in d ) + switch( c ) { + case CData(b): + if( fullData != null ) + fullData.add(b); + else if( data == null ) + data = b; + else { + fullData = new haxe.io.BytesBuffer(); + fullData.add(data); + fullData.add(b); + data = null; + } + default: + } + if( fullData != null ) + data = fullData.getBytes(); + if( data == null ) + throw "Data not found"; + data = iron.format.tools.Inflate.run(data); + var r = 0, w = 0; + var lineDelta = 0; + if( flipY ) { + lineDelta = -h.width * 8; + w = (h.height - 1) * (h.width * 4); + } + var flipY = flipY ? -1 : 1; + switch( h.color ) { + case ColIndexed: + var pal = getPalette(d); + if( pal == null ) throw "PNG Palette is missing"; + + // transparent palette extension + var alpha = null; + for( t in d ) + switch( t ) { + case CUnknown("tRNS", data): alpha = data; break; + default: + } + + // if alpha is incomplete, pad with 0xFF + if( alpha != null && alpha.length < 1 << h.colbits ) { + var alpha2 = haxe.io.Bytes.alloc(1 << h.colbits); + alpha2.blit(0,alpha,0,alpha.length); + alpha2.fill(alpha.length, alpha2.length - alpha.length, 0xFF); + alpha = alpha2; + } + + var width = h.width; + var stride = Math.ceil(width * h.colbits / 8) + 1; + + if( data.length < h.height * stride ) throw "Not enough data"; + + #if flash10 + var bytes = data.getData(); + var start = h.height * stride; + bytes.length = start + h.width * h.height * 4; + if( bytes.length < 1024 ) bytes.length = 1024; + flash.Memory.select(bytes); + var realData = data, realRgba = bgra; + var data = iron.format.tools.MemoryBytes.make(0); + var bgra = iron.format.tools.MemoryBytes.make(start); + #end + + var rline = (h.width * h.colbits) >> 3; + for( y in 0...h.height ) { + var f = data.get(r++); + if( f == 0 ) { + r += rline; + continue; + } + switch( f ) { + case 1: + var c = 0; + for( x in 0...width ) { + var v = data.get(r); + c += v; + data.set(r++, c & 0xFF); + } + case 2: + var stride = y == 0 ? 0 : (rline + 1); + for( x in 0...width ) { + var v = data.get(r); + data.set(r, v + data.get(r - stride)); + r++; + } + case 3: + var c = 0; + var stride = y == 0 ? 0 : (rline + 1); + for( x in 0...width ) { + var v = data.get(r); + c = (v + ((c + data.get(r - stride)) >> 1)) & 0xFF; + data.set(r++, c); + } + case 4: + var stride = rline + 1; + var c = 0; + for( x in 0...width ) { + var v = data.get(r); + c = (filter(data, x, y, stride, c, r, 1) + v) & 0xFF; + data.set(r++, c); + } + default: + throw "Invalid filter "+f; + } + } + + var r = 0; + if( h.colbits == 8 ) { + for( y in 0...h.height ) { + r++; + for( x in 0...h.width ) { + var c = data.get(r++); + bgra.set(w++, pal.get(c * 3 + 2)); + bgra.set(w++, pal.get(c * 3 + 1)); + bgra.set(w++, pal.get(c * 3)); + bgra.set(w++, if( alpha != null ) alpha.get(c) else 0xFF); + } + w += lineDelta; + } + } else if( h.colbits < 8 ) { + var req = h.colbits; + var mask = (1 << req) - 1; + for( y in 0...h.height ) { + r++; + var bits = 0, nbits = 0, v; + for( x in 0...h.width ) { + if( nbits < req ) { + bits = (bits << 8) | data.get(r++); + nbits += 8; + } + var c = (bits >>> (nbits - req)) & mask; + nbits -= req; + bgra.set(w++, pal.get(c * 3 + 2)); + bgra.set(w++, pal.get(c * 3 + 1)); + bgra.set(w++, pal.get(c * 3)); + bgra.set(w++, if( alpha != null ) alpha.get(c) else 0xFF); + } + w += lineDelta; + } + } else + throw h.colbits+" indexed bits per pixel not supported"; + + #if flash10 + var b = realRgba.getData(); + b.position = 0; + b.writeBytes(realData.getData(), start, h.width * h.height * 4); + #end + + case ColGrey(alpha): + if( h.colbits != 8 ) + throw "Unsupported color mode"; + var width = h.width; + var stride = (alpha ? 2 : 1) * width + 1; + if( data.length < h.height * stride ) throw "Not enough data"; + + // transparent palette extension + var alphvaIdx:Int = -1; + if (!alpha) + for( t in d ) + switch( t ) { + case CUnknown("tRNS", data): + if (data.length >= 2) alphvaIdx = data.get(1); // Since library supports only 8-bit greyscale, not bothered with conversions. + break; + default: + } + + + #if flash10 + var bytes = data.getData(); + var start = h.height * stride; + bytes.length = start + h.width * h.height * 4; + if( bytes.length < 1024 ) bytes.length = 1024; + flash.Memory.select(bytes); + var realData = data, realRgba = bgra; + var data = iron.format.tools.MemoryBytes.make(0); + var bgra = iron.format.tools.MemoryBytes.make(start); + #end + + for( y in 0...h.height ) { + var f = data.get(r++); + switch( f ) { + case 0: + if( alpha ) + for( x in 0...width ) { + var v = data.get(r++); + bgra.set(w++,v); + bgra.set(w++,v); + bgra.set(w++,v); + bgra.set(w++,data.get(r++)); + } + else + for( x in 0...width ) { + var v = data.get(r++); + bgra.set(w++,v); + bgra.set(w++,v); + bgra.set(w++,v); + bgra.set(w++,v == alphvaIdx ? 0 : 0xFF); + } + case 1: + var cv = 0, ca = 0; + if( alpha ) + for( x in 0...width ) { + cv += data.get(r++); + bgra.set(w++,cv); + bgra.set(w++,cv); + bgra.set(w++,cv); + ca += data.get(r++); + bgra.set(w++,ca); + } + else + for( x in 0...width ) { + cv += data.get(r++); + bgra.set(w++,cv); + bgra.set(w++,cv); + bgra.set(w++,cv); + bgra.set(w++,cv == alphvaIdx ? 0 : 0xFF); + } + case 2: + var stride = y == 0 ? 0 : width * 4 * flipY; + if( alpha ) + for( x in 0...width ) { + var v = data.get(r++) + bgra.get(w - stride); + bgra.set(w++, v); + bgra.set(w++, v); + bgra.set(w++, v); + bgra.set(w++, data.get(r++) + bgra.get(w - stride)); + } + else + for( x in 0...width ) { + var v = data.get(r++) + bgra.get(w - stride); + bgra.set(w++, v); + bgra.set(w++, v); + bgra.set(w++, v); + bgra.set(w++, v == alphvaIdx ? 0 : 0xFF); + } + case 3: + var cv = 0, ca = 0; + var stride = y == 0 ? 0 : width * 4 * flipY; + if( alpha ) + for( x in 0...width ) { + cv = (data.get(r++) + ((cv + bgra.get(w - stride)) >> 1)) & 0xFF; + bgra.set(w++,cv); + bgra.set(w++,cv); + bgra.set(w++,cv); + ca = (data.get(r++) + ((ca + bgra.get(w - stride)) >> 1)) & 0xFF; + bgra.set(w++,ca); + } + else + for( x in 0...width ) { + cv = (data.get(r++) + ((cv + bgra.get(w - stride)) >> 1)) & 0xFF; + bgra.set(w++,cv); + bgra.set(w++,cv); + bgra.set(w++,cv); + bgra.set(w++, cv == alphvaIdx ? 0 : 0xFF); + } + case 4: + var stride = width * 4 * flipY; + var cv = 0, ca = 0; + if( alpha ) + for( x in 0...width ) { + cv = (filter(bgra, x, y, stride, cv, w) + data.get(r++)) & 0xFF; + bgra.set(w++, cv); + bgra.set(w++, cv); + bgra.set(w++, cv); + ca = (filter(bgra, x, y, stride, ca, w) + data.get(r++)) & 0xFF; + bgra.set(w++, ca); + } + else + for( x in 0...width ) { + cv = (filter(bgra, x, y, stride, cv, w) + data.get(r++)) & 0xFF; + bgra.set(w++, cv); + bgra.set(w++, cv); + bgra.set(w++, cv); + bgra.set(w++, cv == alphvaIdx ? 0 : 0xFF); + } + default: + throw "Invalid filter "+f; + } + w += lineDelta; + } + + #if flash10 + var b = realRgba.getData(); + b.position = 0; + b.writeBytes(realData.getData(), start, h.width * h.height * 4); + #end + + case ColTrue(alpha): + if( h.colbits != 8 ) + throw "Unsupported color mode"; + var width = h.width; + var stride = (alpha ? 4 : 3) * width + 1; + if( data.length < h.height * stride ) throw "Not enough data"; + + #if flash10 + var bytes = data.getData(); + var start = h.height * stride; + bytes.length = start + h.width * h.height * 4; + if( bytes.length < 1024 ) bytes.length = 1024; + flash.Memory.select(bytes); + var realData = data, realRgba = bgra; + var data = iron.format.tools.MemoryBytes.make(0); + var bgra = iron.format.tools.MemoryBytes.make(start); + #end + + // transparent palette extension + var alphaRed:Int = -1; + var alphaGreen:Int = -1; + var alphaBlue:Int = -1; + if (!alpha) + for( t in d ) + switch( t ) { + case CUnknown("tRNS", data): + if (data.length >= 6) { + alphaRed = data.get(1); + alphaGreen = data.get(3); + alphaBlue = data.get(5); + } + break; + default: + } + + var cr = 0, cg = 0, cb = 0, ca = 0; + inline function getAlphaValue():Int + { + return (cr == alphaRed && cg == alphaGreen && cb == alphaBlue) ? 0 : 0xff; + } + + // PNG data is encoded as RGB[A] + for( y in 0...h.height ) { + var f = data.get(r++); + switch( f ) { + case 0: + if( alpha ) + for( x in 0...width ) { + bgra.set(w++,data.get(r+2)); + bgra.set(w++,data.get(r+1)); + bgra.set(w++,data.get(r)); + bgra.set(w++,data.get(r+3)); + r += 4; + } + else + for( x in 0...width ) { + bgra.set(w++,cb = data.get(r+2)); + bgra.set(w++,cg = data.get(r+1)); + bgra.set(w++,cr = data.get(r)); + bgra.set(w++,getAlphaValue()); + r += 3; + } + case 1: + cr = cg = cb = ca = 0; + if( alpha ) + for( x in 0...width ) { + cb += data.get(r + 2); bgra.set(w++,cb); + cg += data.get(r + 1); bgra.set(w++,cg); + cr += data.get(r); bgra.set(w++,cr); + ca += data.get(r + 3); bgra.set(w++,ca); + r += 4; + } + else + for( x in 0...width ) { + cb += data.get(r + 2); bgra.set(w++,cb); + cg += data.get(r + 1); bgra.set(w++,cg); + cr += data.get(r); bgra.set(w++,cr); + bgra.set(w++,getAlphaValue()); + r += 3; + } + case 2: + var stride = y == 0 ? 0 : width * 4 * flipY; + if( alpha ) + for( x in 0...width ) { + bgra.set(w, data.get(r + 2) + bgra.get(w - stride)); w++; + bgra.set(w, data.get(r + 1) + bgra.get(w - stride)); w++; + bgra.set(w, data.get(r) + bgra.get(w - stride)); w++; + bgra.set(w, data.get(r + 3) + bgra.get(w - stride)); w++; + r += 4; + } + else + for( x in 0...width ) { + bgra.set(w, cb = data.get(r + 2) + bgra.get(w - stride)); w++; + bgra.set(w, cg = data.get(r + 1) + bgra.get(w - stride)); w++; + bgra.set(w, cr = data.get(r) + bgra.get(w - stride)); w++; + bgra.set(w++,getAlphaValue()); + r += 3; + } + case 3: + cr = cg = cb = ca = 0; + var stride = y == 0 ? 0 : width * 4 * flipY; + if( alpha ) + for( x in 0...width ) { + cb = (data.get(r + 2) + ((cb + bgra.get(w - stride)) >> 1)) & 0xFF; bgra.set(w++, cb); + cg = (data.get(r + 1) + ((cg + bgra.get(w - stride)) >> 1)) & 0xFF; bgra.set(w++, cg); + cr = (data.get(r + 0) + ((cr + bgra.get(w - stride)) >> 1)) & 0xFF; bgra.set(w++, cr); + ca = (data.get(r + 3) + ((ca + bgra.get(w - stride)) >> 1)) & 0xFF; bgra.set(w++, ca); + r += 4; + } + else + for( x in 0...width ) { + cb = (data.get(r + 2) + ((cb + bgra.get(w - stride)) >> 1)) & 0xFF; bgra.set(w++, cb); + cg = (data.get(r + 1) + ((cg + bgra.get(w - stride)) >> 1)) & 0xFF; bgra.set(w++, cg); + cr = (data.get(r + 0) + ((cr + bgra.get(w - stride)) >> 1)) & 0xFF; bgra.set(w++, cr); + bgra.set(w++,getAlphaValue()); + r += 3; + } + case 4: + var stride = width * 4 * flipY; + cr = cg = cb = ca = 0; + if( alpha ) + for( x in 0...width ) { + cb = (filter(bgra, x, y, stride, cb, w) + data.get(r + 2)) & 0xFF; bgra.set(w++, cb); + cg = (filter(bgra, x, y, stride, cg, w) + data.get(r + 1)) & 0xFF; bgra.set(w++, cg); + cr = (filter(bgra, x, y, stride, cr, w) + data.get(r + 0)) & 0xFF; bgra.set(w++, cr); + ca = (filter(bgra, x, y, stride, ca, w) + data.get(r + 3)) & 0xFF; bgra.set(w++, ca); + r += 4; + } + else + for( x in 0...width ) { + cb = (filter(bgra, x, y, stride, cb, w) + data.get(r + 2)) & 0xFF; bgra.set(w++, cb); + cg = (filter(bgra, x, y, stride, cg, w) + data.get(r + 1)) & 0xFF; bgra.set(w++, cg); + cr = (filter(bgra, x, y, stride, cr, w) + data.get(r + 0)) & 0xFF; bgra.set(w++, cr); + bgra.set(w++,getAlphaValue()); + r += 3; + } + default: + throw "Invalid filter "+f; + } + w += lineDelta; + } + + #if flash10 + var b = realRgba.getData(); + b.position = 0; + b.writeBytes(realData.getData(), start, h.width * h.height * 4); + #end + } + return bgra; + } + + /** + Creates PNG data from bytes that contains one bytes (grey values) for each pixel. + **/ + public static function buildGrey( width : Int, height : Int, data : haxe.io.Bytes, ?level = 9 ) : Data { + var rgb = haxe.io.Bytes.alloc(width * height + height); + // translate RGB to BGR and add filter byte + var w = 0, r = 0; + for( y in 0...height ) { + rgb.set(w++,0); // no filter for this scanline + for( x in 0...width ) + rgb.set(w++,data.get(r++)); + } + var l = new List(); + l.add(CHeader({ width : width, height : height, colbits : 8, color : ColGrey(false), interlaced : false })); + l.add(CData(iron.format.tools.Deflate.run(rgb,level))); + l.add(CEnd); + return l; + } + + /** + Creates PNG data from bytes that contains three bytes (R,G and B values) for each pixel. + **/ + public static function buildRGB( width : Int, height : Int, data : haxe.io.Bytes, ?level = 9 ) : Data { + var rgb = haxe.io.Bytes.alloc(width * height * 3 + height); + // translate RGB to BGR and add filter byte + var w = 0, r = 0; + for( y in 0...height ) { + rgb.set(w++,0); // no filter for this scanline + for( x in 0...width ) { + rgb.set(w++,data.get(r+2)); + rgb.set(w++,data.get(r+1)); + rgb.set(w++,data.get(r)); + r += 3; + } + } + var l = new List(); + l.add(CHeader({ width : width, height : height, colbits : 8, color : ColTrue(false), interlaced : false })); + l.add(CData(iron.format.tools.Deflate.run(rgb,level))); + l.add(CEnd); + return l; + } + + /** + Creates PNG data from bytes that contains four bytes in ARGB format for each pixel. + **/ + public static function build32ARGB( width : Int, height : Int, data : haxe.io.Bytes, ?level = 9 ) : Data { + var rgba = haxe.io.Bytes.alloc(width * height * 4 + height); + // translate ARGB to RGBA and add filter byte + var w = 0, r = 0; + for( y in 0...height ) { + rgba.set(w++,0); // no filter for this scanline + for( x in 0...width ) { + rgba.set(w++,data.get(r+1)); // r + rgba.set(w++,data.get(r+2)); // g + rgba.set(w++,data.get(r+3)); // b + rgba.set(w++,data.get(r)); // a + r += 4; + } + } + var l = new List(); + l.add(CHeader({ width : width, height : height, colbits : 8, color : ColTrue(true), interlaced : false })); + l.add(CData(iron.format.tools.Deflate.run(rgba,level))); + l.add(CEnd); + return l; + } + + /** + Creates PNG data from bytes that contains four bytes in BGRA format for each pixel. + **/ + public static function build32BGRA( width : Int, height : Int, data : haxe.io.Bytes, ?level = 9 ) : Data { + var rgba = haxe.io.Bytes.alloc(width * height * 4 + height); + // translate ARGB to RGBA and add filter byte + var w = 0, r = 0; + for( y in 0...height ) { + rgba.set(w++,0); // no filter for this scanline + for( x in 0...width ) { + rgba.set(w++,data.get(r+2)); // r + rgba.set(w++,data.get(r+1)); // g + rgba.set(w++,data.get(r)); // b + rgba.set(w++,data.get(r+3)); // a + r += 4; + } + } + var l = new List(); + l.add(CHeader({ width : width, height : height, colbits : 8, color : ColTrue(true), interlaced : false })); + l.add(CData(iron.format.tools.Deflate.run(rgba,level))); + l.add(CEnd); + return l; + } + +} diff --git a/Libraries/iron_format/Sources/iron/format/png/Writer.hx b/Libraries/iron_format/Sources/iron/format/png/Writer.hx new file mode 100644 index 00000000..2f5b3a0c --- /dev/null +++ b/Libraries/iron_format/Sources/iron/format/png/Writer.hx @@ -0,0 +1,99 @@ +/* + * format - haXe File Formats + * + * Copyright (c) 2008-2009, The haXe Project Contributors + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE HAXE PROJECT CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE HAXE PROJECT CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + */ +package iron.format.png; +import iron.format.png.Data; + +class Writer { + + var o : haxe.io.Output; + + public function new(o) { + this.o = o; + o.bigEndian = true; + } + + public function write( png : Data ) { + for( b in [137,80,78,71,13,10,26,10] ) + o.writeByte(b); + for( c in png ) + switch( c ) { + case CHeader(h): + var b = new haxe.io.BytesOutput(); + b.bigEndian = true; + #if haxe3 + b.writeInt32(h.width); + b.writeInt32(h.height); + #else + b.writeUInt30(h.width); + b.writeUInt30(h.height); + #end + b.writeByte(h.colbits); + b.writeByte(switch( h.color ) { + case ColGrey(alpha): alpha ? 4 : 0; + case ColTrue(alpha): alpha ? 6 : 2; + case ColIndexed: 3; + }); + b.writeByte(0); + b.writeByte(0); + b.writeByte(h.interlaced ? 1 : 0); + writeChunk("IHDR",b.getBytes()); + case CEnd: + writeChunk("IEND",haxe.io.Bytes.alloc(0)); + case CData(d): + writeChunk("IDAT",d); + case CPalette(b): + writeChunk("PLTE",b); + case CUnknown(id,data): + writeChunk(id,data); + } + } + + function writeChunk( id : String, data : haxe.io.Bytes ) { + #if haxe3 + o.writeInt32(data.length); + #else + o.writeUInt30(data.length); + #end + o.writeString(id); + o.write(data); + // compute CRC + #if haxe3 + var crc = new haxe.crypto.Crc32(); + for( i in 0...4 ) + crc.byte(id.charCodeAt(i)); + crc.update(data, 0, data.length); + o.writeInt32(crc.get()); + #else + var crc = new format.tools.CRC32(); + for( i in 0...4 ) + crc.byte(id.charCodeAt(i)); + crc.run(data); + o.writeInt32(crc.get()); + #end + } + +} diff --git a/Libraries/iron_format/Sources/iron/format/tools/Deflate.hx b/Libraries/iron_format/Sources/iron/format/tools/Deflate.hx new file mode 100644 index 00000000..e9639538 --- /dev/null +++ b/Libraries/iron_format/Sources/iron/format/tools/Deflate.hx @@ -0,0 +1,61 @@ +/* + * format - haXe File Formats + * + * Copyright (c) 2008, The haXe Project Contributors + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE HAXE PROJECT CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE HAXE PROJECT CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + */ +package iron.format.tools; + +class Deflate { + + public static function run( b : haxe.io.Bytes, ?level = 9 ) : haxe.io.Bytes { + // #if (haxe_ver >= 3.2) + + // return haxe.zip.Compress.run(b,level); + + var input = haxe.io.UInt8Array.fromBytes(b); + var output = iron.format.pako.Pako.deflate(input); + return output.view.buffer; + + // #else + // // legacy + // #if neko + // return neko.zip.Compress.run(b,level); + // #elseif flash9 + // var bytes = b.sub(0,b.length); + // var data = bytes.getData(); + // data.compress(); + // return haxe.io.Bytes.ofData(data); + // #elseif cpp + // return cpp.zip.Compress.run(b,level); + // #elseif ( java || php ) + // return haxe.zip.Compress.run(b,level); + // #else + // throw "Deflate is not supported on this platform"; + // return null; + // #end + + // #end + } + +} diff --git a/Libraries/iron_format/Sources/iron/format/tools/Inflate.hx b/Libraries/iron_format/Sources/iron/format/tools/Inflate.hx new file mode 100644 index 00000000..3f45e677 --- /dev/null +++ b/Libraries/iron_format/Sources/iron/format/tools/Inflate.hx @@ -0,0 +1,57 @@ +/* + * format - haXe File Formats + * + * Copyright (c) 2008, The haXe Project Contributors + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE HAXE PROJECT CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE HAXE PROJECT CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + */ +package iron.format.tools; + +class Inflate { + + public static function run( bytes : haxe.io.Bytes ) { + // #if (haxe_ver >= 3.2) + + return haxe.zip.Uncompress.run(bytes); + + // #else + + // // legacy support + // #if neko + // return neko.zip.Uncompress.run(bytes); + // #elseif cpp + // return cpp.zip.Uncompress.run(bytes); + // #elseif flash9 + // var b = new flash.utils.ByteArray(); + // b.writeBytes(bytes.getData(),0,bytes.length); + // b.uncompress(); + // return haxe.io.Bytes.ofData(b); + // #elseif php + // return haxe.zip.Uncompress.run(bytes); + // #else + // return InflateImpl.run(new haxe.io.BytesInput(bytes)); + // #end + + // #end + } + +} diff --git a/Libraries/paint/__pycache__/blender.cpython-35.pyc b/Libraries/paint/__pycache__/blender.cpython-35.pyc new file mode 100644 index 00000000..9fe3627b Binary files /dev/null and b/Libraries/paint/__pycache__/blender.cpython-35.pyc differ diff --git a/Libraries/paint/blender.py b/Libraries/paint/blender.py new file mode 100644 index 00000000..abf6149c --- /dev/null +++ b/Libraries/paint/blender.py @@ -0,0 +1,45 @@ +import arm.material.mat_utils +import arm.material.make_shader +import arm.material.mat_state as mat_state +import arm.material.make_mesh + +def register(): + # arm.material.mat_utils.add_mesh_contexts = ['depth'] + arm.material.make_mesh.write_material_attribs = write_material_attribs + +def write_material_attribs(con, frag): + + if mat_state.material.name != 'Material': + return False + + con.add_elem('tex', 2) + # con.add_elem('tang', 3) + + frag.write('vec3 basecol;') + frag.write('float roughness;') + frag.write('float metallic;') + frag.write('float occlusion;') + frag.write('float opacity;') + + frag.add_uniform('sampler2D texpaint') + frag.write('basecol = pow(texture(texpaint, texCoord).rgb, vec3(2.2));') + + frag.add_uniform('sampler2D texpaint_nor') + # frag.write('vec3 n = texture(texpaint_nor, texCoord).rgb * 2.0 - 1.0;') + frag.add_include('../../Shaders/std/normals.glsl') + frag.write('mat3 TBN = cotangentFrame(n, -vVec, texCoord);') + frag.write('n = texture(texpaint_nor, texCoord).rgb * 2.0 - 1.0;') + frag.write('n = normalize(TBN * normalize(n));') + + frag.add_uniform('sampler2D texpaint_pack') + frag.write('vec4 pack = texture(texpaint_pack, texCoord);') + frag.write('occlusion = pack.r;') + frag.write('roughness = pack.g;') + frag.write('metallic = pack.b;') + + # # TODO: Sample disp at neightbour points to calc normal + # tese.add_uniform('sampler2D texpaint_pack') + # tese.write('vec4 pack = texture(texpaint_pack, texCoord);') + # tese.write('disp = pack.b * 0.05;') + + return True diff --git a/Sources/arm/App.hx b/Sources/arm/App.hx new file mode 100644 index 00000000..6f31f9e2 --- /dev/null +++ b/Sources/arm/App.hx @@ -0,0 +1,21 @@ +package arm; + +class App { + + public static function w():Int { + if (UINodes.show) return Std.int((kha.System.windowWidth() - UITrait.windowW) / 2); + else return kha.System.windowWidth() - UITrait.windowW; + } + + public static function h():Int { + return kha.System.windowHeight(); + } + + public static function realw():Int { + return kha.System.windowWidth(); + } + + public static function realh():Int { + return kha.System.windowHeight(); + } +} diff --git a/Sources/arm/CamBall.hx b/Sources/arm/CamBall.hx new file mode 100644 index 00000000..4ac61b8a --- /dev/null +++ b/Sources/arm/CamBall.hx @@ -0,0 +1,45 @@ +package arm; + +import iron.object.Object; +import iron.Trait; +import iron.system.Input; +import iron.math.Vec4; +import iron.math.Quat; + +@:keep +class CamBall extends Trait { + + public static var moved = false; + + public function new() { + super(); + + notifyOnUpdate(update); + } + + function update() { + if (Input.occupied) return; + if (!UITrait.uienabled) return; + if (UITrait.isScrolling) return; + if (UITrait.isDragging) return; + if (UITrait.cameraType != 0) return; + if (!object.visible) return; + + var mouse = Input.getMouse(); + + // Paint bounds + if (mouse.x > iron.App.w()) return; + // if (UINodes.show && mouse.y > UINodes.wy) return; + // if (UINodesBrush.show && mouse.y > UINodesBrush.wy) return; + + if (mouse.down("right")) { + UITrait.dirty = true; + + // Rotate + object.transform.rotate(new Vec4(0, 0, 1), mouse.movementX / 100); + object.transform.buildMatrix(); + object.transform.rotate(object.transform.world.right(), mouse.movementY / 100); + object.transform.buildMatrix(); + } + } +} diff --git a/Sources/arm/FlyCamera.hx b/Sources/arm/FlyCamera.hx new file mode 100644 index 00000000..9bb1fa6e --- /dev/null +++ b/Sources/arm/FlyCamera.hx @@ -0,0 +1,125 @@ +package arm; + +import iron.Trait; +import iron.system.Input; +import iron.system.Time; +import iron.object.CameraObject; +import iron.math.Vec4; + +class FlyCamera extends Trait { + + public static var enabled = false; + + static inline var speed = 2.0; + var dir = new Vec4(); + var xvec = new Vec4(); + var yvec = new Vec4(); + var easing = true; + var ease = 1.0; + + var camera:CameraObject; + + var keyboard:Keyboard; + var gamepad:Gamepad; + var mouse:Mouse; + + public function new(easing = true) { + super(); + + this.easing = easing; + notifyOnInit(init); + notifyOnUpdate(update); + } + + function init() { + keyboard = Input.getKeyboard(); + gamepad = Input.getGamepad(); + mouse = Input.getMouse(); + + camera = cast(object, CameraObject); + } + + function update() { + if (Input.occupied) return; + + if (UITrait.cameraType != 1) return; + + var moveForward = keyboard.down("w") || keyboard.down("up"); + var moveBackward = keyboard.down("s") || keyboard.down("down"); + var strafeLeft = keyboard.down("a") || keyboard.down("left"); + var strafeRight = keyboard.down("d") || keyboard.down("right"); + var strafeUp = keyboard.down("e"); + var strafeDown = keyboard.down("q"); + var fast = keyboard.down("shift") ? 2.0 : (keyboard.down("alt") ? 0.5 : 1.0); + + // if (gamepad != null) { + // var leftStickY = Math.abs(gamepad.leftStick.y) > 0.05; + // var leftStickX = Math.abs(gamepad.leftStick.x) > 0.05; + // var r1 = gamepad.down("r1") > 0.0; + // var l1 = gamepad.down("l1") > 0.0; + // var rightStickX = Math.abs(gamepad.rightStick.x) > 0.1; + // var rightStickY = Math.abs(gamepad.rightStick.y) > 0.1; + + // if (leftStickY || leftStickX || r1 || l1 || rightStickX || rightStickY) { + // dir.set(0, 0, 0); + + // if (leftStickY) { + // yvec.setFrom(camera.look()); + // yvec.mult(gamepad.leftStick.y); + // dir.add(yvec); + // } + // if (leftStickX) { + // xvec.setFrom(camera.right()); + // xvec.mult(gamepad.leftStick.x); + // dir.add(xvec); + // } + // if (r1) dir.addf(0, 0, 1); + // if (l1) dir.addf(0, 0, -1); + + // var d = Time.delta * speed * fast; + // camera.move(dir, d); + + // if (rightStickX) { + // camera.rotate(Vec4.zAxis(), -gamepad.rightStick.x / 15.0); + // } + // if (rightStickY) { + // camera.rotate(camera.right(), gamepad.rightStick.y / 15.0); + // } + // } + // } + + if (moveForward || moveBackward || strafeRight || strafeLeft || strafeUp || strafeDown) { + if (easing) { + ease += Time.delta * 15; + if (ease > 1.0) ease = 1.0; + } + else ease = 1.0; + dir.set(0, 0, 0); + if (moveForward) dir.addf(camera.look().x, camera.look().y, camera.look().z); + if (moveBackward) dir.addf(-camera.look().x, -camera.look().y, -camera.look().z); + if (strafeRight) dir.addf(camera.right().x, camera.right().y, camera.right().z); + if (strafeLeft) dir.addf(-camera.right().x, -camera.right().y, -camera.right().z); + if (strafeUp) dir.addf(0, 0, 1); + if (strafeDown) dir.addf(0, 0, -1); + } + else { + if (easing) { + ease -= Time.delta * 20.0 * ease; + if (ease < 0.0) ease = 0.0; + } + else ease = 0.0; + } + + var d = Time.delta * speed * fast * ease; + if (d > 0.0) { + UITrait.dirty = true; + camera.move(dir, d); + } + + if (mouse.down("right")) { + UITrait.dirty = true; + camera.rotate(Vec4.zAxis(), -mouse.movementX / 200); + camera.rotate(camera.right(), -mouse.movementY / 200); + } + } +} diff --git a/Sources/arm/NodeCreator.hx b/Sources/arm/NodeCreator.hx new file mode 100644 index 00000000..8d78f05a --- /dev/null +++ b/Sources/arm/NodeCreator.hx @@ -0,0 +1,1337 @@ +package arm; + +import armory.object.Object; +import armory.system.Cycles; +import zui.*; +import zui.Nodes; +import iron.data.SceneFormat; +import iron.data.MaterialData; + +@:access(arm.UINodes) +class NodeCreator { + + public static function createImageTexture(uinodes:UINodes) { + var getNodeX = uinodes.getNodeX; + var getNodeY = uinodes.getNodeY; + var nodes = UINodes.nodes; + var canvas = UINodes.canvas; + + var node_id = nodes.getNodeId(canvas.nodes); + var n:TNode = { + id: node_id, + name: "Image Texture", + type: "TEX_IMAGE", + x: getNodeX(), + y: getNodeY(), + color: 0xff4982a0, + inputs: [ + { + id: nodes.getSocketId(canvas.nodes), + node_id: node_id, + name: "Vector", + type: "VECTOR", + color: 0xff6363c7, + default_value: [0.0, 0.0, 0.0] + } + ], + outputs: [ + { + id: nodes.getSocketId(canvas.nodes), + node_id: node_id, + name: "Color", + type: "RGBA", + color: 0xffc7c729, + default_value: "" + }, + { + id: nodes.getSocketId(canvas.nodes), + node_id: node_id, + name: "Alpha", + type: "VALUE", + color: 0xffa1a1a1, + default_value: 1.0 + } + ], + buttons: [ + { + name: "default_value", + type: "ENUM", + default_value: 0, + output: 0 + } + ] + }; + canvas.nodes.push(n); + nodes.nodeDrag = n; + nodes.nodeSelected = n; + } + + public static var numNodes = [6, 1, 5, 5, 9]; + + public static function draw(uinodes:UINodes, cat:Int) { + var ui = uinodes.ui; + var getNodeX = uinodes.getNodeX; + var getNodeY = uinodes.getNodeY; + var nodes = UINodes.nodes; + var canvas = UINodes.canvas; + + if (cat == 0) { // Input + if (ui.button("RGB")) { + var node_id = nodes.getNodeId(canvas.nodes); + var n:TNode = { + id: node_id, + name: "RGB", + type: "RGB", + x: getNodeX(), + y: getNodeY(), + color: 0xffb34f5a, + inputs: [], + outputs: [ + { + id: nodes.getSocketId(canvas.nodes), + node_id: node_id, + name: "Color", + type: "RGBA", + color: 0xffc7c729, + default_value: [0.5, 0.5, 0.5, 1.0] + } + ], + buttons: [ + { + name: "default_value", + type: "RGBA", + output: 0 + } + ] + }; + canvas.nodes.push(n); + nodes.nodeDrag = n; + nodes.nodeSelected = n; + } + if (ui.button("Value")) { + var node_id = nodes.getNodeId(canvas.nodes); + var n:TNode = { + id: node_id, + name: "Value", + type: "VALUE", + x: getNodeX(), + y: getNodeY(), + color: 0xffb34f5a, + inputs: [], + outputs: [ + { + id: nodes.getSocketId(canvas.nodes), + node_id: node_id, + name: "Value", + type: "VALUE", + color: 0xffa1a1a1, + default_value: 0.5 + } + ], + buttons: [ + { + name: "default_value", + type: "VALUE", + output: 0, + min: 0.0, + max: 10.0 + } + ] + }; + canvas.nodes.push(n); + nodes.nodeDrag = n; + nodes.nodeSelected = n; + } + if (ui.button("Camera Data")) { + var node_id = nodes.getNodeId(canvas.nodes); + var n:TNode = { + id: node_id, + name: "Camera Data", + type: "CAMERA", + x: getNodeX(), + y: getNodeY(), + color: 0xff4982a0, + inputs: [], + outputs: [ + { + id: nodes.getSocketId(canvas.nodes), + node_id: node_id, + name: "View Vector", + type: "VECTOR", + color: 0xff6363c7, + default_value: [0.0, 0.0, 0.0] + }, + { + id: nodes.getSocketId(canvas.nodes), + node_id: node_id, + name: "View Z Depth", + type: "VALUE", + color: 0xffa1a1a1, + default_value: 0.0 + }, + { + id: nodes.getSocketId(canvas.nodes), + node_id: node_id, + name: "View Distance", + type: "VALUE", + color: 0xffa1a1a1, + default_value: 0.0 + } + ], + buttons: [] + }; + canvas.nodes.push(n); + nodes.nodeDrag = n; + nodes.nodeSelected = n; + } + if (ui.button("Geometry")) { + var node_id = nodes.getNodeId(canvas.nodes); + var n:TNode = { + id: node_id, + name: "Geometry", + type: "NEW_GEOMETRY", + x: getNodeX(), + y: getNodeY(), + color: 0xff4982a0, + inputs: [], + outputs: [ + { + id: nodes.getSocketId(canvas.nodes), + node_id: node_id, + name: "Position", + type: "VECTOR", + color: 0xff6363c7, + default_value: [0.0, 0.0, 0.0] + }, + { + id: nodes.getSocketId(canvas.nodes), + node_id: node_id, + name: "Normal", + type: "VECTOR", + color: 0xff6363c7, + default_value: [0.0, 0.0, 0.0] + } + ], + buttons: [] + }; + canvas.nodes.push(n); + nodes.nodeDrag = n; + nodes.nodeSelected = n; + } + if (ui.button("Fresnel")) { + var node_id = nodes.getNodeId(canvas.nodes); + var n:TNode = { + id: node_id, + name: "Fresnel", + type: "FRESNEL", + x: getNodeX(), + y: getNodeY(), + color: 0xff4982a0, + inputs: [ + { + id: nodes.getSocketId(canvas.nodes), + node_id: node_id, + name: "IOR", + type: "VALUE", + color: 0xffa1a1a1, + default_value: 1.450, + min: 0.0, + max: 2.0 + } + ], + outputs: [ + { + id: nodes.getSocketId(canvas.nodes), + node_id: node_id, + name: "Fac", + type: "VALUE", + color: 0xffa1a1a1, + default_value: 0.0 + } + ], + buttons: [] + }; + canvas.nodes.push(n); + nodes.nodeDrag = n; + nodes.nodeSelected = n; + } + if (ui.button("Layer Weight")) { + var node_id = nodes.getNodeId(canvas.nodes); + var n:TNode = { + id: node_id, + name: "Layer Weight", + type: "LAYER_WEIGHT", + x: getNodeX(), + y: getNodeY(), + color: 0xff4982a0, + inputs: [ + { + id: nodes.getSocketId(canvas.nodes), + node_id: node_id, + name: "Blend", + type: "VALUE", + color: 0xffa1a1a1, + default_value: 0.5 + } + ], + outputs: [ + { + id: nodes.getSocketId(canvas.nodes), + node_id: node_id, + name: "Fresnel", + type: "VALUE", + color: 0xffa1a1a1, + default_value: 0.0 + }, + { + id: nodes.getSocketId(canvas.nodes), + node_id: node_id, + name: "Facing", + type: "VALUE", + color: 0xffa1a1a1, + default_value: 0.0 + } + ], + buttons: [] + }; + canvas.nodes.push(n); + nodes.nodeDrag = n; + nodes.nodeSelected = n; + } + } + if (cat == 1) { // Output + if (ui.button("Material Output")) { + + var node_id = nodes.getNodeId(canvas.nodes); + var n:TNode = { + id: node_id, + name: "Material Output", + type: "OUTPUT_MATERIAL_PBR", + x: getNodeX(), + y: getNodeY(), + color: 0xffb34f5a, + inputs: [ + { + id: nodes.getSocketId(canvas.nodes), + node_id: node_id, + name: "Base Color", + type: "RGBA", + color: 0xffc7c729, + default_value: [0.8, 0.8, 0.8, 1.0] + }, + { + id: nodes.getSocketId(canvas.nodes), + node_id: node_id, + name: "Opacity", + type: "VALUE", + color: 0xffa1a1a1, + default_value: 1.0 + }, + { + id: nodes.getSocketId(canvas.nodes), + node_id: node_id, + name: "Occlusion", + type: "VALUE", + color: 0xffa1a1a1, + default_value: 1.0 + }, + { + id: nodes.getSocketId(canvas.nodes), + node_id: node_id, + name: "Roughness", + type: "VALUE", + color: 0xffa1a1a1, + default_value: 0.1 + }, + { + id: nodes.getSocketId(canvas.nodes), + node_id: node_id, + name: "Metallic", + type: "VALUE", + color: 0xffa1a1a1, + default_value: 0.0 + }, + { + id: nodes.getSocketId(canvas.nodes), + node_id: node_id, + name: "Normal Map", + type: "VECTOR", + color: 0xff63c763, + default_value: [0.5, 0.5, 1.0] + } + // , + // { + // id: nodes.getSocketId(canvas.nodes), + // node_id: node_id, + // name: "Emission", + // type: "VALUE", + // color: 0xffa1a1a1, + // default_value: 0.0 + // }, + // { + // id: nodes.getSocketId(canvas.nodes), + // node_id: node_id, + // name: "Height", + // type: "VALUE", + // color: 0xffa1a1a1, + // default_value: 0.0 + // }, + // { + // id: nodes.getSocketId(canvas.nodes), + // node_id: node_id, + // name: "Subsurface", + // type: "VALUE", + // color: 0xffa1a1a1, + // default_value: 0.0 + // } + ], + outputs: [], + buttons: [] + }; + canvas.nodes.push(n); + nodes.nodeDrag = n; + nodes.nodeSelected = n; + } + } + if (cat == 2) { // Texture + if (ui.button("Image")) { + createImageTexture(uinodes); + } + if (ui.button("Checker")) { + var node_id = nodes.getNodeId(canvas.nodes); + var n:TNode = { + id: node_id, + name: "Checker Texture", + type: "TEX_CHECKER", + x: getNodeX(), + y: getNodeY(), + color: 0xff4982a0, + inputs: [ + { + id: nodes.getSocketId(canvas.nodes), + node_id: node_id, + name: "Vector", + type: "VECTOR", + color: 0xff6363c7, + default_value: [0.0, 0.0, 0.0] + }, + { + id: nodes.getSocketId(canvas.nodes), + node_id: node_id, + name: "Color 1", + type: "RGB", + color: 0xffc7c729, + default_value: [0.8, 0.8, 0.8] + }, + { + id: nodes.getSocketId(canvas.nodes), + node_id: node_id, + name: "Color 2", + type: "RGB", + color: 0xffc7c729, + default_value: [0.2, 0.2, 0.2] + }, + { + id: nodes.getSocketId(canvas.nodes), + node_id: node_id, + name: "Scale", + type: "VALUE", + color: 0xffa1a1a1, + default_value: 1.0, + min: 0.0, + max: 10.0 + } + ], + outputs: [ + { + id: nodes.getSocketId(canvas.nodes), + node_id: node_id, + name: "Color", + type: "RGBA", + color: 0xffc7c729, + default_value: [0.8, 0.8, 0.8] + }, + { + id: nodes.getSocketId(canvas.nodes), + node_id: node_id, + name: "Fac", + type: "VALUE", + color: 0xffa1a1a1, + default_value: 1.0 + } + ], + buttons: [] + }; + canvas.nodes.push(n); + nodes.nodeDrag = n; + nodes.nodeSelected = n; + } + if (ui.button("Gradient")) { + var node_id = nodes.getNodeId(canvas.nodes); + var n:TNode = { + id: node_id, + name: "Gradient Texture", + type: "TEX_GRADIENT", + x: getNodeX(), + y: getNodeY(), + color: 0xff4982a0, + inputs: [ + { + id: nodes.getSocketId(canvas.nodes), + node_id: node_id, + name: "Vector", + type: "VECTOR", + color: 0xff6363c7, + default_value: [0.0, 0.0, 0.0] + } + ], + outputs: [ + { + id: nodes.getSocketId(canvas.nodes), + node_id: node_id, + name: "Color", + type: "RGBA", + color: 0xffc7c729, + default_value: [0.8, 0.8, 0.8] + }, + { + id: nodes.getSocketId(canvas.nodes), + node_id: node_id, + name: "Fac", + type: "VALUE", + color: 0xffa1a1a1, + default_value: 1.0 + } + ], + buttons: [ + { + name: "gradient_type", + type: "ENUM", + // data: ["Linear", "Quadratic", "Easing", "Diagonal", "Radial", "Quadratic Sphere", "Spherical"], + data: ["Linear", "Diagonal", "Radial", "Spherical"], + default_value: 0, + output: 0 + } + ] + }; + canvas.nodes.push(n); + nodes.nodeDrag = n; + nodes.nodeSelected = n; + } + if (ui.button("Noise")) { + var node_id = nodes.getNodeId(canvas.nodes); + var n:TNode = { + id: node_id, + name: "Noise Texture", + type: "TEX_NOISE", + x: getNodeX(), + y: getNodeY(), + color: 0xff4982a0, + inputs: [ + { + id: nodes.getSocketId(canvas.nodes), + node_id: node_id, + name: "Vector", + type: "VECTOR", + color: 0xff6363c7, + default_value: [0.0, 0.0, 0.0] + }, + { + id: nodes.getSocketId(canvas.nodes), + node_id: node_id, + name: "Scale", + type: "VALUE", + color: 0xffa1a1a1, + default_value: 1.0, + min: 0.0, + max: 10.0 + } + ], + outputs: [ + { + id: nodes.getSocketId(canvas.nodes), + node_id: node_id, + name: "Color", + type: "RGBA", + color: 0xffc7c729, + default_value: [0.8, 0.8, 0.8] + }, + { + id: nodes.getSocketId(canvas.nodes), + node_id: node_id, + name: "Fac", + type: "VALUE", + color: 0xffa1a1a1, + default_value: 1.0 + } + ], + buttons: [] + }; + canvas.nodes.push(n); + nodes.nodeDrag = n; + nodes.nodeSelected = n; + } + if (ui.button("Voronoi")) { + var node_id = nodes.getNodeId(canvas.nodes); + var n:TNode = { + id: node_id, + name: "Voronoi Texture", + type: "TEX_VORONOI", + x: getNodeX(), + y: getNodeY(), + color: 0xff4982a0, + inputs: [ + { + id: nodes.getSocketId(canvas.nodes), + node_id: node_id, + name: "Vector", + type: "VECTOR", + color: 0xff6363c7, + default_value: [0.0, 0.0, 0.0] + }, + { + id: nodes.getSocketId(canvas.nodes), + node_id: node_id, + name: "Scale", + type: "VALUE", + color: 0xffa1a1a1, + default_value: 1.0, + min: 0.0, + max: 10.0 + } + ], + outputs: [ + { + id: nodes.getSocketId(canvas.nodes), + node_id: node_id, + name: "Color", + type: "RGBA", + color: 0xffc7c729, + default_value: [0.8, 0.8, 0.8] + }, + { + id: nodes.getSocketId(canvas.nodes), + node_id: node_id, + name: "Fac", + type: "VALUE", + color: 0xffa1a1a1, + default_value: 1.0 + } + ], + buttons: [ + { + name: "coloring", + type: "ENUM", + data: ["Intensity", "Cells"], + default_value: 0, + output: 0 + } + ] + }; + canvas.nodes.push(n); + nodes.nodeDrag = n; + nodes.nodeSelected = n; + } + } + if (cat == 3) { // Color + if (ui.button("BrightContrast")) { + var node_id = nodes.getNodeId(canvas.nodes); + var n:TNode = { + id: node_id, + name: "BrightContrast", + type: "BRIGHTCONTRAST", + x: getNodeX(), + y: getNodeY(), + color: 0xff4982a0, + inputs: [ + { + id: nodes.getSocketId(canvas.nodes), + node_id: node_id, + name: "Color", + type: "RGBA", + color: 0xffc7c729, + default_value: [0.8, 0.8, 0.8] + }, + { + id: nodes.getSocketId(canvas.nodes), + node_id: node_id, + name: "Bright", + type: "VALUE", + color: 0xffa1a1a1, + default_value: 0.0 + }, + { + id: nodes.getSocketId(canvas.nodes), + node_id: node_id, + name: "Contrast", + type: "VALUE", + color: 0xffa1a1a1, + default_value: 0.0 + } + ], + outputs: [ + { + id: nodes.getSocketId(canvas.nodes), + node_id: node_id, + name: "Color", + type: "RGBA", + color: 0xffc7c729, + default_value: [0.8, 0.8, 0.8] + } + ], + buttons: [] + }; + canvas.nodes.push(n); + nodes.nodeDrag = n; + nodes.nodeSelected = n; + } + if (ui.button("Gamma")) { + var node_id = nodes.getNodeId(canvas.nodes); + var n:TNode = { + id: node_id, + name: "Gamma", + type: "GAMMA", + x: getNodeX(), + y: getNodeY(), + color: 0xff4982a0, + inputs: [ + { + id: nodes.getSocketId(canvas.nodes), + node_id: node_id, + name: "Color", + type: "RGBA", + color: 0xffc7c729, + default_value: [0.8, 0.8, 0.8] + }, + { + id: nodes.getSocketId(canvas.nodes), + node_id: node_id, + name: "Gamma", + type: "VALUE", + color: 0xffa1a1a1, + default_value: 1.0 + } + ], + outputs: [ + { + id: nodes.getSocketId(canvas.nodes), + node_id: node_id, + name: "Color", + type: "RGBA", + color: 0xffc7c729, + default_value: [0.8, 0.8, 0.8] + } + ], + buttons: [] + }; + canvas.nodes.push(n); + nodes.nodeDrag = n; + nodes.nodeSelected = n; + } + if (ui.button("HueSatVal")) { + var node_id = nodes.getNodeId(canvas.nodes); + var n:TNode = { + id: node_id, + name: "HueSatVal", + type: "HUE_SAT", + x: getNodeX(), + y: getNodeY(), + color: 0xff4982a0, + inputs: [ + { + id: nodes.getSocketId(canvas.nodes), + node_id: node_id, + name: "Hue", + type: "VALUE", + color: 0xffa1a1a1, + default_value: 0.5 + }, + { + id: nodes.getSocketId(canvas.nodes), + node_id: node_id, + name: "Sat", + type: "VALUE", + color: 0xffa1a1a1, + default_value: 1.0 + }, + { + id: nodes.getSocketId(canvas.nodes), + node_id: node_id, + name: "Val", + type: "VALUE", + color: 0xffa1a1a1, + default_value: 1.0 + } + ], + outputs: [ + { + id: nodes.getSocketId(canvas.nodes), + node_id: node_id, + name: "Color", + type: "RGBA", + color: 0xffc7c729, + default_value: [0.8, 0.8, 0.8] + } + ], + buttons: [] + }; + canvas.nodes.push(n); + nodes.nodeDrag = n; + nodes.nodeSelected = n; + } + if (ui.button("Invert")) { + var node_id = nodes.getNodeId(canvas.nodes); + var n:TNode = { + id: node_id, + name: "Invert", + type: "INVERT", + x: getNodeX(), + y: getNodeY(), + color: 0xff4982a0, + inputs: [ + { + id: nodes.getSocketId(canvas.nodes), + node_id: node_id, + name: "Fac", + type: "VALUE", + color: 0xffa1a1a1, + default_value: 0.5 + }, + { + id: nodes.getSocketId(canvas.nodes), + node_id: node_id, + name: "Color", + type: "RGBA", + color: 0xffc7c729, + default_value: [0.8, 0.8, 0.8] + } + ], + outputs: [ + { + id: nodes.getSocketId(canvas.nodes), + node_id: node_id, + name: "Color", + type: "RGBA", + color: 0xffc7c729, + default_value: [0.8, 0.8, 0.8] + } + ], + buttons: [] + }; + canvas.nodes.push(n); + nodes.nodeDrag = n; + nodes.nodeSelected = n; + } + if (ui.button("MixRGB")) { + var node_id = nodes.getNodeId(canvas.nodes); + var n:TNode = { + id: node_id, + name: "MixRGB", + type: "MIX_RGB", + x: getNodeX(), + y: getNodeY(), + color: 0xff4982a0, + inputs: [ + { + id: nodes.getSocketId(canvas.nodes), + node_id: node_id, + name: "Fac", + type: "VALUE", + color: 0xffa1a1a1, + default_value: 0.5 + }, + { + id: nodes.getSocketId(canvas.nodes), + node_id: node_id, + name: "Color1", + type: "RGBA", + color: 0xffc7c729, + default_value: [0.5, 0.5, 0.5] + }, + { + id: nodes.getSocketId(canvas.nodes), + node_id: node_id, + name: "Color2", + type: "RGBA", + color: 0xffc7c729, + default_value: [0.5, 0.5, 0.5] + } + ], + outputs: [ + { + id: nodes.getSocketId(canvas.nodes), + node_id: node_id, + name: "Color", + type: "RGBA", + color: 0xffc7c729, + default_value: [0.8, 0.8, 0.8] + } + ], + buttons: [ + { + name: "blend_type", + type: "ENUM", + data: ["Mix", "Add", "Multiply", "Subtract", "Screen", "Divide", "Difference", "Darken", "Lighten", "Soft Light"], + default_value: 0, + output: 0 + }, + { + name: "use_clamp", + type: "BOOL", + default_value: "false", + output: 0 + } + ] + }; + canvas.nodes.push(n); + nodes.nodeDrag = n; + nodes.nodeSelected = n; + } + } + if (cat == 4) { // Converter + if (ui.button("Combine RGB")) { + var node_id = nodes.getNodeId(canvas.nodes); + var n:TNode = { + id: node_id, + name: "Combine RGB", + type: "COMBRGB", + x: getNodeX(), + y: getNodeY(), + color: 0xff4982a0, + inputs: [ + { + id: nodes.getSocketId(canvas.nodes), + node_id: node_id, + name: "R", + type: "VALUE", + color: 0xffa1a1a1, + default_value: 0.0 + }, + { + id: nodes.getSocketId(canvas.nodes), + node_id: node_id, + name: "G", + type: "VALUE", + color: 0xffa1a1a1, + default_value: 0.0 + }, + { + id: nodes.getSocketId(canvas.nodes), + node_id: node_id, + name: "B", + type: "VALUE", + color: 0xffa1a1a1, + default_value: 0.0 + } + ], + outputs: [ + { + id: nodes.getSocketId(canvas.nodes), + node_id: node_id, + name: "Color", + type: "RGBA", + color: 0xffc7c729, + default_value: [0.8, 0.8, 0.8] + } + ], + buttons: [] + }; + canvas.nodes.push(n); + nodes.nodeDrag = n; + nodes.nodeSelected = n; + } + if (ui.button("Combine XYZ")) { + var node_id = nodes.getNodeId(canvas.nodes); + var n:TNode = { + id: node_id, + name: "Combine XYZ", + type: "COMBXYZ", + x: getNodeX(), + y: getNodeY(), + color: 0xff4982a0, + inputs: [ + { + id: nodes.getSocketId(canvas.nodes), + node_id: node_id, + name: "X", + type: "VALUE", + color: 0xffa1a1a1, + default_value: 0.0 + }, + { + id: nodes.getSocketId(canvas.nodes), + node_id: node_id, + name: "Y", + type: "VALUE", + color: 0xffa1a1a1, + default_value: 0.0 + }, + { + id: nodes.getSocketId(canvas.nodes), + node_id: node_id, + name: "Z", + type: "VALUE", + color: 0xffa1a1a1, + default_value: 0.0 + } + ], + outputs: [ + { + id: nodes.getSocketId(canvas.nodes), + node_id: node_id, + name: "Vector", + type: "VECTOR", + color: 0xff6363c7, + default_value: [0.0, 0.0, 0.0] + } + ], + buttons: [] + }; + canvas.nodes.push(n); + nodes.nodeDrag = n; + nodes.nodeSelected = n; + } + if (ui.button("Wavelength")) { + var node_id = nodes.getNodeId(canvas.nodes); + var n:TNode = { + id: node_id, + name: "Wavelength", + type: "WAVELENGTH", + x: getNodeX(), + y: getNodeY(), + color: 0xff4982a0, + inputs: [ + { + id: nodes.getSocketId(canvas.nodes), + node_id: node_id, + name: "Wavelength", + type: "VALUE", + color: 0xffa1a1a1, + default_value: 500.0, + min: 450.0, + max: 600.0 + } + ], + outputs: [ + { + id: nodes.getSocketId(canvas.nodes), + node_id: node_id, + name: "Color", + type: "RGBA", + color: 0xffc7c729, + default_value: [0.8, 0.8, 0.8] + } + ], + buttons: [] + }; + canvas.nodes.push(n); + nodes.nodeDrag = n; + nodes.nodeSelected = n; + } + if (ui.button("Mapping")) { + var node_id = nodes.getNodeId(canvas.nodes); + var n:TNode = { + id: node_id, + name: "Mapping", + type: "MAPPING", + x: getNodeX(), + y: getNodeY(), + color: 0xff4982a0, + inputs: [ + { + id: nodes.getSocketId(canvas.nodes), + node_id: node_id, + name: "Vector", + type: "VECTOR", + color: 0xff6363c7, + default_value: [0.0, 0.0, 0.0] + }, + { + id: nodes.getSocketId(canvas.nodes), + node_id: node_id, + name: "Translation", + type: "VECTOR", + color: 0xff6363c7, + default_value: [0.0, 0.0, 0.0] + }, + { + id: nodes.getSocketId(canvas.nodes), + node_id: node_id, + name: "Scale", + type: "VECTOR", + color: 0xff6363c7, + default_value: [1.0, 1.0, 1.0] + } + ], + outputs: [ + { + id: nodes.getSocketId(canvas.nodes), + node_id: node_id, + name: "Vector", + type: "VECTOR", + color: 0xff6363c7, + default_value: [0.0, 0.0, 0.0] + } + ], + buttons: [] + }; + canvas.nodes.push(n); + nodes.nodeDrag = n; + nodes.nodeSelected = n; + } + if (ui.button("Vector Math")) { + var node_id = nodes.getNodeId(canvas.nodes); + var n:TNode = { + id: node_id, + name: "Vector Math", + type: "VECT_MATH", + x: getNodeX(), + y: getNodeY(), + color: 0xff4982a0, + inputs: [ + { + id: nodes.getSocketId(canvas.nodes), + node_id: node_id, + name: "Vector", + type: "VECTOR", + color: 0xff6363c7, + default_value: [0.0, 0.0, 0.0] + }, + { + id: nodes.getSocketId(canvas.nodes), + node_id: node_id, + name: "Vector", + type: "VECTOR", + color: 0xff6363c7, + default_value: [0.0, 0.0, 0.0] + } + ], + outputs: [ + { + id: nodes.getSocketId(canvas.nodes), + node_id: node_id, + name: "Vector", + type: "VECTOR", + color: 0xff6363c7, + default_value: [0.0, 0.0, 0.0] + }, + { + id: nodes.getSocketId(canvas.nodes), + node_id: node_id, + name: "Value", + type: "VALUE", + color: 0xffa1a1a1, + default_value: 0.0 + } + ], + buttons: [ + { + name: "operation", + type: "ENUM", + data: ["Add", "Subtract", "Average", "Dot Product", "Cross Product", "Normalize"], + default_value: 0, + output: 0 + } + ] + }; + canvas.nodes.push(n); + nodes.nodeDrag = n; + nodes.nodeSelected = n; + } + if (ui.button("Math")) { + var node_id = nodes.getNodeId(canvas.nodes); + var n:TNode = { + id: node_id, + name: "Math", + type: "MATH", + x: getNodeX(), + y: getNodeY(), + color: 0xff4982a0, + inputs: [ + { + id: nodes.getSocketId(canvas.nodes), + node_id: node_id, + name: "Value", + type: "VALUE", + color: 0xffa1a1a1, + default_value: 0.5 + }, + { + id: nodes.getSocketId(canvas.nodes), + node_id: node_id, + name: "Value", + type: "VALUE", + color: 0xffa1a1a1, + default_value: 0.5 + } + ], + outputs: [ + { + id: nodes.getSocketId(canvas.nodes), + node_id: node_id, + name: "Value", + type: "VALUE", + color: 0xffa1a1a1, + default_value: 0.0 + } + ], + buttons: [ + { + name: "operation", + type: "ENUM", + data: ["Add", "Subtract", "Multiply", "Divide", "Sine", "Cosine", "Tangent", "Arcsine", "Arccosine", "Arctangent", "Power", "Logarithm", "Minimum", "Maximum", "Round", "Less Than", "Greater Than", "Module", "Absolute"], + default_value: 0, + output: 0 + }, + { + name: "use_clamp", + type: "BOOL", + default_value: "false", + output: 0 + } + ] + }; + canvas.nodes.push(n); + nodes.nodeDrag = n; + nodes.nodeSelected = n; + } + if (ui.button("RGB to BW")) { + var node_id = nodes.getNodeId(canvas.nodes); + var n:TNode = { + id: node_id, + name: "RGB to BW", + type: "RGBTOBW", + x: getNodeX(), + y: getNodeY(), + color: 0xff4982a0, + inputs: [ + { + id: nodes.getSocketId(canvas.nodes), + node_id: node_id, + name: "Color", + type: "RGBA", + color: 0xffc7c729, + default_value: "" + } + ], + outputs: [ + { + id: nodes.getSocketId(canvas.nodes), + node_id: node_id, + name: "Val", + type: "VALUE", + color: 0xffa1a1a1, + default_value: 0.0 + } + ], + buttons: [] + }; + canvas.nodes.push(n); + nodes.nodeDrag = n; + nodes.nodeSelected = n; + } + if (ui.button("Separate RGB")) { + var node_id = nodes.getNodeId(canvas.nodes); + var n:TNode = { + id: node_id, + name: "Separate RGB", + type: "SEPRGB", + x: getNodeX(), + y: getNodeY(), + color: 0xff4982a0, + inputs: [ + { + id: nodes.getSocketId(canvas.nodes), + node_id: node_id, + name: "Color", + type: "RGBA", + color: 0xffc7c729, + default_value: [0.8, 0.8, 0.8] + } + ], + outputs: [ + { + id: nodes.getSocketId(canvas.nodes), + node_id: node_id, + name: "R", + type: "VALUE", + color: 0xffa1a1a1, + default_value: 0.0 + }, + { + id: nodes.getSocketId(canvas.nodes), + node_id: node_id, + name: "G", + type: "VALUE", + color: 0xffa1a1a1, + default_value: 0.0 + }, + { + id: nodes.getSocketId(canvas.nodes), + node_id: node_id, + name: "B", + type: "VALUE", + color: 0xffa1a1a1, + default_value: 0.0 + } + ], + buttons: [] + }; + canvas.nodes.push(n); + nodes.nodeDrag = n; + nodes.nodeSelected = n; + } + if (ui.button("Separate XYZ")) { + var node_id = nodes.getNodeId(canvas.nodes); + var n:TNode = { + id: node_id, + name: "Separate XYZ", + type: "SEPXYZ", + x: getNodeX(), + y: getNodeY(), + color: 0xff4982a0, + inputs: [ + { + id: nodes.getSocketId(canvas.nodes), + node_id: node_id, + name: "Vector", + type: "VECTOR", + color: 0xff6363c7, + default_value: [0.0, 0.0, 0.0] + } + ], + outputs: [ + { + id: nodes.getSocketId(canvas.nodes), + node_id: node_id, + name: "X", + type: "VALUE", + color: 0xffa1a1a1, + default_value: 0.0 + }, + { + id: nodes.getSocketId(canvas.nodes), + node_id: node_id, + name: "Y", + type: "VALUE", + color: 0xffa1a1a1, + default_value: 0.0 + }, + { + id: nodes.getSocketId(canvas.nodes), + node_id: node_id, + name: "Z", + type: "VALUE", + color: 0xffa1a1a1, + default_value: 0.0 + } + ], + buttons: [] + }; + canvas.nodes.push(n); + nodes.nodeDrag = n; + nodes.nodeSelected = n; + } + } + } +} diff --git a/Sources/arm/OrbitCamera.hx b/Sources/arm/OrbitCamera.hx new file mode 100644 index 00000000..3bfe5fd3 --- /dev/null +++ b/Sources/arm/OrbitCamera.hx @@ -0,0 +1,39 @@ +package arm; + +class OrbitCamera extends armory.Trait { + + public static var enabled = false; + + public function new() { + super(); + + notifyOnUpdate(function() { + if (iron.system.Input.occupied) return; + if (!UITrait.uienabled) return; + if (UITrait.isScrolling) return; + if (UITrait.isDragging) return; + if (UITrait.cameraType != 0) return; + + var mouse = armory.system.Input.getMouse(); + + if (mouse.x > iron.App.w()) return; + // if (UINodes.show && mouse.y > UINodes.wy) return; + + var keyboard = armory.system.Input.getKeyboard(); + var camera = cast(object, iron.object.CameraObject); + + if (mouse.wheelDelta != 0) { + UITrait.dirty = true; + + camera.move(camera.look(), mouse.wheelDelta * (-0.1)); + } + + if (mouse.down("middle") || (mouse.down("right") && keyboard.down("space"))) { + UITrait.dirty = true; + + camera.transform.loc.addf(-mouse.movementX / 150, 0.0, mouse.movementY / 150); + camera.buildMatrix(); + } + }); + } +} diff --git a/Sources/arm/UINodes.hx b/Sources/arm/UINodes.hx new file mode 100644 index 00000000..7832d73a --- /dev/null +++ b/Sources/arm/UINodes.hx @@ -0,0 +1,604 @@ +package arm; + +import armory.object.Object; +import armory.system.Cycles; +import zui.*; +import zui.Nodes; +import iron.data.SceneFormat; +import iron.data.MaterialData; + +class UINodes extends armory.Trait { + + public static var inst:UINodes; + public static var show = false; + + public static var wx:Int; + public static var wy:Int; + + var ui:Zui; + var drawMenu = false; + var showMenu = false; + var hideMenu = false; + var menuCategory = 0; + var addNodeButton = false; + var popupX = 0.0; + var popupY = 0.0; + + var sc:iron.data.ShaderData.ShaderContext = null; + public var _matcon:TMaterialContext = null; + var _materialcontext:MaterialContext = null; + + static var font:kha.Font; + + public function new() { + super(); + inst = this; + + // Load font for UI labels + iron.data.Data.getFont('droid_sans.ttf', function(f:kha.Font) { + + iron.data.Data.getBlob('default_material.json', function(b:kha.Blob) { + + kha.Assets.loadImage('color_wheel', function(image:kha.Image) { + + canvas = haxe.Json.parse(b.toString()); + + font = f; + var t = Reflect.copy(zui.Themes.dark); + t.FILL_WINDOW_BG = true; + t.ELEMENT_H = 18; + t.BUTTON_H = 16; + // ui = new Zui({font: f, theme: t, scaleFactor: 2.5}); //// + ui = new Zui({font: f, theme: t, color_wheel: image}); + ui.scrollEnabled = false; + armory.Scene.active.notifyOnInit(sceneInit); + }); + }); + }); + } + + // var rt:kha.Image; //// + // var uiWidth = 2048; + // var uiHeight = 1024; + + function sceneInit() { + + // var pui = iron.Scene.active.getChild("PlaneNodeUI"); //// + // rt = kha.Image.createRenderTarget(uiWidth, uiHeight); + // var mat:armory.data.MaterialData = cast(pui, armory.object.MeshObject).materials[0]; + // mat.contexts[0].textures[0] = rt; // Override diffuse texture + + // Store references to cube and plane objects + notifyOnRender2D(render2D); + notifyOnUpdate(update); + } + + var mx = 0.0; + var my = 0.0; + // var wh = 300; + static var frame = 0; + var mdown = false; + var mreleased = false; + var mchanged = false; + public static var changed = false; + function update() { + if (frame == 8) parseMaterial(); // Temp cpp fix + frame++; + + + // + var mouse = iron.system.Input.getMouse(); + mreleased = mouse.released(); + mdown = mouse.down(); + + if (ui.changed) { + mchanged = true; + if (!mdown) { + changed = true; + } + } + if ((mreleased && mchanged) || changed) { + mchanged = changed = false; + parseMaterial(); + } + // + + + if (!show) return; + if (!UITrait.uienabled) return; + var keyboard = iron.system.Input.getKeyboard(); + + wx = Std.int(iron.App.w());//200; + wy = 0;//iron.App.h() - wh; + if (mouse.x < wx || mouse.y < wy) return; + + if (ui.isTyping) return; + + if (mouse.started("right")) { + mx = mouse.x; + my = mouse.y; + } + else if (addNodeButton) { + showMenu = true; + addNodeButton = false; + } + else if (mouse.released()) { + hideMenu = true; + } + + if (keyboard.started("x")) { + nodes.removeNode(nodes.nodeSelected, canvas); + changed = true; + } + + if (keyboard.started("p")) { + trace(haxe.Json.stringify(canvas)); + } + } + + static var nodes = new Nodes(); + + static var canvas:TNodeCanvas = null; + + static var bg:kha.Image = null; + + function getNodeX():Int { + var mouse = iron.system.Input.getMouse(); + return Std.int((mouse.x - wx - nodes.PAN_X()) / nodes.SCALE); + } + + function getNodeY():Int { + var mouse = iron.system.Input.getMouse(); + return Std.int((mouse.y - wy - nodes.PAN_Y()) / nodes.SCALE); + } + + public static var grid:kha.Image = null; + public function drawGrid() { + var ww = iron.App.w(); + var wh = iron.App.h(); + var w = ww + 40 * 2; + var h = wh + 40 * 2; + grid = kha.Image.createRenderTarget(w, h); + grid.g2.begin(true, 0xff242424); + for (i in 0...Std.int(h / 40) + 1) { + grid.g2.color = 0xff282828; + grid.g2.drawLine(0, i * 40, w, i * 40); + grid.g2.color = 0xff323232; + grid.g2.drawLine(0, i * 40 + 20, w, i * 40 + 20); + } + for (i in 0...Std.int(w / 40) + 1) { + grid.g2.color = 0xff282828; + grid.g2.drawLine(i * 40, 0, i * 40, h); + grid.g2.color = 0xff323232; + grid.g2.drawLine(i * 40 + 20, 0, i * 40 + 20, h); + } + grid.g2.end(); + } + + @:access(zui.Zui) + function render2D(g:kha.graphics2.Graphics) { + + if (!show) return; + + if (!UITrait.uienabled && ui.inputRegistered) ui.unregisterInput(); + if (UITrait.uienabled && !ui.inputRegistered) ui.registerInput(); + + g.end(); + + if (grid == null) drawGrid(); + + // Start with UI + ui.begin(g); + // ui.begin(rt.g2); //// + + // Make window + var ww = Std.int(iron.App.w()); + wx = Std.int(iron.App.w()); + wy = 0; + var hwin = Id.handle(); + if (ui.window(hwin, wx, wy, ww, iron.App.h())) { + + ui.g.color = 0xffffffff; + ui.g.drawImage(grid, (nodes.panX * nodes.SCALE) % 40 - 40, (nodes.panY * nodes.SCALE) % 40 - 40); + + ui.g.font = font; + ui.g.fontSize = 22; + var title = "Material"; + var titlew = ui.g.font.width(22, title); + var titleh = ui.g.font.height(22); + ui.g.drawString(title, ww - titlew - 20, iron.App.h() - titleh - 10); + + // Recompile material on change + ui.changed = false; + nodes.nodeCanvas(ui, canvas); + + ui.g.color = ui.t.WINDOW_BG_COL; + ui.g.fillRect(0, 0, ww, 24); + ui.g.color = 0xffffffff; + + ui._x = 3; + ui._y = 3; + ui._w = 105; + if (ui.button("Input")) { addNodeButton = true; menuCategory = 0; popupX = wx + ui._x; popupY = wy + ui._y; } + ui._x += 105 + 3; + ui._y = 3; + if (ui.button("Output")) { addNodeButton = true; menuCategory = 1; popupX = wx + ui._x; popupY = wy + ui._y; } + ui._x += 105 + 3; + ui._y = 3; + if (ui.button("Texture")) { addNodeButton = true; menuCategory = 2; popupX = wx + ui._x; popupY = wy + ui._y; } + ui._x += 105 + 3; + ui._y = 3; + if (ui.button("Color")) { addNodeButton = true; menuCategory = 3; popupX = wx + ui._x; popupY = wy + ui._y; } + ui._x += 105 + 3; + ui._y = 3; + if (ui.button("Converter")) { addNodeButton = true; menuCategory = 4; popupX = wx + ui._x; popupY = wy + ui._y; } + } + + ui.endWindow(); + + if (drawMenu) { + + var ph = NodeCreator.numNodes[menuCategory] * 20; + var py = popupY; + g.color = 0xff222222; + g.fillRect(popupX, py, 105, ph); + + ui.beginLayout(g, Std.int(popupX), Std.int(py), 105); + + NodeCreator.draw(this, menuCategory); + + ui.endLayout(); + } + + ui.end(); + + g.begin(false); + + if (showMenu) { + showMenu = false; + drawMenu = true; + + } + if (hideMenu) { + hideMenu = false; + drawMenu = false; + } + } + + function make_paint(data:ShaderData, matcon:TMaterialContext):armory.system.ShaderContext { + var context_id = 'paint'; + var con_paint:armory.system.ShaderContext = data.add_context({ + name: context_id, + depth_write: false, + compare_mode: 'always', + cull_mode: 'counter_clockwise', + blend_source: 'source_alpha', //blend_one + blend_destination: 'inverse_source_alpha', //blend_zero + blend_operation: 'add', + alpha_blend_source: 'blend_one', + alpha_blend_destination: 'blend_zero', + alpha_blend_operation: 'add', + vertex_structure: [{"name": "pos", "size": 3},{"name": "nor", "size": 3},{"name": "tex", "size": 2}] }); + + var vert = con_paint.make_vert(); + var frag = con_paint.make_frag(); + + vert.add_out('vec3 sp'); + // vert.add_out('vec3 wnormal'); + frag.ins = vert.outs; + // vert.add_uniform('mat4 N', '_normalMatrix'); + vert.add_uniform('mat4 WVP', '_worldViewProjectionMatrix'); + + vert.write('vec2 tpos = vec2(tex.x * 2.0 - 1.0, tex.y * 2.0 - 1.0);'); + + // TODO: Fix seams at uv borders + vert.add_uniform('vec2 sub', '_sub'); + vert.write('tpos += sub;'); + + vert.write('gl_Position = vec4(tpos, 0.0, 1.0);'); + + vert.write('vec4 ndc = WVP * vec4(pos, 1.0);'); + vert.write('ndc.xyz = ndc.xyz / ndc.w;'); + vert.write('sp.xyz = ndc.xyz * 0.5 + 0.5;'); + vert.write('sp.y = 1.0 - sp.y;'); + vert.write('sp.z -= 0.0001;'); // Bias + + vert.add_out('vec3 mposition'); + if (UITrait.brushPaint == 0 && con_paint.is_elem('tex')) { + vert.write('mposition = pos.xyz;'); + } + else { + vert.write('mposition = ndc.xyz;'); + } + + frag.add_out('vec4 fragColor[3]'); + frag.add_uniform('vec4 inp', '_input'); + + // frag.add_uniform('vec3 v', '_cameraLook'); + // frag.write('vec3 n = normalize(wnormal);'); + // frag.write('if (dot(n, v) > 0.0) discard;'); + + frag.add_uniform('float aspectRatio', '_aspectRatioWindowF'); + frag.write('vec2 bsp = sp.xy * 2.0 - 1.0;'); + frag.write('bsp.x *= aspectRatio;'); + frag.write('bsp = bsp * 0.5 + 0.5;'); + + frag.add_uniform('sampler2D paintdb'); + frag.write('if (sp.z > texture(paintdb, vec2(sp.x, 1.0 - bsp.y)).r) discard;'); + + frag.write('vec2 binp = inp.xy * 2.0 - 1.0;'); + frag.write('binp.x *= aspectRatio;'); + frag.write('binp = binp * 0.5 + 0.5;'); + + frag.write('float dist = distance(bsp.xy, binp.xy);'); + frag.add_uniform('float brushRadius', '_brushRadius'); + frag.add_uniform('float brushStrength', '_brushStrength'); + + frag.write('if (dist > brushRadius) discard;'); + + // Texture projection - texcoords + if (UITrait.brushPaint == 0 && con_paint.is_elem('tex')) { + vert.add_uniform('float brushScale', '_brushScale'); + vert.add_out('vec2 texCoord'); + vert.write('texCoord = fract(tex * brushScale);'); // TODO: fract(tex) - somehow clamp is set after first paint + } + else { + frag.add_uniform('float brushScale', '_brushScale'); + // TODO: use prescaled value from VS + Cycles.texCoordName = 'fract(sp * brushScale)'; // Texture projection - project + } + var sout = Cycles.parse(canvas, con_paint, vert, frag, null, null, null, matcon); + Cycles.texCoordName = 'texCoord'; + var base = sout.out_basecol; + var rough = sout.out_roughness; + var met = sout.out_metallic; + var occ = sout.out_occlusion; + var nortan = Cycles.out_normaltan; + frag.write('vec3 basecol = $base;'); + frag.write('float roughness = $rough;'); + frag.write('float metallic = $met;'); + frag.write('float occlusion = $occ;'); + frag.write('vec3 nortan = $nortan;'); + + frag.write(' float str = brushStrength;'); + // Fade + // frag.write(' str *= clamp(brushRadius - dist * 2.0, 0.0, 1.0);'); + + frag.write(' fragColor[0] = vec4(basecol, str);'); + frag.write(' fragColor[1] = vec4(nortan, 1.0);'); // Encode normal, height, opacity, matid,.. + frag.write(' fragColor[2] = vec4(occlusion, roughness, metallic, str);'); + + con_paint.data.shader_from_source = true; + con_paint.data.vertex_shader = vert.get(); + con_paint.data.fragment_shader = frag.get(); + + return con_paint; + } + + function make_mesh_preview(data:ShaderData, matcon:TMaterialContext):armory.system.ShaderContext { + var context_id = 'mesh'; + var con_mesh:armory.system.ShaderContext = data.add_context({ + name: context_id, + depth_write: true, + compare_mode: 'less', + cull_mode: 'clockwise' }); + + var vert = con_mesh.make_vert(); + var frag = con_mesh.make_frag(); + + + frag.ins = vert.outs; + vert.add_uniform('mat4 WVP', '_worldViewProjectionMatrix'); + vert.write('gl_Position = WVP * vec4(pos, 1.0);'); + + + var sout = Cycles.parse(canvas, con_mesh, vert, frag, null, null, null, matcon); + var base = sout.out_basecol; + var rough = sout.out_roughness; + var met = sout.out_metallic; + var occ = sout.out_occlusion; + frag.write('vec3 basecol = $base;'); + frag.write('float roughness = $rough;'); + frag.write('float metallic = $met;'); + frag.write('float occlusion = $occ;'); + + frag.add_out('vec4[2] fragColor'); + frag.write('fragColor[0] = vec4(0.0, 0.0, 0.0, 1.0 - gl_FragCoord.z);'); + frag.write('fragColor[1] = vec4(basecol.rgb, 0.0);'); + + con_mesh.data.shader_from_source = true; + con_mesh.data.vertex_shader = vert.get(); + con_mesh.data.fragment_shader = frag.get(); + + return con_mesh; + } + + function make_depth(data:ShaderData):armory.system.ShaderContext { + var context_id = 'depth'; + var con_depth:armory.system.ShaderContext = data.add_context({ + name: context_id, + depth_write: true, + compare_mode: 'less', + cull_mode: 'clockwise', + color_write_red: false, + color_write_green: false, + color_write_blue: false, + color_write_alpha: false }); + + var vert = con_depth.make_vert(); + var frag = con_depth.make_frag(); + + + frag.ins = vert.outs; + vert.add_uniform('mat4 WVP', '_worldViewProjectionMatrix'); + vert.write('gl_Position = WVP * vec4(pos, 1.0);'); + + con_depth.data.shader_from_source = true; + con_depth.data.vertex_shader = vert.get(); + con_depth.data.fragment_shader = frag.get(); + + return con_depth; + } + + // function make_mesh_paint(data:ShaderData):armory.system.ShaderContext { + // var context_id = 'mesh'; + // var con_mesh:armory.system.ShaderContext = data.add_context({ + // name: context_id, + // depth_write: true, + // compare_mode: 'less', + // cull_mode: 'clockwise' }); + + // var vert = con_mesh.make_vert(); + // var frag = con_mesh.make_frag(); + + // frag.ins = vert.outs; + + + + + + // frag.write('vec3 basecol;'); + // frag.write('float roughness;'); + // frag.write('float metallic;'); + // frag.write('float occlusion;'); + // frag.write('float opacity;'); + + // frag.add_uniform('sampler2D texpaint'); + // frag.write('basecol = pow(texture(texpaint, texCoord).rgb, vec3(2.2));'); + + // frag.add_uniform('sampler2D texpaint_nor'); + // frag.write('vec3 n = texture(texpaint_nor, texCoord).rgb * 2.0 - 1.0;'); + // frag.write('n = normalize(TBN * normalize(n));'); + + // frag.add_uniform('sampler2D texpaint_pack'); + // frag.write('vec4 pack = texture(texpaint_pack, texCoord);'); + // frag.write('occlusion = pack.r;'); + // frag.write('roughness = pack.g;'); + // frag.write('metallic = pack.b;'); + + // // TODO: Sample disp at neightbour points to calc normal + // // tese.add_uniform('sampler2D texpaint_pack') + // // tese.write('vec4 pack = texture(texpaint_pack, texCoord);') + // // tese.write('disp = pack.a * 0.05;') + + + + // con_mesh.data.shader_from_source = true; + // con_mesh.data.vertex_shader = vert.get(); + // con_mesh.data.fragment_shader = frag.get(); + + // return con_mesh; + // } + + function parseMaterial() { + UITrait.dirty = true; + + var mout = false; + for (n in canvas.nodes) if (n.type == "OUTPUT_MATERIAL_PBR") { mout = true; break; } + + if (mout) { + + iron.data.Data.getMaterial("Scene", "Material", function(m:iron.data.MaterialData) { + + var mat:TMaterial = { + name: "Material", + canvas: canvas + }; + var _sd = new ShaderData(mat); + + if (sc == null) { + for (c in m.shader.contexts) { + if (c.raw.name == "paint") { + sc = c; + break; + } + } + } + if (_materialcontext == null) { + for (c in m.contexts) { + if (c.raw.name == "paint") { + _materialcontext = c; + _matcon = c.raw; + break; + } + } + } + + if (sc != null) { + m.shader.raw.contexts.remove(sc.raw); + m.shader.contexts.remove(sc); + m.raw.contexts.remove(_matcon); + m.contexts.remove(_materialcontext); + } + + _matcon = { + name: "paint", + bind_textures: [] + } + + var con = make_paint(_sd, _matcon); + var cdata = con.data; + + // if (sc == null) { + // from_source is synchronous.. + sc = new iron.data.ShaderData.ShaderContext(cdata, null, function(sc:iron.data.ShaderData.ShaderContext){}); + m.shader.raw.contexts.push(sc.raw); + m.shader.contexts.push(sc); + m.raw.contexts.push(_matcon); + + new MaterialContext(_matcon, function(self:MaterialContext) { + _materialcontext = self; + m.contexts.push(self); + }); + + + + var dcon = make_depth(_sd); + var dcdata = dcon.data; + // from_source is synchronous.. + var dsc = new iron.data.ShaderData.ShaderContext(dcdata, null, function(sc:iron.data.ShaderData.ShaderContext){}); + m.shader.contexts.push(dsc); + var dmatcon:TMaterialContext = { + name: "depth" + } + m.raw.contexts.push(dmatcon); + new MaterialContext(dmatcon, function(self:MaterialContext) { + m.contexts.push(self); + }); + // } + // else { + // sc.raw.vertex_shader = cdata.vertex_shader; + // sc.raw.fragment_shader = cdata.fragment_shader; + // sc.compile(); + // } + + + // var sp = iron.Scene.active.getChild("SpherePreview"); + // var md = cast(sp, iron.object.MeshObject).materials[0]; + // var mc = md.shader.contexts[0]; // mesh + + // var pmat:TMaterial = { + // name: "MaterialPreview", + // canvas: canvas + // }; + // var _psd = new ShaderData(pmat); + // _psd.add_elem('pos', 3); + // _psd.add_elem('nor', 3); + // _psd.add_elem('tex', 2); + // _psd.add_elem('tang', 3); + // var pcon = make_mesh_preview(_psd, null); + // var pcdata = pcon.data; + + // mc.raw.shader_from_source = true; + // mc.raw.vertex_shader = pcdata.vertex_shader; + // mc.raw.fragment_shader = pcdata.fragment_shader; + // mc.compile(); + }); + } + } + + public static function acceptDrag(assetIndex:Int) { + NodeCreator.createImageTexture(inst); + nodes.nodeSelected.buttons[0].default_value = assetIndex; + } +} diff --git a/Sources/arm/UINodesBrush.hx b/Sources/arm/UINodesBrush.hx new file mode 100644 index 00000000..46f3ac2b --- /dev/null +++ b/Sources/arm/UINodesBrush.hx @@ -0,0 +1,264 @@ +package arm; + +import armory.object.Object; +import armory.system.Cycles; +import zui.*; +import zui.Nodes; +import iron.data.SceneFormat; +import iron.data.MaterialData; + +class UINodesBrush extends armory.Trait { + + public static var show = false; + + public static var wx:Int; + public static var wy:Int; + + var ui:Zui; + var drawMenu = false; + var showMenu = false; + var hideMenu = false; + var popupX = 0; + var popupY = 0; + + static var font:kha.Font; + + public function new() { + super(); + + // Load font for UI labels + iron.data.Data.getFont('droid_sans.ttf', function(f:kha.Font) { + + iron.data.Data.getBlob('default_brush.json', function(b:kha.Blob) { + + canvas = haxe.Json.parse(b.toString()); + parseBrush(); + + font = f; + + var t = Reflect.copy(zui.Themes.dark); + t.FILL_WINDOW_BG = true; + t.ELEMENT_H = 18; + t.BUTTON_H = 16; + ui = new Zui({font: f, theme: t}); + ui.scrollEnabled = false; + armory.Scene.active.notifyOnInit(sceneInit); + }); + }); + } + + function sceneInit() { + // Store references to cube and plane objects + notifyOnRender2D(render2D); + notifyOnUpdate(update); + } + + var wh = 300; + + function update() { + if (!show) return; + if (!UITrait.uienabled) return; + var mouse = iron.system.Input.getMouse(); + var keyboard = iron.system.Input.getKeyboard(); + + wx = 200; + wy = iron.App.h() - wh; + if (mouse.x < wx || mouse.y < wy) return; + + if (ui.isTyping) return; + + if (mouse.released("right") || keyboard.started("a")) { + showMenu = true; + popupX = Std.int(mouse.x); + popupY = Std.int(mouse.y); + } + else if (mouse.released()) { + hideMenu = true; + } + + if (keyboard.started("x")) { + var n = uinodes.nodeSelected; + var i = 0; + while (i < canvas.links.length) { + var l = canvas.links[i]; + if (l.from_id == n.id || l.to_id == n.id) { + canvas.links.splice(i, 1); + } + else i++; + } + canvas.nodes.remove(n); + } + + if (keyboard.started("p")) { + trace(haxe.Json.stringify(canvas)); + } + } + + static var uinodes = new Nodes(); + + public static var canvas:TNodeCanvas = null; + + static var bg:kha.Image = null; + + function render2D(g:kha.graphics2.Graphics) { + if (!show) return; + + if (!UITrait.uienabled && ui.inputRegistered) ui.unregisterInput(); + if (UITrait.uienabled && !ui.inputRegistered) ui.registerInput(); + + g.end(); + + if (bg == null) { + var w = iron.App.w() - 200; + var h = wh; + bg = kha.Image.createRenderTarget(w, h); + bg.g2.begin(true, 0xff141414); + for (i in 0...Std.int(h / 40) + 1) { + bg.g2.color = 0xff303030; + bg.g2.drawLine(0, i * 40, w, i * 40); + bg.g2.color = 0xff202020; + bg.g2.drawLine(0, i * 40 + 20, w, i * 40 + 20); + } + for (i in 0...Std.int(w / 40) + 1) { + bg.g2.color = 0xff303030; + bg.g2.drawLine(i * 40, 0, i * 40, h); + bg.g2.color = 0xff202020; + bg.g2.drawLine(i * 40 + 20, 0, i * 40 + 20, h); + } + bg.g2.end(); + } + + // Start with UI + ui.begin(g); + + // Make window + var mouse = iron.system.Input.getMouse(); + wx = 200; + wy = iron.App.h() - wh; + var hwin = Id.handle(); + if (ui.window(hwin, wx, wy, iron.App.w() - 200, wh)) { + + ui.g.color = 0xffffffff; + ui.g.drawImage(bg, 0, 0); + + ui.g.font = font; + ui.g.fontSize = 42; + var title = "Brush"; + // var title = "Brush (right-click to add node)"; + var titlew = ui.g.font.width(42, title); + var titleh = ui.g.font.height(42); + ui.g.drawString(title, iron.App.w() - 200 - titlew - 20, wh - titleh - 10); + + uinodes.nodeCanvas(ui, canvas); + } + + ui.endWindow(); + + if (drawMenu) { + ui.beginLayout(g, popupX, popupY, 120); + + if (ui.button("On Brush")) { + var node_id = uinodes.getNodeId(canvas.nodes); + var n:TNode = { + id: node_id, + name: "On Brush", + type: "OnBrushNode", + x: mouse.x - wx, + y: mouse.y - wy, + color: 0xff4982a0, + inputs: [], + outputs: [ + { + id: uinodes.getSocketId(canvas.nodes), + node_id: node_id, + name: "Out", + type: "ACTION", + color: 0xff63c763, + default_value: null + }, + { + id: uinodes.getSocketId(canvas.nodes), + node_id: node_id, + name: "Position", + type: "VECTOR", + color: 0xff63c763, + default_value: null + } + ], + buttons: [] + }; + canvas.nodes.push(n); + uinodes.nodeDrag = n; + uinodes.nodeSelected = n; + } + if (ui.button("Paint")) { + var node_id = uinodes.getNodeId(canvas.nodes); + var n:TNode = { + id: node_id, + name: "Paint", + type: "PaintNode", + x: mouse.x - wx, + y: mouse.y - wy, + color: 0xff4982a0, + inputs: [ + { + id: uinodes.getSocketId(canvas.nodes), + node_id: node_id, + name: "In", + type: "ACTION", + color: 0xff63c763, + default_value: null + }, + { + id: uinodes.getSocketId(canvas.nodes), + node_id: node_id, + name: "Position", + type: "VECTOR", + color: 0xff63c763, + default_value: null + } + ], + outputs: [ + { + id: uinodes.getSocketId(canvas.nodes), + node_id: node_id, + name: "Out", + type: "ACTION", + color: 0xff63c763, + default_value: null + } + ], + buttons: [] + }; + canvas.nodes.push(n); + uinodes.nodeDrag = n; + uinodes.nodeSelected = n; + } + + if (ui.button("Parse")) { + parseBrush(); + } + + ui.endLayout(); + } + + ui.end(); + + g.begin(false); + + if (showMenu) { + showMenu = false; + drawMenu = true; + + } + if (hideMenu) { + hideMenu = false; + drawMenu = false; + } + } + + function parseBrush() { + armory.system.Logic.packageName = "arm.logicnode"; + var tree = armory.system.Logic.parse(canvas, false); + } +} diff --git a/Sources/arm/UITrait.hx b/Sources/arm/UITrait.hx new file mode 100644 index 00000000..fee74620 --- /dev/null +++ b/Sources/arm/UITrait.hx @@ -0,0 +1,792 @@ +package arm; + +import zui.*; +import zui.Zui.State; +import zui.Canvas; +import iron.data.SceneFormat; +import iron.data.MeshData; +import iron.object.MeshObject; + +typedef TPreferences = { + public var w:Int; + public var h:Int; + public var save_location:String; + public var load_location:String; +} + +// typedef TProject = { + // public var brushes:Array<>; + // public var materials:Array<>; +// } + +class UITrait extends armory.Trait { + + public static var uienabled = true; + public static var isScrolling = false; + public static var isDragging = false; + public static var dragAsset:TAsset = null; + + public static var showFiles = false; + public static var filesDone:String->Void; + public static var showSplash = false; + public static var show = true; + public static var dirty = true; + + var dropPath = ""; + var bundled:Map = new Map(); + var ui:Zui; + var uimodal:Zui; + + public static var windowW = 240; // Panel width + + function loadBundled(names:Array, done:Void->Void) { + var loaded = 0; + for (s in names) { + kha.Assets.loadImage(s, function(image:kha.Image) { + bundled.set(s, image); + loaded++; + if (loaded == names.length) done(); + }); + } + } + + public static function depthDirty():Bool { + if (!dirty && !firstPaint) return false; + dirty = false; + return true; + } + + static var _onBrush:ArrayVoid> = []; + + public static function notifyOnBrush(f:Void->Void) { + _onBrush.push(f); + } + + public static var firstPaint = true; + public static var paint = false; + public static var paintX = 0.0; + public static var paintY = 0.0; + public static var lastPaintX = 0.0; + public static var lastPaintY = 0.0; + static var painted = 0; + public static function paintDirty():Bool { + // Paint bounds + if (paintX > iron.App.w()) return false; + // if (UINodes.show && paintY > UINodes.wy) return false; + // if (UINodesBrush.show && paintY > UINodesBrush.wy) return false; + + // Prevent painting the same spot - save perf & reduce projection paint jittering caused by _sub offset + if (paintX == lastPaintX && paintY == lastPaintY) painted++; + else painted = 0; + if (painted > 8) return false; + lastPaintX = paintX; + lastPaintY = paintY; + + if (paint) firstPaint = false; + return paint; + } + + // TODO: Recompile mat instead of uniforms? + public static var brushRadius = 0.5; + public static var brushStrength = 1.0; + public static var brushScale = 0.5; + public static var brushPaint = 0; + function linkFloat(link:String):Null { + + if (link == '_brushRadius') { + return brushRadius / 15.0; + } + else if (link == '_brushStrength') { + return brushStrength; + } + else if (link == '_brushScale') { + return brushScale * 2.0; + } + // else if (link == '_brushPaint') { + // return brushPaint; + // } + + return null; + } + + var sub = 0; + var vsub = new iron.math.Vec4(); + static inline var seps = 0.0002; + function linkVec2(link:String):iron.math.Vec4 { + + if (link == '_sub') { + sub = (sub + 1) % 9; + if (sub == 0) vsub.set(0.0 + seps, 0.0, 0.0); + else if (sub == 1) vsub.set(0.0 - seps, 0.0, 0.0); + else if (sub == 2) vsub.set(0.0, 0.0 + seps, 0.0); + else if (sub == 3) vsub.set(0.0, 0.0 - seps, 0.0); + else if (sub == 4) vsub.set(0.0 + seps, 0.0 + seps, 0.0); + else if (sub == 5) vsub.set(0.0 - seps, 0.0 - seps, 0.0); + else if (sub == 6) vsub.set(0.0 + seps, 0.0 - seps, 0.0); + else if (sub == 7) vsub.set(0.0 - seps, 0.0 + seps, 0.0); + else if (sub == 8) vsub.set(0.0, 0.0, 0.0); + return vsub; + } + + return null; + } + + var font:kha.Font; + public function new() { + super(); + + iron.object.Uniforms.externalFloatLinks = [linkFloat]; + iron.object.Uniforms.externalVec2Links = [linkVec2]; + + iron.data.Data.getFont('droid_sans.ttf', function(f:kha.Font) { + font = f; + zui.Themes.dark.FILL_WINDOW_BG = true; + zui.Nodes.getEnumTexts = getEnumTexts; + zui.Nodes.mapEnum = mapEnum; + ui = new Zui( { font: f } ); + uimodal = new Zui( { font: f } ); + // ui = new Zui( { font: f, scaleFactor: 8, theme: zui.Themes.light } ); //// + loadBundled(['brush', 'mat', 'matbrush', 'cay_thumb'], done); + }); + + kha.System.notifyOnDropFiles(function(filePath:String) { + dropPath = StringTools.rtrim(filePath); + }); + } + + function importAsset(path:String) { + if (!StringTools.endsWith(path, ".jpg") && + !StringTools.endsWith(path, ".png") && + !StringTools.endsWith(path, ".hdr")) return; + + iron.data.Data.getImage(path, function(image:kha.Image) { + var ar = path.split("/"); + var name = ar[ar.length - 1]; + var asset:TAsset = {name: name, file: path, id: assetId++}; + assets.push(asset); + assetNames.push(name); + Canvas.assetMap.set(asset.id, image); + hwnd.redraws = 2; + }); + } + + function getEnumTexts():Array { + return assetNames.length > 0 ? assetNames : [""]; + } + + function mapEnum(s:String):String { + for (a in assets) if (a.name == s) return a.file; + return ""; + } + + // var rt:kha.Image; //// + // var uiWidth = 2048; + // var uiHeight = 2048; + + var currentObject:MeshObject; + + function done() { + + notifyOnInit(function() { + // iron.Scene.active.notifyOnInit(function() { //// + + // var pui = iron.Scene.active.getChild("PlaneUI"); //// + // rt = kha.Image.createRenderTarget(uiWidth, uiHeight); + // var mat:armory.data.MaterialData = cast(pui, armory.object.MeshObject).materials[0]; + // mat.contexts[0].textures[0] = rt; // Override diffuse texture + + currentObject = cast(iron.Scene.active.getChild("Cube"), MeshObject); + + iron.App.notifyOnUpdate(update); + iron.App.notifyOnRender2D(render); + iron.App.notifyOnRender(clearTargetsHandler); + }); + } + + function resize() { + iron.Scene.active.camera.buildProjection(); + } + + function update() { + isScrolling = ui.isScrolling; + updateUI(); + updateSplash(); + updateFiles(); + } + + function updateUI() { + paint = false; + var mouse = iron.system.Input.getMouse(); + // if (mouse.started() && mouse.x < 50 && mouse.y < 50) show = !show; + + isDragging = dragAsset != null; + if (mouse.released() && isDragging) { + if (UINodes.show && mouse.x > UINodes.wx && mouse.y > UINodes.wy) { + var index = 0; + for (i in 0...assets.length) if (assets[i] == dragAsset) { index = i; break; } + UINodes.acceptDrag(index); + } + dragAsset = null; + } + + if (!show) return; + if (!UITrait.uienabled) return; + + if (mouse.down()) { + for (f in _onBrush) f(); + } + } + + var modalW = 625; + var modalH = 545; + var modalHeaderH = 66; + var modalRectW = 625; // No shadow + var modalRectH = 545; + function updateSplash() { + if (!showSplash) return; + + var mouse = iron.system.Input.getMouse(); + + if (mouse.released()) { + var left = iron.App.w() / 2 - modalRectW / 2; + var right = iron.App.w() / 2 + modalRectW / 2; + var top = iron.App.h() / 2 - modalRectH / 2; + var bottom = iron.App.h() / 2 + modalRectH / 2; + if (mouse.x < left || mouse.x > right || mouse.y < top + modalHeaderH || mouse.y > bottom) { + showSplash = false; + } + } + } + + function updateFiles() { + if (!showFiles) return; + + var mouse = iron.system.Input.getMouse(); + + if (mouse.released()) { + var left = iron.App.w() / 2 - modalRectW / 2; + var right = iron.App.w() / 2 + modalRectW / 2; + var top = iron.App.h() / 2 - modalRectH / 2; + var bottom = iron.App.h() / 2 + modalRectH / 2; + if (mouse.x < left || mouse.x > right || mouse.y < top + modalHeaderH || mouse.y > bottom) { + showFiles = false; + } + } + } + + public static var texpaint:kha.Image; + public static var texpaint_nor:kha.Image; + public static var texpaint_pack:kha.Image; + function clearTargetsHandler(g:kha.graphics4.Graphics) { + var pd = iron.RenderPath.active; + texpaint = pd.renderTargets.get("texpaint").image; + texpaint_nor = pd.renderTargets.get("texpaint_nor").image; + texpaint_pack = pd.renderTargets.get("texpaint_pack").image; + g.end(); + + texpaint.g4.begin(); + texpaint.g4.clear(kha.Color.fromFloats(0.2, 0.2, 0.2, 0.0)); // Base + texpaint.g4.end(); + + texpaint_nor.g4.begin(); + texpaint_nor.g4.clear(kha.Color.fromFloats(0.5, 0.5, 1.0, 0.0)); // Nor + texpaint_nor.g4.end(); + + texpaint_pack.g4.begin(); + texpaint_pack.g4.clear(kha.Color.fromFloats(1.0, 1.0, 0.0, 0.0)); // Occ, rough, met + texpaint_pack.g4.end(); + + g.begin(); + iron.App.removeRender(clearTargetsHandler); + } + + function resizeTargetsHandler(g:kha.graphics4.Graphics) { + var res = getTextureRes(); + var pd = iron.RenderPath.active; + var texpaint = pd.renderTargets.get("texpaint"); + var texpaint_nor = pd.renderTargets.get("texpaint_nor"); + var texpaint_pack = pd.renderTargets.get("texpaint_pack"); + + var texpaint_oldimg = texpaint.image; + var texpaint_nor_oldimg = texpaint_nor.image; + var texpaint_pack_oldimg = texpaint_pack.image; + + texpaint.image = kha.Image.createRenderTarget(res, res, kha.graphics4.TextureFormat.RGBA32, kha.graphics4.DepthStencilFormat.Depth16); + texpaint_nor.image = kha.Image.createRenderTarget(res, res, kha.graphics4.TextureFormat.RGBA32, kha.graphics4.DepthStencilFormat.NoDepthAndStencil); + texpaint_pack.image = kha.Image.createRenderTarget(res, res, kha.graphics4.TextureFormat.RGBA32, kha.graphics4.DepthStencilFormat.NoDepthAndStencil); + + g.end(); + texpaint.image.g2.begin(); + texpaint.image.g2.drawScaledImage(texpaint_oldimg, 0, 0, res, res); + texpaint.image.g2.end(); + + texpaint_nor.image.g2.begin(); + texpaint_nor.image.g2.drawScaledImage(texpaint_nor_oldimg, 0, 0, res, res); + texpaint_nor.image.g2.end(); + + texpaint_pack.image.g2.begin(); + texpaint_pack.image.g2.drawScaledImage(texpaint_pack_oldimg, 0, 0, res, res); + texpaint_pack.image.g2.end(); + g.begin(); + + texpaint_oldimg.unload(); + texpaint_nor_oldimg.unload(); + texpaint_pack_oldimg.unload(); + + UITrait.dirty = true; + + iron.App.removeRender(resizeTargetsHandler); + } + + var lastW = 0; + var lastH = 0; + function render(g:kha.graphics2.Graphics) { + if (arm.App.realw() == 0 || arm.App.realh() == 0) return; + + if (dropPath != "") { + if (StringTools.endsWith(dropPath, ".obj")) importMesh(dropPath); + else importAsset(dropPath); + dropPath = ""; + } + + uienabled = !showSplash && !showFiles; + renderUI(g); + // if (showSplash) renderSplash(g); + if (showFiles) renderFiles(g); + + if (lastW > 0 && (lastW != arm.App.realw() || lastH != arm.App.realh())) { + resize(); + } + lastW = arm.App.realw(); + lastH = arm.App.realh(); + + // var ready = showFiles || dirty; + // TODO: Texture params get overwritten + // if (ready) for (t in UINodes.inst._matcon.bind_textures) t.params_set = null; + if (UINodes.inst._matcon != null) for (t in UINodes.inst._matcon.bind_textures) t.params_set = null; + + // iron.Scene.active.camera.renderPath.ready = ready; + // dirty = false; + } + + var assets:Array = []; + var assetNames:Array = []; + var assetId = 0; + + public static var cameraType = 0; + var textureRes = 2; + function getTextureRes():Int { + if (textureRes == 0) return 1024; + if (textureRes == 1) return 2048; + if (textureRes == 2) return 4096; + if (textureRes == 3) return 8192; + if (textureRes == 4) return 16384; + if (textureRes == 5) return 20480; + return 0; + } + + var isBase = true; + var isOcc = true; + var isRough = true; + var isMet = true; + var isNor = true; + var hwnd = Id.handle(); + function renderUI(g:kha.graphics2.Graphics) { + if (!show) return; + + if (!UITrait.uienabled && ui.inputRegistered) ui.unregisterInput(); + if (UITrait.uienabled && !ui.inputRegistered) ui.registerInput(); + + var matImg = bundled.get('mat'); + var matBrushImg = bundled.get('matbrush'); + var envThumbCay = bundled.get('cay_thumb'); + var brushImg = bundled.get('brush'); + var mouse = iron.system.Input.getMouse(); + g.color = 0xffffffff; + + // Brush + if (UITrait.uienabled) { + var psize = Std.int(brushImg.width * brushRadius); + // g.imageScaleQuality = kha.graphics2.ImageScaleQuality.High; + g.drawScaledImage(brushImg, mouse.x - psize / 2, mouse.y - psize / 2, psize, psize); + } + + g.end(); + ui.begin(g); + // ui.begin(rt.g2); //// + + if (ui.window(hwnd, arm.App.realw() - windowW, 0, windowW, iron.App.h())) { + + var htab = Id.handle({position: 0}); + + if (ui.tab(htab, "Project")) { + // ui.row([1/2, 1/2]); + // ui.button("Open"); + // ui.button("Save"); + + // if (ui.button("Help")) { + // showSplash = true; + // } + + if (ui.button("Import Mesh")) { + showFiles = true; + filesDone = function(path:String) { + importMesh(path); + } + } + + if (ui.button("Export Textures")) { + + + + var textureSize = getTextureRes(); + + showFiles = true; + // var path = 'C:\\Users\\lubos\\Documents\\'; + filesDone = function(path:String) { + var bo = new haxe.io.BytesOutput(); + var pixels = texpaint.getPixels(); + var rgb = haxe.io.Bytes.alloc(textureSize * textureSize * 3); + // BGRA to RGB + for (i in 0...textureSize * textureSize) { + rgb.set(i * 3 + 0, pixels.get(i * 4 + 2)); + rgb.set(i * 3 + 1, pixels.get(i * 4 + 1)); + rgb.set(i * 3 + 2, pixels.get(i * 4 + 0)); + } + var pngwriter = new iron.format.png.Writer(bo); + pngwriter.write(iron.format.png.Tools.buildRGB(textureSize, textureSize, rgb)); + // var jpgdata:iron.format.jpg.Data.Data = { + // width: textureSize, + // height: textureSize, + // quality: 80, + // pixels: rgb + // }; + // var jpgwriter = new iron.format.jpg.Writer(bo); + // jpgwriter.write(jpgdata); + #if kha_krom + Krom.fileSaveBytes(path + "/tex_basecol.png", bo.getBytes().getData()); + #end + + pixels = texpaint_nor.getPixels(); + for (i in 0...textureSize * textureSize) { + rgb.set(i * 3 + 0, pixels.get(i * 4 + 2)); + rgb.set(i * 3 + 1, pixels.get(i * 4 + 1)); + rgb.set(i * 3 + 2, pixels.get(i * 4 + 0)); + } + bo = new haxe.io.BytesOutput(); + var pngwriter = new iron.format.png.Writer(bo); + pngwriter.write(iron.format.png.Tools.buildRGB(textureSize, textureSize, rgb)); + #if kha_krom + Krom.fileSaveBytes(path + "/tex_nor.png", bo.getBytes().getData()); + #end + + // for (i in 0...textureSize * textureSize) { + // rgb.set(i * 3 + 0, pixels.get(i * 4 + 3)); + // rgb.set(i * 3 + 1, pixels.get(i * 4 + 3)); + // rgb.set(i * 3 + 2, pixels.get(i * 4 + 3)); + // } + // bo = new haxe.io.BytesOutput(); + // var pngwriter = new iron.format.png.Writer(bo); + // pngwriter.write(iron.format.png.Tools.buildRGB(textureSize, textureSize, rgb)); + // #if kha_krom + // Krom.fileSaveBytes(path + "/tex_height.png", bo.getBytes().getData()); + // #end + + pixels = texpaint_pack.getPixels(); // occ, rough, met + for (i in 0...textureSize * textureSize) { + rgb.set(i * 3 + 0, pixels.get(i * 4)); + rgb.set(i * 3 + 1, pixels.get(i * 4)); + rgb.set(i * 3 + 2, pixels.get(i * 4)); + } + bo = new haxe.io.BytesOutput(); + var pngwriter = new iron.format.png.Writer(bo); + pngwriter.write(iron.format.png.Tools.buildRGB(textureSize, textureSize, rgb)); + #if kha_krom + Krom.fileSaveBytes(path + "/tex_occ.png", bo.getBytes().getData()); + #end + + for (i in 0...textureSize * textureSize) { + rgb.set(i * 3 + 0, pixels.get(i * 4 + 1)); + rgb.set(i * 3 + 1, pixels.get(i * 4 + 1)); + rgb.set(i * 3 + 2, pixels.get(i * 4 + 1)); + } + bo = new haxe.io.BytesOutput(); + var pngwriter = new iron.format.png.Writer(bo); + pngwriter.write(iron.format.png.Tools.buildRGB(textureSize, textureSize, rgb)); + #if kha_krom + Krom.fileSaveBytes(path + "/tex_rough.png", bo.getBytes().getData()); + #end + + for (i in 0...textureSize * textureSize) { + rgb.set(i * 3 + 0, pixels.get(i * 4 + 2)); + rgb.set(i * 3 + 1, pixels.get(i * 4 + 2)); + rgb.set(i * 3 + 2, pixels.get(i * 4 + 2)); + } + bo = new haxe.io.BytesOutput(); + var pngwriter = new iron.format.png.Writer(bo); + pngwriter.write(iron.format.png.Tools.buildRGB(textureSize, textureSize, rgb)); + #if kha_krom + Krom.fileSaveBytes(path + "/tex_met.png", bo.getBytes().getData()); + #end + } + } + + if (ui.panel(Id.handle({selected: false}), "Properties")) { + + var hres = Id.handle({position: textureRes}); + textureRes = ui.combo(hres, ["1K", "2K", "4K", "8K", "16K", "20K"], "Res", true); + if (hres.changed) { + iron.App.notifyOnRender(resizeTargetsHandler); + } + ui.combo(Id.handle(), ["8bit"], "Color", true); + ui.combo(Id.handle(), ["png"], "Format", true); + // ui.text("Channels"); + ui.row([1/2, 1/2]); + isBase = ui.check(Id.handle({selected: isBase}), "Base Color"); + isOcc = ui.check(Id.handle({selected: isOcc}), "Occlusion"); + ui.row([1/2, 1/2]); + isRough = ui.check(Id.handle({selected: isRough}), "Roughness"); + isMet = ui.check(Id.handle({selected: isMet}), "Metallic"); + isNor = ui.check(Id.handle({selected: isNor}), "Normal Map"); + } + + if (ui.panel(Id.handle({selected: true}), "Camera")) { + + ui.row([1/2,1/2]); + cameraType = ui.combo(Id.handle({position: cameraType}), ["Orbit", "Fly"], "Camera"); + if (ui.button("Reset")) { + var scene = iron.Scene.active; + var cam = scene.cameras[0]; + for (o in scene.raw.objects) { + if (o.type == 'camera_object') { + cam.transform.local.setF32(o.transform.values); + cam.transform.decompose(); + currentObject.transform.reset(); + break; + } + } + } + } + + + // if (ui.panel(Id.handle({selected: true}), "TREE")) { + // ui.row([1/3, 1/3, 1/3]); + // if (ui.button("Mesh")) { + // } + // if (ui.button("Lamp")) { + // } + // if (ui.button("Camera")) { + // } + // var h = Id.handle(); + // ui.radio(h, 0, "Mesh"); + // ui.radio(h, 1, "Lamp"); + // ui.radio(h, 2, "Camera"); + // ui.radio(h, 3, "World"); + // } + // ui.separator(); + + // if (ui.panel(Id.handle({selected: true}), "PROPERTIES")) { + // ui.textInput(Id.handle({text: "Mesh"}), "Name", Right); + // ui.row([1/3, 1/3, 1/3]); + // var strx = ui.textInput(Id.handle().nest(0, {text: "0"}), "X", Right); + // var stry = ui.textInput(Id.handle().nest(1, {text: "0"}), "Y", Right); + // var strz = ui.textInput(Id.handle().nest(2, {text: "0"}), "X", Right); + // } + // ui.separator(); + + if (ui.panel(Id.handle({selected: true}), "Environment")) { + ui.image(envThumbCay); + var p = iron.Scene.active.world.getGlobalProbe(); + ui.row([1/2, 1/2]); + var envType = ui.combo(Id.handle({position: 0}), ["Indoor"], "Map"); + p.raw.strength = ui.slider(Id.handle({value: p.raw.strength}), "Strength", 0.0, 5.0, true); + } + + if (ui.panel(Id.handle({selected: true}), "Brush")) { + ui.image(matBrushImg); + // if (ui.button("Nodes")) { + // UINodesBrush.show = !UINodesBrush.show; + // UINodes.show = false; + // } + ui.row([1/2, 1/2]); + // ui.combo(Id.handle(), ["Draw", "Fill"], "Type"); + ui.combo(Id.handle(), ["Draw"], "Type"); + ui.combo(Id.handle(), ["Add"], "Blending"); + ui.row([1/2, 1/2]); + brushRadius = ui.slider(Id.handle({value: brushRadius}), "Radius", 0.0, 1.0, true); + brushStrength = ui.slider(Id.handle({value: brushStrength}), "Strength", 0.0, 1.0, true); + ui.row([1/2, 1/2]); + var paintHandle = Id.handle(); + brushPaint = ui.combo(paintHandle, ["UV", "Project"], "Paint"); + if (paintHandle.changed) { + UINodes.changed = true; + } + brushScale = ui.slider(Id.handle({value: brushScale}), "Scale", 0.0, 1.0, true); + } + ui.separator(); + + if (ui.panel(Id.handle({selected: true}), "Material")) { + ui.image(matImg); + if (ui.button("Nodes")) { + UINodes.show = !UINodes.show; + UINodesBrush.show = false; + resize(); + } + } + } + + if (ui.tab(htab, "Assets")) { + + if (ui.button("Import")) { + showFiles = true; + filesDone = function(path:String) { + importAsset(path); + } + } + + if (assets.length > 0) { + var i = assets.length - 1; + while (i >= 0) { + var asset = assets[i]; + if (ui.image(getImage(asset)) == State.Started) { + dragAsset = asset; + } + ui.row([1/8, 7/8]); + var b = ui.button("X"); + asset.name = ui.textInput(Id.handle().nest(asset.id, {text: asset.name}), "", Right); + assetNames[i] = asset.name; + if (b) { + getImage(asset).unload(); + assets.splice(i, 1); + assetNames.splice(i, 1); + } + i--; + } + } + else { + ui.text("(Drag & drop assets)"); + } + + // if (ui.panel(Id.handle({selected: false}), "LAYERS")) {} + // ui.separator(); + + // if (ui.panel(Id.handle({selected: false}), "HISTORY")) {} + // ui.separator(); + } + } + ui.end(); + g.begin(false); + + if (dragAsset != null) { + UITrait.dirty = true; + var mouse = iron.system.Input.getMouse(); + var ratio = 128 / getImage(dragAsset).width; + var h = getImage(dragAsset).height * ratio; + g.drawScaledImage(getImage(dragAsset), mouse.x, mouse.y, 128, h); + } + } + + function renderSplash(g:kha.graphics2.Graphics) { + var left = iron.App.w() / 2 - modalW / 2; + var top = iron.App.h() / 2 - modalH / 2; + var splashImg = bundled.get('splash'); + g.color = 0xffffffff; + g.drawScaledImage(splashImg, left, top, modalW, modalH); + + uimodal.beginLayout(g, Std.int(left) + 200, Std.int(top) + 500, 120); + uimodal.button("Manual"); + uimodal.endLayout(); + } + + function getImage(asset:TAsset):kha.Image { + return Canvas.assetMap.get(asset.id); + } + + var path = '/'; + function renderFiles(g:kha.graphics2.Graphics) { + var left = iron.App.w() / 2 - modalW / 2; + var top = iron.App.h() / 2 - modalH / 2; + var filesImg = bundled.get('files'); + g.color = 0xff202020; + // g.drawScaledImage(filesImg, left, top, modalW, modalH); + g.fillRect(left, top, modalW, modalH); + + var leftRect = Std.int(iron.App.w() / 2 - modalRectW / 2); + var rightRect = Std.int(iron.App.w() / 2 + modalRectW / 2); + var topRect = Std.int(iron.App.h() / 2 - modalRectH / 2); + var bottomRect = Std.int(iron.App.h() / 2 + modalRectH / 2); + topRect += modalHeaderH; + + g.end(); + uimodal.begin(g); + if (uimodal.window(Id.handle(), leftRect, topRect, modalRectW, modalRectH - 100)) { + var pathHandle = Id.handle(); + pathHandle.text = uimodal.textInput(pathHandle); + path = zui.Ext.fileBrowser(uimodal, pathHandle); + } + uimodal.end(false); + g.begin(false); + + uimodal.beginLayout(g, rightRect - 100, bottomRect - 30, 100); + if (uimodal.button("OK")) { + showFiles = false; + filesDone(path); + UITrait.dirty = true; + } + uimodal.endLayout(false); + + uimodal.beginLayout(g, rightRect - 200, bottomRect - 30, 100); + if (uimodal.button("Cancel")) { + showFiles = false; + UITrait.dirty = true; + } + uimodal.endLayout(); + } + + function importMesh(path:String) { + + iron.data.Data.getBlob(path, function(b:kha.Blob) { + + var obj = new iron.format.obj.Loader(b.toString()); + var pa = new TFloat32Array(obj.indexedVertices.length); + for (i in 0...pa.length) pa[i] = obj.indexedVertices[i]; + var uva = new TFloat32Array(obj.indexedUVs.length); + for (i in 0...uva.length) uva[i] = obj.indexedUVs[i]; + var na = new TFloat32Array(obj.indexedNormals.length); + for (i in 0...na.length) na[i] = obj.indexedNormals[i]; + var ia = new TUint32Array(obj.indices.length); + for (i in 0...ia.length) ia[i] = obj.indices[i]; + + var raw:TMeshData = { + name: "Mesh", + vertex_arrays: [ + { + values: pa, + attrib: "pos" + }, + { + values: na, + attrib: "nor" + }, + { + values: uva, + attrib: "tex" + } + ], + index_arrays: [ + { + values: ia, + material: 0 + } + ] + }; + + new MeshData(raw, function(md:MeshData) { + currentObject.data.delete(); + iron.App.notifyOnRender(clearTargetsHandler); + currentObject.setData(md); + UITrait.dirty = true; + }); + }); + } +} diff --git a/Sources/arm/logicnode/BooleanNode.hx b/Sources/arm/logicnode/BooleanNode.hx new file mode 100644 index 00000000..d6362459 --- /dev/null +++ b/Sources/arm/logicnode/BooleanNode.hx @@ -0,0 +1,24 @@ +package arm.logicnode; + +import armory.logicnode.LogicNode; +import armory.logicnode.LogicTree; + +class BooleanNode extends LogicNode { + + public var value:Bool; + + public function new(tree:LogicTree, value = false) { + super(tree); + this.value = value; + } + + override function get(from:Int):Dynamic { + if (inputs.length > 0) return inputs[0].get(); + return value; + } + + override function set(value:Dynamic) { + if (inputs.length > 0) inputs[0].set(value); + else this.value = value; + } +} diff --git a/Sources/arm/logicnode/ColorNode.hx b/Sources/arm/logicnode/ColorNode.hx new file mode 100644 index 00000000..40b01345 --- /dev/null +++ b/Sources/arm/logicnode/ColorNode.hx @@ -0,0 +1,26 @@ +package arm.logicnode; + +import armory.math.Vec4; +import armory.logicnode.LogicNode; +import armory.logicnode.LogicTree; + +class ColorNode extends LogicNode { + + var value = new Vec4(); + + public function new(tree:LogicTree, r = 0.8, g = 0.8, b = 0.8, a = 1.0) { + super(tree); + + value.set(r, g, b, a); + } + + override function get(from:Int):Dynamic { + if (inputs.length > 0) return inputs[0].get(); + return value; + } + + override function set(value:Dynamic) { + if (inputs.length > 0) inputs[0].set(value); + else this.value = value; + } +} diff --git a/Sources/arm/logicnode/FloatNode.hx b/Sources/arm/logicnode/FloatNode.hx new file mode 100644 index 00000000..d75c0ade --- /dev/null +++ b/Sources/arm/logicnode/FloatNode.hx @@ -0,0 +1,24 @@ +package arm.logicnode; + +import armory.logicnode.LogicNode; +import armory.logicnode.LogicTree; + +class FloatNode extends LogicNode { + + public var value:Float; + + public function new(tree:LogicTree, value = 0.0) { + super(tree); + this.value = value; + } + + override function get(from:Int):Dynamic { + if (inputs.length > 0) return inputs[0].get(); + return value; + } + + override function set(value:Dynamic) { + if (inputs.length > 0) inputs[0].set(value); + else this.value = value; + } +} diff --git a/Sources/arm/logicnode/IntegerNode.hx b/Sources/arm/logicnode/IntegerNode.hx new file mode 100644 index 00000000..54c7a321 --- /dev/null +++ b/Sources/arm/logicnode/IntegerNode.hx @@ -0,0 +1,24 @@ +package arm.logicnode; + +import armory.logicnode.LogicNode; +import armory.logicnode.LogicTree; + +class IntegerNode extends LogicNode { + + public var value:Int; + + public function new(tree:LogicTree, value = 0) { + super(tree); + this.value = value; + } + + override function get(from:Int):Dynamic { + if (inputs.length > 0) return inputs[0].get(); + return value; + } + + override function set(value:Dynamic) { + if (inputs.length > 0) inputs[0].set(value); + else this.value = value; + } +} diff --git a/Sources/arm/logicnode/NullNode.hx b/Sources/arm/logicnode/NullNode.hx new file mode 100644 index 00000000..26863ce6 --- /dev/null +++ b/Sources/arm/logicnode/NullNode.hx @@ -0,0 +1,13 @@ +package arm.logicnode; + +import armory.logicnode.LogicNode; +import armory.logicnode.LogicTree; + +class NullNode extends LogicNode { + + public function new(tree:LogicTree) { + super(tree); + } + + override function get(from:Int):Dynamic { return null; } +} diff --git a/Sources/arm/logicnode/ObjectNode.hx b/Sources/arm/logicnode/ObjectNode.hx new file mode 100644 index 00000000..54b06a14 --- /dev/null +++ b/Sources/arm/logicnode/ObjectNode.hx @@ -0,0 +1,27 @@ +package arm.logicnode; + +import armory.object.Object; +import armory.logicnode.LogicNode; +import armory.logicnode.LogicTree; + +class ObjectNode extends LogicNode { + + public var objectName:String; + public var value:Object; + + public function new(tree:LogicTree, objectName:String = "") { + this.objectName = objectName; + super(tree); + } + + override function get(from:Int):Dynamic { + if (inputs.length > 0) return inputs[0].get(); + value = objectName != "" ? armory.Scene.active.getChild(objectName) : null; + return value; + } + + override function set(value:Dynamic) { + if (inputs.length > 0) inputs[0].set(value); + else this.value = value; + } +} diff --git a/Sources/arm/logicnode/OnBrushNode.hx b/Sources/arm/logicnode/OnBrushNode.hx new file mode 100644 index 00000000..898c0520 --- /dev/null +++ b/Sources/arm/logicnode/OnBrushNode.hx @@ -0,0 +1,26 @@ +package arm.logicnode; + +import armory.logicnode.LogicNode; +import armory.logicnode.LogicTree; +import armory.math.Vec4; + +class OnBrushNode extends LogicNode { + + var coords = new Vec4(); + + public function new(tree:LogicTree) { + super(tree); + arm.UITrait.notifyOnBrush(onBrush); + } + + override function get(from:Int):Dynamic { + var mouse = armory.system.Input.getMouse(); + coords.x = mouse.x; + coords.y = mouse.y; + return coords; + } + + function onBrush() { + run(); + } +} diff --git a/Sources/arm/logicnode/PaintNode.hx b/Sources/arm/logicnode/PaintNode.hx new file mode 100644 index 00000000..1d2c02f0 --- /dev/null +++ b/Sources/arm/logicnode/PaintNode.hx @@ -0,0 +1,22 @@ +package arm.logicnode; + +import armory.logicnode.LogicNode; +import armory.logicnode.LogicTree; +import armory.math.Vec4; + +class PaintNode extends LogicNode { + + public function new(tree:LogicTree) { + super(tree); + } + + override function run() { + arm.UITrait.paint = true; + + var coords:Vec4 = inputs[1].get(); + arm.UITrait.paintX = coords.x; + arm.UITrait.paintY = coords.y; + + super.run(); + } +} diff --git a/Sources/arm/logicnode/StringNode.hx b/Sources/arm/logicnode/StringNode.hx new file mode 100644 index 00000000..5c6ecd9e --- /dev/null +++ b/Sources/arm/logicnode/StringNode.hx @@ -0,0 +1,24 @@ +package arm.logicnode; + +import armory.logicnode.LogicNode; +import armory.logicnode.LogicTree; + +class StringNode extends LogicNode { + + public var value:String; + + public function new(tree:LogicTree, value = "") { + super(tree); + this.value = value; + } + + override function get(from:Int):Dynamic { + if (inputs.length > 0) return inputs[0].get(); + return value; + } + + override function set(value:Dynamic) { + if (inputs.length > 0) inputs[0].set(value); + else this.value = value; + } +} diff --git a/Sources/arm/logicnode/VectorNode.hx b/Sources/arm/logicnode/VectorNode.hx new file mode 100644 index 00000000..32115f0a --- /dev/null +++ b/Sources/arm/logicnode/VectorNode.hx @@ -0,0 +1,33 @@ +package arm.logicnode; + +import armory.logicnode.LogicNode; +import armory.logicnode.LogicTree; +import armory.math.Vec4; + +class VectorNode extends LogicNode { + + var value = new Vec4(); + + public function new(tree:LogicTree, x:Null = null, y:Null = null, z:Null = null) { + super(tree); + + if (x != null) { + addInput(new FloatNode(tree, x), 0); + addInput(new FloatNode(tree, y), 0); + addInput(new FloatNode(tree, z), 0); + } + } + + override function get(from:Int):Dynamic { + value.x = inputs[0].get(); + value.y = inputs[1].get(); + value.z = inputs[2].get(); + return value; + } + + override function set(value:Dynamic) { + inputs[0].set(value.x); + inputs[1].set(value.y); + inputs[2].set(value.z); + } +} diff --git a/Sources/arm/renderpath/RenderPathCreator.hx b/Sources/arm/renderpath/RenderPathCreator.hx new file mode 100644 index 00000000..a5491173 --- /dev/null +++ b/Sources/arm/renderpath/RenderPathCreator.hx @@ -0,0 +1,1240 @@ +// Reference: https://github.com/armory3d/armory_docs/blob/master/dev/renderpath.md +package arm.renderpath; + +import iron.RenderPath; + +class RenderPathCreator { + + static var path:RenderPath; + + public static function get():RenderPath { + path = new RenderPath(); + init(); + path.commands = commands; + return path; + } + + #if (rp_renderer == "Forward") + static function init() { + // #if rp_shadowmap // Auto-created + // { + // var size = getShadowmapSize(); + // var t = new RenderTargetRaw(); + // t.name = "shadowMap"; + // t.width = size; + // t.height = size; + // t.format = "DEPTH16"; + // path.createRenderTarget(t); + // } + // #end + + #if (rp_background == "World") + { + path.loadShader("shader_datas/world/world"); + } + #end + + #if rp_render_to_texture + { + path.createDepthBuffer("main", "DEPTH24"); + + var t = new RenderTargetRaw(); + t.name = "lbuf"; + t.width = 0; + t.height = 0; + t.format = getHdrFormat(); + t.displayp = getDisplayp(); + var ss = getSuperSampling(); + if (ss != 1) t.scale = ss; + t.depth_buffer = "main"; + path.createRenderTarget(t); + + #if rp_compositornodes + { + path.loadShader("shader_datas/compositor_pass/compositor_pass"); + } + #else + { + path.loadShader("shader_datas/copy_pass/copy_pass"); + } + #end + } + #end + + #if (rp_translucency) + { + initTranslucency(); + } + #end + + #if (rp_gi != "Off") + { + initGI(); + } + #end + } + + static function commands() { + + #if rp_shadowmap + { + var faces = path.getLamp(path.currentLampIndex).data.raw.shadowmap_cube ? 6 : 1; + for (i in 0...faces) { + if (faces > 1) path.currentFace = i; + path.setTarget("shadowMap"); + path.clearTarget(null, 1.0); + path.drawMeshes("shadowmap"); + } + path.currentFace = -1; + } + #end + + #if (rp_gi != "Off") + { + if (path.voxelize()) { + path.clearImage("voxels", 0x00000000); + path.setTarget(""); + var res = getVoxelRes(); + path.setViewport(res, res); + path.bindTarget("voxels", "voxels"); + #if rp_shadowmap + { + path.bindTarget("shadowMap", "shadowMap"); + } + #end + path.drawMeshes("voxel"); + path.generateMipmaps("voxels"); + } + } + #end + + #if rp_render_to_texture + { + path.setTarget("lbuf"); + } + #else + { + path.setTarget(""); + } + #end + + #if (rp_background == "Clear") + { + path.clearTarget(-1, 1.0); + } + #else + { + path.clearTarget(null, 1.0); + } + #end + + #if rp_depthprepass + { + path.drawMeshes("depth"); + } + #end + + #if rp_shadowmap + { + path.bindTarget("shadowMap", "shadowMap"); + } + #end + + #if (rp_gi != "Off") + { + path.bindTarget("voxels", "voxels"); + } + #end + + function drawMeshes() { + path.drawMeshes("mesh"); + #if (rp_background == "World") + { + path.drawSkydome("shader_datas/world/world"); + } + #end + + #if rp_translucency + { + drawTranslucency("lbuf"); + } + #end + } + + #if rp_stereo + { + path.drawStereo(drawMeshes); + } + #else + { + drawMeshes(); + } + #end + + #if rp_render_to_texture + { + path.setTarget(""); + path.bindTarget("lbuf", "tex"); + + #if rp_compositornodes + { + path.drawShader("shader_datas/compositor_pass/compositor_pass"); + } + #else + { + path.drawShader("shader_datas/copy_pass/copy_pass"); + } + #end + } + #end + + #if rp_overlays + { + path.clearTarget(null, 1.0); + path.drawMeshes("overlay"); + } + #end + } + #end + + + + + + #if (rp_renderer == "Deferred") + static function init() { + #if (rp_background == "World") + { + path.loadShader("shader_datas/world/world"); + } + #end + + #if (rp_translucency) + { + initTranslucency(); + } + #end + + #if (rp_gi != "Off") + { + initGI(); + } + #end + + { + path.createDepthBuffer("main", "DEPTH24"); + + var t = new RenderTargetRaw(); + t.name = "tex"; + t.width = 0; + t.height = 0; + t.displayp = getDisplayp(); + t.format = getHdrFormat(); + var ss = getSuperSampling(); + if (ss != 1) t.scale = ss; + t.depth_buffer = "main"; + #if rp_autoexposure + t.mipmaps = true; + #end + path.createRenderTarget(t); + } + + { + var t = new RenderTargetRaw(); + t.name = "buf"; + t.width = 0; + t.height = 0; + t.displayp = getDisplayp(); + t.format = getHdrFormat(); + var ss = getSuperSampling(); + if (ss != 1) t.scale = ss; + path.createRenderTarget(t); + } + + { + path.createDepthBuffer("main", "DEPTH24"); + + var t = new RenderTargetRaw(); + t.name = "gbuffer0"; + t.width = 0; + t.height = 0; + t.displayp = getDisplayp(); + t.format = "RGBA64"; + var ss = getSuperSampling(); + if (ss != 1) t.scale = ss; + t.depth_buffer = "main"; + path.createRenderTarget(t); + } + + { + var t = new RenderTargetRaw(); + t.name = "gbuffer1"; + t.width = 0; + t.height = 0; + t.displayp = getDisplayp(); + t.format = "RGBA64"; + var ss = getSuperSampling(); + if (ss != 1) t.scale = ss; + path.createRenderTarget(t); + } + + #if arm_veloc + { + var t = new RenderTargetRaw(); + t.name = "gbuffer2"; + t.width = 0; + t.height = 0; + t.displayp = getDisplayp(); + t.format = "RGBA64"; + var ss = getSuperSampling(); + if (ss != 1) t.scale = ss; + path.createRenderTarget(t); + } + + { + var t = new RenderTargetRaw(); + t.name = "taa"; + t.width = 0; + t.height = 0; + t.displayp = getDisplayp(); + t.format = "RGBA32"; + var ss = getSuperSampling(); + if (ss != 1) t.scale = ss; + path.createRenderTarget(t); + } + #end + + path.loadShader("shader_datas/deferred_indirect/deferred_indirect"); + path.loadShader("shader_datas/deferred_light/deferred_light"); + path.loadShader("shader_datas/deferred_light_quad/deferred_light_quad"); + + #if ((rp_ssgi == "RTGI") || (rp_ssgi == "RTAO")) + { + path.loadShader("shader_datas/ssgi_pass/ssgi_pass"); + path.loadShader("shader_datas/ssgi_blur_pass/ssgi_blur_pass_x"); + path.loadShader("shader_datas/ssgi_blur_pass/ssgi_blur_pass_y"); + } + #elseif (rp_ssgi == "SSAO") + { + path.loadShader("shader_datas/ssao_pass/ssao_pass"); + path.loadShader("shader_datas/blur_edge_pass/blur_edge_pass_x"); + path.loadShader("shader_datas/blur_edge_pass/blur_edge_pass_y"); + } + #end + + #if ((rp_ssgi != "Off") || (rp_antialiasing == "SMAA") || (rp_antialiasing == "TAA")) + { + var t = new RenderTargetRaw(); + t.name = "bufa"; + t.width = 0; + t.height = 0; + t.displayp = getDisplayp(); + t.format = "RGBA32"; + var ss = getSuperSampling(); + if (ss != 1) t.scale = ss; + path.createRenderTarget(t); + } + { + var t = new RenderTargetRaw(); + t.name = "bufb"; + t.width = 0; + t.height = 0; + t.displayp = getDisplayp(); + t.format = "RGBA32"; + var ss = getSuperSampling(); + if (ss != 1) t.scale = ss; + path.createRenderTarget(t); + } + #end + + #if rp_rendercapture + { + var t = new RenderTargetRaw(); + t.name = "capture"; + t.width = 0; + t.height = 0; + t.format = getRenderCaptureFormat(); + path.createRenderTarget(t); + } + #end + + #if rp_compositornodes + { + path.loadShader("shader_datas/compositor_pass/compositor_pass"); + } + #end + + #if ((!rp_compositornodes) || (rp_antialiasing == "TAA") || (rp_rendercapture) || (rp_motionblur == "Camera") || (rp_motionblur == "Object")) + { + path.loadShader("shader_datas/copy_pass/copy_pass"); + } + #end + + #if ((rp_antialiasing == "SMAA") || (rp_antialiasing == "TAA")) + { + path.loadShader("shader_datas/smaa_edge_detect/smaa_edge_detect"); + path.loadShader("shader_datas/smaa_blend_weight/smaa_blend_weight"); + path.loadShader("shader_datas/smaa_neighborhood_blend/smaa_neighborhood_blend"); + + #if (rp_antialiasing == "TAA") + { + path.loadShader("shader_datas/taa_pass/taa_pass"); + } + #end + } + #end + + #if (rp_supersampling == 4) + { + path.loadShader("shader_datas/supersample_resolve/supersample_resolve"); + } + #end + + #if rp_volumetriclight + { + path.loadShader("shader_datas/volumetric_light_quad/volumetric_light_quad"); + path.loadShader("shader_datas/volumetric_light/volumetric_light"); + path.loadShader("shader_datas/blur_edge_pass/blur_edge_pass_x"); + path.loadShader("shader_datas/blur_edge_pass/blur_edge_pass_y_blend_add"); + } + #end + + #if rp_ocean + { + path.loadShader("shader_datas/water_pass/water_pass"); + } + #end + + #if rp_bloom + { + var t = new RenderTargetRaw(); + t.name = "bloomtex"; + t.width = 0; + t.height = 0; + t.scale = 0.25; + t.format = getHdrFormat(); + path.createRenderTarget(t); + } + + { + var t = new RenderTargetRaw(); + t.name = "bloomtex2"; + t.width = 0; + t.height = 0; + t.scale = 0.25; + t.format = getHdrFormat(); + path.createRenderTarget(t); + } + + { + path.loadShader("shader_datas/bloom_pass/bloom_pass"); + path.loadShader("shader_datas/blur_gaus_pass/blur_gaus_pass_x"); + path.loadShader("shader_datas/blur_gaus_pass/blur_gaus_pass_y"); + path.loadShader("shader_datas/blur_gaus_pass/blur_gaus_pass_y_blend"); + } + #end + + #if rp_sss + { + path.loadShader("shader_datas/sss_pass/sss_pass_x"); + path.loadShader("shader_datas/sss_pass/sss_pass_y"); + } + #end + + #if rp_ssr + { + path.loadShader("shader_datas/ssr_pass/ssr_pass"); + path.loadShader("shader_datas/blur_adaptive_pass/blur_adaptive_pass_x"); + path.loadShader("shader_datas/blur_adaptive_pass/blur_adaptive_pass_y3_blend"); + + #if rp_ssr_half + { + var t = new RenderTargetRaw(); + t.name = "ssra"; + t.width = 0; + t.height = 0; + t.scale = 0.5; + t.format = getHdrFormat(); + path.createRenderTarget(t); + } + { + var t = new RenderTargetRaw(); + t.name = "ssrb"; + t.width = 0; + t.height = 0; + t.scale = 0.5; + t.format = getHdrFormat(); + path.createRenderTarget(t); + } + #end + } + #end + + #if ((rp_motionblur == "Camera") || (rp_motionblur == "Object")) + { + #if (rp_motionblur == "Camera") + { + path.loadShader("shader_datas/motion_blur_pass/motion_blur_pass"); + } + #else + { + path.loadShader("shader_datas/motion_blur_veloc_pass/motion_blur_veloc_pass"); + } + #end + } + #end + + #if rp_soft_shadows + { + path.loadShader("shader_datas/dilate_pass/dilate_pass_x"); + path.loadShader("shader_datas/dilate_pass/dilate_pass_y"); + path.loadShader("shader_datas/visibility_pass/visibility_pass"); + path.loadShader("shader_datas/blur_shadow_pass/blur_shadow_pass_x"); + path.loadShader("shader_datas/blur_shadow_pass/blur_shadow_pass_y"); + { + var t = new RenderTargetRaw(); + t.name = "visa"; + t.width = 0; + t.height = 0; + t.format = 'R16'; + path.createRenderTarget(t); + } + { + var t = new RenderTargetRaw(); + t.name = "visb"; + t.width = 0; + t.height = 0; + t.format = 'R16'; + path.createRenderTarget(t); + } + { + var t = new RenderTargetRaw(); + t.name = "dist"; + t.width = 0; + t.height = 0; + t.format = 'R16'; + path.createRenderTarget(t); + } + } + #end + + // Paint + { + path.createDepthBuffer("paintdb", "DEPTH16"); + + var t = new RenderTargetRaw(); + t.name = "texpaint"; + t.width = 4096; + t.height = 4096; + t.format = 'RGBA32'; + t.depth_buffer = "paintdb"; + path.createRenderTarget(t); + } + { + var t = new RenderTargetRaw(); + t.name = "texpaint_nor"; + t.width = 4096; + t.height = 4096; + t.format = 'RGBA32'; + path.createRenderTarget(t); + } + { + var t = new RenderTargetRaw(); + t.name = "texpaint_pack"; + t.width = 4096; + t.height = 4096; + t.format = 'RGBA32'; + path.createRenderTarget(t); + } + // + } + + static function drawShadowMap(l:iron.object.LampObject) { + var faces = l.data.raw.shadowmap_cube ? 6 : 1; + for (j in 0...faces) { + if (faces > 1) path.currentFace = j; + path.setTarget("shadowMap"); + path.clearTarget(null, 1.0); + path.drawMeshes("shadowmap"); + } + path.currentFace = -1; + + // One lamp at a time for now, precompute all lamps for tiled + #if rp_soft_shadows + + path.setTarget("visa"); // Merge using min blend + path.bindTarget("shadowMap", "shadowMap"); + path.drawShader("shader_datas/dilate_pass/dilate_pass_x"); + + path.setTarget("visb"); + path.bindTarget("visa", "shadowMap"); + path.drawShader("shader_datas/dilate_pass/dilate_pass_y"); + + path.setTarget("visa", ["dist"]); + //if (i == 0) path.clearTarget(0x00000000); + path.bindTarget("visb", "dilate"); + path.bindTarget("shadowMap", "shadowMap"); + //path.bindTarget("_main", "gbufferD"); + path.bindTarget("gbuffer0", "gbuffer0"); + path.drawShader("shader_datas/visibility_pass/visibility_pass"); + + path.setTarget("visb"); + path.bindTarget("visa", "tex"); + path.bindTarget("gbuffer0", "gbuffer0"); + path.bindTarget("dist", "dist"); + path.drawShader("shader_datas/blur_shadow_pass/blur_shadow_pass_x"); + + path.setTarget("visa"); + path.bindTarget("visb", "tex"); + path.bindTarget("gbuffer0", "gbuffer0"); + path.bindTarget("dist", "dist"); + path.drawShader("shader_datas/blur_shadow_pass/blur_shadow_pass_y"); + #end + } + + static function commands() { + + // Paint + if (arm.UITrait.depthDirty()) { + path.setTarget("texpaint"); + path.clearTarget(null, 1.0); + path.drawMeshes("depth"); // TODO: CHECK DEPTH EXPORT + } + + if (arm.UITrait.paintDirty()) { + path.setTarget("texpaint", ["texpaint_nor", "texpaint_pack"]); + path.bindTarget("_paintdb", "paintdb"); + path.drawMeshes("paint"); + } + // + + #if rp_dynres + { + DynamicResolutionScale.run(path); + } + #end + + #if arm_veloc + { + path.setTarget("gbuffer2"); + path.clearTarget(0xff000000); + path.setTarget("gbuffer0", ["gbuffer1", "gbuffer2"]); + } + #else + { + path.setTarget("gbuffer0", ["gbuffer1"]); + } + #end + + #if (rp_background == "Clear") + { + path.clearTarget(-1, 1.0); + } + #else + { + path.clearTarget(null, 1.0); + } + #end + + // Paint + path.bindTarget("texpaint", "texpaint"); + path.bindTarget("texpaint_nor", "texpaint_nor"); + path.bindTarget("texpaint_pack", "texpaint_pack"); + // + + path.drawMeshes("mesh"); + + #if rp_decals + { + // path.setTarget("gbuffer0", ["gbuffer1"]); + path.bindTarget("_main", "gbufferD"); + path.drawDecals("decal"); + } + #end + + #if ((rp_ssgi == "RTGI") || (rp_ssgi == "RTAO")) + { + path.setTarget("bufa"); + path.bindTarget("_main", "gbufferD"); + path.bindTarget("gbuffer0", "gbuffer0"); + #if ((rp_ssgi == "RTGI")) + path.bindTarget("gbuffer1", "gbuffer1"); + #end + path.drawShader("shader_datas/ssgi_pass/ssgi_pass"); + + path.setTarget("bufb"); + path.bindTarget("gbuffer0", "gbuffer0"); + path.bindTarget("bufa", "tex"); + path.drawShader("shader_datas/ssgi_blur_pass/ssgi_blur_pass_x"); + + path.setTarget("bufa"); + path.bindTarget("gbuffer0", "gbuffer0"); + path.bindTarget("bufb", "tex"); + path.drawShader("shader_datas/ssgi_blur_pass/ssgi_blur_pass_y"); + } + #elseif (rp_ssgi == "SSAO") + { + path.setTarget("bufa"); + path.bindTarget("_main", "gbufferD"); + path.bindTarget("gbuffer0", "gbuffer0"); + path.drawShader("shader_datas/ssao_pass/ssao_pass"); + + path.setTarget("bufb"); + path.bindTarget("bufa", "tex"); + path.bindTarget("gbuffer0", "gbuffer0"); + path.drawShader("shader_datas/blur_edge_pass/blur_edge_pass_x"); + + path.setTarget("bufa"); + path.bindTarget("bufb", "tex"); + path.bindTarget("gbuffer0", "gbuffer0"); + path.drawShader("shader_datas/blur_edge_pass/blur_edge_pass_y"); + } + #end + + // Voxels + #if (rp_gi != "Off") + { + #if ((rp_shadowmap) && (rp_gi == "Voxel GI")) + { + if (path.lampCastShadow() && iron.Scene.active.lamps.length > 0) { + drawShadowMap(iron.Scene.active.lamps[0]); + } + } + #end + + if (path.voxelize()) { + path.clearImage("voxels", 0x00000000); + path.setTarget(""); + var res = getVoxelRes(); + path.setViewport(res, res); + path.bindTarget("voxels", "voxels"); + #if ((rp_shadowmap) && (rp_gi == "Voxel GI")) + { + path.bindTarget("shadowMap", "shadowMap"); + } + #end + path.drawMeshes("voxel"); + path.generateMipmaps("voxels"); + } + } + #end + + // Indirect + path.setTarget("tex"); + path.bindTarget("_main", "gbufferD"); + path.bindTarget("gbuffer0", "gbuffer0"); + path.bindTarget("gbuffer1", "gbuffer1"); + #if (rp_ssgi != "Off") + { + path.bindTarget("bufa", "ssaotex"); + } + #end + #if (rp_gi != "Off") + { + path.bindTarget("voxels", "voxels"); + } + #end + path.drawShader("shader_datas/deferred_indirect/deferred_indirect"); + + // Direct + var lamps = iron.Scene.active.lamps; + for (i in 0...lamps.length) { + var l = lamps[i]; + if (!l.visible) continue; + path.currentLampIndex = i; + + #if ((rp_shadowmap) && (rp_gi != "Voxel GI")) + { + if (path.lampCastShadow()) { + drawShadowMap(l); + } + } + #end + + path.setTarget("tex"); + path.bindTarget("_main", "gbufferD"); + path.bindTarget("gbuffer0", "gbuffer0"); + path.bindTarget("gbuffer1", "gbuffer1"); + + #if rp_shadowmap + { + if (path.lampCastShadow()) { + #if rp_soft_shadows + path.bindTarget("visa", "svisibility"); + #else + path.bindTarget("shadowMap", "shadowMap"); + #end + } + } + #end + + #if ((rp_voxelgi_shadows) || (rp_voxelgi_refraction)) + { + path.bindTarget("voxels", "voxels"); + } + #end + + if (path.lampIsSun()) { + path.drawShader("shader_datas/deferred_light_quad/deferred_light_quad"); + } + else { + path.drawLampVolume("shader_datas/deferred_light/deferred_light"); + } + + #if rp_volumetriclight + { + path.setTarget("buf"); + path.bindTarget("_main", "gbufferD"); + path.bindTarget("shadowMap", "shadowMap"); + if (path.lampIsSun()) { + path.drawShader("shader_datas/volumetric_light_quad/volumetric_light_quad"); + } + else { + path.drawLampVolume("shader_datas/volumetric_light/volumetric_light"); + } + path.setTarget("gbuffer1"); + path.bindTarget("buf", "tex"); + path.bindTarget("gbuffer0", "gbuffer0"); + path.drawShader("shader_datas/blur_edge_pass/blur_edge_pass_x"); + + path.setTarget("tex"); + path.bindTarget("gbuffer1", "tex"); + path.bindTarget("gbuffer0", "gbuffer0"); + path.drawShader("shader_datas/blur_edge_pass/blur_edge_pass_y_blend_add"); + } + #end + } + path.currentLampIndex = 0; + + #if (rp_background == "World") + { + path.drawSkydome("shader_datas/world/world"); + } + #end + + #if rp_ocean + { + path.setTarget("tex"); + path.bindTarget("_main", "gbufferD"); + path.drawShader("shader_datas/water_pass/water_pass"); + } + #end + + #if rp_blending + { + path.drawMeshes("blend"); + } + #end + + #if rp_translucency + { + drawTranslucency("tex"); + } + #end + + #if rp_bloom + { + path.setTarget("bloomtex"); + path.bindTarget("tex", "tex"); + path.drawShader("shader_datas/bloom_pass/bloom_pass"); + + path.setTarget("bloomtex2"); + path.bindTarget("bloomtex", "tex"); + path.drawShader("shader_datas/blur_gaus_pass/blur_gaus_pass_x"); + + path.setTarget("bloomtex"); + path.bindTarget("bloomtex2", "tex"); + path.drawShader("shader_datas/blur_gaus_pass/blur_gaus_pass_y"); + + path.setTarget("bloomtex2"); + path.bindTarget("bloomtex", "tex"); + path.drawShader("shader_datas/blur_gaus_pass/blur_gaus_pass_x"); + + path.setTarget("bloomtex"); + path.bindTarget("bloomtex2", "tex"); + path.drawShader("shader_datas/blur_gaus_pass/blur_gaus_pass_y"); + + path.setTarget("bloomtex2"); + path.bindTarget("bloomtex", "tex"); + path.drawShader("shader_datas/blur_gaus_pass/blur_gaus_pass_x"); + + path.setTarget("bloomtex"); + path.bindTarget("bloomtex2", "tex"); + path.drawShader("shader_datas/blur_gaus_pass/blur_gaus_pass_y"); + + path.setTarget("bloomtex2"); + path.bindTarget("bloomtex", "tex"); + path.drawShader("shader_datas/blur_gaus_pass/blur_gaus_pass_x"); + + path.setTarget("tex"); + path.bindTarget("bloomtex2", "tex"); + path.drawShader("shader_datas/blur_gaus_pass/blur_gaus_pass_y_blend"); + } + #end + + #if rp_sss + { + path.setTarget("buf"); + path.bindTarget("tex", "tex"); + path.bindTarget("_main", "gbufferD"); + path.bindTarget("gbuffer1", "gbuffer1"); + path.drawShader("shader_datas/sss_pass/sss_pass_x"); + + path.setTarget("tex"); + // TODO: can not bind tex + path.bindTarget("tex", "tex"); + path.bindTarget("_main", "gbufferD"); + path.bindTarget("gbuffer1", "gbuffer1"); + path.drawShader("shader_datas/sss_pass/sss_pass_y"); + } + #end + + #if rp_ssr + { + #if rp_ssr_half + var targeta = "ssra"; + var targetb = "ssrb"; + #else + var targeta = "buf"; + var targetb = "gbuffer1"; + #end + path.setTarget(targeta); + path.bindTarget("tex", "tex"); + path.bindTarget("_main", "gbufferD"); + path.bindTarget("gbuffer0", "gbuffer0"); + path.drawShader("shader_datas/ssr_pass/ssr_pass"); + + path.setTarget(targetb); + path.bindTarget(targeta, "tex"); + path.bindTarget("gbuffer0", "gbuffer0"); + path.drawShader("shader_datas/blur_adaptive_pass/blur_adaptive_pass_x"); + + path.setTarget("tex"); + path.bindTarget(targetb, "tex"); + path.bindTarget("gbuffer0", "gbuffer0"); + path.drawShader("shader_datas/blur_adaptive_pass/blur_adaptive_pass_y3_blend"); + } + #end + + #if ((rp_motionblur == "Camera") || (rp_motionblur == "Object")) + { + path.setTarget("buf"); + path.bindTarget("tex", "tex"); + path.bindTarget("gbuffer0", "gbuffer0"); + #if (rp_motionblur == "Camera") + { + path.bindTarget("_main", "gbufferD"); + path.drawShader("shader_datas/motion_blur_pass/motion_blur_pass"); + } + #else + { + path.bindTarget("gbuffer2", "sveloc"); + path.drawShader("shader_datas/motion_blur_veloc_pass/motion_blur_veloc_pass"); + } + #end + path.setTarget("tex"); + path.bindTarget("buf", "tex"); + path.drawShader("shader_datas/copy_pass/copy_pass"); + } + #end + + // We are just about to enter compositing, add more custom passes here + // #if rp_custom_pass + // { + // } + // #end + + // Begin compositor + #if rp_autoexposure + { + path.generateMipmaps("tex"); + } + #end + + #if ((rp_supersampling == 4) || (rp_rendercapture)) + var framebuffer = "buf"; + #else + var framebuffer = ""; + #end + + #if ((rp_antialiasing == "Off") || (rp_antialiasing == "FXAA")) + { + path.setTarget(framebuffer); + } + #else + { + path.setTarget("buf"); + } + #end + path.bindTarget("tex", "tex"); + + #if rp_compositornodes + { + path.drawShader("shader_datas/compositor_pass/compositor_pass"); + } + #else + { + path.drawShader("shader_datas/copy_pass/copy_pass"); + } + #end + // End compositor + + #if rp_overlays + { + path.clearTarget(null, 1.0); + path.drawMeshes("overlay"); + } + #end + + #if ((rp_antialiasing == "SMAA") || (rp_antialiasing == "TAA")) + { + path.setTarget("bufa"); + path.clearTarget(0x00000000); + path.bindTarget("buf", "colorTex"); + path.drawShader("shader_datas/smaa_edge_detect/smaa_edge_detect"); + + path.setTarget("bufb"); + path.clearTarget(0x00000000); + path.bindTarget("bufa", "edgesTex"); + path.drawShader("shader_datas/smaa_blend_weight/smaa_blend_weight"); + + #if (rp_antialiasing == "TAA") + path.setTarget("bufa"); + #else + path.setTarget(framebuffer); + #end + path.bindTarget("buf", "colorTex"); + path.bindTarget("bufb", "blendTex"); + path.drawShader("shader_datas/smaa_neighborhood_blend/smaa_neighborhood_blend"); + + #if (rp_antialiasing == "TAA") + { + path.setTarget(framebuffer); + path.bindTarget("bufa", "tex"); + path.bindTarget("taa", "tex2"); + path.bindTarget("gbuffer2", "sveloc"); + path.drawShader("shader_datas/taa_pass/taa_pass"); + + path.setTarget("taa"); + path.bindTarget("bufa", "tex"); + path.drawShader("shader_datas/copy_pass/copy_pass"); + } + #end + } + #end + + #if (rp_supersampling == 4) + { + #if rp_rendercapture + // TODO: ss4 + capture broken + var final = "capture"; + #else + var final = ""; + #end + path.setTarget(final); + path.bindTarget(framebuffer, "tex"); + path.drawShader("shader_datas/supersample_resolve/supersample_resolve"); + } + #elseif (rp_rendercapture) + { + path.setTarget("capture"); + path.bindTarget(framebuffer, "tex"); + path.drawShader("shader_datas/copy_pass/copy_pass"); + } + #end + } + #end + + + + + + // #if (rp_renderer == "Deferred Plus") + // static function init() { + + // } + + // static function commands() { + + // } + // #end + + + + + + #if (rp_translucency) + static function initTranslucency() { + path.createDepthBuffer("main", "DEPTH24"); + + var t = new RenderTargetRaw(); + t.name = "accum"; + t.width = 0; + t.height = 0; + t.displayp = getDisplayp(); + t.format = "RGBA64"; + var ss = getSuperSampling(); + if (ss != 1) t.scale = ss; + t.depth_buffer = "main"; + path.createRenderTarget(t); + + var t = new RenderTargetRaw(); + t.name = "revealage"; + t.width = 0; + t.height = 0; + t.displayp = getDisplayp(); + t.format = "RGBA64"; + var ss = getSuperSampling(); + if (ss != 1) t.scale = ss; + t.depth_buffer = "main"; + path.createRenderTarget(t); + + path.loadShader("shader_datas/translucent_resolve/translucent_resolve"); + } + + static function drawTranslucency(target:String) { + path.setTarget("accum"); + path.clearTarget(0xff000000); + path.setTarget("revealage"); + path.clearTarget(0xffffffff); + path.setTarget("accum", ["revealage"]); + #if rp_shadowmap + { + path.bindTarget("shadowMap", "shadowMap"); + } + #end + path.drawMeshes("translucent"); + #if rp_render_to_texture + { + path.setTarget(target); + } + #else + { + path.setTarget(""); + } + #end + path.bindTarget("accum", "gbuffer0"); + path.bindTarget("revealage", "gbuffer1"); + path.drawShader("shader_datas/translucent_resolve/translucent_resolve"); + } + #end + + #if (rp_gi != "Off") + static function initGI() { + var t = new RenderTargetRaw(); + t.name = "voxels"; + #if (rp_gi == "Voxel AO") + { + t.format = "R8"; + } + #elseif (rp_voxelgi_hdr) + { + t.format = "RGBA64"; + } + #else + { + t.format = "RGBA32"; + } + #end + var res = getVoxelRes(); + var resZ = getVoxelResZ(); + t.width = res; + t.height = res; + t.depth = Std.int(res * resZ); + t.is_image = true; + path.createRenderTarget(t); + } + #end + + static inline function getShadowmapSize():Int { + #if (rp_shadowmap_size == 512) + return 512; + #elseif (rp_shadowmap_size == 1024) + return 1024; + #elseif (rp_shadowmap_size == 2048) + return 2048; + #elseif (rp_shadowmap_size == 4096) + return 4096; + #elseif (rp_shadowmap_size == 8192) + return 8192; + #elseif (rp_shadowmap_size == 16384) + return 16384; + #else + return 0; + #end + } + + static inline function getVoxelRes():Int { + #if (rp_voxelgi_resolution == 512) + return 512; + #elseif (rp_voxelgi_resolution == 256) + return 256; + #elseif (rp_voxelgi_resolution == 128) + return 128; + #elseif (rp_voxelgi_resolution == 64) + return 64; + #elseif (rp_voxelgi_resolution == 32) + return 32; + #else + return 0; + #end + } + + static inline function getVoxelResZ():Float { + #if (rp_voxelgi_resolution_z == 1.0) + return 1.0; + #elseif (rp_voxelgi_resolution_z == 0.5) + return 0.5; + #elseif (rp_voxelgi_resolution_z == 0.25) + return 0.25; + #else + return 0.0; + #end + } + + static inline function getSuperSampling():Int { + #if (rp_supersampling == 2) + return 2; + #elseif (rp_supersampling == 4) + return 4; + #else + return 1; + #end + } + + static inline function getHdrFormat():String { + #if rp_hdr + return "RGBA64"; + #else + return "RGBA32"; + #end + } + + static inline function getDisplayp():Null { + #if (rp_resolution == 480) + return 480; + #elseif (rp_resolution == 720) + return 720; + #elseif (rp_resolution == 1080) + return 1080; + #elseif (rp_resolution == 1440) + return 1440; + #elseif (rp_resolution == 2160) + return 2160; + #else + return null; + #end + } + + static inline function getRenderCaptureFormat():String { + #if (rp_rendercapture_format == "8bit") + return "RGBA32"; + #elseif (rp_rendercapture_format == "16bit") + return "RGBA64"; + #elseif (rp_rendercapture_format == "32bit") + return "RGBA128"; + #else + return "RGBA32"; + #end + } +} diff --git a/Tools/cmft/LICENSE b/Tools/cmft/LICENSE new file mode 100644 index 00000000..98970a16 --- /dev/null +++ b/Tools/cmft/LICENSE @@ -0,0 +1,26 @@ +Copyright 2014-2015 Dario Manesku. All rights reserved. + +https://github.com/dariomanesku/cmft + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY COPYRIGHT HOLDER ``AS IS'' AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT +SHALL COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +OF THE POSSIBILITY OF SUCH DAMAGE. + +https://github.com/dariomanesku/cmft/blob/master/LICENSE diff --git a/Tools/cmft/cmft-linux64 b/Tools/cmft/cmft-linux64 new file mode 100644 index 00000000..8f4563f7 Binary files /dev/null and b/Tools/cmft/cmft-linux64 differ diff --git a/Tools/cmft/cmft-osx b/Tools/cmft/cmft-osx new file mode 100644 index 00000000..9aefa17d Binary files /dev/null and b/Tools/cmft/cmft-osx differ diff --git a/Tools/cmft/cmft.exe b/Tools/cmft/cmft.exe new file mode 100644 index 00000000..7bdcc3c6 Binary files /dev/null and b/Tools/cmft/cmft.exe differ diff --git a/Tools/cmft/cmft.sh b/Tools/cmft/cmft.sh new file mode 100644 index 00000000..921f6ee9 --- /dev/null +++ b/Tools/cmft/cmft.sh @@ -0,0 +1,176 @@ +#!/bin/bash + +# +# Copyright 2014-2015 Dario Manesku. All rights reserved. +# License: http://www.opensource.org/licenses/BSD-2-Clause +# + +CMFT=./cmft-osx + +# Prints help. +#eval $CMFT --help + +# Use this to list available OpenCL devices that can be used with cmft for processing. +#eval $CMFT --printCLDevices + +# Typical parameters for irradiance filter. +# eval $CMFT $@ --input "test.jpg" \ +# --filter irradiance \ +# --srcFaceSize 0 \ +# --dstFaceSize 0 \ +# --outputNum 1 \ +# --output0 "test_irr" \ +# --output0params hdr,rgbe,latlong + +# Typical parameters for generating spherical harmonics coefficients. +eval $CMFT $@ --input "test.hdr" \ + --filter shcoeffs \ + --outputNum 1 \ + --output0 "test" + +# Typical parameters for radiance filter. +# eval $CMFT $@ --input "test.jpg" \ +# ::Filter options \ +# --filter radiance \ +# --srcFaceSize 64 \ +# --excludeBase false \ +# --mipCount 7 \ +# --glossScale 10 \ +# --glossBias 3 \ +# --lightingModel blinnbrdf \ +# --edgeFixup none \ +# --dstFaceSize 64 \ +# ::Processing devices \ +# --numCpuProcessingThreads 4 \ +# --useOpenCL true \ +# --clVendor anyGpuVendor \ +# --deviceType gpu \ +# --deviceIndex 0 \ +# ::Aditional operations \ +# --inputGammaNumerator 2.2 \ +# --inputGammaDenominator 1.0 \ +# --outputGammaNumerator 1.0 \ +# --outputGammaDenominator 2.2 \ +# --generateMipChain true \ +# ::Output \ +# --outputNum 1 \ +# --output0 "envmap_rad" \ +# --output0params hdr,rgbe,latlong \ + # --output1 "okretnica_pmrem" \ + # --output1params ktx,rgba8,cubemap + +# Cmft can also be run without any processing filter. This can be used for performing image manipulations or exporting different image format. +#eval $CMFT $@ --input "okretnica.tga" \ +# --filter none \ +# ::Aditional operations \ +# --inputGamma 1.0 \ +# --inputGammaDenominator 1.0 \ +# --outputGamma 1.0 \ +# --outputGammaDenominator 1.0 \ +# --generateMipChain true \ +# ::Cubemap transformations \ +# --posXrotate90 \ +# --posXrotate180 \ +# --posXrotate270 \ +# --posXflipH \ +# --posXflipV \ +# --negXrotate90 \ +# --negXrotate180 \ +# --negXrotate270 \ +# --negXflipH \ +# --negXflipV \ +# --posYrotate90 \ +# --posYrotate180 \ +# --posYrotate270 \ +# --posYflipH \ +# --posYflipV \ +# --negYrotate90 \ +# --negYrotate180 \ +# --negYrotate270 \ +# --negYflipH \ +# --negYflipV \ +# --posZrotate90 \ +# --posZrotate180 \ +# --posZrotate270 \ +# --posZflipH \ +# --posZflipV \ +# --negZrotate90 \ +# --negZrotate180 \ +# --negZrotate270 \ +# --negZflipH \ +# --negZflipV \ +# ::Output \ +# --outputNum 1 \ +# --output0 "okretnica_dds" \ +# --output0params dds,bgra8,cubemap \ + +# Cmft with all parameters listed. This is mainly to have a look at what is all possible. +#eval $CMFT $@ --input "okretnica.tga" \ +# ::Filter options \ +# --filter radiance \ +# --srcFaceSize 256 \ +# --excludeBase false \ +# --mipCount 7 \ +# --glossScale 10 \ +# --glossBias 3 \ +# --lightingModel blinnbrdf \ +# --edgeFixup none \ +# --dstFaceSize 256 \ +# ::Processing devices \ +# --numCpuProcessingThreads 4 \ +# --useOpenCL true \ +# --clVendor anyGpuVendor \ +# --deviceType gpu \ +# --deviceIndex 0 \ +# ::Aditional operations \ +# --inputGamma 1.0 \ +# --inputGammaDenominator 1.0 \ +# --outputGamma 1.0 \ +# --outputGammaDenominator 1.0 \ +# --generateMipChain false \ +# ::Cubemap transformations \ +# --posXrotate90 \ +# --posXrotate180 \ +# --posXrotate270 \ +# --posXflipH \ +# --posXflipV \ +# --negXrotate90 \ +# --negXrotate180 \ +# --negXrotate270 \ +# --negXflipH \ +# --negXflipV \ +# --posYrotate90 \ +# --posYrotate180 \ +# --posYrotate270 \ +# --posYflipH \ +# --posYflipV \ +# --negYrotate90 \ +# --negYrotate180 \ +# --negYrotate270 \ +# --negYflipH \ +# --negYflipV \ +# --posZrotate90 \ +# --posZrotate180 \ +# --posZrotate270 \ +# --posZflipH \ +# --posZflipV \ +# --negZrotate90 \ +# --negZrotate180 \ +# --negZrotate270 \ +# --negZflipH \ +# --negZflipV \ +# ::Output \ +# --outputNum 5 \ +# --output0 "cmft_cubemap" --output0params dds,bgra8,cubemap \ +# --output1 "cmft_hstrip" --output1params dds,bgra8,hstrip \ +# --output2 "cmft_cubecross" --output2params ktx,rgba32f,hcross \ +# --output3 "cmft_facelist" --output3params tga,bgra8,facelist \ +# --output4 "cmft_latlong" --output4params hdr,rgbe,latlong + +# eval $CMFT $@ --input "shrine.hdr" \ +# --filter none \ +# ::Output \ +# --outputNum 1 \ +# --dstFaceSize 0 \ +# --output0 "test" \ +# --output0params hdr,rgbe,latlong \ No newline at end of file diff --git a/armorpaint.blend b/armorpaint.blend new file mode 100644 index 00000000..bed4f239 Binary files /dev/null and b/armorpaint.blend differ diff --git a/raw/brush.psd b/raw/brush.psd new file mode 100644 index 00000000..6f15046c Binary files /dev/null and b/raw/brush.psd differ diff --git a/raw/cube.obj b/raw/cube.obj new file mode 100644 index 00000000..28621dad --- /dev/null +++ b/raw/cube.obj @@ -0,0 +1,2027 @@ +# Blender v2.78 (sub 0) OBJ File: 'armorpaint.blend' +# www.blender.org +o Cube_Cube.001 +v -0.475000 -0.500000 0.475000 +v -0.475000 -0.475000 0.500000 +v -0.500000 -0.475000 0.475000 +v -0.475000 -0.499374 0.480560 +v -0.475000 -0.497525 0.485845 +v -0.475000 -0.494547 0.490586 +v -0.480560 -0.499374 0.475000 +v -0.480739 -0.498631 0.480799 +v -0.480715 -0.496693 0.486034 +v -0.480643 -0.493893 0.490369 +v -0.485845 -0.497525 0.475000 +v -0.486031 -0.496682 0.480764 +v -0.485718 -0.494873 0.485733 +v -0.485321 -0.492507 0.489560 +v -0.490586 -0.494547 0.475000 +v -0.490394 -0.493868 0.480660 +v -0.489564 -0.492500 0.485327 +v -0.488666 -0.490858 0.488666 +v -0.480560 -0.475000 0.499374 +v -0.485845 -0.475000 0.497525 +v -0.490586 -0.475000 0.494547 +v -0.475000 -0.480560 0.499374 +v -0.480799 -0.480739 0.498631 +v -0.486034 -0.480715 0.496693 +v -0.490369 -0.480643 0.493893 +v -0.475000 -0.485845 0.497525 +v -0.480764 -0.486031 0.496682 +v -0.485733 -0.485718 0.494873 +v -0.489560 -0.485321 0.492507 +v -0.475000 -0.490586 0.494547 +v -0.480660 -0.490394 0.493868 +v -0.485327 -0.489564 0.492500 +v -0.488666 -0.488666 0.490858 +v -0.499374 -0.480560 0.475000 +v -0.497525 -0.485845 0.475000 +v -0.494547 -0.490586 0.475000 +v -0.499374 -0.475000 0.480560 +v -0.498631 -0.480799 0.480739 +v -0.496693 -0.486034 0.480715 +v -0.493893 -0.490369 0.480643 +v -0.497525 -0.475000 0.485845 +v -0.496682 -0.480764 0.486031 +v -0.494873 -0.485733 0.485718 +v -0.492507 -0.489560 0.485321 +v -0.494547 -0.475000 0.490586 +v -0.493868 -0.480660 0.490394 +v -0.492500 -0.485327 0.489564 +v -0.490858 -0.488666 0.488666 +v -0.475000 0.475000 0.500000 +v -0.475000 0.500000 0.475000 +v -0.500000 0.475000 0.475000 +v -0.475000 0.480560 0.499374 +v -0.475000 0.485845 0.497525 +v -0.475000 0.490586 0.494547 +v -0.480560 0.475000 0.499374 +v -0.480739 0.480799 0.498631 +v -0.480715 0.486034 0.496693 +v -0.480643 0.490369 0.493893 +v -0.485845 0.475000 0.497525 +v -0.486031 0.480764 0.496682 +v -0.485718 0.485733 0.494873 +v -0.485321 0.489560 0.492507 +v -0.490586 0.475000 0.494547 +v -0.490394 0.480660 0.493868 +v -0.489564 0.485327 0.492500 +v -0.488666 0.488666 0.490858 +v -0.480560 0.499374 0.475000 +v -0.485845 0.497525 0.475000 +v -0.490586 0.494547 0.475000 +v -0.475000 0.499374 0.480560 +v -0.480799 0.498631 0.480739 +v -0.486034 0.496693 0.480715 +v -0.490369 0.493893 0.480643 +v -0.475000 0.497525 0.485845 +v -0.480764 0.496682 0.486031 +v -0.485733 0.494873 0.485718 +v -0.489560 0.492507 0.485321 +v -0.475000 0.494547 0.490586 +v -0.480660 0.493868 0.490394 +v -0.485327 0.492500 0.489564 +v -0.488666 0.490858 0.488666 +v -0.499374 0.475000 0.480560 +v -0.497525 0.475000 0.485845 +v -0.494547 0.475000 0.490586 +v -0.499374 0.480560 0.475000 +v -0.498631 0.480739 0.480799 +v -0.496693 0.480715 0.486034 +v -0.493893 0.480643 0.490369 +v -0.497525 0.485845 0.475000 +v -0.496682 0.486031 0.480764 +v -0.494873 0.485718 0.485733 +v -0.492507 0.485321 0.489560 +v -0.494547 0.490586 0.475000 +v -0.493868 0.490394 0.480660 +v -0.492500 0.489564 0.485327 +v -0.490858 0.488666 0.488666 +v -0.475000 -0.500000 -0.475000 +v -0.500000 -0.475000 -0.475000 +v -0.475000 -0.475000 -0.500000 +v -0.480560 -0.499374 -0.475000 +v -0.485845 -0.497525 -0.475000 +v -0.490586 -0.494547 -0.475000 +v -0.475000 -0.499374 -0.480560 +v -0.480799 -0.498631 -0.480739 +v -0.486034 -0.496693 -0.480715 +v -0.490369 -0.493893 -0.480643 +v -0.475000 -0.497525 -0.485845 +v -0.480764 -0.496682 -0.486031 +v -0.485733 -0.494873 -0.485718 +v -0.489560 -0.492507 -0.485321 +v -0.475000 -0.494547 -0.490586 +v -0.480660 -0.493868 -0.490394 +v -0.485327 -0.492500 -0.489564 +v -0.488666 -0.490858 -0.488666 +v -0.499374 -0.475000 -0.480560 +v -0.497525 -0.475000 -0.485845 +v -0.494547 -0.475000 -0.490586 +v -0.499374 -0.480560 -0.475000 +v -0.498631 -0.480739 -0.480799 +v -0.496693 -0.480715 -0.486034 +v -0.493893 -0.480643 -0.490369 +v -0.497525 -0.485845 -0.475000 +v -0.496682 -0.486031 -0.480764 +v -0.494873 -0.485718 -0.485733 +v -0.492507 -0.485321 -0.489560 +v -0.494547 -0.490586 -0.475000 +v -0.493868 -0.490394 -0.480660 +v -0.492500 -0.489564 -0.485327 +v -0.490858 -0.488666 -0.488666 +v -0.475000 -0.480560 -0.499374 +v -0.475000 -0.485845 -0.497525 +v -0.475000 -0.490586 -0.494547 +v -0.480560 -0.475000 -0.499374 +v -0.480739 -0.480799 -0.498631 +v -0.480715 -0.486034 -0.496693 +v -0.480643 -0.490369 -0.493893 +v -0.485845 -0.475000 -0.497525 +v -0.486031 -0.480764 -0.496682 +v -0.485718 -0.485733 -0.494873 +v -0.485321 -0.489560 -0.492507 +v -0.490586 -0.475000 -0.494547 +v -0.490394 -0.480660 -0.493868 +v -0.489564 -0.485327 -0.492500 +v -0.488666 -0.488666 -0.490858 +v -0.475000 0.500000 -0.475000 +v -0.475000 0.475000 -0.500000 +v -0.500000 0.475000 -0.475000 +v -0.475000 0.499374 -0.480560 +v -0.475000 0.497525 -0.485845 +v -0.475000 0.494547 -0.490586 +v -0.480560 0.499374 -0.475000 +v -0.480739 0.498631 -0.480799 +v -0.480715 0.496693 -0.486034 +v -0.480643 0.493893 -0.490369 +v -0.485845 0.497525 -0.475000 +v -0.486031 0.496682 -0.480764 +v -0.485718 0.494873 -0.485733 +v -0.485321 0.492507 -0.489560 +v -0.490586 0.494547 -0.475000 +v -0.490394 0.493868 -0.480660 +v -0.489564 0.492500 -0.485327 +v -0.488666 0.490858 -0.488666 +v -0.480560 0.475000 -0.499374 +v -0.485845 0.475000 -0.497525 +v -0.490586 0.475000 -0.494547 +v -0.475000 0.480560 -0.499374 +v -0.480799 0.480739 -0.498631 +v -0.486034 0.480715 -0.496693 +v -0.490369 0.480643 -0.493893 +v -0.475000 0.485845 -0.497525 +v -0.480764 0.486031 -0.496682 +v -0.485733 0.485718 -0.494873 +v -0.489560 0.485321 -0.492507 +v -0.475000 0.490586 -0.494547 +v -0.480660 0.490394 -0.493868 +v -0.485327 0.489564 -0.492500 +v -0.488666 0.488666 -0.490858 +v -0.499374 0.480560 -0.475000 +v -0.497525 0.485845 -0.475000 +v -0.494547 0.490586 -0.475000 +v -0.499374 0.475000 -0.480560 +v -0.498631 0.480799 -0.480739 +v -0.496693 0.486034 -0.480715 +v -0.493893 0.490369 -0.480643 +v -0.497525 0.475000 -0.485845 +v -0.496682 0.480764 -0.486031 +v -0.494873 0.485733 -0.485718 +v -0.492507 0.489560 -0.485321 +v -0.494547 0.475000 -0.490586 +v -0.493868 0.480660 -0.490394 +v -0.492500 0.485327 -0.489564 +v -0.490858 0.488666 -0.488666 +v 0.475000 -0.500000 0.475000 +v 0.500000 -0.475000 0.475000 +v 0.475000 -0.475000 0.500000 +v 0.480560 -0.499374 0.475000 +v 0.485845 -0.497525 0.475000 +v 0.490586 -0.494547 0.475000 +v 0.475000 -0.499374 0.480560 +v 0.480799 -0.498631 0.480739 +v 0.486034 -0.496693 0.480715 +v 0.490369 -0.493893 0.480643 +v 0.475000 -0.497525 0.485845 +v 0.480764 -0.496682 0.486031 +v 0.485733 -0.494873 0.485718 +v 0.489560 -0.492507 0.485321 +v 0.475000 -0.494547 0.490586 +v 0.480660 -0.493868 0.490394 +v 0.485327 -0.492500 0.489564 +v 0.488666 -0.490858 0.488666 +v 0.499374 -0.475000 0.480560 +v 0.497525 -0.475000 0.485845 +v 0.494547 -0.475000 0.490586 +v 0.499374 -0.480560 0.475000 +v 0.498631 -0.480739 0.480799 +v 0.496693 -0.480715 0.486034 +v 0.493893 -0.480643 0.490369 +v 0.497525 -0.485845 0.475000 +v 0.496682 -0.486031 0.480764 +v 0.494873 -0.485718 0.485733 +v 0.492507 -0.485321 0.489560 +v 0.494547 -0.490586 0.475000 +v 0.493868 -0.490394 0.480660 +v 0.492500 -0.489564 0.485327 +v 0.490858 -0.488666 0.488666 +v 0.475000 -0.480560 0.499374 +v 0.475000 -0.485845 0.497525 +v 0.475000 -0.490586 0.494547 +v 0.480560 -0.475000 0.499374 +v 0.480739 -0.480799 0.498631 +v 0.480715 -0.486034 0.496693 +v 0.480643 -0.490369 0.493893 +v 0.485845 -0.475000 0.497525 +v 0.486031 -0.480764 0.496682 +v 0.485718 -0.485733 0.494873 +v 0.485321 -0.489560 0.492507 +v 0.490586 -0.475000 0.494547 +v 0.490394 -0.480660 0.493868 +v 0.489564 -0.485327 0.492500 +v 0.488666 -0.488666 0.490858 +v 0.475000 0.500000 0.475000 +v 0.475000 0.475000 0.500000 +v 0.500000 0.475000 0.475000 +v 0.475000 0.499374 0.480560 +v 0.475000 0.497525 0.485845 +v 0.475000 0.494547 0.490586 +v 0.480560 0.499374 0.475000 +v 0.480739 0.498631 0.480799 +v 0.480715 0.496693 0.486034 +v 0.480643 0.493893 0.490369 +v 0.485845 0.497525 0.475000 +v 0.486031 0.496682 0.480764 +v 0.485718 0.494873 0.485733 +v 0.485321 0.492507 0.489560 +v 0.490586 0.494547 0.475000 +v 0.490394 0.493868 0.480660 +v 0.489564 0.492500 0.485327 +v 0.488666 0.490858 0.488666 +v 0.480560 0.475000 0.499374 +v 0.485845 0.475000 0.497525 +v 0.490586 0.475000 0.494547 +v 0.475000 0.480560 0.499374 +v 0.480799 0.480739 0.498631 +v 0.486034 0.480715 0.496693 +v 0.490369 0.480643 0.493893 +v 0.475000 0.485845 0.497525 +v 0.480764 0.486031 0.496682 +v 0.485733 0.485718 0.494873 +v 0.489560 0.485321 0.492507 +v 0.475000 0.490586 0.494547 +v 0.480660 0.490394 0.493868 +v 0.485327 0.489564 0.492500 +v 0.488666 0.488666 0.490858 +v 0.499374 0.480560 0.475000 +v 0.497525 0.485845 0.475000 +v 0.494547 0.490586 0.475000 +v 0.499374 0.475000 0.480560 +v 0.498631 0.480799 0.480739 +v 0.496693 0.486034 0.480715 +v 0.493893 0.490369 0.480643 +v 0.497525 0.475000 0.485845 +v 0.496682 0.480764 0.486031 +v 0.494873 0.485733 0.485718 +v 0.492507 0.489560 0.485321 +v 0.494547 0.475000 0.490586 +v 0.493868 0.480660 0.490394 +v 0.492500 0.485327 0.489564 +v 0.490858 0.488666 0.488666 +v 0.475000 -0.500000 -0.475000 +v 0.475000 -0.475000 -0.500000 +v 0.500000 -0.475000 -0.475000 +v 0.475000 -0.499374 -0.480560 +v 0.475000 -0.497525 -0.485845 +v 0.475000 -0.494547 -0.490586 +v 0.480560 -0.499374 -0.475000 +v 0.480739 -0.498631 -0.480799 +v 0.480715 -0.496693 -0.486034 +v 0.480643 -0.493893 -0.490369 +v 0.485845 -0.497525 -0.475000 +v 0.486031 -0.496682 -0.480764 +v 0.485718 -0.494873 -0.485733 +v 0.485321 -0.492507 -0.489560 +v 0.490586 -0.494547 -0.475000 +v 0.490394 -0.493868 -0.480660 +v 0.489564 -0.492500 -0.485327 +v 0.488666 -0.490858 -0.488666 +v 0.480560 -0.475000 -0.499374 +v 0.485845 -0.475000 -0.497525 +v 0.490586 -0.475000 -0.494547 +v 0.475000 -0.480560 -0.499374 +v 0.480799 -0.480739 -0.498631 +v 0.486034 -0.480715 -0.496693 +v 0.490369 -0.480643 -0.493893 +v 0.475000 -0.485845 -0.497525 +v 0.480764 -0.486031 -0.496682 +v 0.485733 -0.485718 -0.494873 +v 0.489560 -0.485321 -0.492507 +v 0.475000 -0.490586 -0.494547 +v 0.480660 -0.490394 -0.493868 +v 0.485327 -0.489564 -0.492500 +v 0.488666 -0.488666 -0.490858 +v 0.499374 -0.480560 -0.475000 +v 0.497525 -0.485845 -0.475000 +v 0.494547 -0.490586 -0.475000 +v 0.499374 -0.475000 -0.480560 +v 0.498631 -0.480799 -0.480739 +v 0.496693 -0.486034 -0.480715 +v 0.493893 -0.490369 -0.480643 +v 0.497525 -0.475000 -0.485845 +v 0.496682 -0.480764 -0.486031 +v 0.494873 -0.485733 -0.485718 +v 0.492507 -0.489560 -0.485321 +v 0.494547 -0.475000 -0.490586 +v 0.493868 -0.480660 -0.490394 +v 0.492500 -0.485327 -0.489564 +v 0.490858 -0.488666 -0.488666 +v 0.475000 0.500000 -0.475000 +v 0.500000 0.475000 -0.475000 +v 0.475000 0.475000 -0.500000 +v 0.480560 0.499374 -0.475000 +v 0.485845 0.497525 -0.475000 +v 0.490586 0.494547 -0.475000 +v 0.475000 0.499374 -0.480560 +v 0.480799 0.498631 -0.480739 +v 0.486034 0.496693 -0.480715 +v 0.490369 0.493893 -0.480643 +v 0.475000 0.497525 -0.485845 +v 0.480764 0.496682 -0.486031 +v 0.485733 0.494873 -0.485718 +v 0.489560 0.492507 -0.485321 +v 0.475000 0.494547 -0.490586 +v 0.480660 0.493868 -0.490394 +v 0.485327 0.492500 -0.489564 +v 0.488666 0.490858 -0.488666 +v 0.499374 0.475000 -0.480560 +v 0.497525 0.475000 -0.485845 +v 0.494547 0.475000 -0.490586 +v 0.499374 0.480560 -0.475000 +v 0.498631 0.480739 -0.480799 +v 0.496693 0.480715 -0.486034 +v 0.493893 0.480643 -0.490369 +v 0.497525 0.485845 -0.475000 +v 0.496682 0.486031 -0.480764 +v 0.494873 0.485718 -0.485733 +v 0.492507 0.485321 -0.489560 +v 0.494547 0.490586 -0.475000 +v 0.493868 0.490394 -0.480660 +v 0.492500 0.489564 -0.485327 +v 0.490858 0.488666 -0.488666 +v 0.475000 0.480560 -0.499374 +v 0.475000 0.485845 -0.497525 +v 0.475000 0.490586 -0.494547 +v 0.480560 0.475000 -0.499374 +v 0.480739 0.480799 -0.498631 +v 0.480715 0.486034 -0.496693 +v 0.480643 0.490369 -0.493893 +v 0.485845 0.475000 -0.497525 +v 0.486031 0.480764 -0.496682 +v 0.485718 0.485733 -0.494873 +v 0.485321 0.489560 -0.492507 +v 0.490586 0.475000 -0.494547 +v 0.490394 0.480660 -0.493868 +v 0.489564 0.485327 -0.492500 +v 0.488666 0.488666 -0.490858 +vt 0.3417 0.0083 +vt 0.6583 0.3250 +vt 0.3417 0.3250 +vt 0.0083 0.3250 +vt 0.3250 0.0083 +vt 0.3250 0.3250 +vt 0.3250 0.9917 +vt 0.0083 0.6750 +vt 0.3250 0.6750 +vt 0.3417 0.6583 +vt 0.6583 0.3417 +vt 0.6583 0.6583 +vt 0.0083 0.6583 +vt 0.3250 0.3417 +vt 0.3250 0.6583 +vt 0.9917 0.0065 +vt 0.9935 0.0083 +vt 0.9917 0.0083 +vt 0.9917 0.0047 +vt 0.9936 0.0064 +vt 0.9917 0.0031 +vt 0.9936 0.0047 +vt 0.9936 0.0000 +vt 0.9935 0.0032 +vt 0.9953 0.0083 +vt 0.9953 0.0064 +vt 0.9952 0.0048 +vt 0.9951 0.0035 +vt 0.9969 0.0083 +vt 0.9968 0.0064 +vt 0.9962 0.0038 +vt 0.9965 0.0049 +vt 0.9951 0.0000 +vt 0.6602 0.3250 +vt 0.6583 0.3269 +vt 0.6619 0.3250 +vt 0.6603 0.3269 +vt 0.6635 0.3250 +vt 0.6620 0.3269 +vt 0.6667 0.3269 +vt 0.6635 0.3269 +vt 0.6583 0.3286 +vt 0.6603 0.3287 +vt 0.6619 0.3286 +vt 0.6632 0.3284 +vt 0.6583 0.3302 +vt 0.6602 0.3301 +vt 0.6629 0.3296 +vt 0.6618 0.3299 +vt 0.6667 0.3284 +vt 0.6602 0.6583 +vt 0.6583 0.6602 +vt 0.6619 0.6583 +vt 0.6603 0.6602 +vt 0.6635 0.6583 +vt 0.6620 0.6602 +vt 0.6667 0.6602 +vt 0.6635 0.6602 +vt 0.6583 0.6619 +vt 0.6603 0.6620 +vt 0.6619 0.6619 +vt 0.6632 0.6618 +vt 0.6583 0.6635 +vt 0.6602 0.6635 +vt 0.6629 0.6629 +vt 0.6618 0.6632 +vt 0.6667 0.6618 +vt 0.9962 0.0000 +vt 1.0000 0.0038 +vt 0.6583 0.0065 +vt 0.6602 0.0083 +vt 0.6583 0.0083 +vt 0.6583 0.0047 +vt 0.6602 0.0064 +vt 0.6583 0.0031 +vt 0.6602 0.0047 +vt 0.6602 0.0000 +vt 0.6602 0.0032 +vt 0.6619 0.0083 +vt 0.6620 0.0064 +vt 0.6619 0.0048 +vt 0.6618 0.0035 +vt 0.6635 0.0083 +vt 0.6635 0.0064 +vt 0.6629 0.0038 +vt 0.6632 0.0049 +vt 0.6618 0.0000 +vt 0.0083 0.9935 +vt 0.0065 0.9917 +vt 0.0083 0.9917 +vt 0.0083 0.9953 +vt 0.0064 0.9936 +vt 0.0083 0.9969 +vt 0.0064 0.9953 +vt 0.0064 1.0000 +vt 0.0065 0.9968 +vt 0.0047 0.9917 +vt 0.0047 0.9936 +vt 0.0048 0.9952 +vt 0.0049 0.9965 +vt 0.0031 0.9917 +vt 0.0032 0.9936 +vt 0.0038 0.9962 +vt 0.0035 0.9951 +vt 0.0049 1.0000 +vt 0.3417 0.6602 +vt 0.3398 0.6583 +vt 0.3417 0.6619 +vt 0.3398 0.6603 +vt 0.3417 0.6635 +vt 0.3398 0.6620 +vt 0.3398 0.6667 +vt 0.3398 0.6635 +vt 0.3381 0.6583 +vt 0.3380 0.6603 +vt 0.3381 0.6619 +vt 0.3382 0.6632 +vt 0.3365 0.6583 +vt 0.3365 0.6602 +vt 0.3371 0.6629 +vt 0.3368 0.6618 +vt 0.3382 0.6667 +vt 0.6629 0.0000 +vt 0.6667 0.0038 +vt 0.9935 0.3250 +vt 0.9917 0.3269 +vt 0.9917 0.3250 +vt 0.9953 0.3250 +vt 0.9936 0.3269 +vt 0.9969 0.3250 +vt 0.9953 0.3269 +vt 1.0000 0.3269 +vt 0.9968 0.3269 +vt 0.9917 0.3286 +vt 0.9936 0.3287 +vt 0.9952 0.3286 +vt 0.9965 0.3284 +vt 0.9917 0.3302 +vt 0.9936 0.3301 +vt 0.9962 0.3296 +vt 0.9951 0.3299 +vt 1.0000 0.3284 +vt 0.6583 0.3398 +vt 0.6602 0.3417 +vt 0.6583 0.3381 +vt 0.6602 0.3397 +vt 0.6583 0.3365 +vt 0.6602 0.3380 +vt 0.6602 0.3333 +vt 0.6602 0.3365 +vt 0.6619 0.3417 +vt 0.6620 0.3397 +vt 0.6619 0.3381 +vt 0.6618 0.3368 +vt 0.6635 0.3417 +vt 0.6635 0.3398 +vt 0.6629 0.3371 +vt 0.6632 0.3382 +vt 0.6618 0.3333 +vt 0.3269 0.6583 +vt 0.3250 0.6602 +vt 0.3286 0.6583 +vt 0.3269 0.6602 +vt 0.3302 0.6583 +vt 0.3287 0.6602 +vt 0.3333 0.6602 +vt 0.3301 0.6602 +vt 0.3250 0.6619 +vt 0.3269 0.6620 +vt 0.3286 0.6619 +vt 0.3299 0.6618 +vt 0.3250 0.6635 +vt 0.3269 0.6635 +vt 0.3296 0.6629 +vt 0.3284 0.6632 +vt 0.3333 0.6618 +vt 1.0000 0.3296 +vt 0.9962 0.3333 +vt 0.3269 0.9917 +vt 0.3250 0.9935 +vt 0.3286 0.9917 +vt 0.3269 0.9936 +vt 0.3302 0.9917 +vt 0.3287 0.9936 +vt 0.3333 0.9936 +vt 0.3301 0.9935 +vt 0.3250 0.9953 +vt 0.3269 0.9953 +vt 0.3286 0.9952 +vt 0.3299 0.9951 +vt 0.3250 0.9969 +vt 0.3269 0.9968 +vt 0.3296 0.9962 +vt 0.3284 0.9965 +vt 0.3333 0.9951 +vt 0.0083 0.6602 +vt 0.0065 0.6583 +vt 0.0083 0.6619 +vt 0.0064 0.6603 +vt 0.0083 0.6635 +vt 0.0064 0.6620 +vt 0.0064 0.6667 +vt 0.0065 0.6635 +vt 0.0047 0.6583 +vt 0.0047 0.6603 +vt 0.0048 0.6619 +vt 0.0049 0.6632 +vt 0.0031 0.6583 +vt 0.0032 0.6602 +vt 0.0038 0.6629 +vt 0.0035 0.6618 +vt 0.0049 0.6667 +vt 0.3398 0.3417 +vt 0.3417 0.3398 +vt 0.3417 0.3417 +vt 0.3381 0.3417 +vt 0.3397 0.3398 +vt 0.3365 0.3417 +vt 0.3380 0.3398 +vt 0.3333 0.3398 +vt 0.3365 0.3398 +vt 0.3417 0.3381 +vt 0.3397 0.3380 +vt 0.3381 0.3381 +vt 0.3368 0.3382 +vt 0.3417 0.3365 +vt 0.3398 0.3365 +vt 0.3371 0.3371 +vt 0.3382 0.3368 +vt 0.3333 0.3382 +vt 0.3333 0.9962 +vt 0.3296 1.0000 +vt 0.6731 0.0083 +vt 0.6750 0.0065 +vt 0.6750 0.0083 +vt 0.6714 0.0083 +vt 0.6731 0.0064 +vt 0.6698 0.0083 +vt 0.6713 0.0064 +vt 0.6667 0.0064 +vt 0.6699 0.0065 +vt 0.6750 0.0047 +vt 0.6731 0.0047 +vt 0.6714 0.0048 +vt 0.6701 0.0049 +vt 0.6750 0.0031 +vt 0.6731 0.0032 +vt 0.6704 0.0038 +vt 0.6716 0.0035 +vt 0.6667 0.0049 +vt 0.3250 0.0065 +vt 0.3269 0.0083 +vt 0.3250 0.0047 +vt 0.3269 0.0064 +vt 0.3250 0.0031 +vt 0.3269 0.0047 +vt 0.3269 0.0000 +vt 0.3269 0.0032 +vt 0.3286 0.0083 +vt 0.3287 0.0064 +vt 0.3286 0.0048 +vt 0.3284 0.0035 +vt 0.3302 0.0083 +vt 0.3301 0.0064 +vt 0.3296 0.0038 +vt 0.3299 0.0049 +vt 0.3284 0.0000 +vt 0.3417 0.3269 +vt 0.3398 0.3250 +vt 0.3417 0.3286 +vt 0.3398 0.3269 +vt 0.3417 0.3302 +vt 0.3398 0.3287 +vt 0.3398 0.3333 +vt 0.3398 0.3301 +vt 0.3381 0.3250 +vt 0.3380 0.3269 +vt 0.3381 0.3286 +vt 0.3382 0.3299 +vt 0.3365 0.3250 +vt 0.3365 0.3269 +vt 0.3371 0.3296 +vt 0.3368 0.3284 +vt 0.3382 0.3333 +vt 0.6667 0.0038 +vt 0.6704 0.0000 +vt 0.0065 0.6750 +vt 0.0083 0.6731 +vt 0.0047 0.6750 +vt 0.0064 0.6731 +vt 0.0031 0.6750 +vt 0.0047 0.6731 +vt 0.0000 0.6731 +vt 0.0032 0.6731 +vt 0.0083 0.6714 +vt 0.0064 0.6713 +vt 0.0048 0.6714 +vt 0.0035 0.6716 +vt 0.0083 0.6698 +vt 0.0064 0.6699 +vt 0.0038 0.6704 +vt 0.0049 0.6701 +vt 0.0000 0.6716 +vt 0.3398 0.0083 +vt 0.3417 0.0065 +vt 0.3381 0.0083 +vt 0.3397 0.0064 +vt 0.3365 0.0083 +vt 0.3380 0.0064 +vt 0.3333 0.0064 +vt 0.3365 0.0065 +vt 0.3417 0.0047 +vt 0.3397 0.0047 +vt 0.3381 0.0048 +vt 0.3368 0.0049 +vt 0.3417 0.0031 +vt 0.3398 0.0032 +vt 0.3371 0.0038 +vt 0.3382 0.0035 +vt 0.3333 0.0049 +vt 0.0065 0.0083 +vt 0.0083 0.0065 +vt 0.0083 0.0083 +vt 0.0047 0.0083 +vt 0.0064 0.0064 +vt 0.0031 0.0083 +vt 0.0047 0.0064 +vt 0.0000 0.0064 +vt 0.0032 0.0065 +vt 0.0083 0.0047 +vt 0.0064 0.0047 +vt 0.0048 0.0048 +vt 0.0035 0.0049 +vt 0.0083 0.0031 +vt 0.0064 0.0032 +vt 0.0038 0.0038 +vt 0.0049 0.0035 +vt 0.0000 0.0049 +vt 0.0000 0.6704 +vt 0.0038 0.6667 +vt 0.6750 0.3269 +vt 0.6731 0.3250 +vt 0.6750 0.3250 +vt 0.6750 0.3286 +vt 0.6731 0.3269 +vt 0.6750 0.3302 +vt 0.6731 0.3287 +vt 0.6731 0.3333 +vt 0.6731 0.3301 +vt 0.6714 0.3250 +vt 0.6713 0.3269 +vt 0.6714 0.3286 +vt 0.6716 0.3299 +vt 0.6698 0.3250 +vt 0.6699 0.3269 +vt 0.6704 0.3296 +vt 0.6701 0.3284 +vt 0.6716 0.3333 +vt 0.3250 0.3398 +vt 0.3269 0.3417 +vt 0.3250 0.3381 +vt 0.3269 0.3397 +vt 0.3250 0.3365 +vt 0.3269 0.3380 +vt 0.3269 0.3301 +vt 0.3269 0.3365 +vt 0.3286 0.3417 +vt 0.3287 0.3397 +vt 0.3286 0.3381 +vt 0.3284 0.3368 +vt 0.3302 0.3417 +vt 0.3301 0.3398 +vt 0.3296 0.3371 +vt 0.3299 0.3382 +vt 0.3284 0.3299 +vt 0.3269 0.3250 +vt 0.3250 0.3269 +vt 0.3286 0.3250 +vt 0.3269 0.3269 +vt 0.3302 0.3250 +vt 0.3287 0.3269 +vt 0.3333 0.3269 +vt 0.3301 0.3269 +vt 0.3250 0.3286 +vt 0.3269 0.3287 +vt 0.3286 0.3286 +vt 0.3299 0.3284 +vt 0.3250 0.3302 +vt 0.3296 0.3296 +vt 0.3333 0.3284 +vt 0.6704 0.3333 +vt 0.6667 0.3296 +vt 0.3250 0.6731 +vt 0.3269 0.6750 +vt 0.3250 0.6714 +vt 0.3269 0.6731 +vt 0.3250 0.6698 +vt 0.3269 0.6713 +vt 0.3269 0.6667 +vt 0.3269 0.6699 +vt 0.3286 0.6750 +vt 0.3287 0.6731 +vt 0.3286 0.6714 +vt 0.3284 0.6701 +vt 0.3302 0.6750 +vt 0.3301 0.6731 +vt 0.3296 0.6704 +vt 0.3299 0.6716 +vt 0.3284 0.6667 +vt 0.0083 0.3269 +vt 0.0065 0.3250 +vt 0.0083 0.3286 +vt 0.0064 0.3269 +vt 0.0083 0.3302 +vt 0.0064 0.3287 +vt 0.0064 0.3365 +vt 0.0065 0.3301 +vt 0.0047 0.3250 +vt 0.0047 0.3269 +vt 0.0048 0.3286 +vt 0.0049 0.3299 +vt 0.0031 0.3250 +vt 0.0032 0.3269 +vt 0.0038 0.3296 +vt 0.0035 0.3284 +vt 0.0049 0.3368 +vt 0.0065 0.3417 +vt 0.0083 0.3398 +vt 0.0083 0.3417 +vt 0.0047 0.3417 +vt 0.0064 0.3398 +vt 0.0031 0.3417 +vt 0.0047 0.3398 +vt 0.0000 0.3398 +vt 0.0032 0.3398 +vt 0.0083 0.3381 +vt 0.0064 0.3380 +vt 0.0048 0.3381 +vt 0.0035 0.3382 +vt 0.0083 0.3365 +vt 0.0038 0.3371 +vt 0.0000 0.3382 +vt 0.3296 0.6667 +vt 0.3333 0.6704 +vt 1.0000 0.3250 +vt 0.6667 0.3250 +vt 0.0083 1.0000 +vt 0.0083 0.6667 +vt 0.6750 0.3333 +vt 0.3333 0.9917 +vt 0.6667 0.0083 +vt 0.3250 0.6667 +vt 0.3333 0.0083 +vt 0.9917 0.0000 +vt 0.0000 0.6750 +vt 0.6667 0.3296 +vt 0.6667 0.6583 +vt 0.6667 0.6629 +vt 0.6583 0.0000 +vt 0.0038 1.0000 +vt 0.3417 0.6667 +vt 0.3371 0.6667 +vt 0.6583 0.3333 +vt 0.6629 0.3333 +vt 0.3333 0.6583 +vt 0.3333 0.6629 +vt 0.0038 0.6667 +vt 0.3333 0.3417 +vt 0.3333 0.3371 +vt 0.3250 0.0000 +vt 0.3296 0.0000 +vt 0.3417 0.3333 +vt 0.3371 0.3333 +vt 0.3333 0.0038 +vt 0.0000 0.0083 +vt 0.0000 0.0038 +vt 0.3333 0.3250 +vt 0.3333 0.3296 +vt 0.0000 0.3417 +vt 0.0000 0.3371 +vt 1.0000 0.0083 +vt 0.6667 0.0083 +vt 0.3250 1.0000 +vt 0.3250 0.6667 +vt 0.9917 0.3333 +vt 0.3333 0.6750 +vt 0.6667 0.3250 +vt 0.0083 0.6667 +vt 0.3333 0.3250 +vt 0.6750 0.0000 +vt 0.0000 0.9917 +vn 0.0558 0.0558 0.9969 +vn -0.0558 -0.0558 0.9969 +vn 0.0558 -0.0558 0.9969 +vn 0.9969 0.0558 -0.0558 +vn 0.9969 -0.0558 0.0558 +vn 0.9969 -0.0558 -0.0558 +vn -0.0558 0.9969 -0.0558 +vn 0.0558 0.9969 0.0558 +vn 0.0558 0.9969 -0.0558 +vn -0.9969 0.0558 0.0558 +vn -0.9969 -0.0558 -0.0558 +vn -0.9969 -0.0558 0.0558 +vn -0.0558 0.0558 -0.9969 +vn 0.0558 -0.0558 -0.9969 +vn -0.0558 -0.0558 -0.9969 +vn -0.0565 -0.9736 0.2212 +vn -0.2215 -0.9735 0.0570 +vn -0.0558 -0.9969 0.0558 +vn -0.0555 -0.9000 0.4323 +vn -0.2279 -0.9463 0.2292 +vn -0.0550 -0.7805 0.6226 +vn -0.2234 -0.8708 0.4379 +vn -0.2163 -0.6156 0.7577 +vn -0.2159 -0.7584 0.6149 +vn -0.4322 -0.9000 0.0562 +vn -0.4376 -0.8706 0.2247 +vn -0.4207 -0.8035 0.4212 +vn -0.3987 -0.7085 0.5823 +vn -0.6225 -0.7806 0.0552 +vn -0.6156 -0.7577 0.2163 +vn -0.5395 -0.6463 0.5396 +vn -0.5822 -0.7085 0.3989 +vn -0.3989 -0.5822 0.7085 +vn -0.2212 -0.0565 0.9736 +vn -0.0570 -0.2215 0.9735 +vn -0.4323 -0.0555 0.9000 +vn -0.2292 -0.2279 0.9463 +vn -0.6226 -0.0550 0.7805 +vn -0.4379 -0.2234 0.8708 +vn -0.7577 -0.2163 0.6156 +vn -0.6149 -0.2159 0.7584 +vn -0.0562 -0.4322 0.9000 +vn -0.2247 -0.4376 0.8706 +vn -0.4212 -0.4207 0.8035 +vn -0.5823 -0.3987 0.7085 +vn -0.0552 -0.6225 0.7806 +vn -0.5396 -0.5395 0.6464 +vn -0.7085 -0.3989 0.5822 +vn -0.9736 -0.2212 0.0565 +vn -0.9735 -0.0570 0.2215 +vn -0.9000 -0.4323 0.0555 +vn -0.9463 -0.2292 0.2279 +vn -0.7805 -0.6226 0.0550 +vn -0.8708 -0.4379 0.2234 +vn -0.7584 -0.6149 0.2159 +vn -0.9000 -0.0562 0.4322 +vn -0.8706 -0.2247 0.4376 +vn -0.8035 -0.4212 0.4207 +vn -0.7085 -0.5823 0.3987 +vn -0.7806 -0.0552 0.6225 +vn -0.6464 -0.5396 0.5395 +vn -0.0565 0.2212 0.9736 +vn -0.2215 0.0570 0.9735 +vn -0.0558 0.0558 0.9969 +vn -0.0555 0.4323 0.9000 +vn -0.2279 0.2292 0.9463 +vn -0.0550 0.6226 0.7805 +vn -0.2234 0.4379 0.8708 +vn -0.2163 0.7577 0.6156 +vn -0.2159 0.6149 0.7584 +vn -0.4322 0.0562 0.9000 +vn -0.4376 0.2247 0.8706 +vn -0.4207 0.4212 0.8035 +vn -0.3987 0.5823 0.7085 +vn -0.6225 0.0552 0.7806 +vn -0.6156 0.2163 0.7577 +vn -0.5395 0.5396 0.6463 +vn -0.5822 0.3989 0.7085 +vn -0.3989 0.7085 0.5822 +vn -0.2212 0.9736 0.0565 +vn -0.0570 0.9735 0.2215 +vn -0.0558 0.9969 0.0558 +vn -0.4323 0.9000 0.0555 +vn -0.2292 0.9463 0.2279 +vn -0.6226 0.7805 0.0550 +vn -0.4379 0.8708 0.2234 +vn -0.7577 0.6156 0.2163 +vn -0.6149 0.7584 0.2159 +vn -0.0562 0.9000 0.4322 +vn -0.2247 0.8706 0.4376 +vn -0.4212 0.8035 0.4207 +vn -0.5823 0.7085 0.3987 +vn -0.0552 0.7806 0.6225 +vn -0.5396 0.6463 0.5395 +vn -0.7085 0.5822 0.3989 +vn -0.9736 0.0565 0.2212 +vn -0.9735 0.2215 0.0570 +vn -0.9000 0.0555 0.4323 +vn -0.9463 0.2279 0.2292 +vn -0.7805 0.0550 0.6226 +vn -0.8708 0.2234 0.4379 +vn -0.7584 0.2159 0.6149 +vn -0.9000 0.4322 0.0562 +vn -0.8706 0.4376 0.2247 +vn -0.8035 0.4207 0.4212 +vn -0.7085 0.3987 0.5823 +vn -0.7806 0.6225 0.0552 +vn -0.6463 0.5395 0.5396 +vn -0.2212 -0.9736 -0.0565 +vn -0.0570 -0.9735 -0.2215 +vn -0.0558 -0.9969 -0.0558 +vn -0.4323 -0.9000 -0.0555 +vn -0.2292 -0.9463 -0.2279 +vn -0.6226 -0.7805 -0.0550 +vn -0.4379 -0.8708 -0.2234 +vn -0.7577 -0.6156 -0.2163 +vn -0.6149 -0.7584 -0.2159 +vn -0.0562 -0.9000 -0.4322 +vn -0.2247 -0.8706 -0.4376 +vn -0.4212 -0.8035 -0.4207 +vn -0.5823 -0.7085 -0.3987 +vn -0.0552 -0.7806 -0.6225 +vn -0.2163 -0.7577 -0.6156 +vn -0.5396 -0.6463 -0.5395 +vn -0.3989 -0.7085 -0.5822 +vn -0.7085 -0.5822 -0.3989 +vn -0.9736 -0.0565 -0.2212 +vn -0.9735 -0.2215 -0.0570 +vn -0.9000 -0.0555 -0.4323 +vn -0.9463 -0.2279 -0.2292 +vn -0.7805 -0.0550 -0.6226 +vn -0.8708 -0.2234 -0.4379 +vn -0.6156 -0.2163 -0.7577 +vn -0.7584 -0.2159 -0.6149 +vn -0.9000 -0.4322 -0.0562 +vn -0.8706 -0.4376 -0.2247 +vn -0.8035 -0.4207 -0.4212 +vn -0.7085 -0.3987 -0.5823 +vn -0.7806 -0.6225 -0.0552 +vn -0.6463 -0.5395 -0.5396 +vn -0.5822 -0.3989 -0.7085 +vn -0.0565 -0.2212 -0.9736 +vn -0.2215 -0.0570 -0.9735 +vn -0.0555 -0.4323 -0.9000 +vn -0.2279 -0.2292 -0.9463 +vn -0.0550 -0.6226 -0.7805 +vn -0.2234 -0.4379 -0.8708 +vn -0.2159 -0.6149 -0.7584 +vn -0.4322 -0.0562 -0.9000 +vn -0.4376 -0.2247 -0.8706 +vn -0.4207 -0.4212 -0.8035 +vn -0.3987 -0.5823 -0.7085 +vn -0.6225 -0.0552 -0.7806 +vn -0.5395 -0.5396 -0.6463 +vn -0.0565 0.9736 -0.2212 +vn -0.2215 0.9735 -0.0570 +vn -0.0555 0.9000 -0.4323 +vn -0.2279 0.9463 -0.2292 +vn -0.0550 0.7805 -0.6226 +vn -0.2234 0.8708 -0.4379 +vn -0.2163 0.6156 -0.7577 +vn -0.2159 0.7584 -0.6149 +vn -0.4322 0.9000 -0.0562 +vn -0.4376 0.8706 -0.2247 +vn -0.4207 0.8035 -0.4212 +vn -0.3987 0.7085 -0.5823 +vn -0.6225 0.7806 -0.0552 +vn -0.6156 0.7577 -0.2163 +vn -0.5395 0.6463 -0.5396 +vn -0.5822 0.7085 -0.3989 +vn -0.3989 0.5822 -0.7085 +vn -0.2212 0.0565 -0.9736 +vn -0.0570 0.2215 -0.9735 +vn -0.4323 0.0555 -0.9000 +vn -0.2292 0.2279 -0.9463 +vn -0.6226 0.0550 -0.7805 +vn -0.4379 0.2234 -0.8708 +vn -0.7577 0.2163 -0.6156 +vn -0.6149 0.2159 -0.7584 +vn -0.0562 0.4322 -0.9000 +vn -0.2247 0.4376 -0.8706 +vn -0.4212 0.4207 -0.8035 +vn -0.5823 0.3987 -0.7085 +vn -0.0552 0.6225 -0.7806 +vn -0.5396 0.5395 -0.6464 +vn -0.7085 0.3989 -0.5822 +vn -0.9736 0.2212 -0.0565 +vn -0.9735 0.0570 -0.2215 +vn -0.9969 0.0558 -0.0558 +vn -0.9000 0.4323 -0.0555 +vn -0.9463 0.2292 -0.2279 +vn -0.7805 0.6226 -0.0550 +vn -0.8708 0.4379 -0.2234 +vn -0.7584 0.6149 -0.2159 +vn -0.9000 0.0562 -0.4322 +vn -0.8706 0.2247 -0.4376 +vn -0.8035 0.4212 -0.4207 +vn -0.7085 0.5823 -0.3987 +vn -0.7806 0.0552 -0.6225 +vn -0.6464 0.5396 -0.5395 +vn 0.2212 -0.9736 0.0565 +vn 0.0570 -0.9735 0.2215 +vn 0.0558 -0.9969 0.0558 +vn 0.4323 -0.9000 0.0555 +vn 0.2292 -0.9463 0.2279 +vn 0.6226 -0.7805 0.0550 +vn 0.4379 -0.8708 0.2234 +vn 0.7577 -0.6156 0.2163 +vn 0.6149 -0.7584 0.2159 +vn 0.0562 -0.9000 0.4322 +vn 0.2247 -0.8706 0.4376 +vn 0.4212 -0.8035 0.4207 +vn 0.5823 -0.7085 0.3987 +vn 0.0552 -0.7806 0.6225 +vn 0.2163 -0.7577 0.6156 +vn 0.5396 -0.6463 0.5395 +vn 0.3989 -0.7085 0.5822 +vn 0.7085 -0.5822 0.3989 +vn 0.9736 -0.0565 0.2212 +vn 0.9735 -0.2215 0.0570 +vn 0.9000 -0.0555 0.4323 +vn 0.9463 -0.2279 0.2292 +vn 0.7805 -0.0550 0.6226 +vn 0.8708 -0.2234 0.4379 +vn 0.6156 -0.2163 0.7577 +vn 0.7584 -0.2159 0.6149 +vn 0.9000 -0.4322 0.0562 +vn 0.8706 -0.4376 0.2247 +vn 0.8035 -0.4207 0.4212 +vn 0.7085 -0.3987 0.5823 +vn 0.7806 -0.6225 0.0552 +vn 0.6463 -0.5395 0.5396 +vn 0.5822 -0.3989 0.7085 +vn 0.0565 -0.2212 0.9736 +vn 0.2215 -0.0570 0.9735 +vn 0.0555 -0.4323 0.9000 +vn 0.2279 -0.2292 0.9463 +vn 0.0550 -0.6226 0.7805 +vn 0.2234 -0.4379 0.8708 +vn 0.2159 -0.6149 0.7584 +vn 0.4322 -0.0562 0.9000 +vn 0.4376 -0.2247 0.8706 +vn 0.4207 -0.4212 0.8035 +vn 0.3987 -0.5823 0.7085 +vn 0.6225 -0.0552 0.7806 +vn 0.5395 -0.5396 0.6463 +vn 0.0565 0.9736 0.2212 +vn 0.2215 0.9735 0.0570 +vn 0.0555 0.9000 0.4323 +vn 0.2279 0.9463 0.2292 +vn 0.0550 0.7805 0.6226 +vn 0.2234 0.8708 0.4379 +vn 0.2163 0.6156 0.7577 +vn 0.2159 0.7584 0.6149 +vn 0.4322 0.9000 0.0562 +vn 0.4376 0.8706 0.2247 +vn 0.4207 0.8035 0.4212 +vn 0.3987 0.7085 0.5823 +vn 0.6225 0.7806 0.0552 +vn 0.6156 0.7577 0.2163 +vn 0.5395 0.6463 0.5396 +vn 0.5822 0.7085 0.3989 +vn 0.3989 0.5822 0.7085 +vn 0.2212 0.0565 0.9736 +vn 0.0570 0.2215 0.9735 +vn 0.4323 0.0555 0.9000 +vn 0.2292 0.2279 0.9463 +vn 0.6226 0.0550 0.7805 +vn 0.4379 0.2234 0.8708 +vn 0.7577 0.2163 0.6156 +vn 0.6149 0.2159 0.7584 +vn 0.0562 0.4322 0.9000 +vn 0.2247 0.4376 0.8706 +vn 0.4212 0.4207 0.8035 +vn 0.5823 0.3987 0.7085 +vn 0.0552 0.6225 0.7806 +vn 0.5396 0.5395 0.6464 +vn 0.7085 0.3989 0.5822 +vn 0.9736 0.2212 0.0565 +vn 0.9735 0.0570 0.2215 +vn 0.9969 0.0558 0.0558 +vn 0.9000 0.4323 0.0555 +vn 0.9463 0.2292 0.2279 +vn 0.7805 0.6226 0.0550 +vn 0.8708 0.4379 0.2234 +vn 0.7584 0.6149 0.2159 +vn 0.9000 0.0562 0.4322 +vn 0.8706 0.2247 0.4376 +vn 0.8035 0.4212 0.4207 +vn 0.7085 0.5823 0.3987 +vn 0.7806 0.0552 0.6225 +vn 0.6464 0.5396 0.5395 +vn 0.0565 -0.9736 -0.2212 +vn 0.2215 -0.9735 -0.0570 +vn 0.0558 -0.9969 -0.0558 +vn 0.0555 -0.9000 -0.4323 +vn 0.2279 -0.9463 -0.2292 +vn 0.0550 -0.7805 -0.6226 +vn 0.2234 -0.8708 -0.4379 +vn 0.2163 -0.6156 -0.7577 +vn 0.2159 -0.7584 -0.6149 +vn 0.4322 -0.9000 -0.0562 +vn 0.4376 -0.8706 -0.2247 +vn 0.4207 -0.8035 -0.4212 +vn 0.3987 -0.7085 -0.5823 +vn 0.6225 -0.7806 -0.0552 +vn 0.6156 -0.7577 -0.2163 +vn 0.5395 -0.6463 -0.5396 +vn 0.5822 -0.7085 -0.3989 +vn 0.3989 -0.5822 -0.7085 +vn 0.2212 -0.0565 -0.9736 +vn 0.0570 -0.2215 -0.9735 +vn 0.4323 -0.0555 -0.9000 +vn 0.2292 -0.2279 -0.9463 +vn 0.6226 -0.0550 -0.7805 +vn 0.4379 -0.2234 -0.8708 +vn 0.7577 -0.2163 -0.6156 +vn 0.6149 -0.2159 -0.7584 +vn 0.0562 -0.4322 -0.9000 +vn 0.2247 -0.4376 -0.8706 +vn 0.4212 -0.4207 -0.8035 +vn 0.5823 -0.3987 -0.7085 +vn 0.0552 -0.6225 -0.7806 +vn 0.5396 -0.5395 -0.6464 +vn 0.7085 -0.3989 -0.5822 +vn 0.9736 -0.2212 -0.0565 +vn 0.9735 -0.0570 -0.2215 +vn 0.9000 -0.4323 -0.0555 +vn 0.9463 -0.2292 -0.2279 +vn 0.7805 -0.6226 -0.0550 +vn 0.8708 -0.4379 -0.2234 +vn 0.7584 -0.6149 -0.2159 +vn 0.9000 -0.0562 -0.4322 +vn 0.8706 -0.2247 -0.4376 +vn 0.8035 -0.4212 -0.4207 +vn 0.7085 -0.5823 -0.3987 +vn 0.7806 -0.0552 -0.6225 +vn 0.6464 -0.5396 -0.5395 +vn 0.2212 0.9736 -0.0565 +vn 0.0570 0.9735 -0.2215 +vn 0.4323 0.9000 -0.0555 +vn 0.2292 0.9463 -0.2279 +vn 0.6226 0.7805 -0.0550 +vn 0.4379 0.8708 -0.2234 +vn 0.7577 0.6156 -0.2163 +vn 0.6149 0.7584 -0.2159 +vn 0.0562 0.9000 -0.4322 +vn 0.2247 0.8706 -0.4376 +vn 0.4212 0.8035 -0.4207 +vn 0.5823 0.7085 -0.3987 +vn 0.0552 0.7806 -0.6225 +vn 0.2163 0.7577 -0.6156 +vn 0.5396 0.6463 -0.5395 +vn 0.3989 0.7085 -0.5822 +vn 0.7085 0.5822 -0.3989 +vn 0.9736 0.0565 -0.2212 +vn 0.9735 0.2215 -0.0570 +vn 0.9000 0.0555 -0.4323 +vn 0.9463 0.2279 -0.2292 +vn 0.7805 0.0550 -0.6226 +vn 0.8708 0.2234 -0.4379 +vn 0.6156 0.2163 -0.7577 +vn 0.7584 0.2159 -0.6149 +vn 0.9000 0.4322 -0.0562 +vn 0.8706 0.4376 -0.2247 +vn 0.8035 0.4207 -0.4212 +vn 0.7085 0.3987 -0.5823 +vn 0.7806 0.6225 -0.0552 +vn 0.6463 0.5395 -0.5396 +vn 0.5822 0.3989 -0.7085 +vn 0.0565 0.2212 -0.9736 +vn 0.2215 0.0570 -0.9735 +vn 0.0558 0.0558 -0.9969 +vn 0.0555 0.4323 -0.9000 +vn 0.2279 0.2292 -0.9463 +vn 0.0550 0.6226 -0.7805 +vn 0.2234 0.4379 -0.8708 +vn 0.2159 0.6149 -0.7584 +vn 0.4322 0.0562 -0.9000 +vn 0.4376 0.2247 -0.8706 +vn 0.4207 0.4212 -0.8035 +vn 0.3987 0.5823 -0.7085 +vn 0.6225 0.0552 -0.7806 +vn 0.5395 0.5396 -0.6463 +s 1 +f 242/1/1 2/2/2 195/3/3 +f 338/4/4 194/5/5 291/6/6 +f 145/7/7 241/8/8 337/9/9 +f 51/10/10 98/11/11 3/12/12 +f 146/13/13 290/14/14 99/15/15 +f 4/16/16 7/17/17 1/18/18 +f 5/19/19 8/20/20 4/16/16 +f 6/21/21 9/22/22 5/19/19 +f 6/21/21 31/23/23 10/24/24 +f 8/20/20 11/25/25 7/17/17 +f 9/22/22 12/26/26 8/20/20 +f 10/24/24 13/27/27 9/22/22 +f 31/23/23 14/28/28 10/24/24 +f 12/26/26 15/29/29 11/25/25 +f 13/27/27 16/30/30 12/26/26 +f 13/27/27 18/31/31 17/32/32 +f 32/33/33 18/31/31 14/28/28 +f 19/34/34 22/35/35 2/2/2 +f 20/36/36 23/37/37 19/34/34 +f 21/38/38 24/39/39 20/36/36 +f 21/38/38 46/40/40 25/41/41 +f 23/37/37 26/42/42 22/35/35 +f 24/39/39 27/43/43 23/37/37 +f 25/41/41 28/44/44 24/39/39 +f 46/40/40 29/45/45 25/41/41 +f 27/43/43 30/46/46 26/42/42 +f 28/44/44 31/47/23 27/43/43 +f 28/44/44 33/48/47 32/49/33 +f 47/50/48 33/48/47 29/45/45 +f 34/51/49 37/52/50 3/12/12 +f 35/53/51 38/54/52 34/51/49 +f 36/55/53 39/56/54 35/53/51 +f 36/55/53 16/57/30 40/58/55 +f 38/54/52 41/59/56 37/52/50 +f 39/56/54 42/60/57 38/54/52 +f 40/58/55 43/61/58 39/56/54 +f 16/57/30 44/62/59 40/58/55 +f 42/60/57 45/63/60 41/59/56 +f 43/61/58 46/64/40 42/60/57 +f 43/61/58 48/65/61 47/66/48 +f 17/67/32 48/65/61 44/62/59 +f 18/31/31 33/68/47 48/69/61 +f 52/70/62 55/71/63 49/72/64 +f 53/73/65 56/74/66 52/70/62 +f 54/75/67 57/76/68 53/73/65 +f 54/75/67 79/77/69 58/78/70 +f 56/74/66 59/79/71 55/71/63 +f 57/76/68 60/80/72 56/74/66 +f 58/78/70 61/81/73 57/76/68 +f 79/77/69 62/82/74 58/78/70 +f 60/80/72 63/83/75 59/79/71 +f 61/81/73 64/84/76 60/80/72 +f 61/81/73 66/85/77 65/86/78 +f 80/87/79 66/85/77 62/82/74 +f 67/88/80 70/89/81 50/90/82 +f 68/91/83 71/92/84 67/88/80 +f 69/93/85 72/94/86 68/91/83 +f 69/93/85 94/95/87 73/96/88 +f 71/92/84 74/97/89 70/89/81 +f 72/94/86 75/98/90 71/92/84 +f 73/96/88 76/99/91 72/94/86 +f 94/95/87 77/100/92 73/96/88 +f 75/98/90 78/101/93 74/97/89 +f 76/99/91 79/102/69 75/98/90 +f 76/99/91 81/103/94 80/104/79 +f 95/105/95 81/103/94 77/100/92 +f 82/106/96 85/107/97 51/10/10 +f 83/108/98 86/109/99 82/106/96 +f 84/110/100 87/111/101 83/108/98 +f 84/110/100 64/112/76 88/113/102 +f 86/109/99 89/114/103 85/107/97 +f 87/111/101 90/115/104 86/109/99 +f 88/113/102 91/116/105 87/111/101 +f 64/112/76 92/117/106 88/113/102 +f 90/115/104 93/118/107 89/114/103 +f 91/116/105 94/119/87 90/115/104 +f 91/116/105 96/120/108 95/121/95 +f 65/122/78 96/120/108 92/117/106 +f 66/85/77 81/123/94 96/124/108 +f 100/125/109 103/126/110 97/127/111 +f 101/128/112 104/129/113 100/125/109 +f 102/130/114 105/131/115 101/128/112 +f 102/130/114 127/132/116 106/133/117 +f 104/129/113 107/134/118 103/126/110 +f 105/131/115 108/135/119 104/129/113 +f 106/133/117 109/136/120 105/131/115 +f 127/132/116 110/137/121 106/133/117 +f 108/135/119 111/138/122 107/134/118 +f 109/136/120 112/139/123 108/135/119 +f 109/136/120 114/140/124 113/141/125 +f 128/142/126 114/140/124 110/137/121 +f 115/143/127 118/144/128 98/11/11 +f 116/145/129 119/146/130 115/143/127 +f 117/147/131 120/148/132 116/145/129 +f 117/147/131 142/149/133 121/150/134 +f 119/146/130 122/151/135 118/144/128 +f 120/148/132 123/152/136 119/146/130 +f 121/150/134 124/153/137 120/148/132 +f 142/149/133 125/154/138 121/150/134 +f 123/152/136 126/155/139 122/151/135 +f 124/153/137 127/156/116 123/152/136 +f 124/153/137 129/157/140 128/158/126 +f 143/159/141 129/157/140 125/154/138 +f 130/160/142 133/161/143 99/15/15 +f 131/162/144 134/163/145 130/160/142 +f 132/164/146 135/165/147 131/162/144 +f 132/164/146 112/166/123 136/167/148 +f 134/163/145 137/168/149 133/161/143 +f 135/165/147 138/169/150 134/163/145 +f 136/167/148 139/170/151 135/165/147 +f 112/166/123 140/171/152 136/167/148 +f 138/169/150 141/172/153 137/168/149 +f 139/170/151 142/173/133 138/169/150 +f 139/170/151 144/174/154 143/175/141 +f 113/176/125 144/174/154 140/171/152 +f 114/140/124 129/177/140 144/178/154 +f 148/179/155 151/180/156 145/7/7 +f 149/181/157 152/182/158 148/179/155 +f 150/183/159 153/184/160 149/181/157 +f 150/183/159 175/185/161 154/186/162 +f 152/182/158 155/187/163 151/180/156 +f 153/184/160 156/188/164 152/182/158 +f 154/186/162 157/189/165 153/184/160 +f 175/185/161 158/190/166 154/186/162 +f 156/188/164 159/191/167 155/187/163 +f 157/189/165 160/192/168 156/188/164 +f 157/189/165 162/193/169 161/194/170 +f 176/195/171 162/193/169 158/190/166 +f 163/196/172 166/197/173 146/13/13 +f 164/198/174 167/199/175 163/196/172 +f 165/200/176 168/201/177 164/198/174 +f 165/200/176 190/202/178 169/203/179 +f 167/199/175 170/204/180 166/197/173 +f 168/201/177 171/205/181 167/199/175 +f 169/203/179 172/206/182 168/201/177 +f 190/202/178 173/207/183 169/203/179 +f 171/205/181 174/208/184 170/204/180 +f 172/206/182 175/209/161 171/205/181 +f 172/206/182 177/210/185 176/211/171 +f 191/212/186 177/210/185 173/207/183 +f 178/213/187 181/214/188 147/215/189 +f 179/216/190 182/217/191 178/213/187 +f 180/218/192 183/219/193 179/216/190 +f 180/218/192 160/220/168 184/221/194 +f 182/217/191 185/222/195 181/214/188 +f 183/219/193 186/223/196 182/217/191 +f 184/221/194 187/224/197 183/219/193 +f 160/220/168 188/225/198 184/221/194 +f 186/223/196 189/226/199 185/222/195 +f 187/224/197 190/227/178 186/223/196 +f 187/224/197 192/228/200 191/229/186 +f 161/230/170 192/228/200 188/225/198 +f 162/193/169 177/231/185 192/232/200 +f 196/233/201 199/234/202 193/235/203 +f 197/236/204 200/237/205 196/233/201 +f 198/238/206 201/239/207 197/236/204 +f 198/238/206 223/240/208 202/241/209 +f 200/237/205 203/242/210 199/234/202 +f 201/239/207 204/243/211 200/237/205 +f 202/241/209 205/244/212 201/239/207 +f 223/240/208 206/245/213 202/241/209 +f 204/243/211 207/246/214 203/242/210 +f 205/244/212 208/247/215 204/243/211 +f 205/244/212 210/248/216 209/249/217 +f 224/250/218 210/248/216 206/245/213 +f 211/251/219 214/252/220 194/5/5 +f 212/253/221 215/254/222 211/251/219 +f 213/255/223 216/256/224 212/253/221 +f 213/255/223 238/257/225 217/258/226 +f 215/254/222 218/259/227 214/252/220 +f 216/256/224 219/260/228 215/254/222 +f 217/258/226 220/261/229 216/256/224 +f 238/257/225 221/262/230 217/258/226 +f 219/260/228 222/263/231 218/259/227 +f 220/261/229 223/264/208 219/260/228 +f 220/261/229 225/265/232 224/266/218 +f 239/267/233 225/265/232 221/262/230 +f 226/268/234 229/269/235 195/3/3 +f 227/270/236 230/271/237 226/268/234 +f 228/272/238 231/273/239 227/270/236 +f 228/272/238 208/274/215 232/275/240 +f 230/271/237 233/276/241 229/269/235 +f 231/273/239 234/277/242 230/271/237 +f 232/275/240 235/278/243 231/273/239 +f 208/274/215 236/279/244 232/275/240 +f 234/277/242 237/280/245 233/276/241 +f 235/278/243 238/281/225 234/277/242 +f 235/278/243 240/282/246 239/283/233 +f 209/284/217 240/282/246 236/279/244 +f 210/248/216 225/285/232 240/286/246 +f 244/287/247 247/288/248 241/8/8 +f 245/289/249 248/290/250 244/287/247 +f 246/291/251 249/292/252 245/289/249 +f 246/291/251 271/293/253 250/294/254 +f 248/290/250 251/295/255 247/288/248 +f 249/292/252 252/296/256 248/290/250 +f 250/294/254 253/297/257 249/292/252 +f 271/293/253 254/298/258 250/294/254 +f 252/296/256 255/299/259 251/295/255 +f 253/297/257 256/300/260 252/296/256 +f 253/297/257 258/301/261 257/302/262 +f 272/303/263 258/301/261 254/298/258 +f 259/304/264 262/305/265 242/1/1 +f 260/306/266 263/307/267 259/304/264 +f 261/308/268 264/309/269 260/306/266 +f 261/308/268 286/310/270 265/311/271 +f 263/307/267 266/312/272 262/305/265 +f 264/309/269 267/313/273 263/307/267 +f 265/311/271 268/314/274 264/309/269 +f 286/310/270 269/315/275 265/311/271 +f 267/313/273 270/316/276 266/312/272 +f 268/314/274 271/317/253 267/313/273 +f 268/314/274 273/318/277 272/319/263 +f 287/320/278 273/318/277 269/315/275 +f 274/321/279 277/322/280 243/323/281 +f 275/324/282 278/325/283 274/321/279 +f 276/326/284 279/327/285 275/324/282 +f 276/326/284 256/328/260 280/329/286 +f 278/325/283 281/330/287 277/322/280 +f 279/327/285 282/331/288 278/325/283 +f 280/329/286 283/332/289 279/327/285 +f 256/328/260 284/333/290 280/329/286 +f 282/331/288 285/334/291 281/330/287 +f 283/332/289 286/335/270 282/331/288 +f 283/332/289 288/336/292 287/337/278 +f 257/338/262 288/336/292 284/333/290 +f 258/301/261 273/339/277 288/340/292 +f 292/341/293 295/342/294 289/343/295 +f 293/344/296 296/345/297 292/341/293 +f 294/346/298 297/347/299 293/344/296 +f 294/346/298 319/348/300 298/349/301 +f 296/345/297 299/350/302 295/342/294 +f 297/347/299 300/351/303 296/345/297 +f 298/349/301 301/352/304 297/347/299 +f 319/348/300 302/353/305 298/349/301 +f 300/351/303 303/354/306 299/350/302 +f 301/352/304 304/355/307 300/351/303 +f 301/352/304 306/356/308 305/357/309 +f 320/358/310 306/356/308 302/353/305 +f 307/359/311 310/360/312 290/14/14 +f 308/361/313 311/362/314 307/359/311 +f 309/363/315 312/364/316 308/361/313 +f 309/363/315 334/365/317 313/366/318 +f 311/362/314 314/367/319 310/360/312 +f 312/364/316 315/368/320 311/362/314 +f 313/366/318 316/369/321 312/364/316 +f 334/365/317 317/370/322 313/366/318 +f 315/368/320 318/371/323 314/367/319 +f 316/369/321 319/372/300 315/368/320 +f 316/369/321 321/373/324 320/374/310 +f 335/375/325 321/373/324 317/370/322 +f 322/376/326 325/377/327 291/6/6 +f 323/378/328 326/379/329 322/376/326 +f 324/380/330 327/381/331 323/378/328 +f 324/380/330 304/382/307 328/383/332 +f 326/379/329 329/384/333 325/377/327 +f 327/381/331 330/385/334 326/379/329 +f 328/383/332 331/386/335 327/381/331 +f 304/382/307 332/387/336 328/383/332 +f 330/385/334 333/388/337 329/384/333 +f 331/386/335 334/365/317 330/385/334 +f 331/386/335 336/389/338 335/375/325 +f 305/390/309 336/389/338 332/387/336 +f 306/356/308 321/391/324 336/392/338 +f 340/393/339 343/394/340 337/9/9 +f 341/395/341 344/396/342 340/393/339 +f 342/397/343 345/398/344 341/395/341 +f 342/397/343 367/399/345 346/400/346 +f 344/396/342 347/401/347 343/394/340 +f 345/398/344 348/402/348 344/396/342 +f 346/400/346 349/403/349 345/398/344 +f 367/399/345 350/404/350 346/400/346 +f 348/402/348 351/405/351 347/401/347 +f 349/403/349 352/406/352 348/402/348 +f 349/403/349 354/407/353 353/408/354 +f 368/409/355 354/407/353 350/404/350 +f 355/410/356 358/411/357 338/4/4 +f 356/412/358 359/413/359 355/410/356 +f 357/414/360 360/415/361 356/412/358 +f 357/414/360 382/416/362 361/417/363 +f 359/413/359 362/418/364 358/411/357 +f 360/415/361 363/419/365 359/413/359 +f 361/417/363 364/420/366 360/415/361 +f 382/416/362 365/421/367 361/417/363 +f 363/419/365 366/422/368 362/418/364 +f 364/420/366 367/423/345 363/419/365 +f 364/420/366 369/424/369 368/425/355 +f 383/426/370 369/424/369 365/421/367 +f 370/427/371 373/428/372 339/429/373 +f 371/430/374 374/431/375 370/427/371 +f 372/432/376 375/433/377 371/430/374 +f 372/432/376 352/434/352 376/435/378 +f 374/431/375 377/436/379 373/428/372 +f 375/433/377 378/437/380 374/431/375 +f 376/435/378 379/438/381 375/433/377 +f 352/434/352 380/439/382 376/435/378 +f 378/437/380 381/440/383 377/436/379 +f 379/438/381 382/416/362 378/437/380 +f 379/438/381 384/441/384 383/426/370 +f 353/442/354 384/441/384 380/439/382 +f 354/407/353 369/443/369 384/444/384 +f 1/18/18 100/125/109 97/127/111 +f 7/17/17 101/128/112 100/125/109 +f 101/128/112 15/29/29 102/130/114 +f 15/29/29 126/445/139 102/130/114 +f 36/55/53 122/151/135 126/155/139 +f 35/53/51 118/144/128 122/151/135 +f 34/51/49 98/11/11 118/144/128 +f 49/72/64 19/34/34 2/2/2 +f 55/71/63 20/36/36 19/34/34 +f 20/36/36 63/83/75 21/38/38 +f 63/83/75 45/446/60 21/38/38 +f 84/110/100 41/59/56 45/63/60 +f 83/108/98 37/52/50 41/59/56 +f 82/106/96 3/12/12 37/52/50 +f 145/7/7 67/88/80 50/90/82 +f 151/180/156 68/91/83 67/88/80 +f 68/91/83 159/191/167 69/93/85 +f 159/191/167 93/447/107 69/93/85 +f 180/218/192 89/114/103 93/118/107 +f 179/216/190 85/107/97 89/114/103 +f 178/213/187 51/10/10 85/107/97 +f 99/15/15 163/196/172 146/13/13 +f 133/161/143 164/198/174 163/196/172 +f 164/198/174 141/172/153 165/200/176 +f 141/172/153 189/448/199 165/200/176 +f 117/147/131 185/222/195 189/226/199 +f 116/145/129 181/214/188 185/222/195 +f 115/143/127 147/215/189 181/214/188 +f 97/127/111 292/341/293 289/343/295 +f 103/126/110 293/344/296 292/341/293 +f 293/344/296 111/138/122 294/346/298 +f 111/138/122 318/449/323 294/346/298 +f 132/164/146 314/367/319 318/371/323 +f 131/162/144 310/360/312 314/367/319 +f 310/360/312 99/15/15 290/14/14 +f 337/9/9 148/179/155 145/7/7 +f 343/394/340 149/181/157 148/179/155 +f 149/181/157 351/405/351 150/183/159 +f 351/405/351 174/450/184 150/183/159 +f 372/432/376 170/204/180 174/208/184 +f 371/430/374 166/197/173 170/204/180 +f 166/197/173 339/429/373 146/13/13 +f 291/6/6 355/410/356 338/4/4 +f 325/377/327 356/412/358 355/410/356 +f 356/412/358 333/388/337 357/414/360 +f 333/388/337 381/440/383 357/414/360 +f 309/363/315 377/436/379 381/440/383 +f 308/361/313 373/428/372 377/436/379 +f 373/428/372 290/14/14 339/429/373 +f 289/343/295 196/233/201 193/235/203 +f 295/342/294 197/236/204 196/233/201 +f 197/236/204 303/354/306 198/238/206 +f 303/354/306 222/451/231 198/238/206 +f 324/380/330 218/259/227 222/263/231 +f 323/378/328 214/252/220 218/259/227 +f 322/376/326 194/5/5 214/252/220 +f 241/8/8 340/393/339 337/9/9 +f 247/288/248 341/395/341 340/393/339 +f 341/395/341 255/299/259 342/397/343 +f 255/299/259 366/452/368 342/397/343 +f 276/326/284 362/418/364 366/422/368 +f 275/324/282 358/411/357 362/418/364 +f 274/321/279 338/4/4 358/411/357 +f 195/3/3 259/304/264 242/1/1 +f 229/269/235 260/306/266 259/304/264 +f 260/306/266 237/280/245 261/308/268 +f 237/280/245 285/453/291 261/308/268 +f 213/255/223 281/330/287 285/334/291 +f 212/253/221 277/322/280 281/330/287 +f 211/251/219 243/323/281 277/322/280 +f 193/235/203 4/16/16 1/18/18 +f 199/234/202 5/19/19 4/16/16 +f 5/19/19 207/246/214 6/21/21 +f 207/246/214 30/454/46 6/21/21 +f 228/272/238 26/42/42 30/46/46 +f 227/270/236 22/35/35 26/42/42 +f 22/35/35 195/3/3 2/2/2 +f 50/90/82 244/287/247 241/8/8 +f 70/89/81 245/289/249 244/287/247 +f 245/289/249 78/101/93 246/291/251 +f 78/101/93 270/455/276 246/291/251 +f 54/75/67 266/312/272 270/316/276 +f 53/73/65 262/305/265 266/312/272 +f 262/305/265 49/72/64 242/1/1 +f 289/343/295 1/18/18 97/127/111 +f 242/1/1 49/72/64 2/2/2 +f 338/4/4 243/323/281 194/5/5 +f 145/7/7 50/90/82 241/8/8 +f 51/10/10 147/215/189 98/11/11 +f 146/13/13 339/429/373 290/14/14 +f 4/16/16 8/20/20 7/17/17 +f 5/19/19 9/22/22 8/20/20 +f 6/21/21 10/24/24 9/22/22 +f 6/21/21 30/454/46 31/23/23 +f 8/20/20 12/26/26 11/25/25 +f 9/22/22 13/27/27 12/26/26 +f 10/24/24 14/28/28 13/27/27 +f 31/23/23 32/33/33 14/28/28 +f 12/26/26 16/30/30 15/29/29 +f 13/27/27 17/32/32 16/30/30 +f 13/27/27 14/28/28 18/31/31 +f 32/33/33 33/68/47 18/31/31 +f 19/34/34 23/37/37 22/35/35 +f 20/36/36 24/39/39 23/37/37 +f 21/38/38 25/41/41 24/39/39 +f 21/38/38 45/446/60 46/40/40 +f 23/37/37 27/43/43 26/42/42 +f 24/39/39 28/44/44 27/43/43 +f 25/41/41 29/45/45 28/44/44 +f 46/40/40 47/50/48 29/45/45 +f 27/43/43 31/47/23 30/46/46 +f 28/44/44 32/49/33 31/47/23 +f 28/44/44 29/45/45 33/48/47 +f 47/50/48 48/456/61 33/48/47 +f 34/51/49 38/54/52 37/52/50 +f 35/53/51 39/56/54 38/54/52 +f 36/55/53 40/58/55 39/56/54 +f 36/55/53 15/457/29 16/57/30 +f 38/54/52 42/60/57 41/59/56 +f 39/56/54 43/61/58 42/60/57 +f 40/58/55 44/62/59 43/61/58 +f 16/57/30 17/67/32 44/62/59 +f 42/60/57 46/64/40 45/63/60 +f 43/61/58 47/66/48 46/64/40 +f 43/61/58 44/62/59 48/65/61 +f 17/67/32 18/458/31 48/65/61 +f 52/70/62 56/74/66 55/71/63 +f 53/73/65 57/76/68 56/74/66 +f 54/75/67 58/78/70 57/76/68 +f 54/75/67 78/459/93 79/77/69 +f 56/74/66 60/80/72 59/79/71 +f 57/76/68 61/81/73 60/80/72 +f 58/78/70 62/82/74 61/81/73 +f 79/77/69 80/87/79 62/82/74 +f 60/80/72 64/84/76 63/83/75 +f 61/81/73 65/86/78 64/84/76 +f 61/81/73 62/82/74 66/85/77 +f 80/87/79 81/123/94 66/85/77 +f 67/88/80 71/92/84 70/89/81 +f 68/91/83 72/94/86 71/92/84 +f 69/93/85 73/96/88 72/94/86 +f 69/93/85 93/447/107 94/95/87 +f 71/92/84 75/98/90 74/97/89 +f 72/94/86 76/99/91 75/98/90 +f 73/96/88 77/100/92 76/99/91 +f 94/95/87 95/105/95 77/100/92 +f 75/98/90 79/102/69 78/101/93 +f 76/99/91 80/104/79 79/102/69 +f 76/99/91 77/100/92 81/103/94 +f 95/105/95 96/460/108 81/103/94 +f 82/106/96 86/109/99 85/107/97 +f 83/108/98 87/111/101 86/109/99 +f 84/110/100 88/113/102 87/111/101 +f 84/110/100 63/461/75 64/112/76 +f 86/109/99 90/115/104 89/114/103 +f 87/111/101 91/116/105 90/115/104 +f 88/113/102 92/117/106 91/116/105 +f 64/112/76 65/122/78 92/117/106 +f 90/115/104 94/119/87 93/118/107 +f 91/116/105 95/121/95 94/119/87 +f 91/116/105 92/117/106 96/120/108 +f 65/122/78 66/462/77 96/120/108 +f 100/125/109 104/129/113 103/126/110 +f 101/128/112 105/131/115 104/129/113 +f 102/130/114 106/133/117 105/131/115 +f 102/130/114 126/445/139 127/132/116 +f 104/129/113 108/135/119 107/134/118 +f 105/131/115 109/136/120 108/135/119 +f 106/133/117 110/137/121 109/136/120 +f 127/132/116 128/142/126 110/137/121 +f 108/135/119 112/139/123 111/138/122 +f 109/136/120 113/141/125 112/139/123 +f 109/136/120 110/137/121 114/140/124 +f 128/142/126 129/177/140 114/140/124 +f 115/143/127 119/146/130 118/144/128 +f 116/145/129 120/148/132 119/146/130 +f 117/147/131 121/150/134 120/148/132 +f 117/147/131 141/463/153 142/149/133 +f 119/146/130 123/152/136 122/151/135 +f 120/148/132 124/153/137 123/152/136 +f 121/150/134 125/154/138 124/153/137 +f 142/149/133 143/159/141 125/154/138 +f 123/152/136 127/156/116 126/155/139 +f 124/153/137 128/158/126 127/156/116 +f 124/153/137 125/154/138 129/157/140 +f 143/159/141 144/464/154 129/157/140 +f 130/160/142 134/163/145 133/161/143 +f 131/162/144 135/165/147 134/163/145 +f 132/164/146 136/167/148 135/165/147 +f 132/164/146 111/465/122 112/166/123 +f 134/163/145 138/169/150 137/168/149 +f 135/165/147 139/170/151 138/169/150 +f 136/167/148 140/171/152 139/170/151 +f 112/166/123 113/176/125 140/171/152 +f 138/169/150 142/173/133 141/172/153 +f 139/170/151 143/175/141 142/173/133 +f 139/170/151 140/171/152 144/174/154 +f 113/176/125 114/466/124 144/174/154 +f 148/179/155 152/182/158 151/180/156 +f 149/181/157 153/184/160 152/182/158 +f 150/183/159 154/186/162 153/184/160 +f 150/183/159 174/450/184 175/185/161 +f 152/182/158 156/188/164 155/187/163 +f 153/184/160 157/189/165 156/188/164 +f 154/186/162 158/190/166 157/189/165 +f 175/185/161 176/195/171 158/190/166 +f 156/188/164 160/192/168 159/191/167 +f 157/189/165 161/194/170 160/192/168 +f 157/189/165 158/190/166 162/193/169 +f 176/195/171 177/231/185 162/193/169 +f 163/196/172 167/199/175 166/197/173 +f 164/198/174 168/201/177 167/199/175 +f 165/200/176 169/203/179 168/201/177 +f 165/200/176 189/448/199 190/202/178 +f 167/199/175 171/205/181 170/204/180 +f 168/201/177 172/206/182 171/205/181 +f 169/203/179 173/207/183 172/206/182 +f 190/202/178 191/212/186 173/207/183 +f 171/205/181 175/209/161 174/208/184 +f 172/206/182 176/211/171 175/209/161 +f 172/206/182 173/207/183 177/210/185 +f 191/212/186 192/467/200 177/210/185 +f 178/213/187 182/217/191 181/214/188 +f 179/216/190 183/219/193 182/217/191 +f 180/218/192 184/221/194 183/219/193 +f 180/218/192 159/468/167 160/220/168 +f 182/217/191 186/223/196 185/222/195 +f 183/219/193 187/224/197 186/223/196 +f 184/221/194 188/225/198 187/224/197 +f 160/220/168 161/230/170 188/225/198 +f 186/223/196 190/227/178 189/226/199 +f 187/224/197 191/229/186 190/227/178 +f 187/224/197 188/225/198 192/228/200 +f 161/230/170 162/469/169 192/228/200 +f 196/233/201 200/237/205 199/234/202 +f 197/236/204 201/239/207 200/237/205 +f 198/238/206 202/241/209 201/239/207 +f 198/238/206 222/451/231 223/240/208 +f 200/237/205 204/243/211 203/242/210 +f 201/239/207 205/244/212 204/243/211 +f 202/241/209 206/245/213 205/244/212 +f 223/240/208 224/250/218 206/245/213 +f 204/243/211 208/247/215 207/246/214 +f 205/244/212 209/249/217 208/247/215 +f 205/244/212 206/245/213 210/248/216 +f 224/250/218 225/285/232 210/248/216 +f 211/251/219 215/254/222 214/252/220 +f 212/253/221 216/256/224 215/254/222 +f 213/255/223 217/258/226 216/256/224 +f 213/255/223 237/470/245 238/257/225 +f 215/254/222 219/260/228 218/259/227 +f 216/256/224 220/261/229 219/260/228 +f 217/258/226 221/262/230 220/261/229 +f 238/257/225 239/267/233 221/262/230 +f 219/260/228 223/264/208 222/263/231 +f 220/261/229 224/266/218 223/264/208 +f 220/261/229 221/262/230 225/265/232 +f 239/267/233 240/471/246 225/265/232 +f 226/268/234 230/271/237 229/269/235 +f 227/270/236 231/273/239 230/271/237 +f 228/272/238 232/275/240 231/273/239 +f 228/272/238 207/472/214 208/274/215 +f 230/271/237 234/277/242 233/276/241 +f 231/273/239 235/278/243 234/277/242 +f 232/275/240 236/279/244 235/278/243 +f 208/274/215 209/284/217 236/279/244 +f 234/277/242 238/281/225 237/280/245 +f 235/278/243 239/283/233 238/281/225 +f 235/278/243 236/279/244 240/282/246 +f 209/284/217 210/473/216 240/282/246 +f 244/287/247 248/290/250 247/288/248 +f 245/289/249 249/292/252 248/290/250 +f 246/291/251 250/294/254 249/292/252 +f 246/291/251 270/455/276 271/293/253 +f 248/290/250 252/296/256 251/295/255 +f 249/292/252 253/297/257 252/296/256 +f 250/294/254 254/298/258 253/297/257 +f 271/293/253 272/303/263 254/298/258 +f 252/296/256 256/300/260 255/299/259 +f 253/297/257 257/302/262 256/300/260 +f 253/297/257 254/298/258 258/301/261 +f 272/303/263 273/339/277 258/301/261 +f 259/304/264 263/307/267 262/305/265 +f 260/306/266 264/309/269 263/307/267 +f 261/308/268 265/311/271 264/309/269 +f 261/308/268 285/453/291 286/310/270 +f 263/307/267 267/313/273 266/312/272 +f 264/309/269 268/314/274 267/313/273 +f 265/311/271 269/315/275 268/314/274 +f 286/310/270 287/320/278 269/315/275 +f 267/313/273 271/317/253 270/316/276 +f 268/314/274 272/319/263 271/317/253 +f 268/314/274 269/315/275 273/318/277 +f 287/320/278 288/474/292 273/318/277 +f 274/321/279 278/325/283 277/322/280 +f 275/324/282 279/327/285 278/325/283 +f 276/326/284 280/329/286 279/327/285 +f 276/326/284 255/475/259 256/328/260 +f 278/325/283 282/331/288 281/330/287 +f 279/327/285 283/332/289 282/331/288 +f 280/329/286 284/333/290 283/332/289 +f 256/328/260 257/338/262 284/333/290 +f 282/331/288 286/335/270 285/334/291 +f 283/332/289 287/337/278 286/335/270 +f 283/332/289 284/333/290 288/336/292 +f 257/338/262 258/476/261 288/336/292 +f 292/341/293 296/345/297 295/342/294 +f 293/344/296 297/347/299 296/345/297 +f 294/346/298 298/349/301 297/347/299 +f 294/346/298 318/449/323 319/348/300 +f 296/345/297 300/351/303 299/350/302 +f 297/347/299 301/352/304 300/351/303 +f 298/349/301 302/353/305 301/352/304 +f 319/348/300 320/358/310 302/353/305 +f 300/351/303 304/355/307 303/354/306 +f 301/352/304 305/357/309 304/355/307 +f 301/352/304 302/353/305 306/356/308 +f 320/358/310 321/391/324 306/356/308 +f 307/359/311 311/362/314 310/360/312 +f 308/361/313 312/364/316 311/362/314 +f 309/363/315 313/366/318 312/364/316 +f 309/363/315 333/388/337 334/365/317 +f 311/362/314 315/368/320 314/367/319 +f 312/364/316 316/369/321 315/368/320 +f 313/366/318 317/370/322 316/369/321 +f 334/365/317 335/375/325 317/370/322 +f 315/368/320 319/372/300 318/371/323 +f 316/369/321 320/374/310 319/372/300 +f 316/369/321 317/370/322 321/373/324 +f 335/375/325 336/389/338 321/373/324 +f 322/376/326 326/379/329 325/377/327 +f 323/378/328 327/381/331 326/379/329 +f 324/380/330 328/383/332 327/381/331 +f 324/380/330 303/477/306 304/382/307 +f 326/379/329 330/385/334 329/384/333 +f 327/381/331 331/386/335 330/385/334 +f 328/383/332 332/387/336 331/386/335 +f 304/382/307 305/390/309 332/387/336 +f 330/385/334 334/365/317 333/388/337 +f 331/386/335 335/375/325 334/365/317 +f 331/386/335 332/387/336 336/389/338 +f 305/390/309 306/478/308 336/389/338 +f 340/393/339 344/396/342 343/394/340 +f 341/395/341 345/398/344 344/396/342 +f 342/397/343 346/400/346 345/398/344 +f 342/397/343 366/452/368 367/399/345 +f 344/396/342 348/402/348 347/401/347 +f 345/398/344 349/403/349 348/402/348 +f 346/400/346 350/404/350 349/403/349 +f 367/399/345 368/409/355 350/404/350 +f 348/402/348 352/406/352 351/405/351 +f 349/403/349 353/408/354 352/406/352 +f 349/403/349 350/404/350 354/407/353 +f 368/409/355 369/443/369 354/407/353 +f 355/410/356 359/413/359 358/411/357 +f 356/412/358 360/415/361 359/413/359 +f 357/414/360 361/417/363 360/415/361 +f 357/414/360 381/440/383 382/416/362 +f 359/413/359 363/419/365 362/418/364 +f 360/415/361 364/420/366 363/419/365 +f 361/417/363 365/421/367 364/420/366 +f 382/416/362 383/426/370 365/421/367 +f 363/419/365 367/423/345 366/422/368 +f 364/420/366 368/425/355 367/423/345 +f 364/420/366 365/421/367 369/424/369 +f 383/426/370 384/441/384 369/424/369 +f 370/427/371 374/431/375 373/428/372 +f 371/430/374 375/433/377 374/431/375 +f 372/432/376 376/435/378 375/433/377 +f 372/432/376 351/479/351 352/434/352 +f 374/431/375 378/437/380 377/436/379 +f 375/433/377 379/438/381 378/437/380 +f 376/435/378 380/439/382 379/438/381 +f 352/434/352 353/442/354 380/439/382 +f 378/437/380 382/416/362 381/440/383 +f 379/438/381 383/426/370 382/416/362 +f 379/438/381 380/439/382 384/441/384 +f 353/442/354 354/480/353 384/441/384 +f 1/18/18 7/17/17 100/125/109 +f 7/17/17 11/25/25 101/128/112 +f 101/128/112 11/25/25 15/29/29 +f 15/29/29 36/481/53 126/445/139 +f 36/55/53 35/53/51 122/151/135 +f 35/53/51 34/51/49 118/144/128 +f 34/51/49 3/12/12 98/11/11 +f 49/72/64 55/71/63 19/34/34 +f 55/71/63 59/79/71 20/36/36 +f 20/36/36 59/79/71 63/83/75 +f 63/83/75 84/482/100 45/446/60 +f 84/110/100 83/108/98 41/59/56 +f 83/108/98 82/106/96 37/52/50 +f 82/106/96 51/10/10 3/12/12 +f 145/7/7 151/180/156 67/88/80 +f 151/180/156 155/187/163 68/91/83 +f 68/91/83 155/187/163 159/191/167 +f 159/191/167 180/483/192 93/447/107 +f 180/218/192 179/216/190 89/114/103 +f 179/216/190 178/213/187 85/107/97 +f 178/213/187 147/215/189 51/10/10 +f 99/15/15 133/161/143 163/196/172 +f 133/161/143 137/168/149 164/198/174 +f 164/198/174 137/168/149 141/172/153 +f 141/172/153 117/484/131 189/448/199 +f 117/147/131 116/145/129 185/222/195 +f 116/145/129 115/143/127 181/214/188 +f 115/143/127 98/11/11 147/215/189 +f 97/127/111 103/126/110 292/341/293 +f 103/126/110 107/134/118 293/344/296 +f 293/344/296 107/134/118 111/138/122 +f 111/138/122 132/485/146 318/449/323 +f 132/164/146 131/162/144 314/367/319 +f 131/162/144 130/160/142 310/360/312 +f 310/360/312 130/160/142 99/15/15 +f 337/9/9 343/394/340 148/179/155 +f 343/394/340 347/401/347 149/181/157 +f 149/181/157 347/401/347 351/405/351 +f 351/405/351 372/486/376 174/450/184 +f 372/432/376 371/430/374 170/204/180 +f 371/430/374 370/427/371 166/197/173 +f 166/197/173 370/427/371 339/429/373 +f 291/6/6 325/377/327 355/410/356 +f 325/377/327 329/384/333 356/412/358 +f 356/412/358 329/384/333 333/388/337 +f 333/388/337 309/363/315 381/440/383 +f 309/363/315 308/361/313 377/436/379 +f 308/361/313 307/359/311 373/428/372 +f 373/428/372 307/359/311 290/14/14 +f 289/343/295 295/342/294 196/233/201 +f 295/342/294 299/350/302 197/236/204 +f 197/236/204 299/350/302 303/354/306 +f 303/354/306 324/487/330 222/451/231 +f 324/380/330 323/378/328 218/259/227 +f 323/378/328 322/376/326 214/252/220 +f 322/376/326 291/6/6 194/5/5 +f 241/8/8 247/288/248 340/393/339 +f 247/288/248 251/295/255 341/395/341 +f 341/395/341 251/295/255 255/299/259 +f 255/299/259 276/488/284 366/452/368 +f 276/326/284 275/324/282 362/418/364 +f 275/324/282 274/321/279 358/411/357 +f 274/321/279 243/323/281 338/4/4 +f 195/3/3 229/269/235 259/304/264 +f 229/269/235 233/276/241 260/306/266 +f 260/306/266 233/276/241 237/280/245 +f 237/280/245 213/489/223 285/453/291 +f 213/255/223 212/253/221 281/330/287 +f 212/253/221 211/251/219 277/322/280 +f 211/251/219 194/5/5 243/323/281 +f 193/235/203 199/234/202 4/16/16 +f 199/234/202 203/242/210 5/19/19 +f 5/19/19 203/242/210 207/246/214 +f 207/246/214 228/490/238 30/454/46 +f 228/272/238 227/270/236 26/42/42 +f 227/270/236 226/268/234 22/35/35 +f 22/35/35 226/268/234 195/3/3 +f 50/90/82 70/89/81 244/287/247 +f 70/89/81 74/97/89 245/289/249 +f 245/289/249 74/97/89 78/101/93 +f 78/101/93 54/491/67 270/455/276 +f 54/75/67 53/73/65 266/312/272 +f 53/73/65 52/70/62 262/305/265 +f 262/305/265 52/70/62 49/72/64 +f 289/343/295 193/235/203 1/18/18 diff --git a/tex.jpg b/tex.jpg new file mode 100644 index 00000000..e2e593bf Binary files /dev/null and b/tex.jpg differ