UNPKG

three

Version:

JavaScript 3D library

1,007 lines (703 loc) 24.9 kB
/** * @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. */ // /* // 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 ) { this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; }; 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 ); var endOffset = 0; while ( uintBuffer[ offset.value + endOffset ] != 0 ) { endOffset += 1; } var stringValue = new TextDecoder().decode( uintBuffer.slice( offset.value, offset.value + endOffset ) ); offset.value = offset.value + endOffset + 1; return stringValue; } function parseFixedLengthString( buffer, offset, size ) { var stringValue = new TextDecoder().decode( new Uint8Array( buffer ).slice( offset.value, offset.value + size ) ); offset.value = offset.value + size; return stringValue; } function parseUlong( dataView, offset ) { var uLong = dataView.getUint32( 0, true ); offset.value = offset.value + ULONG_SIZE; return uLong; } function parseUint32( dataView, offset ) { var Uint32 = dataView.getUint32(offset.value, true); offset.value = offset.value + INT32_SIZE; return Uint32; } function parseUint8Array( uInt8Array, offset ) { var Uint8 = uInt8Array[offset.value]; offset.value = offset.value + INT8_SIZE; return Uint8; } function parseUint8( dataView, offset ) { var Uint8 = dataView.getUint8(offset.value); offset.value = offset.value + INT8_SIZE; return Uint8; } function parseFloat32( dataView, offset ) { var float = dataView.getFloat32(offset.value, true); offset.value += FLOAT32_SIZE; return float; } // https://stackoverflow.com/questions/5678432/decompressing-half-precision-floats-in-javascript function decodeFloat16( binary ) { var exponent = ( binary & 0x7C00 ) >> 10, fraction = binary & 0x03FF; return ( binary >> 15 ? - 1 : 1 ) * ( exponent ? ( exponent === 0x1F ? fraction ? NaN : Infinity : Math.pow( 2, exponent - 15 ) * ( 1 + fraction / 0x400 ) ) : 6.103515625e-5 * ( fraction / 0x400 ) ); } function parseUint16( dataView, offset ) { var Uint16 = dataView.getUint16( offset.value, true ); offset.value += INT16_SIZE; return Uint16; } function parseFloat16( buffer, offset ) { return decodeFloat16( parseUint16( buffer, offset) ); } function parseChlist( dataView, buffer, offset, size ) { var startOffset = offset.value; var channels = []; while ( offset.value < ( startOffset + size - 1 ) ) { var name = parseNullTerminatedString( 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( dataView, offset ); var ySampling = parseUint32( dataView, offset ); channels.push( { name: name, pixelType: pixelType, pLinear: pLinear, xSampling: xSampling, ySampling: ySampling } ); } offset.value += 1; return channels; } function parseChromaticities( dataView, 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( dataView, offset ) { var compressionCodes = [ 'NO_COMPRESSION', 'RLE_COMPRESSION', 'ZIPS_COMPRESSION', 'ZIP_COMPRESSION', 'PIZ_COMPRESSION' ]; var compression = parseUint8( dataView, offset ); return compressionCodes[ compression ]; } function parseBox2i( dataView, 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( dataView, offset ) { var lineOrders = [ 'INCREASING_Y' ]; var lineOrder = parseUint8( dataView, offset ); return lineOrders[ lineOrder ]; } function parseV2f( dataView, offset ) { var x = parseFloat32( dataView, offset ); var y = parseFloat32( dataView, offset ); return [ x, y ]; } function parseValue( dataView, buffer, offset, type, size ) { if ( type == 'string' || type == 'iccProfile' ) { return parseFixedLengthString( buffer, offset, size ); } else if ( type == 'chlist' ) { return parseChlist( dataView, buffer, offset, size ); } else if ( type == 'chromaticities' ) { return parseChromaticities( buffer, offset ); } else if ( type == 'compression' ) { return parseCompression( dataView, offset ); } else if ( type == 'box2i' ) { return parseBox2i( dataView, offset ); } else if ( type == 'lineOrder' ) { return parseLineOrder( dataView, offset ); } else if ( type == 'float' ) { return parseFloat32( dataView, offset ); } else if ( type == 'v2f' ) { return parseV2f( dataView, offset ); } else { throw 'Cannot parse value for unsupported type: ' + type; } } var bufferDataView = new DataView(buffer); var uInt8Array = new Uint8Array(buffer); var EXRHeader = {}; var magic = bufferDataView.getUint32( 0, true ); var versionByteZero = bufferDataView.getUint8( 4, true ); var fullMask = bufferDataView.getUint8( 5, true ); // start of header var offset = { value: 8 }; // start at 8, after magic stuff var keepReading = true; while ( keepReading ) { var attributeName = parseNullTerminatedString( buffer, offset ); if ( attributeName == 0 ) { keepReading = false; } else { var attributeType = parseNullTerminatedString( buffer, offset ); var attributeSize = parseUint32( bufferDataView, offset ); var attributeValue = parseValue( bufferDataView, buffer, offset, attributeType, attributeSize ); EXRHeader[ attributeName ] = attributeValue; } } // offsets var dataWindowHeight = EXRHeader.dataWindow.yMax + 1; 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( bufferDataView, offset ); } // we should be passed the scanline offset table, start reading pixel data var width = EXRHeader.dataWindow.xMax - EXRHeader.dataWindow.xMin + 1; var height = EXRHeader.dataWindow.yMax - EXRHeader.dataWindow.yMin + 1; var numChannels = EXRHeader.channels.length; var byteArray = new Float32Array( width * height * numChannels ); var channelOffsets = { R: 0, G: 1, B: 2, A: 3 }; if (EXRHeader.compression == 'NO_COMPRESSION') { for ( var y = 0; y < height; y ++ ) { var y_scanline = parseUint32( buffer, offset ); var dataSize = parseUint32( buffer, offset ); for ( var channelID = 0; channelID < EXRHeader.channels.length; channelID ++ ) { 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 { throw 'Only supported pixel format is HALF'; } } } } 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 ]; 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'; } return { header: EXRHeader, width: width, height: height, data: byteArray, format: THREE.RGBFormat, type: THREE.FloatType }; };