Przeglądaj źródła

Merge pull request #13346 from richardmonette/exr-piz-wavelet

exr piz wavelet decompression
Mr.doob 7 lat temu
rodzic
commit
b12d123603

+ 732 - 72
examples/js/loaders/EXRLoader.js

@@ -1,9 +1,77 @@
 /**
  * @author Richard M. / https://github.com/richardmonette
+ *
+ * OpenEXR loader which, currently, supports reading 16 bit half data, in either
+ * uncompressed or PIZ wavelet compressed form.
+ *
+ * Referred to the original Industrial Light & Magic OpenEXR implementation and the TinyEXR / Syoyo Fujita
+ * implementation, so I have preserved their copyright notices.
  */
 
-// https://github.com/mrdoob/three.js/issues/10652
-// https://en.wikipedia.org/wiki/OpenEXR
+// /*
+// Copyright (c) 2014 - 2017, Syoyo Fujita
+// 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.
+//     * Neither the name of the Syoyo Fujita nor the
+//       names of its contributors may be used to endorse or promote products
+//       derived from this software without specific prior written permission.
+
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 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 <COPYRIGHT HOLDER> 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.
+// */
+
+// // TinyEXR contains some OpenEXR code, which is licensed under ------------
+
+// ///////////////////////////////////////////////////////////////////////////
+// //
+// // Copyright (c) 2002, Industrial Light & Magic, a division of Lucas
+// // Digital Ltd. LLC
+// //
+// // 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.
+// // *       Neither the name of Industrial Light & Magic nor the names of
+// // its contributors may be used to endorse or promote products derived
+// // from this software without specific prior written permission.
+// //
+// // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 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 COPYRIGHT
+// // OWNER 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.
+// //
+// ///////////////////////////////////////////////////////////////////////////
+
+// // End of OpenEXR license -------------------------------------------------
 
 THREE.EXRLoader = function ( manager ) {
 
@@ -15,6 +83,518 @@ THREE.EXRLoader.prototype = Object.create( THREE.DataTextureLoader.prototype );
 
 THREE.EXRLoader.prototype._parser = function ( buffer ) {
 
+	const USHORT_RANGE = (1 << 16);
+	const BITMAP_SIZE = (USHORT_RANGE >> 3);
+
+	const HUF_ENCBITS = 16;  // literal (value) bit length
+	const HUF_DECBITS = 14;  // decoding bit size (>= 8)
+
+	const HUF_ENCSIZE = (1 << HUF_ENCBITS) + 1;  // encoding table size
+	const HUF_DECSIZE = 1 << HUF_DECBITS;        // decoding table size
+	const HUF_DECMASK = HUF_DECSIZE - 1;
+
+	const SHORT_ZEROCODE_RUN = 59;
+	const LONG_ZEROCODE_RUN = 63;
+	const SHORTEST_LONG_RUN = 2 + LONG_ZEROCODE_RUN - SHORT_ZEROCODE_RUN;
+	const LONGEST_LONG_RUN = 255 + SHORTEST_LONG_RUN;
+
+	const BYTES_PER_HALF = 2;
+
+	const ULONG_SIZE = 8;
+	const FLOAT32_SIZE = 4;
+	const INT32_SIZE = 4;
+	const INT16_SIZE = 2;
+	const INT8_SIZE = 1;
+
+	function reverseLutFromBitmap(bitmap, lut) {
+		var k = 0;
+
+		for (var i = 0; i < USHORT_RANGE; ++i) {
+			if ((i == 0) || (bitmap[i >> 3] & (1 << (i & 7)))) {
+				lut[k++] = i;
+			}
+		}
+
+		var n = k - 1;
+
+		while (k < USHORT_RANGE) lut[k++] = 0;
+
+		return n;
+	}
+
+	function hufClearDecTable(hdec) {
+		for (var i = 0; i < HUF_DECSIZE; i++) {
+			hdec[i] = {}
+			hdec[i].len = 0;
+			hdec[i].lit = 0;
+			hdec[i].p = null;
+		}
+	}
+
+	const getBitsReturn = { l: 0, c: 0, lc: 0 };
+	function getBits(nBits, c, lc, uInt8Array, inOffset) {
+		while (lc < nBits) {
+			c = (c << 8) | parseUint8Array(uInt8Array, inOffset);
+			lc += 8;
+		}
+
+		lc -= nBits;
+
+		getBitsReturn.l = (c >> lc) & ((1 << nBits) - 1);
+		getBitsReturn.c = c;
+		getBitsReturn.lc = lc;
+	}
+
+	const hufTableBuffer = new Array(59);
+	function hufCanonicalCodeTable(hcode) {
+		for (var i = 0; i <= 58; ++i) hufTableBuffer[i] = 0;
+
+		for (var i = 0; i < HUF_ENCSIZE; ++i) hufTableBuffer[hcode[i]] += 1;
+
+		var c = 0;
+
+		for (var i = 58; i > 0; --i) {
+			var nc = ((c + hufTableBuffer[i]) >> 1);
+			hufTableBuffer[i] = c;
+			c = nc;
+		}
+
+		for (var i = 0; i < HUF_ENCSIZE; ++i) {
+			var l = hcode[i];
+
+			if (l > 0) hcode[i] = l | (hufTableBuffer[l]++ << 6);
+		}
+	}
+
+	function hufUnpackEncTable(uInt8Array, inDataView, inOffset, ni, im, iM, hcode) {
+		var p = inOffset;
+		var c = 0;
+		var lc = 0;
+
+		for (; im <= iM; im++) {
+			if (p.value - inOffset.value > ni) {
+				return false;
+			}
+
+			getBits(6, c, lc, uInt8Array, p);
+			var l = getBitsReturn.l;
+			c = getBitsReturn.c;
+			lc = getBitsReturn.lc;
+			hcode[im] = l;
+
+			if (l == LONG_ZEROCODE_RUN) {
+				if (p.value - inOffset.value > ni) {
+					throw 'Something wrong with hufUnpackEncTable';
+				}
+
+				getBits(8, c, lc, uInt8Array, p);
+				var zerun = getBitsReturn.l + SHORTEST_LONG_RUN;
+				c = getBitsReturn.c;
+				lc = getBitsReturn.lc;
+
+				if (im + zerun > iM + 1) {
+					throw 'Something wrong with hufUnpackEncTable';
+				}
+
+				while (zerun--) hcode[im++] = 0;
+
+				im--;
+			} else if (l >= SHORT_ZEROCODE_RUN) {
+				var zerun = l - SHORT_ZEROCODE_RUN + 2;
+
+				if (im + zerun > iM + 1) {
+					throw 'Something wrong with hufUnpackEncTable';
+				}
+
+				while (zerun--) hcode[im++] = 0;
+
+				im--;
+			}
+		}
+
+		hufCanonicalCodeTable(hcode);
+	}
+
+	function hufLength(code) { return code & 63; }
+
+	function hufCode(code) { return code >> 6; }
+
+	function hufBuildDecTable(hcode, im, iM, hdecod) {
+		for (; im <= iM; im++) {
+			var c = hufCode(hcode[im]);
+			var l = hufLength(hcode[im]);
+
+			if (c >> l) {
+				throw 'Invalid table entry';
+			}
+
+			if (l > HUF_DECBITS) {
+				var pl = hdecod[(c >> (l - HUF_DECBITS))];
+
+				if (pl.len) {
+					throw 'Invalid table entry';
+				}
+
+				pl.lit++;
+
+				if (pl.p) {
+					var p = pl.p;
+					pl.p = new Array(pl.lit);
+
+					for (var i = 0; i < pl.lit - 1; ++i) {
+						pl.p[i] = p[i];
+					}
+				} else {
+					pl.p = new Array(1);
+				}
+
+				pl.p[pl.lit - 1] = im;
+			} else if (l) {
+				var plOffset = 0;
+
+				for (var i = 1 << (HUF_DECBITS - l); i > 0; i--) {
+					var pl = hdecod[(c << (HUF_DECBITS - l)) + plOffset];
+
+					if (pl.len || pl.p) {
+						throw 'Invalid table entry';
+					}
+
+					pl.len = l;
+					pl.lit = im;
+
+					plOffset++;
+				}
+			}
+		}
+
+		return true;
+	}
+
+	const getCharReturn = { c: 0, lc: 0 };
+	function getChar(c, lc, uInt8Array, inOffset) {
+		c = (c << 8) | parseUint8Array(uInt8Array, inOffset);
+		lc += 8;
+
+		getCharReturn.c = c;
+		getCharReturn.lc = lc;
+	}
+
+	const getCodeReturn = { c: 0, lc: 0 };
+	function getCode(po, rlc, c, lc, uInt8Array, inDataView, inOffset, outBuffer, outBufferOffset, outBufferEndOffset) {
+		if (po == rlc) {
+			if (lc < 8) {
+				getChar(c, lc, uInt8Array, inOffset);
+				c = getCharReturn.c;
+				lc = getCharReturn.lc;
+			}
+
+			lc -= 8;
+
+			var cs = (c >> lc);
+
+			if (out + cs > oe) {
+				throw 'Issue with getCode';
+			}
+
+			var s = out[-1];
+
+			while (cs-- > 0) {
+				outBuffer[outBufferOffset.value++] = s;
+			}
+		} else if (outBufferOffset.value < outBufferEndOffset) {
+			outBuffer[outBufferOffset.value++] = po;
+		} else {
+			throw 'Issue with getCode';
+		}
+
+		getCodeReturn.c = c;
+		getCodeReturn.lc = lc;
+	}
+
+	var NBITS = 16;
+	var A_OFFSET = 1 << (NBITS - 1);
+	var M_OFFSET = 1 << (NBITS - 1);
+	var MOD_MASK = (1 << NBITS) - 1;
+
+	function UInt16(value) {
+		return (value & 0xFFFF);
+	};
+
+	function Int16(value) {
+		var ref = UInt16(value);
+		return (ref > 0x7FFF) ? ref - 0x10000 : ref;
+	};
+
+	const wdec14Return = { a: 0, b: 0 };
+	function wdec14(l, h) {
+		var ls = Int16(l);
+		var hs = Int16(h);
+
+		var hi = hs;
+		var ai = ls + (hi & 1) + (hi >> 1);
+
+		var as = ai;
+		var bs = ai - hi;
+
+		wdec14Return.a = as;
+		wdec14Return.b = bs;
+	}
+
+	function wav2Decode(j, buffer, nx, ox, ny, oy, mx) {
+		var n = (nx > ny) ? ny : nx;
+		var p = 1;
+		var p2;
+
+		while (p <= n) p <<= 1;
+
+		p >>= 1;
+		p2 = p;
+		p >>= 1;
+
+		while (p >= 1) {
+			var py = 0;
+			var ey = py + oy * (ny - p2);
+			var oy1 = oy * p;
+			var oy2 = oy * p2;
+			var ox1 = ox * p;
+			var ox2 = ox * p2;
+			var i00, i01, i10, i11;
+
+			for (; py <= ey; py += oy2) {
+				var px = py;
+				var ex = py + ox * (nx - p2);
+
+				for (; px <= ex; px += ox2) {
+					var p01 = px + ox1;
+					var p10 = px + oy1;
+					var p11 = p10 + ox1;
+
+					wdec14(buffer[px + j], buffer[p10 + j]);
+					i00 = wdec14Return.a;
+					i10 = wdec14Return.b;
+
+					wdec14(buffer[p01 + j], buffer[p11 + j]);
+					i01 = wdec14Return.a;
+					i11 = wdec14Return.b;
+
+					wdec14(i00, i01);
+					buffer[px + j] = wdec14Return.a;
+					buffer[p01 + j] = wdec14Return.b;
+
+					wdec14(i10, i11);
+					buffer[p10 + j] = wdec14Return.a;
+					buffer[p11 + j] = wdec14Return.b;
+				}
+
+				if (nx & p) {
+					var p10 = px + oy1;
+
+					wdec14(buffer[px + j], buffer[p10 + j]);
+					i00 = wdec14Return.a;
+					buffer[p10 + j] = wdec14Return.b;
+
+					buffer[px + j] = i00;
+				}
+			}
+
+			if (ny & p) {
+				var px = py;
+				var ex = py + ox * (nx - p2);
+
+				for (; px <= ex; px += ox2) {
+					var p01 = px + ox1;
+
+					wdec14(buffer[px + j], buffer[p01 + j]);
+					i00 = wdec14Return.a;
+					buffer[p01 + j] = wdec14Return.b;
+
+					buffer[px + j] = i00;
+				}
+			}
+
+			p2 = p;
+			p >>= 1;
+		}
+
+		return py;
+	}
+
+	function hufDecode(encodingTable, decodingTable, uInt8Array, inDataView, inOffset, ni, rlc, no, outBuffer, outOffset) {
+		var c = 0;
+		var lc = 0;
+		var outBufferEndOffset = no;
+		var inOffsetEnd = Math.trunc(inOffset.value + (ni + 7) / 8);
+
+		while (inOffset.value < inOffsetEnd) {
+			getChar(c, lc, uInt8Array, inOffset);
+			c = getCharReturn.c;
+			lc = getCharReturn.lc;
+
+			while (lc >= HUF_DECBITS) {
+				var index = (c >> (lc - HUF_DECBITS)) & HUF_DECMASK;
+				var pl = decodingTable[index];
+
+				if (pl.len) {
+					lc -= pl.len;
+					getCode(pl.lit, rlc, c, lc, uInt8Array, inDataView, inOffset, outBuffer, outOffset, outBufferEndOffset);
+					c = getCodeReturn.c;
+					lc = getCodeReturn.lc;
+				} else {
+					if (!pl.p) {
+						throw 'hufDecode issues';
+					}
+
+					var j;
+
+					for (j = 0; j < pl.lit; j++) {
+						var l = hufLength(encodingTable[pl.p[j]]);
+
+						while (lc < l && inOffset.value < inOffsetEnd) {
+							getChar(c, lc, uInt8Array, inOffset);
+							c = getCharReturn.c;
+							lc = getCharReturn.lc;
+						}
+
+						if (lc >= l) {
+							if (hufCode(encodingTable[pl.p[j]]) ==
+								((c >> (lc - l)) & ((1 << l) - 1))) {
+
+								lc -= l;
+								getCode(pl.p[j], rlc, c, lc, uInt8Array, inDataView, inOffset, outBuffer, outOffset, outBufferEndOffset);
+								c = getCodeReturn.c;
+								lc = getCodeReturn.lc;
+								break;
+							}
+						}
+					}
+
+					if (j == pl.lit) {
+						throw 'hufDecode issues';
+					}
+				}
+			}
+		}
+
+		var i = (8 - ni) & 7;
+		c >>= i;
+		lc -= i;
+
+		while (lc > 0) {
+			var pl = decodingTable[(c << (HUF_DECBITS - lc)) & HUF_DECMASK];
+
+			if (pl.len) {
+				lc -= pl.len;
+				getCode(pl.lit, rlc, c, lc, uInt8Array, inDataView, inOffset, outBuffer, outOffset, outBufferEndOffset);
+				c = getCodeReturn.c;
+				lc = getCodeReturn.lc;
+			} else {
+				throw 'hufDecode issues';
+			}
+		}
+
+		return true;
+	}
+
+	function hufUncompress(uInt8Array, inDataView, inOffset, nCompressed, outBuffer, outOffset, nRaw) {
+		var initialInOffset = inOffset.value;
+
+		var im = parseUint32(inDataView, inOffset);
+		var iM = parseUint32(inDataView, inOffset);
+		inOffset.value += 4;
+		var nBits = parseUint32(inDataView, inOffset);
+		inOffset.value += 4;
+
+		if (im < 0 || im >= HUF_ENCSIZE || iM < 0 || iM >= HUF_ENCSIZE) {
+			throw 'Something wrong with HUF_ENCSIZE';
+		}
+
+		var freq = new Array(HUF_ENCSIZE);
+		var hdec = new Array(HUF_DECSIZE);
+
+		hufClearDecTable(hdec);
+
+		var ni = nCompressed - (inOffset.value - initialInOffset);
+
+		hufUnpackEncTable(uInt8Array, inDataView, inOffset, ni, im, iM, freq);
+
+		if (nBits > 8 * (nCompressed - (inOffset.value - initialInOffset))) {
+			throw 'Something wrong with hufUncompress';
+		}
+
+		hufBuildDecTable(freq, im, iM, hdec);
+
+		hufDecode(freq, hdec, uInt8Array, inDataView, inOffset, nBits, iM, nRaw, outBuffer, outOffset);
+	}
+
+	function applyLut(lut, data, nData) {
+		for (var i = 0; i < nData; ++i) {
+			data[i] = lut[data[i]];
+		}
+	}
+
+	function decompressPIZ(outBuffer, outOffset, uInt8Array, inDataView, inOffset, tmpBufSize, num_channels, exrChannelInfos, dataWidth, num_lines) {
+		var bitmap = new Uint8Array(BITMAP_SIZE);
+
+		var minNonZero = parseUint16(inDataView, inOffset);
+		var maxNonZero = parseUint16(inDataView, inOffset);
+
+		if (maxNonZero >= BITMAP_SIZE) {
+			throw 'Something is wrong with PIZ_COMPRESSION BITMAP_SIZE'
+		}
+
+		if (minNonZero <= maxNonZero) {
+			for (var i = 0; i < maxNonZero - minNonZero + 1; i++) {
+				bitmap[i + minNonZero] = parseUint8(inDataView, inOffset);
+			}
+		}
+
+		var lut = new Uint16Array(USHORT_RANGE);
+		var maxValue = reverseLutFromBitmap(bitmap, lut);
+
+		var length = parseUint32(inDataView, inOffset);
+
+		hufUncompress(uInt8Array, inDataView, inOffset, length, outBuffer, outOffset, tmpBufSize);
+
+		var pizChannelData = new Array(num_channels);
+
+		var outBufferEnd = 0
+
+		for (var i = 0; i < num_channels; i++) {
+			var exrChannelInfo = exrChannelInfos[i];
+
+			var pixelSize = 2; // assumes HALF_FLOAT
+
+			pizChannelData[i] = {};
+			pizChannelData[i]['start'] = outBufferEnd;
+			pizChannelData[i]['end'] = pizChannelData[i]['start'];
+			pizChannelData[i]['nx'] = dataWidth;
+			pizChannelData[i]['ny'] = num_lines;
+			pizChannelData[i]['size'] = 1;
+
+			outBufferEnd += pizChannelData[i].nx * pizChannelData[i].ny * pizChannelData[i].size;
+		}
+
+		var fooOffset = 0;
+
+		for (var i = 0; i < num_channels; i++) {
+			for (var j = 0; j < pizChannelData[i].size; ++j) {
+				fooOffset += wav2Decode(
+				j + fooOffset,
+				outBuffer,
+				pizChannelData[i].nx,
+				pizChannelData[i].size,
+				pizChannelData[i].ny,
+				pizChannelData[i].nx * pizChannelData[i].size,
+				maxValue
+				);
+			}
+		}
+
+		applyLut(lut, outBuffer, outBufferEnd);
+
+		return true;
+	}
+
 	function parseNullTerminatedString( buffer, offset ) {
 
 		var uintBuffer = new Uint8Array( buffer );
@@ -27,7 +607,7 @@ THREE.EXRLoader.prototype._parser = function ( buffer ) {
 		}
 
 		var stringValue = new TextDecoder().decode(
-			new Uint8Array( buffer ).slice( offset.value, offset.value + endOffset )
+			uintBuffer.slice( offset.value, offset.value + endOffset )
 		);
 
 		offset.value = offset.value + endOffset + 1;
@@ -48,41 +628,51 @@ THREE.EXRLoader.prototype._parser = function ( buffer ) {
 
 	}
 
-	function parseUlong( buffer, offset ) {
+	function parseUlong( dataView, offset ) {
 
-		var uLong = new DataView( buffer.slice( offset.value, offset.value + 4 ) ).getUint32( 0, true );
+		var uLong = dataView.getUint32( 0, true );
 
-		offset.value = offset.value + 8;
+		offset.value = offset.value + ULONG_SIZE;
 
 		return uLong;
 
 	}
 
-	function parseUint32( buffer, offset ) {
+	function parseUint32( dataView, offset ) {
 
-		var Uint32 = new DataView( buffer.slice( offset.value, offset.value + 4 ) ).getUint32( 0, true );
+		var Uint32 = dataView.getUint32(offset.value, true);
 
-		offset.value = offset.value + 4;
+		offset.value = offset.value + INT32_SIZE;
 
 		return Uint32;
 
 	}
 
-	function parseUint8( buffer, offset ) {
+	function parseUint8Array( uInt8Array, offset ) {
+
+		var Uint8 = uInt8Array[offset.value];
+
+		offset.value = offset.value + INT8_SIZE;
+
+		return Uint8;
+
+	}
+
+	function parseUint8( dataView, offset ) {
 
-		var Uint8 = new DataView( buffer.slice( offset.value, offset.value + 1 ) ).getUint8( 0, true );
+		var Uint8 = dataView.getUint8(offset.value);
 
-		offset.value = offset.value + 1;
+		offset.value = offset.value + INT8_SIZE;
 
 		return Uint8;
 
 	}
 
-	function parseFloat32( buffer, offset ) {
+	function parseFloat32( dataView, offset ) {
 
-		var float = new DataView( buffer.slice( offset.value, offset.value + 4 ) ).getFloat32( 0, true );
+		var float = dataView.getFloat32(offset.value, true);
 
-		offset.value += 4;
+		offset.value += FLOAT32_SIZE;
 
 		return float;
 
@@ -106,17 +696,23 @@ THREE.EXRLoader.prototype._parser = function ( buffer ) {
 
 	}
 
-	function parseFloat16( buffer, offset ) {
+	function parseUint16( dataView, offset ) {
 
-		var float = new DataView( buffer.slice( offset.value, offset.value + 2 ) ).getUint16( 0, true );
+		var Uint16 = dataView.getUint16( offset.value, true );
 
-		offset.value += 2;
+		offset.value += INT16_SIZE;
 
-		return decodeFloat16( float );
+		return Uint16;
 
 	}
 
-	function parseChlist( buffer, offset, size ) {
+	function parseFloat16( buffer, offset ) {
+
+		return decodeFloat16( parseUint16( buffer, offset) );
+
+	}
+
+	function parseChlist( dataView, buffer, offset, size ) {
 
 		var startOffset = offset.value;
 		var channels = [];
@@ -124,11 +720,11 @@ THREE.EXRLoader.prototype._parser = function ( buffer ) {
 		while ( offset.value < ( startOffset + size - 1 ) ) {
 
 			var name = parseNullTerminatedString( buffer, offset );
-			var pixelType = parseUint32( buffer, offset ); // TODO: Cast this to UINT, HALF or FLOAT
-			var pLinear = parseUint8( buffer, offset );
+			var pixelType = parseUint32( dataView, offset ); // TODO: Cast this to UINT, HALF or FLOAT
+			var pLinear = parseUint8( dataView, offset );
 			offset.value += 3; // reserved, three chars
-			var xSampling = parseUint32( buffer, offset );
-			var ySampling = parseUint32( buffer, offset );
+			var xSampling = parseUint32( dataView, offset );
+			var ySampling = parseUint32( dataView, offset );
 
 			channels.push( {
 				name: name,
@@ -146,67 +742,70 @@ THREE.EXRLoader.prototype._parser = function ( buffer ) {
 
 	}
 
-	function parseChromaticities( buffer, offset ) {
+	function parseChromaticities( dataView, offset ) {
 
-		var redX = parseFloat32( buffer, offset );
-		var redY = parseFloat32( buffer, offset );
-		var greenX = parseFloat32( buffer, offset );
-		var greenY = parseFloat32( buffer, offset );
-		var blueX = parseFloat32( buffer, offset );
-		var blueY = parseFloat32( buffer, offset );
-		var whiteX = parseFloat32( buffer, offset );
-		var whiteY = parseFloat32( buffer, offset );
+		var redX = parseFloat32( dataView, offset );
+		var redY = parseFloat32( dataView, offset );
+		var greenX = parseFloat32( dataView, offset );
+		var greenY = parseFloat32( dataView, offset );
+		var blueX = parseFloat32( dataView, offset );
+		var blueY = parseFloat32( dataView, offset );
+		var whiteX = parseFloat32( dataView, offset );
+		var whiteY = parseFloat32( dataView, offset );
 
 		return { redX: redX, redY: redY, greenX, greenY, blueX, blueY, whiteX, whiteY };
 
 	}
 
-	function parseCompression( buffer, offset ) {
+	function parseCompression( dataView, offset ) {
 
 		var compressionCodes = [
 			'NO_COMPRESSION',
+			'RLE_COMPRESSION',
+			'ZIPS_COMPRESSION',
+			'ZIP_COMPRESSION',
 			'PIZ_COMPRESSION'
 		];
 
-		var compression = parseUint8( buffer, offset );
+		var compression = parseUint8( dataView, offset );
 
 		return compressionCodes[ compression ];
 
 	}
 
-	function parseBox2i( buffer, offset ) {
+	function parseBox2i( dataView, offset ) {
 
-		var xMin = parseUint32( buffer, offset );
-		var yMin = parseUint32( buffer, offset );
-		var xMax = parseUint32( buffer, offset );
-		var yMax = parseUint32( buffer, offset );
+		var xMin = parseUint32( dataView, offset );
+		var yMin = parseUint32( dataView, offset );
+		var xMax = parseUint32( dataView, offset );
+		var yMax = parseUint32( dataView, offset );
 
 		return { xMin: xMin, yMin: yMin, xMax: xMax, yMax: yMax };
 
 	}
 
-	function parseLineOrder( buffer, offset ) {
+	function parseLineOrder( dataView, offset ) {
 
 		var lineOrders = [
 			'INCREASING_Y'
 		];
 
-		var lineOrder = parseUint8( buffer, offset );
+		var lineOrder = parseUint8( dataView, offset );
 
 		return lineOrders[ lineOrder ];
 
 	}
 
-	function parseV2f( buffer, offset ) {
+	function parseV2f( dataView, offset ) {
 
-		var x = parseFloat32( buffer, offset );
-		var y = parseFloat32( buffer, offset );
+		var x = parseFloat32( dataView, offset );
+		var y = parseFloat32( dataView, offset );
 
 		return [ x, y ];
 
 	}
 
-	function parseValue( buffer, offset, type, size ) {
+	function parseValue( dataView, buffer, offset, type, size ) {
 
 		if ( type == 'string' || type == 'iccProfile' ) {
 
@@ -214,7 +813,7 @@ THREE.EXRLoader.prototype._parser = function ( buffer ) {
 
 		} else if ( type == 'chlist' ) {
 
-			return parseChlist( buffer, offset, size );
+			return parseChlist( dataView, buffer, offset, size );
 
 		} else if ( type == 'chromaticities' ) {
 
@@ -222,23 +821,23 @@ THREE.EXRLoader.prototype._parser = function ( buffer ) {
 
 		} else if ( type == 'compression' ) {
 
-			return parseCompression( buffer, offset );
+			return parseCompression( dataView, offset );
 
 		} else if ( type == 'box2i' ) {
 
-			return parseBox2i( buffer, offset );
+			return parseBox2i( dataView, offset );
 
 		} else if ( type == 'lineOrder' ) {
 
-			return parseLineOrder( buffer, offset );
+			return parseLineOrder( dataView, offset );
 
 		} else if ( type == 'float' ) {
 
-			return parseFloat32( buffer, offset );
+			return parseFloat32( dataView, offset );
 
 		} else if ( type == 'v2f' ) {
 
-			return parseV2f( buffer, offset );
+			return parseV2f( dataView, offset );
 
 		} else {
 
@@ -248,11 +847,14 @@ THREE.EXRLoader.prototype._parser = function ( buffer ) {
 
 	}
 
+	var bufferDataView = new DataView(buffer);
+	var uInt8Array = new Uint8Array(buffer);
+
 	var EXRHeader = {};
 
-	var magic = new DataView( buffer ).getUint32( 0, true );
-	var versionByteZero = new DataView( buffer ).getUint8( 4, true );
-	var fullMask = new DataView( buffer ).getUint8( 5, true );
+	var magic = bufferDataView.getUint32( 0, true );
+	var versionByteZero = bufferDataView.getUint8( 4, true );
+	var fullMask = bufferDataView.getUint8( 5, true );
 
 	// start of header
 
@@ -271,8 +873,8 @@ THREE.EXRLoader.prototype._parser = function ( buffer ) {
 		} else {
 
 			var attributeType = parseNullTerminatedString( buffer, offset );
-			var attributeSize = parseUint32( buffer, offset );
-			var attributeValue = parseValue( buffer, offset, attributeType, attributeSize );
+			var attributeSize = parseUint32( bufferDataView, offset );
+			var attributeValue = parseValue( bufferDataView, buffer, offset, attributeType, attributeSize );
 
 			EXRHeader[ attributeName ] = attributeValue;
 
@@ -283,12 +885,15 @@ THREE.EXRLoader.prototype._parser = function ( buffer ) {
 	// offsets
 
 	var dataWindowHeight = EXRHeader.dataWindow.yMax + 1;
-	var scanlineBlockSize = 1; // 1 for no compression, 32 for PIZ
+	var scanlineBlockSize = 1; // 1 for NO_COMPRESSION
+	if (EXRHeader.compression == 'PIZ_COMPRESSION') {
+		scanlineBlockSize = 32;
+	}
 	var numBlocks = dataWindowHeight / scanlineBlockSize;
 
 	for ( var i = 0; i < numBlocks; i ++ ) {
 
-		var scanlineOffset = parseUlong( buffer, offset );
+		var scanlineOffset = parseUlong( bufferDataView, offset );
 
 	}
 
@@ -307,41 +912,96 @@ THREE.EXRLoader.prototype._parser = function ( buffer ) {
 		A: 3
 	};
 
-	for ( var y = 0; y < height; y ++ ) {
+	if (EXRHeader.compression == 'NO_COMPRESSION') {
 
-		var y_scanline = parseUint32( buffer, offset );
-		var dataSize = parseUint32( buffer, offset );
+		for ( var y = 0; y < height; y ++ ) {
 
-		for ( var channelID = 0; channelID < EXRHeader.channels.length; channelID ++ ) {
+			var y_scanline = parseUint32( buffer, offset );
+			var dataSize = parseUint32( buffer, offset );
 
-			if ( EXRHeader.channels[ channelID ].pixelType == 1 ) {
+			for ( var channelID = 0; channelID < EXRHeader.channels.length; channelID ++ ) {
 
-				// HALF
-				for ( var x = 0; x < width; x ++ ) {
+				var cOff = channelOffsets[ EXRHeader.channels[ channelID ].name ];
 
-					var val = parseFloat16( buffer, offset );
-					var cOff = channelOffsets[ EXRHeader.channels[ channelID ].name ];
+				if ( EXRHeader.channels[ channelID ].pixelType == 1 ) {
+
+					// HALF
+					for ( var x = 0; x < width; x ++ ) {
+
+						var val = parseFloat16( buffer, offset );
+
+						byteArray[ ( ( ( width - y_scanline ) * ( height * numChannels ) ) + ( x * numChannels ) ) + cOff ] = val;
+
+					}
+
+				} else {
 
-					byteArray[ ( ( ( width - y_scanline ) * ( height * numChannels ) ) + ( x * numChannels ) ) + cOff ] = val;
+					throw 'Only supported pixel format is HALF';
 
 				}
 
-			} else {
+			}
+
+		}
+
+	} else if (EXRHeader.compression == 'PIZ_COMPRESSION') {
+
+		for ( var scanlineBlockIdx = 0; scanlineBlockIdx < height / scanlineBlockSize; scanlineBlockIdx++ ) {
+
+			var line_no = parseUint32( bufferDataView, offset );
+			var data_len = parseUint32( bufferDataView, offset );
+
+			var tmpBufferSize = width * scanlineBlockSize * (EXRHeader.channels.length * BYTES_PER_HALF);
+			var tmpBuffer = new Uint16Array(tmpBufferSize);
+			var tmpOffset = { value: 0 };
+
+			decompressPIZ(tmpBuffer, tmpOffset, uInt8Array, bufferDataView, offset, tmpBufferSize, numChannels, EXRHeader.channels, width, scanlineBlockSize);
+
+			for ( var line_y = 0; line_y < scanlineBlockSize; line_y ++ ) {
+
+				for ( var channelID = 0; channelID < EXRHeader.channels.length; channelID ++ ) {
+
+					var cOff = channelOffsets[ EXRHeader.channels[ channelID ].name ];
 
-				throw 'Only supported pixel format is HALF';
+					if ( EXRHeader.channels[ channelID ].pixelType == 1 ) {
+
+						// HALF
+						for ( var x = 0; x < width; x ++ ) {
+
+							var val = decodeFloat16(tmpBuffer[ (channelID * (scanlineBlockSize * width)) + (line_y * width) + x ]);
+
+							var true_y = line_y + (scanlineBlockIdx * scanlineBlockSize);
+
+							byteArray[ ( ( (height - true_y) * ( width * numChannels ) ) + ( x * numChannels ) ) + cOff ] = val;
+
+						}
+
+					} else {
+
+						throw 'Only supported pixel format is HALF';
+
+					}
+
+				}
 
 			}
 
 		}
 
+	} else {
+
+		throw 'Cannot decompress unsupported compression';
+
 	}
 
+	console.log((endTime - startTime) + 'ms');
+
 	return {
 		header: EXRHeader,
 		width: width,
 		height: height,
 		data: byteArray,
-		format: THREE.RGBAFormat,
+		format: THREE.RGBFormat,
 		type: THREE.FloatType
 	};
 

BIN
examples/textures/piz_compressed.exr


+ 1 - 1
examples/webgl_loader_texture_exr.html

@@ -111,7 +111,7 @@
 
 				var loader = new THREE.EXRLoader();
 
-				var texture = loader.load( "textures/uncompressed.exr", function( texture, textureData ){
+				var texture = loader.load( "textures/piz_compressed.exr", function( texture, textureData ){
 					console.log( textureData.header ); // exr header
 
 					texture.minFilter = THREE.NearestFilter;